feat: add Serwist service worker for offline PWA support

This commit is contained in:
saravanakumardb1 2026-02-27 21:18:43 -08:00
parent cae442d099
commit 28dfa9f929
9 changed files with 1291 additions and 20 deletions

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.chronomind.shared</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,183 @@
// Cascade Tests
// XCTest unit tests mirroring web cascade.test.ts
import XCTest
@testable import ChronoMind
final class CascadeTests: XCTestCase {
// MARK: - Preset Intervals
func testAggressivePreset() {
let intervals = CascadePreset.aggressive.defaultIntervals
XCTAssertEqual(intervals, [240, 180, 120, 90, 60, 30, 15, 5, 1])
}
func testStandardPreset() {
let intervals = CascadePreset.standard.defaultIntervals
XCTAssertEqual(intervals, [120, 60, 30, 15, 5])
}
func testLightPreset() {
let intervals = CascadePreset.light.defaultIntervals
XCTAssertEqual(intervals, [60, 15, 5])
}
func testMinimalPreset() {
let intervals = CascadePreset.minimal.defaultIntervals
XCTAssertEqual(intervals, [15])
}
func testNonePreset() {
let intervals = CascadePreset.none.defaultIntervals
XCTAssertTrue(intervals.isEmpty)
}
// MARK: - Calculate Cascade Warnings
func testCalculateWarnings() {
let now = Date()
let targetTime = now.addingTimeInterval(3600) // 1h from now
let intervals = [60, 30, 15, 5]
let warnings = calculateCascadeWarnings(targetTime: targetTime, intervals: intervals, now: now)
XCTAssertEqual(warnings.count, 4)
// Should be sorted largest first
XCTAssertEqual(warnings[0].minutesBefore, 60)
XCTAssertEqual(warnings[1].minutesBefore, 30)
XCTAssertEqual(warnings[2].minutesBefore, 15)
XCTAssertEqual(warnings[3].minutesBefore, 5)
}
func testWarningsInPastAreFired() {
let now = Date()
let targetTime = now.addingTimeInterval(600) // 10 min from now
let intervals = [120, 60, 15, 5] // 2h and 1h are in the past
let warnings = calculateCascadeWarnings(targetTime: targetTime, intervals: intervals, now: now)
// 120min and 60min warnings should be marked as fired (they're in the past)
let fired = warnings.filter(\.fired)
XCTAssertEqual(fired.count, 2)
XCTAssertTrue(warnings[0].fired) // 120m
XCTAssertTrue(warnings[1].fired) // 60m
XCTAssertFalse(warnings[2].fired) // 15m still in the future
XCTAssertFalse(warnings[3].fired) // 5m still in the future
}
func testWarningScheduledTimes() {
let now = Date()
let targetTime = now.addingTimeInterval(7200) // 2h from now
let intervals = [60, 30]
let warnings = calculateCascadeWarnings(targetTime: targetTime, intervals: intervals, now: now)
// 60min warning should be at targetTime - 60min
let expected60 = targetTime.addingTimeInterval(-3600)
XCTAssertEqual(warnings[0].scheduledTime.timeIntervalSince1970,
expected60.timeIntervalSince1970, accuracy: 1.0)
// 30min warning should be at targetTime - 30min
let expected30 = targetTime.addingTimeInterval(-1800)
XCTAssertEqual(warnings[1].scheduledTime.timeIntervalSince1970,
expected30.timeIntervalSince1970, accuracy: 1.0)
}
func testEmptyIntervals() {
let now = Date()
let targetTime = now.addingTimeInterval(3600)
let warnings = calculateCascadeWarnings(targetTime: targetTime, intervals: [], now: now)
XCTAssertTrue(warnings.isEmpty)
}
// MARK: - Get Next Warning
func testGetNextWarning() {
let now = Date()
let targetTime = now.addingTimeInterval(3600)
let warnings = calculateCascadeWarnings(
targetTime: targetTime,
intervals: [60, 30, 15, 5],
now: now
)
let next = getNextWarning(warnings)
XCTAssertNotNil(next)
XCTAssertEqual(next?.minutesBefore, 60)
}
func testGetNextWarningAllFired() {
let now = Date()
let targetTime = now.addingTimeInterval(60) // 1 min from now
var warnings = calculateCascadeWarnings(
targetTime: targetTime,
intervals: [120, 60, 30],
now: now
)
// Mark all as fired
for i in warnings.indices { warnings[i].fired = true }
let next = getNextWarning(warnings)
XCTAssertNil(next)
}
// MARK: - Check Warnings
func testCheckWarnings() {
let now = Date()
let targetTime = now.addingTimeInterval(3600)
var warnings = calculateCascadeWarnings(
targetTime: targetTime,
intervals: [60, 30, 15, 5],
now: now
)
// Check at a time when 60m warning should fire (targetTime - 60min = now)
let checkTime = targetTime.addingTimeInterval(-3600) // exactly at 60m warning
let fired = checkWarnings(&warnings, now: checkTime.addingTimeInterval(1)) // 1 second after
// The 60m warning should fire
XCTAssertTrue(fired.count >= 1)
}
func testCheckWarningsNoneFire() {
let now = Date()
let targetTime = now.addingTimeInterval(7200) // 2h from now
var warnings = calculateCascadeWarnings(
targetTime: targetTime,
intervals: [60, 30, 15],
now: now
)
// Check at current time no warnings should fire yet (earliest is in 1h)
let fired = checkWarnings(&warnings, now: now)
XCTAssertTrue(fired.isEmpty)
}
// MARK: - Get Cascade Intervals
func testGetCascadeIntervalsPreset() {
let config = CascadeConfig(preset: .standard, intervals: [])
let intervals = getCascadeIntervals(config)
XCTAssertEqual(intervals, [120, 60, 30, 15, 5])
}
func testGetCascadeIntervalsCustom() {
let config = CascadeConfig(preset: .custom, intervals: [10, 45, 5, 30])
let intervals = getCascadeIntervals(config)
XCTAssertEqual(intervals, [45, 30, 10, 5]) // sorted descending
}
// MARK: - Format Minutes Before
func testFormatMinutesBefore() {
XCTAssertEqual(formatMinutesBefore(5), "5m")
XCTAssertEqual(formatMinutesBefore(15), "15m")
XCTAssertEqual(formatMinutesBefore(60), "1h")
XCTAssertEqual(formatMinutesBefore(90), "1h 30m")
XCTAssertEqual(formatMinutesBefore(120), "2h")
XCTAssertEqual(formatMinutesBefore(240), "4h")
}
}

