···66import { playInputs, plays, playsHistorical, queueStates, relations } from "../schema/schema.ts";
77import { type PlayNew, type PlaySelect, type PlayInputNew, type FindWhere, type FindMany, type QueueStateSelect, type FindWith, type PlaySelectWithQueueStates, type WhereClause, type PlayWith, type PlayHistoricalSelect, type PlayHistoricalNew } from "../drizzleTypes.ts";;
88import type { MarkOptional } from "ts-essentials";
99-import { genGroupIdStrFromPlay, removeEmptyArrays, removeUndefinedKeys } from "../../../../utils.ts";
99+import { genGroupIdStrFromPlay, removeEmptyArrays } from "../../../../utils.ts";
1010+import { removeUndefinedKeys } from '../../../../../core/DataUtils.ts';
1011import dayjs, { type Dayjs } from "dayjs";
1112import { type RelationsFieldFilter, eq, inArray, ne, notInArray, desc, asc, and, sql, Placeholder } from "drizzle-orm";
1213import { type CompactableProperty, type RetentionOptions, retentionPlayTypes } from "../../../infrastructure/config/database.ts";
1314import { shortTodayAwareFormat } from "../../../../../core/TimeUtils.ts";
1414-import { buildDateCompare, type CompareDateOp, type ComponentConstrainedRepoOpts, DrizzleBaseRepository, type DrizzleRepositoryOpts, type PaginatedQueryResponse, type PaginatedResponse } from "./BaseRepository.ts";
1515+import { buildDateCompare, type CompareDateOp, type ComponentConstrainedRepoOpts, DrizzleBaseRepository, type DrizzleRepositoryOpts } from "./BaseRepository.ts";
1616+import { type PaginatedResponse } from "../../../../../core/Api.ts";
1717+import { type PaginatedQueryResponse } from "../../../../../core/Api.ts";
1518import { asPlay } from "../../../../../core/PlayMarshalUtils.ts";
1619import assert, { Assert } from "node:assert";
1720import { hashObject, parseArrayFromMaybeString } from "../../../../utils/StringUtils.ts";
···11-import { childLogger, type Logger, type LoggerAppExtras } from "@foxxmd/logging";
22-import { type DbConcrete, runTransaction } from "../drizzleUtils.ts";
33-import clone from 'clone';
44-import { Traverse, type TraverseContext } from 'neotraverse/modern';
55-import { loggerNoop } from "../../../MaybeLogger.ts";
66-import { type DateLike, type DeepReplaceValue, type ErrorLike, type PlayObject, type PlayState, type QueueName, REGEX_ISO8601_LOOSE, TA_CLOSE, TA_DEFAULT_ACCURACY, TA_EXACT, type TemporalAccuracy } from "../../../../../core/Atomic.ts";
77-import { generateInputEntity, generatePlayEntity, type PlayEntityOpts, hydratePlaySelect, type PlayHydateOptions } from "../entityUtils.ts";
88-import { playInputs, plays, queueStates, relations, schema } from "../schema/schema.ts";
99-import { type PlayNew, type PlaySelect, type PlayInputNew, type FindWhere, type FindMany, type QueueStateSelect, type FindWith, type PlaySelectWithQueueStates, type WhereClause, type PlayWith } from "../drizzleTypes.ts";;
1010-import type { MarkOptional, MarkRequired, PathValue } from "ts-essentials";
1111-import { genGroupIdStrFromPlay, removeEmptyArrays, removeUndefinedKeys } from "../../../../utils.ts";
11+import { childLogger } from "@foxxmd/logging";
122import dayjs, { type Dayjs } from "dayjs";
1313-import { type RelationsFieldFilter, eq, inArray, ne, notInArray, desc, asc, and, sql, Placeholder, relationsFilterToSQL } from "drizzle-orm";
1414-import { type CompactableProperty, type RetentionOptions, retentionPlayTypes } from "../../../infrastructure/config/database.ts";
33+import { eq, inArray, relationsFilterToSQL, sql } from "drizzle-orm";
44+import assert from "node:assert";
55+import type { MarkOptional } from "ts-essentials";
66+import { type DateLike, type DeepReplaceValue, type PlayObject, type PlayState, type QueueName, TA_DEFAULT_ACCURACY, type TemporalAccuracy } from "../../../../../core/Atomic.ts";
77+import { removeUndefinedKeys } from '../../../../../core/DataUtils.ts';
158import { shortTodayAwareFormat } from "../../../../../core/TimeUtils.ts";
1616-import { buildDateCompare, type CompareDateOp, type ComponentConstrainedRepoOpts, DrizzleBaseRepository, type DrizzleRepositoryOpts, type PaginatedQueryResponse, type PaginatedResponse } from "./BaseRepository.ts";
1717-import assert, { Assert } from "node:assert";
1818-import { hashObject, parseArrayFromMaybeString } from "../../../../utils/StringUtils.ts";
199import { playContentBasicInvariantTransform, playMbidIdentifier } from "../../../../utils/PlayComparisonUtils.ts";
2020-import { comparePlayTemporally, getScrobbleTsSOCDate, getScrobbleTsSOCDateWithContext, getTemporalAccuracyCloseVal, hasAcceptableTemporalAccuracy } from "../../../../utils/TimeUtils.ts";
1010+import { hashObject } from "../../../../utils/StringUtils.ts";
1111+import { comparePlayTemporally, getTemporalAccuracyCloseVal, hasAcceptableTemporalAccuracy } from "../../../../utils/TimeUtils.ts";
1212+import { type CompactableProperty, type RetentionOptions, retentionPlayTypes } from "../../../infrastructure/config/database.ts";
2113import { type SourceType } from "../../../infrastructure/config/source/sources.ts";
1414+import { type FindMany, type FindWhere, type FindWith, type PlayInputNew, type PlayNew, type PlaySelect, type PlaySelectWithQueueStates, type PlayWith, type QueueStateSelect, type WhereClause } from "../drizzleTypes.ts";
1515+import { type DbConcrete, runTransaction } from "../drizzleUtils.ts";
1616+import { generateInputEntity, generatePlayEntity, hydratePlaySelect, type PlayEntityOpts, type PlayHydateOptions } from "../entityUtils.ts";
1717+import { playInputs, plays, relations } from "../schema/schema.ts";
1818+import { buildDateCompare, type CompareDateOp, type ComponentConstrainedRepoOpts, DrizzleBaseRepository, type DrizzleRepositoryOpts } from "./BaseRepository.ts";
1919+import { type PaginatedResponse } from "../../../../../core/Api.ts";
2020+import { type PaginatedQueryResponse } from "../../../../../core/Api.ts";
2121+;
22222323// https://github.com/drizzle-team/drizzle-orm/issues/695 may be useful for typing models with relations?
2424
+6-11
src/backend/common/infrastructure/Atomic.ts
···11-import type { Logger } from '@foxxmd/logging';
11+import { type Logger, type LogDataPretty, type LogLevel } from '@foxxmd/logging';
22import type { Dayjs, ManipulateType } from "dayjs";
33import { type Request, type Response } from "express";
44import type { NextFunction, ParamsDictionary, Query } from "express-serve-static-core";
55import { FixedSizeList } from 'fixed-size-list';
66-import { type ErrorLike, isPlayObject, type PlayMeta, type PlayObject, type PlayObjectMinimal, type UnixTimestamp } from "../../../core/Atomic.ts";
66+import { type DeviceId, type ErrorLike, isPlayObject, type PlayMeta, type PlayObject, type PlayObjectMinimal, type PlayPlatformId, type PlayUserId, type UnixTimestamp } from "../../../core/Atomic.ts";
77import TupleMap from "../TupleMap.ts";
88import { MusicBrainzApi } from 'musicbrainz-api';
99import type { SourceType } from './config/source/sources.ts';
1010import { type ClientType, clientTypes } from './config/client/clients.ts';
1111import assert from 'assert';
1212+1313+export interface LeveledLogData extends LogDataPretty {
1414+ levelLabel: string
1515+}
12161317export const lowGranularitySources: SourceType[] = ['subsonic', 'ytmusic'];
1418···114118 initialTrackProgressPosition?: number
115119 }
116120}
117117-export type DeviceId = string;
118118-export type PlayUserId = string;
119119-export type PlayPlatformId = [DeviceId, PlayUserId];
120120-export type PlayPlatformIdStr = string;
121121-122121export type GroupedPlays = TupleMap<DeviceId,PlayUserId,ProgressAwarePlayObject[]>;
123122124123export type GroupedFixedPlays = TupleMap<DeviceId,PlayUserId,FixedSizeList<ProgressAwarePlayObject>>;
···190189export type ExpressRequest = Request<ParamsDictionary, any, any, Query, Record<string, any>>;
191190export type ExpressResponse = Response<any, Record<string, any>>;
192191export type ExpressHandler = (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>
193193-194194-export const DELIMITERS = [',','&','/','\\'];
195195-export const DELIMITERS_NO_AMP = [',','/','\\'];
196196-export const DELIMETERS_REGEX: RegExp = new RegExp(/[,&\/\\]/);
197192198193export const ARTIST_WEIGHT = 0.3;
199194export const TITLE_WEIGHT = 0.4;
···44import { type ExternalMetadataTerm, type PlayTransformMetadataStage } from "../infrastructure/Transform.ts";
55import AtomicPartsTransformer from "./AtomicPartsTransformer.ts";
66import { type TransformerOptions } from "./AbstractTransformer.ts";
77-import { ARTIST_WEIGHT, DELIMITERS, MUSICBRAINZ_URL, type MusicbrainzApiConfigData, TITLE_WEIGHT } from "../infrastructure/Atomic.ts";
77+import { ARTIST_WEIGHT, MUSICBRAINZ_URL, type MusicbrainzApiConfigData, TITLE_WEIGHT } from "../infrastructure/Atomic.ts";
88+import { DELIMITERS } from '../../../core/Atomic.ts';
89import { MaybeLogger } from '../MaybeLogger.ts';
910import { childLogger, type Logger } from "@foxxmd/logging";
1011import { MusicbrainzApiClient, type MusicbrainzApiConfig, recordingToPlay, type UsingTypes } from "../vendor/musicbrainz/MusicbrainzApiClient.ts";
1112import { type IRecordingList, type IRecordingMatch, type IRelease, MusicBrainzApi } from "musicbrainz-api";
1212-import { intersect, isDebugMode, missingMbidTypes, removeUndefinedKeys } from "../../utils.ts";
1313+import { intersect, isDebugMode, missingMbidTypes } from "../../utils.ts";
1414+import { removeUndefinedKeys } from '../../../core/DataUtils.ts';
1315import { SimpleError, SkipTransformStageError, StagePrerequisiteError, StageTransformError } from "../errors/MSErrors.ts";
1416import { parseArrayFromMaybeString, scoreNormalizedStringsWeighted } from "../../utils/StringUtils.ts";
1517import clone from "clone";
···66import { parseArtistCredits, parseTrackCredits, uniqueNormalizedStrArr } from "../../utils/StringUtils.ts";
77import { parseRegexSingle, parseToRegexOrLiteralSearch } from "@foxxmd/regex-buddy-core";
88import { type TransformerOptions } from "./AbstractTransformer.ts";
99-import { DELIMITERS_NO_AMP } from "../infrastructure/Atomic.ts";
99+import { DELIMITERS_NO_AMP } from '../../../core/Atomic.ts';
1010import { asArray } from "../../utils/DataUtils.ts";
1111import { MaybeLogger } from '../MaybeLogger.ts';
1212import { childLogger } from "@foxxmd/logging";
+2-1
src/backend/common/vendor/LastfmApiClient.ts
···11import dayjs, { type Dayjs, type ManipulateType } from "dayjs";
22import { type BrainzMeta, type PlayObject, type PlayObjectMinimal, type ScrobbleActionResult, type UnixTimestamp, type URLData, type Writeable } from "../../../core/Atomic.ts";
33import { artistNamesToCredits, artistNameToCredit, nonEmptyStringOrDefault, splitByFirstFound } from "../../../core/StringUtils.ts";
44-import { removeUndefinedKeys, sleep } from "../../utils.ts";
44+import { sleep } from "../../utils.ts";
55+import { removeUndefinedKeys } from '../../../core/DataUtils.ts';
56import { writeFile } from '../../utils/FSUtils.ts';
67import { objectIsEmpty, readJson } from '../../utils/DataUtils.ts';
78import { isPortReachableConnect, joinedUrl, normalizeWebAddress } from "../../utils/NetworkUtils.ts";
···1313} from "../../utils/StringUtils.ts";
1414import { findDelimiters } from "../../../core/StringUtils.ts";
1515import { UpstreamError } from "../errors/UpstreamError.ts";
1616-import { type AbstractApiOptions, DEFAULT_RETRY_MULTIPLIER, DELIMITERS, type FormatPlayObjectOptions, type PagelessListensTimeRangeOptions, type PagelessTimeRangeListens, type PagelessTimeRangeListensResult } from "../infrastructure/Atomic.ts";
1616+import { type AbstractApiOptions, DEFAULT_RETRY_MULTIPLIER, type FormatPlayObjectOptions, type PagelessListensTimeRangeOptions, type PagelessTimeRangeListens, type PagelessTimeRangeListensResult } from "../infrastructure/Atomic.ts";
1717+import { DELIMITERS } from '../../../core/Atomic.ts';
1718import { DEFAULT_ITEMS_PER_GET_LZ, type ListenBrainzClientData, MAX_ITEMS_PER_GET_LZ } from "../infrastructure/config/client/listenbrainz.ts";
1819import AbstractApiClient from "./AbstractApiClient.ts";
1920import { getBaseFromUrl, isPortReachableConnect, joinedUrl, normalizeWebAddress } from '../../utils/NetworkUtils.ts';
2020-import { removeUndefinedKeys, unique } from '../../utils.ts';
2121+import { unique } from '../../utils.ts';
2222+import { removeUndefinedKeys } from '../../../core/DataUtils.ts';
2123import { type ListenPayload, type ListenResponse, type ListenType, type SubmitPayload } from './listenbrainz/interfaces.ts';
2224import { baseFormatPlayObj } from '../../utils/PlayTransformUtils.ts';
2325import { ScrobbleSubmitError, SimpleError } from '../errors/MSErrors.ts';
···33import { type DiscordStrongData, type StatusType, type DiscordWSData, type ActivityData, ACTIVITY_TYPE } from "../../infrastructure/config/client/discord.ts";
44import type { _DataPayload, _NonDispatchPayload, APIUser, GatewayActivity, GatewayActivityAssets, GatewayHeartbeatRequest, GatewayHelloData, GatewayIdentify, GatewayInvalidSessionData, GatewayPresenceUpdateData, GatewayReadyDispatchData, GatewayResumeData, GatewayUpdatePresence } from "discord.js";
55import { GatewayCloseCodes, GatewayDispatchEvents, GatewayOpcodes, PresenceUpdateStatus } from "discord.js"
66-import { isDebugMode, removeUndefinedKeys, sleep } from "../../../utils.ts";
66+import { isDebugMode, sleep } from "../../../utils.ts";
77+import { removeUndefinedKeys } from '../../../../core/DataUtils.ts';
78import pEvent from 'p-event';
89import EventEmitter from "events";
910import { randomInt } from "crypto";
···824825825826 const activity: Omit<GatewayActivity, 'id'> = removeUndefinedKeys<Omit<GatewayActivity, 'id'>>({
826827 status_display_type: statusDisplayType,
828828+ // @ts-expect-error its fine its an enum thing
827829 type: activityType,
828830 details_url: detailsUrl,
829831 state_url: stateUrl,
+2-1
src/backend/common/vendor/listenbrainz/lzUtils.ts
···11import { type PlayObject } from "../../../../core/Atomic.ts";
22-import { isEmptyArrayOrUndefined, removeUndefinedKeys } from "../../../utils.ts";
22+import { isEmptyArrayOrUndefined } from "../../../utils.ts";
33+import { removeUndefinedKeys } from '../../../../core/DataUtils.ts';
34import { getScrobbleTsSOCDate } from "../../../utils/TimeUtils.ts";
45import { type SubmitOptions } from "../ListenbrainzApiClient.ts";
56import { type ListenPayload, type MinimumTrack, type SubmitListenAdditionalTrackInfo, type SubmitPayload } from "./interfaces.ts";
···88import { type AbstractApiOptions, DEFAULT_RETRY_MULTIPLIER, type FormatPlayObjectOptions, type PaginatedListensTimeRangeOptions, type PaginatedTimeRangeListens, type PaginatedTimeRangeListensResult } from "../../infrastructure/Atomic.ts";
99import { isNodeNetworkException } from "../../errors/NodeErrors.ts";
1010import { isSuperAgentResponseError } from "../../errors/ErrorUtils.ts";
1111-import { getNonEmptyVal, parseRetryAfterSecsFromObj, removeUndefinedKeys, sleep } from "../../../utils.ts";
1111+import { getNonEmptyVal, parseRetryAfterSecsFromObj, sleep } from "../../../utils.ts";
1212+import { removeUndefinedKeys } from '../../../../core/DataUtils.ts';
1213import { UpstreamError } from "../../errors/UpstreamError.ts";
1314import { getMalojaResponseError, isMalojaAPIErrorBody, type MalojaResponseV3CommonData, type MalojaScrobbleData, type MalojaScrobbleRequestData, type MalojaScrobbleV3RequestData, type MalojaScrobbleV3ResponseData, type MalojaScrobbleWarning } from "./interfaces.ts";
1415import { getScrobbleTsSOCDate, getScrobbleTsSOCDateWithContext } from '../../../utils/TimeUtils.ts';
+1-1
src/backend/common/vendor/teal/TealApiClient.ts
···11import dayjs, { Dayjs, type ManipulateType } from "dayjs";
22import { type PlayObject, type PlayObjectMinimal, type BrainzMeta, type MBID, type ScrobbleActionResult, type UnixTimestamp } from "../../../../core/Atomic.ts";
33import { getRoot } from "../../../ioc.ts";
44-import { removeUndefinedKeys } from "../../../utils.ts";
44+import { removeUndefinedKeys } from '../../../../core/DataUtils.ts';
55import { baseFormatPlayObj } from "../../../utils/PlayTransformUtils.ts";
66import { MSCache } from "../../Cache.ts";
77import { type AbstractApiOptions, type PagelessListensTimeRangeOptions, type PagelessTimeRangeListens, type PagelessTimeRangeListensResult } from "../../infrastructure/Atomic.ts";
+1-1
src/backend/scrobblers/AbstractScrobbleClient.ts
···5151 parseBool,
5252 playObjDataMatch,
5353 pollingBackoff,
5454- removeUndefinedKeys,
5554 sleep,
5655 sortByOldestPlayDate,
5756} from "../utils.ts";
5757+import { removeUndefinedKeys } from '../../core/DataUtils.ts';
5858import { findCauseByReference } from "../utils/ErrorUtils.ts";
5959import { type ErrorIsh, messageWithCausesTruncatedDefault } from "../../core/ErrorUtils.ts";
6060import { messageWithCauses } from "../../core/ErrorUtils.ts";
+2-1
src/backend/scrobblers/RockskyScrobbler.ts
···99import { type ListenPayload } from '../common/vendor/listenbrainz/interfaces.ts';
1010import { Notifiers } from "../notifier/Notifiers.ts";
11111212-import { durationToHuman, isDebugMode } from "../utils.ts";
1212+import { isDebugMode } from "../utils.ts";
1313+import { durationToHuman } from '../../core/TimeUtils.ts';
1314import { RockSkyApiClient, rockskyScrobbleToPlay, type SubmitResponse } from "../common/vendor/RockSkyApiClient.ts";
1415import { type RockSkyClientConfig } from "../common/infrastructure/config/client/rocksky.ts";
1516import { ScrobbleSubmitError } from "../common/errors/MSErrors.ts";
+2-1
src/backend/scrobblers/ScrobbleClients.ts
···1111import { type MalojaClientConfig, type MalojaData } from "../common/infrastructure/config/client/maloja.ts";
1212import { WildcardEmitter } from "../common/WildcardEmitter.ts";
1313import { Notifiers } from "../notifier/Notifiers.ts";
1414-import { isDebugMode, nonEmptyObj, parseBool, removeUndefinedKeys } from "../utils.ts";
1414+import { isDebugMode, nonEmptyObj, parseBool } from "../utils.ts";
1515+import { removeUndefinedKeys } from '../../core/DataUtils.ts';
1516import { getCommonComponentEnvConfig, readJson } from '../utils/DataUtils.ts';
1617import { validateJson } from "../utils/ValidationUtils.ts";
1718import AbstractScrobbleClient from "./AbstractScrobbleClient.ts";
+2-1
src/backend/scrobblers/TealfmScrobbler.ts
···2020import { nowPlayingExpirationDuration } from "./AbstractScrobbleClient.ts";
2121import { recordToPlay } from "../common/vendor/teal/TealApiClient.ts";
2222import dayjs, { type Dayjs } from "dayjs";
2323-import { durationToHuman, isDebugMode } from "../utils.ts";
2323+import { isDebugMode } from "../utils.ts";
2424+import { durationToHuman } from '../../core/TimeUtils.ts';
2425import AbstractHistoricalScrobbleClient from "./AbstractHistoricalScrobbleClient.ts";
2526import { fromStream } from '@atcute/repo';
2627import { playToRepositoryCreatePlayHistoricalOpts, type RepositoryCreatePlayHistoricalOpts } from "../common/database/drizzle/repositories/PlayHistoricalRepository.ts";
+1-2
src/backend/server/api.ts
···99 CLIENT_DEAD_QUEUE,
1010 type ClientStatusData,
1111 type DeadLetterScrobble,
1212- type LeveledLogData,
1312 type LogOutputConfig,
1413 PLAY_CLIENT_STATE,
1514 PLAY_SOURCE_STATE,
···2019 type SourceStatusData,
2120} from "../../core/Atomic.ts";
2221import { capitalize } from "../../core/StringUtils.ts";
2323-import { type ExpressHandler } from "../common/infrastructure/Atomic.ts";
2222+import { type ExpressHandler, type LeveledLogData } from "../common/infrastructure/Atomic.ts";
2423import { getRoot } from "../ioc.ts";
2524import AbstractScrobbleClient from "../scrobblers/AbstractScrobbleClient.ts";
2625import AbstractSource from "../sources/AbstractSource.ts";
+4-4
src/backend/sources/AbstractSource.ts
···1010 DEFAULT_POLLING_INTERVAL,
1111 DEFAULT_POLLING_MAX_INTERVAL,
1212 DEFAULT_RETRY_MULTIPLIER,
1313- type DeviceId,
1413 type GroupedFixedPlays,
1514 type InternalConfig,
1615 NO_USER,
1717- type PlayPlatformId,
1818- type PlayUserId,
1916 type ProgressAwarePlayObject,
2017 SINGLE_USER_PLATFORM_ID,
2118} from "../common/infrastructure/Atomic.ts";
1919+import { type PlayPlatformId } from '../../core/Atomic.ts';
2020+import { type PlayUserId } from '../../core/Atomic.ts';
2121+import { type DeviceId } from '../../core/Atomic.ts';
2222import { type SourceType, type SourceConfig } from '../common/infrastructure/config/source/sources.ts';
2323import { TRANSFORM_HOOK } from "../common/infrastructure/Transform.ts";
2424import TupleMap from "../common/TupleMap.ts";
···5151import { AsyncTask, SimpleIntervalJob, ToadScheduler } from 'toad-scheduler';
5252import { type ComponentMinimalSelect } from '../common/database/drizzle/drizzleTypes.ts';
5353import { COMPONENT_STATE, type ComponentSourceApi, type ComponentSourceApiJson, type ComponentState, type PlayApiCommonDetailed } from '../../core/Api.ts';
5454-import { type PaginatedResponse } from '../common/database/drizzle/repositories/BaseRepository.ts';
5454+import { type PaginatedResponse } from "../../core/Api.ts";
55555656export interface RecentlyPlayedOptions {
5757 limit?: number
+1-1
src/backend/sources/AzuracastSource.ts
···1010 type FormatPlayObjectOptions,
1111 type InternalConfig,
1212 type PlayerStateData,
1313- type PlayPlatformId,
1413 REPORTED_PLAYER_STATUSES,
1514 SINGLE_USER_PLATFORM_ID,
1615} from "../common/infrastructure/Atomic.ts";
1616+import { type PlayPlatformId } from '../../core/Atomic.ts';
1717import { type AzuracastSourceConfig, type AzuraNowPlayingResponse, type AzuraStationResponse } from "../common/infrastructure/config/source/azuracast.ts";
1818import { isPortReachable, normalizeWSAddress } from "../utils/NetworkUtils.ts";
1919import { baseFormatPlayObj } from "../utils/PlayTransformUtils.ts";
+1-1
src/backend/sources/EndpointLastfmSource.ts
···77 type InternalConfig,
88 NO_USER,
99 type PlayerStateData,
1010- type PlayPlatformId,
1110 REPORTED_PLAYER_STATUSES,
1211 type ReportedPlayerStatus
1312} from "../common/infrastructure/Atomic.ts";
1313+import { type PlayPlatformId } from '../../core/Atomic.ts';
1414import MemorySource from "./MemorySource.ts";
1515import { type LastFMEndpointSourceConfig } from "../common/infrastructure/config/source/endpointlfm.ts";
1616import { type LastFMScrobbleRequestPayload, scrobblePayloadToPlay } from "../common/vendor/LastfmApiClient.ts";
+1-1
src/backend/sources/EndpointListenbrainzSource.ts
···77 type InternalConfig,
88 NO_USER,
99 type PlayerStateData,
1010- type PlayPlatformId,
1110 REPORTED_PLAYER_STATUSES,
1211 type ReportedPlayerStatus
1312} from "../common/infrastructure/Atomic.ts";
1313+import { type PlayPlatformId } from '../../core/Atomic.ts';
1414import { type ListenbrainzEndpointSourceConfig } from "../common/infrastructure/config/source/endpointlz.ts";
1515import { ListenbrainzApiClient, listenPayloadToPlay } from "../common/vendor/ListenbrainzApiClient.ts";
1616import { type SubmitPayload } from '../common/vendor/listenbrainz/interfaces.ts';
+2-1
src/backend/sources/JellyfinApiSource.ts
···4949 type InternalConfig,
5050 type PlayerStateData,
5151 type PlayerStateDataMaybePlay,
5252- type PlayPlatformId, REPORTED_PLAYER_STATUSES
5252+ REPORTED_PLAYER_STATUSES
5353} from "../common/infrastructure/Atomic.ts";
5454+import { type PlayPlatformId } from '../../core/Atomic.ts';
5455import { type JellyApiSourceConfig } from "../common/infrastructure/config/source/jellyfin.ts";
5556import { getPlatformIdFromData, isDebugMode, parseBool, } from "../utils.ts";
5657import { genGroupIdStr } from '../../core/PlayUtils.ts';
+2-1
src/backend/sources/LastfmSource.ts
···11import EventEmitter from "events";
22import { type PlayObject, SOURCE_SOT } from "../../core/Atomic.ts";
33-import { type FormatPlayObjectOptions, type InternalConfig, type PlayPlatformId, type TimeRangeListensFetcher } from "../common/infrastructure/Atomic.ts";
33+import { type FormatPlayObjectOptions, type InternalConfig, type TimeRangeListensFetcher } from "../common/infrastructure/Atomic.ts";
44+import { type PlayPlatformId } from '../../core/Atomic.ts';
45import { type SourceType } from '../common/infrastructure/config/source/sources.ts';
56import { type LastfmSourceConfig } from "../common/infrastructure/config/source/lastfm.ts";
67import LastfmApiClient, { formatPlayObj } from "../common/vendor/LastfmApiClient.ts";
+2-1
src/backend/sources/ListenbrainzSource.ts
···11import EventEmitter from "events";
22import { type PlayObject, SOURCE_SOT } from "../../core/Atomic.ts";
33import { isNodeNetworkException } from "../common/errors/NodeErrors.ts";
44-import { type FormatPlayObjectOptions, type InternalConfig, type PlayPlatformId, type TimeRangeListensFetcher } from "../common/infrastructure/Atomic.ts";
44+import { type FormatPlayObjectOptions, type InternalConfig, type TimeRangeListensFetcher } from "../common/infrastructure/Atomic.ts";
55+import { type PlayPlatformId } from '../../core/Atomic.ts';
56import { type ListenBrainzSourceConfig } from "../common/infrastructure/config/source/listenbrainz.ts";
67import { ListenbrainzApiClient } from "../common/vendor/ListenbrainzApiClient.ts";
78import { type RecentlyPlayedOptions } from "./AbstractSource.ts";
+1-1
src/backend/sources/MPRISSource.ts
···11-import { type Interfaces as Notifications } from '@dbus-types/notifications'
11+import type { Interfaces as Notifications } from '@dbus-types/notifications'
22import dayjs from "dayjs";
33import { DBusInterface, sessionBus, Connection, type ConnectOpts } from 'dbus-ts';
44import EventEmitter from "events";
+2-1
src/backend/sources/MemoryPositionalSource.ts
···11import { type Logger } from "@foxxmd/logging";
22-import { type InternalConfig, type PlayPlatformId } from "../common/infrastructure/Atomic.ts";
22+import { type InternalConfig } from "../common/infrastructure/Atomic.ts";
33+import { type PlayPlatformId } from '../../core/Atomic.ts';
34import MemorySource from "./MemorySource.ts";
45import { type PlayerStateOptions } from "./PlayerState/AbstractPlayerState.ts";
56import { PositionalPlayerState } from "./PlayerState/PositionalPlayerState.ts";
+1-1
src/backend/sources/MemorySource.ts
···88 asPlayerStateDataMaybePlay,
99 CALCULATED_PLAYER_STATUSES,
1010 type InternalConfig, type PlayerStateDataMaybePlay,
1111- type PlayPlatformId,
1211 type ProgressAwarePlayObject} from "../common/infrastructure/Atomic.ts";
1212+import { type PlayPlatformId } from '../../core/Atomic.ts';
1313import { type SourceType, type SourceConfig } from '../common/infrastructure/config/source/sources.ts';
1414import { type PollingOptions } from "../common/infrastructure/config/common.ts";
1515import {
···88 type CalculatedPlayerStatus,
99 type PlayerStateData,
1010 type PlayerStateDataMaybePlay,
1111- type PlayPlatformId,
1211 REPORTED_PLAYER_STATUSES,
1312 type ReportedPlayerStatus,
1413} from "../../common/infrastructure/Atomic.ts";
1414+import { type PlayPlatformId } from '../../../core/Atomic.ts';
1515import { type PollingOptions } from "../../common/infrastructure/config/common.ts";
1616import { playObjDataMatch, progressBar } from "../../utils.ts";
1717import { genGroupIdStr } from '../../../core/PlayUtils.ts';
···11import { type Logger } from "@foxxmd/logging";
22-import { CALCULATED_PLAYER_STATUSES, type PlayPlatformId } from "../../common/infrastructure/Atomic.ts";
22+import { CALCULATED_PLAYER_STATUSES } from "../../common/infrastructure/Atomic.ts";
33+import { type PlayPlatformId } from '../../../core/Atomic.ts';
34import { AbstractPlayerState, type PlayerStateOptions } from "./AbstractPlayerState.ts";
45import { type PlayProgress } from "../../../core/Atomic.ts";
56import { type ListenProgress, ListenProgressTS } from "./ListenProgress.ts";
···11import { type Logger } from "@foxxmd/logging";
22import { type PlayObject } from "../../../core/Atomic.ts";
33-import { type PlayerStateDataMaybePlay, type PlayPlatformId, type ReportedPlayerStatus } from "../../common/infrastructure/Atomic.ts";
33+import { type PlayerStateDataMaybePlay, type ReportedPlayerStatus } from "../../common/infrastructure/Atomic.ts";
44+import { type PlayPlatformId } from '../../../core/Atomic.ts';
45import { type PlayerStateOptions } from "./AbstractPlayerState.ts";
56import { GenericPlayerState } from "./GenericPlayerState.ts";
67import { PositionalPlayerState } from "./PositionalPlayerState.ts";
···11import { type Logger } from "@foxxmd/logging";
22-import { type PlayPlatformId, REPORTED_PLAYER_STATUSES } from "../../common/infrastructure/Atomic.ts";
22+import { REPORTED_PLAYER_STATUSES } from "../../common/infrastructure/Atomic.ts";
33+import { type PlayPlatformId } from '../../../core/Atomic.ts';
34import { AbstractPlayerState, type PlayerStateOptions } from "./AbstractPlayerState.ts";
45import { GenericPlayerState } from "./GenericPlayerState.ts";
56import { PositionalPlayerState } from "./PositionalPlayerState.ts";
···11import { type Logger } from "@foxxmd/logging";
22-import { CALCULATED_PLAYER_STATUSES, type PlayPlatformId, REPORTED_PLAYER_STATUSES } from "../../common/infrastructure/Atomic.ts";
22+import { CALCULATED_PLAYER_STATUSES, REPORTED_PLAYER_STATUSES } from "../../common/infrastructure/Atomic.ts";
33+import { type PlayPlatformId } from '../../../core/Atomic.ts';
34import { AbstractPlayerState, type PlayerStateOptions } from "./AbstractPlayerState.ts";
45import { type PlayProgressPositional, type Second } from "../../../core/Atomic.ts";
56import { type Dayjs } from "dayjs";
···11import { type Logger } from "@foxxmd/logging";
22-import { type PlayPlatformId, REPORTED_PLAYER_STATUSES } from "../../common/infrastructure/Atomic.ts";
22+import { REPORTED_PLAYER_STATUSES } from "../../common/infrastructure/Atomic.ts";
33+import { type PlayPlatformId } from '../../../core/Atomic.ts';
34import { AbstractPlayerState, type PlayerStateOptions } from "./AbstractPlayerState.ts";
45import { GenericPlayerState } from "./GenericPlayerState.ts";
56import { GenericRealtimePlayer, RealtimePlayer } from "./RealtimePlayer.ts";
+2-1
src/backend/sources/PlexApiSource.ts
···99 NO_USER,
1010 type PlayerStateData,
1111 type PlayerStateDataMaybePlay,
1212- type PlayPlatformId, REPORTED_PLAYER_STATUSES
1212+ REPORTED_PLAYER_STATUSES
1313} from "../common/infrastructure/Atomic.ts";
1414+import { type PlayPlatformId } from '../../core/Atomic.ts';
1415import { getFirstNonEmptyString, getPlatformIdFromData, isDebugMode, parseBool, } from "../utils.ts";
1516import { genGroupIdStr } from '../../core/PlayUtils.ts';
1617import { buildStatePlayerPlayIdententifyingInfo, hashObject, parseArrayFromMaybeString } from "../utils/StringUtils.ts";
+2-1
src/backend/sources/RockskySource.ts
···11import EventEmitter from "events";
22import { type PlayObject, SOURCE_SOT } from "../../core/Atomic.ts";
33import { isNodeNetworkException } from "../common/errors/NodeErrors.ts";
44-import { type FormatPlayObjectOptions, type InternalConfig, type PlayPlatformId } from "../common/infrastructure/Atomic.ts";
44+import { type FormatPlayObjectOptions, type InternalConfig } from "../common/infrastructure/Atomic.ts";
55+import { type PlayPlatformId } from '../../core/Atomic.ts';
56import { type RecentlyPlayedOptions } from "./AbstractSource.ts";
67import MemorySource from "./MemorySource.ts";
78import { RockSkyApiClient } from "../common/vendor/RockSkyApiClient.ts";
+2-1
src/backend/sources/ScrobbleSources.ts
···3636import { type YandexMusicBridgeData, type YandexMusicBridgeSourceConfig } from "../common/infrastructure/config/source/ymbridge.ts";
3737import { type SonosData, type SonosSourceConfig } from "../common/infrastructure/config/source/sonos.ts";
3838import { WildcardEmitter } from "../common/WildcardEmitter.ts";
3939-import { nonEmptyObj, parseBool, removeUndefinedKeys } from "../utils.ts";
3939+import { nonEmptyObj, parseBool } from "../utils.ts";
4040+import { removeUndefinedKeys } from '../../core/DataUtils.ts';
4041import { getCommonComponentEnvConfig, readJson } from '../utils/DataUtils.ts';
4142import { validateJson } from "../utils/ValidationUtils.ts";
4243import AbstractSource from "./AbstractSource.ts";
+2-1
src/backend/sources/SubsonicSource.ts
···66import { type PlayObject, type PlayObjectMinimal } from "../../core/Atomic.ts";
77import { isNodeNetworkException } from "../common/errors/NodeErrors.ts";
88import { UpstreamError } from "../common/errors/UpstreamError.ts";
99-import { DEFAULT_RETRY_MULTIPLIER, type FormatPlayObjectOptions, type InternalConfig, type PlayPlatformId } from "../common/infrastructure/Atomic.ts";
99+import { DEFAULT_RETRY_MULTIPLIER, type FormatPlayObjectOptions, type InternalConfig } from "../common/infrastructure/Atomic.ts";
1010+import { type PlayPlatformId } from '../../core/Atomic.ts';
1011import { type SubSonicSourceConfig } from "../common/infrastructure/config/source/subsonic.ts";
1112import { getSubsonicResponse, type SubsonicResponse, type SubsonicResponseCommon } from "../common/vendor/subsonic/interfaces.ts";
1213import { removeDuplicates } from "../utils.ts";
+1-1
src/backend/sources/WebScrobblerSource.ts
···66 type InternalConfig,
77 NO_USER,
88 type PlayerStateData,
99- type PlayPlatformId,
109 REPORTED_PLAYER_STATUSES,
1110 type ReportedPlayerStatus,
1211} from "../common/infrastructure/Atomic.ts";
1212+import { type PlayPlatformId } from '../../core/Atomic.ts';
1313import { type WebScrobblerSourceConfig } from "../common/infrastructure/config/source/webscrobbler.ts";
1414import {
1515 type WebScrobblerHookEvent,
+2-1
src/backend/tests/scrobbler/scrobblers.test.ts
···1616import MockDate from 'mockdate';
17171818import { NowPlayingScrobbler, TestAuthScrobbler, TestScrobbler } from "./TestScrobbler.ts";
1919-import { type PaginatedTimeRangeOptions, type PlayPlatformId, REFRESH_STALE_DEFAULT } from '../../common/infrastructure/Atomic.ts';
1919+import { type PaginatedTimeRangeOptions, REFRESH_STALE_DEFAULT } from '../../common/infrastructure/Atomic.ts';
2020+import { type PlayPlatformId } from '../../../core/Atomic.ts';
2021import { shuffleArray } from '../../utils/DataUtils.ts';
2122import { DEFAULT_CONSOLIDATE_DURATION, DEFAULT_GROUP_DURATION, groupPlaysToTimeRanges } from '../../utils/ListenFetchUtils.ts';
2223import { asPlay } from '../../../core/PlayMarshalUtils.ts';
···33import { MaybeLogger } from '../common/MaybeLogger.ts';
44import { deepEqual } from 'fast-equals';
55import { type CommonConfigPrimitives } from "../common/infrastructure/config/common.ts";
66-import { parseBoolStrict, removeUndefinedKeys } from "../utils.ts";
66+import { parseBoolStrict } from "../utils.ts";
77+import { removeUndefinedKeys } from '../../core/DataUtils.ts';
78import { nonEmptyStringOrDefault } from "../../core/StringUtils.ts";
89910export const asArray = <T>(data: T | T[]): T[] => {
+3-1
src/backend/utils/StringUtils.ts
···11import { strategies, stringSameness, type StringSamenessResult } from "@foxxmd/string-sameness";
22import { hasher } from 'node-object-hash';
33import { type PlayObject } from "../../core/Atomic.ts";
44-import { asPlayerStateData, DELIMITERS, DELIMITERS_NO_AMP, type PlayerStateDataMaybePlay } from "../common/infrastructure/Atomic.ts";
44+import { asPlayerStateData, type PlayerStateDataMaybePlay } from "../common/infrastructure/Atomic.ts";
55+import { DELIMITERS_NO_AMP } from '../../core/Atomic.ts';
66+import { DELIMITERS } from '../../core/Atomic.ts';
57import { getPlatformIdFromData, intersect, parseBool, parseBoolStrict } from "../utils.ts";
68import { genGroupIdStr } from '../../core/PlayUtils.ts';
79import { buildTrackString } from "../../core/StringUtils.ts";
-2
src/backend/utils/TimeUtils.ts
···337337 });
338338};
339339340340-export type Milliseconds = number;
341341-342340/** Is Position earlier than X seconds or Y% percent of the start of a Play? */
343341export const closeToPlayStart = (play: PlayObject, position: number, thresholds: {absolute?: number, percent?: number, hintPrefix?: boolean} = {}): [boolean, string] => {
344342 const {
+10-12
src/client/AppNext.tsx
···11-import React, {useState, useRef, Fragment} from 'react';
11+import { Box, Container } from '@chakra-ui/react';
22+import { SSEProvider } from "@flamefrontend/sse-runtime-react";
23import {
34 createBrowserRouter,
44- createHashRouter, type RouteObject,
55- RouterProvider, useLocation, Outlet
55+ createHashRouter,
66+ Outlet,
77+ type RouteObject,
88+ RouterProvider, useLocation
69} from "react-router-dom";
77-import {connect, type ConnectedProps, Provider as ReduxProvider} from 'react-redux'
1010+import { type MsSseEvent } from '../core/Api';
811import './App.css';
1212+import { AppHeader } from './components/AppHeader';
913import CopyToClipboard from "./components/CopyToClipboard";
1010-import {store} from './store';
1414+import { MSErrorBoundary } from './components/ErrorBoundary';
1515+import { ComponentDetailedRoutable } from './components/msComponent/MSComponentDetailed';
1116import { MSComponentListFetchable } from './components/msComponent/MSComponentList';
1217import { Provider } from './components/Provider';
1313-import { Container, Box, Center, Splitter, useSplitter } from '@chakra-ui/react';
1414-import { MsSseEvent } from '../core/Api';
1515-import { SSEProvider } from "@flamefrontend/sse-runtime-react";
1616-import { AppHeader } from './components/AppHeader';
1717-import { NAV_LINKS, SideNavItems } from './components/SideNav';
1818-import { ComponentDetailedRoutable } from './components/msComponent/MSComponentDetailed';
1919-import { MSErrorBoundary } from './components/ErrorBoundary';
20182119function NoMatch() {
2220 const location = useLocation();
+15-17
src/client/components/ActivityDetail.tsx
···11-import React, { useState, Fragment, useEffect } from "react"
22-import { Accordion, Span, Code, Alert, Stack, Box, Separator, HStack, Flex, useAccordionItemContext, Skeleton, SkeletonText, Collapsible, BadgeProps } from '@chakra-ui/react';
33-import { CLIENT_DEAD_QUEUE, ComponentType, Second } from "../../core/Atomic";
44-import { PlayData } from "./PlayData";
55-import { ErrorAlert } from "./ErrorAlert";
11+import { Accordion, Alert, Box, Code, Collapsible, Flex, HStack, Separator, Skeleton, SkeletonText, Span, Stack, useAccordionItemContext, type BadgeProps } from '@chakra-ui/react';
22+import { useSSEContext, useSSEEvent } from "@flamefrontend/sse-runtime-react";
33+import { useQuery, useQueryClient, type InfiniteData } from '@tanstack/react-query';
44+import React, { Fragment, useEffect, useState } from "react";
55+import { LuChevronRight } from "react-icons/lu";
66+import { type MarkOptional } from "ts-essentials";
77+import { type MsSseEvent, type PaginatedResponse, type PlayApiCommonDetailed, type QueryPlaysOptsJson, type SortPlaysByProps } from "../../core/Api";
88+import { CLIENT_DEAD_QUEUE, type ComponentType, type Second } from "../../core/Atomic";
99+import { tanQueries } from "../queries";
1010+import { activityTimelineHasIssue } from "../utils/ComponentUtils";
611import { ActivityTimeline } from "./ActivityTimeline";
77-import { ExpandCollapse } from "./ExpandCollapse";
88-import { MsSseEvent, PlayApiCommonDetailed, SortPlaysByProps } from "../../core/Api";
99-import { type InfiniteData, useQuery, useQueryClient } from '@tanstack/react-query';
1010-import { ShortDateDisplay } from "./DateDisplay";
1111-import { TextMuted } from "./TextMuted";
1212import { EphemeralElement, PlayStateBadge } from "./Badges";
1313-import { MarkOptional } from "ts-essentials";
1414-import { QueryPlaysOptsJson } from "../../backend/common/database/drizzle/repositories/PlayRepository";
1515-import { tanQueries } from "../queries";
1616-import { PaginatedResponse } from "../../backend/common/database/drizzle/repositories/BaseRepository";
1717-import { LuChevronRight } from "react-icons/lu";
1818-import { useSSEContext, useSSEEvent } from "@flamefrontend/sse-runtime-react";
1313+import { ShortDateDisplay } from "./DateDisplay";
1414+import { ErrorAlert } from "./ErrorAlert";
1515+import { ExpandCollapse } from "./ExpandCollapse";
1916import { DebugCopy, ExclamationCircleIcon, ExclamationTriangleIcon, InsertedIcon, RetryButton, UpdatedIcon } from "./icons/ChakraIcons";
2020-import { activityTimelineHasIssue } from "../utils/ComponentUtils";
1717+import { PlayData } from "./PlayData";
1818+import { TextMuted } from "./TextMuted";
21192220type UseActivityQueryOptions = {
2321 msQuery?: QueryPlaysOptsJson
+16-16
src/client/components/ActivityTimeline.tsx
···11-import React, { Fragment } from "react"
22-import { Timeline, Icon, Span, Card, Tabs, SkeletonCircle, Collapsible, SkeletonText, HTMLChakraProps } from '@chakra-ui/react';
33-import { CLIENT_DEAD_QUEUE, CLIENT_INGRESS_QUEUE, ComponentType, JsonPlayObject, LifecycleStep, PlayMatchResult, QUEUE_STATUS_COMPLETED, QUEUE_STATUS_FAILED, QUEUE_STATUS_QUEUED, ScrobbleResult } from "../../core/Atomic";
44-import { PlayData } from "./PlayData";
55-import { ErrorAlert } from "./ErrorAlert";
66-import { IoMdCodeDownload } from "react-icons/io";
11+import { Card, Collapsible, Icon, SkeletonCircle, SkeletonText, Span, Tabs, Timeline, type HTMLChakraProps } from '@chakra-ui/react';
22+import dayjs, { Dayjs } from "dayjs";
33+import React from "react";
74import { BiWrench } from "react-icons/bi";
85import { HiMiniMagnifyingGlass } from "react-icons/hi2";
66+import { IoMdCodeDownload } from "react-icons/io";
97import { TbDatabaseEdit } from "react-icons/tb";
1010-import { capitalize, capitalizeWords } from "../../core/StringUtils";
88+import { type PlayApiCommonDetailed, type QueueStateApi } from "../../core/Api";
99+import { CLIENT_DEAD_QUEUE, CLIENT_INGRESS_QUEUE, QUEUE_STATUS_COMPLETED, QUEUE_STATUS_FAILED, QUEUE_STATUS_QUEUED, type ComponentType, type JsonPlayObject, type LifecycleStep, type PlayMatchResult, type ScrobbleResult } from "../../core/Atomic";
1010+import { sortByNewestDate } from "../../core/PlayUtils";
1111+import { capitalizeWords } from "../../core/StringUtils";
1112import { shortTodayAwareFormat } from "../../core/TimeUtils";
1212-import dayjs, { Dayjs } from "dayjs";
1313+import { activityTransformHasIssue, timelineTextFormatting } from "../utils/ComponentUtils";
1314import { ChakraCodeBlockShort } from "./CodeBlock";
1414-import { diffElements, TransformSteps } from "./TransformSteps";
1515-import { ScrobbleMatchResult } from "./ScrobbleMatchResult";
1616-import { ScrobbleActionResult } from "./ScrobbleActionResult";
1515+import { ErrorAlert } from "./ErrorAlert";
1616+import { MSErrorBoundary } from "./ErrorBoundary";
1717+import { CheckIcon, TimelineIndicatorIconQueued } from "./icons/ChakraIcons";
1718import { MSCollapsible } from "./MSCollapsible";
1919+import { PlayData } from "./PlayData";
2020+import { ScrobbleActionResult } from "./ScrobbleActionResult";
2121+import { ScrobbleMatchResult } from "./ScrobbleMatchResult";
1822import { TimelineErrorIcon } from "./timeline/TimelineIcon";
2323+import { diffElements, TransformSteps } from "./TransformSteps";
1924import { Muted } from "./Typography";
2020-import { PlayApiCommonDetailed, QueueStateApi } from "../../core/Api";
2121-import { MSErrorBoundary } from "./ErrorBoundary";
2222-import { activityTransformHasIssue, timelineTextFormatting } from "../utils/ComponentUtils";
2323-import { CheckIcon, TimelineIndicatorIconQueued } from "./icons/ChakraIcons";
2424-import { sortByNewestDate } from "../../core/PlayUtils";
252526262727export interface ActivityDetailProps {
+12-12
src/client/components/AppHeader.tsx
···11-import React, { type ComponentProps, useMemo, forwardRef, Fragment, useEffect, type PropsWithChildren, useCallback, useRef, useState } from "react"
22-import { Image, Heading, HStack, Link, LinkOverlay, LinkBox, Span, Flex, Box, Separator, Switch, FloatingPanel, Portal, Text, IconButton, Status, Stack } from '@chakra-ui/react';
11+import { Box, Flex, FloatingPanel, Heading, HStack, IconButton, Image, LinkBox, LinkOverlay, Portal, Stack, Status, Switch, Text } from '@chakra-ui/react';
22+import {
33+ useWindowSize,
44+} from '@react-hook/window-size';
55+import React, { type ComponentProps, type PropsWithChildren } from "react";
66+import { LuGripHorizontal, LuMinus } from "react-icons/lu";
77+import { Link as RouterLink } from 'react-router';
38import { VersionNext } from "../Version";
99+import { MobileSidebarNav } from "./MobileMenu";
410import { TextMuted } from "./TextMuted";
511import { TerminalButton, TerminalIcon, XButton } from "./icons/ChakraIcons";
66-import { MobileSidebarNav } from "./MobileMenu";
77-import { LuGripHorizontal, LuMinus } from "react-icons/lu"
88-import {Link as RouterLink} from 'react-router';
99-import {
1010- useWindowSize,
1111-} from '@react-hook/window-size'
12121313-import { LogsFetchable } from "./LogsNext";
1414-import { Ripple } from "./icons/AnimatedIcons";
1513import { useSSEContext, useSSEStatus } from "@flamefrontend/sse-runtime-react";
1616-import { ToggleTip } from "./ToggleTip";
1717-import { ErrorLike } from "../../core/Atomic";
1414+import { type ErrorLike } from "../../core/Atomic";
1815import { ErrorAlert } from "./ErrorAlert";
1916import { MSErrorBoundary } from "./ErrorBoundary";
2017import { ExternaLinksMenu } from "./ExternaLinksMenu";
1818+import { LogsFetchable } from "./LogsNext";
1919+import { ToggleTip } from "./ToggleTip";
2020+import { Ripple } from "./icons/AnimatedIcons";
21212222export const AppTitle = (props: { fetchable?: boolean } = {}) => {
2323 const {
+3-3
src/client/components/ArtistCreditDisplay.tsx
···11-import React, { Fragment } from 'react';
22-import { ArtistCredit as AC } from '../../core/Atomic';
11+import { Fragment } from 'react';
22+import { type ArtistCredit as AC } from '../../core/Atomic';
3344-import { HStack, Tag } from "@chakra-ui/react"
44+import { HStack, Tag } from "@chakra-ui/react";
55import { SiMusicbrainz } from "react-icons/si";
66import { Tooltip } from './ChakraTooltip';
77
+6-6
src/client/components/Badges.tsx
···11-import { Badge, Separator, HStack } from "@chakra-ui/react";
22-import React, { type ComponentProps, useState, useCallback, useEffect, type PropsWithChildren } from "react";
33-import { COMPONENT_STATE, ComponentCommonApiJson, componentStateToFriendly, MsSseEvent, PlayApiCommon } from "../../core/Api";
44-import { capitalizeWords } from "../../core/StringUtils";
55-import {useSSEContext, useSSEEvent} from "@flamefrontend/sse-runtime-react";
66-import { Second } from "../../core/Atomic";
11+import { Badge, HStack, Separator } from "@chakra-ui/react";
22+import { useSSEContext, useSSEEvent } from "@flamefrontend/sse-runtime-react";
33+import React, { type ComponentProps, type PropsWithChildren, useCallback, useEffect, useState } from "react";
74import { useTimeout } from 'react-use-timeout';
55+import { COMPONENT_STATE, type ComponentCommonApiJson, componentStateToFriendly, type MsSseEvent, type PlayApiCommon } from "../../core/Api";
66+import { type Second } from "../../core/Atomic";
77+import { capitalizeWords } from "../../core/StringUtils";
8899export const PlayStateBadge = (props: PropsWithChildren<ComponentProps<typeof Badge>> & { state: PlayApiCommon['state'], suffix?: React.JSX.Element, hasDeadQueue?: boolean }) => {
1010
+3-3
src/client/components/ChakraClipboard.tsx
···11-import { type ComponentProps, useMemo, useCallback, useEffect, useState } from 'react';
22-import { Clipboard, IconButton, useClipboard } from "@chakra-ui/react"
33-import {safeStringify} from '../../core/StringUtils';
11+import { Clipboard, IconButton, useClipboard } from "@chakra-ui/react";
22+import { type ComponentProps, useCallback, useEffect, useMemo, useState } from 'react';
33+import { safeStringify } from '../../core/StringUtils';
44import { CheckIcon, CopyIcon } from './icons/ChakraIcons';
5566
+3-5
src/client/components/CodeBlock.tsx
···11-import type { HighlighterGeneric } from "shiki"
22-import { createShikiAdapter, CodeBlock, IconButton, ClientOnly, ScrollArea } from "@chakra-ui/react"
33-import { useColorMode } from "./Color-Mode";
44-import { type ComponentProps, type PropsWithChildren, useMemo } from "react";
11+import { ClientOnly, CodeBlock, IconButton } from "@chakra-ui/react";
22+import { type ComponentProps, useMemo } from "react";
33+import { type MarkOptional } from "ts-essentials";
54import { safeStringify } from "../../core/StringUtils";
66-import { MarkOptional } from "ts-essentials";
75import { shikiAdapter } from "./shikiAdapter";
8697// const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
+4-5
src/client/components/ErrorAlert.tsx
···11-import { Alert, Accordion, Stack, Text, Box, Collapsible, HStack, Highlight, Span, Code, useCollapsible, UseCollapsibleReturn } from '@chakra-ui/react';
22-import { Fragment, type ComponentProps, useEffect, useState } from 'react';
11+import { Alert, Box, Code, Collapsible, HStack, Span, Stack, Text, useCollapsible, type UseCollapsibleReturn } from '@chakra-ui/react';
22+import { Fragment, useEffect, useState, type ComponentProps } from 'react';
33+import { isErrorIsh, type ErrorIsh } from '../../core/ErrorUtils';
44+import { ChakraClip } from './ChakraClipboard';
35import { ChakraCodeBlock } from './CodeBlock';
44-import { ChakraClip } from './ChakraClipboard';
55-import { ErrorIsh, isErrorIsh } from '../../core/ErrorUtils';
66import { EllipsisButton, FatArrowRight } from './icons/ChakraIcons';
77-import { type ErrorObject } from 'serialize-error';
8798export interface ErrorAlertProps {
109 error: ErrorIsh
+3-3
src/client/components/ExpandCollapse.tsx
···11-import { IconButton, HStack } from "@chakra-ui/react"
22-import { type ComponentProps, type PropsWithChildren } from "react";
33-import { FaRegSquarePlus, FaRegSquareMinus } from "react-icons/fa6";
11+import { HStack, IconButton } from "@chakra-ui/react";
22+import { type ComponentProps } from "react";
33+import { FaRegSquareMinus, FaRegSquarePlus } from "react-icons/fa6";
4455export interface ExpandCollapseProps {
66 onClick: (val: boolean) => void,
+2-2
src/client/components/ExternaLinksMenu.tsx
···11-import { Button, Menu, Portal, Box, ChakraComponent } from "@chakra-ui/react"
11+import { Box, Button, Menu, Portal } from "@chakra-ui/react"
22+import { type ComponentProps } from "react"
23import { ExternalLinkIcon } from "./icons/ChakraIcons"
34import { EXTERNAL_LINKS } from "./SideNav"
44-import { type ComponentProps } from "react"
556677export const ExternaLinksMenu = (props: ComponentProps<typeof Box> = {}) => (
+8-9
src/client/components/LogsNext.tsx
···11-import React, {type PropsWithChildren, useState, useEffect, useRef, useCallback} from 'react';
11+import { Box, HStack, SegmentGroup, Separator, Span, Stack, Text } from '@chakra-ui/react';
22+import { useSSE } from "@flamefrontend/sse-runtime-react";
33+import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
24import * as AnsiImport from "ansi-to-react";
33-import { Text, Box, SegmentGroup, Separator, HStack, Stack, Span } from '@chakra-ui/react';
44-import {FixedSizeList} from "fixed-size-list";
55-import {useSSE} from "@flamefrontend/sse-runtime-react";
66-import { useQueryClient, type QueryFunctionContext, useQuery, useMutation } from '@tanstack/react-query'
77-import { LogOutputConfig } from '../../core/Atomic';
55+import { FixedSizeList } from "fixed-size-list";
86import ky from 'ky';
99-import { type LogLevel } from '@foxxmd/logging';
77+import React, { useCallback, useEffect, useRef, useState } from 'react';
88+import { type LogLevelStandalone, type LogOutputConfig } from '../../core/Atomic';
109import { tanQueries } from '../queries';
1111-import { ChakraClip, ChakraClipDynamic } from './ChakraClipboard';
1010+import { ChakraClipDynamic } from './ChakraClipboard';
12111312// @ts-expect-error Ansi export is built incorrectly
1413const Ansi = AnsiImport.default.default as typeof AnsiImport.default;
···9392 });
94939594 const levelGroup = <SegmentGroup.Root value={logLevel} size="xs" onValueChange={(val) => {
9696- mutateLogSettings.mutate({level: val.value as LogLevel});
9595+ mutateLogSettings.mutate({level: val.value as LogLevelStandalone});
9796 }}>
9897 <SegmentGroup.Indicator />
9998 <SegmentGroup.Items items={[
+3-3
src/client/components/MSCollapsible.tsx
···11-import { Collapsible, Stack, Box, useBreakpointValue } from "@chakra-ui/react"
22-import { type ComponentProps, type PropsWithChildren, useState, useEffect, useMemo } from "react";
33-import { LuChevronRight } from "react-icons/lu"
11+import { Box, Collapsible, useBreakpointValue } from "@chakra-ui/react";
22+import { type ComponentProps, type PropsWithChildren, useEffect, useMemo, useState } from "react";
33+import { LuChevronRight } from "react-icons/lu";
44import { MSErrorBoundary } from "./ErrorBoundary";
5566//padding="0" borderWidth="0px"
+5-5
src/client/components/MobileMenu.tsx
···11import {
22+ Drawer,
23 IconButton,
34 Portal,
44- Drawer,
55- BreakpointName,
66-} from "@chakra-ui/react"
77-import { useState, useEffect, type ComponentProps } from 'react';
55+ type BreakpointName,
66+} from "@chakra-ui/react";
77+import { useEffect, useState, type ComponentProps } from 'react';
88import { useLocation } from 'react-router';
99-import { MenuButton, MenuIcon, XIcon } from "./icons/ChakraIcons";
99+import { MenuButton, XIcon } from "./icons/ChakraIcons";
1010import { NAV_LINKS, SideNavItems } from "./SideNav";
11111212const MobileMenuButton = MenuButton;
+4-4
src/client/components/PlayDisplay.tsx
···11-import React, {Fragment, type PropsWithChildren, type ReactElement} from 'react';
11+import { Fragment, type ReactElement } from 'react';
2233-import {JsonPlayObject, TrackStringOptions} from "../../core/Atomic";
44-import {buildTrackString} from "../../core/StringUtils";
55-import {buildTrackStringReactOptions} from "../utils/index";
33+import { type JsonPlayObject, type TrackStringOptions } from "../../core/Atomic";
44+import { buildTrackString } from "../../core/StringUtils";
55+import { buildTrackStringReactOptions } from "../utils/index";
6677interface PlayObjectProps {
88 data: JsonPlayObject
+6-11
src/client/components/Provider.tsx
···11"use client"
2233-import { ChakraProvider, defaultSystem } from "@chakra-ui/react"
33+import { ChakraProvider, createSystem, defaultConfig, defineConfig } from "@chakra-ui/react"
44+import {
55+ QueryClient,
66+ QueryClientProvider
77+} from '@tanstack/react-query'
88+import '../index-next.css'
49import {
510 ColorModeProvider,
611 type ColorModeProviderProps,
712} from "./Color-Mode"
88-import {
99- useQuery,
1010- useMutation,
1111- useQueryClient,
1212- QueryClient,
1313- QueryClientProvider,
1414-} from '@tanstack/react-query'
1515-import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
1616-import { createSystem, defaultConfig, defineConfig } from "@chakra-ui/react"
1717-import '../index-next.css';
18131914const queryClient = new QueryClient()
2015
+2-2
src/client/components/ScrobbleActionResult.tsx
···44import { HiOutlineCloudUpload, HiOutlineCloudDownload } from "react-icons/hi";
55import { ChakraCodeBlockShort } from "./CodeBlock";
66import { capitalize } from "../../core/StringUtils";
77-import { MSCollapsible, MSCollapsibleExternalProps } from "./MSCollapsible";
77+import { MSCollapsible, type MSCollapsibleExternalProps } from "./MSCollapsible";
88import { TimelineErrorIcon } from "./timeline/TimelineIcon";
99-import { ScrobbleResult } from "../../core/Atomic";
99+import { type ScrobbleResult } from "../../core/Atomic";
10101111export interface ScrobbleActionResultProps extends MSCollapsibleExternalProps {
1212 result: ScrobbleResult<string>,
+1-1
src/client/components/ScrobbleMatchResult.tsx
···11import { Icon, Tabs, DataList, List } from '@chakra-ui/react';
22-import { PlayMatchResult } from "../../core/Atomic";
22+import { type PlayMatchResult } from "../../core/Atomic";
33import { PlayData } from "./PlayData";
44import { LuCheck, LuX } from "react-icons/lu";
55import { formatNumber } from "../../core/DataUtils";
+3-3
src/client/components/SideNav.tsx
···22 Stack,
33 HStack,
44 Link,
55- LinkProps,
66- StackProps,
77- BadgeProps,
55+ type LinkProps,
66+ type StackProps,
77+ type BadgeProps,
88 Badge
99} from "@chakra-ui/react"
1010import { Link as RouterLink } from "react-router";
+2-2
src/client/components/ToggleButton.tsx
···11-import React, { type ComponentProps, Fragment, useMemo, useState, useCallback } from "react"
22-import { Accordion, Span, Stack, Text, Box, HStack, Flex, Container, SkeletonText, Collapsible, ScrollArea, Button } from '@chakra-ui/react';
11+import { Button } from '@chakra-ui/react';
22+import { type ComponentProps, useState } from "react";
3344interface ToggleButtonProps {
55 value?: boolean
+9-11
src/client/components/TransformSteps.tsx
···11-import React, { type ComponentProps, Fragment, useMemo } from "react"
22-import { Timeline, Icon, Span, Stack, Heading, Box, Text } from '@chakra-ui/react';
33-import { JsonPlayObject, LifecycleStep } from "../../core/Atomic";
44-import { PlayData } from "./PlayData";
55-import { ErrorAlert } from "./ErrorAlert";
66-import { BsSkipForward } from "react-icons/bs";
77-import { BsStoplights } from "react-icons/bs";
88-import { BsExclamationTriangle, BsExclamationOctagon } from "react-icons/bs";
11+import { Heading, Icon, Span, Stack, Text, Timeline } from '@chakra-ui/react';
22+import React, { Fragment, useMemo } from "react";
33+import { BsExclamationTriangle, BsSkipForward, BsStoplights } from "react-icons/bs";
94import { MdMusicNote } from "react-icons/md";
1010-import { ChakraCodeBlockShort, ChakraPlainBlockShort } from "./CodeBlock";
55+import { type JsonPlayObject, type LifecycleStep } from "../../core/Atomic";
116import { patchObject } from "../../core/DataUtils";
1212-import { MSCollapsible, MSCollapsibleExternalProps } from "./MSCollapsible";
77+import { timelineTextFormatting } from "../utils/ComponentUtils";
88+import { ChakraCodeBlockShort } from "./CodeBlock";
99+import { ErrorAlert } from "./ErrorAlert";
1010+import { MSCollapsible, type MSCollapsibleExternalProps } from "./MSCollapsible";
1111+import { PlayData } from "./PlayData";
1312import { Muted } from "./Typography";
1413import { JsonDiffPatch } from "./diffs/JsonDiff";
1515-import { timelineTextFormatting } from "../utils/ComponentUtils";
16141715export interface LifeycleStepsTimelineProps extends MSCollapsibleExternalProps {
1816 steps: LifecycleStep[]
+6-8
src/client/components/chakraPlayer/Player.tsx
···11-import React, { type ComponentProps, useMemo, forwardRef, Fragment, useState, useEffect, useCallback } from "react"
22-import { Accordion, Highlight, Em, Progress, For, Span, Stack, Spacer, Bleed, Text, Image, Box, Heading, AbsoluteCenter, Button, Separator, HStack, Flex, Center, Badge, IconButton, Container, Collapsible, Card, LinkOverlay, LinkBox } from '@chakra-ui/react';
11+import React, { type ComponentProps, useMemo, useState, useEffect } from "react"
22+import { Em, Span, Stack, Spacer, Text, Image, Box, Heading, HStack, Flex, Center, Container } from '@chakra-ui/react';
33import { TextMuted } from "../TextMuted";
44-import { SOURCE_SOT, SOURCE_SOT_TYPES, SourcePlayerJson } from "../../../core/Atomic";
44+import { SOURCE_SOT, type SOURCE_SOT_TYPES, type SourcePlayerJson } from "../../../core/Atomic";
55import { timeToHumanTimestamp } from "../../../core/TimeUtils";
66import { capitalize } from "../../../core/StringUtils";
77-import { type QueryFunctionContext, queryOptions, useQuery, useQueryClient } from '@tanstack/react-query';
77+import { useQuery, useQueryClient } from '@tanstack/react-query';
88import { ErrorAlert } from "../ErrorAlert";
99-import ky from 'ky';
1010-import { baseUrl } from "../../utils";
119import {
1210 useSSEAnyEvent,
1311 useSSEContext,
1412 useSSEEvent,
1513} from "@flamefrontend/sse-runtime-react";
1616-import { ComponentCommonApiJson, ComponentSourceApiJson, isComponentClientApiJson, isComponentSourceApiJson, MsSseEvent, MsSseEventPayload } from "../../../core/Api";
1414+import { type ComponentCommonApiJson, type MsSseEvent, type MsSseEventPayload } from "../../../core/Api";
1715import LinearProgress from '@mui/material/LinearProgress';
1818-import { InfoTip, ToggleTip } from "../ToggleTip";
1616+import { InfoTip } from "../ToggleTip";
1917import { tanQueries } from "../../queries";
2018import dayjs from "dayjs";
2119import { MSErrorBoundary } from "../ErrorBoundary";
+1-2
src/client/components/diffs/JsonDiff.tsx
···11-import React, { type ComponentProps } from 'react';
21import {
32 parseDiffFromFile,
43 type FileContents
···87 type FileDiffProps,
98 FileDiff,
109} from '@pierre/diffs/react';
1111-import { MarkRequired } from 'ts-essentials';
1010+import { type MarkRequired } from 'ts-essentials';
1211import { type Changeset } from 'json-diff-ts';
1312import { MSErrorBoundary } from '../ErrorBoundary';
1413import { patchObject } from "../../../core/DataUtils";
+1-1
src/client/components/icons/ChakraIcons.tsx
···3232import { SiGoogledocs } from "react-icons/si";
3333import { IconButton, Clipboard, useClipboard, Spinner, Icon } from "@chakra-ui/react"
3434import { type ComponentProps, type PropsWithChildren } from 'react';
3535-import { IconBaseProps, IconType } from "react-icons/lib";
3535+import { type IconBaseProps, type IconType } from "react-icons/lib";
36363737export const makeIconButton = (Icon: IconType) => (props: PropsWithChildren<ComponentProps<typeof IconButton>> & { iconProps?: IconBaseProps, loading?: boolean }) => {
3838 const {
···11-import React, { type ComponentProps, useMemo, useState, forwardRef, Fragment } from "react"
22-import { Stack, SegmentGroup, Text, Box, Center, Card, SkeletonText } from '@chakra-ui/react';
33-import { ComponentsApiJson } from "../../../core/Api";
11+import { useState } from "react"
22+import { Stack, SegmentGroup, Center, Card, SkeletonText } from '@chakra-ui/react';
33+import { type ComponentsApiJson } from "../../../core/Api";
44import { MSComponentSummary, MSComponentSummaryFetchable } from "./MSComponentSummary";
55-import { type QueryFunctionContext, queryOptions, useQuery } from '@tanstack/react-query';
66-import ky from 'ky';
77-import { baseUrl } from "../../utils";
55+import { useQuery } from '@tanstack/react-query';
86import { ErrorAlert } from "../ErrorAlert";
97import { tanQueries } from "../../queries";
108import { MSErrorBoundary } from "../ErrorBoundary";
···11-import { Accordion, Span, Stack, Text, Box, HStack, Flex, Container, SkeletonText, Wrap, Card, Collapsible, Separator } from '@chakra-ui/react';
22-import { type ComponentType, type PlayState, } from '../../../core/Atomic.js';
33-import React, { type ComponentProps, Fragment, useMemo, useCallback, useState } from "react"
44-import dayjs, { Dayjs } from 'dayjs';
11+import { Accordion, Container, Stack } from '@chakra-ui/react';
22+import { useSSEAnyEvent, useSSEContext } from '@flamefrontend/sse-runtime-react';
33+import { type InfiniteData, useInfiniteQuery, type UseInfiniteQueryResult, useQueryClient } from '@tanstack/react-query';
44+import dayjs from 'dayjs';
55import doy from 'dayjs/plugin/dayOfYear.js';
66+import { type ComponentProps, Fragment, useMemo, useState } from "react";
77+import { type MsSseEvent, type MsSseEventPayload, type PaginatedResponse, type PlayApiCommonDetailed, type QueryPlaysOptsJson } from '../../../core/Api.js';
88+import { type ComponentType } from '../../../core/Atomic.js';
99+import { type QueryPlaysOptsJsonRefreshable, tanQueries } from '../../queries/index.js';
610import { ActivityDetailFetchable, ActivityDetails, ActivitySummary, ActivitySummaryFetchable, ActivitySummarySkeleton } from '../ActivityDetail.js';
77-import "./PlayList.scss";
88-import { type MsSseEvent, type MsSseEventPayload, type PlayApiCommon, type PlayApiCommonDetailed } from '../../../core/Api.js';
99-import { useQuery, useInfiniteQuery, type UseInfiniteQueryResult, type InfiniteData, useQueryClient } from '@tanstack/react-query';
1011import { ErrorAlert } from '../ErrorAlert.js';
1111-import { type QueryPlaysOptsJsonRefreshable, tanQueries } from '../../queries/index.js';
1212-import { VirtualizedListNormal } from './VirtualListNormal.js';
1212+import { ListFilters, todayRange } from './ListFilters.js';
1313+import { type ActivityLogProps, generateGroupPlays, GroupHeader } from './ListParts.js';
1314import { NoPlayResults, VirtualizedListDynamic } from './VirtualListDynamic.js';
1415import { VirtualizedListExp } from './VirtualListExperimental.js';
1515-import { type ActivityLogProps, generateGroupPlays, GroupHeader } from './ListParts.js';
1616-import { ListFilters, todayRange } from './ListFilters.js';
1717-import { type QueryPlaysOpts, type QueryPlaysOptsJson } from '../../../backend/common/database/drizzle/repositories/PlayRepository.js';
1818-import { type PaginatedResponse } from '../../../backend/common/database/drizzle/repositories/BaseRepository.js';
1919-import { useSSEAnyEvent, useSSEContext } from '@flamefrontend/sse-runtime-react';
1616+import { VirtualizedListNormal } from './VirtualListNormal.js';
20172118dayjs.extend(doy);
2219
···11-import { Span, Stack, Text, Box, Spinner, HStack, Wrap, Flex, Container, Select, Portal, createListCollection, useSelectContext, DatePicker, VStack, Button, Spacer, TagsInput, Card } from '@chakra-ui/react';
22-import { type ComponentType, isComponentTypeSource, PLAY_CLIENT_STATE, PLAY_SOURCE_STATE, type PlayState } from '../../../core/Atomic.js';
33-import React, { type ComponentProps, Fragment, useMemo, useCallback, useState } from "react"
44-import dayjs, { Dayjs } from 'dayjs';
55-import doy from 'dayjs/plugin/dayOfYear.js';
66-import "./PlayList.scss";
77-import { PlayStateBadge } from '../Badges.js';
11+import { Button, Card, createListCollection, DatePicker, Flex, HStack, Portal, Select, Span, TagsInput, useSelectContext, VStack, Wrap } from '@chakra-ui/react';
82import {
99- DateFormatter,
33+ type DateValue,
104 getLocalTimeZone,
1111- isSameDay,
1212- isToday,
55+ parseAbsolute,
66+ Time,
77+ toCalendarDateTime,
138 today,
1414- Time,
1515- type DateValue,
169 toZoned,
1717- parseDateTime,
1818- toCalendarDateTime,
1919- parseAbsolute,
2010 ZonedDateTime
2121-} from "@internationalized/date"
2222-import { type QueryPlaysOptsJson } from '../../../backend/common/database/drizzle/repositories/PlayRepository.js';
1111+} from "@internationalized/date";
1212+import { useQueryClient } from '@tanstack/react-query';
1313+import { type ComponentProps, useCallback, useMemo, useState } from "react";
1414+import { type CompareDateBetween, type PlayStateUI } from '../../../core/Api.js';
1515+import { type ComponentType, isComponentTypeSource, PLAY_CLIENT_STATE, PLAY_SOURCE_STATE, type PlayState } from '../../../core/Atomic.js';
1616+import { capitalizeWords } from '../../../core/StringUtils.js';
1717+import { type QueryPlaysOptsJsonRefreshable, tanQueries, useQueryWatcher } from '../../queries/index.js';
2318import { cardHeaderSeparator } from '../../utils/ComponentUtils.js';
2424-import { type CompareDateBetween } from '../../../backend/common/database/drizzle/repositories/BaseRepository.js';
1919+import { PlayStateBadge } from '../Badges.js';
2520import { CalendarButton, RefreshButton } from '../icons/ChakraIcons.js';
2626-import { type QueryPlaysOptsJsonRefreshable, tanQueries, useQueryWatcher } from '../../queries/index.js';
2727-import { useQueryClient } from '@tanstack/react-query';
2828-import { type PlayStateUI } from '../../../core/Api.js';
2929-import { capitalizeWords } from '../../../core/StringUtils.js';
30213122const noop = (_) => null;
3223
+5-6
src/client/components/playActivity/ListParts.tsx
···11-import { Accordion, Span, Stack, Text, Box, Separator, HStack, Flex, IconButton, Container, SkeletonText, Collapsible, ScrollArea } from '@chakra-ui/react';
22-import { type ComponentType } from '../../../core/Atomic.js';
33-import React, { type ComponentProps, Fragment, useMemo, useCallback } from "react"
11+import { Box, Flex, IconButton, Separator, Text } from '@chakra-ui/react';
42import dayjs, { Dayjs } from 'dayjs';
53import doy from 'dayjs/plugin/dayOfYear.js';
66-import { type PlayApiCommon, type PlayApiCommonDetailed, type SortPlaysByProps } from '../../../core/Api.js';
77-import { type QueryPlaysOpts, type QueryPlaysOptsJson } from '../../../backend/common/database/drizzle/repositories/PlayRepository.js';
44+import { type ComponentProps } from "react";
85import { VscDebugRestart } from 'react-icons/vsc';
99-import { sortByNewestDate } from '../../../core/PlayUtils.js';
66+import { type PlayApiCommon, type PlayApiCommonDetailed, type QueryPlaysOptsJson, type SortPlaysByProps } from '../../../core/Api.js';
77+import { type ComponentType } from '../../../core/Atomic.js';
108import { getAllIndexes } from '../../../core/DataUtils.js';
99+import { sortByNewestDate } from '../../../core/PlayUtils.js';
11101211dayjs.extend(doy);
1312
···11-import { Accordion, Span, Stack, Text, Box, Separator, HStack, Flex, IconButton, Container, SkeletonText, Collapsible, ScrollArea } from '@chakra-ui/react';
22-import React, { type ComponentProps, Fragment, useMemo, useCallback } from "react"
11+import { Box, ScrollArea } from '@chakra-ui/react';
22+import { type VirtualItem, useVirtualizer } from "@tanstack/react-virtual";
33+import React, { useCallback, useMemo } from "react";
34import { ActivityCollapsible } from '../ActivityDetail.js';
44-import { type VirtualItem, useVirtualizer } from "@tanstack/react-virtual"
55import { type ActivityLogProps, generateFlatItems, GroupHeader, isGroupInfo } from './ListParts.js';
6677export const VirtualizedListNormal = (props: ActivityLogProps) => {
+1-1
src/client/components/player/Player.tsx
···11import React, {useState, useCallback, Fragment} from 'react';
22import './player.scss';
33import PlayerTimestamp from "./PlayerTimestamp";
44-import {SOURCE_SOT, SOURCE_SOT_TYPES, SourcePlayerJson} from "../../../core/Atomic";
44+import {SOURCE_SOT, type SOURCE_SOT_TYPES, type SourcePlayerJson} from "../../../core/Atomic";
55import PlayerInfo from "./PlayerInfo";
66import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
77import {faBars, faTimes, faQuestion} from '@fortawesome/free-solid-svg-icons'
+1-1
src/client/components/player/PlayerInfo.tsx
···11import React, {Fragment} from 'react';
22import './trackInfo.scss';
33-import {SourcePlayerJson} from "../../../core/Atomic";
33+import {type SourcePlayerJson} from "../../../core/Atomic";
44import {isoToHuman} from "../../utils/index";
5566export interface PlayerInfoProps {
···11import React, {Fragment, useCallback} from 'react';
22-import StatusCardSkeleton, {StatusCardSkeletonData} from "./StatusCardSkeleton";
22+import StatusCardSkeleton, {type StatusCardSkeletonData} from "./StatusCardSkeleton";
33import SkeletonParagraph from "../skeleton/SkeletonParagraph";
44import {clientAdapter} from "../../status/ducks";
55-import {RootState} from "../../store";
55+import {type RootState} from "../../store";
66import {connect, type ConnectedProps} from "react-redux";
77import {Link} from "react-router-dom";
88import {useStartClientMutation} from "./clientDucks";
···11import React, {Fragment, useCallback, useMemo} from 'react';
22-import StatusCardSkeleton, {StatusCardSkeletonData} from "./StatusCardSkeleton";
22+import StatusCardSkeleton, {type StatusCardSkeletonData} from "./StatusCardSkeleton";
33import SkeletonParagraph from "../skeleton/SkeletonParagraph";
44import {Link} from "react-router-dom";
55import {sourceAdapter} from "../../status/ducks";
66-import {RootState} from "../../store";
66+import {type RootState} from "../../store";
77import {connect, type ConnectedProps} from "react-redux";
88import Player from "../player/Player";
99import {useStartSourceMutation, useListenSourceMutation} from "./sourceDucks";
···11import React, {type PropsWithChildren, type ReactElement} from 'react';
22import SkeletonTitle from "../skeleton/SkeletonTitle";
33import SkeletonParagraph from "../skeleton/SkeletonParagraph";
44-import StatusIndicator, {StatusType} from "../StatusIndicator";
44+import StatusIndicator, {type StatusType} from "../StatusIndicator";
5566export interface StatusCardSkeletonData {
77 loading?: boolean
+1-1
src/client/deadLetter/DeadPage.tsx
···1212 clearDead,
1313} from "./deadLetterDucks";
1414import dayjs from "dayjs";
1515-import {RootState} from "../store";
1515+import {type RootState} from "../store";
1616import {connect, type ConnectedProps} from "react-redux";
1717import { faBug } from "@fortawesome/free-solid-svg-icons";
1818import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+2-2
src/client/deadLetter/deadLetterDucks.ts
···11import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react";
22-import {DeadLetterScrobble, JsonPlayObject} from "../../core/Atomic";
22+import {type DeadLetterScrobble, type JsonPlayObject} from "../../core/Atomic";
33import {createAction, createEntityAdapter, createSlice} from "@reduxjs/toolkit";
44-import {ApiEventPayload, clientUpdate} from "../status/ducks";
44+import {type ApiEventPayload, clientUpdate} from "../status/ducks";
5566type DeadResponse = DeadLetterScrobble<JsonPlayObject, string>[];
77export const deadApi = createApi({
+8-8
src/client/logs/LogsSection.tsx
···11-import React, {useState, useCallback, useEffect} from 'react';
22-import './LogsSection.css';
33-import {FixedSizeList} from "fixed-size-list";
44-import {useEventSource, useEventSourceListener} from "@react-nano/use-event-source";
55-import LogLine from "./LogLine";
66-import {useGetLogsQuery, useLazySetLogSettingsQuery} from "./logsApi";
77-import {connect, type ConnectedProps} from "react-redux";
88-import {RootState} from "../store";
11+import { useEventSource, useEventSourceListener } from "@react-nano/use-event-source";
22+import { FixedSizeList } from "fixed-size-list";
33+import { useCallback, useEffect, useState } from 'react';
44+import { connect, type ConnectedProps } from "react-redux";
95import Loading from "../components/loading/Loading";
66+import { type RootState } from "../store";
77+import LogLine from "./LogLine";
88+import { useGetLogsQuery, useLazySetLogSettingsQuery } from "./logsApi";
99+import './LogsSection.css';
10101111const logBuffer: { message: string, id: string, level: number, levelLabel:string }[] = [];
1212
+9-8
src/client/logs/logDucks.ts
···11import {
22- createEntityAdapter, createReducer,
33- createSlice
44-} from '@reduxjs/toolkit'
55-import {logsApi} from "./logsApi";
66-import {LogOutputConfig} from "../../core/Atomic";
77-import {type LogDataPretty} from "@foxxmd/logging";
22+ createReducer
33+} from '@reduxjs/toolkit';
44+import { type LogOutputConfig } from "../../core/Atomic";
55+import { logsApi } from "./logsApi";
86export interface LogsState {
99- data: (LogDataPretty & {levelLabel: string})[],
77+ // TODO remove after new ui switchover
88+ // needed to remove this type to remove @foxxmd/logging from vite deps
99+ data: any[],
1010 settings: LogOutputConfig
1111}
1212const initialState: LogsState = {data: [], settings: {level: 'trace', sort: 'desc', limit: 50}};
···2121 )
2222});
23232424-export {logsReducer};
2424+export { logsReducer };
2525+
···22import { useQueryClient, hashKey, QueryObserver } from '@tanstack/react-query'
33import { useEffect, useState, useMemo } from 'react';
44import ky from 'ky';
55-import { QueryPlaysOpts, QueryPlaysOptsJson } from "../../backend/common/database/drizzle/repositories/PlayRepository";
65import qs from 'qs';
76import { baseUrl } from "../utils";
88-import { PaginatedResponse } from "../../backend/common/database/drizzle/repositories/BaseRepository";
99-import { ComponentsApiJson, PlayApiCommonDetailed, PlayStateUI } from "../../core/Api";
1010-import { CLIENT_DEAD_QUEUE, CLIENT_INGRESS_QUEUE, isPlayState, SourcePlayerJson } from "../../core/Atomic";
77+import { type ComponentsApiJson, type PaginatedResponse, type PlayApiCommonDetailed, type PlayStateUI, type QueryPlaysOptsJson } from "../../core/Api";
88+import { CLIENT_DEAD_QUEUE, CLIENT_INGRESS_QUEUE, isPlayState, type SourcePlayerJson } from "../../core/Atomic";
1191210export type QueryPlaysOptsJsonRefreshable = Omit<QueryPlaysOptsJson, 'state'> & {nonce?: string, state?: PlayStateUI[]};
1311
+1-1
src/client/recent/recentDucks.ts
···11import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react";
22-import {JsonPlayObject} from "../../core/Atomic";
22+import {type JsonPlayObject} from "../../core/Atomic";
3344type RecentResponse = (JsonPlayObject & { index: number })[];
55export const recentApi = createApi({
+1-1
src/client/scrobbled/scrobbledDucks.ts
···11import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react";
22-import {JsonPlayObject} from "../../core/Atomic";
22+import {type JsonPlayObject} from "../../core/Atomic";
3344type ScrobbledResponse = (JsonPlayObject & { index: number })[];
55export const scrobbledApi = createApi({
+1-1
src/client/status/StatusSection.tsx
···55import ClientStatusCard from "../components/statusCard/ClientStatusCard";
66import {useGetStatusQuery} from "./statusApi";
77import {clientAdapter, sourceAdapter} from "./ducks";
88-import {RootState} from "../store";
88+import {type RootState} from "../store";
99const StatusSection = (props: PropsFromRedux) => {
1010 const {
1111 } = props;
+1-1
src/client/status/ducks.ts
···77} from '@reduxjs/toolkit'
88import { type Api } from '@reduxjs/toolkit/query/react';
99import {statusApi} from "./statusApi";
1010-import {ClientStatusData, SourcePlayerJson, SourceStatusData} from "../../core/Atomic";
1010+import {type ClientStatusData, type SourcePlayerJson, type SourceStatusData} from "../../core/Atomic";
11111212export interface ApiEventPayload {
1313 type: string,
+1-1
src/client/status/statusApi.ts
···11import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
22-import {ClientStatusData, SourceStatusData} from "../../core/Atomic";
22+import {type ClientStatusData, type SourceStatusData} from "../../core/Atomic";
3344export const statusApi = createApi({
55 reducerPath: 'statusApi',
+3-3
src/client/utils/ComponentUtils.tsx
···11-import { Card, HTMLChakraProps } from '@chakra-ui/react';
22-import { PlayApiCommon, PlayApiCommonDetailed } from '../../core/Api';
33-import { LifecycleStep, QUEUE_STATUS_COMPLETED, QUEUE_STATUS_FAILED } from '../../core/Atomic';
11+import { Card, type HTMLChakraProps } from '@chakra-ui/react';
22+import { type PlayApiCommonDetailed } from '../../core/Api';
33+import { type LifecycleStep, QUEUE_STATUS_COMPLETED, QUEUE_STATUS_FAILED } from '../../core/Atomic';
4455export const cardHeaderSeparator: Card.HeaderProps = {
66 borderBottomWidth: "1px",
+1-1
src/client/utils/index.tsx
···11-import {TrackStringOptions} from "../../core/Atomic";
11+import {type TrackStringOptions} from "../../core/Atomic";
22import React, {type ReactElement, Fragment} from "react";
33import {defaultBuildTrackStringTransformers} from "../../core/StringUtils";
44import dayjs, {Dayjs} from 'dayjs';
+61-8
src/core/Api.ts
···11import type { PickKeys } from "ts-essentials"
22-import { type ComponentMinimalSelect } from "../backend/common/database/drizzle/drizzleTypes.ts"
33-import { type ClientType } from "../backend/common/infrastructure/config/client/clients.ts"
44-import { type SourceType } from "../backend/common/infrastructure/config/source/sources.ts"
55-import { type ComponentType, type ErrorLike, type JsonPlayObject, type PlayState, type Replace, type SOURCE_SOT_TYPES, type SourcePlayerJson } from "./Atomic.ts"
66-import { type Dayjs } from "dayjs"
77-import { INITIALIZING } from "../backend/common/infrastructure/Atomic.ts"
88-import { type ErrorIsh } from "./ErrorUtils.ts"
22+import type { CompareOpKey, ComponentMinimalSelect } from "../backend/common/database/drizzle/drizzleTypes.ts"
33+import type { ClientType } from "../backend/common/infrastructure/config/client/clients.ts"
44+import type { SourceType } from "../backend/common/infrastructure/config/source/sources.ts"
55+import type { ComponentType, DateLike, ErrorLike, JsonPlayObject, PlayState, QueueName, Replace, SOURCE_SOT_TYPES, SourcePlayerJson } from "./Atomic.ts"
66+import type { Dayjs } from "dayjs"
77+import type { ErrorIsh } from "./ErrorUtils.ts"
98109export interface PlayApiCommon {
1110 uid: string
···155154 sortBy: SortPlaysBy
156155}
157156158158-export type PlayStateUI = PlayState | 'dead queued';157157+export type PlayStateUI = PlayState | 'dead queued';
158158+159159+export type QueryPlaysOptsJson = {
160160+ sort?: "playedAt" | "seenAt";
161161+ order?: "asc" | "desc";
162162+ with?: ("input" | "parent" | "parent-input" | "queues")[];
163163+ limit?: number;
164164+ offset?: number;
165165+ state?: ("queued" | "discovered" | "discarded" | "scrobbled" | "failed" | "duped")[];
166166+ stateNot?: ("queued" | "discovered" | "discarded" | "scrobbled" | "failed" | "duped")[];
167167+ componentId?: number;
168168+ seenAt?: {
169169+ type: "eq" | "ne" | "gt" | "gte" | "lt" | "lte";
170170+ date: string;
171171+ } | {
172172+ type: "between";
173173+ range: [string, string];
174174+ inclusive?: boolean;
175175+ };
176176+ playedAt?: {
177177+ type: "eq" | "ne" | "gt" | "gte" | "lt" | "lte";
178178+ date: string;
179179+ } | {
180180+ type: "between";
181181+ range: [string, string];
182182+ inclusive?: boolean;
183183+ };
184184+queues?: {
185185+ queueName: QueueName;
186186+ queueStatus: ('queued' | 'failed' | 'completed')[] | ('queued' | 'failed' | 'completed');
187187+ }[];
188188+ uid?: string[];
189189+ text?: string[];
190190+}
191191+export interface PaginatedQueryResponse {
192192+ limit: number;
193193+ offset: number;
194194+ total?: number;
195195+}
196196+export interface PaginatedResponse<T> {
197197+ data: T[];
198198+ meta: PaginatedQueryResponse;
199199+}
200200+201201+export type CompareDateBetween<D extends DateLike = Dayjs> = {
202202+ type: 'between';
203203+ range: [D, D];
204204+ inclusive?: boolean;
205205+};
206206+207207+export type CompareDateSingle<D extends DateLike = Dayjs> = {
208208+ type: CompareOpKey<D>;
209209+ date: D;
210210+};
211211+
+17-12
src/core/Atomic.ts
···11-import type { LogDataPretty, LogLevel } from "@foxxmd/logging";
21import type { Dayjs } from "dayjs";
32import type { AdditionalTrackInfoResponse } from "../backend/common/vendor/listenbrainz/interfaces.ts";
44-import type { Merge, RequiredKeys, StrictOmit } from "ts-essentials";
55-import type { ErrorObject } from "serialize-error";
66-import type { PlayPlatformIdStr } from "../backend/common/infrastructure/Atomic.ts";
33+import { type Merge, type RequiredKeys, type StrictOmit } from "ts-essentials";
44+import { type ErrorObject } from "serialize-error";
75import type { FlowControlTerm, TransformHook } from "../backend/common/infrastructure/Transform.ts";
88-import type { Changeset } from "json-diff-ts";
99-import type { IParseBaseOptions } from 'qs';
66+import { type Changeset } from "json-diff-ts";
77+import { type IParseBaseOptions } from 'qs';
108119export type ComponentTypeClient = 'client';
1210export const COMPONENT_TYPE_CLIENT: ComponentTypeClient = 'client';
···403401 playDateCompleted?: Dayjs
404402}
405403404404+export type LogLevelStandalone = 'debug' | 'error' | 'verbose' | 'info' | 'silly' | 'silent' | 'log' | 'trace' | 'warn' | 'fatal';
405405+406406export interface LogOutputConfig {
407407- level: LogLevel,
407407+ level: LogLevelStandalone,
408408 sort: string,
409409 limit: number
410410}
411411+412412+export type PlayPlatformIdStr = string;
411413412414export interface SourcePlayerObj<D extends DateLike = Dayjs> {
413415 platformId: PlayPlatformIdStr,
···497499 HISTORY: 'history' as SOURCE_SOT_TYPES
498500}
499501export const sourceSotTypes: SOURCE_SOT_TYPES[] = ['player','history'];
500500-501501-export interface LeveledLogData extends LogDataPretty {
502502- levelLabel: string
503503-}
504502505503export interface URLData {
506504 url: URL
···712710 arrayLimit: 20,
713711 plainObjects: true,
714712 allowPrototypes: false
715715-}713713+};
714714+715715+export const DELIMITERS = [',', '&', '/', '\\'];export const DELIMETERS_REGEX: RegExp = new RegExp(/[,&\/\\]/);
716716+export const DELIMITERS_NO_AMP = [',', '/', '\\'];
717717+export type DeviceId = string;
718718+export type PlayUserId = string;
719719+export type PlayPlatformId = [DeviceId, PlayUserId];
720720+
+28-1
src/core/DataUtils.ts
···135135 if (truthyFunc(arr[i]))
136136 indexes.push(i);
137137 return indexes;
138138-}138138+};
139139+export const removeUndefinedKeys = <T extends Record<string, any>>(obj: T, returnUndefined: boolean = true): T | undefined => {
140140+ const newObj: any = {};
141141+ Object.keys(obj).forEach((key) => {
142142+ if (Array.isArray(obj[key])) {
143143+ newObj[key] = obj[key];
144144+ } else if (obj[key] === Object(obj[key])) {
145145+ // dumb assign nested objects
146146+ // bc they may be third party library-objects that use prototyping and we don't want to mess with
147147+ newObj[key] = obj[key];
148148+ } else if (obj[key] !== undefined) {
149149+ newObj[key] = obj[key];
150150+ }
151151+ });
152152+ if (Object.keys(newObj).length === 0) {
153153+ if (returnUndefined) {
154154+ return undefined;
155155+ }
156156+ return newObj;
157157+ }
158158+ Object.keys(newObj).forEach(key => {
159159+ if (newObj[key] === undefined || (null !== newObj[key] && typeof newObj[key] === 'object' && Object.keys(newObj[key]).length === 0)) {
160160+ delete newObj[key];
161161+ }
162162+ });
163163+ //Object.keys(newObj).forEach(key => newObj[key] === undefined || newObj[key] && delete newObj[key])
164164+ return newObj;
165165+};
+2-4
src/core/PlayMarshalUtils.ts
···22import dayjs from 'dayjs';
33import { Traverse, type TraverseContext } from 'neotraverse/modern';
44import { ListenRange } from '../backend/sources/PlayerState/ListenRange.ts';
55-import { type AmbPlayObject, type DateLike, type ErrorLike, type JsonPlayObject, type PlayObject, type PlayProgressAmb, REGEX_ISO8601_LOOSE, type Replace } from './Atomic.ts';
66-import { ListenProgressPositional, ListenProgressTS } from '../backend/sources/PlayerState/ListenProgress.ts';
77-import type { DeepPick, ElementOf, MarkOptional, PickKeys } from 'ts-essentials';
88-import { type PlaySelectWithQueueStates } from '../backend/common/database/drizzle/drizzleTypes.ts';
55+import { type AmbPlayObject, type DateLike, type JsonPlayObject, type PlayObject, REGEX_ISO8601_LOOSE, type Replace } from './Atomic.ts';
66+import type { ElementOf, MarkOptional } from 'ts-essentials';
97import { type ErrorObject, serializeError } from 'serialize-error';
108import { type PlayApiCommonDetailed } from './Api.ts';
119
+3-2
src/core/PlayTestUtils.ts
···88import { type ArtistCredit, type BrainzMeta, FEAT, JOINERS, JOINERS_FINAL, type JsonPlayObject, type MBID, type MissingMbidType, type ObjectPlayData, type PlayMeta, type PlayObject, type SourcePlayerObj } from "./Atomic.ts";
99import { genGroupIdStr } from './PlayUtils.ts';
1010import { sortByNewestPlayDate } from './PlayUtils.ts';
1111-import { CALCULATED_PLAYER_STATUSES, NO_DEVICE, NO_USER, type PlayerStateDataMaybePlay, type PlayPlatformId, REPORTED_PLAYER_STATUSES, SINGLE_USER_PLATFORM_ID } from '../backend/common/infrastructure/Atomic.ts';
1111+import { CALCULATED_PLAYER_STATUSES, NO_DEVICE, NO_USER, type PlayerStateDataMaybePlay, REPORTED_PLAYER_STATUSES, SINGLE_USER_PLATFORM_ID } from '../backend/common/infrastructure/Atomic.ts';
1212+import { type PlayPlatformId } from './Atomic.ts';
1213import { arrayListAnd, artistNamesToCredits } from './StringUtils.ts';
1314import { findDelimiters } from "./StringUtils.ts";
1415import { type ListRecord } from '../backend/common/infrastructure/config/client/tealfm.ts';
1516import { nanoid } from 'nanoid';
1617import { type LastFMTrackObject } from '../backend/common/vendor/LastfmApiClient.ts';
1718import clone from 'clone';
1818-import { removeUndefinedKeys } from '../backend/utils.ts';
1919+import { removeUndefinedKeys } from './DataUtils.ts';
1920import { FmTealAlphaFeedPlay } from '../backend/common/vendor/teal/lexicons/index.ts';
20212122dayjs.extend(utc)
+1-2
src/core/PlayUtils.ts
···11-import { type PlayPlatformId } from "../backend/common/infrastructure/Atomic.ts";
22-import { type AmbPlayObject, type DateLike, type PlayObject, type PlayObjectMinimal } from "./Atomic.ts";
11+import { type AmbPlayObject, type DateLike, type PlayObject, type PlayObjectMinimal, type PlayPlatformId } from "./Atomic.ts";
32import dayjs from "dayjs";
4354export const sortByNewestDate = (aPlayDate: DateLike, bPlayDate: DateLike) => {
+2-2
src/core/StringUtils.ts
···1414 type ScrobbleTsSOC,
1515 type TrackStringOptions
1616} from "./Atomic.ts";
1717-import { DELIMETERS_REGEX, DELIMITERS } from "../backend/common/infrastructure/Atomic.ts";
1717+import { DELIMETERS_REGEX, DELIMITERS } from './Atomic.ts';
1818import { parseRegexSingle } from "@foxxmd/regex-buddy-core";
1919-import { removeUndefinedKeys } from "../backend/utils.ts";
1919+import { removeUndefinedKeys } from './DataUtils.ts';
2020import { nanoid } from "nanoid";
21212222dayjs.extend(utc)