From c437dfc35aa2bfde44a31483aa83340972ac9f66 Mon Sep 17 00:00:00 2001 From: Lane Sawyer Date: Sat, 11 Jul 2026 04:05:34 -0700 Subject: [PATCH] chore(ui): removes dead utilities and op-log leftovers [#8260 - Tier A] (#8892) * chore(ui): removes dead utilities and op-log leftovers [#8260 - Tier A] * update readme and remove-unused-log-imports.ts * restore benchmark test and make runnable --- build/electronMac{.original}.js | 0 .../operation-log-architecture.md | 2 +- .../sqlite-migration-followup.md | 5 +- e2e/README.md | 1 - e2e/pages/schedule.page.ts | 2 - src/app/core/base-component/base.component.ts | 16 ------ src/app/core/clipboard-image/index.ts | 2 - .../resolve-clipboard-url.pipe.ts | 52 ------------------- .../archive/store/archive-old.reducer.ts | 20 ------- .../archive/store/archive-young.reducer.ts | 20 ------- .../features/issue-panel/issue-panel.model.ts | 9 ---- .../project/util/is-valid-project-export.ts | 10 ---- .../sync-providers/native-http-retry.ts | 40 -------------- .../operation-log-stress.benchmark.ts | 17 ++++-- src/app/op-log/validation/fix-number-field.ts | 12 ----- .../op-log/validation/repair-global-config.ts | 45 ---------------- src/app/root-store/index.ts | 4 -- src/app/ui/animations/dynamic-height.ani.ts | 13 ----- src/app/ui/animations/noop.ani.ts | 4 -- .../ui/animations/slide-in-from-left.ani.ts | 16 ------ src/app/ui/animations/slide.ani.ts | 28 ---------- src/app/util/filter-local-action.ts | 42 --------------- src/app/util/format-iso-data-with-offset.ts | 34 ------------ src/app/util/get-text-from-array-buffer.ts | 37 ------------- src/app/util/get-yesterdays-date.ts | 6 --- src/app/util/hash.ts | 21 -------- src/app/util/is-chrome.ts | 1 - src/app/util/is-email.ts | 3 -- src/app/util/obs-log.ts | 42 --------------- src/app/util/observe-object-property.ts | 26 ---------- src/app/util/resize-observer-obs.ts | 21 -------- .../timestamp-to-datetime-input-string.ts | 8 --- src/tsconfig.spec.json | 5 +- 33 files changed, 21 insertions(+), 543 deletions(-) delete mode 100644 build/electronMac{.original}.js delete mode 100644 e2e/pages/schedule.page.ts delete mode 100644 src/app/core/base-component/base.component.ts delete mode 100644 src/app/core/clipboard-image/index.ts delete mode 100644 src/app/core/clipboard-image/resolve-clipboard-url.pipe.ts delete mode 100644 src/app/features/archive/store/archive-old.reducer.ts delete mode 100644 src/app/features/archive/store/archive-young.reducer.ts delete mode 100644 src/app/features/issue-panel/issue-panel.model.ts delete mode 100644 src/app/features/project/util/is-valid-project-export.ts delete mode 100644 src/app/op-log/sync-providers/native-http-retry.ts delete mode 100644 src/app/op-log/validation/fix-number-field.ts delete mode 100644 src/app/op-log/validation/repair-global-config.ts delete mode 100644 src/app/root-store/index.ts delete mode 100644 src/app/ui/animations/dynamic-height.ani.ts delete mode 100644 src/app/ui/animations/noop.ani.ts delete mode 100644 src/app/ui/animations/slide-in-from-left.ani.ts delete mode 100644 src/app/ui/animations/slide.ani.ts delete mode 100644 src/app/util/filter-local-action.ts delete mode 100644 src/app/util/format-iso-data-with-offset.ts delete mode 100644 src/app/util/get-text-from-array-buffer.ts delete mode 100644 src/app/util/get-yesterdays-date.ts delete mode 100644 src/app/util/hash.ts delete mode 100644 src/app/util/is-chrome.ts delete mode 100644 src/app/util/is-email.ts delete mode 100644 src/app/util/obs-log.ts delete mode 100644 src/app/util/observe-object-property.ts delete mode 100644 src/app/util/resize-observer-obs.ts delete mode 100644 src/app/util/timestamp-to-datetime-input-string.ts diff --git a/build/electronMac{.original}.js b/build/electronMac{.original}.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs/sync-and-op-log/operation-log-architecture.md b/docs/sync-and-op-log/operation-log-architecture.md index 5a2cd5197d..c334967eac 100644 --- a/docs/sync-and-op-log/operation-log-architecture.md +++ b/docs/sync-and-op-log/operation-log-architecture.md @@ -2365,7 +2365,7 @@ src/app/op-log/ │ ├── test-client.helper.ts # Test client utilities │ └── operation-factory.helper.ts # Test operation builders └── benchmarks/ - └── operation-log-stress.spec.ts # Performance stress tests + └── operation-log-stress.benchmark.ts # Manual perf harness (not auto-run in CI) src/app/features/work-context/store/ ├── work-context-meta.actions.ts # Move actions (moveTaskInTodayList, etc.) diff --git a/docs/sync-and-op-log/sqlite-migration-followup.md b/docs/sync-and-op-log/sqlite-migration-followup.md index a217613a79..31f20ef969 100644 --- a/docs/sync-and-op-log/sqlite-migration-followup.md +++ b/docs/sync-and-op-log/sqlite-migration-followup.md @@ -138,7 +138,10 @@ captured on the next tick. - ⏳ **Remains: the on-device real-engine run.** sql.js validates the engine, not the Capacitor bridge or the native plugin's specific SQLite build/flags. The `operation-log-stress.benchmark.ts` harness is the lever for the on-device - perf + behavior pass (see B1 perf note). + perf + behavior pass (see B1 perf note). Run it explicitly with + `npm run test:file src/app/op-log/testing/benchmarks/operation-log-stress.benchmark.ts`; + it is compiled by `src/tsconfig.spec.json` but excluded from the auto-run + `**/*.spec.ts` set, so it never runs in normal CI. ### B3. Flip the DI token on native — init fix ✅ landed; token flip device-gated diff --git a/e2e/README.md b/e2e/README.md index 1a87854d0b..6d412f3957 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -83,7 +83,6 @@ e2e/ │ ├── settings.page.ts │ ├── dialog.page.ts │ ├── planner.page.ts -│ ├── schedule.page.ts │ ├── side-nav.page.ts │ ├── sync.page.ts │ ├── tag.page.ts diff --git a/e2e/pages/schedule.page.ts b/e2e/pages/schedule.page.ts deleted file mode 100644 index 0ba9023b5c..0000000000 --- a/e2e/pages/schedule.page.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Schedule page removed - not used -export {}; diff --git a/src/app/core/base-component/base.component.ts b/src/app/core/base-component/base.component.ts deleted file mode 100644 index 942f47f4fc..0000000000 --- a/src/app/core/base-component/base.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core'; -import { Subject } from 'rxjs'; - -@Component({ - selector: 'base', - template: '', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BaseComponent implements OnDestroy { - protected onDestroy$ = new Subject(); - - ngOnDestroy(): void { - this.onDestroy$.next(); - this.onDestroy$.complete(); - } -} diff --git a/src/app/core/clipboard-image/index.ts b/src/app/core/clipboard-image/index.ts deleted file mode 100644 index 3f09a06da4..0000000000 --- a/src/app/core/clipboard-image/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './clipboard-image.service'; -export * from './resolve-clipboard-images.directive'; diff --git a/src/app/core/clipboard-image/resolve-clipboard-url.pipe.ts b/src/app/core/clipboard-image/resolve-clipboard-url.pipe.ts deleted file mode 100644 index e41f1c5dff..0000000000 --- a/src/app/core/clipboard-image/resolve-clipboard-url.pipe.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Pipe, PipeTransform, inject } from '@angular/core'; -import { ClipboardImageService } from './clipboard-image.service'; -import { Observable, from, of, defer } from 'rxjs'; -import { map, shareReplay, catchError } from 'rxjs/operators'; -import { Log } from '../log'; - -@Pipe({ - name: 'resolveClipboardUrl', - standalone: true, -}) -export class ResolveClipboardUrlPipe implements PipeTransform { - private readonly _clipboardImageService = inject(ClipboardImageService); - private readonly _cache = new Map>(); - - transform(url: string | undefined | null): Observable { - if (!url) { - return of(''); - } - - // If it's not an indexeddb URL, return as-is - if (!url.startsWith('indexeddb://clipboard-images/')) { - return of(url); - } - - // Check cache first - if already resolving or resolved, return the cached observable - const cached = this._cache.get(url); - if (cached) { - return cached; - } - - // Use defer to ensure the promise is created fresh and properly handled - const resolved$ = defer(() => - from(this._clipboardImageService.resolveIndexedDbUrl(url)), - ).pipe( - map((resolvedUrl) => { - if (resolvedUrl) { - return resolvedUrl; - } - return url; - }), - catchError((error) => { - Log.err('Error resolving clipboard URL:', error); - return of(url); - }), - shareReplay({ bufferSize: 1, refCount: false }), - ); - - // Cache immediately before returning so concurrent calls will get the same observable - this._cache.set(url, resolved$); - return resolved$; - } -} diff --git a/src/app/features/archive/store/archive-old.reducer.ts b/src/app/features/archive/store/archive-old.reducer.ts deleted file mode 100644 index f81b4b07a6..0000000000 --- a/src/app/features/archive/store/archive-old.reducer.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createFeatureSelector, createReducer, on } from '@ngrx/store'; -import { loadAllData } from '../../../root-store/meta/load-all-data.action'; -import { ArchiveModel } from '../archive.model'; - -export const ARCHIVE_OLD_FEATURE_NAME = 'archiveOld'; - -export const initialArchiveOldState: ArchiveModel | null = null; - -export const archiveOldReducer = createReducer( - initialArchiveOldState, - on( - loadAllData, - (_state, { appDataComplete }) => - (appDataComplete as { archiveOld?: ArchiveModel | null }).archiveOld ?? null, - ), -); - -export const selectArchiveOldFeatureState = createFeatureSelector( - ARCHIVE_OLD_FEATURE_NAME, -); diff --git a/src/app/features/archive/store/archive-young.reducer.ts b/src/app/features/archive/store/archive-young.reducer.ts deleted file mode 100644 index e6d67da612..0000000000 --- a/src/app/features/archive/store/archive-young.reducer.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createFeatureSelector, createReducer, on } from '@ngrx/store'; -import { loadAllData } from '../../../root-store/meta/load-all-data.action'; -import { ArchiveModel } from '../archive.model'; - -export const ARCHIVE_YOUNG_FEATURE_NAME = 'archiveYoung'; - -export const initialArchiveYoungState: ArchiveModel | null = null; - -export const archiveYoungReducer = createReducer( - initialArchiveYoungState, - on( - loadAllData, - (_state, { appDataComplete }) => - (appDataComplete as { archiveYoung?: ArchiveModel | null }).archiveYoung ?? null, - ), -); - -export const selectArchiveYoungFeatureState = createFeatureSelector( - ARCHIVE_YOUNG_FEATURE_NAME, -); diff --git a/src/app/features/issue-panel/issue-panel.model.ts b/src/app/features/issue-panel/issue-panel.model.ts deleted file mode 100644 index 546f29f68b..0000000000 --- a/src/app/features/issue-panel/issue-panel.model.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { SearchResultItem } from '../issue/issue.model'; - -// eslint-disable-next-line @typescript-eslint/no-namespace -export namespace AddTaskPanel { - export interface IssueItem { - issueProviderId: string; - searchResultItem: SearchResultItem; - } -} diff --git a/src/app/features/project/util/is-valid-project-export.ts b/src/app/features/project/util/is-valid-project-export.ts deleted file mode 100644 index f9a1c37b31..0000000000 --- a/src/app/features/project/util/is-valid-project-export.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const isValidProjectExport = (d: any): boolean => { - return !!( - d && - d.id && - d.title && - d.relatedModels && - d.advancedCfg && - d.relatedModels.task - ); -}; diff --git a/src/app/op-log/sync-providers/native-http-retry.ts b/src/app/op-log/sync-providers/native-http-retry.ts deleted file mode 100644 index 981ca8af90..0000000000 --- a/src/app/op-log/sync-providers/native-http-retry.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { CapacitorHttp, HttpResponse } from '@capacitor/core'; -import { - executeNativeRequestWithRetry as packageExecuteNativeRequestWithRetry, - isTransientNetworkError as packageIsTransientNetworkError, - type NativeHttpExecutor, - type NativeHttpRequestConfig, - type NativeHttpResponse, -} from '@sp/sync-providers/http'; -import { OP_LOG_SYNC_LOGGER } from '../core/sync-logger.adapter'; - -export type NativeRequestConfig = NativeHttpRequestConfig; - -/** - * App-side adapter: wires CapacitorHttp + OP_LOG_SYNC_LOGGER into the - * package-owned retry helper. Existing callers keep their positional API. - * - * The package version (with `{ executor, logger, label, delay }` options) - * is the canonical interface — provider files that move into - * `@sp/sync-providers` should call it directly. - */ -export const executeNativeRequestWithRetry = async ( - config: NativeRequestConfig, - label: string = 'NativeHttp', - requestFn: (opts: NativeRequestConfig) => Promise = (opts) => - CapacitorHttp.request(opts), - delayFn?: (ms: number) => Promise, -): Promise => { - const executor: NativeHttpExecutor = (cfg) => - requestFn(cfg) as unknown as Promise; - - const response = await packageExecuteNativeRequestWithRetry(config, { - executor, - logger: OP_LOG_SYNC_LOGGER, - label, - delay: delayFn, - }); - return response as unknown as HttpResponse; -}; - -export const isTransientNetworkError = packageIsTransientNetworkError; diff --git a/src/app/op-log/testing/benchmarks/operation-log-stress.benchmark.ts b/src/app/op-log/testing/benchmarks/operation-log-stress.benchmark.ts index b2268c8482..3fc89168f1 100644 --- a/src/app/op-log/testing/benchmarks/operation-log-stress.benchmark.ts +++ b/src/app/op-log/testing/benchmarks/operation-log-stress.benchmark.ts @@ -6,28 +6,35 @@ * * To run these benchmarks: * 1. Run: npm run test:file src/app/op-log/testing/benchmarks/operation-log-stress.benchmark.ts - * 3. Watch the console output for timing information + * 2. Watch the console output for timing information * * Note: Results vary significantly based on: * - Browser/test runner * - System I/O performance * - Concurrent processes * - * These tests are excluded from regular test runs to avoid flaky CI failures - * due to timing dependencies on system load. + * These tests are excluded from regular test runs (the Karma builder only + * auto-runs `**\/*.spec.ts`) to avoid flaky CI failures from timing + * dependencies on system load. They are still part of the spec TS program + * (`src/tsconfig.spec.json` includes `**\/*.benchmark.ts`) so they compile and + * only run when named explicitly via `test:file`. */ import { TestBed } from '@angular/core/testing'; import { OperationLogStoreService } from '../../persistence/operation-log-store.service'; import { ActionType, Operation, OpType, EntityType } from '../../core/operation.types'; import { uuidv7 } from '../../../util/uuid-v7'; -// Increase timeout for stress tests -jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000; +// Stress-run timeout for the large-payload / 10k-op benchmarks. Must be set +// in `beforeEach` (not at module scope): `src/test.ts` has a global `beforeAll` +// that resets `DEFAULT_TIMEOUT_INTERVAL` to 2000ms, which would otherwise clobber +// a module-level assignment and time these benchmarks out. +const STRESS_TIMEOUT_MS = 60000; describe('OperationLog Performance Benchmarks', () => { let service: OperationLogStoreService; beforeEach(async () => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = STRESS_TIMEOUT_MS; TestBed.configureTestingModule({ providers: [OperationLogStoreService], }); diff --git a/src/app/op-log/validation/fix-number-field.ts b/src/app/op-log/validation/fix-number-field.ts deleted file mode 100644 index c85b5f256a..0000000000 --- a/src/app/op-log/validation/fix-number-field.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const fixNumberField = (value: unknown, defaultValue?: number): number => { - if (typeof value === 'number') { - return value; - } - if (typeof value === 'string') { - const parsedValue = parseFloat(value); - if (!isNaN(parsedValue)) { - return parsedValue; - } - } - return typeof defaultValue === 'number' ? defaultValue : 0; -}; diff --git a/src/app/op-log/validation/repair-global-config.ts b/src/app/op-log/validation/repair-global-config.ts deleted file mode 100644 index fbebc3f93f..0000000000 --- a/src/app/op-log/validation/repair-global-config.ts +++ /dev/null @@ -1,45 +0,0 @@ -// import { GlobalConfigState } from '../../features/config/global-config.model'; -// import { fixNumberField } from './fix-number-field'; -// import { DEFAULT_GLOBAL_CONFIG } from '../../features/config/default-global-config.const'; -// -// export const repairGlobalConfig = ( -// globalConfig: GlobalConfigState, -// ): GlobalConfigState => { -// // Helper function to fix a number field at a specific path -// const fixNumberFieldInConfig = (obj: any, path: string[], defaultValue: number) => { -// const lastKey = path[path.length - 1]; -// const parentObj = -// path.length > 1 ? path.slice(0, -1).reduce((o, k) => o[k], obj) : obj; -// -// if (typeof parentObj[lastKey] !== 'number') { -// parentObj[lastKey] = fixNumberField(parentObj[lastKey], defaultValue); -// } -// }; -// -// // Fields to check with their paths and default values -// const fieldsToFix = [ -// { -// path: ['misc', 'firstDayOfWeek'], -// defaultValue: DEFAULT_GLOBAL_CONFIG.misc.firstDayOfWeek, -// }, -// { -// path: ['misc', 'startOfNextDay'], -// defaultValue: DEFAULT_GLOBAL_CONFIG.misc.startOfNextDay, -// }, -// { -// path: ['pomodoro', 'breakDuration'], -// defaultValue: DEFAULT_GLOBAL_CONFIG.pomodoro.breakDuration, -// }, -// { -// path: ['pomodoro', 'cyclesBeforeLongerBreak'], -// defaultValue: DEFAULT_GLOBAL_CONFIG.pomodoro.cyclesBeforeLongerBreak, -// }, -// ]; -// -// // Apply fixes to all fields -// fieldsToFix.forEach(({ path, defaultValue }) => { -// fixNumberFieldInConfig(globalConfig, path, defaultValue); -// }); -// -// return globalConfig as GlobalConfigState; -// }; diff --git a/src/app/root-store/index.ts b/src/app/root-store/index.ts deleted file mode 100644 index c91cb3c09d..0000000000 --- a/src/app/root-store/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ActionReducerMap } from '@ngrx/store'; -import { RootState } from './root-state'; - -export const reducers: Partial> = {}; diff --git a/src/app/ui/animations/dynamic-height.ani.ts b/src/app/ui/animations/dynamic-height.ani.ts deleted file mode 100644 index 569a87ca52..0000000000 --- a/src/app/ui/animations/dynamic-height.ani.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { animate, style, transition, trigger } from '@angular/animations'; -import { ANI_STANDARD_TIMING } from './animation.const'; - -export const dynamicHeightAnimation = [ - trigger('dynamicHeight', [ - transition('void <=> *', []), - transition( - '* <=> *', - [style({ height: '{{startHeight}}px', opacity: 0 }), animate(ANI_STANDARD_TIMING)], - { params: { startHeight: 0 } }, - ), - ]), -]; diff --git a/src/app/ui/animations/noop.ani.ts b/src/app/ui/animations/noop.ani.ts deleted file mode 100644 index 1015fc3cc0..0000000000 --- a/src/app/ui/animations/noop.ani.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { transition, trigger } from '@angular/animations'; - -// use on parent to skip initial animation -export const noopAnimation = trigger('noop', [transition(':enter', [])]); diff --git a/src/app/ui/animations/slide-in-from-left.ani.ts b/src/app/ui/animations/slide-in-from-left.ani.ts deleted file mode 100644 index a8783bdaee..0000000000 --- a/src/app/ui/animations/slide-in-from-left.ani.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { animate, style, transition, trigger } from '@angular/animations'; -import { ANI_ENTER_TIMING } from './animation.const'; - -export const slideInFromLeftAni = [ - trigger('slideInFromLeft', [ - transition(':enter', [ - style({ transform: 'translateX(-100%)' }), - animate(ANI_ENTER_TIMING, style({ transform: 'translateX(0)' })), - ]), - - // transition(':leave', [ - // style({ transform: 'translateX(0)' }), - // animate(ANI_ENTER_TIMING, style({ transform: 'translateX(-100%)' })), - // ]), - ]), -]; diff --git a/src/app/ui/animations/slide.ani.ts b/src/app/ui/animations/slide.ani.ts deleted file mode 100644 index 89785d085e..0000000000 --- a/src/app/ui/animations/slide.ani.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { animate, style, transition, trigger } from '@angular/animations'; -import { ANI_ENTER_TIMING, ANI_LEAVE_TIMING } from './animation.const'; - -export const slideAnimation = [ - trigger('slide', [ - transition(':enter', [ - style({ marginTop: '-{{elHeight}}px', opacity: 0 }), - animate(ANI_ENTER_TIMING, style({ marginTop: '*', opacity: 1 })), - ]), // void => * - transition(':leave', [ - style({ marginTop: 0, opacity: 1 }), - animate(ANI_LEAVE_TIMING, style({ marginTop: '-{{elHeight}}px', opacity: 0 })), - ]), - ]), -]; - -// export const slideAnimation = [ -// trigger('slide', [ -// transition(':enter', [ -// style({height: 0, overflow: 'hidden', transform: 'translateY(-120%)'}), -// animate(ANI_ENTER_TIMING, style({height: '*', transform: 'translateY(0)'})) -// ]), // void => * -// transition(':leave', [ -// style({overflow: 'hidden'}), -// animate(ANI_LEAVE_TIMING, style({height: 0, transform: 'translateY(-120%)'})) -// ]) -// ]) -// ]; diff --git a/src/app/util/filter-local-action.ts b/src/app/util/filter-local-action.ts deleted file mode 100644 index 89ec5ffb76..0000000000 --- a/src/app/util/filter-local-action.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { filter, OperatorFunction } from 'rxjs'; -import { Action } from '@ngrx/store'; - -/** - * RxJS operator that filters out remote actions from sync. - * - * Use this in effects that should only run for local user actions, - * not for actions replayed from remote sync. - * - * @example - * ```typescript - * myEffect$ = createEffect(() => - * this._actions$.pipe( - * ofType(TaskSharedActions.addTask), - * filterLocalAction(), - * // ... rest of effect - * ) - * ); - * ``` - */ -export const filterLocalAction = (): OperatorFunction => - filter((action: T) => !(action as any).meta?.isRemote); - -/** - * RxJS operator that filters to only remote actions from sync. - * - * Use this in effects that should only run for remote sync actions, - * not for local user actions. - * - * @example - * ```typescript - * myEffect$ = createEffect(() => - * this._actions$.pipe( - * ofType(TaskSharedActions.moveToArchive), - * filterRemoteAction(), - * // ... rest of effect - * ) - * ); - * ``` - */ -export const filterRemoteAction = (): OperatorFunction => - filter((action: T) => !!(action as any).meta?.isRemote); diff --git a/src/app/util/format-iso-data-with-offset.ts b/src/app/util/format-iso-data-with-offset.ts deleted file mode 100644 index 73ac5ce996..0000000000 --- a/src/app/util/format-iso-data-with-offset.ts +++ /dev/null @@ -1,34 +0,0 @@ -export const formatISODateWithOffset = (date: Date): string => { - const pad = (num, length = 2): string => String(num).padStart(length, '0'); - - const year = date.getFullYear(); - const month = pad(date.getMonth() + 1); // Months are 0-indexed - const day = pad(date.getDate()); - const hours = pad(date.getHours()); - const minutes = pad(date.getMinutes()); - const seconds = pad(date.getSeconds()); - const milliseconds = pad(date.getMilliseconds(), 3); // Standard 3 digits - - // --- Timezone Offset Calculation --- - const offsetMinutes = date.getTimezoneOffset(); - const offsetHours = Math.abs(Math.floor(offsetMinutes / 60)); - const offsetMinPart = Math.abs(offsetMinutes % 60); - // getTimezoneOffset returns POSITIVE for zones WEST of UTC, and NEGATIVE for EAST. - // The standard ISO format requires the opposite sign (+ for EAST, - for WEST). - const offsetSign = offsetMinutes <= 0 ? '+' : '-'; - const offsetFormatted = `${offsetSign}${pad(offsetHours)}:${pad(offsetMinPart)}`; - // Special case for UTC (offset is 0) - some prefer 'Z' - // const offsetFormatted = offsetMinutes === 0 ? 'Z' : `${offsetSign}${pad(offsetHours)}:${pad(offsetMinPart)}`; - - // --- Assemble the String --- - // Using standard 3-digit milliseconds (.SSS) - const formattedString = `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}${offsetFormatted}`; - - /* - // If you strictly need 2-digit milliseconds (.SS), uncomment this: - const millisecondsTwoDigits = milliseconds.substring(0, 2); - formattedString = `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${millisecondsTwoDigits}${offsetFormatted}`; - */ - - return formattedString; -}; diff --git a/src/app/util/get-text-from-array-buffer.ts b/src/app/util/get-text-from-array-buffer.ts deleted file mode 100644 index b5c47fb877..0000000000 --- a/src/app/util/get-text-from-array-buffer.ts +++ /dev/null @@ -1,37 +0,0 @@ -// borrowed -export const getTextFromArrayBuffer = ( - arrayBuffer: ArrayBuffer, - encoding: string = 'UTF-8', -): Promise => { - return new Promise((resolve /*, reject*/) => { - if (typeof Blob === 'undefined') { - const buffer = new Buffer(new Uint8Array(arrayBuffer)); - resolve(buffer.toString(encoding)); - } else { - let blob; - const gc = window as any; - // TODO fix as BlobBuilder is not available in all browsers - // @see https://developer.mozilla.org/en-US/docs/Web/API/BlobBuilder - gc.BlobBuilder = gc.BlobBuilder || gc.WebKitBlobBuilder; - if (typeof gc.BlobBuilder !== 'undefined') { - const bb = new gc.BlobBuilder(); - bb.append(arrayBuffer); - blob = bb.getBlob(); - } else { - blob = new Blob([arrayBuffer]); - } - - const fileReader = new FileReader(); - if (typeof fileReader.addEventListener === 'function') { - fileReader.addEventListener('loadend', (evt) => { - resolve(evt.target.result); - }); - } else { - fileReader.onloadend = (evt) => { - resolve(evt.target.result); - }; - } - fileReader.readAsText(blob, encoding); - } - }); -}; diff --git a/src/app/util/get-yesterdays-date.ts b/src/app/util/get-yesterdays-date.ts deleted file mode 100644 index 40e7654b33..0000000000 --- a/src/app/util/get-yesterdays-date.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const getYesterdaysDate = (): Date => { - const today = new Date(); - const yesterday = new Date(today); - yesterday.setDate(today.getDate() - 1); - return yesterday; -}; diff --git a/src/app/util/hash.ts b/src/app/util/hash.ts deleted file mode 100644 index d04ff06ffe..0000000000 --- a/src/app/util/hash.ts +++ /dev/null @@ -1,21 +0,0 @@ -// TAKEN from https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js -// NOTE: NEVER CHANGE THIS AS IT IS USED FOR ID GENERATION -const SEED = 454; -const cyrb53a = (str: string): number => { - let h1 = 0xdeadbeef ^ SEED, - h2 = 0x41c6ce57 ^ SEED; - for (let i = 0, ch; i < str.length; i++) { - ch = str.charCodeAt(i); - h1 = Math.imul(h1 ^ ch, 0x85ebca77); - h2 = Math.imul(h2 ^ ch, 0xc2b2ae3d); - } - h1 ^= Math.imul(h1 ^ (h2 >>> 15), 0x735a2d97); - h2 ^= Math.imul(h2 ^ (h1 >>> 15), 0xcaf649a9); - h1 ^= h2 >>> 16; - h2 ^= h1 >>> 16; - // eslint-disable-next-line no-mixed-operators - return 2097152 * (h2 >>> 0) + (h1 >>> 11); -}; - -// NOTE: NEVER CHANGE THIS FN AS IT IS USED FOR ID GENERATION -export const hash = (str: string): string => cyrb53a(str).toString(); diff --git a/src/app/util/is-chrome.ts b/src/app/util/is-chrome.ts deleted file mode 100644 index 56a35c150c..0000000000 --- a/src/app/util/is-chrome.ts +++ /dev/null @@ -1 +0,0 @@ -export const IS_CHROME = navigator.userAgent.toLowerCase().indexOf('chrome') > -1; diff --git a/src/app/util/is-email.ts b/src/app/util/is-email.ts deleted file mode 100644 index def47f95b2..0000000000 --- a/src/app/util/is-email.ts +++ /dev/null @@ -1,3 +0,0 @@ -const emailRegex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; -export const isEmail = (str: string): boolean => - typeof str === 'string' && !!str.match(emailRegex); diff --git a/src/app/util/obs-log.ts b/src/app/util/obs-log.ts deleted file mode 100644 index dd6de21c79..0000000000 --- a/src/app/util/obs-log.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { defer, Observable, Subscriber } from 'rxjs'; -import { finalize, tap } from 'rxjs/operators'; -import { Log } from '../core/log'; - -export const obsLogAll$ = ( - sourceOrName: Observable | string, - source?: Observable, -): Observable => { - const name = typeof sourceOrName === 'string' ? sourceOrName : 'obs$'; - const usedSource = typeof sourceOrName === 'string' ? source : sourceOrName; - return subscriberCount$(obsLog$(usedSource, name), name); -}; - -export const obsLog$ = (source: Observable, name: string = 'obs$'): Observable => - defer(() => { - Log.log(`_o_ ${name}: subscribed ++`); - return source.pipe( - tap({ - next: (value) => Log.log(`_o_ ${name}: ${value}`), - complete: () => Log.log(`_o_ ${name}: $$$ complete $$$`), - }), - finalize(() => Log.log(`_o_ ${name}: unsubscribed --`)), - ); - }); - -export const subscriberCount$ = ( - sourceObservable: Observable, - name: string = 'obs$', -): Observable => { - let counter = 0; - return new Observable((subscriber: Subscriber) => { - const subscription = sourceObservable.subscribe(subscriber); - counter++; - Log.log(`_o_ ${name} subs: ${counter}`); - - return () => { - subscription.unsubscribe(); - counter--; - Log.log(`_o_ ${name} subs: ${counter}`); - }; - }); -}; diff --git a/src/app/util/observe-object-property.ts b/src/app/util/observe-object-property.ts deleted file mode 100644 index 9b238bdae4..0000000000 --- a/src/app/util/observe-object-property.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { BehaviorSubject, Observable } from 'rxjs'; - -export const observeObjectProperty = ( - obj: T, - propertyKey: K, -): Observable => { - // Create a subject with the initial value - const subject = new BehaviorSubject(obj[propertyKey]); - - // Store the original value - let value = obj[propertyKey]; - - // Define a getter/setter for the property - Object.defineProperty(obj, propertyKey, { - configurable: true, - enumerable: true, - get: () => value, - set: (newValue) => { - value = newValue; - subject.next(newValue); - }, - }); - - // Return the observable part of the subject - return subject.asObservable(); -}; diff --git a/src/app/util/resize-observer-obs.ts b/src/app/util/resize-observer-obs.ts deleted file mode 100644 index e6f4ac1339..0000000000 --- a/src/app/util/resize-observer-obs.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Observable, Subscriber } from 'rxjs'; -import { Log } from '../core/log'; - -export const observeWidth = (target: HTMLElement): Observable => { - return new Observable((observer: Subscriber) => { - if ((window as any).ResizeObserver) { - const resizeObserver = new (window as any).ResizeObserver( - (entries: ResizeObserverEntry[]) => { - observer.next(entries[0].contentRect.width); - }, - ); - resizeObserver.observe(target); - return () => { - resizeObserver.unobserve(target); - }; - } else { - Log.err('ResizeObserver not supported in this browser'); - return undefined; - } - }); -}; diff --git a/src/app/util/timestamp-to-datetime-input-string.ts b/src/app/util/timestamp-to-datetime-input-string.ts deleted file mode 100644 index ae9a812956..0000000000 --- a/src/app/util/timestamp-to-datetime-input-string.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const timestampToDatetimeInputString = (timestamp: number): string => { - const date = new Date(timestamp + _getTimeZoneOffsetInMs()); - return date.toISOString().slice(0, 19); -}; - -const _getTimeZoneOffsetInMs = (): number => { - return new Date().getTimezoneOffset() * -60 * 1000; -}; diff --git a/src/tsconfig.spec.json b/src/tsconfig.spec.json index 07bd753a58..884f9b2fe8 100644 --- a/src/tsconfig.spec.json +++ b/src/tsconfig.spec.json @@ -29,5 +29,8 @@ } }, "files": ["test.ts", "polyfills.ts"], - "include": ["**/*.spec.ts", "**/*.d.ts"] + // `**/*.benchmark.ts` files are part of the spec TS program so they compile + // (and are type-checked in CI), but they are NOT auto-run: the Karma builder's + // default `include` is `**/*.spec.ts`, so benchmarks need to be run manually. + "include": ["**/*.spec.ts", "**/*.benchmark.ts", "**/*.d.ts"] }