View File

@ -0,0 +1,112 @@
// Format & Time Blindness Tests
import XCTest
@testable import ChronoMind
final class FormatTests: XCTestCase {
// MARK: - Format Duration
func testFormatDurationZero() {
XCTAssertEqual(formatDuration(0), "00:00")
XCTAssertEqual(formatDuration(-5), "00:00")
}
func testFormatDurationSeconds() {
XCTAssertEqual(formatDuration(30), "00:30")
XCTAssertEqual(formatDuration(59), "00:59")
}
func testFormatDurationMinutes() {
XCTAssertEqual(formatDuration(60), "01:00")
XCTAssertEqual(formatDuration(90), "01:30")
XCTAssertEqual(formatDuration(600), "10:00")
XCTAssertEqual(formatDuration(1500), "25:00")
}
func testFormatDurationHours() {
XCTAssertEqual(formatDuration(3600), "01:00:00")
XCTAssertEqual(formatDuration(5400), "01:30:00")
XCTAssertEqual(formatDuration(7200), "02:00:00")
}
// MARK: - Format Duration Compact
func testFormatDurationCompactZero() {
XCTAssertEqual(formatDurationCompact(0), "0s")
XCTAssertEqual(formatDurationCompact(-1), "0s")
}
func testFormatDurationCompactSeconds() {
XCTAssertEqual(formatDurationCompact(30), "30s")
XCTAssertEqual(formatDurationCompact(59), "59s")
}
func testFormatDurationCompactMinutes() {
XCTAssertEqual(formatDurationCompact(60), "1m")
XCTAssertEqual(formatDurationCompact(150), "2m 30s")
XCTAssertEqual(formatDurationCompact(600), "10m")
XCTAssertEqual(formatDurationCompact(1500), "25m")
}
func testFormatDurationCompactHours() {
XCTAssertEqual(formatDurationCompact(3600), "1h")
XCTAssertEqual(formatDurationCompact(5400), "1h 30m")
XCTAssertEqual(formatDurationCompact(7200), "2h")
}
// MARK: - Format Relative Time
func testFormatRelativeTimeNow() {
let now = Date()
XCTAssertEqual(formatRelativeTime(now, now: now), "now")
XCTAssertEqual(formatRelativeTime(now.addingTimeInterval(10), now: now), "now")
}
func testFormatRelativeTimeFuture() {
let now = Date()
let result = formatRelativeTime(now.addingTimeInterval(300), now: now) // 5 min
XCTAssertTrue(result.hasPrefix("in "))
}
func testFormatRelativeTimePast() {
let now = Date()
let result = formatRelativeTime(now.addingTimeInterval(-300), now: now) // 5 min ago
XCTAssertTrue(result.hasSuffix(" ago"))
}
// MARK: - Time Blindness
func testTimeReferenceZero() {
XCTAssertNil(getTimeReference(minutes: 0))
XCTAssertNil(getTimeReference(minutes: -1))
}
func testTimeReferenceShort() {
let ref = getTimeReference(minutes: 1)
XCTAssertNotNil(ref)
XCTAssertTrue(ref!.contains("deep breath"))
}
func testTimeReferencePomodoro() {
let ref = getTimeReference(minutes: 25)
XCTAssertNotNil(ref)
XCTAssertTrue(ref!.contains("Pomodoro"))
}
func testTimeReferenceLong() {
let ref = getTimeReference(minutes: 90)
XCTAssertNotNil(ref)
XCTAssertTrue(ref!.contains("movie"))
}
func testTimeReferenceTooLong() {
XCTAssertNil(getTimeReference(minutes: 1000))
}
func testTimeReferenceFromSeconds() {
let ref = getTimeReference(seconds: 1500) // 25 min
XCTAssertNotNil(ref)
XCTAssertTrue(ref!.contains("Pomodoro"))
}
}

