PhoenixKit.Install.ViewerLibraries (phoenix_kit v2.60.2)

Copy Markdown View Source

Serves the viewer/editor libraries from the host's own origin.

phoenix_kit.js used to lazy-load Fresco, Tessera, Etcher, Leaf, SortableJS, Panzoom and wavesurfer from cdn.jsdelivr.net at runtime. A host whose Content-Security-Policy is script-src 'self' silently lost the photo viewer's zoom, its sharper-version swap, annotations, the editor and the sortable lists; every viewer open made third-party requests; and the CDN tags had to be kept in step with the Hex versions by hand (they drifted twice). See dev_docs/plans/2026-10-07-self-hosted-viewer-libraries.md, Parts B1 and B2.

vendor!/1 copies each library into the host's priv/static/assets/vendor/lib/ as <name>-<version>-<hash>.js:

  • The Hex libraries come from the consumer's installed dependency (:code.priv_dir/1), versioned by the consumer's loaded application version — not core's lockfile, which can resolve a different minor than the host does.
  • SortableJS, Panzoom and wavesurfer (npm-only) ship inside core, each at an exact version with its checksum, licence and provenance README (priv/static/assets/vendor_libs/).
  • The content hash is in the name because a path dependency can change its bytes without changing its version; with it, each name is cacheable for good.
  • Files are written only when absent and never deleted, so a rolling deploy or an open tab can still fetch the previous build's names — as far as the deployment itself keeps them (a clean release image does not; that retention is the deployment's, not the compiler's).

It returns the facts the browser needs — window.PHOENIX_KIT_LIBS, written by the :phoenix_kit_js_sources compiler next to the module bundles — so phoenix_kit.js names no CDN, no tag and no version of its own. A CDN is used only when the host opts in (config :phoenix_kit, library_cdn_fallback: true), as a fallback after a failed local load, with a URL built from that same consumer version.

Summary

Functions

The JavaScript statement carrying the facts, for the install-facts preamble.

Where the library files land, relative to the host's root.

The manifest: every library this module vendors.

Copies every library into root's priv/static/assets/vendor/lib and returns the facts map (name => %{file, cdn}). Raises when a library's source is missing: a dependency that is not where it should be is a broken install, and a silent skip is exactly what this replaces.

Functions

facts_js(facts)

@spec facts_js(map()) :: String.t()

The JavaScript statement carrying the facts, for the install-facts preamble.

lib_dir()

Where the library files land, relative to the host's root.

libraries()

The manifest: every library this module vendors.

vendor!(root)

@spec vendor!(Path.t()) :: %{
  required(String.t()) => %{file: String.t(), cdn: String.t() | nil}
}

Copies every library into root's priv/static/assets/vendor/lib and returns the facts map (name => %{file, cdn}). Raises when a library's source is missing: a dependency that is not where it should be is a broken install, and a silent skip is exactly what this replaces.