Author SHA1 Message Date
James Ritchie c57d313b6d Merge pull request #80 from jsonhero-io/font-swap
Swapped mono font to Roboto Mono
2022-06-13 11:16:24 +01:00
James Ritchie c504b0c8c6 Change the mono spaced font to roboto mono 2022-06-13 10:31:39 +01:00
Eric Allam dc3bb5d2ab Merge branch 'sascha10000-feature/76-better-handle-long-key-names' 2022-06-10 15:06:58 +01:00
sascha10000 4c810901c3 -Added ellipsis in column and line-break in the InfoPanel 2022-06-09 23:48:34 +02:00
Eric Allam 25e1b20d9c Remove Chrome extension banner 2022-06-09 15:12:05 +01:00
Eric Allam 52b7d1962d Ensure text is shown during custom font load 2022-06-09 14:52:10 +01:00
Eric Allam d4c03f38d2 Cache static assets in the browser using remix asset imports 2022-06-09 14:16:10 +01:00
Eric Allam e37607b377 Remove PH banner 2022-06-09 12:39:02 +01:00
James Ritchie abd804ca4e Fixed PH banner mobile layout 2022-06-08 08:37:11 +01:00
Eric Allam ac31a5a1cf PH launch 2022-06-08 07:39:57 +01:00
Eric Allam a53506564d A nasty workaround for IPFS previewing 2022-06-07 14:13:24 +01:00
James Ritchie 2cb9bcd991 Reverted PH badge for now 2022-06-07 13:59:56 +01:00
James Ritchie d7225b201f Added Product Hunt Banner to homepage 2022-06-07 13:56:34 +01:00
Eric Allam 0358dbd5e2 Add some logging to discover what cloudflare is doing 2022-06-07 13:55:46 +01:00
Eric Allam bc5e5561c7 Fixed previewing IPFS images in cloudflare 2022-06-07 13:49:40 +01:00
Eric Allam 6df767080a IPFS previews work without the extension 2022-06-07 13:37:43 +01:00
sascha10000 2fb902ee01 fixed error due to issue with server sided preview generation (#70) 2022-06-07 13:15:18 +01:00
Eric AllamandJames Ritchie fa7010da0b Chrome extension banner (#74)
* Added chrome extension banner

* Only show the chrome banner when in Chrome

Co-authored-by: James Ritchie <james@jamesritchie.co.uk>
2022-06-06 14:11:20 +01:00
Eric AllamandJames Ritchie 1b42269739 GitHub button footer in minimal mode (#72)
* Added a new github star button to the footer

* Improved the props to work the same as hero icons

* Only show the github link in the footer if on minimal theme

Co-authored-by: James Ritchie <james@jamesritchie.co.uk>
2022-06-06 11:38:55 +01:00
Eric Allam c9d309c16d Fixed tree view minimal height 2022-06-01 15:22:11 +01:00
Eric Allam 3acf393009 Added privacy policy for jsonhero.io 2022-06-01 12:05:08 +01:00
Eric Allam 7508a96d3f Support minimal theme to strip out the header (for the chrome extension) 2022-06-01 11:28:24 +01:00
Eric Allam 49e0c57a33 Add ability to override the theme with a query param 2022-05-26 14:40:31 +01:00
Eric Allam d898d820bb Allow clients in browsers to call the create API 2022-05-26 09:15:33 +01:00
Eric Allam a4b7a38e67 Upgrade to miniflare 2.4.0 to fix issue with Node.js 17.7+ 2022-05-18 09:36:11 +01:00
Eric Allam d722b915a9 Ability to preview ipfs images 2022-05-17 16:26:10 +01:00
Eric Allam b3515bfff0 Display related values on Array and Object paths as well 2022-05-13 10:05:11 +01:00
Eric Allam d4c23be1b6 Move the examples out of app 2022-05-12 10:08:31 +01:00
James Ritchie 35082d9c43 Small header tweak to fix mobile portrait 2022-05-07 18:33:32 +01:00
D-K-P 48ac6bcdf7 Added Twitter icon to the header / footer of the homepage 2022-05-06 21:13:20 +01:00
James Ritchie 7e46e60d21 Added some example JSON files to help when running locally 2022-05-06 17:12:33 +01:00
Eric Allam 927cc26669 Convert the rest of the samples to ExampleDocs 2022-05-04 21:06:22 +01:00
Eric Allam 401205b57f Convert the Unsplash to the new ExampleDoc 2022-05-04 20:57:00 +01:00
Eric Allam a08fe62e78 Allow url documents to be injested, so it only hits the url once 2022-05-04 20:48:09 +01:00
Eric Allam fdd972b973 Allow creating read-only documents through the /new endpoint with readonly=true (fixes issue #48) 2022-05-03 22:36:27 +01:00
Eric Allam d793753a0a Make sure video element reloads if src changes 2022-05-03 22:05:51 +01:00
Eric Allam 6f548261a1 Adding additional logging to the $id endpoint 2022-05-03 16:39:40 +01:00
Eric Allam 635a97b133 Preview audio & video urls (fixes issue #46) 2022-05-03 15:09:19 +01:00
David Sanchez 3b0a5b80e7 New copy to clipboard shortcut feature and footer icon (#27) 2022-05-03 11:28:30 +01:00
Eric Allam cabd9e3379 Fixed the star count to exactly match github 2022-05-02 21:42:32 +01:00
Eric Allam 436f01e459 Add ttl option to the /new endpoint, allows setting a min 60s timeout 2022-05-02 21:30:19 +01:00
59 changed files with 2632 additions and 398 deletions

Before

Width:  |  Height:  |  Size: 620 KiB

After

Width:  |  Height:  |  Size: 620 KiB

Before

Width:  |  Height:  |  Size: 279 B

After

Width:  |  Height:  |  Size: 279 B

Before

Width:  |  Height:  |  Size: 336 B

After

Width:  |  Height:  |  Size: 336 B

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 486 B

+6 -1
View File
@@ -6,6 +6,7 @@ import {
ViewUpdate,
} from "@uiw/react-codemirror";
import { useRef, useEffect } from "react";
import { useJsonDoc } from "~/hooks/useJsonDoc";
import { getEditorSetup } from "~/utilities/codeMirrorSetup";
import { darkTheme, lightTheme } from "~/utilities/codeMirrorTheme";
import { useTheme } from "./ThemeProvider";
@@ -92,10 +93,14 @@ export function CodeEditor(opts: CodeEditorProps) {
}
}, [selection, view, setSelectionRef.current]);
const { minimal } = useJsonDoc();
return (
<div>
<div
className="h-jsonViewerHeight overflow-y-auto no-scrollbar"
className={`${
minimal ? "h-jsonViewerHeightMinimal" : "h-jsonViewerHeight"
} overflow-y-auto no-scrollbar`}
ref={editor}
/>
</div>
+8 -2
View File
@@ -3,6 +3,7 @@ import { colorForItemAtPath } from "~/utilities/colors";
import { IconComponent } from "~/useColumnView";
import { useJson } from "../hooks/useJson";
import { memo, useMemo } from "react";
import { useJsonDoc } from "~/hooks/useJsonDoc";
export type ColumnProps = {
id: string;
@@ -15,6 +16,7 @@ export type ColumnProps = {
function ColumnElement(column: ColumnProps) {
const { id, title, children } = column;
const [json] = useJson();
const { minimal } = useJsonDoc();
const iconColor = useMemo(() => colorForItemAtPath(id, json), [id, json]);
return (
@@ -25,9 +27,13 @@ function ColumnElement(column: ColumnProps) {
>
<div className="flex items-center text-slate-800 bg-slate-50 mb-[3px] p-2 pb-0 transition dark:bg-slate-900 dark:text-slate-300">
{column.icon && <column.icon className="h-6 w-6 mr-1" />}
<Title className="">{title}</Title>
<Title className="text-ellipsis overflow-hidden">{title}</Title>
</div>
<div className="overflow-y-auto h-viewerHeight no-scrollbar">
<div
className={`overflow-y-auto ${
minimal ? "h-viewerHeightMinimal" : "h-viewerHeight"
} no-scrollbar`}
>
{children}
</div>
</div>
+20
View File
@@ -0,0 +1,20 @@
import { useHotkeys } from "react-hotkeys-hook";
import { useSelectedInfo } from "../hooks/useSelectedInfo";
export function CopySelectedNodeShortcut() {
const selectedInfo = useSelectedInfo();
useHotkeys(
'shift+c,shift+C',
(e) => {
e.preventDefault();
const selectedJSON = selectedInfo?.name === "string"
? selectedInfo?.value
: JSON.stringify(selectedInfo?.value, null, 2);
navigator.clipboard.writeText(selectedJSON);
},
[selectedInfo]
);
return <></>;
}
+57 -43
View File
@@ -16,52 +16,66 @@ export function DocumentTitle() {
}
}, [updateDoc]);
const startEditing = useCallback(() => {
ref.current?.select();
ref.current?.focus();
}, [ref.current]);
return (
<updateDoc.Form method="post" action={`/actions/${doc.id}/update`}>
if (doc.readOnly) {
return (
<div
className="flex justify-center items-center w-full"
title={doc.title}
>
<label className="relative block group">
<PencilAltIcon className="h-5 w-5 absolute top-1/2 transform -translate-y-1/2 left-3 text-white opacity-0 transition pointer-events-none group-hover:opacity-80 group-focus:opacity-80" />
<input
ref={ref}
className={
"min-w-[15vw] border-none text-ellipsis text-slate-300 px-2 pl-10 py-1 rounded-sm bg-transparent placeholder:text-slate-400 focus:bg-black/30 focus:outline-none focus:border-none hover:bg-black hover:bg-opacity-30 hover:cursor-text transition dark:bg-transparent dark:text-slate-200 dark:placeholder:text-slate-400 dark:focus:bg-black dark:focus:bg-opacity-10 dark:hover:bg-black dark:hover:bg-opacity-10"
}
type="text"
name="title"
spellCheck="false"
placeholder="Name your JSON file"
value={editedTitle}
onChange={(e) => setEditedTitle(e.target.value)}
/>
</label>
{match(editedTitle)
.with(doc.title, () => <p className="ml-2 text-transparent">Save</p>)
.with("", () => (
<button
className="ml-2 text-lime-500 hover:text-lime-600 transition"
onClick={() => setEditedTitle(doc.title)}
>
Reset
</button>
))
.otherwise(() => (
<button
type="submit"
className="ml-2 text-lime-500 hover:text-lime-600 transition"
>
Save
</button>
))}
<span
className={
"min-w-[15vw] border-none text-ellipsis text-slate-300 px-2 pl-10 py-1 rounded-sm bg-transparent placeholder:text-slate-400 focus:bg-black/30 focus:outline-none focus:border-none hover:cursor-text transition dark:bg-transparent dark:text-slate-200 dark:placeholder:text-slate-400 dark:focus:bg-black dark:focus:bg-opacity-10"
}
>
{doc.title}
</span>
</div>
</updateDoc.Form>
);
);
} else {
return (
<updateDoc.Form method="post" action={`/actions/${doc.id}/update`}>
<div
className="flex justify-center items-center w-full"
title={doc.title}
>
<label className="relative block group">
<PencilAltIcon className="h-5 w-5 absolute top-1/2 transform -translate-y-1/2 left-3 text-white opacity-0 transition pointer-events-none group-hover:opacity-80 group-focus:opacity-80" />
<input
ref={ref}
className={
"min-w-[15vw] border-none text-ellipsis text-slate-300 px-2 pl-10 py-1 rounded-sm bg-transparent placeholder:text-slate-400 focus:bg-black/30 focus:outline-none focus:border-none hover:bg-black hover:bg-opacity-30 hover:cursor-text transition dark:bg-transparent dark:text-slate-200 dark:placeholder:text-slate-400 dark:focus:bg-black dark:focus:bg-opacity-10 dark:hover:bg-black dark:hover:bg-opacity-10"
}
type="text"
name="title"
spellCheck="false"
placeholder="Name your JSON file"
value={editedTitle}
onChange={(e) => setEditedTitle(e.target.value)}
/>
</label>
{match(editedTitle)
.with(doc.title, () => (
<p className="ml-2 text-transparent">Save</p>
))
.with("", () => (
<button
className="ml-2 text-lime-500 hover:text-lime-600 transition"
onClick={() => setEditedTitle(doc.title)}
>
Reset
</button>
))
.otherwise(() => (
<button
type="submit"
className="ml-2 text-lime-500 hover:text-lime-600 transition"
>
Save
</button>
))}
</div>
</updateDoc.Form>
);
}
}
+20
View File
@@ -0,0 +1,20 @@
import { Link } from "remix";
export function ExampleDoc({
id,
title,
path,
}: {
id: string;
title: string;
path?: string;
}) {
return (
<Link
to={`/j/${id}${path ? `?path=${path}` : ""}`}
className="bg-slate-900 px-4 py-2 rounded-md whitespace-nowrap text-lime-300 transition hover:text-lime-500"
>
{title}
</Link>
);
}
+21 -1
View File
@@ -1,10 +1,15 @@
import { useJsonDoc } from "~/hooks/useJsonDoc";
import { ArrowKeysIcon } from "./Icons/ArrowKeysIcon";
import { CopyShortcutIcon } from "./Icons/CopyShortcutIcon";
import { EscapeKeyIcon } from "./Icons/EscapeKeyIcon";
import { SquareBracketsIcon } from "./Icons/SquareBracketsIcon";
import { Body } from "./Primitives/Body";
import { ThemeModeToggler } from "./ThemeModeToggle";
import { GithubStarSmall } from "./UI/GithubStarSmall";
export function Footer() {
const { minimal } = useJsonDoc();
return (
<footer className="flex items-center justify-between w-screen h-[30px] bg-slate-200 dark:bg-slate-800 border-t-[1px] border-slate-400 transition dark:border-slate-600">
<ol className="flex pl-3">
@@ -26,8 +31,23 @@ export function Footer() {
Reset path
</Body>
</li>
<li className="flex items-center">
<CopyShortcutIcon className="transition text-slate-300 dark:text-slate-500" />
<Body className="pl-2 pr-4 text-slate-800 transition dark:text-white">
Copy selected node
</Body>
</li>
</ol>
<ol className="flex items-center">
{minimal && (
<li>
<GithubStarSmall />
</li>
)}
<li>
<ThemeModeToggler />
</li>
</ol>
<ThemeModeToggler />
</footer>
);
}
@@ -3,6 +3,8 @@ import { ExtraLargeTitle } from "../Primitives/ExtraLargeTitle";
import { SmallSubtitle } from "../Primitives/SmallSubtitle";
import { HomeSection } from "./HomeSection";
import shareVideo from "~/assets/home/JsonHeroShare.mp4";
export function HomeCollaborateSection() {
return (
<HomeSection
@@ -20,7 +22,7 @@ export function HomeCollaborateSection() {
</SmallSubtitle>
</div>
<div className="w-full md:w-1/2">
<AutoplayVideo src="/home/JsonHeroShare.mp4" />
<AutoplayVideo src={shareVideo} />
</div>
</HomeSection>
);
+3 -1
View File
@@ -3,6 +3,8 @@ import { ExtraLargeTitle } from "../Primitives/ExtraLargeTitle";
import { SmallSubtitle } from "../Primitives/SmallSubtitle";
import { HomeSection } from "./HomeSection";
import edgeCasesVideo from "~/assets/home/UncoverEdgeCases.mp4";
export function HomeEdgeCasesSection() {
return (
<HomeSection
@@ -21,7 +23,7 @@ export function HomeEdgeCasesSection() {
</SmallSubtitle>
</div>
<div className="w-full md:w-1/2">
<AutoplayVideo src="/home/UncoverEdgeCases.mp4" />
<AutoplayVideo src={edgeCasesVideo} />
</div>
</HomeSection>
);
+11
View File
@@ -1,7 +1,9 @@
import { Link } from "remix";
import { DiscordIcon } from "../Icons/DiscordIcon";
import { EmailIcon } from "../Icons/EmailIcon";
import { GithubIcon } from "../Icons/GithubIcon";
import { Logo } from "../Icons/Logo";
import { TwitterIcon } from "../Icons/TwitterIcon";
export type HomeFooterProps = {
maxWidth?: string;
@@ -36,6 +38,15 @@ export function HomeFooter({ maxWidth = "1150px" }: HomeFooterProps) {
<DiscordIcon />
</a>
</li>
<li className="ml-2 hover:cursor-pointer">
<a href="https://twitter.com/json_hero" target="_blank">
<TwitterIcon />
</a>
</li>
<li className="ml-2 hover:cursor-pointer text-indigo-700">
<Link to="/privacy">Privacy Policy</Link>
</li>
</ol>
</div>
</footer>
+13 -3
View File
@@ -1,5 +1,6 @@
import { DiscordIconTransparent } from "../Icons/DiscordIconTransparent";
import { EmailIconTransparent } from "../Icons/EmailIconTransparent";
import { TwitterIcon } from "../Icons/TwitterIcon";
import { Logo } from "../Icons/Logo";
import { NewDocument } from "../NewDocument";
import { GithubStar } from "../UI/GithubStar";
@@ -10,10 +11,14 @@ import {
PopoverTrigger,
} from "../UI/Popover";
export function HomeHeader() {
export function HomeHeader({ fixed }: { fixed?: boolean }) {
return (
<header className="fixed z-20 flex items-center justify-between w-screen h-[82px] px-4 bg-indigo-700">
<div className="flex w-44 mr-3">
<header
className={`${
fixed ? "fixed" : ""
} z-20 flex items-center justify-between w-screen h-[82px] px-4 bg-indigo-700`}
>
<div className="flex w-36 sm:w-44 mr-3">
<Logo />
</div>
<ol className="flex items-center gap-2">
@@ -45,6 +50,11 @@ export function HomeHeader() {
<DiscordIconTransparent />
</a>
</li>
<li className="hover:cursor-pointer opacity-90 hover:opacity-100 transition">
<a href="https://twitter.com/json_hero" target="_blank">
<TwitterIcon />
</a>
</li>
</ol>
</header>
);
+3 -2
View File
@@ -2,7 +2,8 @@ import { AutoplayVideo } from "../AutoplayVideo";
import { NewFile } from "../NewFile";
import { ExtraLargeTitle } from "../Primitives/ExtraLargeTitle";
import { SmallSubtitle } from "../Primitives/SmallSubtitle";
import { HomeSection } from "./HomeSection";
import heroVideo from "~/assets/home/JsonHero2.mp4";
const jsonHeroTitle = "JSON sucks.";
const jsonHeroSlogan = "But we're making it better.";
@@ -14,7 +15,7 @@ export function HomeHeroSection() {
>
<div className="self-center md:w-1/2 md:pr-10 flex justify-end">
<div className=" max-w-3xl">
<AutoplayVideo src="/home/JsonHero2.mp4" />
<AutoplayVideo src={heroVideo} />
</div>
</div>
<div className="self-center flex align-center md:w-1/2 px-6 pb-8 mt-8 lg:mt-0">
+7 -1
View File
@@ -142,7 +142,13 @@ function SampleJSONPreview({
}) {
return (
<JsonDocProvider
doc={{ id: "sample", title: "Sample", type: "raw", contents: "" }}
doc={{
id: "sample",
title: "Sample",
type: "raw",
readOnly: false,
contents: "",
}}
path={initialSelection}
>
<JsonProvider initialJson={json}>
+3 -1
View File
@@ -3,6 +3,8 @@ import { ExtraLargeTitle } from "../Primitives/ExtraLargeTitle";
import { SmallSubtitle } from "../Primitives/SmallSubtitle";
import { HomeSection } from "./HomeSection";
import searchVideo from "~/assets/home/JsonHeroSearch.mp4";
export function HomeSearchSection() {
return (
<HomeSection containerClassName="py-10 px-6 bg-black md:py-36 lg:py-20">
@@ -17,7 +19,7 @@ export function HomeSearchSection() {
</SmallSubtitle>
</div>
<div className="w-full md:w-1/2">
<AutoplayVideo src="/home/JsonHeroSearch.mp4" />
<AutoplayVideo src={searchVideo} />
</div>
</HomeSection>
);
+23
View File
@@ -0,0 +1,23 @@
export function CopyShortcutIcon(props: React.SVGProps<SVGSVGElement>) {
return (
<svg
className={props.className}
width="30"
height="14"
viewBox="0 0 30 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="14" height="14" rx="1.53846" fill="currentColor" />
<rect x="16" width="14" height="14" rx="1.53846" fill="currentColor" />
<path
d="M5.64,10.22H8.25V7a.39.39,0,0,1,.38-.39H10l-3-3L4,6.65H5.26A.39.39,0,0,1,5.64,7v3.18Zm3,.78H5.26a.38.38,0,0,1-.39-.39V7.43H3.11a.39.39,0,0,1-.28-.66L6.72,2.86a.39.39,0,0,1,.55,0l3.88,3.91a.38.38,0,0,1-.27.66H9v3.18a.38.38,0,0,1-.39.39Z"
stroke="#0f172a" strokeWidth="0.35px" fill="#0f172a"
/>
<path
d="M23.81,9.52a1.66,1.66,0,0,0,.53-.27,1.57,1.57,0,0,0,.35-.42,1.07,1.07,0,0,0,.13-.53h1.6a2.26,2.26,0,0,1-.25,1,2.87,2.87,0,0,1-.7.85,3.35,3.35,0,0,1-1,.58,3.56,3.56,0,0,1-1.22.21,3.73,3.73,0,0,1-1.55-.31,3.2,3.2,0,0,1-1.11-.84,3.61,3.61,0,0,1-.67-1.22,4.91,4.91,0,0,1-.23-1.5V6.88a4.91,4.91,0,0,1,.23-1.5,3.54,3.54,0,0,1,.67-1.22,3.33,3.33,0,0,1,1.11-.84,3.94,3.94,0,0,1,2.83-.09,3,3,0,0,1,1,.59,2.66,2.66,0,0,1,.67.92,2.94,2.94,0,0,1,.23,1.17h-1.6a1.48,1.48,0,0,0-.45-1.07,1.65,1.65,0,0,0-.52-.33,1.75,1.75,0,0,0-.65-.12,1.59,1.59,0,0,0-1.44.78,2.27,2.27,0,0,0-.31.8,4.53,4.53,0,0,0-.09.91v.24a4.63,4.63,0,0,0,.09.92,2.46,2.46,0,0,0,.3.8,1.67,1.67,0,0,0,.56.56,1.63,1.63,0,0,0,.89.22A2.05,2.05,0,0,0,23.81,9.52Z"
fill="#0f172a"
/>
</svg>
);
}
+8 -4
View File
@@ -1,11 +1,15 @@
export function GithubIconSimple(props: React.SVGProps<SVGSVGElement>) {
export type IconProps = {
className?: string;
};
export function GithubIconSimple({ className }: IconProps) {
return (
<svg
className={props.className}
className={className}
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_571_3822)">
@@ -13,7 +17,7 @@ export function GithubIconSimple(props: React.SVGProps<SVGSVGElement>) {
fillRule="evenodd"
clipRule="evenodd"
d="M12 0C5.37017 0 0 5.50708 0 12.306C0 17.745 3.44015 22.3532 8.20626 23.9849C8.80295 24.0982 9.02394 23.7205 9.02394 23.3881C9.02394 23.0935 9.01657 22.3229 9.00921 21.2956C5.67219 22.0359 4.96501 19.6487 4.96501 19.6487C4.41989 18.2285 3.63168 17.8508 3.63168 17.8508C2.54144 17.0878 3.71271 17.1029 3.71271 17.1029C4.91344 17.1936 5.55433 18.372 5.55433 18.372C6.62247 20.2531 8.36096 19.7092 9.04604 19.3919C9.15654 18.5987 9.46593 18.0548 9.80479 17.745C7.13812 17.4353 4.33886 16.3777 4.33886 11.6638C4.33886 10.3192 4.80295 9.2238 5.57643 8.36261C5.4512 8.05288 5.03867 6.79887 5.69429 5.1067C5.69429 5.1067 6.7035 4.77432 8.99447 6.36827C9.95212 6.09632 10.9761 5.96034 12 5.95279C13.0166 5.95279 14.0479 6.09632 15.0055 6.36827C17.2965 4.77432 18.3057 5.1067 18.3057 5.1067C18.9613 6.79887 18.5488 8.05288 18.4236 8.36261C19.1897 9.2238 19.6538 10.3192 19.6538 11.6638C19.6538 16.3928 16.8471 17.4278 14.1731 17.7375C14.6004 18.1152 14.9908 18.8706 14.9908 20.0189C14.9908 21.6657 14.9761 22.9877 14.9761 23.3957C14.9761 23.728 15.1897 24.1058 15.8011 23.9849C20.5672 22.3532 24 17.745 24 12.3135C24 5.50708 18.6298 0 12 0Z"
fill="#231E1B"
fill="currentColor"
/>
</g>
<defs>
+4 -3
View File
@@ -6,11 +6,11 @@ import { useJsonColumnViewState } from "~/hooks/useJsonColumnView";
import { concatenated, getHierarchicalTypes } from "~/utilities/dataType";
import { formatRawValue } from "~/utilities/formatter";
import { isNullable } from "~/utilities/nullable";
import { CopyTextButton } from "./CopyTextButton";
import { Body } from "./Primitives/Body";
import { LargeMono } from "./Primitives/LargeMono";
import { Title } from "./Primitives/Title";
import { ValueIcon, ValueIconSize } from "./ValueIcon";
import { CopyTextButton } from "./CopyTextButton";
export type InfoHeaderProps = {
relatedPaths: string[];
@@ -41,12 +41,13 @@ export function InfoHeader({ relatedPaths }: InfoHeaderProps) {
}, [relatedPaths, json]);
const [hovering, setHovering] = useState(false);
console.warn(selectedInfo);
return (
<div className="mb-4 pb-4">
<div className="flex items-center">
<Title className="flex-1 mr-2 text-slate-700 transition dark:text-slate-200">
{selectedName ?? "nothing"}
<Title className="flex-1 mr-2 overflow-hidden overflow-ellipsis break-words text-slate-700 transition dark:text-slate-200">
{ selectedName ?? "nothing" }
</Title>
<div>
<ValueIcon
+8 -10
View File
@@ -4,27 +4,25 @@ import { PropertiesValue } from "./Properties/PropertiesValue";
import { InfoHeader } from "./InfoHeader";
import { ContainerInfo } from "./ContainerInfo";
import { useSelectedInfo } from "~/hooks/useSelectedInfo";
import { useMemo } from "react";
import { useJson } from "~/hooks/useJson";
import { getRelatedPathsAtPath } from "~/utilities/relatedValues";
import { useJsonColumnViewState } from "~/hooks/useJsonColumnView";
import { useRelatedPaths } from "~/hooks/useRelatedPaths";
import { useJsonDoc } from "~/hooks/useJsonDoc";
export function InfoPanel() {
const { minimal } = useJsonDoc();
const selectedInfo = useSelectedInfo();
const { selectedNodeId } = useJsonColumnViewState();
const relatedPaths = useRelatedPaths();
if (!selectedInfo) {
return <></>;
}
const isSelectedLeafNode =
selectedInfo.name !== "object" && selectedInfo.name !== "array";
return (
<>
<div className="h-inspectorHeight p-4 bg-white border-l-[1px] border-slate-300 overflow-y-auto no-scrollbar transition dark:bg-slate-800 dark:border-slate-600">
<div
className={`${
minimal ? "h-inspectorHeightMinimal" : "h-inspectorHeight"
} p-4 bg-white border-l-[1px] border-slate-300 overflow-y-auto no-scrollbar transition dark:bg-slate-800 dark:border-slate-600`}
>
<InfoHeader relatedPaths={relatedPaths} />
<div className="mb-4">
@@ -34,7 +32,7 @@ export function InfoPanel() {
<ContainerInfo />
{isSelectedLeafNode && <RelatedValues relatedPaths={relatedPaths} />}
<RelatedValues relatedPaths={relatedPaths} />
</div>
</>
);
+4 -1
View File
@@ -4,6 +4,7 @@ import {
} from "../hooks/useJsonColumnView";
import { useHotkeys } from "react-hotkeys-hook";
import { Columns } from "./Columns";
import { CopySelectedNodeShortcut } from "./CopySelectedNode";
export function JsonColumnView() {
const { getColumnViewProps, columns } = useJsonColumnViewState();
@@ -67,5 +68,7 @@ function KeyboardShortcuts() {
[api]
);
return <></>;
return <>
<CopySelectedNodeShortcut />
</>;
}
+1 -1
View File
@@ -81,7 +81,7 @@ export function JsonEditor() {
<CodeEditor
language="json"
content={jsonMapped.json}
readOnly={false}
readOnly={true}
onUpdate={onUpdate}
selection={selection}
/>
+29 -22
View File
@@ -4,8 +4,10 @@ import {
useJsonColumnViewAPI,
useJsonColumnViewState,
} from "~/hooks/useJsonColumnView";
import { useJsonDoc } from "~/hooks/useJsonDoc";
import { JsonTreeViewNode, useJsonTreeViewContext } from "~/hooks/useJsonTree";
import { VirtualNode } from "~/hooks/useVirtualTree";
import { CopySelectedNodeShortcut } from "./CopySelectedNode";
import { Body } from "./Primitives/Body";
import { Mono } from "./Primitives/Mono";
@@ -77,32 +79,37 @@ export function JsonTreeView() {
}
}, [treeRef.current]);
const { minimal } = useJsonDoc();
return (
<div
className="text-white w-full"
ref={parentRef}
style={{
height: `calc(100vh - 106px)`,
overflowY: "auto",
overflowX: "hidden",
}}
>
<>
<CopySelectedNodeShortcut />
<div
className="relative w-full outline-none"
style={{ height: `${tree.totalSize}px` }}
{...tree.getTreeProps()}
ref={treeRef}
className="text-white w-full"
ref={parentRef}
style={{
height: `calc(100vh - ${minimal ? "66px" : "106px"})`,
overflowY: "auto",
overflowX: "hidden",
}}
>
{tree.nodes.map((virtualNode) => (
<TreeViewNode
virtualNode={virtualNode}
key={virtualNode.node.id}
onToggle={(node, e) => tree.toggleNode(node.id, e)}
selectedNodeId={selectedNodeId}
/>
))}
<div
className="relative w-full outline-none"
style={{ height: `${tree.totalSize}px` }}
{...tree.getTreeProps()}
ref={treeRef}
>
{tree.nodes.map((virtualNode) => (
<TreeViewNode
virtualNode={virtualNode}
key={virtualNode.node.id}
onToggle={(node, e) => tree.toggleNode(node.id, e)}
selectedNodeId={selectedNodeId}
/>
))}
</div>
</div>
</div>
</>
);
}
+3 -1
View File
@@ -4,6 +4,8 @@ import { useJsonColumnViewAPI } from "~/hooks/useJsonColumnView";
import { ColumnViewNode, IconComponent } from "~/useColumnView";
import { Body } from "./Primitives/Body";
import eyeIcon from "~/assets/svgs/EyeIcon.svg";
export type PathPreviewProps = {
nodes: ColumnViewNode[];
maxComponents?: number;
@@ -77,7 +79,7 @@ export function PathPreview({
<div
className={`flex select-none pl-7 ${
isEnabled
? "relative transition hover:bg-slate-200 hover:cursor-pointer dark:hover:bg-slate-600 after:transition after:absolute after:h-3 after:w-3 after:opacity-0 hover:after:opacity-100 after:top-1 after:left-1 after:content-[''] after:bg-[url('/svgs/EyeIcon.svg')] after:bg-no-repeat"
? `relative transition hover:bg-slate-200 hover:cursor-pointer dark:hover:bg-slate-600 after:transition after:absolute after:h-3 after:w-3 after:opacity-0 hover:after:opacity-100 after:top-1 after:left-1 after:content-[''] after:bg-[url('${eyeIcon}')] after:bg-no-repeat`
: "disabled"
}`}
onClick={() =>
@@ -0,0 +1,42 @@
import { useRef } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { Body } from "~/components/Primitives/Body";
import { PreviewBox } from "../PreviewBox";
export function PreviewAudioUri({
src,
contentType,
}: {
src: string;
contentType: string;
}) {
const mediaRef = useRef<HTMLMediaElement>(null);
useHotkeys(
"space",
(e) => {
e.preventDefault();
if (mediaRef.current) {
if (mediaRef.current.paused) {
mediaRef.current.play();
} else {
mediaRef.current.pause();
}
}
},
[mediaRef]
);
return (
<div>
<PreviewBox>
<Body>
<audio controls src={src} ref={mediaRef}>
Sorry, your browser doesn't support embedded audio.
</audio>
</Body>
</PreviewBox>
</div>
);
}
@@ -0,0 +1,18 @@
import { PreviewImageUri } from "./PreviewImageUri";
export function PreviewIPFSImage({ src }: { src: URL }) {
const newSrc = createPreviewIPFSImageURL(src);
return <PreviewImageUri src={newSrc} />;
}
const createPreviewIPFSImageURL = (src: URL): string => {
const url = new URL(src.href);
url.protocol = "https:";
url.pathname = `/ipfs/${src.pathname.substring(1)}`;
url.hostname = "ipfs.io";
// Remove the leading slash
return url.href;
};
@@ -6,11 +6,11 @@ export function PreviewImageUri({
contentType,
}: {
src: string;
contentType: string;
contentType?: string;
}) {
return (
<div>
<PreviewBox>
<PreviewBox link={src}>
<Body>
<img src={src} />
</Body>
+32 -1
View File
@@ -6,9 +6,12 @@ import {
import Color from "color";
import { CodeViewer } from "~/components/CodeViewer";
import { PreviewBox } from "../PreviewBox";
import { PreviewAudioUri } from "./PreviewAudioUri";
import { PreviewDate } from "./PreviewDate";
import { PreviewImageUri } from "./PreviewImageUri";
import { PreviewIPFSImage } from "./PreviewIPFSImage";
import { PreviewUri } from "./PreviewUri";
import { PreviewVideoUri } from "./PreviewVideoUri";
export function PreviewString({ info }: { info: JSONStringType }) {
if (info.format == null) {
@@ -23,9 +26,37 @@ export function PreviewString({ info }: { info: JSONStringType }) {
info.format.contentType === "image/gif" ||
info.format.contentType === "image/svg+xml" ||
info.format.contentType === "image/webp"
) {
const url = new URL(info.value);
if (url.protocol === "ipfs:") {
return <PreviewIPFSImage src={url} />;
} else {
return (
<PreviewImageUri
src={info.value}
contentType={info.format.contentType}
/>
);
}
} else if (
info.format.contentType === "video/mp4" ||
info.format.contentType === "video/webm" ||
info.format.contentType === "video/ogg"
) {
return (
<PreviewImageUri
<PreviewVideoUri
src={info.value}
contentType={info.format.contentType}
/>
);
} else if (
info.format.contentType === "audio/mpeg" ||
info.format.contentType === "audio/ogg" ||
info.format.contentType === "audio/wav"
) {
return (
<PreviewAudioUri
src={info.value}
contentType={info.format.contentType}
/>
+9 -1
View File
@@ -23,7 +23,15 @@ export function PreviewUri(props: PreviewUriProps) {
<div>
{previewFetcher.type === "done" ? (
<>
{"error" in previewFetcher.data ? (
{typeof previewFetcher.data == "string" ? (
<PreviewBox>
<Body>
<span
dangerouslySetInnerHTML={{ __html: previewFetcher.data }}
></span>
</Body>
</PreviewBox>
) : "error" in previewFetcher.data ? (
<PreviewBox>
<Body>{previewFetcher.data.error}</Body>
</PreviewBox>
@@ -0,0 +1,43 @@
import { useRef } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { Body } from "~/components/Primitives/Body";
import { PreviewBox } from "../PreviewBox";
export function PreviewVideoUri({
src,
contentType,
}: {
src: string;
contentType: string;
}) {
const videoRef = useRef<HTMLVideoElement>(null);
useHotkeys(
"space",
(e) => {
e.preventDefault();
if (videoRef.current) {
if (videoRef.current.paused) {
videoRef.current.play();
} else {
videoRef.current.pause();
}
}
},
[videoRef]
);
return (
<div>
<PreviewBox>
<Body>
<video key={src} controls ref={videoRef}>
<source src={src} type={contentType} />
Sorry, your browser doesn't support embedded videos.
</video>
</Body>
</PreviewBox>
</div>
);
}
+14 -17
View File
@@ -1,31 +1,28 @@
import { ExampleDoc } from "./ExampleDoc";
import { ExampleUrl } from "./ExampleUrl";
export function SampleUrls() {
return (
<div className="flex justify-start flex-wrap gap-2">
<ExampleUrl
url="https://gist.githubusercontent.com/ericallam/332aca65c34dbc0fdb1b3cffbdf3f7a4/raw/4c6e264c3afd3432b608bad628290a7d71035253/unsplash.json"
title="Unsplash API Example"
displayTitle="Unsplash API"
<ExampleDoc
id="xW6aDgcbKgwC"
title="Unsplash API"
path="4.urls.regular"
/>
<ExampleUrl
url="https://gist.githubusercontent.com/ericallam/77e37e93e370b32387ce8d598dd06fc8/raw/9306a2c039fc27ab42bdadb80cbd6dbca49d27ec/tweet.json"
title="Tweet API Example"
displayTitle="Tweet JSON"
<ExampleDoc
id="d9udW60cLOok"
title="Tweet JSON"
path="data.0.entities.urls.0.expanded_url"
/>
<ExampleUrl
url="https://gist.githubusercontent.com/ericallam/f11f4981adf72b0427427c349afb3a09/raw/37eee4234b628f3278c9d4f644fee4a1c4987593/Airtable.json"
title="Airtable Product Catalog"
displayTitle="Airtable API"
<ExampleDoc
id="JZw4Wx9Mgj6r"
title="Airtable API"
path="records.3.createdTime"
/>
<ExampleUrl
url="https://gist.githubusercontent.com/ericallam/51b77c0051c21a34ea33d601bb0b1bef/raw/0b099f7d3bb5fe47f1fd55007d6a5fe9c5fd046b/GitHub.json"
title="List of Github Repos"
displayTitle="Github API"
/>
<ExampleDoc id="PjHo1o5MVeH4" title="Github API" />
</div>
);
}
+20 -2
View File
@@ -6,6 +6,7 @@ import { useJsonDoc } from "~/hooks/useJsonDoc";
import { ToolTip } from "./ToolTip";
import { Body } from "./Primitives/Body";
import { ShortcutIcon } from "./Icons/ShortcutIcon";
import { useTheme } from "./ThemeProvider";
export function SideBar() {
const { doc } = useJsonDoc();
@@ -81,6 +82,23 @@ function SidebarLink({
const isActive = location.pathname === to;
const { minimal } = useJsonDoc();
const [theme] = useTheme();
const queryParams = new URLSearchParams();
if (typeof minimal === "boolean") {
queryParams.set("minimal", String(minimal));
if (theme) {
queryParams.set("theme", theme);
}
}
const href = `${to}${
queryParams.toString().length > 0 ? `?${queryParams.toString()}` : ""
}`;
if (hotKey) {
const navigate = useNavigate();
useHotkeys(
@@ -88,7 +106,7 @@ function SidebarLink({
(e) => {
e.preventDefault();
if (!isActive && to) {
navigate(to);
navigate(href);
}
},
[navigate, isActive, to]
@@ -100,7 +118,7 @@ function SidebarLink({
: "relative w-10 h-10 mb-1 text-slate-700 hover:bg-slate-300 rounded-sm cursor:pointer transition dark:text-white dark:hover:bg-slate-700";
return !!to ? (
<Link to={to} prefetch={isActive ? "none" : "render"}>
<Link to={href} prefetch={isActive ? "none" : "render"}>
<li className={classes}>{children}</li>
</Link>
) : (
+4 -2
View File
@@ -15,8 +15,10 @@ export function ThemeModeToggler() {
return (
<button
className={`flex text-xl items-center pr-2 ${
theme === "light" ? "text-slate-800" : "text-white"
className={`flex text-xl items-center px-2 py-1.5 transition ${
theme === "light"
? "text-slate-800 hover:bg-slate-300"
: "text-white hover:bg-slate-700"
}`}
onClick={toggleTheme}
>
+34
View File
@@ -0,0 +1,34 @@
import { formatStarCount } from "~/utilities/formatStarCount";
import { GithubIconSimple } from "../Icons/GithubIconSimple";
import { Body } from "../Primitives/Body";
import { useStarCount } from "../StarCountProvider";
export type GithubStarSmallProps = {
className?: string;
};
export function GithubStarSmall({ className }: GithubStarSmallProps) {
const starCount = useStarCount();
return (
<a
href="https://github.com/jsonhero-io/jsonhero-web"
target="_blank"
className="flex p-1 text-slate-700 dark:text-slate-300 hover:bg-slate-300 dark:hover:bg-slate-700 transition hover:cursor-pointer"
>
<div className="flex items-center gap-1.5 pl-1 rounded-l-sm">
<GithubIconSimple className="w-4 h-4 ml-1 text-slate-700 dark:text-white transition"></GithubIconSimple>
<Body className="font-semibold text-slate-800 dark:text-slate-100">
Star
</Body>
</div>
{starCount && (
<div className="pr-2 pl-1">
<Body className="font-bold dark:text-slate-100">
{formatStarCount(starCount)}
</Body>
</div>
)}
</a>
);
}
+4 -1
View File
@@ -5,6 +5,7 @@ import { JSONDocument } from "~/jsonDoc.server";
type JsonDocType = {
doc: JSONDocument;
path?: string;
minimal?: boolean;
};
const JsonDocContext = createContext<JsonDocType | undefined>(undefined);
@@ -13,13 +14,15 @@ export function JsonDocProvider({
children,
doc,
path,
minimal,
}: {
children: ReactNode;
doc: JSONDocument;
path?: string;
minimal?: boolean;
}) {
return (
<JsonDocContext.Provider value={{ doc, path }}>
<JsonDocContext.Provider value={{ doc, path, minimal }}>
{children}
</JsonDocContext.Provider>
);
+30 -4
View File
@@ -1,8 +1,10 @@
import { customRandom } from "nanoid";
import safeFetch from "./utilities/safeFetch";
type BaseJsonDocument = {
id: string;
title: string;
readOnly: boolean;
};
export type RawJsonDocument = BaseJsonDocument & {
@@ -17,6 +19,8 @@ export type UrlJsonDocument = BaseJsonDocument & {
export type CreateJsonOptions = {
ttl?: number;
readOnly?: boolean;
injest?: boolean;
metadata?: any;
};
@@ -37,17 +41,33 @@ export async function createFromUrlOrRawJson(
export async function createFromUrl(
url: URL,
title?: string
title?: string,
options?: CreateJsonOptions
): Promise<JSONDocument> {
if (options?.injest) {
const response = await safeFetch(url.href);
if (!response.ok) {
throw new Error(`Failed to injest ${url.href}`);
}
return createFromRawJson(title || url.href, await response.text(), options);
}
const docId = createId();
const doc = {
const doc: JSONDocument = {
id: docId,
type: <const>"url",
url: url.href,
title: title ?? url.hostname,
readOnly: options?.readOnly ?? false,
};
await DOCUMENTS.put(docId, JSON.stringify(doc));
await DOCUMENTS.put(docId, JSON.stringify(doc), {
expirationTtl: options?.ttl ?? undefined,
metadata: options?.metadata ?? undefined,
});
return doc;
}
@@ -58,7 +78,13 @@ export async function createFromRawJson(
options?: CreateJsonOptions
): Promise<JSONDocument> {
const docId = createId();
const doc = { id: docId, type: <const>"raw", contents, title: filename };
const doc: JSONDocument = {
id: docId,
type: <const>"raw",
contents,
title: filename,
readOnly: options?.readOnly ?? false,
};
await DOCUMENTS.put(docId, JSON.stringify(doc), {
expirationTtl: options?.ttl ?? undefined,
+18 -4
View File
@@ -18,6 +18,8 @@ import {
useTheme,
} from "~/components/ThemeProvider";
import openGraphImage from "~/assets/images/opengraph.png";
export const meta: MetaFunction = ({ location }) => {
const description =
"JSON Hero makes reading and understand JSON files easy by giving you a clean and beautiful UI packed with extra features.";
@@ -25,11 +27,11 @@ export const meta: MetaFunction = ({ location }) => {
title: "JSON Viewer - JSON Hero",
viewport: "width=device-width,initial-scale=1",
description,
"og:image": `https://jsonhero.io/images/opengraph.png`,
"og:image": `https://jsonhero.io${openGraphImage}`,
"og:url": `https://jsonhero.io${location.pathname}`,
"og:title": "JSON Hero - A beautiful JSON viewer",
"og:description": description,
"twitter:image": "https://jsonhero.io/images/opengraph.png",
"twitter:image": `https://jsonhero.io${openGraphImage}`,
"twitter:card": "summary_large_image",
"twitter:creator": "@json_hero",
"twitter:site": "@json_hero",
@@ -50,20 +52,32 @@ export function links() {
export type LoaderData = {
theme?: Theme;
starCount?: number;
themeOverride?: Theme;
};
export const loader: LoaderFunction = async ({ request }) => {
const themeSession = await getThemeSession(request);
const starCount = await getStarCount();
const themeOverride = getThemeFromRequest(request);
const data: LoaderData = {
theme: themeSession.getTheme(),
starCount,
themeOverride,
};
return data;
};
function getThemeFromRequest(request: Request): Theme | undefined {
const url = new URL(request.url);
const theme = url.searchParams.get("theme");
if (theme) {
return theme as Theme;
}
return undefined;
}
function App() {
const [theme] = useTheme();
@@ -86,7 +100,7 @@ function App() {
}
export default function AppWithProviders() {
const { theme, starCount } = useLoaderData<LoaderData>();
const { theme, starCount, themeOverride } = useLoaderData<LoaderData>();
const location = useLocation();
@@ -96,7 +110,7 @@ export default function AppWithProviders() {
return (
<ThemeProvider
specifiedTheme={theme}
themeOverride={forceDarkMode ? "dark" : undefined}
themeOverride={forceDarkMode ? "dark" : themeOverride}
>
<StarCountProvider starCount={starCount}>
<App />
+23 -2
View File
@@ -1,8 +1,22 @@
import { ActionFunction, json } from "remix";
import { ActionFunction, json, LoaderFunction } from "remix";
import invariant from "tiny-invariant";
import { sendEvent } from "~/graphJSON.server";
import { createFromRawJson, CreateJsonOptions } from "~/jsonDoc.server";
export const loader: LoaderFunction = async ({ request }) => {
if (request.method === "OPTIONS") {
return new Response(null, {
status: 204,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST",
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Max-Age": "86400",
},
});
}
};
export const action: ActionFunction = async ({ request, context }) => {
const url = new URL(request.url);
@@ -42,5 +56,12 @@ export const action: ActionFunction = async ({ request, context }) => {
})
);
return json({ id: doc.id, title, location: url.toString() });
return json(
{ id: doc.id, title, location: url.toString() },
{
headers: {
"Access-Control-Allow-Origin": "*",
},
}
);
};
+1 -1
View File
@@ -11,7 +11,7 @@ import { GithubBanner } from "~/components/Home/HomeGithubBanner";
export default function Index() {
return (
<div className="overflow-x-hidden">
<HomeHeader />
<HomeHeader fixed={true} />
<HomeHeroSection />
<GithubBanner />
<HomeInfoBoxSection />
+30 -3
View File
@@ -41,6 +41,7 @@ export const loader: LoaderFunction = async ({ params, request }) => {
}
const path = getPathFromRequest(request);
const minimal = getMinimalFromRequest(request);
if (doc.type == "url") {
console.log(`Fetching ${doc.url}...`);
@@ -48,6 +49,10 @@ export const loader: LoaderFunction = async ({ params, request }) => {
const jsonResponse = await safeFetch(doc.url);
if (!jsonResponse.ok) {
console.log(
`Failed to fetch ${doc.url}: ${jsonResponse.status} (${jsonResponse.statusText})`
);
throw new Response(jsonResponse.statusText, {
status: jsonResponse.status,
});
@@ -55,16 +60,20 @@ export const loader: LoaderFunction = async ({ params, request }) => {
const json = await jsonResponse.json();
console.log(`Fetched ${doc.url} with json, returning...`);
return {
doc,
json,
path,
minimal,
};
} else {
return {
doc,
json: JSON.parse(doc.contents),
path,
minimal,
};
}
};
@@ -85,7 +94,24 @@ function getPathFromRequest(request: Request): string | null {
return `$.${path}`;
}
type LoaderData = { doc: JSONDocument; json: unknown; path?: string };
function getMinimalFromRequest(request: Request): boolean | undefined {
const url = new URL(request.url);
const minimal = url.searchParams.get("minimal");
if (!minimal) {
return;
}
return minimal === "true";
}
type LoaderData = {
doc: JSONDocument;
json: unknown;
path?: string;
minimal?: boolean;
};
export const meta: MetaFunction = ({
data,
@@ -118,6 +144,7 @@ export default function JsonDocumentRoute() {
doc={loaderData.doc}
path={loaderData.path}
key={loaderData.doc.id}
minimal={loaderData.minimal}
>
<JsonProvider initialJson={loaderData.json}>
<JsonSchemaProvider>
@@ -125,7 +152,7 @@ export default function JsonDocumentRoute() {
<JsonSearchProvider>
<JsonTreeViewProvider overscan={25}>
<div>
<div className="block sm:hidden fixed bg-black/80 h-screen w-screen z-50 text-white">
<div className="block md:hidden fixed bg-black/80 h-screen w-screen z-50 text-white">
<div className="flex flex-col items-center justify-center h-full text-center">
<LargeTitle>JSON Hero only works on desktop</LargeTitle>
<LargeTitle>👇</LargeTitle>
@@ -139,7 +166,7 @@ export default function JsonDocumentRoute() {
</div>
</div>
<div className="h-screen flex flex-col sm:overflow-hidden">
<Header />
{!loaderData.minimal && <Header />}
<div className="bg-slate-50 flex-grow transition dark:bg-slate-900">
<div className="main-container flex justify-items-stretch h-full">
<SideBar />
+34 -4
View File
@@ -1,23 +1,49 @@
import { LoaderFunction, redirect } from "remix";
import { json, LoaderFunction, redirect } from "remix";
import invariant from "tiny-invariant";
import { sendEvent } from "~/graphJSON.server";
import { createFromRawJson, createFromUrl } from "~/jsonDoc.server";
import {
createFromRawJson,
createFromUrl,
CreateJsonOptions,
} from "~/jsonDoc.server";
export let loader: LoaderFunction = async ({ request, context }) => {
const url = new URL(request.url);
const jsonUrl = url.searchParams.get("url");
const base64EncodedJson = url.searchParams.get("j");
const ttl = url.searchParams.get("ttl");
const readOnly = url.searchParams.get("readonly");
const title = url.searchParams.get("title");
const injest = url.searchParams.get("injest");
if (!jsonUrl && !base64EncodedJson) {
return redirect("/");
}
const options: CreateJsonOptions = {};
if (typeof ttl === "string") {
invariant(ttl.match(/^\d+$/), "ttl must be a number");
options.ttl = parseInt(ttl, 10);
invariant(options.ttl >= 60, "ttl must be at least 60 seconds");
}
if (typeof readOnly === "string") {
options.readOnly = readOnly === "true";
}
if (typeof injest === "string") {
options.injest = injest === "true";
}
if (jsonUrl) {
const jsonURL = new URL(jsonUrl);
invariant(jsonURL, "url must be a valid URL");
const doc = await createFromUrl(jsonURL, jsonURL.href);
const doc = await createFromUrl(jsonURL, title ?? jsonURL.href, options);
context.waitUntil(
sendEvent({
@@ -33,7 +59,11 @@ export let loader: LoaderFunction = async ({ request, context }) => {
}
if (base64EncodedJson) {
const doc = await createFromRawJson("Untitled", atob(base64EncodedJson));
const doc = await createFromRawJson(
title ?? "Untitled",
atob(base64EncodedJson),
options
);
context.waitUntil(
sendEvent({
+310
View File
@@ -0,0 +1,310 @@
---
meta:
title: JSON Hero - Privacy
---
import { HomeHeader } from "~/components/Home/HomeHeader";
<HomeHeader />
<div style={{width: "800px", marginLeft: "12px", marginTop: "12px", marginBottom: "12px"}}>
**PRIVACY NOTICE**
<br />
### Last updated June 01, 2022
<br />
This privacy notice for Stack Hero Limited ("Company," "we," "us," or "our"), describes how and why we might collect, store, use, and/or share ("process") your information when you use our services ("Services"), such as when you:
Visit our website at https://jsonhero.io, or any website of ours that links to this privacy notice
Engage with us in other related ways, including any sales, marketing, or events
Questions or concerns? Reading this privacy notice will help you understand your privacy rights and choices. If you do not agree with our policies and practices, please do not use our Services. If you still have any questions or concerns, please contact us at hello@jsonhero.io.
<br />
**SUMMARY OF KEY POINTS**
<br />
This summary provides key points from our privacy notice, but you can find out more details about any of these topics by clicking the link following each key point or by using our table of contents below to find the section you are looking for. You can also click here to go directly to our table of contents.
<br />
What personal information do we process? When you visit, use, or navigate our Services, we may process personal information depending on how you interact with Stack Hero Limited and the Services, the choices you make, and the products and features you use. Click here to learn more.
<br />
Do we process any sensitive personal information? We may process sensitive personal information when necessary with your consent or as otherwise permitted by applicable law. Click here to learn more.
<br />
Do we receive any information from third parties? We do not receive any information from third parties.
<br />
How do we process your information? We process your information to provide, improve, and administer our Services, communicate with you, for security and fraud prevention, and to comply with law. We may also process your information for other purposes with your consent. We process your information only when we have a valid legal reason to do so. Click here to learn more.
<br />
In what situations and with which parties do we share personal information? We may share information in specific situations and with specific third parties. Click here to learn more.
<br />
How do we keep your information safe? We have organizational and technical processes and procedures in place to protect your personal information. However, no electronic transmission over the internet or information storage technology can be guaranteed to be 100% secure, so we cannot promise or guarantee that hackers, cybercriminals, or other unauthorized third parties will not be able to defeat our security and improperly collect, access, steal, or modify your information. Click here to learn more.
<br />
What are your rights? Depending on where you are located geographically, the applicable privacy law may mean you have certain rights regarding your personal information. Click here to learn more.
<br />
How do I exercise my rights? The easiest way to exercise your rights is by filling out our data subject request form available here: hello@jsonhero.io, or by contacting us. We will consider and act upon any request in accordance with applicable data protection laws.
<br />
Want to learn more about what Stack Hero Limited does with any information we collect? Click here to review the notice in full.
<br />
**1. WHAT INFORMATION DO WE COLLECT?**
<br />
In Short: We collect personal information that you provide to us.
<br />
We collect personal information that you voluntarily provide to us when you express an interest in obtaining information about us or our products and Services, when you participate in activities on the Services, or otherwise when you contact us.
<br />
Sensitive Information. When necessary, with your consent or as otherwise permitted by applicable law, we process the following categories of sensitive information:
All personal information that you provide to us must be true, complete, and accurate, and you must notify us of any changes to such personal information.
<br />
Information automatically collected
<br />
In Short: Some information — such as your Internet Protocol (IP) address and/or browser and device characteristics — is collected automatically when you visit our Services.
<br />
We automatically collect certain information when you visit, use, or navigate the Services. This information does not reveal your specific identity (like your name or contact information) but may include device and usage information, such as your IP address, browser and device characteristics, operating system, language preferences, referring URLs, device name, country, location, information about how and when you use our Services, and other technical information. This information is primarily needed to maintain the security and operation of our Services, and for our internal analytics and reporting purposes.
<br />
Like many businesses, we also collect information through cookies and similar technologies.
<br />
The information we collect includes:
Log and Usage Data. Log and usage data is service-related, diagnostic, usage, and performance information our servers automatically collect when you access or use our Services and which we record in log files. Depending on how you interact with us, this log data may include your IP address, device information, browser type, and settings and information about your activity in the Services (such as the date/time stamps associated with your usage, pages and files viewed, searches, and other actions you take such as which features you use), device event information (such as system activity, error reports (sometimes called "crash dumps"), and hardware settings).
<br />
**2. HOW DO WE PROCESS YOUR INFORMATION?**
<br />
In Short: We process your information to provide, improve, and administer our Services, communicate with you, for security and fraud prevention, and to comply with law. We may also process your information for other purposes with your consent.
<br />
We process your personal information for a variety of reasons, depending on how you interact with our Services, including:
<br />
To save or protect an individual's vital interest. We may process your information when necessary to save or protect an individuals vital interest, such as to prevent harm.
<br />
**3. WHAT LEGAL BASES DO WE RELY ON TO PROCESS YOUR INFORMATION?**
<br />
In Short: We only process your personal information when we believe it is necessary and we have a valid legal reason (i.e., legal basis) to do so under applicable law, like with your consent, to comply with laws, to provide you with services to enter into or fulfill our contractual obligations, to protect your rights, or to fulfill our legitimate business interests.
<br />
If you are located in the EU or UK, this section applies to you.
The General Data Protection Regulation (GDPR) and UK GDPR require us to explain the valid legal bases we rely on in order to process your personal information. As such, we may rely on the following legal bases to process your personal information:
Consent. We may process your information if you have given us permission (i.e., consent) to use your personal information for a specific purpose. You can withdraw your consent at any time. Click here to learn more.
Legal Obligations. We may process your information where we believe it is necessary for compliance with our legal obligations, such as to cooperate with a law enforcement body or regulatory agency, exercise or defend our legal rights, or disclose your information as evidence in litigation in which we are involved.
Vital Interests. We may process your information where we believe it is necessary to protect your vital interests or the vital interests of a third party, such as situations involving potential threats to the safety of any person.
<br />
If you are located in Canada, this section applies to you.
We may process your information if you have given us specific permission (i.e., express consent) to use your personal information for a specific purpose, or in situations where your permission can be inferred (i.e., implied consent). You can withdraw your consent at any time. Click here to learn more.
<br />
In some exceptional cases, we may be legally permitted under applicable law to process your information without your consent, including, for example:
If collection is clearly in the interests of an individual and consent cannot be obtained in a timely way
For investigations and fraud detection and prevention
For business transactions provided certain conditions are met
If it is contained in a witness statement and the collection is necessary to assess, process, or settle an insurance claim
For identifying injured, ill, or deceased persons and communicating with next of kin
If we have reasonable grounds to believe an individual has been, is, or may be victim of financial abuse
If it is reasonable to expect collection and use with consent would compromise the availability or the accuracy of the information and the collection is reasonable for purposes related to investigating a breach of an agreement or a contravention of the laws of Canada or a province
If disclosure is required to comply with a subpoena, warrant, court order, or rules of the court relating to the production of records
If it was produced by an individual in the course of their employment, business, or profession and the collection is consistent with the purposes for which the information was produced
If the collection is solely for journalistic, artistic, or literary purposes
If the information is publicly available and is specified by the regulations
<br />
**4. WHEN AND WITH WHOM DO WE SHARE YOUR PERSONAL INFORMATION?**
<br />
In Short: We may share information in specific situations described in this section and/or with the following third parties.
<br />
We may need to share your personal information in the following situations:
Business Transfers. We may share or transfer your information in connection with, or during negotiations of, any merger, sale of company assets, financing, or acquisition of all or a portion of our business to another company.
<br />
**5. DO WE USE COOKIES AND OTHER TRACKING TECHNOLOGIES?**
In Short: We may use cookies and other tracking technologies to collect and store your information.
<br />
We may use cookies and similar tracking technologies (like web beacons and pixels) to access or store information. Specific information about how we use such technologies and how you can refuse certain cookies is set out in our Cookie Notice.
<br />
**6. HOW LONG DO WE KEEP YOUR INFORMATION?**
<br />
In Short: We keep your information for as long as necessary to fulfill the purposes outlined in this privacy notice unless otherwise required by law.
<br />
We will only keep your personal information for as long as it is necessary for the purposes set out in this privacy notice, unless a longer retention period is required or permitted by law (such as tax, accounting, or other legal requirements). No purpose in this notice will require us keeping your personal information for longer than 2 years.
<br />
When we have no ongoing legitimate business need to process your personal information, we will either delete or anonymize such information, or, if this is not possible (for example, because your personal information has been stored in backup archives), then we will securely store your personal information and isolate it from any further processing until deletion is possible.
<br />
**7. HOW DO WE KEEP YOUR INFORMATION SAFE?**
<br />
In Short: We aim to protect your personal information through a system of organizational and technical security measures.
<br />
We have implemented appropriate and reasonable technical and organizational security measures designed to protect the security of any personal information we process. However, despite our safeguards and efforts to secure your information, no electronic transmission over the Internet or information storage technology can be guaranteed to be 100% secure, so we cannot promise or guarantee that hackers, cybercriminals, or other unauthorized third parties will not be able to defeat our security and improperly collect, access, steal, or modify your information. Although we will do our best to protect your personal information, transmission of personal information to and from our Services is at your own risk. You should only access the Services within a secure environment.
<br />
**8. WHAT ARE YOUR PRIVACY RIGHTS?**
<br />
In Short: In some regions, such as the European Economic Area (EEA), United Kingdom (UK), and Canada, you have rights that allow you greater access to and control over your personal information. You may review, change, or terminate your account at any time.
<br />
In some regions (like the EEA, UK, and Canada), you have certain rights under applicable data protection laws. These may include the right (i) to request access and obtain a copy of your personal information, (ii) to request rectification or erasure; (iii) to restrict the processing of your personal information; and (iv) if applicable, to data portability. In certain circumstances, you may also have the right to object to the processing of your personal information. You can make such a request by contacting us by using the contact details provided in the section "HOW CAN YOU CONTACT US ABOUT THIS NOTICE?" below.
<br />
We will consider and act upon any request in accordance with applicable data protection laws.
<br />
If you are located in the EEA or UK and you believe we are unlawfully processing your personal information, you also have the right to complain to your local data protection supervisory authority. You can find their contact details here: https://ec.europa.eu/justice/data-protection/bodies/authorities/index_en.htm.
<br />
If you are located in Switzerland, the contact details for the data protection authorities are available here: https://www.edoeb.admin.ch/edoeb/en/home.html.
<br />
Withdrawing your consent: If we are relying on your consent to process your personal information, which may be express and/or implied consent depending on the applicable law, you have the right to withdraw your consent at any time. You can withdraw your consent at any time by contacting us by using the contact details provided in the section "HOW CAN YOU CONTACT US ABOUT THIS NOTICE?" below.
<br />
However, please note that this will not affect the lawfulness of the processing before its withdrawal, nor when applicable law allows, will it affect the processing of your personal information conducted in reliance on lawful processing grounds other than consent.
<br />
Cookies and similar technologies: Most Web browsers are set to accept cookies by default. If you prefer, you can usually choose to set your browser to remove cookies and to reject cookies. If you choose to remove cookies or reject cookies, this could affect certain features or services of our Services. To opt out of interest-based advertising by advertisers on our Services visit http://www.aboutads.info/choices/.
<br />
If you have questions or comments about your privacy rights, you may email us at hello@jsonhero.io.
<br />
**9. CONTROLS FOR DO-NOT-TRACK FEATURES**
<br />
Most web browsers and some mobile operating systems and mobile applications include a Do-Not-Track ("DNT") feature or setting you can activate to signal your privacy preference not to have data about your online browsing activities monitored and collected. At this stage no uniform technology standard for recognizing and implementing DNT signals has been finalized. As such, we do not currently respond to DNT browser signals or any other mechanism that automatically communicates your choice not to be tracked online. If a standard for online tracking is adopted that we must follow in the future, we will inform you about that practice in a revised version of this privacy notice.
<br />
**10. DO CALIFORNIA RESIDENTS HAVE SPECIFIC PRIVACY RIGHTS?**
<br />
In Short: Yes, if you are a resident of California, you are granted specific rights regarding access to your personal information.
<br />
California Civil Code Section 1798.83, also known as the "Shine The Light" law, permits our users who are California residents to request and obtain from us, once a year and free of charge, information about categories of personal information (if any) we disclosed to third parties for direct marketing purposes and the names and addresses of all third parties with which we shared personal information in the immediately preceding calendar year. If you are a California resident and would like to make such a request, please submit your request in writing to us using the contact information provided below.
<br />
If you are under 18 years of age, reside in California, and have a registered account with Services, you have the right to request removal of unwanted data that you publicly post on the Services. To request removal of such data, please contact us using the contact information provided below and include the email address associated with your account and a statement that you reside in California. We will make sure the data is not publicly displayed on the Services, but please be aware that the data may not be completely or comprehensively removed from all our systems (e.g., backups, etc.).
<br />
**11. DO WE MAKE UPDATES TO THIS NOTICE?**
<br />
In Short: Yes, we will update this notice as necessary to stay compliant with relevant laws.
<br />
We may update this privacy notice from time to time. The updated version will be indicated by an updated "Revised" date and the updated version will be effective as soon as it is accessible. If we make material changes to this privacy notice, we may notify you either by prominently posting a notice of such changes or by directly sending you a notification. We encourage you to review this privacy notice frequently to be informed of how we are protecting your information.
<br />
**12. HOW CAN YOU CONTACT US ABOUT THIS NOTICE?**
<br />
If you have questions or comments about this notice, you may email us at hello@jsonhero.io or by post to:
<br />
Stack Hero Limited
68 Hanbury St
London, England E1 5JL
United Kingdom
<br />
**13. HOW CAN YOU REVIEW, UPDATE, OR DELETE THE DATA WE COLLECT FROM YOU?**
<br />
Based on the applicable laws of your country, you may have the right to request access to the personal information we collect from you, change that information, or delete it in some circumstances. To request to review, update, or delete your personal information, please visit: hello@jsonhero.io.
</div>
+39 -5
View File
@@ -37,7 +37,11 @@ async function getPeekalink(link: string): Promise<PreviewResult> {
}
export async function getUriPreview(uri: string): Promise<PreviewResult> {
const head = await headUri(uri);
console.log(`[getPreview][getUriPreview] ${uri}`);
const url = rewriteUrl(uri);
const head = await headUri(url.href);
// If the url is an image content type, return a preview image
if (
@@ -46,14 +50,14 @@ export async function getUriPreview(uri: string): Promise<PreviewResult> {
contentType.includes(head.contentType)
)
) {
const previewImage = createPreviewImage(uri, head);
const previewImage = createPreviewImage(url.href, head);
return previewImage;
}
// If the url is a json content type, attempt to request the json and return a preview json
if (head?.contentType.includes("application/json")) {
const response = await safeFetch(uri, {
const response = await safeFetch(url.href, {
headers: {
accept: "application/json",
},
@@ -65,10 +69,10 @@ export async function getUriPreview(uri: string): Promise<PreviewResult> {
const jsonBody = await response.json();
return createPreviewJson(uri, jsonBody);
return createPreviewJson(url.href, jsonBody);
}
const peekalinkResult = await getPeekalink(uri);
const peekalinkResult = await getPeekalink(url.href);
return peekalinkResult;
}
@@ -149,3 +153,33 @@ function createPreviewImage(uri: string, head: HeadInfo): PreviewImage {
size: head.contentLength,
};
}
// Rewrites the URL to convert an ipfs: url to use https://ipfs.io/ipfs/
function rewriteUrl(url: string): URL {
const unmodifiedUrl = new URL(url);
console.log(
`[getPreview][rewriteUrl] ${unmodifiedUrl.href}, protocol: ${unmodifiedUrl.protocol}, hostname: ${unmodifiedUrl.hostname}, pathname: ${unmodifiedUrl.pathname}, search: ${unmodifiedUrl.search}, hash: ${unmodifiedUrl.hash}`
);
// Rewrite the URL if it is a relative URL
if (unmodifiedUrl.protocol === "ipfs:") {
if (unmodifiedUrl.hostname === "") {
return new URL(
`https://ipfs.io/ipfs/${unmodifiedUrl.pathname.substring(2)}`
);
} else {
// Parse out the "hostname" from the raw url because hostnames are case-insensitive and automatically lowercased
const urlMatches = url.match(/^ipfs:\/\/([A-Za-z0-9]+)(\/.*)?/i);
const hostname = urlMatches?.[1];
return new URL(
`https://ipfs.io/ipfs/${hostname ?? unmodifiedUrl.hostname}${
unmodifiedUrl.pathname.length > 0 ? `/${unmodifiedUrl.pathname}` : ""
}${unmodifiedUrl.search}`
);
}
}
return unmodifiedUrl;
}
+8 -22
View File
@@ -1,17 +1,6 @@
// Should truncate the number to not show the exact number of stars
// If over 1000, show 1k
// If between 1100 and 1199, show 1.1k
// If between 1200 and 1299, show 1.2k
// If between 1300 and 1399, show 1.3k
// If over 10000, show 10k
// if between 10100 and 10199, show 10.1k
// if between 10200 and 10299, show 10.2k
// if between 10300 and 10399, show 10.3k
// if between 11000 and 11099, show 11k
// if between 11100 and 11199, show 11.1k
// if over 100000, show 100k
// if between 100100 and 100199, show 100.1k
// if between 100200 and 100299, show 100.2k
// Round up to the nearest hundred, so for example, 1150 becomes 1.2k, 1101 becomes 1.1k, etc.
export function formatStarCount(count: number | undefined): string {
if (count === undefined) {
return "⭐️";
@@ -20,14 +9,11 @@ export function formatStarCount(count: number | undefined): string {
if (count < 1000) {
return count.toString();
}
if (count < 10000) {
return `${Math.floor(count / 100) / 10}k`;
}
if (count < 100000) {
return `${Math.floor(count / 100) / 10}k`;
}
if (count < 1000000) {
return `${Math.floor(count / 100) / 10}k`;
}
return `${Math.floor(count / 100000)}k`;
return `${roundWithPrecision(count / 1000, 1)}k`;
}
function roundWithPrecision(value: number, precision: number): number {
const multiplier = Math.pow(10, precision);
return Math.round(value * multiplier) / multiplier;
}
+6
View File
@@ -28,6 +28,10 @@ export function groupRelatedValues(
return "undefined";
} else if (value == null) {
return "null";
} else if (Array.isArray(value)) {
return "[...]";
} else if (typeof value === "object") {
return "{...}";
} else {
return value.toString();
}
@@ -71,6 +75,8 @@ export function getRelatedPathsAtPath(
if (inferredType.name !== "array") continue;
if (index + 1 === pathDepth) continue;
for (
let childIndex = 0;
childIndex < inferredType.value.length;
+402
View File
@@ -0,0 +1,402 @@
[
{
"movie": "Cars 3",
"year": 2017,
"release_date": "2017-05-23",
"director": "Brian Fee",
"character": "Lighting McQueen",
"movie_duration": "01:42:25",
"timestamp": "00:25:10",
"full_line": "Oh, hey, Mr. Sterling. Wow.",
"current_wow_in_movie": 3,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/43fOBsgY8iOJL0dijvFnfl/ea361efc5131a859c173ab5dd3fdfe1e/Cars_3_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/MwAHRe21gYt7qgtQqQSgi/20188401d4ae548db5c4f861fad96f49/Cars_3_Wow_3_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/7dL6LdSOmZ2mbXgdi4KeZl/6fe6efef423d28e5704637658a3650ec/Cars_3_Wow_3_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/7uciNXhLeVXBByZu8ipAr4/5bcae43dde19fa0bb249e9318953ba09/Cars_3_Wow_3_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/KL91bYDqfWVeK6NdaXh1O/f1baca7b2988a2e5853256be5f9e2c89/Cars_3_Wow_3_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/6PlYbs7cO3iXEEM1szcfHZ/bf10fba861c8e0d2c20ac4714974bde1/Cars_3_Wow_3.mp3"
},
{
"movie": "The Internship",
"year": 2013,
"release_date": "2013-05-29",
"director": "Shawn Levy",
"character": "Nick Campbell",
"movie_duration": "02:05:02",
"timestamp": "00:38:59",
"full_line": "Wow! Seven projects.",
"current_wow_in_movie": 1,
"total_wows_in_movie": 5,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6XhmTpc1PoiTf0q1nDYMyv/594947f72b4dba7f3c24938a680dd603/The_Internship_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/11VKtDgrvqvU4atLRF9QV9/f1f1ae91486a001e41fb3f66821f1e62/The_Internship_Wow_1_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/6uDteZpujqfebwhVAXoFkm/768bbb0a63dcef265b0040b54e417483/The_Internship_Wow_1_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/4JTqjEo1kTWtALpSdE9pVs/f9242e24e89c9bd32aa3f5212eca08e9/The_Internship_Wow_1_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/5VGJ7XEkBhxEbLMuDR4AIC/e5b80d57316dbb910cd72685f72b8f2b/The_Internship_Wow_1_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/2vwdiSubw2iEcsl1yCUk0A/3cb1ba763c12fd7711823619b4c3d904/The_Internship_Wow_1.mp3"
},
{
"movie": "Cars 3",
"year": 2017,
"release_date": "2017-05-23",
"director": "Brian Fee",
"character": "Lighting McQueen",
"movie_duration": "01:42:25",
"timestamp": "01:03:08",
"full_line": "Wow. You don't mince words around here, do you?",
"current_wow_in_movie": 10,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/43fOBsgY8iOJL0dijvFnfl/ea361efc5131a859c173ab5dd3fdfe1e/Cars_3_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/3KJZ435sr6Y5k00AnZdOJ2/2aadc7fbc687b38c3f1536a9efc16cdb/Cars_3_Wow_10_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/5vQtn6ny9J79PNG16jsJMR/1b4804366cfb9206d70472ab9b7f37e6/Cars_3_Wow_10_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/6kIDoftFyBJOgvCBe5Jr5k/51be6dcd0a40aff052a4215258f6f2cb/Cars_3_Wow_10_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/923BnYGWVuiPpWeM7e6wv/db39600a6bd7c6a807e95d397d129dd4/Cars_3_Wow_10_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/3iZaUMMLnxCH1fIqcXpYZ0/78e6ee967cae46e64a6f65c6f451115a/Cars_3_Wow_10.mp3"
},
{
"movie": "Night at the Museum: Secret of the Tomb",
"year": 2014,
"release_date": "2014-12-11",
"director": "Shawn Levy",
"character": "Jedediah Smith",
"movie_duration": "01:37:48",
"timestamp": "00:39:36",
"full_line": "Wow. We're a long way from home, boy.",
"current_wow_in_movie": 1,
"total_wows_in_movie": 1,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/4cwNnRAOKM1HuAtlH6dscl/52762736d4815c2390619a4009d66d93/Night_at_the_Museum_Secret_of_the_Tomb_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/uHF5B0MFoK8H0S40Sqld9/fa9aca4d8c52ebff4ab4f390196ab0f1/Night_at_the_Museum_Secret_of_the_Tomb_Wow_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/47qSLctyurmw22A3z7X1YG/8651f02d7e58ebb1eb312a26f732515b/Night_at_the_Museum_Secret_of_the_Tomb_Wow_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/3ZLiolsGZkiTp3p8aLJb3d/e330311fcaed4716f7a603d058ee8767/Night_at_the_Museum_Secret_of_the_Tomb_Wow_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/1ZwmDYe0zdKzsy1MJrANSE/e5e0f5ea1c745baf61b205b6e6574605/Night_at_the_Museum_Secret_of_the_Tomb_Wow_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/5o3CSC67KEoIojYH0UMghP/fbf9dea48ac1914eb7f7bffca802b012/Night_at_the_Museum_Secret_of_the_Tomb_Wow.mp3"
},
{
"movie": "The Internship",
"year": 2013,
"release_date": "2013-05-29",
"director": "Shawn Levy",
"character": "Nick Campbell",
"movie_duration": "02:05:02",
"timestamp": "01:26:54",
"full_line": "You're so cute. It's beautiful. Wow.",
"current_wow_in_movie": 4,
"total_wows_in_movie": 5,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6XhmTpc1PoiTf0q1nDYMyv/594947f72b4dba7f3c24938a680dd603/The_Internship_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/61gtrYzY8qsD3Onc4hUNdx/e1685933255e13fce8578594dbfc30aa/The_Internship_Wow_4_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/3CQQDswj7JP1UU95KdHZ2x/af3e2709d1b8c730a177a56e0395bce3/The_Internship_Wow_4_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/67gEv3fXOzKu5IsscIYAxw/bf8e18b6543eec4ac1952d6c6845cb8f/The_Internship_Wow_4_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/1ZrN3pCfLhimJ2fxxf86n3/43116038f56d789a1514778081c7dcc2/The_Internship_Wow_4_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/5Jphk5c8qFsc7ZiWNeJOKQ/80f43d3758ee8730987c6a859ffec667/The_Internship_Wow_4.mp3"
},
{
"movie": "Cars 3",
"year": 2017,
"release_date": "2017-05-23",
"director": "Brian Fee",
"character": "Lighting McQueen",
"movie_duration": "01:42:25",
"timestamp": "00:22:42",
"full_line": "Wow!",
"current_wow_in_movie": 2,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/43fOBsgY8iOJL0dijvFnfl/ea361efc5131a859c173ab5dd3fdfe1e/Cars_3_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/1xpGU65BqXMplnWjcs0HG7/19ad3a66f22c976b7a345ba7a2d931ce/Cars_3_Wow_2_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/NXF2Haueh2DGbaHOpLDuV/21d43d80d211f6f93062b4f35c6d68ba/Cars_3_Wow_2_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/6mN4TgA3TUcgqUEec3qCOV/c30c18bc851f43db95b3cab17aea0fb0/Cars_3_Wow_2_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/316quJhdUfAkcmKkzPjww0/1780af73d03a7a631c71429d59a064e9/Cars_3_Wow_2_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/7rkrQVW7vsUj6amBflfTQ9/fe38afabd1eeaed70d14a8bbcf68d417/Cars_3_Wow_2.mp3"
},
{
"movie": "Hall Pass",
"year": 2011,
"release_date": "2011-02-25",
"director": "Peter Farrelly and Bobby Farrelly",
"character": "Rick Mills",
"movie_duration": "01:51:35",
"timestamp": "01:26:28",
"full_line": "Wow.",
"current_wow_in_movie": 6,
"total_wows_in_movie": 6,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6jFEUPmYiKifaTuC2cugm8/22087834d091445fc9393cdd9163a901/Hall_Pass_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/V9JGQ3qgfufm4vUkzyJYI/6182dce642b9f31ab698d7ab448688bd/Hall_Pass_Wow_6_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/3RkKyI4NFfakkYzUEO83hG/21d29c6b7695d1b072084a92e05b6283/Hall_Pass_Wow_6_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/54Zd2b5BQrQi4hvZ8IAg8j/9a719307cdf6525257e0dba0afa9ceee/Hall_Pass_Wow_6_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/7Jo3JA6zl3vzAiIkriLLOR/5f488e12cdc1c7792484f098732b790d/Hall_Pass_Wow_6_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/7mZxDvl1IsR95NAg8mKj0t/43a0b44fd325c1de3f86cf6cec3a80d5/Hall_Pass_Wow_6.mp3"
},
{
"movie": "Cars",
"year": 2006,
"release_date": "2006-06-09",
"director": "John Lasseter",
"character": "Lightning McQueen",
"movie_duration": "01:56:36",
"timestamp": "01:23:13",
"full_line": "Wow, you were right!",
"current_wow_in_movie": 4,
"total_wows_in_movie": 5,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6dsHUil72TJLYqbwYMEjH4/387a2d9994a2f1fb069d970a0f30ba32/Cars_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/5WH68GXLSVdosuz0ouwFDF/0b55b456669a8bf8a13e6aecd46cae89/Cars_Wow_4_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/7KSwlI2izHJDNtiGAH32SU/7d7698ac27be72753ddc1e2291399b46/Cars_Wow_4_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/6OExsgI6VJHXcjNZtlqBkx/3152bd6700486416ec0b818829de9374/Cars_Wow_4_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/2ibZvAYOWwmoqQtANL3d8o/9c09f29a7f42ecd996ac991b52b2cc65/Cars_Wow_4_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/iJ5LIuyc3pmvEAaiSVSLS/85e36dc0324b28549ea56a6882a03062/Cars_Wow_4.mp3"
},
{
"movie": "The Big Bounce",
"year": 2004,
"release_date": "2004-01-30",
"director": "George Armitage",
"character": "Jack Ryan",
"movie_duration": "01:28:10",
"timestamp": "00:58:00",
"full_line": "What is it about the produce section? Wow.",
"current_wow_in_movie": 2,
"total_wows_in_movie": 2,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/4hPpMWXhuXQ8HzLusmcaS5/4c46fa4fa8bce13b06d8b4444f6150ef/The_Big_Bounce_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/6TPALdqhVTMztHMSw2qOOj/81d512b2ee8b7c05e7e851eae7790d0e/The_Big_Bounce_Wow_2_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/dEuiSxnBLWP26YHaWiHbz/599ac6fb7a6aead87e8e6efdab759f74/The_Big_Bounce_Wow_2_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/7tA15W0lgJr7CpJNCf1m4Z/86a953df81d88f5fa79e35aa1e4e4dc1/The_Big_Bounce_Wow_2_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/1iKZTtL0qQRDek4y4SduD5/bbd83798b30435b43a2df4c360130d72/The_Big_Bounce_Wow_2_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/6pjOYj0xK38VAkPAl1QK7R/a5f083c60ce9e9c4fa93a8be70817d22/The_Big_Bounce_Wow_2.mp3"
},
{
"movie": "The Big Year",
"year": 2011,
"release_date": "2011-10-14",
"director": "David Frankel",
"character": "Kenny Bostick",
"movie_duration": "01:43:09",
"timestamp": "01:08:23",
"full_line": "Wow, and here I thought I was the bomb at 728.",
"current_wow_in_movie": 3,
"total_wows_in_movie": 3,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/pCjGOhbTCQVjLRN9zTwIi/ce7cdf4b40f3549326d881697aa468a1/The_Big_Year_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/uOdKu6VANHZjtwZXeBU8M/bf1f0f44c7bc01faa648a1b97b1205d3/The_Big_Year_Wow_3_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/y9wR96fHZaNnZr367mjyI/1262c2af0400cb1dc8528c3c8e6a83a4/The_Big_Year_Wow_3_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/55z94kbUxNFIL0rYb1i2mH/ca0505ca5e2017b663f18cca8af571b2/The_Big_Year_Wow_3_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6moRL0SzmkTGzgEuZeVByI/c63fa33d0992c0594ecbaed4fcd7e891/The_Big_Year_Wow_3_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/1dXEYZQP1Fl6CX1roy7ZAH/f31b90a6f961dcc16c16b294f4faf664/The_Big_Year_Wow_3.mp3"
},
{
"movie": "Hall Pass",
"year": 2011,
"release_date": "2011-02-25",
"director": "Peter Farrelly and Bobby Farrelly",
"character": "Rick Mills",
"movie_duration": "01:51:35",
"timestamp": "00:46:55",
"full_line": "Wow. This is awkward. I feel like I'm at my first junior high mixer. You know? When you don't know what to say.",
"current_wow_in_movie": 3,
"total_wows_in_movie": 6,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6jFEUPmYiKifaTuC2cugm8/22087834d091445fc9393cdd9163a901/Hall_Pass_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/4QcL02MHJ8ApVkbfN8cP6E/264c28c1e9195d87f0206e143c5ca54a/Hall_Pass_Wow_3_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/15h0sMoIhdeaPDB8qSsUN9/36245f66352b595dc40bc4d9903fa5b3/Hall_Pass_Wow_3_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/74fQiVcwuT7ePQemGC7ih4/b102922c97c9ff38f47268d648628a22/Hall_Pass_Wow_3_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/7mSGl1rSVtGdfSacwnKVsu/e7ac36e5684f6b64978987d2f68c43db/Hall_Pass_Wow_3_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/2NBIVPDF4o7cy0epTvPOwR/406cd5c17e9b01511f1e350bb96df352/Hall_Pass_Wow_3.mp3"
},
{
"movie": "No Escape",
"year": 2015,
"release_date": "2015-08-26",
"director": "John Erick Dowdle",
"character": "Jack Dwyer",
"movie_duration": "01:43:06",
"timestamp": "00:08:11",
"full_line": "Wow.",
"current_wow_in_movie": 1,
"total_wows_in_movie": 2,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/R3PGhNHo0k5uhnPM7bqIp/5e2b3723c69f7c70313be19417c6fdba/No_Escape_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/3WxIt1hZQtpqnveyAb4xw3/54773cfd70ab4380c47048324f216fc4/No_Escape_Wow_1_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/2JcYMonzmocD3qABm1dpNg/019b752a33b2517033d75ea5bfd20b59/No_Escape_Wow_1_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/5QMtRpqqyfg9irqjPPx5JO/5a24212c22dc20944b5831e5b81efe4d/No_Escape_Wow_1_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6aou6AH16gfvXSccxgcZFf/347d5ff951c851d5fe9928323e6067eb/No_Escape_Wow_1_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/24lDtvWBG8mDqaqBM8Qsr0/4c08f87367dd3399efb6a5554f6f4858/No_Escape_Wow_1.mp3"
},
{
"movie": "Midnight in Paris",
"year": 2011,
"release_date": "2011-05-20",
"director": "Woody Allen",
"character": "Gil Pender",
"movie_duration": "01:33:57",
"timestamp": "00:58:22",
"full_line": "Wow.",
"current_wow_in_movie": 5,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/2ZcfSCe2dlfoVzYMr4b9nK/d566e5ad044dee56645f3bffc7200d64/Midnight_in_Paris_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/1DSaYjQ8SnL1Imeuxe0eXE/100d3106c8a0bf5e80372e2187daf325/Midnight_in_Paris_Wow_5_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/eFI1n7voe4CsxkWEWv2q0/36781ed8f50e508e8e1f79c8e65e601a/Midnight_in_Paris_Wow_5_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/6oSaIzfy7k3DJMvvy3j1kN/d77192d7decb3ba9d06a5210a58df1fa/Midnight_in_Paris_Wow_5_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/4LQ4lkhx41XDC6UcMlmjbA/dfd5e3604c85c9fb9d38ea2dfdd02a2f/Midnight_in_Paris_Wow_5_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/2A5G34x8JCQBuYwU4D2l9S/27cc467b3ff5796ff1bbe113a06a6e64/Midnight_in_Paris_Wow_5.mp3"
},
{
"movie": "The Haunting",
"year": 1999,
"release_date": "1999-07-23",
"director": "Jan de Bont",
"character": "Luke Sanderson",
"movie_duration": "01:52:37",
"timestamp": "00:41:34",
"full_line": "Wow, I sorta got screwed on the ol' bedroom selection.",
"current_wow_in_movie": 4,
"total_wows_in_movie": 5,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6Zu7ux0JYrUWk0UC8vxPtj/d0da8bfbad670655779a9921ac514759/The_Haunting_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/01C128tlGJ79vtlFEPc7V9/184759a9ca6fb1ab57114977afccb2ff/The_Haunting_Wow_4_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/3fR8AigFIp2I1vkSLRhzur/afb0c226daf7806bc9bd9c5e7b008fb4/The_Haunting_Wow_4_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/1OCimrvhXGGfffHqBKaqz7/52d16b12a2d1a8a264b09364afc4c87c/The_Haunting_Wow_4_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6bwt57tdhBCbhsXZ4i0pce/7f6debc7e52f80c78a618f19c8087985/The_Haunting_Wow_4_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/3yvd3DvmY0XUyqWPLUYLqp/1aabc0f7a6b58003ccdaa61469b8d5ee/The_Haunting_Wow_4.mp3"
},
{
"movie": "Cars 3",
"year": 2017,
"release_date": "2017-05-23",
"director": "Brian Fee",
"character": "Lighting McQueen",
"movie_duration": "01:42:25",
"timestamp": "00:26:23",
"full_line": "Wow.",
"current_wow_in_movie": 5,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/43fOBsgY8iOJL0dijvFnfl/ea361efc5131a859c173ab5dd3fdfe1e/Cars_3_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/4Vp8pVwekVrXfmHT3xYCBm/5775501444824d3ee90acda0698e3472/Cars_3_Wow_5_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/6IoA0jwJiPwyXcSbDCrbJ5/5e65c8caa57c62ce9fc862aaeb327255/Cars_3_Wow_5_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/JrOrzMzr2aHCVPkRJUCae/06c5cb7543e95211a6f99ed89e6ea498/Cars_3_Wow_5_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6uufhJa3yX9e0Ib0xsOb46/16f9e55246ad32a33a34d58e621b8942/Cars_3_Wow_5_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/4aUtZzrqLHol2Hhb52zgHQ/ed5c4ef03ebba9ae34659e9058e58461/Cars_3_Wow_5.mp3"
},
{
"movie": "Cars 3",
"year": 2017,
"release_date": "2017-05-23",
"director": "Brian Fee",
"character": "Lighting McQueen",
"movie_duration": "01:42:25",
"timestamp": "00:26:11",
"full_line": "Wow.",
"current_wow_in_movie": 4,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/43fOBsgY8iOJL0dijvFnfl/ea361efc5131a859c173ab5dd3fdfe1e/Cars_3_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/7G6jLS5xfYBOepUxkW7R3c/c78d22f63ba51f371a1550834888b9ca/Cars_3_Wow_4_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/lIhPcmNS5L4DnpY41EpUA/96b0c5b44acf3fe16e68d1e67156d41c/Cars_3_Wow_4_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/1h3am3BfJYUm0VXV4uKF9P/e75775ec0621889efd40ce1bc372735a/Cars_3_Wow_4_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6eO8c3ojQA4rWMlE1hMMOa/c3a9ce4c01b2d342b33144c9f90b9e3e/Cars_3_Wow_4_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/6aJuGg3bTjqd20j59vP1Wv/6cb60311194d3a9aa040882911a5f516/Cars_3_Wow_4.mp3"
},
{
"movie": "Midnight in Paris",
"year": 2011,
"release_date": "2011-05-20",
"director": "Woody Allen",
"character": "Gil Pender",
"movie_duration": "01:33:57",
"timestamp": "01:19:23",
"full_line": "Wow. Yes. Yes. Yes.",
"current_wow_in_movie": 7,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/2ZcfSCe2dlfoVzYMr4b9nK/d566e5ad044dee56645f3bffc7200d64/Midnight_in_Paris_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/7N21xMDnGKfrmBO42xvoc9/c8e05e53e5841c6ba8a659aaf4f6736a/Midnight_in_Paris_Wow_7_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/6CF2q8U9OYgt3yFA7JUBkr/d6ddbf8d57b1cf9d38127735a6266431/Midnight_in_Paris_Wow_7_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/4IoMNf4pF7L3wDZ72Ad31d/efd2e6859fecf3a82a7ecdaf43175826/Midnight_in_Paris_Wow_7_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/5yF8k9rwFj99H69WASEZWU/f9741ed0771da2afd9a26454863137bb/Midnight_in_Paris_Wow_7_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/UmpPa6skALBubhqeKQFpn/2a64c51d16877b8c0bf8c23a74749f7e/Midnight_in_Paris_Wow_7.mp3"
},
{
"movie": "Midnight in Paris",
"year": 2011,
"release_date": "2011-05-20",
"director": "Woody Allen",
"character": "Gil Pender",
"movie_duration": "01:33:57",
"timestamp": "00:48:45",
"full_line": "Wow.",
"current_wow_in_movie": 3,
"total_wows_in_movie": 10,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/2ZcfSCe2dlfoVzYMr4b9nK/d566e5ad044dee56645f3bffc7200d64/Midnight_in_Paris_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/7BBJiej0qdBbuB3Xvimrae/932bc77c9e9a9918949709f846035541/Midnight_in_Paris_Wow_3_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/5tBiVUM6ef2mARH3OXzP92/8ad999e82b7bff5ac4a07d1b4dcb45f3/Midnight_in_Paris_Wow_3_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/39zl8hKK4QP1kASldccYyN/7fc8a7c86872abb9fd527a350365beea/Midnight_in_Paris_Wow_3_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/6rl512MqIH6r04L4O9jHU2/2d843c19937d770860f17719960d8352/Midnight_in_Paris_Wow_3_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/7BDkqWADSOxxYOSQGqNRcr/6fc4696de0d8bcafa487ddea97bb6d3b/Midnight_in_Paris_Wow_3.mp3"
},
{
"movie": "The Internship",
"year": 2013,
"release_date": "2013-05-29",
"director": "Shawn Levy",
"character": "Nick Campbell",
"movie_duration": "02:05:02",
"timestamp": "01:27:38",
"full_line": "Wow, a little heart and everything.",
"current_wow_in_movie": 5,
"total_wows_in_movie": 5,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/6XhmTpc1PoiTf0q1nDYMyv/594947f72b4dba7f3c24938a680dd603/The_Internship_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/1tZ97B7BWV7WhuYJSRL0aQ/c86ca6ce4c67a0f1f5343e68a7b1f428/The_Internship_Wow_5_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/2QlKc3s0Nf10vMLaKGFhVf/a76e1c99d80b13687ae593ad95837706/The_Internship_Wow_5_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/2t0rkVxOB7MVJVzUtVobAV/c396fd523e190052d4b009081b22423f/The_Internship_Wow_5_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/1W9V229ebo4J74HLZGO7pF/f4379d501080fc77221ca8df020a7cd6/The_Internship_Wow_5_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/30nYCeovpGBN5dwVmnTxrf/62c07aaab901bb488bc53ed61d2edffc/The_Internship_Wow_5.mp3"
},
{
"movie": "The Big Year",
"year": 2011,
"release_date": "2011-10-14",
"director": "David Frankel",
"character": "Kenny Bostick",
"movie_duration": "01:43:09",
"timestamp": "00:44:48",
"full_line": "Wow!",
"current_wow_in_movie": 1,
"total_wows_in_movie": 3,
"poster": "https://images.ctfassets.net/bs8ntwkklfua/pCjGOhbTCQVjLRN9zTwIi/ce7cdf4b40f3549326d881697aa468a1/The_Big_Year_Poster.jpg",
"video": {
"1080p": "https://videos.ctfassets.net/bs8ntwkklfua/2IZMW5Aeytz5rtu5MzcFk8/f675c3f6ef153c5df0c81679c80677d4/The_Big_Year_Wow_1_1080p.mp4",
"720p": "https://videos.ctfassets.net/bs8ntwkklfua/2K0XjRiHYd8zbdYcmiTMH4/b2e4a9b02db184a4d4bd71fe15ab37a9/The_Big_Year_Wow_1_720p.mp4",
"480p": "https://videos.ctfassets.net/bs8ntwkklfua/6QMkzBk6HrfuVxVT97mQg/5877904b163d7ccdc4baa92d449858d8/The_Big_Year_Wow_1_480p.mp4",
"360p": "https://videos.ctfassets.net/bs8ntwkklfua/5SFeCoTmyTUEJYtK0kOO92/462afffbd6315182db59271f99a5a829/The_Big_Year_Wow_1_360p.mp4"
},
"audio": "https://assets.ctfassets.net/bs8ntwkklfua/45tt9xH4Uf3Rgt0EJtykZZ/b5cef0c5d09d368359f4a9c0e66a6663/The_Big_Year_Wow_1.mp3"
}
]
+936
View File
@@ -0,0 +1,936 @@
{
"abilities": [
{
"ability": {
"name": "limber",
"url": "https://pokeapi.co/api/v2/ability/7/"
},
"is_hidden": false,
"slot": 1
},
{
"ability": {
"name": "imposter",
"url": "https://pokeapi.co/api/v2/ability/150/"
},
"is_hidden": true,
"slot": 3
}
],
"base_experience": 101,
"forms": [
{
"name": "ditto",
"url": "https://pokeapi.co/api/v2/pokemon-form/132/"
}
],
"game_indices": [
{
"game_index": 76,
"version": {
"name": "red",
"url": "https://pokeapi.co/api/v2/version/1/"
}
},
{
"game_index": 76,
"version": {
"name": "blue",
"url": "https://pokeapi.co/api/v2/version/2/"
}
},
{
"game_index": 76,
"version": {
"name": "yellow",
"url": "https://pokeapi.co/api/v2/version/3/"
}
},
{
"game_index": 132,
"version": {
"name": "gold",
"url": "https://pokeapi.co/api/v2/version/4/"
}
},
{
"game_index": 132,
"version": {
"name": "silver",
"url": "https://pokeapi.co/api/v2/version/5/"
}
},
{
"game_index": 132,
"version": {
"name": "crystal",
"url": "https://pokeapi.co/api/v2/version/6/"
}
},
{
"game_index": 132,
"version": {
"name": "ruby",
"url": "https://pokeapi.co/api/v2/version/7/"
}
},
{
"game_index": 132,
"version": {
"name": "sapphire",
"url": "https://pokeapi.co/api/v2/version/8/"
}
},
{
"game_index": 132,
"version": {
"name": "emerald",
"url": "https://pokeapi.co/api/v2/version/9/"
}
},
{
"game_index": 132,
"version": {
"name": "firered",
"url": "https://pokeapi.co/api/v2/version/10/"
}
},
{
"game_index": 132,
"version": {
"name": "leafgreen",
"url": "https://pokeapi.co/api/v2/version/11/"
}
},
{
"game_index": 132,
"version": {
"name": "diamond",
"url": "https://pokeapi.co/api/v2/version/12/"
}
},
{
"game_index": 132,
"version": {
"name": "pearl",
"url": "https://pokeapi.co/api/v2/version/13/"
}
},
{
"game_index": 132,
"version": {
"name": "platinum",
"url": "https://pokeapi.co/api/v2/version/14/"
}
},
{
"game_index": 132,
"version": {
"name": "heartgold",
"url": "https://pokeapi.co/api/v2/version/15/"
}
},
{
"game_index": 132,
"version": {
"name": "soulsilver",
"url": "https://pokeapi.co/api/v2/version/16/"
}
},
{
"game_index": 132,
"version": {
"name": "black",
"url": "https://pokeapi.co/api/v2/version/17/"
}
},
{
"game_index": 132,
"version": {
"name": "white",
"url": "https://pokeapi.co/api/v2/version/18/"
}
},
{
"game_index": 132,
"version": {
"name": "black-2",
"url": "https://pokeapi.co/api/v2/version/21/"
}
},
{
"game_index": 132,
"version": {
"name": "white-2",
"url": "https://pokeapi.co/api/v2/version/22/"
}
}
],
"height": 3,
"held_items": [
{
"item": {
"name": "metal-powder",
"url": "https://pokeapi.co/api/v2/item/234/"
},
"version_details": [
{
"rarity": 5,
"version": {
"name": "ruby",
"url": "https://pokeapi.co/api/v2/version/7/"
}
},
{
"rarity": 5,
"version": {
"name": "sapphire",
"url": "https://pokeapi.co/api/v2/version/8/"
}
},
{
"rarity": 5,
"version": {
"name": "emerald",
"url": "https://pokeapi.co/api/v2/version/9/"
}
},
{
"rarity": 5,
"version": {
"name": "firered",
"url": "https://pokeapi.co/api/v2/version/10/"
}
},
{
"rarity": 5,
"version": {
"name": "leafgreen",
"url": "https://pokeapi.co/api/v2/version/11/"
}
},
{
"rarity": 5,
"version": {
"name": "diamond",
"url": "https://pokeapi.co/api/v2/version/12/"
}
},
{
"rarity": 5,
"version": {
"name": "pearl",
"url": "https://pokeapi.co/api/v2/version/13/"
}
},
{
"rarity": 5,
"version": {
"name": "platinum",
"url": "https://pokeapi.co/api/v2/version/14/"
}
},
{
"rarity": 5,
"version": {
"name": "heartgold",
"url": "https://pokeapi.co/api/v2/version/15/"
}
},
{
"rarity": 5,
"version": {
"name": "soulsilver",
"url": "https://pokeapi.co/api/v2/version/16/"
}
},
{
"rarity": 5,
"version": {
"name": "black",
"url": "https://pokeapi.co/api/v2/version/17/"
}
},
{
"rarity": 5,
"version": {
"name": "white",
"url": "https://pokeapi.co/api/v2/version/18/"
}
},
{
"rarity": 5,
"version": {
"name": "black-2",
"url": "https://pokeapi.co/api/v2/version/21/"
}
},
{
"rarity": 5,
"version": {
"name": "white-2",
"url": "https://pokeapi.co/api/v2/version/22/"
}
},
{
"rarity": 5,
"version": {
"name": "x",
"url": "https://pokeapi.co/api/v2/version/23/"
}
},
{
"rarity": 5,
"version": {
"name": "y",
"url": "https://pokeapi.co/api/v2/version/24/"
}
},
{
"rarity": 5,
"version": {
"name": "omega-ruby",
"url": "https://pokeapi.co/api/v2/version/25/"
}
},
{
"rarity": 5,
"version": {
"name": "alpha-sapphire",
"url": "https://pokeapi.co/api/v2/version/26/"
}
},
{
"rarity": 5,
"version": {
"name": "sun",
"url": "https://pokeapi.co/api/v2/version/27/"
}
},
{
"rarity": 5,
"version": {
"name": "moon",
"url": "https://pokeapi.co/api/v2/version/28/"
}
},
{
"rarity": 5,
"version": {
"name": "ultra-sun",
"url": "https://pokeapi.co/api/v2/version/29/"
}
},
{
"rarity": 5,
"version": {
"name": "ultra-moon",
"url": "https://pokeapi.co/api/v2/version/30/"
}
}
]
},
{
"item": {
"name": "quick-powder",
"url": "https://pokeapi.co/api/v2/item/251/"
},
"version_details": [
{
"rarity": 50,
"version": {
"name": "diamond",
"url": "https://pokeapi.co/api/v2/version/12/"
}
},
{
"rarity": 50,
"version": {
"name": "pearl",
"url": "https://pokeapi.co/api/v2/version/13/"
}
},
{
"rarity": 50,
"version": {
"name": "platinum",
"url": "https://pokeapi.co/api/v2/version/14/"
}
},
{
"rarity": 50,
"version": {
"name": "heartgold",
"url": "https://pokeapi.co/api/v2/version/15/"
}
},
{
"rarity": 50,
"version": {
"name": "soulsilver",
"url": "https://pokeapi.co/api/v2/version/16/"
}
},
{
"rarity": 50,
"version": {
"name": "black",
"url": "https://pokeapi.co/api/v2/version/17/"
}
},
{
"rarity": 50,
"version": {
"name": "white",
"url": "https://pokeapi.co/api/v2/version/18/"
}
},
{
"rarity": 50,
"version": {
"name": "black-2",
"url": "https://pokeapi.co/api/v2/version/21/"
}
},
{
"rarity": 50,
"version": {
"name": "white-2",
"url": "https://pokeapi.co/api/v2/version/22/"
}
},
{
"rarity": 50,
"version": {
"name": "x",
"url": "https://pokeapi.co/api/v2/version/23/"
}
},
{
"rarity": 50,
"version": {
"name": "y",
"url": "https://pokeapi.co/api/v2/version/24/"
}
},
{
"rarity": 50,
"version": {
"name": "omega-ruby",
"url": "https://pokeapi.co/api/v2/version/25/"
}
},
{
"rarity": 50,
"version": {
"name": "alpha-sapphire",
"url": "https://pokeapi.co/api/v2/version/26/"
}
},
{
"rarity": 50,
"version": {
"name": "sun",
"url": "https://pokeapi.co/api/v2/version/27/"
}
},
{
"rarity": 50,
"version": {
"name": "moon",
"url": "https://pokeapi.co/api/v2/version/28/"
}
},
{
"rarity": 50,
"version": {
"name": "ultra-sun",
"url": "https://pokeapi.co/api/v2/version/29/"
}
},
{
"rarity": 50,
"version": {
"name": "ultra-moon",
"url": "https://pokeapi.co/api/v2/version/30/"
}
}
]
}
],
"id": 132,
"is_default": true,
"location_area_encounters": "https://pokeapi.co/api/v2/pokemon/132/encounters",
"moves": [
{
"move": {
"name": "transform",
"url": "https://pokeapi.co/api/v2/move/144/"
},
"version_group_details": [
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "red-blue",
"url": "https://pokeapi.co/api/v2/version-group/1/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "yellow",
"url": "https://pokeapi.co/api/v2/version-group/2/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "gold-silver",
"url": "https://pokeapi.co/api/v2/version-group/3/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "crystal",
"url": "https://pokeapi.co/api/v2/version-group/4/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "ruby-sapphire",
"url": "https://pokeapi.co/api/v2/version-group/5/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "emerald",
"url": "https://pokeapi.co/api/v2/version-group/6/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "firered-leafgreen",
"url": "https://pokeapi.co/api/v2/version-group/7/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "diamond-pearl",
"url": "https://pokeapi.co/api/v2/version-group/8/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "platinum",
"url": "https://pokeapi.co/api/v2/version-group/9/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "heartgold-soulsilver",
"url": "https://pokeapi.co/api/v2/version-group/10/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "black-white",
"url": "https://pokeapi.co/api/v2/version-group/11/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "colosseum",
"url": "https://pokeapi.co/api/v2/version-group/12/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "xd",
"url": "https://pokeapi.co/api/v2/version-group/13/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "black-2-white-2",
"url": "https://pokeapi.co/api/v2/version-group/14/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "x-y",
"url": "https://pokeapi.co/api/v2/version-group/15/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "omega-ruby-alpha-sapphire",
"url": "https://pokeapi.co/api/v2/version-group/16/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "sun-moon",
"url": "https://pokeapi.co/api/v2/version-group/17/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "ultra-sun-ultra-moon",
"url": "https://pokeapi.co/api/v2/version-group/18/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "lets-go-pikachu-lets-go-eevee",
"url": "https://pokeapi.co/api/v2/version-group/19/"
}
},
{
"level_learned_at": 1,
"move_learn_method": {
"name": "level-up",
"url": "https://pokeapi.co/api/v2/move-learn-method/1/"
},
"version_group": {
"name": "sword-shield",
"url": "https://pokeapi.co/api/v2/version-group/20/"
}
}
]
}
],
"name": "ditto",
"order": 214,
"past_types": [],
"species": {
"name": "ditto",
"url": "https://pokeapi.co/api/v2/pokemon-species/132/"
},
"sprites": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/132.png",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/shiny/132.png",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/132.png",
"front_shiny_female": null,
"other": {
"dream_world": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/132.svg",
"front_female": null
},
"home": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/home/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/home/shiny/132.png",
"front_shiny_female": null
},
"official-artwork": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/132.png"
}
},
"versions": {
"generation-i": {
"red-blue": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/back/132.png",
"back_gray": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/back/gray/132.png",
"back_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/transparent/back/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/132.png",
"front_gray": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/gray/132.png",
"front_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/red-blue/transparent/132.png"
},
"yellow": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/back/132.png",
"back_gray": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/back/gray/132.png",
"back_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/transparent/back/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/132.png",
"front_gray": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/gray/132.png",
"front_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-i/yellow/transparent/132.png"
}
},
"generation-ii": {
"crystal": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/back/132.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/back/shiny/132.png",
"back_shiny_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/transparent/back/shiny/132.png",
"back_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/transparent/back/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/shiny/132.png",
"front_shiny_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/transparent/shiny/132.png",
"front_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/crystal/transparent/132.png"
},
"gold": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/gold/back/132.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/gold/back/shiny/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/gold/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/gold/shiny/132.png",
"front_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/gold/transparent/132.png"
},
"silver": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/silver/back/132.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/silver/back/shiny/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/silver/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/silver/shiny/132.png",
"front_transparent": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-ii/silver/transparent/132.png"
}
},
"generation-iii": {
"emerald": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/emerald/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/emerald/shiny/132.png"
},
"firered-leafgreen": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/firered-leafgreen/back/132.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/firered-leafgreen/back/shiny/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/firered-leafgreen/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/firered-leafgreen/shiny/132.png"
},
"ruby-sapphire": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/ruby-sapphire/back/132.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/ruby-sapphire/back/shiny/132.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/ruby-sapphire/132.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iii/ruby-sapphire/shiny/132.png"
}
},
"generation-iv": {
"diamond-pearl": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/diamond-pearl/back/132.png",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/diamond-pearl/back/shiny/132.png",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/diamond-pearl/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/diamond-pearl/shiny/132.png",
"front_shiny_female": null
},
"heartgold-soulsilver": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/heartgold-soulsilver/back/132.png",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/heartgold-soulsilver/back/shiny/132.png",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/heartgold-soulsilver/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/heartgold-soulsilver/shiny/132.png",
"front_shiny_female": null
},
"platinum": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/platinum/back/132.png",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/platinum/back/shiny/132.png",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/platinum/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-iv/platinum/shiny/132.png",
"front_shiny_female": null
}
},
"generation-v": {
"black-white": {
"animated": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/animated/back/132.gif",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/animated/back/shiny/132.gif",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/animated/132.gif",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/animated/shiny/132.gif",
"front_shiny_female": null
},
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/back/132.png",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/back/shiny/132.png",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/shiny/132.png",
"front_shiny_female": null
}
},
"generation-vi": {
"omegaruby-alphasapphire": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vi/omegaruby-alphasapphire/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vi/omegaruby-alphasapphire/shiny/132.png",
"front_shiny_female": null
},
"x-y": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vi/x-y/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vi/x-y/shiny/132.png",
"front_shiny_female": null
}
},
"generation-vii": {
"icons": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vii/icons/132.png",
"front_female": null
},
"ultra-sun-ultra-moon": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vii/ultra-sun-ultra-moon/132.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-vii/ultra-sun-ultra-moon/shiny/132.png",
"front_shiny_female": null
}
},
"generation-viii": {
"icons": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/versions/generation-viii/icons/132.png",
"front_female": null
}
}
}
},
"stats": [
{
"base_stat": 48,
"effort": 1,
"stat": {
"name": "hp",
"url": "https://pokeapi.co/api/v2/stat/1/"
}
},
{
"base_stat": 48,
"effort": 0,
"stat": {
"name": "attack",
"url": "https://pokeapi.co/api/v2/stat/2/"
}
},
{
"base_stat": 48,
"effort": 0,
"stat": {
"name": "defense",
"url": "https://pokeapi.co/api/v2/stat/3/"
}
},
{
"base_stat": 48,
"effort": 0,
"stat": {
"name": "special-attack",
"url": "https://pokeapi.co/api/v2/stat/4/"
}
},
{
"base_stat": 48,
"effort": 0,
"stat": {
"name": "special-defense",
"url": "https://pokeapi.co/api/v2/stat/5/"
}
},
{
"base_stat": 48,
"effort": 0,
"stat": {
"name": "speed",
"url": "https://pokeapi.co/api/v2/stat/6/"
}
}
],
"types": [
{
"slot": 1,
"type": {
"name": "normal",
"url": "https://pokeapi.co/api/v2/type/1/"
}
}
],
"weight": 40
}
+22
View File
@@ -0,0 +1,22 @@
[
"There has never been a sadness that cant been cured by breakfast food.",
"Dear frozen yogurt, you are the celery of desserts. Be ice cream or be nothing. Zero stars.",
"Shorts over six inches are capri pants, shorts under six inches are European.",
"Sting like a bee. Do not float like a butterfly. Thats ridiculous.",
"Well, I am not usually one for speeches. So, goodbye.",
"I change my locks every 16 days.",
"Are you going to tell a man that he can't fart in his own car?",
"I love nothing.",
"Fishing relaxes me. It's like yoga, except I still get to kill something.",
"My son is several weeks old. He is very familiar with the sound of power tools.",
"Do you have any history of mental illness in your family? I have an uncle who does yoga.",
"If there were more food and fewer people, this would be a perfect party.",
"The three most useless jobs in the world in order are: lawyer, congressman, and doctor.",
"One rage every three months is permitted. Try not to hurt anyone who doesn't deserve it.",
"I hate everything.",
"Ive had the same haircut since 1978 and Ive driven the same car since 1991. Ive used the same wooden comb for three decades. I have one bowl. I still get my milk delivered by horse.",
"Tom put all my records into this rectangle!",
"Its pointless for a human to paint scenes of nature when they can go outside and stand in it.",
"My first ex-wifes name is Tammy. My second ex-wifes name is Tammy. My Moms name is Tamara…she goes by Tammy.",
"Live your life how you want, but dont confuse drama with happiness."
]
+229 -216
View File
@@ -53,10 +53,11 @@
"@types/lodash-es": "^4.17.6",
"@types/react": "^17.0.24",
"@types/react-dom": "^17.0.9",
"@types/ua-parser-js": "^0.7.36",
"concurrently": "^7.0.0",
"esbuild-visualizer": "^0.3.1",
"jest": "^27.4.7",
"miniflare": "^2.1.0",
"miniflare": "^2.4.0",
"patch-package": "^6.4.7",
"rimraf": "^3.0.2",
"tailwindcss": "^3.0.22",
@@ -1724,9 +1725,9 @@
}
},
"node_modules/@jsonhero/json-infer-types": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/@jsonhero/json-infer-types/-/json-infer-types-1.2.9.tgz",
"integrity": "sha512-QuIOEy7M57bB4fhwadtDUQv5HjrUFD9SXjvQfWUMSQ6zOL8l9S9SV/wRQCn0y0dal+xZPBoMoDqTMJyEBIHnQQ==",
"version": "1.2.10",
"resolved": "https://registry.npmjs.org/@jsonhero/json-infer-types/-/json-infer-types-1.2.10.tgz",
"integrity": "sha512-D91mdtH59U76CRflPZrRfkPMuUPQjBaCqDpH18WnSC0tTqLKymF11XAGcY0KLeXoFd2EqJu9Q8HwxsFOTQQa8A==",
"dependencies": {
"ip-address": "^8.1.0",
"json5": "^2.2.0",
@@ -1793,13 +1794,13 @@
}
},
"node_modules/@miniflare/cache": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.3.0.tgz",
"integrity": "sha512-lOf3WVtrs0ac7KOtsQ+JOGHWR90zsqqJbolE+Wt4hIZuvM2U3t1RD2Ms7U20J301msVIsBAd02IL57H95LXQtQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.4.0.tgz",
"integrity": "sha512-tMDXlUVlThgFubJmlxZoKmLK8kBxDmuMbVMt7csHpXegzkuo2TmIsDqBE/C3CRiJ5xeCQgpD6iZtKBu5Zn5fRA==",
"dev": true,
"dependencies": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"http-cache-semantics": "^4.1.0",
"undici": "4.13.0"
},
@@ -1808,12 +1809,12 @@
}
},
"node_modules/@miniflare/cli-parser": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.3.0.tgz",
"integrity": "sha512-yQzU+OBBvWshr9qVxAe6bWXJLNfpmLYrfkfR3u+rPOo2LYnccWg0f/8jtxrXM1TIyu+tCOJGqCODpO//XtrwWw==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.4.0.tgz",
"integrity": "sha512-Xr5lO8f+oIr9r/b2dfo0on1p0MNN+pkwRHWoY5ACSnp9FaGnwm/g71DM7AajIQPIk0TpRsSVNDx8Ygj1LPT+sQ==",
"dev": true,
"dependencies": {
"@miniflare/shared": "2.3.0",
"@miniflare/shared": "2.4.0",
"kleur": "^4.1.4"
},
"engines": {
@@ -1821,14 +1822,14 @@
}
},
"node_modules/@miniflare/core": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.3.0.tgz",
"integrity": "sha512-Dltb3p+Uq5Lx53DuInckHYaDoMrBq49HE/p/oUDXmzHmQy7EAM6v+nJGKmiM9Uen4OqyqsmlmPWqntSMjaX7Jg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.4.0.tgz",
"integrity": "sha512-vYl8xaWTFzxtkbzx3IkT4Py0OAFdfmFnVo627O1HKHWVGlkjVr8UKtxBpIR+f5pq/HCMzzqA1HM9FXO0dQfy3A==",
"dev": true,
"dependencies": {
"@iarna/toml": "^2.2.5",
"@miniflare/shared": "2.3.0",
"@miniflare/watcher": "2.3.0",
"@miniflare/shared": "2.4.0",
"@miniflare/watcher": "2.4.0",
"busboy": "^0.3.1",
"dotenv": "^10.0.0",
"kleur": "^4.1.4",
@@ -1840,14 +1841,14 @@
}
},
"node_modules/@miniflare/durable-objects": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.3.0.tgz",
"integrity": "sha512-YuzxBBu9xBU3xxD4TzGdeVVMXrE76AWJE4MIqkJR5UmASqayyv//cApGw3+C01NHe07cpbrTM1QTgFo2eBncwQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.4.0.tgz",
"integrity": "sha512-VVLaUXXcAQcYE/3YmDLTacZf5OzR8bib6q1T9NqVb0uK5sLMQqyHvQdsG5rMqs7iyxfJxyZ0bL2OW9XGALOkoQ==",
"dev": true,
"dependencies": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/storage-memory": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"@miniflare/storage-memory": "2.4.0",
"undici": "4.13.0"
},
"engines": {
@@ -1855,13 +1856,13 @@
}
},
"node_modules/@miniflare/html-rewriter": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.3.0.tgz",
"integrity": "sha512-cyWhmQgzhtRlBeg0mxP5FmAq/75ChuLwWQlhokx88XkGnteuh0/DOk3OxIMaveF2oDHKmIlLFgSlzv4NbT1Mng==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.4.0.tgz",
"integrity": "sha512-ZG8819N7LelDD+8+Ss5FZpVyQQq/V2igod0qE68JK4he/w4/yn57Rk6Efb49y15HoHAXl2RpCCsnCyIow/Xjug==",
"dev": true,
"dependencies": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"html-rewriter-wasm": "^0.4.1",
"undici": "4.13.0"
},
@@ -1870,14 +1871,14 @@
}
},
"node_modules/@miniflare/http-server": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.3.0.tgz",
"integrity": "sha512-jTzsRuro3yWVb4T85znBGw1w+ydimgi2PJvHrTuGFKDjFam+R037fGH+HZCAjAgPiBHTroXfkeSXypJLXbQBEg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.4.0.tgz",
"integrity": "sha512-r6Z/nqxE0oa1z63L95yvnG0PUeLRxZOeGS7ADxZMFKan4WD5lvYtSKDuDEm0lkbQshCOHQ3uXFr0cotOm8JoMQ==",
"dev": true,
"dependencies": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/web-sockets": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"@miniflare/web-sockets": "2.4.0",
"kleur": "^4.1.4",
"selfsigned": "^2.0.0",
"undici": "4.13.0",
@@ -1889,9 +1890,9 @@
}
},
"node_modules/@miniflare/http-server/node_modules/ws": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz",
"integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==",
"dev": true,
"engines": {
"node": ">=10.0.0"
@@ -1910,37 +1911,37 @@
}
},
"node_modules/@miniflare/kv": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.3.0.tgz",
"integrity": "sha512-m0zu5sRoaFDm+WVTnLRrz8+77b6eVxQN7cLN6ZQiKP3Nd8cAggPdaIIef8dVksWbPvOW9DztoUVZTB1v9EOaLA==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.4.0.tgz",
"integrity": "sha512-1UW7f1386xR6EDEXNZOR1TpFwQfRRSxUPqD6m/U0WprlsbM0cIYGz+AUeaVbkFf8lfE2MeXCUrjbWsLOvsnw3g==",
"dev": true,
"dependencies": {
"@miniflare/shared": "2.3.0"
"@miniflare/shared": "2.4.0"
},
"engines": {
"node": ">=16.7"
}
},
"node_modules/@miniflare/runner-vm": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.3.0.tgz",
"integrity": "sha512-/aB997YodNcRFxtlnvEn4BJSIqKXRw29FexOqCWwWv+b7XQkTTxd4IfSocIFi6du3265gp85icqP/hxsLAQBVg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.4.0.tgz",
"integrity": "sha512-7sdwBYzXQTwYeR3tTvQ+vJfzc7BXwqR8AUPK9l5gvCtg+Geq9sMslr5SikIJpgcvbYqKDjvC9DQEPJ3sqr9cSQ==",
"dev": true,
"dependencies": {
"@miniflare/shared": "2.3.0"
"@miniflare/shared": "2.4.0"
},
"engines": {
"node": ">=16.7"
}
},
"node_modules/@miniflare/scheduler": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.3.0.tgz",
"integrity": "sha512-Er80srMNXbxqGbYXtiZkD8UlveHsw4cpwcytQgsEJCHG7/48vC/rpmtN0zKOWFEHifaOUvHhURZRJu+S2E4Stg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.4.0.tgz",
"integrity": "sha512-dfMCXoAS8Y+3xABNxYju62I2xIBS54Op7ohCHoatvAM5RvualJUPICEMPZzX6/z29q5xPIeSLhLDhl/asAQ19w==",
"dev": true,
"dependencies": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"cron-schedule": "^3.0.4"
},
"engines": {
@@ -1948,9 +1949,9 @@
}
},
"node_modules/@miniflare/shared": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.3.0.tgz",
"integrity": "sha512-TvCSUe1op5AKINx3zHSSdfAbdoV3eSF3MzeVQA4+1NmtuefY8cjujQCdILeAxg3oPbHXaZkc7j1zZ13rYdvwGQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.4.0.tgz",
"integrity": "sha512-lPQFzBUVGNQ93gQ/dliToWnO0OqAgsD3/902Pd/IixVSRwRj3BTnYv2dHMUKZcODBPrhnbqZeqcPWdBLzEx8uw==",
"dev": true,
"dependencies": {
"ignore": "^5.1.8",
@@ -1961,64 +1962,64 @@
}
},
"node_modules/@miniflare/sites": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.3.0.tgz",
"integrity": "sha512-WkRBxQbf6k/eS1D/D1mw30Zs253TFeIkWFh8HQ8Z36Kdt79rNVezT9YmRpahUfvYXmubTquV5JftNOAhvbOJXA==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.4.0.tgz",
"integrity": "sha512-YZy/TujnR1lkBvCncDDQ8tsWsXRE4JJ4x9a0bKN/XnZh7r6OhDM0sw4BFcBQhT6Ukdtttam1O3FlJxnMitrDGg==",
"dev": true,
"dependencies": {
"@miniflare/kv": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/storage-file": "2.3.0"
"@miniflare/kv": "2.4.0",
"@miniflare/shared": "2.4.0",
"@miniflare/storage-file": "2.4.0"
},
"engines": {
"node": ">=16.7"
}
},
"node_modules/@miniflare/storage-file": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.3.0.tgz",
"integrity": "sha512-1rda/MC0R8wVC6N7Na2EdSGNAZv6o/yugp7Z+GikJ1vbgi1+9soAczDzFtaq4jrBzAkCFOMT79DgkSKSl59vxg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.4.0.tgz",
"integrity": "sha512-f1AUMz8xps/4VhNJMb8JeCevZFeU4pg2lIWmC11gG4xeq2nibTPBi6Qtx594Le7ZKij/tF6rRsoNfGau2Q5gdw==",
"dev": true,
"dependencies": {
"@miniflare/shared": "2.3.0",
"@miniflare/storage-memory": "2.3.0"
"@miniflare/shared": "2.4.0",
"@miniflare/storage-memory": "2.4.0"
},
"engines": {
"node": ">=16.7"
}
},
"node_modules/@miniflare/storage-memory": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.3.0.tgz",
"integrity": "sha512-cMqQK9neRySchoNUVhQYOmLFrYkAjT5V113Wfxmsz93dladQ1/mXOsndqPrlb+5hhnwbw4Ns67nyzkHUe9sX/g==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.4.0.tgz",
"integrity": "sha512-mhWwgHhDNtEa7y1bYbdVucV0lqUmzagYXUSppAdSGS5JPyJMyw3HseqRNTk6gC/vKlvEYlFf3ugWcREGCedr9A==",
"dev": true,
"dependencies": {
"@miniflare/shared": "2.3.0"
"@miniflare/shared": "2.4.0"
},
"engines": {
"node": ">=16.7"
}
},
"node_modules/@miniflare/watcher": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.3.0.tgz",
"integrity": "sha512-G0ZE9WHIcdR2TlPXwwQabULDIMZ9LujfC0tn+yXmPXKxgWoJWcWFmUbkhVDcOJzkIz2TAhRlLbW11n8nChUHFQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.4.0.tgz",
"integrity": "sha512-gDQRUxwOjmctvowyd4Hcdy3fjxz3ERKzirp6TvA3AWUohKZk3IhwGlaA8aCwbdP+ELYQlG5wK44AfLSGi956fg==",
"dev": true,
"dependencies": {
"@miniflare/shared": "2.3.0"
"@miniflare/shared": "2.4.0"
},
"engines": {
"node": ">=16.7"
}
},
"node_modules/@miniflare/web-sockets": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.3.0.tgz",
"integrity": "sha512-3xcPG1vfiMb2obVC4jNtS5VzUxzM1rdUrWPCG86gpZaHky0xC1U7Tp+eFdxQFTty99HPYrrVKXl1ww2h1LGWRA==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.4.0.tgz",
"integrity": "sha512-cz/cN0GoQOXRLh80UmlcEJODPIw2ijKBK3PLRzvfTzqQ5avK6wp2M8Fj8C/5JIT6g7siwvBANyKXD3U3RpKGHQ==",
"dev": true,
"dependencies": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"undici": "4.13.0",
"ws": "^8.2.2"
},
@@ -2027,9 +2028,9 @@
}
},
"node_modules/@miniflare/web-sockets/node_modules/ws": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz",
"integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==",
"dev": true,
"engines": {
"node": ">=10.0.0"
@@ -3050,6 +3051,12 @@
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
"dev": true
},
"node_modules/@types/ua-parser-js": {
"version": "0.7.36",
"resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz",
"integrity": "sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==",
"dev": true
},
"node_modules/@types/unist": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
@@ -4396,9 +4403,9 @@
"integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA=="
},
"node_modules/cron-schedule": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.5.tgz",
"integrity": "sha512-YjtB4jy7RJEX8j9GokHp+y8S/ihCHjrD2Z3E13LSGP/+G0Sdv+MEKsZu5wPLLWwW1HQc4HwpGMFU3GUTStZTaA==",
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz",
"integrity": "sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==",
"dev": true
},
"node_modules/cross-spawn": {
@@ -10236,25 +10243,25 @@
}
},
"node_modules/miniflare": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.3.0.tgz",
"integrity": "sha512-0lK4Df+jfqLDSDDgj4AOJffb1G6sN7XvB5QGUaquEakW+qPdurydyNhFKcFKBgpk4ozN6WTmB2x802iPXQcJJQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.4.0.tgz",
"integrity": "sha512-xOBL/dQsUL95rxIYO+KrrVPPpm2TAf6TKl4AvhcjSfUeVkzDWd/y9f7hXCt8x6srDoK0Z2LpYouatvSfSUzGOw==",
"dev": true,
"dependencies": {
"@miniflare/cache": "2.3.0",
"@miniflare/cli-parser": "2.3.0",
"@miniflare/core": "2.3.0",
"@miniflare/durable-objects": "2.3.0",
"@miniflare/html-rewriter": "2.3.0",
"@miniflare/http-server": "2.3.0",
"@miniflare/kv": "2.3.0",
"@miniflare/runner-vm": "2.3.0",
"@miniflare/scheduler": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/sites": "2.3.0",
"@miniflare/storage-file": "2.3.0",
"@miniflare/storage-memory": "2.3.0",
"@miniflare/web-sockets": "2.3.0",
"@miniflare/cache": "2.4.0",
"@miniflare/cli-parser": "2.4.0",
"@miniflare/core": "2.4.0",
"@miniflare/durable-objects": "2.4.0",
"@miniflare/html-rewriter": "2.4.0",
"@miniflare/http-server": "2.4.0",
"@miniflare/kv": "2.4.0",
"@miniflare/runner-vm": "2.4.0",
"@miniflare/scheduler": "2.4.0",
"@miniflare/shared": "2.4.0",
"@miniflare/sites": "2.4.0",
"@miniflare/storage-file": "2.4.0",
"@miniflare/storage-memory": "2.4.0",
"@miniflare/web-sockets": "2.4.0",
"kleur": "^4.1.4",
"semiver": "^1.1.0",
"source-map-support": "^0.5.20",
@@ -10267,7 +10274,7 @@
"node": ">=16.7"
},
"peerDependencies": {
"@miniflare/storage-redis": "2.3.0",
"@miniflare/storage-redis": "2.4.0",
"cron-schedule": "^3.0.4",
"ioredis": "^4.27.9"
},
@@ -10491,9 +10498,9 @@
}
},
"node_modules/node-forge": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz",
"integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"dev": true,
"engines": {
"node": ">= 6.13.0"
@@ -11919,12 +11926,12 @@
}
},
"node_modules/selfsigned": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz",
"integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz",
"integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==",
"dev": true,
"dependencies": {
"node-forge": "^1.2.0"
"node-forge": "^1"
},
"engines": {
"node": ">=10"
@@ -14905,9 +14912,9 @@
}
},
"@jsonhero/json-infer-types": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/@jsonhero/json-infer-types/-/json-infer-types-1.2.9.tgz",
"integrity": "sha512-QuIOEy7M57bB4fhwadtDUQv5HjrUFD9SXjvQfWUMSQ6zOL8l9S9SV/wRQCn0y0dal+xZPBoMoDqTMJyEBIHnQQ==",
"version": "1.2.10",
"resolved": "https://registry.npmjs.org/@jsonhero/json-infer-types/-/json-infer-types-1.2.10.tgz",
"integrity": "sha512-D91mdtH59U76CRflPZrRfkPMuUPQjBaCqDpH18WnSC0tTqLKymF11XAGcY0KLeXoFd2EqJu9Q8HwxsFOTQQa8A==",
"requires": {
"ip-address": "^8.1.0",
"json5": "^2.2.0",
@@ -14962,36 +14969,36 @@
}
},
"@miniflare/cache": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.3.0.tgz",
"integrity": "sha512-lOf3WVtrs0ac7KOtsQ+JOGHWR90zsqqJbolE+Wt4hIZuvM2U3t1RD2Ms7U20J301msVIsBAd02IL57H95LXQtQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.4.0.tgz",
"integrity": "sha512-tMDXlUVlThgFubJmlxZoKmLK8kBxDmuMbVMt7csHpXegzkuo2TmIsDqBE/C3CRiJ5xeCQgpD6iZtKBu5Zn5fRA==",
"dev": true,
"requires": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"http-cache-semantics": "^4.1.0",
"undici": "4.13.0"
}
},
"@miniflare/cli-parser": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.3.0.tgz",
"integrity": "sha512-yQzU+OBBvWshr9qVxAe6bWXJLNfpmLYrfkfR3u+rPOo2LYnccWg0f/8jtxrXM1TIyu+tCOJGqCODpO//XtrwWw==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.4.0.tgz",
"integrity": "sha512-Xr5lO8f+oIr9r/b2dfo0on1p0MNN+pkwRHWoY5ACSnp9FaGnwm/g71DM7AajIQPIk0TpRsSVNDx8Ygj1LPT+sQ==",
"dev": true,
"requires": {
"@miniflare/shared": "2.3.0",
"@miniflare/shared": "2.4.0",
"kleur": "^4.1.4"
}
},
"@miniflare/core": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.3.0.tgz",
"integrity": "sha512-Dltb3p+Uq5Lx53DuInckHYaDoMrBq49HE/p/oUDXmzHmQy7EAM6v+nJGKmiM9Uen4OqyqsmlmPWqntSMjaX7Jg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.4.0.tgz",
"integrity": "sha512-vYl8xaWTFzxtkbzx3IkT4Py0OAFdfmFnVo627O1HKHWVGlkjVr8UKtxBpIR+f5pq/HCMzzqA1HM9FXO0dQfy3A==",
"dev": true,
"requires": {
"@iarna/toml": "^2.2.5",
"@miniflare/shared": "2.3.0",
"@miniflare/watcher": "2.3.0",
"@miniflare/shared": "2.4.0",
"@miniflare/watcher": "2.4.0",
"busboy": "^0.3.1",
"dotenv": "^10.0.0",
"kleur": "^4.1.4",
@@ -15000,38 +15007,38 @@
}
},
"@miniflare/durable-objects": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.3.0.tgz",
"integrity": "sha512-YuzxBBu9xBU3xxD4TzGdeVVMXrE76AWJE4MIqkJR5UmASqayyv//cApGw3+C01NHe07cpbrTM1QTgFo2eBncwQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.4.0.tgz",
"integrity": "sha512-VVLaUXXcAQcYE/3YmDLTacZf5OzR8bib6q1T9NqVb0uK5sLMQqyHvQdsG5rMqs7iyxfJxyZ0bL2OW9XGALOkoQ==",
"dev": true,
"requires": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/storage-memory": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"@miniflare/storage-memory": "2.4.0",
"undici": "4.13.0"
}
},
"@miniflare/html-rewriter": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.3.0.tgz",
"integrity": "sha512-cyWhmQgzhtRlBeg0mxP5FmAq/75ChuLwWQlhokx88XkGnteuh0/DOk3OxIMaveF2oDHKmIlLFgSlzv4NbT1Mng==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.4.0.tgz",
"integrity": "sha512-ZG8819N7LelDD+8+Ss5FZpVyQQq/V2igod0qE68JK4he/w4/yn57Rk6Efb49y15HoHAXl2RpCCsnCyIow/Xjug==",
"dev": true,
"requires": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"html-rewriter-wasm": "^0.4.1",
"undici": "4.13.0"
}
},
"@miniflare/http-server": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.3.0.tgz",
"integrity": "sha512-jTzsRuro3yWVb4T85znBGw1w+ydimgi2PJvHrTuGFKDjFam+R037fGH+HZCAjAgPiBHTroXfkeSXypJLXbQBEg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.4.0.tgz",
"integrity": "sha512-r6Z/nqxE0oa1z63L95yvnG0PUeLRxZOeGS7ADxZMFKan4WD5lvYtSKDuDEm0lkbQshCOHQ3uXFr0cotOm8JoMQ==",
"dev": true,
"requires": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/web-sockets": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"@miniflare/web-sockets": "2.4.0",
"kleur": "^4.1.4",
"selfsigned": "^2.0.0",
"undici": "4.13.0",
@@ -15040,47 +15047,47 @@
},
"dependencies": {
"ws": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz",
"integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==",
"dev": true,
"requires": {}
}
}
},
"@miniflare/kv": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.3.0.tgz",
"integrity": "sha512-m0zu5sRoaFDm+WVTnLRrz8+77b6eVxQN7cLN6ZQiKP3Nd8cAggPdaIIef8dVksWbPvOW9DztoUVZTB1v9EOaLA==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.4.0.tgz",
"integrity": "sha512-1UW7f1386xR6EDEXNZOR1TpFwQfRRSxUPqD6m/U0WprlsbM0cIYGz+AUeaVbkFf8lfE2MeXCUrjbWsLOvsnw3g==",
"dev": true,
"requires": {
"@miniflare/shared": "2.3.0"
"@miniflare/shared": "2.4.0"
}
},
"@miniflare/runner-vm": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.3.0.tgz",
"integrity": "sha512-/aB997YodNcRFxtlnvEn4BJSIqKXRw29FexOqCWwWv+b7XQkTTxd4IfSocIFi6du3265gp85icqP/hxsLAQBVg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.4.0.tgz",
"integrity": "sha512-7sdwBYzXQTwYeR3tTvQ+vJfzc7BXwqR8AUPK9l5gvCtg+Geq9sMslr5SikIJpgcvbYqKDjvC9DQEPJ3sqr9cSQ==",
"dev": true,
"requires": {
"@miniflare/shared": "2.3.0"
"@miniflare/shared": "2.4.0"
}
},
"@miniflare/scheduler": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.3.0.tgz",
"integrity": "sha512-Er80srMNXbxqGbYXtiZkD8UlveHsw4cpwcytQgsEJCHG7/48vC/rpmtN0zKOWFEHifaOUvHhURZRJu+S2E4Stg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.4.0.tgz",
"integrity": "sha512-dfMCXoAS8Y+3xABNxYju62I2xIBS54Op7ohCHoatvAM5RvualJUPICEMPZzX6/z29q5xPIeSLhLDhl/asAQ19w==",
"dev": true,
"requires": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"cron-schedule": "^3.0.4"
}
},
"@miniflare/shared": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.3.0.tgz",
"integrity": "sha512-TvCSUe1op5AKINx3zHSSdfAbdoV3eSF3MzeVQA4+1NmtuefY8cjujQCdILeAxg3oPbHXaZkc7j1zZ13rYdvwGQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.4.0.tgz",
"integrity": "sha512-lPQFzBUVGNQ93gQ/dliToWnO0OqAgsD3/902Pd/IixVSRwRj3BTnYv2dHMUKZcODBPrhnbqZeqcPWdBLzEx8uw==",
"dev": true,
"requires": {
"ignore": "^5.1.8",
@@ -15088,60 +15095,60 @@
}
},
"@miniflare/sites": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.3.0.tgz",
"integrity": "sha512-WkRBxQbf6k/eS1D/D1mw30Zs253TFeIkWFh8HQ8Z36Kdt79rNVezT9YmRpahUfvYXmubTquV5JftNOAhvbOJXA==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.4.0.tgz",
"integrity": "sha512-YZy/TujnR1lkBvCncDDQ8tsWsXRE4JJ4x9a0bKN/XnZh7r6OhDM0sw4BFcBQhT6Ukdtttam1O3FlJxnMitrDGg==",
"dev": true,
"requires": {
"@miniflare/kv": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/storage-file": "2.3.0"
"@miniflare/kv": "2.4.0",
"@miniflare/shared": "2.4.0",
"@miniflare/storage-file": "2.4.0"
}
},
"@miniflare/storage-file": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.3.0.tgz",
"integrity": "sha512-1rda/MC0R8wVC6N7Na2EdSGNAZv6o/yugp7Z+GikJ1vbgi1+9soAczDzFtaq4jrBzAkCFOMT79DgkSKSl59vxg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.4.0.tgz",
"integrity": "sha512-f1AUMz8xps/4VhNJMb8JeCevZFeU4pg2lIWmC11gG4xeq2nibTPBi6Qtx594Le7ZKij/tF6rRsoNfGau2Q5gdw==",
"dev": true,
"requires": {
"@miniflare/shared": "2.3.0",
"@miniflare/storage-memory": "2.3.0"
"@miniflare/shared": "2.4.0",
"@miniflare/storage-memory": "2.4.0"
}
},
"@miniflare/storage-memory": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.3.0.tgz",
"integrity": "sha512-cMqQK9neRySchoNUVhQYOmLFrYkAjT5V113Wfxmsz93dladQ1/mXOsndqPrlb+5hhnwbw4Ns67nyzkHUe9sX/g==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.4.0.tgz",
"integrity": "sha512-mhWwgHhDNtEa7y1bYbdVucV0lqUmzagYXUSppAdSGS5JPyJMyw3HseqRNTk6gC/vKlvEYlFf3ugWcREGCedr9A==",
"dev": true,
"requires": {
"@miniflare/shared": "2.3.0"
"@miniflare/shared": "2.4.0"
}
},
"@miniflare/watcher": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.3.0.tgz",
"integrity": "sha512-G0ZE9WHIcdR2TlPXwwQabULDIMZ9LujfC0tn+yXmPXKxgWoJWcWFmUbkhVDcOJzkIz2TAhRlLbW11n8nChUHFQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.4.0.tgz",
"integrity": "sha512-gDQRUxwOjmctvowyd4Hcdy3fjxz3ERKzirp6TvA3AWUohKZk3IhwGlaA8aCwbdP+ELYQlG5wK44AfLSGi956fg==",
"dev": true,
"requires": {
"@miniflare/shared": "2.3.0"
"@miniflare/shared": "2.4.0"
}
},
"@miniflare/web-sockets": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.3.0.tgz",
"integrity": "sha512-3xcPG1vfiMb2obVC4jNtS5VzUxzM1rdUrWPCG86gpZaHky0xC1U7Tp+eFdxQFTty99HPYrrVKXl1ww2h1LGWRA==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.4.0.tgz",
"integrity": "sha512-cz/cN0GoQOXRLh80UmlcEJODPIw2ijKBK3PLRzvfTzqQ5avK6wp2M8Fj8C/5JIT6g7siwvBANyKXD3U3RpKGHQ==",
"dev": true,
"requires": {
"@miniflare/core": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/core": "2.4.0",
"@miniflare/shared": "2.4.0",
"undici": "4.13.0",
"ws": "^8.2.2"
},
"dependencies": {
"ws": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz",
"integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==",
"dev": true,
"requires": {}
}
@@ -16018,6 +16025,12 @@
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
"dev": true
},
"@types/ua-parser-js": {
"version": "0.7.36",
"resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz",
"integrity": "sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==",
"dev": true
},
"@types/unist": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
@@ -17037,9 +17050,9 @@
"integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA=="
},
"cron-schedule": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.5.tgz",
"integrity": "sha512-YjtB4jy7RJEX8j9GokHp+y8S/ihCHjrD2Z3E13LSGP/+G0Sdv+MEKsZu5wPLLWwW1HQc4HwpGMFU3GUTStZTaA==",
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz",
"integrity": "sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==",
"dev": true
},
"cross-spawn": {
@@ -21188,25 +21201,25 @@
"dev": true
},
"miniflare": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.3.0.tgz",
"integrity": "sha512-0lK4Df+jfqLDSDDgj4AOJffb1G6sN7XvB5QGUaquEakW+qPdurydyNhFKcFKBgpk4ozN6WTmB2x802iPXQcJJQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.4.0.tgz",
"integrity": "sha512-xOBL/dQsUL95rxIYO+KrrVPPpm2TAf6TKl4AvhcjSfUeVkzDWd/y9f7hXCt8x6srDoK0Z2LpYouatvSfSUzGOw==",
"dev": true,
"requires": {
"@miniflare/cache": "2.3.0",
"@miniflare/cli-parser": "2.3.0",
"@miniflare/core": "2.3.0",
"@miniflare/durable-objects": "2.3.0",
"@miniflare/html-rewriter": "2.3.0",
"@miniflare/http-server": "2.3.0",
"@miniflare/kv": "2.3.0",
"@miniflare/runner-vm": "2.3.0",
"@miniflare/scheduler": "2.3.0",
"@miniflare/shared": "2.3.0",
"@miniflare/sites": "2.3.0",
"@miniflare/storage-file": "2.3.0",
"@miniflare/storage-memory": "2.3.0",
"@miniflare/web-sockets": "2.3.0",
"@miniflare/cache": "2.4.0",
"@miniflare/cli-parser": "2.4.0",
"@miniflare/core": "2.4.0",
"@miniflare/durable-objects": "2.4.0",
"@miniflare/html-rewriter": "2.4.0",
"@miniflare/http-server": "2.4.0",
"@miniflare/kv": "2.4.0",
"@miniflare/runner-vm": "2.4.0",
"@miniflare/scheduler": "2.4.0",
"@miniflare/shared": "2.4.0",
"@miniflare/sites": "2.4.0",
"@miniflare/storage-file": "2.4.0",
"@miniflare/storage-memory": "2.4.0",
"@miniflare/web-sockets": "2.4.0",
"kleur": "^4.1.4",
"semiver": "^1.1.0",
"source-map-support": "^0.5.20",
@@ -21364,9 +21377,9 @@
}
},
"node-forge": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz",
"integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"dev": true
},
"node-int64": {
@@ -22398,12 +22411,12 @@
}
},
"selfsigned": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz",
"integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz",
"integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==",
"dev": true,
"requires": {
"node-forge": "^1.2.0"
"node-forge": "^1"
}
},
"semiver": {
+2 -1
View File
@@ -73,10 +73,11 @@
"@types/lodash-es": "^4.17.6",
"@types/react": "^17.0.24",
"@types/react-dom": "^17.0.9",
"@types/ua-parser-js": "^0.7.36",
"concurrently": "^7.0.0",
"esbuild-visualizer": "^0.3.1",
"jest": "^27.4.7",
"miniflare": "^2.1.0",
"miniflare": "^2.4.0",
"patch-package": "^6.4.7",
"rimraf": "^3.0.2",
"tailwindcss": "^3.0.22",
+1 -6
View File
@@ -1,9 +1,4 @@
@import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;700;900&display=swap");
@font-face {
font-family: "MonoLisa";
src: url("/fonts/MonoLisa/woff2/MonoLisa-Regular.woff2") format("woff2"),
url("/fonts/MonoLisa/woff/MonoLisa-Regular.woff") format("woff");
}
@import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;700;900&family=Roboto+Mono&display=swap");
@tailwind base;
@tailwind components;
+4 -2
View File
@@ -16,17 +16,19 @@ module.exports = {
"6xl": ["2.625rem", "3rem"], // 42px
"7xl": "4rem", // 64px
"8xl": "8rem", // 128px
},
extend: {
height: {
viewerHeight: "calc(100vh - 146px)",
inspectorHeight: "calc(100vh - 70px)",
jsonViewerHeight: "calc(100vh - 106px)",
viewerHeightMinimal: "calc(100vh - 106px)",
inspectorHeightMinimal: "calc(100vh - 30px)",
jsonViewerHeightMinimal: "calc(100vh - 66px)",
},
fontFamily: {
sans: ["Source Sans Pro", "sans-serif"],
mono: ["MonoLisa", "monospace"],
mono: ["Roboto Mono", "monospace"],
},
},
},
+5 -3
View File
@@ -6,16 +6,18 @@ describe("formatStarCount", () => {
expect(formatStarCount(0)).toBe("0");
expect(formatStarCount(999)).toBe("999");
expect(formatStarCount(1000)).toBe("1k");
expect(formatStarCount(1050)).toBe("1.1k");
expect(formatStarCount(1100)).toBe("1.1k");
expect(formatStarCount(1200)).toBe("1.2k");
expect(formatStarCount(1300)).toBe("1.3k");
expect(formatStarCount(10000)).toBe("10k");
expect(formatStarCount(10050)).toBe("10.1k");
expect(formatStarCount(10100)).toBe("10.1k");
expect(formatStarCount(10101)).toBe("10.1k");
expect(formatStarCount(10199)).toBe("10.1k");
expect(formatStarCount(10199)).toBe("10.2k");
expect(formatStarCount(10200)).toBe("10.2k");
expect(formatStarCount(52678)).toBe("52.6k");
expect(formatStarCount(99999)).toBe("99.9k");
expect(formatStarCount(52678)).toBe("52.7k");
expect(formatStarCount(99949)).toBe("99.9k");
expect(formatStarCount(100000)).toBe("100k");
expect(formatStarCount(100100)).toBe("100.1k");
expect(formatStarCount(101100)).toBe("101.1k");
+55 -1
View File
@@ -46,6 +46,9 @@ const json = {
},
id: "B65hEUWW01ErVKDDGImKcBquYhwEAkjW6Ic3lPY0299Cc",
created_at: "1607587513",
another_field: {
nested: "value",
},
modifiedAt: null,
},
{
@@ -75,11 +78,62 @@ const json = {
},
id: "B65hEUWW01ErVKDDGImKcBquYhwEAkjW6Ic3lPY0299Cc",
created_at: "1607587513",
another_field: {
nested: "value",
},
},
],
};
describe("calculateRelatedValuesGroups", () => {
test("it should return the correct values when path is an object", () => {
const path = "$.data.1.another_field";
const result = calculateRelatedValuesGroups(path, json);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"paths": Array [
"$.data.1.another_field",
"$.data.2.another_field",
],
"value": "{...}",
},
Object {
"paths": Array [
"$.data.0.another_field",
],
"value": "undefined",
},
]
`);
});
test("it should return the correct values when path is an array", () => {
const path = "$.data.1.recent_asset_ids";
const result = calculateRelatedValuesGroups(path, json);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"paths": Array [
"$.data.1.recent_asset_ids",
"$.data.2.recent_asset_ids",
],
"value": "[...]",
},
Object {
"paths": Array [
"$.data.0.recent_asset_ids",
],
"value": "undefined",
},
]
`);
});
test("it should return the correct related values grouped by value", () => {
const path = "$.data.1.playback_ids.0.policy";
@@ -106,7 +160,7 @@ describe("calculateRelatedValuesGroups", () => {
paths: ["$.data.2.playback_ids.1.policy"],
},
{
value: "[object Object]",
value: "{...}",
paths: ["$.data.2.playback_ids.2.policy"],
},
]);