View File

@ -0,0 +1,307 @@
// Timer Engine Tests
// XCTest unit tests mirroring web Vitest tests
import XCTest
@testable import ChronoMind
final class TimerEngineTests: XCTestCase {
// MARK: - Create Alarm
func testCreateAlarm() {
let target = Date().addingTimeInterval(3600) // 1h from now
let timer = createAlarm(CreateAlarmParams(label: "Test Alarm", targetTime: target))
XCTAssertEqual(timer.type, .alarm)
XCTAssertEqual(timer.label, "Test Alarm")
XCTAssertEqual(timer.urgency, .standard)
XCTAssertEqual(timer.state, .active)
XCTAssertNotNil(timer.startedAt)
XCTAssertEqual(timer.snoozeCount, 0)
XCTAssertFalse(timer.id.isEmpty)
}
func testCreateAlarmWithUrgency() {
let target = Date().addingTimeInterval(7200)
let timer = createAlarm(CreateAlarmParams(
label: "Critical Meeting",
targetTime: target,
urgency: .critical,
cascade: CascadeConfig(preset: .aggressive, intervals: [])
))
XCTAssertEqual(timer.urgency, .critical)
XCTAssertEqual(timer.cascade.preset, .aggressive)
XCTAssertFalse(timer.warnings.isEmpty)
}
// MARK: - Create Countdown
func testCreateCountdown() {
let timer = createCountdown(CreateCountdownParams(
label: "Pasta",
durationSeconds: 600 // 10 min
))
XCTAssertEqual(timer.type, .countdown)
XCTAssertEqual(timer.label, "Pasta")
XCTAssertEqual(timer.duration, 600)
XCTAssertEqual(timer.state, .active)
XCTAssert(timer.targetTime > Date())
}
func testCreateCountdownWithCustomCascade() {
let timer = createCountdown(CreateCountdownParams(
label: "Test",
durationSeconds: 3600,
cascade: CascadeConfig(preset: .custom, intervals: [30, 15, 5])
))
// Custom intervals should produce warnings
let unfired = timer.warnings.filter { !$0.fired }
XCTAssertEqual(unfired.count, 3)
}
// MARK: - Create Pomodoro
func testCreatePomodoro() {
let timer = createPomodoro()
XCTAssertEqual(timer.type, .pomodoro)
XCTAssertEqual(timer.label, "Focus Session")
XCTAssertEqual(timer.state, .active)
XCTAssertEqual(timer.pomodoroConfig?.workMinutes, 25)
XCTAssertEqual(timer.pomodoroConfig?.breakMinutes, 5)
XCTAssertEqual(timer.pomodoroConfig?.rounds, 4)
XCTAssertEqual(timer.pomodoroState?.currentRound, 1)
XCTAssertEqual(timer.pomodoroState?.isBreak, false)
XCTAssertEqual(timer.pomodoroState?.completedRounds, 0)
}
func testCreatePomodoroCustomConfig() {
let config = PomodoroConfig(workMinutes: 50, breakMinutes: 10, longBreakMinutes: 30, rounds: 3)
let timer = createPomodoro(CreatePomodoroParams(
label: "Deep Work",
config: config
))
XCTAssertEqual(timer.label, "Deep Work")
XCTAssertEqual(timer.pomodoroConfig?.workMinutes, 50)
XCTAssertEqual(timer.pomodoroConfig?.rounds, 3)
XCTAssertEqual(timer.duration, 3000) // 50 * 60
}
// MARK: - State Transitions
func testPauseTimer() {
let timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
let paused = pauseTimer(timer)
XCTAssertEqual(paused.state, .paused)
XCTAssertNotNil(paused.pausedAt)
}
func testPauseOnlyActiveOrWarning() {
var timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
timer.state = .dismissed
let result = pauseTimer(timer)
XCTAssertEqual(result.state, .dismissed) // unchanged
}
func testResumeTimer() {
let timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
let paused = pauseTimer(timer)
let resumed = resumeTimer(paused)
XCTAssertEqual(resumed.state, .active)
XCTAssertNil(resumed.pausedAt)
XCTAssertNotNil(resumed.startedAt)
XCTAssert(resumed.targetTime > Date())
}
func testResumeOnlyPaused() {
let timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
let result = resumeTimer(timer) // already active, not paused
XCTAssertEqual(result.state, .active) // unchanged
}
func testFireTimer() {
let timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
let fired = fireTimer(timer)
XCTAssertEqual(fired.state, .firing)
XCTAssertNotNil(fired.firedAt)
}
func testFireIgnoresDismissed() {
var timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
timer = dismissTimer(timer)
let result = fireTimer(timer)
XCTAssertEqual(result.state, .dismissed)
}
func testSnoozeTimer() {
var timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
timer = fireTimer(timer)
let snoozed = snoozeTimer(timer, snoozeMinutes: 5)
XCTAssertEqual(snoozed.state, .snoozed)
XCTAssertEqual(snoozed.snoozeCount, 1)
XCTAssertNotNil(snoozed.snoozedUntil)
// Snooze until should be ~5 minutes from now
let diff = snoozed.snoozedUntil!.timeIntervalSinceNow
XCTAssert(diff > 290 && diff < 310) // ~300 seconds with tolerance
}
func testSnoozeOnlyFiring() {
let timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
let result = snoozeTimer(timer, snoozeMinutes: 5)
XCTAssertEqual(result.state, .active) // unchanged, not firing
}
func testDismissTimer() {
let timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
let dismissed = dismissTimer(timer)
XCTAssertEqual(dismissed.state, .dismissed)
XCTAssertNotNil(dismissed.dismissedAt)
}
func testCompleteTimer() {
let timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
let completed = completeTimer(timer)
XCTAssertEqual(completed.state, .completed)
XCTAssertNotNil(completed.completedAt)
}
// MARK: - Pomodoro Transitions
func testAdvancePomodoroWorkToBreak() {
let timer = createPomodoro()
guard let advanced = advancePomodoro(timer) else {
XCTFail("Should advance")
return
}
XCTAssertEqual(advanced.state, .active)
XCTAssertEqual(advanced.pomodoroState?.isBreak, true)
XCTAssertEqual(advanced.pomodoroState?.completedRounds, 1)
XCTAssertEqual(advanced.duration, 300) // 5 min break
}
func testAdvancePomodoroBreakToWork() {
var timer = createPomodoro()
// Advance to break
timer = advancePomodoro(timer)!
// Advance back to work
guard let next = advancePomodoro(timer) else {
XCTFail("Should advance")
return
}
XCTAssertEqual(next.pomodoroState?.isBreak, false)
XCTAssertEqual(next.pomodoroState?.currentRound, 2)
XCTAssertEqual(next.duration, 1500) // 25 min work
}
func testAdvancePomodoroToLongBreak() {
var timer = createPomodoro()
// Go through all 4 work rounds
for _ in 1...4 {
timer = advancePomodoro(timer)! // work break (or long break)
if timer.pomodoroState?.isLongBreak == true || timer.state == .completed {
break
}
if timer.pomodoroState?.isBreak == true {
timer = advancePomodoro(timer)! // break next work
}
}
// After 4 rounds, should be on long break
XCTAssertTrue(timer.pomodoroState?.isLongBreak == true || timer.state == .completed)
}
func testAdvancePomodoroCompletion() {
var timer = createPomodoro(CreatePomodoroParams(
config: PomodoroConfig(workMinutes: 1, breakMinutes: 1, longBreakMinutes: 1, rounds: 1)
))
// Work long break (only 1 round)
timer = advancePomodoro(timer)!
XCTAssertTrue(timer.pomodoroState?.isLongBreak == true)
// Long break complete
guard let completed = advancePomodoro(timer) else {
XCTFail("Should complete")
return
}
XCTAssertEqual(completed.state, .completed)
}
func testAdvancePomodoroReturnsNilForNonPomodoro() {
let timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
XCTAssertNil(advancePomodoro(timer))
}
// MARK: - Utility Functions
func testGetRemainingSeconds() {
let timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
let remaining = getRemainingSeconds(timer)
XCTAssert(remaining > 598 && remaining <= 600) // within 2 seconds
}
func testGetRemainingSecondsPaused() {
var timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
// Simulate some elapsed time
timer.elapsedBeforePause = 100
timer.state = .paused
let remaining = getRemainingSeconds(timer)
XCTAssertEqual(remaining, 500) // 600 - 100
}
func testIsTimerActive() {
var timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
XCTAssertTrue(isTimerActive(timer))
timer.state = .warning
XCTAssertTrue(isTimerActive(timer))
timer.state = .snoozed
XCTAssertTrue(isTimerActive(timer))
timer.state = .paused
XCTAssertFalse(isTimerActive(timer))
timer.state = .dismissed
XCTAssertFalse(isTimerActive(timer))
}
func testShouldTimerFire() {
var timer = createCountdown(CreateCountdownParams(label: "Test", durationSeconds: 600))
let now = Date()
// Not yet
XCTAssertFalse(shouldTimerFire(timer, now: now))
// Past target time
XCTAssertTrue(shouldTimerFire(timer, now: timer.targetTime.addingTimeInterval(1)))
// Snoozed and past snooze time
timer.state = .snoozed
timer.snoozedUntil = now.addingTimeInterval(-10)
XCTAssertTrue(shouldTimerFire(timer, now: now))
// Snoozed but not past snooze time
timer.snoozedUntil = now.addingTimeInterval(300)
XCTAssertFalse(shouldTimerFire(timer, now: now))
}
}

