From bf5ef636566d3e0ecb239f1f354a60107efeda09 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 26 Aug 2022 11:09:25 +0800 Subject: [PATCH 01/37] Remove test code causing iOS slider to be in wrong position when first opened if paused --- apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift index b66552bc9..b67795805 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift @@ -226,7 +226,6 @@ public struct MiniPlayer: View { slider.setThumbImage(image, for: .normal) slider.minimumTrackTintColor = tintColor - slider.value = 10 } HStack { From 112932ecdd496c03c9e2862c8626e45b392bbeb0 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 26 Aug 2022 11:12:35 +0800 Subject: [PATCH 02/37] Dont scale image during drag close animation --- .../Sources/App/Views/AudioPlayer/MiniPlayer.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift index b67795805..03a0f5662 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift @@ -122,10 +122,7 @@ public struct MiniPlayer: View { Group { if let imageURL = item.imageURL { let maxSize = 2 * (min(geom.size.width, geom.size.height) / 3) - let scale = (geom.size.height - offset) / geom.size.height - let dim2 = maxSize * scale - let dim = expanded ? dim2 : 64 - // print("offset", offset, "maxSize", maxSize) + let dim = expanded ? maxSize : 64 AsyncLoadingImage(url: imageURL) { imageStatus in if case let AsyncImageStatus.loaded(image) = imageStatus { From e52013ccb1092944e3398766762a20bf192d9afd Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 26 Aug 2022 11:54:33 +0800 Subject: [PATCH 03/37] It seems to have some issue with disabling puppeteer timeout by setting it to 0. So I set the timeout to 2 minutes which should be enough and it works in my local env --- packages/content-fetch/fetch-content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/content-fetch/fetch-content.js b/packages/content-fetch/fetch-content.js index 6a87f912a..01dbe199c 100644 --- a/packages/content-fetch/fetch-content.js +++ b/packages/content-fetch/fetch-content.js @@ -110,7 +110,7 @@ const getBrowserPromise = (async () => { defaultViewport: { height: 1080, width: 1920 }, executablePath: process.env.CHROMIUM_PATH , headless: !!process.env.LAUNCH_HEADLESS, - timeout: 0, + timeout: 120000, // 2 minutes }); })(); From d186e28b680aeeeda58585c6753a876065bbfb37 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 26 Aug 2022 12:29:01 +0800 Subject: [PATCH 04/37] Use AsyncImage to fix issue with image not updating when playing audio changes --- .../App/Views/AudioPlayer/MiniPlayer.swift | 107 +++++++++--------- 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift index 03a0f5662..dfd125a45 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift @@ -67,46 +67,54 @@ public struct MiniPlayer: View { ) } + var shareButton: some View { + Button( + action: { + let shareActivity = UIActivityViewController(activityItems: [self.audioSession.localAudioUrl], applicationActivities: nil) + if let vc = UIApplication.shared.windows.first?.rootViewController { + shareActivity.popoverPresentationController?.sourceView = vc.view + // Setup share activity position on screen on bottom center + shareActivity.popoverPresentationController?.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height, width: 0, height: 0) + shareActivity.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.down + vc.present(shareActivity, animated: true, completion: nil) + } + }, + label: { + Image(systemName: "square.and.arrow.up") + .font(.appCallout) + .tint(.appGrayText) + } + ) + } + + var closeButton: some View { + Button( + action: { + withAnimation(.interactiveSpring()) { + self.expanded = false + } + }, + label: { + Image(systemName: "chevron.down") + .font(.appCallout) + .tint(.appGrayText) + } + ) + } + // swiftlint:disable:next function_body_length func playerContent(_ item: LinkedItem) -> some View { GeometryReader { geom in VStack { if expanded { ZStack { - Button( - action: { - withAnimation(.interactiveSpring()) { - self.expanded = false - } - }, - label: { - Image(systemName: "chevron.down") - .font(.appCallout) - .tint(.appGrayText) - } - ) - .padding(.top, 8) - .frame(maxWidth: .infinity, alignment: .leading) + closeButton + .padding(.top, 8) + .frame(maxWidth: .infinity, alignment: .leading) - Button( - action: { - let shareActivity = UIActivityViewController(activityItems: [self.audioSession.localAudioUrl], applicationActivities: nil) - if let vc = UIApplication.shared.windows.first?.rootViewController { - shareActivity.popoverPresentationController?.sourceView = vc.view - // Setup share activity position on screen on bottom center - shareActivity.popoverPresentationController?.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height, width: 0, height: 0) - shareActivity.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.down - vc.present(shareActivity, animated: true, completion: nil) - } - }, - label: { - Image(systemName: "square.and.arrow.up") - .font(.appCallout) - .tint(.appGrayText) - } - ) - .padding(.top, 8) - .frame(maxWidth: .infinity, alignment: .trailing) + shareButton + .padding(.top, 8) + .frame(maxWidth: .infinity, alignment: .trailing) Capsule() .fill(.gray) @@ -119,28 +127,19 @@ public struct MiniPlayer: View { Spacer(minLength: 0) HStack { - Group { - if let imageURL = item.imageURL { - let maxSize = 2 * (min(geom.size.width, geom.size.height) / 3) - let dim = expanded ? maxSize : 64 + let maxSize = 2 * (min(geom.size.width, geom.size.height) / 3) + let dim = expanded ? maxSize : 64 - AsyncLoadingImage(url: imageURL) { imageStatus in - if case let AsyncImageStatus.loaded(image) = imageStatus { - image - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: dim, height: dim) - .cornerRadius(6) - .matchedGeometryEffect(id: "ArticleArt", in: animation) - } else if case AsyncImageStatus.loading = imageStatus { - Color.appButtonBackground - .frame(width: dim, height: dim) - .cornerRadius(6) - } else { - EmptyView().frame(width: dim, height: dim, alignment: .top) - } - } - } + AsyncImage(url: item.imageURL) { image in + image + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: dim, height: dim) + .cornerRadius(6) + } placeholder: { + Color.appButtonBackground + .frame(width: dim, height: dim) + .cornerRadius(6) } if !expanded { From 357ed7a8ec0333003c9ef74343ee088bd8c45aae Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Thu, 25 Aug 2022 21:36:58 -0700 Subject: [PATCH 05/37] use async image for profile card and linked item cards --- .../Sources/Views/FeedItem/GridCard.swift | 10 +++++----- .../Views/FeedItem/HomeFeedCardView.swift | 10 +++++----- .../OmnivoreKit/Sources/Views/ProfileCard.swift | 17 +++++------------ 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift index 1e8e46254..2cb04ca89 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift @@ -133,19 +133,19 @@ public struct GridCard: View { Spacer() if let imageURL = item.imageURL { - AsyncLoadingImage(url: imageURL) { imageStatus in - if case let AsyncImageStatus.loaded(image) = imageStatus { + AsyncImage(url: imageURL) { phase in + if let image = phase.image { image .resizable() .aspectRatio(contentMode: .fill) .frame(width: geo.size.width / 3, height: (geo.size.width * 2) / 9) .cornerRadius(3) - } else if case AsyncImageStatus.loading = imageStatus { + } else if phase.error != nil { + EmptyView() + } else { Color.appButtonBackground .frame(width: geo.size.width / 3, height: (geo.size.width * 2) / 9) .cornerRadius(3) - } else { - EmptyView() } } } diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift index f7515393c..daac1fc81 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift @@ -47,19 +47,19 @@ public struct FeedCard: View { Group { if let imageURL = item.imageURL { - AsyncLoadingImage(url: imageURL) { imageStatus in - if case let AsyncImageStatus.loaded(image) = imageStatus { + AsyncImage(url: imageURL) { phase in + if let image = phase.image { image .resizable() .aspectRatio(contentMode: .fill) .frame(width: 80, height: 80) .cornerRadius(6) - } else if case AsyncImageStatus.loading = imageStatus { + } else if phase.error != nil { + EmptyView().frame(width: 80, height: 80, alignment: .top) + } else { Color.appButtonBackground .frame(width: 80, height: 80) .cornerRadius(6) - } else { - EmptyView().frame(width: 80, height: 80, alignment: .top) } } } diff --git a/apple/OmnivoreKit/Sources/Views/ProfileCard.swift b/apple/OmnivoreKit/Sources/Views/ProfileCard.swift index 61583e480..9e923a560 100644 --- a/apple/OmnivoreKit/Sources/Views/ProfileCard.swift +++ b/apple/OmnivoreKit/Sources/Views/ProfileCard.swift @@ -22,18 +22,11 @@ public struct ProfileCard: View { public var body: some View { HStack(alignment: .center) { Group { - if let url = data.imageURL { - AsyncLoadingImage(url: url) { imageStatus in - if case let AsyncImageStatus.loaded(image) = imageStatus { - image.resizable() - } else { - Image(systemName: "person.crop.circle").resizable() - } - } - } else { - Image(systemName: "person.crop.circle") - .resizable() - } + AsyncImage( + url: data.imageURL, + content: { $0.resizable() }, + placeholder: { Image(systemName: "person.crop.circle").resizable() } + ) } .aspectRatio(contentMode: .fill) .frame(width: 70, height: 70, alignment: .center) From b70a8a8585978634360ccf015b040d1fa460eb2b Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Thu, 25 Aug 2022 21:45:33 -0700 Subject: [PATCH 06/37] remove AsyncLoadingImage struct --- .../Share/Views/ShareExtensionView.swift | 10 ++- .../Sources/Views/AsyncLoadingImage.swift | 81 ------------------- 2 files changed, 6 insertions(+), 85 deletions(-) delete mode 100644 apple/OmnivoreKit/Sources/Views/AsyncLoadingImage.swift diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift index ecba3393b..b7b7d39f0 100644 --- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift +++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/Views/ShareExtensionView.swift @@ -73,19 +73,21 @@ public struct ShareExtensionView: View { HStack { if let iconURLStr = viewModel.iconURL, let iconURL = URL(string: iconURLStr) { if !iconURL.isFileURL { - AsyncLoadingImage(url: iconURL) { imageStatus in - if case let AsyncImageStatus.loaded(image) = imageStatus { + AsyncImage( + url: iconURL, + content: { image in image .resizable() .aspectRatio(contentMode: .fill) .frame(width: 61, height: 61) .clipped() - } else { + }, + placeholder: { Color.appButtonBackground .aspectRatio(contentMode: .fill) .frame(width: 61, height: 61) } - } + ) } else { if let localImage = localImage(from: iconURL) { localImage diff --git a/apple/OmnivoreKit/Sources/Views/AsyncLoadingImage.swift b/apple/OmnivoreKit/Sources/Views/AsyncLoadingImage.swift deleted file mode 100644 index b25e732aa..000000000 --- a/apple/OmnivoreKit/Sources/Views/AsyncLoadingImage.swift +++ /dev/null @@ -1,81 +0,0 @@ -import Foundation -import Models -import SwiftUI -import Utils - -public enum AsyncImageStatus { - case loading - case loaded(image: Image) - case error -} - -public struct AsyncLoadingImage: View { - let viewBuilder: (AsyncImageStatus) -> Content - let url: URL - @StateObject private var imageLoader = ImageLoader() - - public init(url: URL, @ViewBuilder viewBuilder: @escaping (AsyncImageStatus) -> Content) { - self.url = url - self.viewBuilder = viewBuilder - } - - public var body: some View { - viewBuilder(imageLoader.status) - .task { await imageLoader.load(fromUrl: url) } - } -} - -@MainActor private final class ImageLoader: ObservableObject { - @Published var status: AsyncImageStatus = .loading - var loadStarted = false - - func load(fromUrl url: URL) async { - guard !loadStarted else { return } - loadStarted = true - - if let cachedImage = ImageCache.shared[url] { - #if os(iOS) - status = .loaded(image: Image(uiImage: cachedImage)) - #else - status = .loaded(image: Image(nsImage: cachedImage)) - #endif - return - } - - if let imageData = try? await fetchImageData(url: url) { - #if os(iOS) - let fetchedImage = UIImage(data: imageData) - #else - let fetchedImage = NSImage(data: imageData) - #endif - - guard let fetchedImage = fetchedImage else { - status = .error - return - } - ImageCache.shared[url] = fetchedImage - - #if os(iOS) - status = .loaded(image: Image(uiImage: fetchedImage)) - #else - status = .loaded(image: Image(nsImage: fetchedImage)) - #endif - } else { - status = .error - } - } -} - -private func fetchImageData(url: URL) async throws -> Data { - do { - let (data, response) = try await URLSession.shared.data(from: url) - - if let httpResponse = response as? HTTPURLResponse, 200 ..< 300 ~= httpResponse.statusCode { - return data - } else { - throw BasicError.message(messageText: "failed") - } - } catch { - throw BasicError.message(messageText: "failed") - } -} From 32711ef657a53ff0970bd606207281a74a62d9c1 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Thu, 25 Aug 2022 21:52:13 -0700 Subject: [PATCH 07/37] remove image cache --- .../Services/AudioSession/AudioSession.swift | 13 ---- .../Sources/Utils/ImageCache.swift | 63 ------------------- 2 files changed, 76 deletions(-) delete mode 100644 apple/OmnivoreKit/Sources/Utils/ImageCache.swift diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioSession.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioSession.swift index 781c481fd..6300379cd 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioSession.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioSession.swift @@ -303,19 +303,6 @@ public class AudioSession: NSObject, ObservableObject, AVAudioPlayerDelegate { ] } -// if let imageURL = item?.imageURL, let cachedImage = ImageCache.shared[imageURL] { - //// #if os(iOS) - //// status = .loaded(image: Image(uiImage: cachedImage)) - //// #else - //// status = .loaded(image: Image(nsImage: cachedImage)) - //// #endif -// MPNowPlayingInfoCenter.default().nowPlayingInfo = [ -// // MPMediaItemPropertyArtwork: cachedImage, -// MPMediaItemPropertyArtist: item?.author ?? "Omnivore", -// MPMediaItemPropertyTitle: item?.title ?? "Your Omnivore Article" -// ] -// } - let commandCenter = MPRemoteCommandCenter.shared() commandCenter.playCommand.isEnabled = true diff --git a/apple/OmnivoreKit/Sources/Utils/ImageCache.swift b/apple/OmnivoreKit/Sources/Utils/ImageCache.swift deleted file mode 100644 index f05003969..000000000 --- a/apple/OmnivoreKit/Sources/Utils/ImageCache.swift +++ /dev/null @@ -1,63 +0,0 @@ -#if os(iOS) - import UIKit - - public typealias PlatformImage = UIImage -#elseif os(macOS) - import AppKit - - public typealias PlatformImage = NSImage -#endif - -/// Reference: https://www.onswiftwings.com/posts/reusable-image-cache/ - -public final class ImageCache { - public static let shared = ImageCache() - - public subscript(_ key: URL) -> PlatformImage? { - get { - image(key) - } - set { - insertImage(newValue, url: key) - } - } - - public func removeAllObjects() { - cache.removeAllObjects() - } - - private let queue = DispatchQueue(label: "app.omnivore.image.cache.queue", attributes: .concurrent) - private let cache = NSCache() - - private init() { - cache.totalCostLimit = 1024 * 1024 * 1024 * 50 // 50 MB - } - - private func image(_ url: URL) -> PlatformImage? { - var cachedImage: PlatformImage? - queue.sync { - cachedImage = cache.object(forKey: NSString(string: url.absoluteString)) - } - return cachedImage - } - - private func insertImage(_ image: PlatformImage?, url: URL) { - guard let image = image else { return } - queue.async(flags: .barrier) { - self.cache.setObject(image, forKey: NSString(string: url.absoluteString), cost: 1) - } - } -} - -private extension PlatformImage { - var diskSize: Int { - #if os(iOS) - guard let cgImage = cgImage else { return 0 } - return cgImage.bytesPerRow * cgImage.height - #elseif os(macOS) - // Instead of calculating the nsimage size just assume 250k - // which will allow for up to 200 images in the cache - (1024 * 1024) / 4 - #endif - } -} From e1d43f236640199e7367230d553f8bd597e03e34 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 26 Aug 2022 13:39:26 +0800 Subject: [PATCH 08/37] Setup new cloud function for the text to speech service --- packages/text-to-speech/.eslintignore | 4 +++ packages/text-to-speech/.eslintrc | 6 ++++ packages/text-to-speech/.gcloudignore | 16 +++++++++ packages/text-to-speech/mocha-config.json | 5 +++ packages/text-to-speech/package.json | 33 +++++++++++++++++++ packages/text-to-speech/src/index.ts | 14 ++++++++ .../text-to-speech/test/babel-register.js | 3 ++ packages/text-to-speech/test/stub.test.ts | 13 ++++++++ packages/text-to-speech/tsconfig.json | 9 +++++ 9 files changed, 103 insertions(+) create mode 100644 packages/text-to-speech/.eslintignore create mode 100644 packages/text-to-speech/.eslintrc create mode 100644 packages/text-to-speech/.gcloudignore create mode 100644 packages/text-to-speech/mocha-config.json create mode 100644 packages/text-to-speech/package.json create mode 100644 packages/text-to-speech/src/index.ts create mode 100644 packages/text-to-speech/test/babel-register.js create mode 100644 packages/text-to-speech/test/stub.test.ts create mode 100644 packages/text-to-speech/tsconfig.json diff --git a/packages/text-to-speech/.eslintignore b/packages/text-to-speech/.eslintignore new file mode 100644 index 000000000..c77bedc74 --- /dev/null +++ b/packages/text-to-speech/.eslintignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +readabilityjs/ +src/generated/ \ No newline at end of file diff --git a/packages/text-to-speech/.eslintrc b/packages/text-to-speech/.eslintrc new file mode 100644 index 000000000..e006282a6 --- /dev/null +++ b/packages/text-to-speech/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": "../../.eslintrc", + "parserOptions": { + "project": "tsconfig.json" + } +} \ No newline at end of file diff --git a/packages/text-to-speech/.gcloudignore b/packages/text-to-speech/.gcloudignore new file mode 100644 index 000000000..ccc4eb240 --- /dev/null +++ b/packages/text-to-speech/.gcloudignore @@ -0,0 +1,16 @@ +# This file specifies files that are *not* uploaded to Google Cloud Platform +# using gcloud. It follows the same syntax as .gitignore, with the addition of +# "#!include" directives (which insert the entries of the given .gitignore-style +# file at that point). +# +# For more information, run: +# $ gcloud topic gcloudignore +# +.gcloudignore +# If you would like to upload your .git directory, .gitignore file or files +# from your .gitignore file, remove the corresponding line +# below: +.git +.gitignore + +node_modules diff --git a/packages/text-to-speech/mocha-config.json b/packages/text-to-speech/mocha-config.json new file mode 100644 index 000000000..44d1d24c1 --- /dev/null +++ b/packages/text-to-speech/mocha-config.json @@ -0,0 +1,5 @@ +{ + "extension": ["ts"], + "spec": "test/**/*.test.ts", + "require": "test/babel-register.js" + } \ No newline at end of file diff --git a/packages/text-to-speech/package.json b/packages/text-to-speech/package.json new file mode 100644 index 000000000..d55567b35 --- /dev/null +++ b/packages/text-to-speech/package.json @@ -0,0 +1,33 @@ +{ + "name": "@omnivore/text-to-speech-handler", + "version": "1.0.0", + "description": "", + "main": "build/src/index.js", + "types": "build/src/index.d.ts", + "files": [ + "build/src" + ], + "license": "Apache-2.0", + "keywords": [], + "scripts": { + "test": "yarn mocha -r ts-node/register --config mocha-config.json", + "lint": "eslint src --ext ts,js,tsx,jsx", + "compile": "tsc", + "build": "tsc", + "start": "functions-framework --source=build/src/ --target=textToSpeechHandler", + "dev": "concurrently \"tsc -w\" \"nodemon --watch ./build/ --exec npm run start\"", + "gcloud-deploy": "gcloud functions deploy textToSpeechHandler --gen2 --trigger-http --allow-unauthenticated --region=$npm_config_region --runtime nodejs14 --env-vars-file=../gcf-shared/env-$npm_config_env.yaml", + "deploy": "yarn build && yarn gcloud-deploy" + }, + "devDependencies": { + "@types/node": "^14.11.2", + "eslint-plugin-prettier": "^4.0.0" + }, + "dependencies": { + "@google-cloud/functions-framework": "3.1.2", + "@google-cloud/pubsub": "^2.18.4", + "@sentry/serverless": "^6.16.1", + "axios": "^0.27.2", + "jsonwebtoken": "^8.5.1" + } +} diff --git a/packages/text-to-speech/src/index.ts b/packages/text-to-speech/src/index.ts new file mode 100644 index 000000000..06317208e --- /dev/null +++ b/packages/text-to-speech/src/index.ts @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import * as Sentry from '@sentry/serverless' +import parseHeaders from 'parse-headers' + +export const textToSpeechHandler = Sentry.GCPFunction.wrapHttpFunction( + async (req, res) => { + console.log('new request', req) + res.send('OK') + } +) diff --git a/packages/text-to-speech/test/babel-register.js b/packages/text-to-speech/test/babel-register.js new file mode 100644 index 000000000..a6f65f60a --- /dev/null +++ b/packages/text-to-speech/test/babel-register.js @@ -0,0 +1,3 @@ +const register = require('@babel/register').default + +register({ extensions: ['.ts', '.tsx', '.js', '.jsx'] }) diff --git a/packages/text-to-speech/test/stub.test.ts b/packages/text-to-speech/test/stub.test.ts new file mode 100644 index 000000000..173ca4917 --- /dev/null +++ b/packages/text-to-speech/test/stub.test.ts @@ -0,0 +1,13 @@ +import 'mocha' +import * as chai from 'chai' +import { expect } from 'chai' +import 'chai/register-should' +import chaiString from 'chai-string' + +chai.use(chaiString) + +describe('Stub test', () => { + it('should pass', () => { + expect(true).to.be.true + }) +}) diff --git a/packages/text-to-speech/tsconfig.json b/packages/text-to-speech/tsconfig.json new file mode 100644 index 000000000..f450acf38 --- /dev/null +++ b/packages/text-to-speech/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@tsconfig/node14/tsconfig.json", + "compilerOptions": { + "outDir": "build", + "rootDir": ".", + "lib": ["dom"] + }, + "include": ["src", "test"] +} From a8617f260595bf9950943872bd96855ef9a8df18 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 26 Aug 2022 14:54:12 +0800 Subject: [PATCH 09/37] Synthesize and upload in cloud function --- packages/text-to-speech/package.json | 5 +- packages/text-to-speech/src/index.ts | 385 ++++++++++++++++++++++++++- yarn.lock | 63 ++++- 3 files changed, 447 insertions(+), 6 deletions(-) diff --git a/packages/text-to-speech/package.json b/packages/text-to-speech/package.json index d55567b35..14ccdd9e3 100644 --- a/packages/text-to-speech/package.json +++ b/packages/text-to-speech/package.json @@ -26,8 +26,11 @@ "dependencies": { "@google-cloud/functions-framework": "3.1.2", "@google-cloud/pubsub": "^2.18.4", + "@google-cloud/storage": "^6.4.1", "@sentry/serverless": "^6.16.1", "axios": "^0.27.2", - "jsonwebtoken": "^8.5.1" + "jsonwebtoken": "^8.5.1", + "linkedom": "^0.14.12", + "microsoft-cognitiveservices-speech-sdk": "^1.22.0" } } diff --git a/packages/text-to-speech/src/index.ts b/packages/text-to-speech/src/index.ts index 06317208e..b10172f92 100644 --- a/packages/text-to-speech/src/index.ts +++ b/packages/text-to-speech/src/index.ts @@ -4,11 +4,390 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import * as Sentry from '@sentry/serverless' -import parseHeaders from 'parse-headers' +import { parseHTML } from 'linkedom' +import { File, Storage } from '@google-cloud/storage' +import { + CancellationDetails, + CancellationReason, + ResultReason, + SpeechConfig, + SpeechSynthesisOutputFormat, + SpeechSynthesisResult, + SpeechSynthesizer, +} from 'microsoft-cognitiveservices-speech-sdk' +import { PubSub } from '@google-cloud/pubsub' + +interface TextToSpeechInput { + id: string + text: string + voice?: string + languageCode?: string + textType?: 'text' | 'ssml' + rate?: number + volume?: number + complimentaryVoice?: string + bucket: string +} + +interface TextToSpeechOutput { + audioFileName: string + speechMarksFileName: string +} + +interface SpeechMark { + time: number + start?: number + length?: number + word: string + type: 'word' | 'bookmark' +} + +const storage = new Storage() +const pubsub = new PubSub() +const SPEECH_UPDATE_TOPIC = 'speech-update' + +const uploadToBucket = async ( + filePath: string, + data: Buffer, + bucket: string, + options?: { contentType?: string; public?: boolean } +): Promise => { + await storage.bucket(bucket).file(filePath).save(data, options) +} + +const createGCSFile = (bucket: string, filename: string): File => { + return storage.bucket(bucket).file(filename) +} + +const updateSpeech = ( + speechId: string, + audioFileName: string, + speechMarksFileName: string +): Promise => { + return pubsub + .topic(SPEECH_UPDATE_TOPIC) + .publishMessage({ json: { speechId, audioFileName, speechMarksFileName } }) + .catch((err) => { + console.error('error publishing speech update:', err) + return undefined + }) +} + +const synthesizeTextToSpeech = async ( + input: TextToSpeechInput +): Promise => { + if (!process.env.azureSpeechKey || !process.env.azureSpeechRegion) { + throw new Error('Azure Speech Key or Region not set') + } + const audioFileName = `speech/${input.id}.mp3` + const audioFile = createGCSFile(input.bucket, audioFileName) + const writeStream = audioFile.createWriteStream({ + resumable: true, + }) + const speechConfig = SpeechConfig.fromSubscription( + process.env.azureSpeechKey, + process.env.azureSpeechRegion + ) + const textType = input.textType || 'text' + if (textType === 'text') { + speechConfig.speechSynthesisLanguage = input.languageCode || 'en-US' + speechConfig.speechSynthesisVoiceName = input.voice || 'en-US-JennyNeural' + } + speechConfig.speechSynthesisOutputFormat = + SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3 + + // Create the speech synthesizer. + const synthesizer = new SpeechSynthesizer(speechConfig) + const speechMarks: SpeechMark[] = [] + let timeOffset = 0 + let characterOffset = 0 + + synthesizer.synthesizing = function (s, e) { + // convert arrayBuffer to stream and write to gcs file + writeStream.write(Buffer.from(e.result.audioData)) + } + + // The event synthesis completed signals that the synthesis is completed. + synthesizer.synthesisCompleted = (s, e) => { + console.info( + `(synthesized) Reason: ${ResultReason[e.result.reason]} Audio length: ${ + e.result.audioData.byteLength + }` + ) + } + + // The synthesis started event signals that the synthesis is started. + synthesizer.synthesisStarted = (s, e) => { + console.info('(synthesis started)') + } + + // The event signals that the service has stopped processing speech. + // This can happen when an error is encountered. + synthesizer.SynthesisCanceled = (s, e) => { + const cancellationDetails = CancellationDetails.fromResult(e.result) + let str = + '(cancel) Reason: ' + CancellationReason[cancellationDetails.reason] + if (cancellationDetails.reason === CancellationReason.Error) { + str += ': ' + e.result.errorDetails + } + console.info(str) + } + + // The unit of e.audioOffset is tick (1 tick = 100 nanoseconds), divide by 10,000 to convert to milliseconds. + synthesizer.wordBoundary = (s, e) => { + speechMarks.push({ + word: e.text, + time: (timeOffset + e.audioOffset) / 10000, + start: characterOffset + e.textOffset, + length: e.wordLength, + type: 'word', + }) + } + + synthesizer.bookmarkReached = (s, e) => { + console.debug( + `(Bookmark reached), Audio offset: ${ + e.audioOffset / 10000 + }ms, bookmark text: ${e.text}` + ) + speechMarks.push({ + word: e.text, + time: (timeOffset + e.audioOffset) / 10000, + type: 'bookmark', + }) + } + + const speakTextAsyncPromise = ( + text: string + ): Promise => { + return new Promise((resolve, reject) => { + synthesizer.speakTextAsync( + text, + (result) => { + resolve(result) + }, + (error) => { + reject(error) + } + ) + }) + } + + const speakSsmlAsyncPromise = ( + text: string + ): Promise => { + return new Promise((resolve, reject) => { + synthesizer.speakSsmlAsync( + text, + (result) => { + resolve(result) + }, + (error) => { + reject(error) + } + ) + }) + } + + if (textType === 'text') { + // slice the text into chunks of 5,000 characters + let currentTextChunk = '' + const textChunks = input.text.split('\n') + for (let i = 0; i < textChunks.length; i++) { + currentTextChunk += textChunks[i] + '\n' + if (currentTextChunk.length < 5000 && i < textChunks.length - 1) { + continue + } + console.debug(`synthesizing ${currentTextChunk}`) + const result = await speakTextAsyncPromise(currentTextChunk) + timeOffset = timeOffset + result.audioDuration + characterOffset = characterOffset + currentTextChunk.length + currentTextChunk = '' + } + } else { + const document = parseHTML(input.text).document + const elements = document.querySelectorAll( + 'h1, h2, h3, p, ul, ol, blockquote' + ) + // convert html elements to the ssml document + for (const e of Array.from(elements)) { + const htmlElement = e as HTMLElement + if (htmlElement.innerText) { + // use complimentary voice for blockquote, hardcoded for now + const voice = + htmlElement.tagName.toLowerCase() === 'blockquote' + ? input.complimentaryVoice || 'en-US-AriaNeural' + : input.voice + const ssml = htmlElementToSsml({ + htmlElement: e, + language: input.languageCode, + rate: input.rate, + volume: input.volume, + voice, + }) + console.debug(`synthesizing ${ssml}`) + const result = await speakSsmlAsyncPromise(ssml) + // if (result.reason === ResultReason.Canceled) { + // synthesizer.close() + // throw new Error(result.errorDetails) + // } + timeOffset = timeOffset + result.audioDuration + // characterOffset = characterOffset + htmlElement.innerText.length + } + } + } + writeStream.end() + synthesizer.close() + + console.debug(`audio file: ${audioFileName}`) + + // upload Speech Marks file to GCS + const speechMarksFileName = `speech/${input.id}.json` + await uploadToBucket( + speechMarksFileName, + Buffer.from(JSON.stringify(speechMarks)), + input.bucket + ) + + return { + audioFileName, + speechMarksFileName, + } +} + +const htmlElementToSsml = ({ + htmlElement, + language = 'en-US', + voice = 'en-US-JennyNeural', + rate = 1, + volume = 100, +}: { + htmlElement: Element + language?: string + voice?: string + rate?: number + volume?: number +}): string => { + const replaceElement = (newElement: Element, oldElement: Element) => { + const id = oldElement.getAttribute('data-omnivore-anchor-idx') + if (id) { + const e = htmlElement.querySelector(`[data-omnivore-anchor-idx="${id}"]`) + e?.parentNode?.replaceChild(newElement, e) + } + } + + const appendBookmarkElement = (parent: Element, element: Element) => { + const id = element.getAttribute('data-omnivore-anchor-idx') + if (id) { + const bookMark = ssml.createElement('bookmark') + bookMark.setAttribute('mark', `data-omnivore-anchor-idx-${id}`) + parent.appendChild(bookMark) + } + } + + const replaceWithEmphasis = (element: Element, level: string) => { + const parent = ssml.createDocumentFragment() as unknown as Element + appendBookmarkElement(parent, element) + const emphasisElement = ssml.createElement('emphasis') + emphasisElement.setAttribute('level', level) + emphasisElement.innerHTML = element.innerHTML.trim() + parent.appendChild(emphasisElement) + replaceElement(parent, element) + } + + const replaceWithSentence = (element: Element) => { + const parent = ssml.createDocumentFragment() as unknown as Element + appendBookmarkElement(parent, element) + const sentenceElement = ssml.createElement('s') + sentenceElement.innerHTML = element.innerHTML.trim() + parent.appendChild(sentenceElement) + replaceElement(parent, element) + } + + // create new ssml document + const ssml = parseHTML('').document + const speakElement = ssml.createElement('speak') + speakElement.setAttribute('version', '1.0') + speakElement.setAttribute('xmlns', 'http://www.w3.org/2001/10/synthesis') + speakElement.setAttribute('xml:lang', language) + const voiceElement = ssml.createElement('voice') + voiceElement.setAttribute('name', voice) + speakElement.appendChild(voiceElement) + const prosodyElement = ssml.createElement('prosody') + prosodyElement.setAttribute('rate', `${rate}`) + prosodyElement.setAttribute('volume', volume.toString()) + voiceElement.appendChild(prosodyElement) + // add each paragraph to the ssml document + appendBookmarkElement(prosodyElement, htmlElement) + // replace emphasis elements with ssml + htmlElement.querySelectorAll('*').forEach((e) => { + switch (e.tagName.toLowerCase()) { + case 's': + replaceWithEmphasis(e, 'moderate') + break + case 'sub': + if (e.getAttribute('alias') === null) { + replaceWithEmphasis(e, 'moderate') + } + break + case 'i': + case 'em': + case 'q': + case 'blockquote': + case 'cite': + case 'del': + case 'strike': + case 'sup': + case 'summary': + case 'caption': + case 'figcaption': + replaceWithEmphasis(e, 'moderate') + break + case 'b': + case 'strong': + case 'dt': + case 'dfn': + case 'u': + case 'mark': + case 'th': + case 'title': + case 'var': + replaceWithEmphasis(e, 'moderate') + break + case 'li': + replaceWithSentence(e) + break + default: { + const parent = ssml.createDocumentFragment() as unknown as Element + appendBookmarkElement(parent, e) + const text = (e as HTMLElement).innerText.trim() + const textElement = ssml.createTextNode(text) + parent.appendChild(textElement) + replaceElement(parent, e) + } + } + }) + prosodyElement.appendChild(htmlElement) + + return speakElement.outerHTML.replace(/ |\n/g, '') +} export const textToSpeechHandler = Sentry.GCPFunction.wrapHttpFunction( async (req, res) => { - console.log('new request', req) - res.send('OK') + console.debug('New text to speech request', req) + const input = req.body as TextToSpeechInput + const { audioFileName, speechMarksFileName } = await synthesizeTextToSpeech( + input + ) + const result = await updateSpeech( + input.id, + audioFileName, + speechMarksFileName + ) + + if (!result) { + return res.status(500).send('Failed to update speech') + } + res.send('OK') } ) diff --git a/yarn.lock b/yarn.lock index e03b59cf2..b8a67aeb9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2458,7 +2458,7 @@ resolved "https://registry.yarnpkg.com/@google-cloud/opentelemetry-resource-util/-/opentelemetry-resource-util-1.1.0.tgz#0bd1fe708ba27288f6efc9712fbd3705fd325540" integrity sha512-AXfQiqIxeespEYcRNaotC05ddiy2Vgk2yqY73b7Hl1UoJ75Gt4kSRcswrVn18eoDI0YQkSTBh7Ye9ugfFLN5HA== -"@google-cloud/paginator@^3.0.0", "@google-cloud/paginator@^3.0.6": +"@google-cloud/paginator@^3.0.0", "@google-cloud/paginator@^3.0.6", "@google-cloud/paginator@^3.0.7": version "3.0.7" resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-3.0.7.tgz#fb6f8e24ec841f99defaebf62c75c2e744dd419b" integrity sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ== @@ -2548,6 +2548,30 @@ stream-events "^1.0.4" xdg-basedir "^4.0.0" +"@google-cloud/storage@^6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@google-cloud/storage/-/storage-6.4.1.tgz#83334150d4e224cb48691de4d7f9c38e143a0970" + integrity sha512-lAddmRJ8tvxPykUqJfONBQA5XGwGk0vut1POXublc64+nCdB5aQMxwuBMf7J1zubx19QGpYPQwW6wR7YTWrvLw== + dependencies: + "@google-cloud/paginator" "^3.0.7" + "@google-cloud/projectify" "^3.0.0" + "@google-cloud/promisify" "^3.0.0" + abort-controller "^3.0.0" + arrify "^2.0.0" + async-retry "^1.3.3" + compressible "^2.0.12" + duplexify "^4.0.0" + ent "^2.2.0" + extend "^3.0.2" + gaxios "^5.0.0" + google-auth-library "^8.0.1" + mime "^3.0.0" + mime-types "^2.0.8" + p-limit "^3.0.1" + retry-request "^5.0.0" + teeny-request "^8.0.0" + uuid "^8.0.0" + "@google-cloud/tasks@^2.3.0": version "2.5.0" resolved "https://registry.yarnpkg.com/@google-cloud/tasks/-/tasks-2.5.0.tgz#e6c2598038001550c408845e91570d176c18a25a" @@ -14122,7 +14146,7 @@ gaxios@^4.0.0: is-stream "^2.0.0" node-fetch "^2.3.0" -gaxios@^5.0.0: +gaxios@^5.0.0, gaxios@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.0.1.tgz#50fc76a2d04bc1700ed8c3ff1561e52255dfc6e0" integrity sha512-keK47BGKHyyOVQxgcUaSaFvr3ehZYAlvhvpHXy0YB2itzZef+GqZR8TBsfVRWghdwlKrYsn+8L8i3eblF7Oviw== @@ -14512,6 +14536,21 @@ google-auth-library@^7.0.0, google-auth-library@^7.6.1, google-auth-library@^7.9 jws "^4.0.0" lru-cache "^6.0.0" +google-auth-library@^8.0.1: + version "8.4.0" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.4.0.tgz#3a5414344bb313ee64ceeef1f7e5162cc1fdf04b" + integrity sha512-cg/usxyQEmq4PPDBQRt+kGIrfL3k+mOrAoS9Xv1hitQL66AoY7iWvRBcYo3Rb0w4V1t9e/GqW2/D4honlAtMDg== + dependencies: + arrify "^2.0.0" + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + fast-text-encoding "^1.0.0" + gaxios "^5.0.0" + gcp-metadata "^5.0.0" + gtoken "^6.1.0" + jws "^4.0.0" + lru-cache "^6.0.0" + google-auth-library@^8.0.2: version "8.1.0" resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.1.0.tgz#879e8d2e90a9d47e6eab32fd1d5fd9ed52d7d441" @@ -14744,6 +14783,15 @@ gtoken@^6.0.0: google-p12-pem "^4.0.0" jws "^4.0.0" +gtoken@^6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-6.1.1.tgz#29ebf3e6893719176d180f5694f1cad525ce3c04" + integrity sha512-HPM4VzzPEGxjQ7T2xLrdSYBs+h1c0yHAUiN+8RHPDoiZbndlpg9Sx3SjWcrTt9+N3FHsSABEpjvdQVan5AAuZQ== + dependencies: + gaxios "^5.0.1" + google-p12-pem "^4.0.0" + jws "^4.0.0" + gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -17490,6 +17538,17 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= +linkedom@^0.14.12: + version "0.14.12" + resolved "https://registry.yarnpkg.com/linkedom/-/linkedom-0.14.12.tgz#3b19442e41de33a9ef9b035ccdd97bf5b66c77e1" + integrity sha512-8uw8LZifCwyWeVWr80T79sQTMmNXt4Da7oN5yH5gTXRqQM+TuZWJyBqRMcIp32zx/f8anHNHyil9Avw9y76ziQ== + dependencies: + css-select "^5.1.0" + cssom "^0.5.0" + html-escaper "^3.0.3" + htmlparser2 "^8.0.1" + uhyphen "^0.1.0" + linkedom@^0.14.9: version "0.14.9" resolved "https://registry.yarnpkg.com/linkedom/-/linkedom-0.14.9.tgz#34c6f15eddc809406f42d8ee48cd30b0222eccb0" From 5a1dbb594e4f6a10203106329338597bf1a438b7 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 26 Aug 2022 15:41:06 +0800 Subject: [PATCH 10/37] Call backend api to update speech in db --- .../api/src/routers/svc/text_to_speech.ts | 59 ++++++------------- packages/text-to-speech/package.json | 1 - packages/text-to-speech/src/index.ts | 50 +++++++++++----- 3 files changed, 53 insertions(+), 57 deletions(-) diff --git a/packages/api/src/routers/svc/text_to_speech.ts b/packages/api/src/routers/svc/text_to_speech.ts index 8f597c652..61098e86f 100644 --- a/packages/api/src/routers/svc/text_to_speech.ts +++ b/packages/api/src/routers/svc/text_to_speech.ts @@ -9,11 +9,7 @@ import { getPageById } from '../../elastic/pages' import { Speech, SpeechState } from '../../entity/speech' import { buildLogger } from '../../utils/logger' import { getClaimsByToken } from '../../utils/auth' -import { - setSpeechFailure, - shouldSynthesize, - synthesize, -} from '../../services/speech' +import { shouldSynthesize, synthesize } from '../../services/speech' import { readPushSubscription } from '../../datalayer/pubsub' const logger = buildLogger('app.dispatch') @@ -79,58 +75,41 @@ export function speechServiceRouter() { router.options('/', cors({ ...corsConfig, maxAge: 600 })) // eslint-disable-next-line @typescript-eslint/no-misused-promises router.post('/', async (req, res) => { - logger.info('Synthesize svc request', { + logger.info('Updating speech', { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment body: req.body, }) + let userId: string const token = req.query.token as string try { - if (!(await getClaimsByToken(token))) { + const claims = await getClaimsByToken(token) + if (!claims) { logger.info('Unauthorized request', { token }) - return res.status(200).send('UNAUTHORIZED') + return res.status(401).send('UNAUTHORIZED') } + userId = claims.uid } catch (error) { logger.error('Unauthorized request', { token, error }) - return res.status(200).send('UNAUTHORIZED') + return res.status(401).send('UNAUTHORIZED') } - const { userId, speechId } = req.body as { - userId: string + const { speechId, audioFileName, speechMarksFileName } = req.body as { speechId: string + audioFileName: string + speechMarksFileName: string } - if (!userId || !speechId) { - return res.status(200).send('Invalid data') + if (!speechId || !audioFileName || !speechMarksFileName) { + return res.status(400).send('Invalid data') } - logger.info(`Create article speech`, { - body: { - userId, - speechId, - }, - labels: { - source: 'CreateArticleSpeech', - }, + // set state to completed + await getRepository(Speech).update(speechId, { + audioFileName: audioFileName, + speechMarksFileName: speechMarksFileName, + state: SpeechState.COMPLETED, }) - const speech = await getRepository(Speech).findOneBy({ - id: speechId, - user: { id: userId }, - }) - if (!speech) { - return res.status(200).send('Speech not found') - } - const page = await getPageById(speech.elasticPageId) - if (!page) { - await setSpeechFailure(speech.id) - return res.status(200).send('Page not found') - } - - try { - await synthesize(page, speech) - } catch (error) { - logger.error(`Error synthesizing article`, { error }) - res.status(500).send('Error synthesizing article') - } + res.send('OK') }) return router diff --git a/packages/text-to-speech/package.json b/packages/text-to-speech/package.json index 14ccdd9e3..3e3901bc4 100644 --- a/packages/text-to-speech/package.json +++ b/packages/text-to-speech/package.json @@ -25,7 +25,6 @@ }, "dependencies": { "@google-cloud/functions-framework": "3.1.2", - "@google-cloud/pubsub": "^2.18.4", "@google-cloud/storage": "^6.4.1", "@sentry/serverless": "^6.16.1", "axios": "^0.27.2", diff --git a/packages/text-to-speech/src/index.ts b/packages/text-to-speech/src/index.ts index b10172f92..ec3f8403a 100644 --- a/packages/text-to-speech/src/index.ts +++ b/packages/text-to-speech/src/index.ts @@ -15,7 +15,8 @@ import { SpeechSynthesisResult, SpeechSynthesizer, } from 'microsoft-cognitiveservices-speech-sdk' -import { PubSub } from '@google-cloud/pubsub' +import axios from 'axios' +import * as jwt from 'jsonwebtoken' interface TextToSpeechInput { id: string @@ -43,8 +44,6 @@ interface SpeechMark { } const storage = new Storage() -const pubsub = new PubSub() -const SPEECH_UPDATE_TOPIC = 'speech-update' const uploadToBucket = async ( filePath: string, @@ -59,18 +58,25 @@ const createGCSFile = (bucket: string, filename: string): File => { return storage.bucket(bucket).file(filename) } -const updateSpeech = ( +const updateSpeech = async ( speechId: string, audioFileName: string, - speechMarksFileName: string -): Promise => { - return pubsub - .topic(SPEECH_UPDATE_TOPIC) - .publishMessage({ json: { speechId, audioFileName, speechMarksFileName } }) - .catch((err) => { - console.error('error publishing speech update:', err) - return undefined - }) + speechMarksFileName: string, + token: string +): Promise => { + if (!process.env.REST_BACKEND_ENDPOINT) { + throw new Error('backend rest api endpoint not exists') + } + const response = await axios.post( + `${process.env.REST_BACKEND_ENDPOINT}/svc/text-to-speech?token=${token}`, + { + speechId, + audioFileName, + speechMarksFileName, + } + ) + + return response.status === 200 } const synthesizeTextToSpeech = async ( @@ -375,17 +381,29 @@ const htmlElementToSsml = ({ export const textToSpeechHandler = Sentry.GCPFunction.wrapHttpFunction( async (req, res) => { console.debug('New text to speech request', req) + const token = req.query.token as string + if (!process.env.JWT_SECRET) { + console.error('JWT_SECRET not exists') + return res.status(500).send('JWT_SECRET not exists') + } + try { + jwt.verify(token, process.env.JWT_SECRET) + } catch (e) { + console.error(e) + return res.status(200).send('UNAUTHENTICATED') + } const input = req.body as TextToSpeechInput const { audioFileName, speechMarksFileName } = await synthesizeTextToSpeech( input ) - const result = await updateSpeech( + const updated = await updateSpeech( input.id, audioFileName, - speechMarksFileName + speechMarksFileName, + token ) - if (!result) { + if (!updated) { return res.status(500).send('Failed to update speech') } res.send('OK') From b2d49d1fbf1135b858c7d065c86e3deb3d4d6759 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 26 Aug 2022 16:30:09 +0800 Subject: [PATCH 11/37] Add dotenv --- packages/api/src/routers/article_router.ts | 9 ++++- packages/api/src/utils/createTask.ts | 13 +++++-- packages/text-to-speech/package.json | 1 + packages/text-to-speech/src/index.ts | 45 ++++++++++++++-------- yarn.lock | 5 +++ 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/packages/api/src/routers/article_router.ts b/packages/api/src/routers/article_router.ts index 7915abc4f..68e804071 100644 --- a/packages/api/src/routers/article_router.ts +++ b/packages/api/src/routers/article_router.ts @@ -153,7 +153,14 @@ export function articleRouter() { voice: voice || userPersonalization?.speechVoice || 'en-US-JennyNeural', }) // enqueue a task to convert text to speech - const taskName = await enqueueTextToSpeech(uid, speech.id) + const taskName = await enqueueTextToSpeech( + uid, + speech.id, + page.content, + 'ssml', + speech.voice, + env.fileUpload.gcsUploadBucket + ) logger.info('Start Text to speech task', { taskName }) res.status(202).send('Text to speech task started') } diff --git a/packages/api/src/utils/createTask.ts b/packages/api/src/utils/createTask.ts index fc6a858cb..ddc21be2b 100644 --- a/packages/api/src/utils/createTask.ts +++ b/packages/api/src/utils/createTask.ts @@ -330,12 +330,19 @@ export const enqueueSyncWithIntegration = async ( export const enqueueTextToSpeech = async ( userId: string, - speechId: string + speechId: string, + text: string, + textType: 'text' | 'ssml', + voice: string, + bucket: string ): Promise => { const { GOOGLE_CLOUD_PROJECT } = process.env const payload = { - userId, - speechId, + id: speechId, + text, + voice, + bucket, + textType, } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore diff --git a/packages/text-to-speech/package.json b/packages/text-to-speech/package.json index 3e3901bc4..8743a9d88 100644 --- a/packages/text-to-speech/package.json +++ b/packages/text-to-speech/package.json @@ -28,6 +28,7 @@ "@google-cloud/storage": "^6.4.1", "@sentry/serverless": "^6.16.1", "axios": "^0.27.2", + "dotenv": "^16.0.1", "jsonwebtoken": "^8.5.1", "linkedom": "^0.14.12", "microsoft-cognitiveservices-speech-sdk": "^1.22.0" diff --git a/packages/text-to-speech/src/index.ts b/packages/text-to-speech/src/index.ts index ec3f8403a..73a9e6a58 100644 --- a/packages/text-to-speech/src/index.ts +++ b/packages/text-to-speech/src/index.ts @@ -17,6 +17,8 @@ import { } from 'microsoft-cognitiveservices-speech-sdk' import axios from 'axios' import * as jwt from 'jsonwebtoken' +import * as dotenv from 'dotenv' // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import +dotenv.config() interface TextToSpeechInput { id: string @@ -60,9 +62,10 @@ const createGCSFile = (bucket: string, filename: string): File => { const updateSpeech = async ( speechId: string, - audioFileName: string, - speechMarksFileName: string, - token: string + token: string, + state: 'COMPLETED' | 'FAILED', + audioFileName?: string, + speechMarksFileName?: string ): Promise => { if (!process.env.REST_BACKEND_ENDPOINT) { throw new Error('backend rest api endpoint not exists') @@ -73,6 +76,7 @@ const updateSpeech = async ( speechId, audioFileName, speechMarksFileName, + state, } ) @@ -82,7 +86,7 @@ const updateSpeech = async ( const synthesizeTextToSpeech = async ( input: TextToSpeechInput ): Promise => { - if (!process.env.azureSpeechKey || !process.env.azureSpeechRegion) { + if (!process.env.AZURE_SPEECH_KEY || !process.env.AZURE_SPEECH_REGION) { throw new Error('Azure Speech Key or Region not set') } const audioFileName = `speech/${input.id}.mp3` @@ -91,8 +95,8 @@ const synthesizeTextToSpeech = async ( resumable: true, }) const speechConfig = SpeechConfig.fromSubscription( - process.env.azureSpeechKey, - process.env.azureSpeechRegion + process.env.AZURE_SPEECH_KEY, + process.env.AZURE_SPEECH_REGION ) const textType = input.textType || 'text' if (textType === 'text') { @@ -393,19 +397,26 @@ export const textToSpeechHandler = Sentry.GCPFunction.wrapHttpFunction( return res.status(200).send('UNAUTHENTICATED') } const input = req.body as TextToSpeechInput - const { audioFileName, speechMarksFileName } = await synthesizeTextToSpeech( - input - ) - const updated = await updateSpeech( - input.id, - audioFileName, - speechMarksFileName, - token - ) + try { + const { audioFileName, speechMarksFileName } = + await synthesizeTextToSpeech(input) + const updated = await updateSpeech( + input.id, + token, + 'COMPLETED', + audioFileName, + speechMarksFileName + ) - if (!updated) { - return res.status(500).send('Failed to update speech') + if (!updated) { + return res.status(500).send('Failed to update speech') + } + } catch (e) { + console.error(e) + await updateSpeech(input.id, token, 'FAILED') + return res.status(500).send('Failed to synthesize') } + res.send('OK') } ) diff --git a/yarn.lock b/yarn.lock index b8a67aeb9..6cbf70c34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12474,6 +12474,11 @@ dotenv@^16.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411" integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== +dotenv@^16.0.1: + version "16.0.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d" + integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ== + dotenv@^8.0.0, dotenv@^8.2.0: version "8.6.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" From 5201f5d36000a87e7432f114ba1db275f7215c4e Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 26 Aug 2022 16:56:33 +0800 Subject: [PATCH 12/37] Add RLS to speech table --- .../api/src/routers/svc/text_to_speech.ts | 28 +++++++++++-------- .../migrations/0095.do.add_rls_to_speech.sql | 11 ++++++++ .../0095.undo.add_rls_to_speech.sql | 9 ++++++ 3 files changed, 37 insertions(+), 11 deletions(-) create mode 100755 packages/db/migrations/0095.do.add_rls_to_speech.sql create mode 100755 packages/db/migrations/0095.undo.add_rls_to_speech.sql diff --git a/packages/api/src/routers/svc/text_to_speech.ts b/packages/api/src/routers/svc/text_to_speech.ts index 61098e86f..b7ec7ac1f 100644 --- a/packages/api/src/routers/svc/text_to_speech.ts +++ b/packages/api/src/routers/svc/text_to_speech.ts @@ -4,13 +4,14 @@ import express from 'express' import cors from 'cors' import { corsConfig } from '../../utils/corsConfig' -import { getRepository } from '../../entity/utils' +import { getRepository, setClaims } from '../../entity/utils' import { getPageById } from '../../elastic/pages' import { Speech, SpeechState } from '../../entity/speech' import { buildLogger } from '../../utils/logger' import { getClaimsByToken } from '../../utils/auth' import { shouldSynthesize, synthesize } from '../../services/speech' import { readPushSubscription } from '../../datalayer/pubsub' +import { AppDataSource } from '../../server' const logger = buildLogger('app.dispatch') @@ -93,20 +94,25 @@ export function speechServiceRouter() { return res.status(401).send('UNAUTHORIZED') } - const { speechId, audioFileName, speechMarksFileName } = req.body as { - speechId: string - audioFileName: string - speechMarksFileName: string - } - if (!speechId || !audioFileName || !speechMarksFileName) { + const { speechId, audioFileName, speechMarksFileName, state } = + req.body as { + speechId: string + audioFileName: string + speechMarksFileName: string + state: SpeechState + } + if (!speechId) { return res.status(400).send('Invalid data') } // set state to completed - await getRepository(Speech).update(speechId, { - audioFileName: audioFileName, - speechMarksFileName: speechMarksFileName, - state: SpeechState.COMPLETED, + await AppDataSource.transaction(async (t) => { + await setClaims(t, userId) + await t.getRepository(Speech).update(speechId, { + audioFileName: audioFileName, + speechMarksFileName: speechMarksFileName, + state, + }) }) res.send('OK') diff --git a/packages/db/migrations/0095.do.add_rls_to_speech.sql b/packages/db/migrations/0095.do.add_rls_to_speech.sql new file mode 100755 index 000000000..9408ef84e --- /dev/null +++ b/packages/db/migrations/0095.do.add_rls_to_speech.sql @@ -0,0 +1,11 @@ +-- Type: DO +-- Name: add_rls_to_speech +-- Description: Add Row level security to speech table + +BEGIN; + +CREATE POLICY update_speech on omnivore.speech + FOR UPDATE TO omnivore_user + USING (user_id = omnivore.get_current_user_id()); + +COMMIT; diff --git a/packages/db/migrations/0095.undo.add_rls_to_speech.sql b/packages/db/migrations/0095.undo.add_rls_to_speech.sql new file mode 100755 index 000000000..e81f2dbfc --- /dev/null +++ b/packages/db/migrations/0095.undo.add_rls_to_speech.sql @@ -0,0 +1,9 @@ +-- Type: UNDO +-- Name: add_rls_to_speech +-- Description: Add Row level security to speech table + +BEGIN; + +DROP POLICY IF EXISTS update_speech ON omnivore.speech; + +COMMIT; From 050a2a28066cd1247af1dafd464b4c32eb49fba3 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 26 Aug 2022 17:05:14 +0800 Subject: [PATCH 13/37] Update deploy command --- packages/text-to-speech/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/text-to-speech/package.json b/packages/text-to-speech/package.json index 8743a9d88..fa9db0f24 100644 --- a/packages/text-to-speech/package.json +++ b/packages/text-to-speech/package.json @@ -16,7 +16,7 @@ "build": "tsc", "start": "functions-framework --source=build/src/ --target=textToSpeechHandler", "dev": "concurrently \"tsc -w\" \"nodemon --watch ./build/ --exec npm run start\"", - "gcloud-deploy": "gcloud functions deploy textToSpeechHandler --gen2 --trigger-http --allow-unauthenticated --region=$npm_config_region --runtime nodejs14 --env-vars-file=../gcf-shared/env-$npm_config_env.yaml", + "gcloud-deploy": "gcloud functions deploy text-to-speech --gen2 --trigger-http --allow-unauthenticated --region=us-west2 --runtime nodejs14", "deploy": "yarn build && yarn gcloud-deploy" }, "devDependencies": { From 7a9eca2622bde97d41805035c36b7a45b5fecaf9 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 26 Aug 2022 17:53:26 +0800 Subject: [PATCH 14/37] Update text-to-speech endpoint --- .../src/routers/{svc => }/text_to_speech.ts | 20 +++++++++---------- packages/api/src/server.ts | 4 ++-- packages/text-to-speech/src/index.ts | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) rename packages/api/src/routers/{svc => }/text_to_speech.ts (86%) diff --git a/packages/api/src/routers/svc/text_to_speech.ts b/packages/api/src/routers/text_to_speech.ts similarity index 86% rename from packages/api/src/routers/svc/text_to_speech.ts rename to packages/api/src/routers/text_to_speech.ts index b7ec7ac1f..70e636e0b 100644 --- a/packages/api/src/routers/svc/text_to_speech.ts +++ b/packages/api/src/routers/text_to_speech.ts @@ -3,19 +3,19 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import express from 'express' import cors from 'cors' -import { corsConfig } from '../../utils/corsConfig' -import { getRepository, setClaims } from '../../entity/utils' -import { getPageById } from '../../elastic/pages' -import { Speech, SpeechState } from '../../entity/speech' -import { buildLogger } from '../../utils/logger' -import { getClaimsByToken } from '../../utils/auth' -import { shouldSynthesize, synthesize } from '../../services/speech' -import { readPushSubscription } from '../../datalayer/pubsub' -import { AppDataSource } from '../../server' +import { corsConfig } from '../utils/corsConfig' +import { getRepository, setClaims } from '../entity/utils' +import { getPageById } from '../elastic/pages' +import { Speech, SpeechState } from '../entity/speech' +import { buildLogger } from '../utils/logger' +import { getClaimsByToken } from '../utils/auth' +import { shouldSynthesize, synthesize } from '../services/speech' +import { readPushSubscription } from '../datalayer/pubsub' +import { AppDataSource } from '../server' const logger = buildLogger('app.dispatch') -export function speechServiceRouter() { +export function textToSpeechRouter() { const router = express.Router() // eslint-disable-next-line @typescript-eslint/no-misused-promises diff --git a/packages/api/src/server.ts b/packages/api/src/server.ts index bc82e067f..62dbc8d40 100755 --- a/packages/api/src/server.ts +++ b/packages/api/src/server.ts @@ -45,7 +45,7 @@ import { uploadServiceRouter } from './routers/svc/upload' import rateLimit from 'express-rate-limit' import { webhooksServiceRouter } from './routers/svc/webhooks' import { integrationsServiceRouter } from './routers/svc/integrations' -import { speechServiceRouter } from './routers/svc/text_to_speech' +import { textToSpeechRouter } from './routers/text_to_speech' const PORT = process.env.PORT || 4000 @@ -111,6 +111,7 @@ export const createApp = (): { app.use('/api/page', pageRouter()) app.use('/api/article', articleRouter()) app.use('/api/mobile-auth', mobileAuthRouter()) + app.use('/api/text-to-speech', textToSpeechRouter()) app.use('/svc/pubsub/content', contentServiceRouter()) app.use('/svc/pubsub/links', linkServiceRouter()) app.use('/svc/pubsub/newsletters', newsletterServiceRouter()) @@ -120,7 +121,6 @@ export const createApp = (): { app.use('/svc/pubsub/integrations', integrationsServiceRouter()) app.use('/svc/reminders', remindersServiceRouter()) app.use('/svc/pdf-attachments', pdfAttachmentsRouter()) - app.use('/svc/text-to-speech', speechServiceRouter()) if (env.dev.isLocal) { app.use('/local/debug', localDebugRouter()) diff --git a/packages/text-to-speech/src/index.ts b/packages/text-to-speech/src/index.ts index 73a9e6a58..28833d879 100644 --- a/packages/text-to-speech/src/index.ts +++ b/packages/text-to-speech/src/index.ts @@ -71,7 +71,7 @@ const updateSpeech = async ( throw new Error('backend rest api endpoint not exists') } const response = await axios.post( - `${process.env.REST_BACKEND_ENDPOINT}/svc/text-to-speech?token=${token}`, + `${process.env.REST_BACKEND_ENDPOINT}/text-to-speech?token=${token}`, { speechId, audioFileName, From 3d16b1d0b4a0ecddac5d9099ef4657f9d9f09ba9 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Sun, 28 Aug 2022 09:36:30 -0700 Subject: [PATCH 15/37] add redirect uri for android apple login --- .../api/src/routers/auth/mobile/mobile_auth_router.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/api/src/routers/auth/mobile/mobile_auth_router.ts b/packages/api/src/routers/auth/mobile/mobile_auth_router.ts index 50890aaab..7bd0c20e9 100644 --- a/packages/api/src/routers/auth/mobile/mobile_auth_router.ts +++ b/packages/api/src/routers/auth/mobile/mobile_auth_router.ts @@ -11,6 +11,7 @@ import { createMobileEmailSignUpResponse, } from './sign_up' import { createMobileAccountCreationResponse } from './account_creation' +import { env } from '../../../env' export function mobileAuthRouter() { const router = express.Router() @@ -60,5 +61,12 @@ export function mobileAuthRouter() { res.status(payload.statusCode).json(payload.json) }) + router.post('/android-apple-redirect', (req, res) => { + const { id_token } = req.body + return res.redirect( + `${env.client.url}/android-apple-token?token=${id_token as string}` + ) + }) + return router } From a720d5a5d58df0339bee7807338eea1e15046720 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Sun, 28 Aug 2022 09:48:39 -0700 Subject: [PATCH 16/37] set cors config for android web redirect --- .../api/src/routers/auth/mobile/mobile_auth_router.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/api/src/routers/auth/mobile/mobile_auth_router.ts b/packages/api/src/routers/auth/mobile/mobile_auth_router.ts index 7bd0c20e9..c43df9bc8 100644 --- a/packages/api/src/routers/auth/mobile/mobile_auth_router.ts +++ b/packages/api/src/routers/auth/mobile/mobile_auth_router.ts @@ -12,6 +12,8 @@ import { } from './sign_up' import { createMobileAccountCreationResponse } from './account_creation' import { env } from '../../../env' +import { corsConfig } from '../../../utils/corsConfig' +import cors from 'cors' export function mobileAuthRouter() { const router = express.Router() @@ -61,6 +63,12 @@ export function mobileAuthRouter() { res.status(payload.statusCode).json(payload.json) }) + // Required since this will be called from Android WebView + router.options( + '/android-apple-redirect', + cors({ ...corsConfig, maxAge: 600 }) + ) + router.post('/android-apple-redirect', (req, res) => { const { id_token } = req.body return res.redirect( From 3f6ade9614b335cd5b9aacd5cd77dcad91a92175 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Sun, 28 Aug 2022 09:55:41 -0700 Subject: [PATCH 17/37] verify apple token with both audience names used for the app --- packages/api/src/routers/auth/apple_auth.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/api/src/routers/auth/apple_auth.ts b/packages/api/src/routers/auth/apple_auth.ts index 07404924a..c11fef607 100644 --- a/packages/api/src/routers/auth/apple_auth.ts +++ b/packages/api/src/routers/auth/apple_auth.ts @@ -41,8 +41,7 @@ async function fetchApplePublicKey(kid: string): Promise { } export async function decodeAppleToken( - token: string, - isWeb?: boolean + token: string ): Promise { const decodedToken = jwt.decode(token, { complete: true }) const { kid, alg } = (decodedToken as any).header @@ -54,8 +53,8 @@ export async function decodeAppleToken( } const jwtClaims: any = jwt.verify(token, publicKey, { algorithms: [alg] }) const issVerified = (jwtClaims.iss ?? '') === appleBaseURL - const audVerified = - (jwtClaims.aud ?? '') === isWeb ? webAudienceName : audienceName + const audience = jwtClaims.aud ?? '' + const audVerified = audience == webAudienceName || audience === audienceName if (issVerified && audVerified && jwtClaims.email) { return { email: jwtClaims.email, @@ -106,7 +105,7 @@ export async function handleAppleWebAuth( return env.client.url } - const decodedTokenResult = await decodeAppleToken(idToken, true) + const decodedTokenResult = await decodeAppleToken(idToken) const authFailedRedirect = `${baseURL()}/login?errorCodes=${ LoginErrorCode.AuthFailed }` From b63c9b0265e21cd5ab68209c08f12550a056a4d7 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 29 Aug 2022 11:28:54 +0800 Subject: [PATCH 18/37] Make event names match in Swift and JS --- apple/OmnivoreKit/Sources/Views/Article/OmnivoreWebView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apple/OmnivoreKit/Sources/Views/Article/OmnivoreWebView.swift b/apple/OmnivoreKit/Sources/Views/Article/OmnivoreWebView.swift index bc27bcbb4..cf1abe816 100644 --- a/apple/OmnivoreKit/Sources/Views/Article/OmnivoreWebView.swift +++ b/apple/OmnivoreKit/Sources/Views/Article/OmnivoreWebView.swift @@ -300,7 +300,7 @@ public enum WebViewDispatchEvent { case let .updateFontSize(size: size): return "event.fontSize = '\(size)';" case let .updateColorMode(isDark: isDark): - return "event.isDarkMode = '\(isDark)';" + return "event.isDark = '\(isDark)';" case let .updateFontFamily(family: family): return "event.fontFamily = '\(family)';" case let .saveAnnotation(annotation: annotation): From 2beb75d7471fd893cf194df79505bf71d77c8961 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 29 Aug 2022 11:31:07 +0800 Subject: [PATCH 19/37] isDark is a string in Swift so we need to check for "true" --- apple/OmnivoreKit/Sources/Views/Resources/bundle.js | 2 +- .../web/components/templates/article/ArticleContainer.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apple/OmnivoreKit/Sources/Views/Resources/bundle.js b/apple/OmnivoreKit/Sources/Views/Resources/bundle.js index 416534fee..e120a69c6 100644 --- a/apple/OmnivoreKit/Sources/Views/Resources/bundle.js +++ b/apple/OmnivoreKit/Sources/Views/Resources/bundle.js @@ -1,2 +1,2 @@ /*! For license information please see bundle.js.LICENSE.txt */ -(()=>{var e,t,n={7162:(e,t,n)=>{e.exports=n(5047)},6279:function(e,t){var n="undefined"!=typeof self?self:this,r=function(){function e(){this.fetch=!1,this.DOMException=n.DOMException}return e.prototype=n,new e}();!function(e){!function(t){var n="URLSearchParams"in e,r="Symbol"in e&&"iterator"in Symbol,o="FileReader"in e&&"Blob"in e&&function(){try{return new Blob,!0}catch(e){return!1}}(),i="FormData"in e,a="ArrayBuffer"in e;if(a)var l=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],s=ArrayBuffer.isView||function(e){return e&&l.indexOf(Object.prototype.toString.call(e))>-1};function u(e){if("string"!=typeof e&&(e=String(e)),/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(e))throw new TypeError("Invalid character in header field name");return e.toLowerCase()}function c(e){return"string"!=typeof e&&(e=String(e)),e}function f(e){var t={next:function(){var t=e.shift();return{done:void 0===t,value:t}}};return r&&(t[Symbol.iterator]=function(){return t}),t}function d(e){this.map={},e instanceof d?e.forEach((function(e,t){this.append(t,e)}),this):Array.isArray(e)?e.forEach((function(e){this.append(e[0],e[1])}),this):e&&Object.getOwnPropertyNames(e).forEach((function(t){this.append(t,e[t])}),this)}function p(e){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}function h(e){return new Promise((function(t,n){e.onload=function(){t(e.result)},e.onerror=function(){n(e.error)}}))}function g(e){var t=new FileReader,n=h(t);return t.readAsArrayBuffer(e),n}function m(e){if(e.slice)return e.slice(0);var t=new Uint8Array(e.byteLength);return t.set(new Uint8Array(e)),t.buffer}function v(){return this.bodyUsed=!1,this._initBody=function(e){var t;this._bodyInit=e,e?"string"==typeof e?this._bodyText=e:o&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:i&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:n&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():a&&o&&(t=e)&&DataView.prototype.isPrototypeOf(t)?(this._bodyArrayBuffer=m(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):a&&(ArrayBuffer.prototype.isPrototypeOf(e)||s(e))?this._bodyArrayBuffer=m(e):this._bodyText=e=Object.prototype.toString.call(e):this._bodyText="",this.headers.get("content-type")||("string"==typeof e?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):n&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},o&&(this.blob=function(){var e=p(this);if(e)return e;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this._bodyArrayBuffer?p(this)||Promise.resolve(this._bodyArrayBuffer):this.blob().then(g)}),this.text=function(){var e,t,n,r=p(this);if(r)return r;if(this._bodyBlob)return e=this._bodyBlob,n=h(t=new FileReader),t.readAsText(e),n;if(this._bodyArrayBuffer)return Promise.resolve(function(e){for(var t=new Uint8Array(e),n=new Array(t.length),r=0;r-1?r:n),this.mode=t.mode||this.mode||null,this.signal=t.signal||this.signal,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&o)throw new TypeError("Body not allowed for GET or HEAD requests");this._initBody(o)}function w(e){var t=new FormData;return e.trim().split("&").forEach((function(e){if(e){var n=e.split("="),r=n.shift().replace(/\+/g," "),o=n.join("=").replace(/\+/g," ");t.append(decodeURIComponent(r),decodeURIComponent(o))}})),t}function x(e,t){t||(t={}),this.type="default",this.status=void 0===t.status?200:t.status,this.ok=this.status>=200&&this.status<300,this.statusText="statusText"in t?t.statusText:"OK",this.headers=new d(t.headers),this.url=t.url||"",this._initBody(e)}b.prototype.clone=function(){return new b(this,{body:this._bodyInit})},v.call(b.prototype),v.call(x.prototype),x.prototype.clone=function(){return new x(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new d(this.headers),url:this.url})},x.error=function(){var e=new x(null,{status:0,statusText:""});return e.type="error",e};var E=[301,302,303,307,308];x.redirect=function(e,t){if(-1===E.indexOf(t))throw new RangeError("Invalid status code");return new x(null,{status:t,headers:{location:e}})},t.DOMException=e.DOMException;try{new t.DOMException}catch(e){t.DOMException=function(e,t){this.message=e,this.name=t;var n=Error(e);this.stack=n.stack},t.DOMException.prototype=Object.create(Error.prototype),t.DOMException.prototype.constructor=t.DOMException}function k(e,n){return new Promise((function(r,i){var a=new b(e,n);if(a.signal&&a.signal.aborted)return i(new t.DOMException("Aborted","AbortError"));var l=new XMLHttpRequest;function s(){l.abort()}l.onload=function(){var e,t,n={status:l.status,statusText:l.statusText,headers:(e=l.getAllResponseHeaders()||"",t=new d,e.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach((function(e){var n=e.split(":"),r=n.shift().trim();if(r){var o=n.join(":").trim();t.append(r,o)}})),t)};n.url="responseURL"in l?l.responseURL:n.headers.get("X-Request-URL");var o="response"in l?l.response:l.responseText;r(new x(o,n))},l.onerror=function(){i(new TypeError("Network request failed"))},l.ontimeout=function(){i(new TypeError("Network request failed"))},l.onabort=function(){i(new t.DOMException("Aborted","AbortError"))},l.open(a.method,a.url,!0),"include"===a.credentials?l.withCredentials=!0:"omit"===a.credentials&&(l.withCredentials=!1),"responseType"in l&&o&&(l.responseType="blob"),a.headers.forEach((function(e,t){l.setRequestHeader(t,e)})),a.signal&&(a.signal.addEventListener("abort",s),l.onreadystatechange=function(){4===l.readyState&&a.signal.removeEventListener("abort",s)}),l.send(void 0===a._bodyInit?null:a._bodyInit)}))}k.polyfill=!0,e.fetch||(e.fetch=k,e.Headers=d,e.Request=b,e.Response=x),t.Headers=d,t.Request=b,t.Response=x,t.fetch=k,Object.defineProperty(t,"__esModule",{value:!0})}({})}(r),r.fetch.ponyfill=!0,delete r.fetch.polyfill;var o=r;(t=o.fetch).default=o.fetch,t.fetch=o.fetch,t.Headers=o.Headers,t.Request=o.Request,t.Response=o.Response,e.exports=t},1427:e=>{var t=function(){this.Diff_Timeout=1,this.Diff_EditCost=4,this.Match_Threshold=.5,this.Match_Distance=1e3,this.Patch_DeleteThreshold=.5,this.Patch_Margin=4,this.Match_MaxBits=32},n=-1;t.Diff=function(e,t){return[e,t]},t.prototype.diff_main=function(e,n,r,o){void 0===o&&(o=this.Diff_Timeout<=0?Number.MAX_VALUE:(new Date).getTime()+1e3*this.Diff_Timeout);var i=o;if(null==e||null==n)throw new Error("Null input. (diff_main)");if(e==n)return e?[new t.Diff(0,e)]:[];void 0===r&&(r=!0);var a=r,l=this.diff_commonPrefix(e,n),s=e.substring(0,l);e=e.substring(l),n=n.substring(l),l=this.diff_commonSuffix(e,n);var u=e.substring(e.length-l);e=e.substring(0,e.length-l),n=n.substring(0,n.length-l);var c=this.diff_compute_(e,n,a,i);return s&&c.unshift(new t.Diff(0,s)),u&&c.push(new t.Diff(0,u)),this.diff_cleanupMerge(c),c},t.prototype.diff_compute_=function(e,r,o,i){var a;if(!e)return[new t.Diff(1,r)];if(!r)return[new t.Diff(n,e)];var l=e.length>r.length?e:r,s=e.length>r.length?r:e,u=l.indexOf(s);if(-1!=u)return a=[new t.Diff(1,l.substring(0,u)),new t.Diff(0,s),new t.Diff(1,l.substring(u+s.length))],e.length>r.length&&(a[0][0]=a[2][0]=n),a;if(1==s.length)return[new t.Diff(n,e),new t.Diff(1,r)];var c=this.diff_halfMatch_(e,r);if(c){var f=c[0],d=c[1],p=c[2],h=c[3],g=c[4],m=this.diff_main(f,p,o,i),v=this.diff_main(d,h,o,i);return m.concat([new t.Diff(0,g)],v)}return o&&e.length>100&&r.length>100?this.diff_lineMode_(e,r,i):this.diff_bisect_(e,r,i)},t.prototype.diff_lineMode_=function(e,r,o){var i=this.diff_linesToChars_(e,r);e=i.chars1,r=i.chars2;var a=i.lineArray,l=this.diff_main(e,r,!1,o);this.diff_charsToLines_(l,a),this.diff_cleanupSemantic(l),l.push(new t.Diff(0,""));for(var s=0,u=0,c=0,f="",d="";s=1&&c>=1){l.splice(s-u-c,u+c),s=s-u-c;for(var p=this.diff_main(f,d,!1,o),h=p.length-1;h>=0;h--)l.splice(s,0,p[h]);s+=p.length}c=0,u=0,f="",d=""}s++}return l.pop(),l},t.prototype.diff_bisect_=function(e,r,o){for(var i=e.length,a=r.length,l=Math.ceil((i+a)/2),s=l,u=2*l,c=new Array(u),f=new Array(u),d=0;do);b++){for(var w=-b+g;w<=b-m;w+=2){for(var x=s+w,E=(C=w==-b||w!=b&&c[x-1]i)m+=2;else if(E>a)g+=2;else if(h&&(_=s+p-w)>=0&&_=(S=i-f[_]))return this.diff_bisectSplit_(e,r,C,E,o)}for(var k=-b+v;k<=b-y;k+=2){for(var S,_=s+k,O=(S=k==-b||k!=b&&f[_-1]i)y+=2;else if(O>a)v+=2;else if(!h){var C;if((x=s+p-k)>=0&&x=(S=i-S))return this.diff_bisectSplit_(e,r,C,E,o)}}}return[new t.Diff(n,e),new t.Diff(1,r)]},t.prototype.diff_bisectSplit_=function(e,t,n,r,o){var i=e.substring(0,n),a=t.substring(0,r),l=e.substring(n),s=t.substring(r),u=this.diff_main(i,a,!1,o),c=this.diff_main(l,s,!1,o);return u.concat(c)},t.prototype.diff_linesToChars_=function(e,t){var n=[],r={};function o(e){for(var t="",o=0,a=-1,l=n.length;ar?e=e.substring(n-r):nt.length?e:t,r=e.length>t.length?t:e;if(n.length<4||2*r.length=e.length?[r,i,a,l,c]:null}var a,l,s,u,c,f=i(n,r,Math.ceil(n.length/4)),d=i(n,r,Math.ceil(n.length/2));return f||d?(a=d?f&&f[4].length>d[4].length?f:d:f,e.length>t.length?(l=a[0],s=a[1],u=a[2],c=a[3]):(u=a[0],c=a[1],l=a[2],s=a[3]),[l,s,u,c,a[4]]):null},t.prototype.diff_cleanupSemantic=function(e){for(var r=!1,o=[],i=0,a=null,l=0,s=0,u=0,c=0,f=0;l0?o[i-1]:-1,s=0,u=0,c=0,f=0,a=null,r=!0)),l++;for(r&&this.diff_cleanupMerge(e),this.diff_cleanupSemanticLossless(e),l=1;l=g?(h>=d.length/2||h>=p.length/2)&&(e.splice(l,0,new t.Diff(0,p.substring(0,h))),e[l-1][1]=d.substring(0,d.length-h),e[l+1][1]=p.substring(h),l++):(g>=d.length/2||g>=p.length/2)&&(e.splice(l,0,new t.Diff(0,d.substring(0,g))),e[l-1][0]=1,e[l-1][1]=p.substring(0,p.length-g),e[l+1][0]=n,e[l+1][1]=d.substring(g),l++),l++}l++}},t.prototype.diff_cleanupSemanticLossless=function(e){function n(e,n){if(!e||!n)return 6;var r=e.charAt(e.length-1),o=n.charAt(0),i=r.match(t.nonAlphaNumericRegex_),a=o.match(t.nonAlphaNumericRegex_),l=i&&r.match(t.whitespaceRegex_),s=a&&o.match(t.whitespaceRegex_),u=l&&r.match(t.linebreakRegex_),c=s&&o.match(t.linebreakRegex_),f=u&&e.match(t.blanklineEndRegex_),d=c&&n.match(t.blanklineStartRegex_);return f||d?5:u||c?4:i&&!l&&s?3:l||s?2:i||a?1:0}for(var r=1;r=d&&(d=p,u=o,c=i,f=a)}e[r-1][1]!=u&&(u?e[r-1][1]=u:(e.splice(r-1,1),r--),e[r][1]=c,f?e[r+1][1]=f:(e.splice(r+1,1),r--))}r++}},t.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/,t.whitespaceRegex_=/\s/,t.linebreakRegex_=/[\r\n]/,t.blanklineEndRegex_=/\n\r?\n$/,t.blanklineStartRegex_=/^\r?\n\r?\n/,t.prototype.diff_cleanupEfficiency=function(e){for(var r=!1,o=[],i=0,a=null,l=0,s=!1,u=!1,c=!1,f=!1;l0?o[i-1]:-1,c=f=!1),r=!0)),l++;r&&this.diff_cleanupMerge(e)},t.prototype.diff_cleanupMerge=function(e){e.push(new t.Diff(0,""));for(var r,o=0,i=0,a=0,l="",s="";o1?(0!==i&&0!==a&&(0!==(r=this.diff_commonPrefix(s,l))&&(o-i-a>0&&0==e[o-i-a-1][0]?e[o-i-a-1][1]+=s.substring(0,r):(e.splice(0,0,new t.Diff(0,s.substring(0,r))),o++),s=s.substring(r),l=l.substring(r)),0!==(r=this.diff_commonSuffix(s,l))&&(e[o][1]=s.substring(s.length-r)+e[o][1],s=s.substring(0,s.length-r),l=l.substring(0,l.length-r))),o-=i+a,e.splice(o,i+a),l.length&&(e.splice(o,0,new t.Diff(n,l)),o++),s.length&&(e.splice(o,0,new t.Diff(1,s)),o++),o++):0!==o&&0==e[o-1][0]?(e[o-1][1]+=e[o][1],e.splice(o,1)):o++,a=0,i=0,l="",s=""}""===e[e.length-1][1]&&e.pop();var u=!1;for(o=1;ot));r++)a=o,l=i;return e.length!=r&&e[r][0]===n?l:l+(t-a)},t.prototype.diff_prettyHtml=function(e){for(var t=[],r=/&/g,o=//g,a=/\n/g,l=0;l");switch(s){case 1:t[l]=''+u+"";break;case n:t[l]=''+u+"";break;case 0:t[l]=""+u+""}}return t.join("")},t.prototype.diff_text1=function(e){for(var t=[],n=0;nthis.Match_MaxBits)throw new Error("Pattern too long for this browser.");var r=this.match_alphabet_(t),o=this;function i(e,r){var i=e/t.length,a=Math.abs(n-r);return o.Match_Distance?i+a/o.Match_Distance:a?1:i}var a=this.Match_Threshold,l=e.indexOf(t,n);-1!=l&&(a=Math.min(i(0,l),a),-1!=(l=e.lastIndexOf(t,n+t.length))&&(a=Math.min(i(0,l),a)));var s,u,c=1<=h;v--){var y=r[e.charAt(v-1)];if(m[v]=0===p?(m[v+1]<<1|1)&y:(m[v+1]<<1|1)&y|(f[v+1]|f[v])<<1|1|f[v+1],m[v]&c){var b=i(p,v-1);if(b<=a){if(a=b,!((l=v-1)>n))break;h=Math.max(1,2*n-l)}}}if(i(p+1,n)>a)break;f=m}return l},t.prototype.match_alphabet_=function(e){for(var t={},n=0;n2&&(this.diff_cleanupSemantic(a),this.diff_cleanupEfficiency(a));else if(e&&"object"==typeof e&&void 0===r&&void 0===o)a=e,i=this.diff_text1(a);else if("string"==typeof e&&r&&"object"==typeof r&&void 0===o)i=e,a=r;else{if("string"!=typeof e||"string"!=typeof r||!o||"object"!=typeof o)throw new Error("Unknown call format to patch_make.");i=e,a=o}if(0===a.length)return[];for(var l=[],s=new t.patch_obj,u=0,c=0,f=0,d=i,p=i,h=0;h=2*this.Patch_Margin&&u&&(this.patch_addContext_(s,d),l.push(s),s=new t.patch_obj,u=0,d=p,c=f)}1!==g&&(c+=m.length),g!==n&&(f+=m.length)}return u&&(this.patch_addContext_(s,d),l.push(s)),l},t.prototype.patch_deepCopy=function(e){for(var n=[],r=0;rthis.Match_MaxBits?-1!=(l=this.match_main(t,c.substring(0,this.Match_MaxBits),u))&&(-1==(f=this.match_main(t,c.substring(c.length-this.Match_MaxBits),u+c.length-this.Match_MaxBits))||l>=f)&&(l=-1):l=this.match_main(t,c,u),-1==l)i[a]=!1,o-=e[a].length2-e[a].length1;else if(i[a]=!0,o=l-u,c==(s=-1==f?t.substring(l,l+c.length):t.substring(l,f+this.Match_MaxBits)))t=t.substring(0,l)+this.diff_text2(e[a].diffs)+t.substring(l+c.length);else{var d=this.diff_main(c,s,!1);if(c.length>this.Match_MaxBits&&this.diff_levenshtein(d)/c.length>this.Patch_DeleteThreshold)i[a]=!1;else{this.diff_cleanupSemanticLossless(d);for(var p,h=0,g=0;ga[0][1].length){var l=n-a[0][1].length;a[0][1]=r.substring(a[0][1].length)+a[0][1],i.start1-=l,i.start2-=l,i.length1+=l,i.length2+=l}return 0==(a=(i=e[e.length-1]).diffs).length||0!=a[a.length-1][0]?(a.push(new t.Diff(0,r)),i.length1+=n,i.length2+=n):n>a[a.length-1][1].length&&(l=n-a[a.length-1][1].length,a[a.length-1][1]+=r.substring(0,l),i.length1+=l,i.length2+=l),r},t.prototype.patch_splitMax=function(e){for(var r=this.Match_MaxBits,o=0;o2*r?(u.length1+=d.length,a+=d.length,c=!1,u.diffs.push(new t.Diff(f,d)),i.diffs.shift()):(d=d.substring(0,r-u.length1-this.Patch_Margin),u.length1+=d.length,a+=d.length,0===f?(u.length2+=d.length,l+=d.length):c=!1,u.diffs.push(new t.Diff(f,d)),d==i.diffs[0][1]?i.diffs.shift():i.diffs[0][1]=i.diffs[0][1].substring(d.length))}s=(s=this.diff_text2(u.diffs)).substring(s.length-this.Patch_Margin);var p=this.diff_text1(i.diffs).substring(0,this.Patch_Margin);""!==p&&(u.length1+=p.length,u.length2+=p.length,0!==u.diffs.length&&0===u.diffs[u.diffs.length-1][0]?u.diffs[u.diffs.length-1][1]+=p:u.diffs.push(new t.Diff(0,p))),c||e.splice(++o,0,u)}}},t.prototype.patch_toText=function(e){for(var t=[],n=0;n{"use strict";e.exports=function(e){var t=e.uri,n=e.name,r=e.type;this.uri=t,this.name=n,this.type=r}},2929:(e,t,n)=>{"use strict";var r=n(1278);e.exports=function e(t,n,o){var i;void 0===n&&(n=""),void 0===o&&(o=r);var a=new Map;function l(e,t){var n=a.get(t);n?n.push.apply(n,e):a.set(t,e)}if(o(t))i=null,l([n],t);else{var s=n?n+".":"";if("undefined"!=typeof FileList&&t instanceof FileList)i=Array.prototype.map.call(t,(function(e,t){return l([""+s+t],e),null}));else if(Array.isArray(t))i=t.map((function(t,n){var r=e(t,""+s+n,o);return r.files.forEach(l),r.clone}));else if(t&&t.constructor===Object)for(var u in i={},t){var c=e(t[u],""+s+u,o);c.files.forEach(l),i[u]=c.clone}else i=t}return{clone:i,files:a}}},9384:(e,t,n)=>{"use strict";t.ReactNativeFile=n(7570),t.extractFiles=n(2929),t.isExtractableFile=n(1278)},1278:(e,t,n)=>{"use strict";var r=n(7570);e.exports=function(e){return"undefined"!=typeof File&&e instanceof File||"undefined"!=typeof Blob&&e instanceof Blob||e instanceof r}},1688:e=>{e.exports="object"==typeof self?self.FormData:window.FormData},8749:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(9384),i=r(n(1688)),a=function(e){return o.isExtractableFile(e)||null!==e&&"object"==typeof e&&"function"==typeof e.pipe};t.default=function(e,t,n){var r=o.extractFiles({query:e,variables:t,operationName:n},"",a),l=r.clone,s=r.files;if(0===s.size){if(!Array.isArray(e))return JSON.stringify(l);if(void 0!==t&&!Array.isArray(t))throw new Error("Cannot create request body with given variable type, array expected");var u=e.reduce((function(e,n,r){return e.push({query:n,variables:t?t[r]:void 0}),e}),[]);return JSON.stringify(u)}var c=new("undefined"==typeof FormData?i.default:FormData);c.append("operations",JSON.stringify(l));var f={},d=0;return s.forEach((function(e){f[++d]=e})),c.append("map",JSON.stringify(f)),d=0,s.forEach((function(e,t){c.append(""+ ++d,t)})),c}},6647:function(e,t,n){"use strict";var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=e.prototype.toJSON;"function"==typeof t||(0,r.default)(0),e.prototype.inspect=t,o.default&&(e.prototype[o.default]=t)};var r=i(n(5006)),o=i(n(8019));function i(e){return e&&e.__esModule?e:{default:e}}},8048:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return a(e,[])};var r,o=(r=n(8019))&&r.__esModule?r:{default:r};function i(e){return i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},i(e)}function a(e,t){switch(i(e)){case"string":return JSON.stringify(e);case"function":return e.name?"[function ".concat(e.name,"]"):"[function]";case"object":return null===e?"null":function(e,t){if(-1!==t.indexOf(e))return"[Circular]";var n=[].concat(t,[e]),r=function(e){var t=e[String(o.default)];return"function"==typeof t?t:"function"==typeof e.inspect?e.inspect:void 0}(e);if(void 0!==r){var i=r.call(e);if(i!==e)return"string"==typeof i?i:a(i,n)}else if(Array.isArray(e))return function(e,t){if(0===e.length)return"[]";if(t.length>2)return"[Array]";for(var n=Math.min(10,e.length),r=e.length-n,o=[],i=0;i1&&o.push("... ".concat(r," more items")),"["+o.join(", ")+"]"}(e,n);return function(e,t){var n=Object.keys(e);return 0===n.length?"{}":t.length>2?"["+function(e){var t=Object.prototype.toString.call(e).replace(/^\[object /,"").replace(/]$/,"");if("Object"===t&&"function"==typeof e.constructor){var n=e.constructor.name;if("string"==typeof n&&""!==n)return n}return t}(e)+"]":"{ "+n.map((function(n){return n+": "+a(e[n],t)})).join(", ")+" }"}(e,n)}(e,t);default:return String(e)}}},5006:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){if(!Boolean(e))throw new Error(null!=t?t:"Unexpected invariant triggered.")}},8019:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):void 0;t.default=n},4560:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isNode=function(e){return null!=e&&"string"==typeof e.kind},t.Token=t.Location=void 0;var r,o=(r=n(2678))&&r.__esModule?r:{default:r},i=function(){function e(e,t,n){this.start=e.start,this.end=t.end,this.startToken=e,this.endToken=t,this.source=n}return e.prototype.toJSON=function(){return{start:this.start,end:this.end}},e}();t.Location=i,(0,o.default)(i);var a=function(){function e(e,t,n,r,o,i,a){this.kind=e,this.start=t,this.end=n,this.line=r,this.column=o,this.value=a,this.prev=i,this.next=null}return e.prototype.toJSON=function(){return{kind:this.kind,value:this.value,line:this.line,column:this.column}},e}();t.Token=a,(0,o.default)(a)},9501:(e,t)=>{"use strict";function n(e){for(var t=0;ta&&n(t[l-1]);)--l;return t.slice(a,l).join("\n")},t.getBlockStringIndentation=r,t.printBlockString=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=-1===e.indexOf("\n"),o=" "===e[0]||"\t"===e[0],i='"'===e[e.length-1],a="\\"===e[e.length-1],l=!r||i||a||n,s="";return!l||r&&o||(s+="\n"+t),s+=t?e.replace(/\n/g,"\n"+t):e,l&&(s+="\n"),'"""'+s.replace(/"""/g,'\\"""')+'"""'}},3083:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.print=function(e){return(0,r.visit)(e,{leave:i})};var r=n(2624),o=n(9501),i={Name:function(e){return e.value},Variable:function(e){return"$"+e.name},Document:function(e){return l(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=u("(",l(e.variableDefinitions,", "),")"),o=l(e.directives," "),i=e.selectionSet;return n||o||r||"query"!==t?l([t,l([n,r]),o,i]," "):i},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,o=e.directives;return t+": "+n+u(" = ",r)+u(" ",l(o," "))},SelectionSet:function(e){return s(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,o=e.directives,i=e.selectionSet,a=u("",t,": ")+n,s=a+u("(",l(r,", "),")");return s.length>80&&(s=a+u("(\n",c(l(r,"\n")),"\n)")),l([s,l(o," "),i]," ")},Argument:function(e){return e.name+": "+e.value},FragmentSpread:function(e){return"..."+e.name+u(" ",l(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return l(["...",u("on ",t),l(n," "),r]," ")},FragmentDefinition:function(e){var t=e.name,n=e.typeCondition,r=e.variableDefinitions,o=e.directives,i=e.selectionSet;return"fragment ".concat(t).concat(u("(",l(r,", "),")")," ")+"on ".concat(n," ").concat(u("",l(o," ")," "))+i},IntValue:function(e){return e.value},FloatValue:function(e){return e.value},StringValue:function(e,t){var n=e.value;return e.block?(0,o.printBlockString)(n,"description"===t?"":" "):JSON.stringify(n)},BooleanValue:function(e){return e.value?"true":"false"},NullValue:function(){return"null"},EnumValue:function(e){return e.value},ListValue:function(e){return"["+l(e.values,", ")+"]"},ObjectValue:function(e){return"{"+l(e.fields,", ")+"}"},ObjectField:function(e){return e.name+": "+e.value},Directive:function(e){return"@"+e.name+u("(",l(e.arguments,", "),")")},NamedType:function(e){return e.name},ListType:function(e){return"["+e.type+"]"},NonNullType:function(e){return e.type+"!"},SchemaDefinition:a((function(e){var t=e.directives,n=e.operationTypes;return l(["schema",l(t," "),s(n)]," ")})),OperationTypeDefinition:function(e){return e.operation+": "+e.type},ScalarTypeDefinition:a((function(e){return l(["scalar",e.name,l(e.directives," ")]," ")})),ObjectTypeDefinition:a((function(e){var t=e.name,n=e.interfaces,r=e.directives,o=e.fields;return l(["type",t,u("implements ",l(n," & ")),l(r," "),s(o)]," ")})),FieldDefinition:a((function(e){var t=e.name,n=e.arguments,r=e.type,o=e.directives;return t+(d(n)?u("(\n",c(l(n,"\n")),"\n)"):u("(",l(n,", "),")"))+": "+r+u(" ",l(o," "))})),InputValueDefinition:a((function(e){var t=e.name,n=e.type,r=e.defaultValue,o=e.directives;return l([t+": "+n,u("= ",r),l(o," ")]," ")})),InterfaceTypeDefinition:a((function(e){var t=e.name,n=e.interfaces,r=e.directives,o=e.fields;return l(["interface",t,u("implements ",l(n," & ")),l(r," "),s(o)]," ")})),UnionTypeDefinition:a((function(e){var t=e.name,n=e.directives,r=e.types;return l(["union",t,l(n," "),r&&0!==r.length?"= "+l(r," | "):""]," ")})),EnumTypeDefinition:a((function(e){var t=e.name,n=e.directives,r=e.values;return l(["enum",t,l(n," "),s(r)]," ")})),EnumValueDefinition:a((function(e){return l([e.name,l(e.directives," ")]," ")})),InputObjectTypeDefinition:a((function(e){var t=e.name,n=e.directives,r=e.fields;return l(["input",t,l(n," "),s(r)]," ")})),DirectiveDefinition:a((function(e){var t=e.name,n=e.arguments,r=e.repeatable,o=e.locations;return"directive @"+t+(d(n)?u("(\n",c(l(n,"\n")),"\n)"):u("(",l(n,", "),")"))+(r?" repeatable":"")+" on "+l(o," | ")})),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return l(["extend schema",l(t," "),s(n)]," ")},ScalarTypeExtension:function(e){return l(["extend scalar",e.name,l(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,o=e.fields;return l(["extend type",t,u("implements ",l(n," & ")),l(r," "),s(o)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,o=e.fields;return l(["extend interface",t,u("implements ",l(n," & ")),l(r," "),s(o)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return l(["extend union",t,l(n," "),r&&0!==r.length?"= "+l(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return l(["extend enum",t,l(n," "),s(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return l(["extend input",t,l(n," "),s(r)]," ")}};function a(e){return function(t){return l([t.description,e(t)],"\n")}}function l(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return null!==(t=null==e?void 0:e.filter((function(e){return e})).join(n))&&void 0!==t?t:""}function s(e){return u("{\n",c(l(e,"\n")),"\n}")}function u(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function c(e){return u(" ",e.replace(/\n/g,"\n "))}function f(e){return-1!==e.indexOf("\n")}function d(e){return null!=e&&e.some(f)}},2624:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.visit=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:a,r=void 0,u=Array.isArray(e),c=[e],f=-1,d=[],p=void 0,h=void 0,g=void 0,m=[],v=[],y=e;do{var b=++f===c.length,w=b&&0!==d.length;if(b){if(h=0===v.length?void 0:m[m.length-1],p=g,g=v.pop(),w){if(u)p=p.slice();else{for(var x={},E=0,k=Object.keys(p);E{var r=n(7772).Symbol;e.exports=r},3366:(e,t,n)=>{var r=n(857),o=n(2107),i=n(7157),a=r?r.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":a&&a in Object(e)?o(e):i(e)}},1704:(e,t,n)=>{var r=n(2153),o=/^\s+/;e.exports=function(e){return e?e.slice(0,r(e)+1).replace(o,""):e}},1242:(e,t,n)=>{var r="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g;e.exports=r},2107:(e,t,n)=>{var r=n(857),o=Object.prototype,i=o.hasOwnProperty,a=o.toString,l=r?r.toStringTag:void 0;e.exports=function(e){var t=i.call(e,l),n=e[l];try{e[l]=void 0;var r=!0}catch(e){}var o=a.call(e);return r&&(t?e[l]=n:delete e[l]),o}},7157:e=>{var t=Object.prototype.toString;e.exports=function(e){return t.call(e)}},7772:(e,t,n)=>{var r=n(1242),o="object"==typeof self&&self&&self.Object===Object&&self,i=r||o||Function("return this")();e.exports=i},2153:e=>{var t=/\s/;e.exports=function(e){for(var n=e.length;n--&&t.test(e.charAt(n)););return n}},4073:(e,t,n)=>{var r=n(9259),o=n(1100),i=n(7642),a=Math.max,l=Math.min;e.exports=function(e,t,n){var s,u,c,f,d,p,h=0,g=!1,m=!1,v=!0;if("function"!=typeof e)throw new TypeError("Expected a function");function y(t){var n=s,r=u;return s=u=void 0,h=t,f=e.apply(r,n)}function b(e){return h=e,d=setTimeout(x,t),g?y(e):f}function w(e){var n=e-p;return void 0===p||n>=t||n<0||m&&e-h>=c}function x(){var e=o();if(w(e))return E(e);d=setTimeout(x,function(e){var n=t-(e-p);return m?l(n,c-(e-h)):n}(e))}function E(e){return d=void 0,v&&s?y(e):(s=u=void 0,f)}function k(){var e=o(),n=w(e);if(s=arguments,u=this,p=e,n){if(void 0===d)return b(p);if(m)return clearTimeout(d),d=setTimeout(x,t),y(p)}return void 0===d&&(d=setTimeout(x,t)),f}return t=i(t)||0,r(n)&&(g=!!n.leading,c=(m="maxWait"in n)?a(i(n.maxWait)||0,t):c,v="trailing"in n?!!n.trailing:v),k.cancel=function(){void 0!==d&&clearTimeout(d),h=0,s=p=u=d=void 0},k.flush=function(){return void 0===d?f:E(o())},k}},9259:e=>{e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},5125:e=>{e.exports=function(e){return null!=e&&"object"==typeof e}},4795:(e,t,n)=>{var r=n(3366),o=n(5125);e.exports=function(e){return"symbol"==typeof e||o(e)&&"[object Symbol]"==r(e)}},1100:(e,t,n)=>{var r=n(7772);e.exports=function(){return r.Date.now()}},7642:(e,t,n)=>{var r=n(1704),o=n(9259),i=n(4795),a=/^[-+]0x[0-9a-f]+$/i,l=/^0b[01]+$/i,s=/^0o[0-7]+$/i,u=parseInt;e.exports=function(e){if("number"==typeof e)return e;if(i(e))return NaN;if(o(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=o(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=r(e);var n=l.test(e);return n||s.test(e)?u(e.slice(2),n?2:8):a.test(e)?NaN:+e}},9410:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){let e=null;return{mountedInstances:new Set,updateHead:t=>{const n=e=Promise.resolve().then((()=>{if(n!==e)return;e=null;const i={};t.forEach((e=>{if("link"===e.type&&e.props["data-optimized-fonts"]){if(document.querySelector(`style[data-href="${e.props["data-href"]}"]`))return;e.props.href=e.props["data-href"],e.props["data-href"]=void 0}const t=i[e.type]||[];t.push(e),i[e.type]=t}));const a=i.title?i.title[0]:null;let l="";if(a){const{children:e}=a.props;l="string"==typeof e?e:Array.isArray(e)?e.join(""):""}l!==document.title&&(document.title=l),["meta","base","link","style","script"].forEach((e=>{!function(e,t){const n=document.getElementsByTagName("head")[0],i=n.querySelector("meta[name=next-head-count]"),a=Number(i.content),l=[];for(let t=0,n=i.previousElementSibling;t{for(let t=0,n=l.length;t{var t;return null===(t=e.parentNode)||void 0===t?void 0:t.removeChild(e)})),u.forEach((e=>n.insertBefore(e,i))),i.content=(a-l.length+u.length).toString()}(e,i[e]||[])}))}))}}},t.isEqualNode=o,t.DOMAttributeNames=void 0;const n={acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv",noModule:"noModule"};function r({type:e,props:t}){const r=document.createElement(e);for(const o in t){if(!t.hasOwnProperty(o))continue;if("children"===o||"dangerouslySetInnerHTML"===o)continue;if(void 0===t[o])continue;const i=n[o]||o.toLowerCase();"script"!==e||"async"!==i&&"defer"!==i&&"noModule"!==i?r.setAttribute(i,t[o]):r[i]=!!t[o]}const{children:o,dangerouslySetInnerHTML:i}=t;return i?r.innerHTML=i.__html||"":o&&(r.textContent="string"==typeof o?o:Array.isArray(o)?o.join(""):""),r}function o(e,t){if(e instanceof HTMLElement&&t instanceof HTMLElement){const n=t.getAttribute("nonce");if(n&&!e.getAttribute("nonce")){const r=t.cloneNode(!0);return r.setAttribute("nonce",""),r.nonce=n,n===e.nonce&&e.isEqualNode(r)}}return e.isEqualNode(t)}t.DOMAttributeNames=n,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},104:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var{src:t,sizes:n,unoptimized:r=!1,priority:o=!1,loading:c,lazyRoot:f=null,lazyBoundary:v="200px",className:E,quality:S,width:_,height:O,style:C,objectFit:P,objectPosition:j,onLoadingComplete:L,placeholder:T="empty",blurDataURL:R}=e,A=p(e,["src","sizes","unoptimized","priority","loading","lazyRoot","lazyBoundary","className","quality","width","height","style","objectFit","objectPosition","onLoadingComplete","placeholder","blurDataURL"]);const I=i.useContext(u.ImageConfigContext),M=i.useMemo((()=>{const e=h||I||l.imageConfigDefault,t=[...e.deviceSizes,...e.imageSizes].sort(((e,t)=>e-t)),n=e.deviceSizes.sort(((e,t)=>e-t));return d({},e,{allSizes:t,deviceSizes:n})}),[I]);let D=A,N=n?"responsive":"intrinsic";"layout"in D&&(D.layout&&(N=D.layout),delete D.layout);let z=x;if("loader"in D){if(D.loader){const e=D.loader;z=t=>{const{config:n}=t,r=p(t,["config"]);return e(r)}}delete D.loader}let F="";if(function(e){return"object"==typeof e&&(y(e)||function(e){return void 0!==e.src}(e))}(t)){const e=y(t)?t.default:t;if(!e.src)throw new Error(`An object should only be passed to the image component src parameter if it comes from a static image import. It must include src. Received ${JSON.stringify(e)}`);if(R=R||e.blurDataURL,F=e.src,!(N&&"fill"===N||(O=O||e.height,_=_||e.width,e.height&&e.width)))throw new Error(`An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received ${JSON.stringify(e)}`)}t="string"==typeof t?t:F;const $=w(_),B=w(O),U=w(S);let W=!o&&("lazy"===c||void 0===c);(t.startsWith("data:")||t.startsWith("blob:"))&&(r=!0,W=!1),"undefined"!=typeof window&&g.has(t)&&(W=!1);const[H,V]=i.useState(!1),[q,X,G]=s.useIntersection({rootRef:f,rootMargin:v,disabled:!W}),Y=!W||X,Q={boxSizing:"border-box",display:"block",overflow:"hidden",width:"initial",height:"initial",background:"none",opacity:1,border:0,margin:0,padding:0},K={boxSizing:"border-box",display:"block",width:"initial",height:"initial",background:"none",opacity:1,border:0,margin:0,padding:0};let Z,J=!1;const ee={position:"absolute",top:0,left:0,bottom:0,right:0,boxSizing:"border-box",padding:0,border:"none",margin:"auto",display:"block",width:0,height:0,minWidth:"100%",maxWidth:"100%",minHeight:"100%",maxHeight:"100%",objectFit:P,objectPosition:j},te=Object.assign({},C,"raw"===N?{}:ee),ne="blur"!==T||H?{}:{filter:"blur(20px)",backgroundSize:P||"cover",backgroundImage:`url("${R}")`,backgroundPosition:j||"0% 0%"};if("fill"===N)Q.display="block",Q.position="absolute",Q.top=0,Q.left=0,Q.bottom=0,Q.right=0;else if(void 0!==$&&void 0!==B){const e=B/$,t=isNaN(e)?"100%":100*e+"%";"responsive"===N?(Q.display="block",Q.position="relative",J=!0,K.paddingTop=t):"intrinsic"===N?(Q.display="inline-block",Q.position="relative",Q.maxWidth="100%",J=!0,K.maxWidth="100%",Z=`data:image/svg+xml,%3csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20version=%271.1%27%20width=%27${$}%27%20height=%27${B}%27/%3e`):"fixed"===N&&(Q.display="inline-block",Q.position="relative",Q.width=$,Q.height=B)}let re={src:m,srcSet:void 0,sizes:void 0};Y&&(re=b({config:M,src:t,unoptimized:r,layout:N,width:$,quality:U,sizes:n,loader:z}));let oe=t,ie="imagesrcset",ae="imagesizes";window.omnivoreEnv.__NEXT_REACT_ROOT&&(ie="imageSrcSet",ae="imageSizes");const le={[ie]:re.srcSet,[ae]:re.sizes},se="undefined"==typeof window?i.default.useEffect:i.default.useLayoutEffect,ue=i.useRef(L),ce=i.useRef(t);i.useEffect((()=>{ue.current=L}),[L]),se((()=>{ce.current!==t&&(G(),ce.current=t)}),[G,t]);const fe=d({isLazy:W,imgAttributes:re,heightInt:B,widthInt:$,qualityInt:U,layout:N,className:E,imgStyle:te,blurStyle:ne,loading:c,config:M,unoptimized:r,placeholder:T,loader:z,srcString:oe,onLoadingCompleteRef:ue,setBlurComplete:V,setIntersection:q,isVisible:Y},D);return i.default.createElement(i.default.Fragment,null,"raw"===N?i.default.createElement(k,Object.assign({},fe)):i.default.createElement("span",{style:Q},J?i.default.createElement("span",{style:K},Z?i.default.createElement("img",{style:{display:"block",maxWidth:"100%",width:"initial",height:"initial",background:"none",opacity:1,border:0,margin:0,padding:0},alt:"","aria-hidden":!0,src:Z}):null):null,i.default.createElement(k,Object.assign({},fe))),o?i.default.createElement(a.default,null,i.default.createElement("link",Object.assign({key:"__nimg-"+re.src+re.srcSet+re.sizes,rel:"preload",as:"image",href:re.srcSet?void 0:re.src},le))):null)};var r,o,i=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(2784)),a=(r=n(5977))&&r.__esModule?r:{default:r},l=n(8467),s=n(3321),u=n(4329),c=(n(1624),n(1119));function f(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function d(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}null===(o=window.omnivoreEnv.__NEXT_IMAGE_OPTS)||void 0===o||o.experimentalLayoutRaw;const h=window.omnivoreEnv.__NEXT_IMAGE_OPTS,g=new Set;new Map;const m="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";"undefined"==typeof window&&(n.g.__NEXT_IMAGE_IMPORTED=!0);const v=new Map([["default",function({config:e,src:t,width:n,quality:r}){return t.endsWith(".svg")&&!e.dangerouslyAllowSVG?t:`${c.normalizePathTrailingSlash(e.path)}?url=${encodeURIComponent(t)}&w=${n}&q=${r||75}`}],["imgix",function({config:e,src:t,width:n,quality:r}){const o=new URL(`${e.path}${S(t)}`),i=o.searchParams;return i.set("auto",i.get("auto")||"format"),i.set("fit",i.get("fit")||"max"),i.set("w",i.get("w")||n.toString()),r&&i.set("q",r.toString()),o.href}],["cloudinary",function({config:e,src:t,width:n,quality:r}){const o=["f_auto","c_limit","w_"+n,"q_"+(r||"auto")].join(",")+"/";return`${e.path}${o}${S(t)}`}],["akamai",function({config:e,src:t,width:n}){return`${e.path}${S(t)}?imwidth=${n}`}],["custom",function({src:e}){throw new Error(`Image with src "${e}" is missing "loader" prop.\nRead more: https://nextjs.org/docs/messages/next-image-missing-loader`)}]]);function y(e){return void 0!==e.default}function b({config:e,src:t,unoptimized:n,layout:r,width:o,quality:i,sizes:a,loader:l}){if(n)return{src:t,srcSet:void 0,sizes:void 0};const{widths:s,kind:u}=function({deviceSizes:e,allSizes:t},n,r,o){if(o&&("fill"===r||"responsive"===r||"raw"===r)){const n=/(^|\s)(1?\d?\d)vw/g,r=[];for(let e;e=n.exec(o);e)r.push(parseInt(e[2]));if(r.length){const n=.01*Math.min(...r);return{widths:t.filter((t=>t>=e[0]*n)),kind:"w"}}return{widths:t,kind:"w"}}return"number"!=typeof n||"fill"===r||"responsive"===r?{widths:e,kind:"w"}:{widths:[...new Set([n,2*n].map((e=>t.find((t=>t>=e))||t[t.length-1])))],kind:"x"}}(e,o,r,a),c=s.length-1;return{sizes:a||"w"!==u?a:"100vw",srcSet:s.map(((n,r)=>`${l({config:e,src:t,quality:i,width:n})} ${"w"===u?n:r+1}${u}`)).join(", "),src:l({config:e,src:t,quality:i,width:s[c]})}}function w(e){return"number"==typeof e?e:"string"==typeof e?parseInt(e,10):void 0}function x(e){var t;const n=(null===(t=e.config)||void 0===t?void 0:t.loader)||"default",r=v.get(n);if(r)return r(e);throw new Error(`Unknown "loader" found in "next.config.js". Expected: ${l.VALID_LOADERS.join(", ")}. Received: ${n}`)}function E(e,t,n,r,o,i){e&&e.src!==m&&e["data-loaded-src"]!==t&&(e["data-loaded-src"]=t,("decode"in e?e.decode():Promise.resolve()).catch((()=>{})).then((()=>{if(e.parentNode&&(g.add(t),"blur"===r&&i(!0),null==o?void 0:o.current)){const{naturalWidth:t,naturalHeight:n}=e;o.current({naturalWidth:t,naturalHeight:n})}})))}const k=e=>{var{imgAttributes:t,heightInt:n,widthInt:r,qualityInt:o,layout:a,className:l,imgStyle:s,blurStyle:u,isLazy:c,placeholder:f,loading:h,srcString:g,config:m,unoptimized:v,loader:y,onLoadingCompleteRef:w,setBlurComplete:x,setIntersection:k,onLoad:S,onError:_,isVisible:O}=e,C=p(e,["imgAttributes","heightInt","widthInt","qualityInt","layout","className","imgStyle","blurStyle","isLazy","placeholder","loading","srcString","config","unoptimized","loader","onLoadingCompleteRef","setBlurComplete","setIntersection","onLoad","onError","isVisible"]);return i.default.createElement(i.default.Fragment,null,i.default.createElement("img",Object.assign({},C,t,"raw"===a?{height:n,width:r}:{},{decoding:"async","data-nimg":a,className:l,style:d({},s,u),ref:i.useCallback((e=>{k(e),(null==e?void 0:e.complete)&&E(e,g,0,f,w,x)}),[k,g,a,f,w,x]),onLoad:e=>{E(e.currentTarget,g,0,f,w,x),S&&S(e)},onError:e=>{"blur"===f&&x(!0),_&&_(e)}})),(c||"blur"===f)&&i.default.createElement("noscript",null,i.default.createElement("img",Object.assign({},C,b({config:m,src:g,unoptimized:v,layout:a,width:r,quality:o,sizes:t.sizes,loader:y}),"raw"===a?{height:n,width:r}:{},{decoding:"async","data-nimg":a,style:s,className:l,loading:h||"lazy"}))))};function S(e){return"/"===e[0]?e.slice(1):e}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},4529:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,o=(r=n(2784))&&r.__esModule?r:{default:r},i=n(6640),a=n(9518),l=n(3321);const s={};function u(e,t,n,r){if("undefined"==typeof window||!e)return;if(!i.isLocalURL(t))return;e.prefetch(t,n,r).catch((e=>{}));const o=r&&void 0!==r.locale?r.locale:e&&e.locale;s[t+"%"+n+(o?"%"+o:"")]=!0}var c=o.default.forwardRef(((e,t)=>{const{legacyBehavior:n=!0!==Boolean(window.omnivoreEnv.__NEXT_NEW_LINK_BEHAVIOR)}=e;let r;const{href:c,as:f,children:d,prefetch:p,passHref:h,replace:g,shallow:m,scroll:v,locale:y,onClick:b,onMouseEnter:w}=e,x=function(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}(e,["href","as","children","prefetch","passHref","replace","shallow","scroll","locale","onClick","onMouseEnter"]);r=d,n&&"string"==typeof r&&(r=o.default.createElement("a",null,r));const E=!1!==p,k=a.useRouter(),{href:S,as:_}=o.default.useMemo((()=>{const[e,t]=i.resolveHref(k,c,!0);return{href:e,as:f?i.resolveHref(k,f):t||e}}),[k,c,f]),O=o.default.useRef(S),C=o.default.useRef(_);let P;n&&(P=o.default.Children.only(r));const j=n?P&&"object"==typeof P&&P.ref:t,[L,T,R]=l.useIntersection({rootMargin:"200px"}),A=o.default.useCallback((e=>{C.current===_&&O.current===S||(R(),C.current=_,O.current=S),L(e),j&&("function"==typeof j?j(e):"object"==typeof j&&(j.current=e))}),[_,j,S,R,L]);o.default.useEffect((()=>{const e=T&&E&&i.isLocalURL(S),t=void 0!==y?y:k&&k.locale,n=s[S+"%"+_+(t?"%"+t:"")];e&&!n&&u(k,S,_,{locale:t})}),[_,S,T,y,E,k]);const I={ref:A,onClick:e=>{n||"function"!=typeof b||b(e),n&&P.props&&"function"==typeof P.props.onClick&&P.props.onClick(e),e.defaultPrevented||function(e,t,n,r,o,a,l,s){const{nodeName:u}=e.currentTarget;("A"!==u.toUpperCase()||!function(e){const{target:t}=e.currentTarget;return t&&"_self"!==t||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.nativeEvent&&2===e.nativeEvent.which}(e)&&i.isLocalURL(n))&&(e.preventDefault(),t[o?"replace":"push"](n,r,{shallow:a,locale:s,scroll:l}))}(e,k,S,_,g,m,v,y)},onMouseEnter:e=>{n||"function"!=typeof w||w(e),n&&P.props&&"function"==typeof P.props.onMouseEnter&&P.props.onMouseEnter(e),i.isLocalURL(S)&&u(k,S,_,{priority:!0})}};if(!n||h||"a"===P.type&&!("href"in P.props)){const e=void 0!==y?y:k&&k.locale,t=k&&k.isLocaleDomain&&i.getDomainLocale(_,e,k&&k.locales,k&&k.domainLocales);I.href=t||i.addBasePath(i.addLocale(_,e,k&&k.defaultLocale))}return n?o.default.cloneElement(P,I):o.default.createElement("a",Object.assign({},x,I),r)}));t.default=c,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},1119:(e,t)=>{"use strict";function n(e){return e.endsWith("/")&&"/"!==e?e.slice(0,-1):e}Object.defineProperty(t,"__esModule",{value:!0}),t.removePathTrailingSlash=n,t.normalizePathTrailingSlash=void 0;const r=window.omnivoreEnv.__NEXT_TRAILING_SLASH?e=>/\.[^/]+\/?$/.test(e)?n(e):e.endsWith("/")?e:e+"/":n;t.normalizePathTrailingSlash=r,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},1976:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.cancelIdleCallback=t.requestIdleCallback=void 0;const n="undefined"!=typeof self&&self.requestIdleCallback&&self.requestIdleCallback.bind(window)||function(e){let t=Date.now();return setTimeout((function(){e({didTimeout:!1,timeRemaining:function(){return Math.max(0,50-(Date.now()-t))}})}),1)};t.requestIdleCallback=n;const r="undefined"!=typeof self&&self.cancelIdleCallback&&self.cancelIdleCallback.bind(window)||function(e){return clearTimeout(e)};t.cancelIdleCallback=r,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},7928:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.markAssetError=u,t.isAssetError=function(e){return e&&s in e},t.getClientBuildManifest=f,t.getMiddlewareManifest=function(){return self.__MIDDLEWARE_MANIFEST?Promise.resolve(self.__MIDDLEWARE_MANIFEST):c(new Promise((e=>{const t=self.__MIDDLEWARE_MANIFEST_CB;self.__MIDDLEWARE_MANIFEST_CB=()=>{e(self.__MIDDLEWARE_MANIFEST),t&&t()}})),i,u(new Error("Failed to load client middleware manifest")))},t.createRouteLoader=function(e){const t=new Map,n=new Map,r=new Map,s=new Map;function f(e){{let t=n.get(e);return t||(document.querySelector(`script[src^="${e}"]`)?Promise.resolve():(n.set(e,t=function(e,t){return new Promise(((n,r)=>{(t=document.createElement("script")).onload=n,t.onerror=()=>r(u(new Error(`Failed to load script: ${e}`))),t.crossOrigin=window.omnivoreEnv.__NEXT_CROSS_ORIGIN,t.src=e,document.body.appendChild(t)}))}(e)),t))}}function p(e){let t=r.get(e);return t||(r.set(e,t=fetch(e).then((t=>{if(!t.ok)throw new Error(`Failed to load stylesheet: ${e}`);return t.text().then((t=>({href:e,content:t})))})).catch((e=>{throw u(e)}))),t)}return{whenEntrypoint:e=>a(e,t),onEntrypoint(e,n){(n?Promise.resolve().then((()=>n())).then((e=>({component:e&&e.default||e,exports:e})),(e=>({error:e}))):Promise.resolve(void 0)).then((n=>{const r=t.get(e);r&&"resolve"in r?n&&(t.set(e,n),r.resolve(n)):(n?t.set(e,n):t.delete(e),s.delete(e))}))},loadRoute(n,r){return a(n,s,(()=>c(d(e,n).then((({scripts:e,css:r})=>Promise.all([t.has(n)?[]:Promise.all(e.map(f)),Promise.all(r.map(p))]))).then((e=>this.whenEntrypoint(n).then((t=>({entrypoint:t,styles:e[1]}))))),i,u(new Error(`Route did not complete loading: ${n}`))).then((({entrypoint:e,styles:t})=>{const n=Object.assign({styles:t},e);return"error"in e?e:n})).catch((e=>{if(r)throw e;return{error:e}})).finally((()=>{}))))},prefetch(t){let n;return(n=navigator.connection)&&(n.saveData||/2g/.test(n.effectiveType))?Promise.resolve():d(e,t).then((e=>Promise.all(l?e.scripts.map((e=>{return t=e,n="script",new Promise(((e,o)=>{const i=`\n link[rel="prefetch"][href^="${t}"],\n link[rel="preload"][href^="${t}"],\n script[src^="${t}"]`;if(document.querySelector(i))return e();(r=document.createElement("link")).as=n,r.rel="prefetch",r.crossOrigin=window.omnivoreEnv.__NEXT_CROSS_ORIGIN,r.onload=e,r.onerror=o,r.href=t,document.head.appendChild(r)}));var t,n,r})):[]))).then((()=>{o.requestIdleCallback((()=>this.loadRoute(t,!0).catch((()=>{}))))})).catch((()=>{}))}}},(r=n(9983))&&r.__esModule;var r,o=n(1976);const i=3800;function a(e,t,n){let r,o=t.get(e);if(o)return"future"in o?o.future:Promise.resolve(o);const i=new Promise((e=>{r=e}));return t.set(e,o={resolve:r,future:i}),n?n().then((e=>(r(e),e))).catch((n=>{throw t.delete(e),n})):i}const l=function(e){try{return e=document.createElement("link"),!!window.MSInputMethodContext&&!!document.documentMode||e.relList.supports("prefetch")}catch(e){return!1}}(),s=Symbol("ASSET_LOAD_ERROR");function u(e){return Object.defineProperty(e,s,{})}function c(e,t,n){return new Promise(((r,i)=>{let a=!1;e.then((e=>{a=!0,r(e)})).catch(i),o.requestIdleCallback((()=>setTimeout((()=>{a||i(n)}),t)))}))}function f(){return self.__BUILD_MANIFEST?Promise.resolve(self.__BUILD_MANIFEST):c(new Promise((e=>{const t=self.__BUILD_MANIFEST_CB;self.__BUILD_MANIFEST_CB=()=>{e(self.__BUILD_MANIFEST),t&&t()}})),i,u(new Error("Failed to load client build manifest")))}function d(e,t){return f().then((n=>{if(!(t in n))throw u(new Error(`Failed to lookup route: ${t}`));const r=n[t].map((t=>e+"/_next/"+encodeURI(t)));return{scripts:r.filter((e=>e.endsWith(".js"))),css:r.filter((e=>e.endsWith(".css")))}}))}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},9518:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"Router",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,"withRouter",{enumerable:!0,get:function(){return l.default}}),t.useRouter=function(){return r.default.useContext(i.RouterContext)},t.createRouter=function(...e){return u.router=new o.default(...e),u.readyCallbacks.forEach((e=>e())),u.readyCallbacks=[],u.router},t.makePublicRouterInstance=function(e){const t=e,n={};for(const e of c)"object"!=typeof t[e]?n[e]=t[e]:n[e]=Object.assign(Array.isArray(t[e])?[]:{},t[e]);return n.events=o.default.events,f.forEach((e=>{n[e]=(...n)=>t[e](...n)})),n},t.default=void 0;var r=s(n(2784)),o=s(n(6640)),i=n(6510),a=s(n(274)),l=s(n(9564));function s(e){return e&&e.__esModule?e:{default:e}}const u={router:null,readyCallbacks:[],ready(e){if(this.router)return e();"undefined"!=typeof window&&this.readyCallbacks.push(e)}},c=["pathname","route","query","asPath","components","isFallback","basePath","locale","locales","defaultLocale","isReady","isPreview","isLocaleDomain","domainLocales"],f=["push","replace","reload","back","prefetch","beforePopState"];function d(){if(!u.router)throw new Error('No router instance found.\nYou should only use "next/router" on the client side of your app.\n');return u.router}Object.defineProperty(u,"events",{get:()=>o.default.events}),c.forEach((e=>{Object.defineProperty(u,e,{get:()=>d()[e]})})),f.forEach((e=>{u[e]=(...t)=>d()[e](...t)})),["routeChangeStart","beforeHistoryChange","routeChangeComplete","routeChangeError","hashChangeStart","hashChangeComplete"].forEach((e=>{u.ready((()=>{o.default.events.on(e,((...t)=>{const n=`on${e.charAt(0).toUpperCase()}${e.substring(1)}`,r=u;if(r[n])try{r[n](...t)}catch(e){console.error(`Error when running the Router event: ${n}`),console.error(a.default(e)?`${e.message}\n${e.stack}`:e+"")}}))}))}));var p=u;t.default=p,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},9515:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.handleClientScriptLoad=p,t.initScriptLoader=function(e){e.forEach(p),[...document.querySelectorAll('[data-nscript="beforeInteractive"]'),...document.querySelectorAll('[data-nscript="beforePageRender"]')].forEach((e=>{const t=e.id||e.getAttribute("src");c.add(t)}))},t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(2784)),o=n(7177),i=n(9410),a=n(1976);function l(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function s(e){for(var t=1;t{const{src:t,id:n,onLoad:r=(()=>{}),dangerouslySetInnerHTML:o,children:a="",strategy:l="afterInteractive",onError:s}=e,d=n||t;if(d&&c.has(d))return;if(u.has(t))return c.add(d),void u.get(t).then(r,s);const p=document.createElement("script"),h=new Promise(((e,t)=>{p.addEventListener("load",(function(t){e(),r&&r.call(this,t)})),p.addEventListener("error",(function(e){t(e)}))})).catch((function(e){s&&s(e)}));t&&u.set(t,h),c.add(d),o?p.innerHTML=o.__html||"":a?p.textContent="string"==typeof a?a:Array.isArray(a)?a.join(""):"":t&&(p.src=t);for(const[t,n]of Object.entries(e)){if(void 0===n||f.includes(t))continue;const e=i.DOMAttributeNames[t]||t.toLowerCase();p.setAttribute(e,n)}"worker"===l&&p.setAttribute("type","text/partytown"),p.setAttribute("data-nscript",l),document.body.appendChild(p)};function p(e){const{strategy:t="afterInteractive"}=e;"lazyOnload"===t?window.addEventListener("load",(()=>{a.requestIdleCallback((()=>d(e)))})):d(e)}t.default=function(e){const{src:t="",onLoad:n=(()=>{}),strategy:i="afterInteractive",onError:l}=e,u=function(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}(e,["src","onLoad","strategy","onError"]),{updateScripts:f,scripts:p,getIsSsr:h}=r.useContext(o.HeadManagerContext);return r.useEffect((()=>{"afterInteractive"===i?d(e):"lazyOnload"===i&&function(e){"complete"===document.readyState?a.requestIdleCallback((()=>d(e))):window.addEventListener("load",(()=>{a.requestIdleCallback((()=>d(e)))}))}(e)}),[e,i]),"beforeInteractive"!==i&&"worker"!==i||(f?(p[i]=(p[i]||[]).concat([s({src:t,onLoad:n,onError:l},u)]),f(p)):h&&h()?c.add(u.id||t):h&&!h()&&d(e)),null},("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},3321:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.useIntersection=function({rootRef:e,rootMargin:t,disabled:n}){const s=n||!i,u=r.useRef(),[c,f]=r.useState(!1),[d,p]=r.useState(e?e.current:null),h=r.useCallback((e=>{u.current&&(u.current(),u.current=void 0),s||c||e&&e.tagName&&(u.current=function(e,t,n){const{id:r,observer:o,elements:i}=function(e){const t={root:e.root||null,margin:e.rootMargin||""};let n,r=l.find((e=>e.root===t.root&&e.margin===t.margin));if(r?n=a.get(r):(n=a.get(t),l.push(t)),n)return n;const o=new Map,i=new IntersectionObserver((e=>{e.forEach((e=>{const t=o.get(e.target),n=e.isIntersecting||e.intersectionRatio>0;t&&n&&t(n)}))}),e);return a.set(t,n={id:t,observer:i,elements:o}),n}(n);return i.set(e,(e=>e&&f(e))),o.observe(e),function(){if(i.delete(e),o.unobserve(e),0===i.size){o.disconnect(),a.delete(r);let e=l.findIndex((e=>e.root===r.root&&e.margin===r.margin));e>-1&&l.splice(e,1)}}}(e,0,{root:d,rootMargin:t}))}),[s,d,t,c]),g=r.useCallback((()=>{f(!1)}),[]);return r.useEffect((()=>{if(!i&&!c){const e=o.requestIdleCallback((()=>f(!0)));return()=>o.cancelIdleCallback(e)}}),[c]),r.useEffect((()=>{e&&p(e.current)}),[e]),[h,c,g]};var r=n(2784),o=n(1976);const i="undefined"!=typeof IntersectionObserver,a=new Map,l=[];("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},9564:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){function t(t){return o.default.createElement(e,Object.assign({router:i.useRouter()},t))}return t.getInitialProps=e.getInitialProps,t.origGetInitialProps=e.origGetInitialProps,t};var r,o=(r=n(2784))&&r.__esModule?r:{default:r},i=n(9518);("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},9264:(e,t)=>{"use strict";function n(e,t){void 0===t&&(t={});for(var n=function(e){for(var t=[],n=0;n=48&&s<=57||s>=65&&s<=90||s>=97&&s<=122||95===s))break;a+=e[l++]}if(!a)throw new TypeError("Missing parameter name at "+n);t.push({type:"NAME",index:n,value:a}),n=l}else t.push({type:"CLOSE",index:n,value:e[n++]});else t.push({type:"OPEN",index:n,value:e[n++]});else t.push({type:"ESCAPED_CHAR",index:n++,value:e[n++]});else t.push({type:"MODIFIER",index:n,value:e[n++]})}return t.push({type:"END",index:n,value:""}),t}(e),r=t.prefixes,o=void 0===r?"./":r,a="[^"+i(t.delimiter||"/#?")+"]+?",l=[],s=0,u=0,c="",f=function(e){if(u-1:void 0===E;o||(g+="(?:"+h+"(?="+p+"))?"),k||(g+="(?="+h+"|"+p+")")}return new RegExp(g,a(n))}function s(e,t,r){return e instanceof RegExp?function(e,t){if(!t)return e;var n=e.source.match(/\((?!\?)/g);if(n)for(var r=0;r{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=o,t.getProperError=function(e){return o(e)?e:new Error(r.isPlainObject(e)?JSON.stringify(e):e+"")};var r=n(9910);function o(e){return"object"==typeof e&&null!==e&&"name"in e&&"message"in e}},1719:(e,t,n)=>{"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.AmpStateContext=void 0;const o=((r=n(2784))&&r.__esModule?r:{default:r}).default.createContext({});t.AmpStateContext=o},9739:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isInAmpMode=a,t.useAmp=function(){return a(o.default.useContext(i.AmpStateContext))};var r,o=(r=n(2784))&&r.__esModule?r:{default:r},i=n(1719);function a({ampFirst:e=!1,hybrid:t=!1,hasQuery:n=!1}={}){return e||t&&n}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},8058:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.escapeStringRegexp=function(e){return n.test(e)?e.replace(r,"\\$&"):e};const n=/[|\\{}()[\]^$+*?.-]/,r=/[|\\{}()[\]^$+*?.-]/g},7177:(e,t,n)=>{"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.HeadManagerContext=void 0;const o=((r=n(2784))&&r.__esModule?r:{default:r}).default.createContext({});t.HeadManagerContext=o},5977:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.defaultHead=u,t.default=void 0;var r,o=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(2784)),i=(r=n(1889))&&r.__esModule?r:{default:r},a=n(1719),l=n(7177),s=n(9739);function u(e=!1){const t=[o.default.createElement("meta",{charSet:"utf-8"})];return e||t.push(o.default.createElement("meta",{name:"viewport",content:"width=device-width"})),t}function c(e,t){return"string"==typeof t||"number"==typeof t?e:t.type===o.default.Fragment?e.concat(o.default.Children.toArray(t.props.children).reduce(((e,t)=>"string"==typeof t||"number"==typeof t?e:e.concat(t)),[])):e.concat(t)}n(1624);const f=["name","httpEquiv","charSet","itemProp"];function d(e,t){return e.reduce(((e,t)=>{const n=o.default.Children.toArray(t.props.children);return e.concat(n)}),[]).reduce(c,[]).reverse().concat(u(t.inAmpMode)).filter(function(){const e=new Set,t=new Set,n=new Set,r={};return o=>{let i=!0,a=!1;if(o.key&&"number"!=typeof o.key&&o.key.indexOf("$")>0){a=!0;const t=o.key.slice(o.key.indexOf("$")+1);e.has(t)?i=!1:e.add(t)}switch(o.type){case"title":case"base":t.has(o.type)?i=!1:t.add(o.type);break;case"meta":for(let e=0,t=f.length;e{const r=e.key||n;if(window.omnivoreEnv.__NEXT_OPTIMIZE_FONTS&&!t.inAmpMode&&"link"===e.type&&e.props.href&&["https://fonts.googleapis.com/css","https://use.typekit.net/"].some((t=>e.props.href.startsWith(t)))){const t={...e.props||{}};return t["data-href"]=t.href,t.href=void 0,t["data-optimized-fonts"]=!0,o.default.cloneElement(e,t)}return o.default.cloneElement(e,{key:r})}))}t.default=function({children:e}){const t=o.useContext(a.AmpStateContext),n=o.useContext(l.HeadManagerContext);return o.default.createElement(i.default,{reduceComponentsToState:d,headManager:n,inAmpMode:s.isInAmpMode(t)},e)},("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&(Object.assign(t.default,t),e.exports=t.default)},927:(e,t)=>{"use strict";t.D=function(e,t,n){let r;if(e){n&&(n=n.toLowerCase());for(const a of e){var o,i;if(t===(null===(o=a.domain)||void 0===o?void 0:o.split(":")[0].toLowerCase())||n===a.defaultLocale.toLowerCase()||(null===(i=a.locales)||void 0===i?void 0:i.some((e=>e.toLowerCase()===n)))){r=a;break}}}return r}},816:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.normalizeLocalePath=function(e,t){let n;const r=e.split("/");return(t||[]).some((t=>!(!r[1]||r[1].toLowerCase()!==t.toLowerCase()||(n=t,r.splice(1,1),e=r.join("/")||"/",0)))),{pathname:e,detectedLocale:n}}},4329:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ImageConfigContext=void 0;var r,o=(r=n(2784))&&r.__esModule?r:{default:r},i=n(8467);const a=o.default.createContext(i.imageConfigDefault);t.ImageConfigContext=a},8467:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.imageConfigDefault=t.VALID_LOADERS=void 0,t.VALID_LOADERS=["default","imgix","cloudinary","akamai","custom"];t.imageConfigDefault={deviceSizes:[640,750,828,1080,1200,1920,2048,3840],imageSizes:[16,32,48,64,96,128,256,384],path:"/_next/image",loader:"default",domains:[],disableStaticImages:!1,minimumCacheTTL:60,formats:["image/webp"],dangerouslyAllowSVG:!1,contentSecurityPolicy:"script-src 'none'; frame-src 'none'; sandbox;"}},9910:(e,t)=>{"use strict";function n(e){return Object.prototype.toString.call(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.getObjectClassLabel=n,t.isPlainObject=function(e){if("[object Object]"!==n(e))return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}},7471:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){const e=Object.create(null);return{on(t,n){(e[t]||(e[t]=[])).push(n)},off(t,n){e[t]&&e[t].splice(e[t].indexOf(n)>>>0,1)},emit(t,...n){(e[t]||[]).slice().map((e=>{e(...n)}))}}}},997:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.denormalizePagePath=function(e){let t=o.normalizePathSep(e);return t.startsWith("/index/")&&!r.isDynamicRoute(t)?t.slice(6):"/index"!==t?t:"/"};var r=n(9150),o=n(9356)},9356:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.normalizePathSep=function(e){return e.replace(/\\/g,"/")}},6510:(e,t,n)=>{"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.RouterContext=void 0;const o=((r=n(2784))&&r.__esModule?r:{default:r}).default.createContext(null);t.RouterContext=o},6640:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getDomainLocale=function(e,t,n,r){if(window.omnivoreEnv.__NEXT_I18N_SUPPORT){t=t||s.normalizeLocalePath(e,n).detectedLocale;const o=w(r,void 0,t);return!!o&&`http${o.http?"":"s"}://${o.domain}${x||""}${t===o.defaultLocale?"":`/${t}`}${e}`}return!1},t.addLocale=_,t.delLocale=O,t.hasBasePath=P,t.addBasePath=j,t.delBasePath=L,t.isLocalURL=T,t.interpolateAs=R,t.resolveHref=I,t.default=void 0;var r=n(1119),o=n(7928),i=n(9515),a=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(274)),l=n(997),s=n(816),u=b(n(7471)),c=n(1624),f=n(7482),d=n(1577),p=n(646),h=b(n(5317)),g=n(3107),m=n(4794),v=n(2763),y=n(6555);function b(e){return e&&e.__esModule?e:{default:e}}let w;window.omnivoreEnv.__NEXT_I18N_SUPPORT&&(w=n(927).D);const x=window.omnivoreEnv.__NEXT_ROUTER_BASEPATH||"";function E(){return Object.assign(new Error("Route Cancelled"),{cancelled:!0})}function k(e,t){if(!e.startsWith("/")||!t)return e;const n=C(e);return r.normalizePathTrailingSlash(`${t}${n}`)+e.slice(n.length)}function S(e,t){return(e=C(e))===t||e.startsWith(t+"/")}function _(e,t,n){if(window.omnivoreEnv.__NEXT_I18N_SUPPORT&&t&&t!==n){const n=C(e).toLowerCase();if(!S(n,"/"+t.toLowerCase())&&!S(n,"/api"))return k(e,"/"+t)}return e}function O(e,t){if(window.omnivoreEnv.__NEXT_I18N_SUPPORT){const n=C(e),r=n.toLowerCase(),o=t&&t.toLowerCase();return t&&(r.startsWith("/"+o+"/")||r==="/"+o)?(n.length===t.length+1?"/":"")+e.slice(t.length+1):e}return e}function C(e){const t=e.indexOf("?"),n=e.indexOf("#");return(t>-1||n>-1)&&(e=e.substring(0,t>-1?t:n)),e}function P(e){return S(e,x)}function j(e){return k(e,x)}function L(e){return(e=e.slice(x.length)).startsWith("/")||(e=`/${e}`),e}function T(e){if(e.startsWith("/")||e.startsWith("#")||e.startsWith("?"))return!0;try{const t=c.getLocationOrigin(),n=new URL(e,t);return n.origin===t&&P(n.pathname)}catch(e){return!1}}function R(e,t,n){let r="";const o=m.getRouteRegex(e),i=o.groups,a=(t!==e?g.getRouteMatcher(o)(t):"")||n;r=e;const l=Object.keys(i);return l.every((e=>{let t=a[e]||"";const{repeat:n,optional:o}=i[e];let l=`[${n?"...":""}${e}]`;return o&&(l=`${t?"":"/"}[${l}]`),n&&!Array.isArray(t)&&(t=[t]),(o||e in a)&&(r=r.replace(l,n?t.map((e=>encodeURIComponent(e))).join("/"):encodeURIComponent(t))||"/")}))||(r=""),{params:l,result:r}}function A(e,t){const n={};return Object.keys(e).forEach((r=>{t.includes(r)||(n[r]=e[r])})),n}function I(e,t,n){let o,i="string"==typeof t?t:y.formatWithValidation(t);const a=i.match(/^[a-zA-Z]{1,}:\/\//),l=a?i.slice(a[0].length):i;if((l.split("?")[0]||"").match(/(\/\/|\\)/)){console.error(`Invalid href passed to next/router: ${i}, repeated forward-slashes (//) or backslashes \\ are not valid in the href`);const e=c.normalizeRepeatedSlashes(l);i=(a?a[0]:"")+e}if(!T(i))return n?[i]:i;try{o=new URL(i.startsWith("#")?e.asPath:e.pathname,"http://n")}catch(e){o=new URL("/","http://n")}try{const e=new URL(i,o);e.pathname=r.normalizePathTrailingSlash(e.pathname);let t="";if(f.isDynamicRoute(e.pathname)&&e.searchParams&&n){const n=p.searchParamsToUrlQuery(e.searchParams),{result:r,params:o}=R(e.pathname,e.pathname,n);r&&(t=y.formatWithValidation({pathname:r,hash:e.hash,query:A(n,o)}))}const a=e.origin===o.origin?e.href.slice(e.origin.length):e.href;return n?[a,t||a]:a}catch(e){return n?[i]:i}}function M(e){const t=c.getLocationOrigin();return e.startsWith(t)?e.substring(t.length):e}function D(e,t,n){let[r,o]=I(e,t,!0);const i=c.getLocationOrigin(),a=r.startsWith(i),l=o&&o.startsWith(i);r=M(r),o=o?M(o):o;const s=a?r:j(r),u=n?M(I(e,n)):o||r;return{url:s,as:l?u:j(u)}}function N(e,t){const n=r.removePathTrailingSlash(l.denormalizePagePath(e));return"/404"===n||"/_error"===n?e:(t.includes(n)||t.some((t=>{if(f.isDynamicRoute(t)&&m.getRouteRegex(t).re.test(n))return e=t,!0})),r.removePathTrailingSlash(e))}const z=window.omnivoreEnv.__NEXT_SCROLL_RESTORATION&&"undefined"!=typeof window&&"scrollRestoration"in window.history&&!!function(){try{let e="__next";return sessionStorage.setItem(e,e),sessionStorage.removeItem(e),!0}catch(e){}}(),F=Symbol("SSG_DATA_NOT_FOUND");function $(e,t,n){return fetch(e,{credentials:"same-origin"}).then((r=>{if(!r.ok){if(t>1&&r.status>=500)return $(e,t-1,n);if(404===r.status)return r.json().then((e=>{if(e.notFound)return{notFound:F};throw new Error("Failed to load static props")}));throw new Error("Failed to load static props")}return n.text?r.text():r.json()}))}function B(e,t,n,r,i){const{href:a}=new URL(e,window.location.href);return void 0!==r[a]?r[a]:r[a]=$(e,t?3:1,{text:n}).catch((e=>{throw t||o.markAssetError(e),e})).then((e=>(i||delete r[a],e))).catch((e=>{throw delete r[a],e}))}class U{constructor(e,t,n,{initialProps:o,pageLoader:i,App:a,wrapApp:l,Component:s,err:u,subscription:p,isFallback:h,locale:g,locales:m,defaultLocale:v,domainLocales:b,isPreview:E,isRsc:k}){this.sdc={},this.sdr={},this.sde={},this._idx=0,this.onPopState=e=>{const t=e.state;if(!t){const{pathname:e,query:t}=this;return void this.changeState("replaceState",y.formatWithValidation({pathname:j(e),query:t}),c.getURL())}if(!t.__N)return;let n;const{url:r,as:o,options:i,idx:a}=t;if(window.omnivoreEnv.__NEXT_SCROLL_RESTORATION&&z&&this._idx!==a){try{sessionStorage.setItem("__next_scroll_"+this._idx,JSON.stringify({x:self.pageXOffset,y:self.pageYOffset}))}catch{}try{const e=sessionStorage.getItem("__next_scroll_"+a);n=JSON.parse(e)}catch{n={x:0,y:0}}}this._idx=a;const{pathname:l}=d.parseRelativeUrl(r);this.isSsr&&o===j(this.asPath)&&l===j(this.pathname)||this._bps&&!this._bps(t)||this.change("replaceState",r,o,Object.assign({},i,{shallow:i.shallow&&this._shallow,locale:i.locale||this.defaultLocale}),n)};const S=r.removePathTrailingSlash(e);this.components={},"/_error"!==e&&(this.components[S]={Component:s,initial:!0,props:o,err:u,__N_SSG:o&&o.__N_SSG,__N_SSP:o&&o.__N_SSP,__N_RSC:!!k}),this.components["/_app"]={Component:a,styleSheets:[]},this.events=U.events,this.pageLoader=i;const _=f.isDynamicRoute(e)&&self.__NEXT_DATA__.autoExport;if(this.basePath=x,this.sub=p,this.clc=null,this._wrapApp=l,this.isSsr=!0,this.isLocaleDomain=!1,this.isReady=!(!(self.__NEXT_DATA__.gssp||self.__NEXT_DATA__.gip||self.__NEXT_DATA__.appGip&&!self.__NEXT_DATA__.gsp)&&(_||self.location.search||window.omnivoreEnv.__NEXT_HAS_REWRITES)),window.omnivoreEnv.__NEXT_I18N_SUPPORT&&(this.locales=m,this.defaultLocale=v,this.domainLocales=b,this.isLocaleDomain=!!w(b,self.location.hostname)),this.state={route:S,pathname:e,query:t,asPath:_?e:n,isPreview:!!E,locale:window.omnivoreEnv.__NEXT_I18N_SUPPORT?g:void 0,isFallback:h},"undefined"!=typeof window){if(!n.startsWith("//")){const r={locale:g};r._shouldResolveHref=n!==e,this.changeState("replaceState",y.formatWithValidation({pathname:j(e),query:t}),c.getURL(),r)}window.addEventListener("popstate",this.onPopState),window.omnivoreEnv.__NEXT_SCROLL_RESTORATION&&z&&(window.history.scrollRestoration="manual")}}reload(){window.location.reload()}back(){window.history.back()}push(e,t,n={}){if(window.omnivoreEnv.__NEXT_SCROLL_RESTORATION&&z)try{sessionStorage.setItem("__next_scroll_"+this._idx,JSON.stringify({x:self.pageXOffset,y:self.pageYOffset}))}catch{}return({url:e,as:t}=D(this,e,t)),this.change("pushState",e,t,n)}replace(e,t,n={}){return({url:e,as:t}=D(this,e,t)),this.change("replaceState",e,t,n)}async change(e,t,n,l,u){if(!T(t))return window.location.href=t,!1;const p=l._h||l._shouldResolveHref||C(t)===C(n),v={...this.state};l._h&&(this.isReady=!0);const b=v.locale;if(window.omnivoreEnv.__NEXT_I18N_SUPPORT){v.locale=!1===l.locale?this.defaultLocale:l.locale||v.locale,void 0===l.locale&&(l.locale=v.locale);const e=d.parseRelativeUrl(P(n)?L(n):n),r=s.normalizeLocalePath(e.pathname,this.locales);r.detectedLocale&&(v.locale=r.detectedLocale,e.pathname=j(e.pathname),n=y.formatWithValidation(e),t=j(s.normalizeLocalePath(P(t)?L(t):t,this.locales).pathname));let o=!1;var x;window.omnivoreEnv.__NEXT_I18N_SUPPORT&&((null===(x=this.locales)||void 0===x?void 0:x.includes(v.locale))||(e.pathname=_(e.pathname,v.locale),window.location.href=y.formatWithValidation(e),o=!0));const i=w(this.domainLocales,void 0,v.locale);if(window.omnivoreEnv.__NEXT_I18N_SUPPORT&&!o&&i&&this.isLocaleDomain&&self.location.hostname!==i.domain){const e=L(n);window.location.href=`http${i.http?"":"s"}://${i.domain}${j(`${v.locale===i.defaultLocale?"":`/${v.locale}`}${"/"===e?"":e}`||"/")}`,o=!0}if(o)return new Promise((()=>{}))}l._h||(this.isSsr=!1),c.ST&&performance.mark("routeChange");const{shallow:E=!1,scroll:k=!0}=l,S={shallow:E};this._inFlightRoute&&this.abortComponentLoad(this._inFlightRoute,S),n=j(_(P(n)?L(n):n,l.locale,this.defaultLocale));const I=O(P(n)?L(n):n,v.locale);this._inFlightRoute=n;let M=b!==v.locale;if(!l._h&&this.onlyAHashChange(I)&&!M)return v.asPath=I,U.events.emit("hashChangeStart",n,S),this.changeState(e,t,n,{...l,scroll:!1}),k&&this.scrollToHash(I),this.set(v,this.components[v.route],null),U.events.emit("hashChangeComplete",n,S),!0;let z,$,B=d.parseRelativeUrl(t),{pathname:W,query:H}=B;try{[z,{__rewrites:$}]=await Promise.all([this.pageLoader.getPageList(),o.getClientBuildManifest(),this.pageLoader.getMiddlewareList()])}catch(e){return window.location.href=n,!1}this.urlIsNew(I)||M||(e="replaceState");let V=n;if(W=W?r.removePathTrailingSlash(L(W)):W,p&&"/_error"!==W)if(l._shouldResolveHref=!0,window.omnivoreEnv.__NEXT_HAS_REWRITES&&n.startsWith("/")){const e=h.default(j(_(I,v.locale)),z,$,H,(e=>N(e,z)),this.locales);if(e.externalDest)return location.href=n,!0;V=e.asPath,e.matchedPage&&e.resolvedHref&&(W=e.resolvedHref,B.pathname=j(W),t=y.formatWithValidation(B))}else B.pathname=N(W,z),B.pathname!==W&&(W=B.pathname,B.pathname=j(W),t=y.formatWithValidation(B));if(!T(n))return window.location.href=n,!1;if(V=O(L(V),v.locale),(!l.shallow||1===l._h)&&(1!==l._h||f.isDynamicRoute(r.removePathTrailingSlash(W)))){const r=await this._preflightRequest({as:n,cache:!0,pages:z,pathname:W,query:H,locale:v.locale,isPreview:v.isPreview});if("rewrite"===r.type)H={...H,...r.parsedAs.query},V=r.asPath,W=r.resolvedHref,B.pathname=r.resolvedHref,t=y.formatWithValidation(B);else{if("redirect"===r.type&&r.newAs)return this.change(e,r.newUrl,r.newAs,l);if("redirect"===r.type&&r.destination)return window.location.href=r.destination,new Promise((()=>{}));if("refresh"===r.type&&n!==window.location.pathname)return window.location.href=n,new Promise((()=>{}))}}const q=r.removePathTrailingSlash(W);if(f.isDynamicRoute(q)){const e=d.parseRelativeUrl(V),r=e.pathname,o=m.getRouteRegex(q),i=g.getRouteMatcher(o)(r),a=q===r,l=a?R(q,r,H):{};if(!i||a&&!l.result){const e=Object.keys(o.groups).filter((e=>!H[e]));if(e.length>0)throw new Error((a?`The provided \`href\` (${t}) value is missing query values (${e.join(", ")}) to be interpolated properly. `:`The provided \`as\` value (${r}) is incompatible with the \`href\` value (${q}). `)+"Read more: https://nextjs.org/docs/messages/"+(a?"href-interpolation-failed":"incompatible-href-as"))}else a?n=y.formatWithValidation(Object.assign({},e,{pathname:l.result,query:A(H,l.params)})):Object.assign(H,i)}U.events.emit("routeChangeStart",n,S);try{var X,G;let r=await this.getRouteInfo(q,W,H,n,V,S,v.locale,v.isPreview),{error:o,props:a,__N_SSG:s,__N_SSP:c}=r;const f=r.Component;if(f&&f.unstable_scriptLoader&&[].concat(f.unstable_scriptLoader()).forEach((e=>{i.handleClientScriptLoad(e.props)})),(s||c)&&a){if(a.pageProps&&a.pageProps.__N_REDIRECT){const t=a.pageProps.__N_REDIRECT;if(t.startsWith("/")&&!1!==a.pageProps.__N_REDIRECT_BASE_PATH){const n=d.parseRelativeUrl(t);n.pathname=N(n.pathname,z);const{url:r,as:o}=D(this,t,t);return this.change(e,r,o,l)}return window.location.href=t,new Promise((()=>{}))}if(v.isPreview=!!a.__N_PREVIEW,a.notFound===F){let e;try{await this.fetchComponent("/404"),e="/404"}catch(t){e="/_error"}r=await this.getRouteInfo(e,e,H,n,V,{shallow:!1},v.locale,v.isPreview)}}U.events.emit("beforeHistoryChange",n,S),this.changeState(e,t,n,l),l._h&&"/_error"===W&&500===(null===(X=self.__NEXT_DATA__.props)||void 0===X||null===(G=X.pageProps)||void 0===G?void 0:G.statusCode)&&(null==a?void 0:a.pageProps)&&(a.pageProps.statusCode=500);const p=l.shallow&&v.route===q;var Y;const h=(null!==(Y=l.scroll)&&void 0!==Y?Y:!p)?{x:0,y:0}:null;if(await this.set({...v,route:q,pathname:W,query:H,asPath:I,isFallback:!1},r,null!=u?u:h).catch((e=>{if(!e.cancelled)throw e;o=o||e})),o)throw U.events.emit("routeChangeError",o,I,S),o;return window.omnivoreEnv.__NEXT_I18N_SUPPORT&&v.locale&&(document.documentElement.lang=v.locale),U.events.emit("routeChangeComplete",n,S),!0}catch(e){if(a.default(e)&&e.cancelled)return!1;throw e}}changeState(e,t,n,r={}){"pushState"===e&&c.getURL()===n||(this._shallow=r.shallow,window.history[e]({url:t,as:n,options:r,__N:!0,idx:this._idx="pushState"!==e?this._idx:this._idx+1},"",n))}async handleRouteInfoError(e,t,n,r,i,l){if(e.cancelled)throw e;if(o.isAssetError(e)||l)throw U.events.emit("routeChangeError",e,r,i),window.location.href=r,E();try{let r,o,i;void 0!==r&&void 0!==o||({page:r,styleSheets:o}=await this.fetchComponent("/_error"));const a={props:i,Component:r,styleSheets:o,err:e,error:e};if(!a.props)try{a.props=await this.getInitialProps(r,{err:e,pathname:t,query:n})}catch(e){console.error("Error in error page `getInitialProps`: ",e),a.props={}}return a}catch(e){return this.handleRouteInfoError(a.default(e)?e:new Error(e+""),t,n,r,i,!0)}}async getRouteInfo(e,t,n,r,o,i,l,s){try{const a=this.components[e];if(i.shallow&&a&&this.route===e)return a;let u;a&&!("initial"in a)&&(u=a);const c=u||await this.fetchComponent(e).then((e=>({Component:e.page,styleSheets:e.styleSheets,__N_SSG:e.mod.__N_SSG,__N_SSP:e.mod.__N_SSP,__N_RSC:!!e.mod.__next_rsc__}))),{Component:f,__N_SSG:d,__N_SSP:p,__N_RSC:h}=c;let g;const m=p&&h;(d||p||h)&&(g=this.pageLoader.getDataHref({href:y.formatWithValidation({pathname:t,query:n}),asPath:o,ssg:d,flight:m,locale:l}));const v=await this._getData((()=>(d||p||h)&&!m?B(g,this.isSsr,!1,d?this.sdc:this.sdr,!!d&&!s):this.getInitialProps(f,{pathname:t,query:n,asPath:r,locale:l,locales:this.locales,defaultLocale:this.defaultLocale})));if(h)if(m){const{data:e}=await this._getData((()=>this._getFlightData(g)));v.pageProps=Object.assign(v.pageProps,{__flight__:e})}else{const{__flight__:e}=v;v.pageProps=Object.assign({},v.pageProps,{__flight__:e})}return c.props=v,this.components[e]=c,c}catch(e){return this.handleRouteInfoError(a.getProperError(e),t,n,r,i)}}set(e,t,n){return this.state=e,this.sub(t,this.components["/_app"].Component,n)}beforePopState(e){this._bps=e}onlyAHashChange(e){if(!this.asPath)return!1;const[t,n]=this.asPath.split("#"),[r,o]=e.split("#");return!(!o||t!==r||n!==o)||t===r&&n!==o}scrollToHash(e){const[,t=""]=e.split("#");if(""===t||"top"===t)return void window.scrollTo(0,0);const n=document.getElementById(t);if(n)return void n.scrollIntoView();const r=document.getElementsByName(t)[0];r&&r.scrollIntoView()}urlIsNew(e){return this.asPath!==e}async prefetch(e,t=e,n={}){let i=d.parseRelativeUrl(e),{pathname:a,query:l}=i;if(window.omnivoreEnv.__NEXT_I18N_SUPPORT&&!1===n.locale){a=s.normalizeLocalePath(a,this.locales).pathname,i.pathname=a,e=y.formatWithValidation(i);let r=d.parseRelativeUrl(t);const o=s.normalizeLocalePath(r.pathname,this.locales);r.pathname=o.pathname,n.locale=o.detectedLocale||this.defaultLocale,t=y.formatWithValidation(r)}const u=await this.pageLoader.getPageList();let c=t;if(window.omnivoreEnv.__NEXT_HAS_REWRITES&&t.startsWith("/")){let n;({__rewrites:n}=await o.getClientBuildManifest());const r=h.default(j(_(t,this.locale)),u,n,i.query,(e=>N(e,u)),this.locales);if(r.externalDest)return;c=O(L(r.asPath),this.locale),r.matchedPage&&r.resolvedHref&&(a=r.resolvedHref,i.pathname=a,e=y.formatWithValidation(i))}else i.pathname=N(i.pathname,u),i.pathname!==a&&(a=i.pathname,i.pathname=a,e=y.formatWithValidation(i));const f=await this._preflightRequest({as:j(t),cache:!0,pages:u,pathname:a,query:l,locale:this.locale,isPreview:this.isPreview});"rewrite"===f.type&&(i.pathname=f.resolvedHref,a=f.resolvedHref,l={...l,...f.parsedAs.query},c=f.asPath,e=y.formatWithValidation(i));const p=r.removePathTrailingSlash(a);await Promise.all([this.pageLoader._isSsg(p).then((t=>!!t&&B(this.pageLoader.getDataHref({href:e,asPath:c,ssg:!0,locale:void 0!==n.locale?n.locale:this.locale}),!1,!1,this.sdc,!0))),this.pageLoader[n.priority?"loadPage":"prefetch"](p)])}async fetchComponent(e){let t=!1;const n=this.clc=()=>{t=!0},r=()=>{if(t){const t=new Error(`Abort fetching component for route: "${e}"`);throw t.cancelled=!0,t}n===this.clc&&(this.clc=null)};try{const t=await this.pageLoader.loadPage(e);return r(),t}catch(e){throw r(),e}}_getData(e){let t=!1;const n=()=>{t=!0};return this.clc=n,e().then((e=>{if(n===this.clc&&(this.clc=null),t){const e=new Error("Loading initial props cancelled");throw e.cancelled=!0,e}return e}))}_getFlightData(e){return B(e,!0,!0,this.sdc,!1).then((e=>({data:e})))}async _preflightRequest(e){const t=C(e.as),n=O(P(t)?L(t):t,e.locale);if(!(await this.pageLoader.getMiddlewareList()).some((([e,t])=>g.getRouteMatcher(v.getMiddlewareRegex(e,!t))(n))))return{type:"next"};const o=_(e.as,e.locale);let i;try{i=await this._getPreflightData({preflightHref:o,shouldCache:e.cache,isPreview:e.isPreview})}catch(t){return{type:"redirect",destination:e.as}}if(i.rewrite){if(!i.rewrite.startsWith("/"))return{type:"redirect",destination:e.as};const t=d.parseRelativeUrl(s.normalizeLocalePath(P(i.rewrite)?L(i.rewrite):i.rewrite,this.locales).pathname),n=r.removePathTrailingSlash(t.pathname);let o,a;return e.pages.includes(n)?(o=!0,a=n):(a=N(n,e.pages),a!==t.pathname&&e.pages.includes(a)&&(o=!0)),{type:"rewrite",asPath:t.pathname,parsedAs:t,matchedPage:o,resolvedHref:a}}if(i.redirect){if(i.redirect.startsWith("/")){const e=r.removePathTrailingSlash(s.normalizeLocalePath(P(i.redirect)?L(i.redirect):i.redirect,this.locales).pathname),{url:t,as:n}=D(this,e,e);return{type:"redirect",newUrl:t,newAs:n}}return{type:"redirect",destination:i.redirect}}return i.refresh&&!i.ssr?{type:"refresh"}:{type:"next"}}_getPreflightData(e){const{preflightHref:t,shouldCache:n=!1,isPreview:r}=e,{href:o}=new URL(t,window.location.href);return!r&&n&&this.sde[o]?Promise.resolve(this.sde[o]):fetch(t,{method:"HEAD",credentials:"same-origin",headers:{"x-middleware-preflight":"1"}}).then((e=>{if(!e.ok)throw new Error("Failed to preflight request");return{cache:e.headers.get("x-middleware-cache"),redirect:e.headers.get("Location"),refresh:e.headers.has("x-middleware-refresh"),rewrite:e.headers.get("x-middleware-rewrite"),ssr:!!e.headers.get("x-middleware-ssr")}})).then((e=>(n&&"no-cache"!==e.cache&&(this.sde[o]=e),e))).catch((e=>{throw delete this.sde[o],e}))}getInitialProps(e,t){const{Component:n}=this.components["/_app"],r=this._wrapApp(n);return t.AppTree=r,c.loadGetInitialProps(n,{AppTree:r,Component:e,router:this,ctx:t})}abortComponentLoad(e,t){this.clc&&(U.events.emit("routeChangeError",E(),e,t),this.clc(),this.clc=null)}get route(){return this.state.route}get pathname(){return this.state.pathname}get query(){return this.state.query}get asPath(){return this.state.asPath}get locale(){return this.state.locale}get isFallback(){return this.state.isFallback}get isPreview(){return this.state.isPreview}}t.default=U,U.events=u.default()},6555:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.formatUrl=i,t.formatWithValidation=function(e){return i(e)},t.urlObjectKeys=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(646));const o=/https?|ftp|gopher|file/;function i(e){let{auth:t,hostname:n}=e,i=e.protocol||"",a=e.pathname||"",l=e.hash||"",s=e.query||"",u=!1;t=t?encodeURIComponent(t).replace(/%3A/i,":")+"@":"",e.host?u=t+e.host:n&&(u=t+(~n.indexOf(":")?`[${n}]`:n),e.port&&(u+=":"+e.port)),s&&"object"==typeof s&&(s=String(r.urlQueryToSearchParams(s)));let c=e.search||s&&`?${s}`||"";return i&&!i.endsWith(":")&&(i+=":"),e.slashes||(!i||o.test(i))&&!1!==u?(u="//"+(u||""),a&&"/"!==a[0]&&(a="/"+a)):u||(u=""),l&&"#"!==l[0]&&(l="#"+l),c&&"?"!==c[0]&&(c="?"+c),a=a.replace(/[?#]/g,encodeURIComponent),c=c.replace("#","%23"),`${i}${u}${a}${c}${l}`}t.urlObjectKeys=["auth","hash","host","hostname","href","path","pathname","port","protocol","query","search","slashes"]},9983:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t=""){return("/"===e?"/index":/^\/index(\/|$)/.test(e)?`/index${e}`:`${e}`)+t}},2763:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getMiddlewareRegex=function(e,t=!0){const n=r.getParametrizedRoute(e);let o=t?"(?!_next).*":"",i=t?"(?:(/.*)?)":"";return"routeKeys"in n?"/"===n.parameterizedRoute?{groups:{},namedRegex:`^/${o}$`,re:new RegExp(`^/${o}$`),routeKeys:{}}:{groups:n.groups,namedRegex:`^${n.namedParameterizedRoute}${i}$`,re:new RegExp(`^${n.parameterizedRoute}${i}$`),routeKeys:n.routeKeys}:"/"===n.parameterizedRoute?{groups:{},re:new RegExp(`^/${o}$`)}:{groups:{},re:new RegExp(`^${n.parameterizedRoute}${i}$`)}};var r=n(4794)},9150:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"getMiddlewareRegex",{enumerable:!0,get:function(){return r.getMiddlewareRegex}}),Object.defineProperty(t,"getRouteMatcher",{enumerable:!0,get:function(){return o.getRouteMatcher}}),Object.defineProperty(t,"getRouteRegex",{enumerable:!0,get:function(){return i.getRouteRegex}}),Object.defineProperty(t,"getSortedRoutes",{enumerable:!0,get:function(){return a.getSortedRoutes}}),Object.defineProperty(t,"isDynamicRoute",{enumerable:!0,get:function(){return l.isDynamicRoute}});var r=n(2763),o=n(3107),i=n(4794),a=n(9036),l=n(7482)},7482:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isDynamicRoute=function(e){return n.test(e)};const n=/\/\[[^/]+?\](?=\/|$)/},1577:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parseRelativeUrl=function(e,t){const n=new URL("undefined"==typeof window?"http://n":r.getLocationOrigin()),i=t?new URL(t,n):n,{pathname:a,searchParams:l,search:s,hash:u,href:c,origin:f}=new URL(e,i);if(f!==n.origin)throw new Error(`invariant: invalid relative URL, router received ${e}`);return{pathname:a,query:o.searchParamsToUrlQuery(l),search:s,hash:u,href:c.slice(n.origin.length)}};var r=n(1624),o=n(646)},2011:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parseUrl=function(e){if(e.startsWith("/"))return o.parseRelativeUrl(e);const t=new URL(e);return{hash:t.hash,hostname:t.hostname,href:t.href,pathname:t.pathname,port:t.port,protocol:t.protocol,query:r.searchParamsToUrlQuery(t.searchParams),search:t.search}};var r=n(646),o=n(1577)},1095:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getPathMatch=function(e,t){const n=[],o=r.pathToRegexp(e,n,{delimiter:"/",sensitive:!1,strict:null==t?void 0:t.strict}),i=r.regexpToFunction((null==t?void 0:t.regexModifier)?new RegExp(t.regexModifier(o.source),o.flags):o,n);return(e,r)=>{const o=null!=e&&i(e);if(!o)return!1;if(null==t?void 0:t.removeUnnamedParams)for(const e of n)"number"==typeof e.name&&delete o.params[e.name];return{...r,...o.params}}};var r=n(9264)},9716:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.matchHas=function(e,t,n){const r={};return!!t.every((t=>{let o,i=t.key;switch(t.type){case"header":i=i.toLowerCase(),o=e.headers[i];break;case"cookie":o=e.cookies[t.key];break;case"query":o=n[i];break;case"host":{const{host:t}=(null==e?void 0:e.headers)||{};o=null==t?void 0:t.split(":")[0].toLowerCase();break}}if(!t.value&&o)return r[function(e){let t="";for(let n=0;n64&&r<91||r>96&&r<123)&&(t+=e[n])}return t}(i)]=o,!0;if(o){const e=new RegExp(`^${t.value}$`),n=Array.isArray(o)?o.slice(-1)[0].match(e):o.match(e);if(n)return Array.isArray(n)&&(n.groups?Object.keys(n.groups).forEach((e=>{r[e]=n.groups[e]})):"host"===t.type&&n[0]&&(r.host=n[0])),!0}return!1}))&&r},t.compileNonPath=a,t.prepareDestination=function(e){const t=Object.assign({},e.query);delete t.__nextLocale,delete t.__nextDefaultLocale;let n=e.destination;for(const r of Object.keys({...e.params,...t}))s=r,n=n.replace(new RegExp(`:${o.escapeStringRegexp(s)}`,"g"),`__ESC_COLON_${s}`);var s;const u=i.parseUrl(n),c=u.query,f=l(`${u.pathname}${u.hash||""}`),d=l(u.hostname||""),p=[],h=[];r.pathToRegexp(f,p),r.pathToRegexp(d,h);const g=[];p.forEach((e=>g.push(e.name))),h.forEach((e=>g.push(e.name)));const m=r.compile(f,{validate:!1}),v=r.compile(d,{validate:!1});for(const[t,n]of Object.entries(c))Array.isArray(n)?c[t]=n.map((t=>a(l(t),e.params))):c[t]=a(l(n),e.params);let y,b=Object.keys(e.params).filter((e=>"nextInternalLocale"!==e));if(e.appendParamsToQuery&&!b.some((e=>g.includes(e))))for(const t of b)t in c||(c[t]=e.params[t]);try{y=m(e.params);const[t,n]=y.split("#");u.hostname=v(e.params),u.pathname=t,u.hash=`${n?"#":""}${n||""}`,delete u.search}catch(e){if(e.message.match(/Expected .*? to not repeat, but got an array/))throw new Error("To use a multi-match in the destination you must add `*` at the end of the param name to signify it should repeat. https://nextjs.org/docs/messages/invalid-multi-match");throw e}return u.query={...t,...u.query},{newUrl:y,destQuery:c,parsedDestination:u}};var r=n(9264),o=n(8058),i=n(2011);function a(e,t){if(!e.includes(":"))return e;for(const n of Object.keys(t))e.includes(`:${n}`)&&(e=e.replace(new RegExp(`:${n}\\*`,"g"),`:${n}--ESCAPED_PARAM_ASTERISKS`).replace(new RegExp(`:${n}\\?`,"g"),`:${n}--ESCAPED_PARAM_QUESTION`).replace(new RegExp(`:${n}\\+`,"g"),`:${n}--ESCAPED_PARAM_PLUS`).replace(new RegExp(`:${n}(?!\\w)`,"g"),`--ESCAPED_PARAM_COLON${n}`));return e=e.replace(/(:|\*|\?|\+|\(|\)|\{|\})/g,"\\$1").replace(/--ESCAPED_PARAM_PLUS/g,"+").replace(/--ESCAPED_PARAM_COLON/g,":").replace(/--ESCAPED_PARAM_QUESTION/g,"?").replace(/--ESCAPED_PARAM_ASTERISKS/g,"*"),r.compile(`/${e}`,{validate:!1})(t).slice(1)}function l(e){return e.replace(/__ESC_COLON_/gi,":")}},646:(e,t)=>{"use strict";function n(e){return"string"==typeof e||"number"==typeof e&&!isNaN(e)||"boolean"==typeof e?String(e):""}Object.defineProperty(t,"__esModule",{value:!0}),t.searchParamsToUrlQuery=function(e){const t={};return e.forEach(((e,n)=>{void 0===t[n]?t[n]=e:Array.isArray(t[n])?t[n].push(e):t[n]=[t[n],e]})),t},t.urlQueryToSearchParams=function(e){const t=new URLSearchParams;return Object.entries(e).forEach((([e,r])=>{Array.isArray(r)?r.forEach((r=>t.append(e,n(r)))):t.set(e,n(r))})),t},t.assign=function(e,...t){return t.forEach((t=>{Array.from(t.keys()).forEach((t=>e.delete(t))),t.forEach(((t,n)=>e.append(n,t)))})),e}},5317:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t,n,u,c,f){let d,p=!1,h=!1,g=l.parseRelativeUrl(e),m=i.removePathTrailingSlash(a.normalizeLocalePath(s.delBasePath(g.pathname),f).pathname);const v=n=>{let l=r.getPathMatch(n.source,{removeUnnamedParams:!0,strict:!0})(g.pathname);if(n.has&&l){const e=o.matchHas({headers:{host:document.location.hostname},cookies:document.cookie.split("; ").reduce(((e,t)=>{const[n,...r]=t.split("=");return e[n]=r.join("="),e}),{})},n.has,g.query);e?Object.assign(l,e):l=!1}if(l){if(!n.destination)return h=!0,!0;const r=o.prepareDestination({appendParamsToQuery:!0,destination:n.destination,params:l,query:u});if(g=r.parsedDestination,e=r.newUrl,Object.assign(u,r.parsedDestination.query),m=i.removePathTrailingSlash(a.normalizeLocalePath(s.delBasePath(e),f).pathname),t.includes(m))return p=!0,d=m,!0;if(d=c(m),d!==e&&t.includes(d))return p=!0,!0}};let y=!1;for(let e=0;e{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getRouteMatcher=function(e){const{re:t,groups:n}=e;return e=>{const o=t.exec(e);if(!o)return!1;const i=e=>{try{return decodeURIComponent(e)}catch(e){throw new r.DecodeError("failed to decode param")}},a={};return Object.keys(n).forEach((e=>{const t=n[e],r=o[t.pos];void 0!==r&&(a[e]=~r.indexOf("/")?r.split("/").map((e=>i(e))):t.repeat?[i(r)]:i(r))})),a}};var r=n(1624)},4794:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getParametrizedRoute=i,t.getRouteRegex=function(e){const t=i(e);return"routeKeys"in t?{re:new RegExp(`^${t.parameterizedRoute}(?:/)?$`),groups:t.groups,routeKeys:t.routeKeys,namedRegex:`^${t.namedParameterizedRoute}(?:/)?$`}:{re:new RegExp(`^${t.parameterizedRoute}(?:/)?$`),groups:t.groups}};var r=n(8058);function o(e){const t=e.startsWith("[")&&e.endsWith("]");t&&(e=e.slice(1,-1));const n=e.startsWith("...");return n&&(e=e.slice(3)),{key:e,repeat:n,optional:t}}function i(e){const t=(e.replace(/\/$/,"")||"/").slice(1).split("/"),n={};let i=1;const a=t.map((e=>{if(e.startsWith("[")&&e.endsWith("]")){const{key:t,optional:r,repeat:a}=o(e.slice(1,-1));return n[t]={pos:i++,repeat:a,optional:r},a?r?"(?:/(.+?))?":"/(.+?)":"/([^/]+?)"}return`/${r.escapeStringRegexp(e)}`})).join("");if("undefined"==typeof window){let e=97,i=1;const l=()=>{let t="";for(let n=0;n122&&(i++,e=97);return t},s={};return{parameterizedRoute:a,namedParameterizedRoute:t.map((e=>{if(e.startsWith("[")&&e.endsWith("]")){const{key:t,optional:n,repeat:r}=o(e.slice(1,-1));let i=t.replace(/\W/g,""),a=!1;return(0===i.length||i.length>30)&&(a=!0),isNaN(parseInt(i.slice(0,1)))||(a=!0),a&&(i=l()),s[i]=t,r?n?`(?:/(?<${i}>.+?))?`:`/(?<${i}>.+?)`:`/(?<${i}>[^/]+?)`}return`/${r.escapeStringRegexp(e)}`})).join(""),groups:n,routeKeys:s}}return{parameterizedRoute:a,groups:n}}},9036:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getSortedRoutes=function(e){const t=new n;return e.forEach((e=>t.insert(e))),t.smoosh()};class n{insert(e){this._insert(e.split("/").filter(Boolean),[],!1)}smoosh(){return this._smoosh()}_smoosh(e="/"){const t=[...this.children.keys()].sort();null!==this.slugName&&t.splice(t.indexOf("[]"),1),null!==this.restSlugName&&t.splice(t.indexOf("[...]"),1),null!==this.optionalRestSlugName&&t.splice(t.indexOf("[[...]]"),1);const n=t.map((t=>this.children.get(t)._smoosh(`${e}${t}/`))).reduce(((e,t)=>[...e,...t]),[]);if(null!==this.slugName&&n.push(...this.children.get("[]")._smoosh(`${e}[${this.slugName}]/`)),!this.placeholder){const t="/"===e?"/":e.slice(0,-1);if(null!=this.optionalRestSlugName)throw new Error(`You cannot define a route with the same specificity as a optional catch-all route ("${t}" and "${t}[[...${this.optionalRestSlugName}]]").`);n.unshift(t)}return null!==this.restSlugName&&n.push(...this.children.get("[...]")._smoosh(`${e}[...${this.restSlugName}]/`)),null!==this.optionalRestSlugName&&n.push(...this.children.get("[[...]]")._smoosh(`${e}[[...${this.optionalRestSlugName}]]/`)),n}_insert(e,t,r){if(0===e.length)return void(this.placeholder=!1);if(r)throw new Error("Catch-all must be the last part of the URL.");let o=e[0];if(o.startsWith("[")&&o.endsWith("]")){let i=o.slice(1,-1),a=!1;if(i.startsWith("[")&&i.endsWith("]")&&(i=i.slice(1,-1),a=!0),i.startsWith("...")&&(i=i.substring(3),r=!0),i.startsWith("[")||i.endsWith("]"))throw new Error(`Segment names may not start or end with extra brackets ('${i}').`);if(i.startsWith("."))throw new Error(`Segment names may not start with erroneous periods ('${i}').`);function l(e,n){if(null!==e&&e!==n)throw new Error(`You cannot use different slug names for the same dynamic path ('${e}' !== '${n}').`);t.forEach((e=>{if(e===n)throw new Error(`You cannot have the same slug name "${n}" repeat within a single dynamic path`);if(e.replace(/\W/g,"")===o.replace(/\W/g,""))throw new Error(`You cannot have the slug names "${e}" and "${n}" differ only by non-word symbols within a single dynamic path`)})),t.push(n)}if(r)if(a){if(null!=this.restSlugName)throw new Error(`You cannot use both an required and optional catch-all route at the same level ("[...${this.restSlugName}]" and "${e[0]}" ).`);l(this.optionalRestSlugName,i),this.optionalRestSlugName=i,o="[[...]]"}else{if(null!=this.optionalRestSlugName)throw new Error(`You cannot use both an optional and required catch-all route at the same level ("[[...${this.optionalRestSlugName}]]" and "${e[0]}").`);l(this.restSlugName,i),this.restSlugName=i,o="[...]"}else{if(a)throw new Error(`Optional route parameters are not yet supported ("${e[0]}").`);l(this.slugName,i),this.slugName=i,o="[]"}}this.children.has(o)||this.children.set(o,new n),this.children.get(o)._insert(e.slice(1),t,r)}constructor(){this.placeholder=!0,this.children=new Map,this.slugName=null,this.restSlugName=null,this.optionalRestSlugName=null}}},1889:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n(2784));const o="undefined"==typeof window;class i extends r.Component{constructor(e){super(e),this.emitChange=()=>{this._hasHeadManager&&this.props.headManager.updateHead(this.props.reduceComponentsToState([...this.props.headManager.mountedInstances],this.props))},this._hasHeadManager=this.props.headManager&&this.props.headManager.mountedInstances,o&&this._hasHeadManager&&(this.props.headManager.mountedInstances.add(this),this.emitChange())}componentDidMount(){this._hasHeadManager&&this.props.headManager.mountedInstances.add(this),this.emitChange()}componentDidUpdate(){this.emitChange()}componentWillUnmount(){this._hasHeadManager&&this.props.headManager.mountedInstances.delete(this),this.emitChange()}render(){return null}}t.default=i},1624:(e,t)=>{"use strict";function n(){const{protocol:e,hostname:t,port:n}=window.location;return`${e}//${t}${n?":"+n:""}`}function r(e){return"string"==typeof e?e:e.displayName||e.name||"Unknown"}function o(e){return e.finished||e.headersSent}Object.defineProperty(t,"__esModule",{value:!0}),t.execOnce=function(e){let t,n=!1;return(...r)=>(n||(n=!0,t=e(...r)),t)},t.getLocationOrigin=n,t.getURL=function(){const{href:e}=window.location,t=n();return e.substring(t.length)},t.getDisplayName=r,t.isResSent=o,t.normalizeRepeatedSlashes=function(e){const t=e.split("?");return t[0].replace(/\\/g,"/").replace(/\/\/+/g,"/")+(t[1]?`?${t.slice(1).join("?")}`:"")},t.loadGetInitialProps=async function e(t,n){const i=n.res||n.ctx&&n.ctx.res;if(!t.getInitialProps)return n.ctx&&n.Component?{pageProps:await e(n.Component,n.ctx)}:{};const a=await t.getInitialProps(n);if(i&&o(i))return a;if(!a){const e=`"${r(t)}.getInitialProps()" should resolve to an object. But found "${a}" instead.`;throw new Error(e)}return a},t.ST=t.SP=t.warnOnce=void 0,t.warnOnce=e=>{};const i="undefined"!=typeof performance;t.SP=i;const a=i&&"function"==typeof performance.mark&&"function"==typeof performance.measure;t.ST=a;class l extends Error{}t.DecodeError=l;class s extends Error{}t.NormalizeError=s},6577:(e,t,n)=>{n(104)},9097:(e,t,n)=>{n(4529)},5632:(e,t,n)=>{e.exports=n(9518)},7320:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;function o(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,i){for(var a,l,s=o(e),u=1;u{"use strict";var r=n(2784),o=n(7320),i=n(4616);function a(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n