diff --git a/lua/PandocCLI/Lua.hs b/lua/PandocCLI/Lua.hs
--- a/lua/PandocCLI/Lua.hs
+++ b/lua/PandocCLI/Lua.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE OverloadedStrings #-}
 {- |
    Module      : PandocCLI.Lua
@@ -9,8 +10,8 @@
 -}
 module PandocCLI.Lua (runLuaInterpreter, getEngine) where
 
+#ifdef REPL
 import Control.Monad ((<=<))
-import HsLua.CLI (EnvBehavior (..), Settings (..), runStandalone)
 import System.Environment (lookupEnv)
 import System.IO.Temp (withSystemTempFile)
 import System.IO (hClose)
@@ -18,6 +19,12 @@
 import Text.Pandoc.Error (handleError)
 import Text.Pandoc.Lua (runLua, runLuaNoEnv, getEngine)
 import Text.Pandoc.Version (pandocVersionText)
+import HsLua.CLI (EnvBehavior (..), Settings (..), runStandalone)
+#else
+import Text.Pandoc.Lua (getEngine)
+import System.IO (stderr, hPutStrLn)
+import System.Exit (exitWith, ExitCode(..))
+#endif
 
 -- | Runs pandoc as a Lua interpreter that is (mostly) compatible with
 -- the default @lua@ program shipping with Lua.
@@ -28,6 +35,7 @@
 runLuaInterpreter :: String    -- ^ Program name
                   -> [String]  -- ^ Command line arguments
                   -> IO ()
+#ifdef REPL
 runLuaInterpreter progName args = do
   -- We need some kind of temp
   mbhistfile <- lookupEnv "PANDOC_REPL_HISTORY"
@@ -54,3 +62,8 @@
                       IgnoreEnvVars  -> runLuaNoEnv
                       ConsultEnvVars -> runLua
       in handleError <=< runIOorExplode . runLua'
+#else
+runLuaInterpreter _ _ = do
+  hPutStrLn stderr "Pandoc not compiled with Lua interpreter support."
+  exitWith $ ExitFailure 4
+#endif
diff --git a/man/pandoc-lua.1 b/man/pandoc-lua.1
--- a/man/pandoc-lua.1
+++ b/man/pandoc-lua.1
@@ -1,6 +1,6 @@
-.\" Automatically generated by Pandoc 3.8.3
+.\" Automatically generated by Pandoc 3.9
 .\"
-.TH "pandoc-lua" "1" "September 22, 2022" "pandoc 3.8.3" "Pandoc User\[cq]s Guide"
+.TH "pandoc-lua" "1" "September 22, 2022" "pandoc 3.9" "Pandoc User\[cq]s Guide"
 .SH SYNOPSIS
 \f[CR]pandoc\-lua\f[R] [\f[I]options\f[R]] [\f[I]script\f[R]
 [\f[I]args\f[R]]]
diff --git a/man/pandoc-server.1 b/man/pandoc-server.1
--- a/man/pandoc-server.1
+++ b/man/pandoc-server.1
@@ -1,6 +1,6 @@
-.\" Automatically generated by Pandoc 3.8.3
+.\" Automatically generated by Pandoc 3.9
 .\"
-.TH "pandoc-server" "1" "August 15, 2022" "pandoc 3.8.3" "Pandoc User\[cq]s Guide"
+.TH "pandoc-server" "1" "August 15, 2022" "pandoc 3.9" "Pandoc User\[cq]s Guide"
 .SH SYNOPSIS
 \f[CR]pandoc\-server\f[R] [\f[I]options\f[R]]
 .SH DESCRIPTION
diff --git a/man/pandoc.1 b/man/pandoc.1
--- a/man/pandoc.1
+++ b/man/pandoc.1
@@ -1,6 +1,6 @@
-.\" Automatically generated by Pandoc 3.8.3
+.\" Automatically generated by Pandoc 3.9
 .\"
-.TH "pandoc" "1" "2025\-12\-01" "pandoc 3.8.3" "Pandoc User\[cq]s Guide"
+.TH "pandoc" "1" "2025\-02\-02" "pandoc 3.9" "Pandoc User\[cq]s Guide"
 .SH NAME
 pandoc - general markup converter
 .SH SYNOPSIS
