{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "provider-google-tanstack-valibot",
  "title": "Google Provider (TanStack Form + Valibot)",
  "description": "A self-contained form component that emits an OidcPolicy for a Google service account caller: account email, optional rename-proof unique ID, and audience. Built with TanStack Form and validated with Valibot.",
  "dependencies": [
    "@tanstack/react-form",
    "valibot"
  ],
  "registryDependencies": [
    "https://ui.shadcn.com/oidc/r/policy.json",
    "https://ui.shadcn.com/oidc/r/claims.json",
    "button",
    "field",
    "input"
  ],
  "files": [
    {
      "path": "registry/oidc/flavors/tanstack-valibot/provider-google.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { useForm } from \"@tanstack/react-form\"\nimport * as v from \"valibot\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Field,\n  FieldDescription,\n  FieldError,\n  FieldLabel,\n} from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  ClaimChip,\n  ClaimsList,\n  claimRowsFromPolicy,\n  claimsListValid,\n  mergeClaimRows,\n  type ClaimRow,\n} from \"@/components/oidc/claims\"\nimport type { OidcPolicy } from \"@/lib/oidc/policy\"\n\nfunction GoogleIcon({ ...props }: React.ComponentProps<\"svg\">) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" {...props}>\n      <path d=\"M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z\" />\n    </svg>\n  )\n}\n\nconst emailSchema = v.pipe(\n  v.string(),\n  v.minLength(1, \"Enter the service account email.\"),\n  v.regex(/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/, \"Enter a valid email address.\")\n)\nconst uniqueIdSchema = v.union([\n  v.literal(\"\"),\n  v.pipe(v.string(), v.regex(/^\\d+$/, \"The numeric ID only.\")),\n])\nconst audienceSchema = v.pipe(v.string(), v.minLength(1, \"Enter the audience.\"))\n\ntype Fields = {\n  email: v.InferOutput<typeof emailSchema>\n  uniqueId: v.InferOutput<typeof uniqueIdSchema>\n  audience: v.InferOutput<typeof audienceSchema>\n  extraClaims: ClaimRow[]\n}\n\nconst DEFAULTS: Fields = {\n  email: \"\",\n  uniqueId: \"\",\n  audience: \"\",\n  extraClaims: [],\n}\n\nconst FIRST_CLASS_CLAIMS = [\"aud\", \"email\", \"sub\"]\n\nconst ISSUER = \"https://accounts.google.com\"\n\n// Identity is the service account email; the numeric unique ID (sub)\n// survives account deletion and recreation, so pin it when known.\nfunction compile(fields: Fields) {\n  const claims: Record<string, string[]> = {}\n  if (fields.audience) {\n    claims.aud = [fields.audience]\n  }\n  if (fields.email) {\n    claims.email = [fields.email]\n  }\n  if (fields.uniqueId) {\n    claims.sub = [fields.uniqueId]\n  }\n  mergeClaimRows(claims, fields.extraClaims, FIRST_CLASS_CLAIMS)\n  return { issuer: ISSUER, claims }\n}\n\nfunction parse(policy: OidcPolicy) {\n  if (policy.issuer !== ISSUER) {\n    return null\n  }\n  if (\n    FIRST_CLASS_CLAIMS.some((name) => (policy.claims[name]?.length ?? 0) > 1)\n  ) {\n    return null\n  }\n  const extraClaims = claimRowsFromPolicy(policy.claims, FIRST_CLASS_CLAIMS)\n  if (extraClaims === null) {\n    return null\n  }\n\n  return {\n    email: policy.claims.email?.[0] ?? \"\",\n    uniqueId: policy.claims.sub?.[0] ?? \"\",\n    audience: policy.claims.aud?.[0] ?? \"\",\n    extraClaims,\n  }\n}\n\n/** True when this form can represent the policy — for routing an edit. */\nfunction matchesGooglePolicy(policy: OidcPolicy) {\n  return parse(policy) !== null\n}\n\nfunction GooglePolicyForm({\n  defaultValue,\n  onChange,\n  onSubmit,\n  className,\n}: {\n  defaultValue?: OidcPolicy\n  onChange?: (policy: OidcPolicy) => void\n  onSubmit: (policy: OidcPolicy) => void\n  className?: string\n}) {\n  const form = useForm({\n    defaultValues: (defaultValue\n      ? (parse(defaultValue) ?? DEFAULTS)\n      : DEFAULTS) as Fields,\n    onSubmit: ({ value: fields }) => {\n      if (!claimsListValid(fields.extraClaims)) {\n        return\n      }\n      const policy = compile(fields)\n      onSubmit(\n        defaultValue?.label ? { ...policy, label: defaultValue.label } : policy\n      )\n    },\n  })\n\n  const onChangeRef = React.useRef(onChange)\n  React.useEffect(() => {\n    onChangeRef.current = onChange\n  }, [onChange])\n\n  React.useEffect(() => {\n    let previousValues = form.store.state.values\n    const subscription = form.store.subscribe(() => {\n      const { values } = form.store.state\n      if (values === previousValues) {\n        return\n      }\n      previousValues = values\n      const policy = compile(values)\n      onChangeRef.current?.(\n        defaultValue?.label ? { ...policy, label: defaultValue.label } : policy\n      )\n    })\n    return () => subscription.unsubscribe()\n  }, [form, defaultValue?.label])\n\n  return (\n    <form\n      data-slot=\"google-policy-form\"\n      className={cn(\"flex flex-col gap-4\", className)}\n      onSubmit={(event) => {\n        event.preventDefault()\n        event.stopPropagation()\n        void form.handleSubmit()\n      }}\n    >\n      <Field>\n        <FieldLabel htmlFor=\"gc-issuer\" className=\"w-full\">\n          Issuer <ClaimChip>iss</ClaimChip>\n        </FieldLabel>\n        <Input id=\"gc-issuer\" value={ISSUER} readOnly disabled />\n        <FieldDescription>Fixed for Google service accounts.</FieldDescription>\n      </Field>\n      <form.Field name=\"email\" validators={{ onChange: emailSchema }}>\n        {(field) => {\n          const isInvalid =\n            field.state.meta.isTouched && !field.state.meta.isValid\n          return (\n            <Field data-invalid={isInvalid}>\n              <FieldLabel htmlFor=\"gc-email\" className=\"w-full\">\n                Service account email <ClaimChip>email</ClaimChip>\n              </FieldLabel>\n              <Input\n                id=\"gc-email\"\n                value={field.state.value}\n                placeholder=\"deploy-bot@acme-prod.iam.gserviceaccount.com\"\n                aria-invalid={isInvalid}\n                onBlur={field.handleBlur}\n                onChange={(event) =>\n                  field.handleChange(event.target.value.trim())\n                }\n              />\n              {isInvalid && <FieldError errors={field.state.meta.errors} />}\n            </Field>\n          )\n        }}\n      </form.Field>\n      <form.Field name=\"uniqueId\" validators={{ onChange: uniqueIdSchema }}>\n        {(field) => {\n          const isInvalid =\n            field.state.meta.isTouched && !field.state.meta.isValid\n          return (\n            <Field data-invalid={isInvalid}>\n              <FieldLabel htmlFor=\"gc-uniqueId\" className=\"w-full\">\n                Unique ID <ClaimChip>sub</ClaimChip>\n              </FieldLabel>\n              <Input\n                id=\"gc-uniqueId\"\n                value={field.state.value}\n                placeholder=\"117234567890123456789\"\n                aria-invalid={isInvalid}\n                onBlur={field.handleBlur}\n                onChange={(event) =>\n                  field.handleChange(event.target.value.trim())\n                }\n              />\n              {isInvalid && <FieldError errors={field.state.meta.errors} />}\n              <FieldDescription>\n                Optional: IAM → Service Accounts → Unique ID. Survives account\n                recreation.\n              </FieldDescription>\n            </Field>\n          )\n        }}\n      </form.Field>\n      <form.Field name=\"audience\" validators={{ onChange: audienceSchema }}>\n        {(field) => {\n          const isInvalid =\n            field.state.meta.isTouched && !field.state.meta.isValid\n          return (\n            <Field data-invalid={isInvalid}>\n              <FieldLabel htmlFor=\"gc-audience\" className=\"w-full\">\n                Audience <ClaimChip>aud</ClaimChip>\n              </FieldLabel>\n              <Input\n                id=\"gc-audience\"\n                value={field.state.value}\n                placeholder=\"https://your-app.example.com\"\n                aria-invalid={isInvalid}\n                onBlur={field.handleBlur}\n                onChange={(event) =>\n                  field.handleChange(event.target.value.trim())\n                }\n              />\n              {isInvalid && <FieldError errors={field.state.meta.errors} />}\n              <FieldDescription>\n                The audience the caller requests its token for.\n              </FieldDescription>\n            </Field>\n          )\n        }}\n      </form.Field>\n      <form.Field name=\"extraClaims\">\n        {(field) => (\n          <ClaimsList\n            value={field.state.value}\n            onChange={field.handleChange}\n            reserved={FIRST_CLASS_CLAIMS}\n          />\n        )}\n      </form.Field>\n      <Button type=\"submit\" className=\"self-start\">\n        Save\n      </Button>\n    </form>\n  )\n}\n\nexport { GoogleIcon, GooglePolicyForm, matchesGooglePolicy }\n",
      "type": "registry:component",
      "target": "@components/oidc/provider-google.tsx"
    }
  ],
  "meta": {
    "tagline": "Trust a service account"
  },
  "categories": [
    "auth",
    "forms"
  ],
  "type": "registry:component"
}