All files shared.ts

100% Statements 82/82
100% Branches 29/29
100% Functions 4/4
100% Lines 82/82

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 831x 1x 1x 132x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 75x 75x 75x 75x 75x 75x 87x 15x 15x 76x 76x 72x 72x 72x 87x 4x 4x 84x 84x 52x 52x 84x 84x 16x 16x 16x 16x 15x 87x 13x 13x 84x 84x 3x 3x 3x 3x 75x 75x 75x 1x 16x 16x 1x 1x 16x 16x 16x 16x 6x 10x 16x 1x 15x 15x 15x 6x 3x 3x 15x 15x  
import { z } from "zod"
 
const emptyStringToUndefined = (value: unknown) =>
  value === "" ? undefined : value
const nodeEnvValues = ["development", "test", "production"] as const
 
type NodeEnv = (typeof nodeEnvValues)[number]
 
type OptionalUrlOptions = {
  nodeEnv?: NodeEnv | (() => string | undefined)
}
 
export const optionalString = z.preprocess(
  emptyStringToUndefined,
  z.string().min(1).optional()
)
export const optionalUrl = createOptionalUrl()
 
export const nodeEnv = z.enum(nodeEnvValues).default("development")
 
export function createOptionalUrl(options: OptionalUrlOptions = {}) {
  return z.preprocess(
    emptyStringToUndefined,
    z
      .url()
      .optional()
      .superRefine((value, ctx) => {
        if (value === undefined) {
          return
        }
 
        let url: URL
 
        try {
          url = new URL(value)
        } catch {
          return
        }
 
        if (url.protocol === "https:") {
          return
        }
 
        const currentNodeEnv = resolveNodeEnv(options.nodeEnv)
 
        if (
          url.protocol === "http:" &&
          currentNodeEnv !== "production" &&
          isLoopbackHostname(url.hostname)
        ) {
          return
        }
 
        ctx.addIssue({
          code: "custom",
          message:
            "URL must use HTTPS unless it is explicit loopback HTTP outside production.",
        })
      })
  )
}
 
function resolveNodeEnv(value: OptionalUrlOptions["nodeEnv"]): NodeEnv {
  const defaultNodeEnv =
    /* c8 ignore next -- defensive for non-Node runtimes; this package is tested under Node. */
    typeof process === "undefined" ? undefined : process.env.NODE_ENV
  const resolvedValue =
    typeof value === "function" ? value() : (value ?? defaultNodeEnv)
 
  return nodeEnvValues.includes(resolvedValue as NodeEnv)
    ? (resolvedValue as NodeEnv)
    : "development"
}
 
function isLoopbackHostname(hostname: string): boolean {
  return (
    hostname === "localhost" ||
    hostname === "127.0.0.1" ||
    hostname === "::1" ||
    hostname === "[::1]"
  )
}