@@ -337,7 +337,7 @@
 .IP \(bu 2
 \f[CR]bbcode_phpbb\f[R] BBCode (phpBB)
 .IP \(bu 2
-\f[CR]bbcode_steam\f[R] BBCode (Hubzilla)
+\f[CR]bbcode_steam\f[R] BBCode (Steam)
 .IP \(bu 2
 \f[CR]bbcode_hubzilla\f[R] BBCode (Hubzilla)
 .IP \(bu 2
@@ -515,14 +515,15 @@
 .TP
 \f[CR]\-d\f[R] \f[I]FILE\f[R], \f[CR]\-\-defaults=\f[R]\f[I]FILE\f[R]
 Specify a set of default option settings.
-\f[I]FILE\f[R] is a YAML file whose fields correspond to command\-line
-option settings.
+\f[I]FILE\f[R] is a YAML or JSON file whose fields correspond to
+command\-line option settings.
 All options for document conversion, including input and output files,
 can be set using a defaults file.
 The file will be searched for first in the working directory, and then
 in the \f[CR]defaults\f[R] subdirectory of the user data directory (see
 \f[CR]\-\-data\-dir\f[R]).
-The \f[CR].yaml\f[R] extension may be omitted.
+The \f[CR].yaml\f[R] extension will be added if \f[I]FILE\f[R] lacs an
+extension.
 See the section Defaults files for more information on the file format.
 Settings from the defaults file may be overridden or extended by
 subsequent options on the command line.
@@ -761,7 +762,7 @@
 the affected paragraph break.
 This option only affects the docx reader.
 .TP
-\f[CR]\-\-extract\-media=\f[R]\f[I]DIR\f[R]
+\f[CR]\-\-extract\-media=\f[R]\f[I]DIR\f[R]|\f[I]FILE\f[R]\f[CR].zip\f[R]
 Extract images and other media contained in or linked from the source
 document to the path \f[I]DIR\f[R], creating it if necessary, and adjust
 the images references in the document so they point to the extracted
@@ -771,6 +772,11 @@
 The original file paths are used if they are relative paths not
 containing \f[CR]..\f[R].
 Otherwise filenames are constructed from the SHA1 hash of the contents.
+.RS
+.PP
+If the path given ends in \f[CR].zip\f[R], then instead of creating a
+directory, pandoc will create a zip archive containing the media files.
+.RE
 .TP
 \f[CR]\-\-abbreviations=\f[R]\f[I]FILE\f[R]
 Specifies a custom abbreviations file, with abbreviations one to a line.
@@ -969,28 +975,21 @@
 This does not apply to HTML comments inside raw HTML blocks when the
 \f[CR]markdown_in_html_blocks\f[R] extension is not set.
 .TP
-\f[CR]\-\-syntax\-highlighting=\(dqdefault\(dq|\(dqnone\(dq|\(dqidiomatic\(dq|\f[R]\f[I]STYLE\f[R]\f[CR]|\f[R]\f[I]FILE\f[R]
+\f[CR]\-\-syntax\-highlighting=default|none|idiomatic|\f[R]\f[I]STYLE\f[R]\f[CR]|\f[R]\f[I]FILE\f[R]
 The method to use for code syntax highlighting.
 Setting a specific \f[I]STYLE\f[R] causes highlighting to be performed
 with the internal highlighting engine, using KDE syntax definitions and
 styles.
-The \f[CR]\(dqidiomatic\(dq\f[R] method uses a format\-specific
-highlighter if one is available, or the default style if the target
-format has no idiomatic highlighting method.
+The \f[CR]idiomatic\f[R] method uses a format\-specific highlighter if
+one is available, or the default style if the target format has no
+idiomatic highlighting method.
 Setting this option to \f[CR]none\f[R] disables all syntax highlighting.
-The \f[CR]\(dqdefault\(dq\f[R] method uses a format\-specific default.
+The \f[CR]default\f[R] method uses a format\-specific default.
 .RS
 .PP
 The default for HTML, EPUB, Docx, Ms, Man, and LaTeX output is to use
-the internal highlighter with the default style; Typst output relies on
-Typst\(cqs own syntax highlighting system by default.
-.PP
-The \f[CR]listings\f[R] LaTeX package is used for idiomatic highlighting
-in LaTeX.
-The package does not support multi\-byte encoding for source code.
-To handle UTF\-8 you would need to use a custom template.
-This issue is fully documented here: Encoding issue with the listings
-package.
+the internal highlighter with the default style; for Typst it is to use
+Typst\(cqs own syntax highlighting system.
 .PP
 Style options are \f[CR]pygments\f[R] (the default), \f[CR]kate\f[R],
 \f[CR]monochrome\f[R], \f[CR]breezeDark\f[R], \f[CR]espresso\f[R],
@@ -1515,7 +1514,7 @@
 (padded with leading 0s to 3 digits), \f[CR]%s\f[R] with the section
 number of the chunk, \f[CR]%h\f[R] with the heading text (with
 formatting removed), \f[CR]%i\f[R] with the section identifier.
-For example, \f[CR]%section\-%s\-%i.html\f[R] might be resolved to
+For example, \f[CR]section\-%s\-%i.html\f[R] might be resolved to
 \f[CR]section\-1.1\-introduction.html\f[R].
 The characters \f[CR]/\f[R] and \f[CR]\(rs\f[R] are not allowed in chunk
 templates and will be ignored.
@@ -1867,7 +1866,9 @@
 .RE
 .SH DEFAULTS FILES
 The \f[CR]\-\-defaults\f[R] option may be used to specify a package of
-options, in the form of a YAML file.
+options, in the form of a YAML or JSON file.
+Examples in this section will be given in YAML, but the equivalent forms
+in JSON will also work.
 .PP
 Fields that are omitted will just have their regular default values.
 So a defaults file can be as simple as one line:
@@ -1876,6 +1877,12 @@
 verbosity\f[B]:\f[R] INFO
 .EE
 .PP
+or in JSON:
+.IP
+.EX
+{ \(dqverbosity\(dq: \(dqINFO\(dq }
+.EE
+.PP
 In fields that expect a file path (or list of file paths), the following
 syntax may be used to interpolate environment variables:
 .IP
@@ -2058,7 +2065,7 @@
 
  \-\-dpi 300                           dpi: 300                          
 
- \-\-wrap 60                           wrap: 60                          
+ \-\-wrap preserve                     wrap: \(dqpreserve\(dq                  
 
  \-\-columns 72                        columns: 72                       
 
@@ -2907,6 +2914,12 @@
 \f[CR]title\-slide\-attributes\f[R]
 additional attributes for the title slide of reveal.js slide shows.
 See background in reveal.js, beamer, and pptx for an example.
+.TP
+\f[CR]highlightjs\-theme\f[R]
+highlight.js theme for code highlighting when using
+\f[CR]\-\-syntax\-highlighting=idiomatic\f[R] with reveal.js (defaults
+to \f[CR]monokai\f[R]).
+See the highlight.js demo page for available themes.
 .PP
 All reveal.js configuration options are available as variables.
 To turn off boolean flags that default to true in reveal.js, use
@@ -3257,6 +3270,23 @@
 .RS
 See the section on reproducible builds.
 .RE
+.TP
+\f[CR]pdfstandard\f[R]
+PDF standard(s) for the document, e.g.\ \f[CR]ua\-2\f[R],
+\f[CR]a\-4f\f[R].
+Supports PDF/A, PDF/X, and PDF/UA variants.
+Requires LuaLaTeX and LaTeX 2023+.
+Repeat for multiple standards:
+.RS
+.IP
+.EX
+\-\-\-
+pdfstandard:
+\- ua\-2
+\- a\-4f
+\&...
+.EE
+.RE
 .SS Variables for ConTeXt
 Pandoc uses these variables when creating a PDF with ConTeXt.
 .TP
@@ -6174,6 +6204,20 @@
 citations will not.
 For this reason, it is sometimes preferable to use the author\-in\-text
 style inside notes when using a note style.
+.PP
+Many CSL styles will format citations differently when the same source
+has been cited earlier.
+In documents with chapters, it is usually desirable to reset this
+position information at the beginning of every chapter.
+To do this, add the class \f[CR]reset\-citation\-positions\f[R] to the
+heading for each chapter:
+.IP
+.EX
+# The Beginning {.reset\-citation\-positions}
+.EE
+.PP
+Note that this class only has an effect when placed on top\-level
+headings; it is ignored in nested blocks.
 .SS Non\-default extensions
 The following Markdown syntax extensions are not enabled by default in
 pandoc, but may be enabled by adding \f[CR]+EXTENSION\f[R] to the format
@@ -6355,9 +6399,6 @@
 > [!TIP]
 > Helpful advice for doing things better or more easily.
 .EE
-.PP
-Note: This extension currently only works with commonmark:
-\f[CR]commonmark\f[R], \f[CR]gfm\f[R], \f[CR]commonmark_x\f[R].
 .SS Extension: \f[CR]autolink_bare_uris\f[R]
 Makes all absolute URIs into links, even when not surrounded by pointy
 braces \f[CR]<...>\f[R].
@@ -7150,6 +7191,25 @@
 .PP
 Notes are not yet supported for other slide formats, but the notes will
 not appear on the slides themselves.
+.SS Speaker notes on the title slide (PowerPoint)
+For PowerPoint output, the title slide is generated from the
+document\(cqs YAML metadata block.
+To add speaker notes to this slide, use a \f[CR]notes\f[R] field in the
+metadata:
+.IP
+.EX
+\-\-\-
+title: My Presentation
+author: Jane Doe
+notes: |
+  Welcome everyone to this presentation.
+
+  Remember to introduce yourself and mention the key topics.
+\-\-\-
+.EE
+.PP
+The \f[CR]notes\f[R] field can contain multiple paragraphs and Markdown
+formatting.
 .SS Columns
 To put material in side by side columns, you can use a native div
 container with class \f[CR]columns\f[R], containing two or more div
@@ -7858,6 +7918,29 @@
 Byte\-Order Mark (BOM).
 .PP
 To disable highlighting, use \f[CR]\-\-syntax\-highlighting=none\f[R].
+.PP
+To use a format\(cqs idiomatic syntax highlighting instead of
+pandoc\(cqs built\-in highlighting, use
+\f[CR]\-\-syntax\-highlighting=idiomatic\f[R].
+Currently, \f[CR]idiomatic\f[R] only affects the following formats:
+.IP \(bu 2
+In reveal.js, it causes reveal.js\(cqs highlighting plugin to be used
+for source code highlighting.
+The style may be customized by setting the \f[CR]highlightjs\-theme\f[R]
+variable.
+.IP \(bu 2
+In Typst, it causes Typst\(cqs built\-in highlighting to be used.
+(This is also the default for Typst.)
+.IP \(bu 2
+In LaTeX, it causes the \f[CR]listings\f[R] package to be used.
+Note that \f[CR]listings\f[R] does not support multi\-byte encoding for
+source code.
+To handle UTF\-8 you would need to use a custom template.
+This issue is fully documented here: Encoding issue with the listings
+package.
+.IP \(bu 2
+In other formats, \f[CR]idiomatic\f[R] will have the same result as
+\f[CR]default\f[R].
 .SH CUSTOM STYLES
 Custom styles can be used in the docx, odt and ICML formats.
 .SS Output
@@ -8036,10 +8119,10 @@
 to add semantic information to the document.
 .PP
 Pandoc defaults to LaTeX to generate PDF.
-Tagging support in LaTeX is in development and not readily available, so
-PDFs generated in this way will always be untagged and not accessible.
-This means that alternative engines must be used to generate accessible
-PDFs.
+LaTeX\(cqs \f[CR]\(rsDocumentMetadata\f[R] interface supports PDF
+standards and tagging when using LuaLaTeX; set the
+\f[CR]pdfstandard\f[R] variable to enable this (see below).
+For older LaTeX installations, alternative engines must be used.
 .PP
 The PDF standards PDF/A and PDF/UA define further restrictions intended
 to optimize PDFs for archiving and accessibility.
@@ -8050,6 +8133,28 @@
 including the colorspace of embedded images.
 Pandoc cannot check this, and external programs must be used to ensure
 that generated PDFs are in compliance.
+.SS LaTeX
+Set the \f[CR]pdfstandard\f[R] variable to produce tagged PDFs
+conforming to PDF/A, PDF/X, or PDF/UA standards.
+For example:
+.IP
+.EX
+pandoc \-V pdfstandard=ua\-2 \-\-pdf\-engine=lualatex doc.md \-o doc.pdf
+.EE
+.PP
+Multiple standards can be combined:
+.IP
+.EX
+\-\-\-
+pdfstandard:
+  \- ua\-2
+  \- a\-4f
+\-\-\-
+.EE
+.PP
+The required PDF version is inferred automatically.
+This feature requires LuaLaTeX in TeX Live 2025 with LaTeX kernel
+2025\-06\-01 or newer.
 .SS ConTeXt
 ConTeXt always produces tagged PDFs, but the quality depends on the
 input.
diff --git a/pandoc-cli.cabal b/pandoc-cli.cabal
--- a/pandoc-cli.cabal
+++ b/pandoc-cli.cabal
@@ -1,6 +1,6 @@
 cabal-version:   2.4
 name:            pandoc-cli
-version:         3.8.3
+version:         3.9
 build-type:      Simple
 license:         GPL-2.0-or-later
 license-file:    COPYING.md
@@ -14,7 +14,6 @@
 synopsis:        Conversion between documentation formats
 description:     Pandoc-cli provides a command-line executable that uses the
                  pandoc library to convert between markup formats.
--- data-files:
 extra-source-files:
                  man/pandoc.1
                  man/pandoc-lua.1
@@ -32,6 +31,10 @@
   Description:   Include support for running pandoc as an HTTP server.
   Default:       True
 
+flag repl
+  Description:   Include support for running a pandoc Lua repl.
+  Default:       True
+
 flag nightly
   Description:   Add '-nightly-COMPILEDATE' to the output of '--version'.
   Default:       False
@@ -61,19 +64,26 @@
 
 common common-executable
   import:           common-options
-  ghc-options:      -rtsopts -with-rtsopts=-A8m -threaded
+  ghc-options:      -rtsopts -with-rtsopts=-H64m
 
 executable pandoc
   import:          common-executable
-  hs-source-dirs:  src
   main-is:         pandoc.hs
+  hs-source-dirs:  src
   buildable:       True
   -- Note: we always link to an exact version of pandoc, with the
   -- same version as this package:
-  build-depends:   pandoc == 3.8.3,
-                   text
+  build-depends:   pandoc == 3.9, text
   other-modules:   PandocCLI.Lua
                  , PandocCLI.Server
+
+  if arch(wasm32)
+    hs-source-dirs: wasm
+    other-modules:  PandocWasm
+    cpp-options:    -DINCLUDE_WASM
+    build-depends:  aeson, containers, bytestring, skylighting, filepath, pandoc-lua-engine
+    ghc-options:    -optl-Wl,--export=__wasm_call_ctors,--export=hs_init_with_rtsopts,--export=malloc,--export=convert,--export=query
+
   if flag(nightly)
     cpp-options:    -DNIGHTLY
     build-depends:  template-haskell,
@@ -88,9 +98,12 @@
     hs-source-dirs:  no-server
 
   if flag(lua)
-    build-depends:   hslua-cli         >= 1.4.1 && < 1.5,
-                     pandoc-lua-engine >= 0.5 && < 0.6,
-                     temporary         >= 1.1 && < 1.4
+    build-depends:   pandoc-lua-engine >= 0.5.1 && < 0.6
     hs-source-dirs:  lua
   else
     hs-source-dirs:  no-lua
+
+  if flag(repl)
+     build-depends:    hslua-cli       >= 1.4.1   && < 1.5,
+                       temporary         >= 1.1 && < 1.4
+     cpp-options:      -DREPL
diff --git a/src/pandoc.hs b/src/pandoc.hs
--- a/src/pandoc.hs
+++ b/src/pandoc.hs
@@ -1,5 +1,7 @@
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell #-}
+
 {- |
    Module      : Main
    Copyright   : Copyright (C) 2006-2024 John MacFarlane
@@ -23,10 +25,12 @@
 import PandocCLI.Server
 import Text.Pandoc.Scripting (ScriptingEngine(..))
 import qualified Data.Text as T
-
 #ifdef NIGHTLY
 import qualified Language.Haskell.TH as TH
 import Data.Time
+#endif
+#ifdef INCLUDE_WASM
+import PandocWasm()
 #endif
 
 #ifdef NIGHTLY
diff --git a/wasm/PandocWasm.hs b/wasm/PandocWasm.hs
new file mode 100644
--- /dev/null
+++ b/wasm/PandocWasm.hs
@@ -0,0 +1,171 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+{- |
+   Module      : Main
+   Copyright   : Copyright (C) 2006-2024 John MacFarlane
+   License     : GNU GPL, version 2 or above
+
+   Maintainer  : John MacFarlane <jgm@berkeley@edu>
+   Stability   : alpha
+   Portability : portable
+
+Exposes wasm functions to convert documents and get information
+from pandoc.
+-}
+module PandocWasm where
+import qualified Data.Map as M
+import qualified Control.Exception as E
+import Data.List (sort)
+import Data.Maybe (fromMaybe)
+import Text.Pandoc.App ( convertWithOpts, Opt(..), defaultOpts )
+import Text.Pandoc (Verbosity(ERROR), pandocVersion, Reader, Writer, PandocIO,
+                    readers, writers, runIO, setUserDataDir)
+import Text.Pandoc.Highlighting (highlightingStyles)
+import Text.Pandoc.Templates (getDefaultTemplate)
+import Skylighting (defaultSyntaxMap, Syntax(..))
+import Text.Pandoc.Extensions (extensionsToList, extensionEnabled, getAllExtensions,
+                               getDefaultExtensions)
+import Text.Pandoc.Error
+import Text.Pandoc.Scripting (ScriptingEngine(..), customTemplate)
+import System.FilePath (splitExtension)
+import PandocCLI.Lua
+import Control.Exception
+import Foreign
+import Foreign.C
+import Data.Aeson as Aeson
+import qualified Text.Pandoc.UTF8 as UTF8
+import qualified Data.ByteString.Lazy as BL
+import qualified Data.Text as T
+import Data.Version (showVersion)
+
+foreign export ccall "convert" convert :: Ptr CChar -> Int -> IO ()
+
+-- | Do a pandoc conversion. The parameters are a pointer and length
+-- for a C string containing JSON-encoded pandoc options (isomorphic
+-- to a defaults file). The calling program should set up a virtual
+-- file system containing @/stdin@ (input), @/stdout@ (output), and
+-- @/warnings@ (output). @/stdin@ can be used to pass input to pandoc.
+convert :: Ptr CChar -> Int -> IO ()
+convert ptr len =
+  E.catch act (\(err :: SomeException) ->
+                 writeFile "/stderr" ("ERROR: " <> displayException err))
+  where
+    act = do
+      args <- getCString ptr len
+      engine <- getEngine
+      let aesonRes = Aeson.eitherDecode (UTF8.fromStringLazy args)
+      case aesonRes of
+        Left e -> error e
+        Right (f :: Opt -> Opt) -> do
+          let opts = f defaultOpts
+          let opts' = opts{ optInputFiles =
+                             Just $ fromMaybe ["/stdin"] (optInputFiles opts)
+                          , optOutputFile =
+                             Just $ fromMaybe "/stdout" (optOutputFile opts)
+                          , optLogFile =
+                             Just $ fromMaybe "/warnings" (optLogFile opts)
+                          , optVerbosity = ERROR -- only show errors to stderr
+                          }
+          convertWithOpts engine opts'
+
+foreign export ccall "query" query :: Ptr CChar -> Int -> IO ()
+
+-- | Retrieve information from pandoc. The parameters are a pointer
+-- and length for a C string containing a JSON-encoded query.
+-- The calling program should set up a virtual file system containing
+-- @/stdout@, which will be used to hold the output, and @/stderr@,
+-- which will be used to hold any error messages.
+-- The following queries can be used:
+-- * @{"query":"version"}@ returns the pandoc version as a JSON-encoded
+--   string.
+-- * @{"query:"input-formats"}@ returns a JSON list of supported input
+--   formats.
+-- * @{"query:"output-formats"}@ returns a JSON list of supported output
+--   formats.
+-- * @{"query:"highlight-languages"}@ returns a JSON list of languages
+--   for which syntax highlighting is defined.
+-- * @{"query:"highlight-styles"}@ returns a JSON list of defined syntax
+--   highlighting styles.
+-- * @{"query":"default-template","format": FORMAT}@ returns
+--   the default template for the format as a JSON-encoded string.
+-- * @{"query":"extensions-for-format","format": FORMAT}@ returns
+--   an object mapping extension names to true or false for all the
+--   extensions relevant for the format.
+query :: Ptr CChar -> Int -> IO ()
+query ptr len =
+  E.catch act (\(err :: SomeException) ->
+                 writeFile "/stderr" ("ERROR: " <> displayException err))
+  where
+    jsonOut :: forall a . ToJSON a => a -> IO ()
+    jsonOut = BL.writeFile "/stdout" . Aeson.encode
+    act = do
+      args <- getCString ptr len
+      case Aeson.eitherDecode (UTF8.fromStringLazy args) of
+        Left e -> error e
+        Right PandocVersion -> jsonOut $ showVersion pandocVersion
+        Right HighlightStyles -> jsonOut $ map fst highlightingStyles
+        Right HighlightLanguages -> jsonOut $ sort $
+          [ T.toLower (sShortname s)
+            | s <- M.elems defaultSyntaxMap
+            , sShortname s `notElem` ["Alert", "Alert_indent"]
+          ]
+        Right (DefaultTemplate format) -> do
+          templ <- runIO $
+                     case splitExtension (T.unpack format) of
+                        (_, "") -> do
+                          -- built-in format
+                          setUserDataDir Nothing
+                          getDefaultTemplate format
+                        _ -> do
+                          -- format looks like a filepath => custom writer
+                          engine <- getEngine
+                          components <- engineLoadCustom engine (T.unpack format)
+                          case customTemplate components of
+                            Just t  -> pure t
+                            Nothing -> E.throw $ PandocNoTemplateError format
+          case templ of
+               Right t
+                 | T.null t -> -- e.g. for docx, odt, json:
+                     E.throwIO $ PandocCouldNotFindDataFileError $ T.pack
+                       ("templates/default." ++ T.unpack format)
+                 | otherwise -> jsonOut t
+               Left e  -> E.throwIO e
+        Right InputFormats -> jsonOut readersNames
+        Right OutputFormats -> jsonOut writersNames
+        Right (ExtensionsForFormat format) -> do
+          let allExts = getAllExtensions format
+          let defExts = getDefaultExtensions format
+          let addExt x = M.insert (drop 4 (show x))
+                              (extensionEnabled x defExts)
+          jsonOut $ foldr addExt mempty (extensionsToList allExts)
+    readersNames = sort (map fst (readers :: [(T.Text, Reader PandocIO)]))
+    writersNames = sort
+      ("pdf" : map fst (writers :: [(T.Text, Writer PandocIO)]))
+
+
+data Query =
+    PandocVersion
+  | InputFormats
+  | OutputFormats
+  | HighlightLanguages
+  | HighlightStyles
+  | ExtensionsForFormat T.Text
+  | DefaultTemplate T.Text
+  deriving (Show)
+
+instance FromJSON Query where
+  parseJSON = withObject "Query" $ \o -> do
+    queryType <- o .: "query"
+    case queryType of
+      "version" -> pure PandocVersion
+      "input-formats" -> pure InputFormats
+      "output-formats" -> pure OutputFormats
+      "highlight-languages" -> pure HighlightLanguages
+      "highlight-styles" -> pure HighlightStyles
+      "default-template" -> DefaultTemplate <$> o .: "format"
+      "extensions-for-format" -> ExtensionsForFormat <$> o .: "format"
+      _ -> fail $ "Unknown query type " <> queryType
+
+getCString :: Ptr CChar -> Int -> IO String
+getCString ptr len = peekCStringLen (ptr, len) <* free ptr