82
ios/project.yml Normal file
View File

@ -0,0 +1,82 @@
name: ChronoMind
options:
bundleIdPrefix: com.chronomind
deploymentTarget:
iOS: "17.0"
watchOS: "10.0"
macOS: "14.0"
xcodeVersion: "16.0"
createIntermediateGroups: true
generateEmptyDirectories: true
groupSortPosition: top
settings:
base:
SWIFT_VERSION: "5.9"
TARGETED_DEVICE_FAMILY: "1,2"
DEVELOPMENT_TEAM: 748N7QPX7J
CODE_SIGN_STYLE: Automatic
targets:
ChronoMind:
type: application
platform: iOS
deploymentTarget: "17.0"
sources:
- path: ChronoMind
excludes:
- "**/.DS_Store"
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.chronomind.app
INFOPLIST_GENERATION_MODE: GeneratedFile
MARKETING_VERSION: "1.0.0"
CURRENT_PROJECT_VERSION: "1"
GENERATE_INFOPLIST_FILE: true
INFOPLIST_KEY_UIApplicationSceneManifest_Generation: true
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents: true
INFOPLIST_KEY_UILaunchScreen_Generation: true
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad: "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone: "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"
INFOPLIST_KEY_CFBundleDisplayName: ChronoMind
SUPPORTS_MACCATALYST: false
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: false
entitlements:
path: ChronoMind/ChronoMind.entitlements
properties:
com.apple.security.application-groups:
- group.com.chronomind.shared
ChronoMindTests:
type: bundle.unit-test
platform: iOS
deploymentTarget: "17.0"
sources:
- path: ChronoMindTests
excludes:
- "**/.DS_Store"
dependencies:
- target: ChronoMind
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.chronomind.tests
GENERATE_INFOPLIST_FILE: true
schemes:
ChronoMind:
build:
targets:
ChronoMind: all
ChronoMindTests: [test]
run:
config: Debug
test:
config: Debug
targets:
- ChronoMindTests
profile:
config: Release
analyze:
config: Debug
archive:
config: Release

View File

@ -1,7 +1,13 @@
import type { NextConfig } from "next";
import withSerwistInit from "@serwist/next";
const withSerwist = withSerwistInit({
swSrc: "src/app/sw.ts",
swDest: "public/sw.js",
});
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;
export default withSerwist(nextConfig);

586
web/package-lock.json generated
View File

@ -8,12 +8,14 @@
"name": "web",
"version": "0.1.0",
"dependencies": {
"@serwist/next": "^9.5.6",
"date-fns": "^4.1.0",
"idb": "^8.0.3",
"lucide-react": "^0.575.0",
"next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3",
"serwist": "^9.5.6",
"uuid": "^13.0.0",
"zod": "^4.3.6",
"zustand": "^5.0.11"
@ -1672,6 +1674,23 @@
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@isaacs/cliui/-/cliui-8.0.2.tgz",
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"license": "ISC",
"dependencies": {
"string-width": "^5.1.2",
"string-width-cjs": "npm:string-width@^4.2.0",
"strip-ansi": "^7.0.1",
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
"wrap-ansi": "^8.1.0",
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@ -1927,6 +1946,16 @@
"node": ">=12.4.0"
}
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=14"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.59.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
@ -2284,6 +2313,137 @@
"dev": true,
"license": "MIT"
},
"node_modules/@serwist/build": {
"version": "9.5.6",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@serwist/build/-/build-9.5.6.tgz",
"integrity": "sha512-/YUi2BKrvnIkYg8k/PW5N/lAR4N0h/F8eBaqCaDNOy2fdOiNCkvRaWq/ZaoYN5tocvNsMc7OSm7+m1aJqR7trQ==",
"license": "MIT",
"dependencies": {
"@serwist/utils": "9.5.6",
"common-tags": "1.8.2",
"glob": "10.5.0",
"pretty-bytes": "6.1.1",
"source-map": "0.8.0-beta.0",
"zod": "4.3.6"
},
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"typescript": ">=5.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@serwist/next": {
"version": "9.5.6",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@serwist/next/-/next-9.5.6.tgz",
"integrity": "sha512-xObhrC3ctSgLMXeDiAypJr9smetEKTKLd79Z5GrgVzh+xjCIOqsdr2f/FrlzDxKX9SO8TMjRt7BjIjv4RrcOBg==",
"license": "MIT",
"dependencies": {
"@serwist/build": "9.5.6",
"@serwist/utils": "9.5.6",
"@serwist/webpack-plugin": "9.5.6",
"@serwist/window": "9.5.6",
"browserslist": "4.28.1",
"glob": "10.5.0",
"kolorist": "1.8.0",
"semver": "7.7.3",
"serwist": "9.5.6",
"zod": "4.3.6"
},
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"@serwist/cli": "^9.5.6",
"next": ">=14.0.0",
"react": ">=18.0.0",
"typescript": ">=5.0.0"
},
"peerDependenciesMeta": {
"@serwist/cli": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/@serwist/next/node_modules/semver": {
"version": "7.7.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@serwist/utils": {
"version": "9.5.6",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@serwist/utils/-/utils-9.5.6.tgz",
"integrity": "sha512-WV5hAZd/Oo8hyHv7Pd39EDZu3bIhKe0lW39lyMlKVHm5gIGEnPdrH3DojlXAFHiV18nz/bLeqkVo6rK82kBGHw==",
"license": "MIT",
"peerDependencies": {
"browserslist": ">=4"
},
"peerDependenciesMeta": {
"browserslist": {
"optional": true
}
}
},
"node_modules/@serwist/webpack-plugin": {
"version": "9.5.6",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@serwist/webpack-plugin/-/webpack-plugin-9.5.6.tgz",
"integrity": "sha512-kdDqe4AVDJMcS3zTCpV42p+WjJRKb4t0P3flqmceMXfKUDrvhZR3kUWN6yCFi8TLSbHd4hnZZ0cyKa5bCHaa+Q==",
"license": "MIT",
"dependencies": {
"@serwist/build": "9.5.6",
"@serwist/utils": "9.5.6",
"pretty-bytes": "6.1.1",
"zod": "4.3.6"
},
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"typescript": ">=5.0.0",
"webpack": "4.4.0 || ^5.9.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
},
"webpack": {
"optional": true
}
}
},
"node_modules/@serwist/window": {
"version": "9.5.6",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@serwist/window/-/window-9.5.6.tgz",
"integrity": "sha512-/RztZ97HxiEFlDSCpiLd/6nGz3oDQkKMSDF8epJcta7xdUTAZVwMksEodl3x9Y7jyGItF6T/jY7OBCPrN5IVqQ==",
"license": "MIT",
"dependencies": {
"@types/trusted-types": "2.0.7",
"serwist": "9.5.6"
},
"peerDependencies": {
"typescript": ">=5.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@standard-schema/spec": {
"version": "1.1.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@standard-schema/spec/-/spec-1.1.0.tgz",
@ -2746,6 +2906,12 @@
"@types/react": "^19.2.0"
}
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"license": "MIT"
},
"node_modules/@types/uuid": {
"version": "10.0.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/@types/uuid/-/uuid-10.0.0.tgz",
@ -3484,7 +3650,6 @@
"version": "5.0.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@ -3494,7 +3659,6 @@
"version": "4.3.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
@ -3750,7 +3914,6 @@
"version": "1.0.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
@ -3803,7 +3966,6 @@
"version": "4.28.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"dev": true,
"funding": [
{
"type": "opencollective",
@ -3951,7 +4113,6 @@
"version": "2.0.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
@ -3964,9 +4125,17 @@
"version": "1.1.4",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/common-tags": {
"version": "1.8.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/common-tags/-/common-tags-1.8.2.tgz",
"integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
"license": "MIT",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/concat-map/-/concat-map-0.0.1.tgz",
@ -3985,7 +4154,6 @@
"version": "7.0.6",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@ -4258,18 +4426,22 @@
"node": ">= 0.4"
}
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"license": "MIT"
},
"node_modules/electron-to-chromium": {
"version": "1.5.302",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz",
"integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==",
"dev": true,
"license": "ISC"
},
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true,
"license": "MIT"
},
"node_modules/enhanced-resolve": {
@ -4529,7 +4701,6 @@
"version": "3.2.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@ -5141,6 +5312,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/foreground-child": {
"version": "3.3.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/foreground-child/-/foreground-child-3.3.1.tgz",
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
"license": "ISC",
"dependencies": {
"cross-spawn": "^7.0.6",
"signal-exit": "^4.0.1"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/fsevents/-/fsevents-2.3.3.tgz",
@ -5287,6 +5474,27 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/glob": {
"version": "10.5.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/glob/-/glob-10.5.0.tgz",
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^3.1.2",
"minimatch": "^9.0.4",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^1.11.1"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/glob-parent/-/glob-parent-6.0.2.tgz",
@ -5300,6 +5508,30 @@
"node": ">=10.13.0"
}
},
"node_modules/glob/node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/glob/node_modules/minimatch": {
"version": "9.0.9",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/minimatch/-/minimatch-9.0.9.tgz",
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/globals": {
"version": "14.0.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/globals/-/globals-14.0.0.tgz",
@ -5754,6 +5986,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/is-generator-function": {
"version": "1.1.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/is-generator-function/-/is-generator-function-1.1.2.tgz",
@ -6003,7 +6244,6 @@
"version": "2.0.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true,
"license": "ISC"
},
"node_modules/iterator.prototype": {
@ -6024,6 +6264,21 @@
"node": ">= 0.4"
}
},
"node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/jiti": {
"version": "2.6.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/jiti/-/jiti-2.6.1.tgz",
@ -6169,6 +6424,12 @@
"json-buffer": "3.0.1"
}
},
"node_modules/kolorist": {
"version": "1.8.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/kolorist/-/kolorist-1.8.0.tgz",
"integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==",
"license": "MIT"
},
"node_modules/language-subtag-registry": {
"version": "0.3.23",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
@ -6487,6 +6748,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/lodash.sortby": {
"version": "4.7.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
"integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
"license": "MIT"
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/loose-envify/-/loose-envify-1.4.0.tgz",
@ -6613,6 +6880,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/minipass": {
"version": "7.1.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/minipass/-/minipass-7.1.3.tgz",
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/ms/-/ms-2.1.3.tgz",
@ -6765,7 +7041,6 @@
"version": "2.0.27",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/node-releases/-/node-releases-2.0.27.tgz",
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"dev": true,
"license": "MIT"
},
"node_modules/object-assign": {
@ -6970,6 +7245,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/package-json-from-dist": {
"version": "1.0.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"license": "BlueOak-1.0.0"
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/parent-module/-/parent-module-1.0.1.tgz",
@ -7010,7 +7291,6 @@
"version": "3.1.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@ -7023,6 +7303,28 @@
"dev": true,
"license": "MIT"
},
"node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/path-scurry/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/pathe/-/pathe-2.0.3.tgz",
@ -7098,6 +7400,18 @@
"node": ">= 0.8.0"
}
},
"node_modules/pretty-bytes": {
"version": "6.1.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
"integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==",
"license": "MIT",
"engines": {
"node": "^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pretty-format": {
"version": "27.5.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/pretty-format/-/pretty-format-27.5.1.tgz",
@ -7149,7 +7463,6 @@
"version": "2.3.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@ -7479,6 +7792,24 @@
"semver": "bin/semver.js"
}
},
"node_modules/serwist": {
"version": "9.5.6",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/serwist/-/serwist-9.5.6.tgz",
"integrity": "sha512-WoseghF1DUevNGnEqsmyXzVyk1KT18S3CJoFZmzav4vEqGWit+I7ErFav+ocrYq2IUoDhJpbTg15a68UdZy0Vw==",
"license": "MIT",
"dependencies": {
"@serwist/utils": "9.5.6",
"idb": "8.0.3"
},
"peerDependencies": {
"typescript": ">=5.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/set-function-length/-/set-function-length-1.2.2.tgz",
@ -7590,7 +7921,6 @@
"version": "2.0.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@ -7603,7 +7933,6 @@
"version": "3.0.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@ -7692,6 +8021,31 @@
"dev": true,
"license": "ISC"
},
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"license": "ISC",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/source-map": {
"version": "0.8.0-beta.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/source-map/-/source-map-0.8.0-beta.0.tgz",
"integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
"deprecated": "The work that was done in this beta branch won't be included in future versions",
"license": "BSD-3-Clause",
"dependencies": {
"whatwg-url": "^7.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/source-map-js/-/source-map-js-1.2.1.tgz",
@ -7701,6 +8055,32 @@
"node": ">=0.10.0"
}
},
"node_modules/source-map/node_modules/tr46": {
"version": "1.0.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/tr46/-/tr46-1.0.1.tgz",
"integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",
"license": "MIT",
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/source-map/node_modules/webidl-conversions": {
"version": "4.0.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
"license": "BSD-2-Clause"
},
"node_modules/source-map/node_modules/whatwg-url": {
"version": "7.1.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/whatwg-url/-/whatwg-url-7.1.0.tgz",
"integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
"license": "MIT",
"dependencies": {
"lodash.sortby": "^4.7.0",
"tr46": "^1.0.1",
"webidl-conversions": "^4.0.2"
}
},
"node_modules/stable-hash": {
"version": "0.0.5",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/stable-hash/-/stable-hash-0.0.5.tgz",
@ -7736,6 +8116,56 @@
"node": ">= 0.4"
}
},
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/string-width/-/string-width-5.1.2.tgz",
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"license": "MIT",
"dependencies": {
"eastasianwidth": "^0.2.0",
"emoji-regex": "^9.2.2",
"strip-ansi": "^7.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/string-width-cjs": {
"name": "string-width",
"version": "4.2.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/string-width-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/string-width-cjs/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/string.prototype.includes": {
"version": "2.0.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
@ -7849,6 +8279,46 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/strip-ansi": {
"version": "7.2.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/strip-ansi/-/strip-ansi-7.2.0.tgz",
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.2.2"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/strip-ansi-cjs": {
"name": "strip-ansi",
"version": "6.0.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi/node_modules/ansi-regex": {
"version": "6.2.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/ansi-regex/-/ansi-regex-6.2.2.tgz",
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/strip-bom": {
"version": "3.0.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/strip-bom/-/strip-bom-3.0.0.tgz",
@ -8237,7 +8707,7 @@
"version": "5.9.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
@ -8347,7 +8817,6 @@
"version": "1.2.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dev": true,
"funding": [
{
"type": "opencollective",
@ -8403,6 +8872,7 @@
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@ -8647,7 +9117,6 @@
"version": "2.0.2",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@ -8775,6 +9244,85 @@
"node": ">=0.10.0"
}
},
"node_modules/wrap-ansi": {
"version": "8.1.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.1.0",
"string-width": "^5.0.1",
"strip-ansi": "^7.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrap-ansi-cjs": {
"name": "wrap-ansi",
"version": "7.0.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/ansi-styles": {
"version": "6.2.3",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/ansi-styles/-/ansi-styles-6.2.3.tgz",
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/xml-name-validator": {
"version": "5.0.0",
"resolved": "https://jfrog-pkg-proxy.it.att.com/artifactory/api/npm/att-npm-proxy-group/xml-name-validator/-/xml-name-validator-5.0.0.tgz",

View File

@ -12,12 +12,14 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@serwist/next": "^9.5.6",
"date-fns": "^4.1.0",
"idb": "^8.0.3",
"lucide-react": "^0.575.0",
"next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3",
"serwist": "^9.5.6",
"uuid": "^13.0.0",
"zod": "^4.3.6",
"zustand": "^5.0.11"

21
web/src/app/sw.ts Normal file
View File

@ -0,0 +1,21 @@
import { defaultCache } from '@serwist/next/worker';
import type { PrecacheEntry, SerwistGlobalConfig } from 'serwist';
import { Serwist } from 'serwist';
declare global {
interface WorkerGlobalScope extends SerwistGlobalConfig {
__SW_MANIFEST: (PrecacheEntry | string)[] | undefined;
}
}
declare const self: WorkerGlobalScope & typeof globalThis;
const serwist = new Serwist({
precacheEntries: self.__SW_MANIFEST,
skipWaiting: true,
clientsClaim: true,
navigationPreload: true,
runtimeCaching: defaultCache,
});
serwist.addEventListeners();