PageSourceSearch

https://opensource.contentauthenticity.org/assets/js/a97283a0.9abf1087.js

js contentauthenticity.org collected 2026-09-24 08:25:27 UTC 16,783 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkopensource_contentauth_org=globalThis.webpackChunkopensource_contentauth_org||[]).push([[4294],{8453:(e,t,n)=>{n.d(t,{R:()=>c,x:()=>a});var s=n(6540);const i={},r=s.createContext(i);function c(e){const t=s.useContext(r);return s.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:c(e.components),s.createElement(r.Provider,{value:t},e.children)}},8544:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>o,contentTitle:()=>a,default:()=>h,frontMatter:()=>c,metadata:()=>s,toc:()=>d});const s=JSON.parse('{"id":"sdk-repos/c2pa-js/packages/c2pa-utilities/README","title":"c2pa-utilities","description":"c2pa-utilities is a home for shared utilities and libraries used by both c2pa-web and c2pa-node. Most clients will get these transitively as a dependency of one of those two packages, but @contentauth/c2pa-utilities is also published standalone for cases where you want to reuse a piece of it directly.","source":"@site/docs/sdk-repos/c2pa-js/packages/c2pa-utilities/README.md","sourceDirName":"sdk-repos/c2pa-js/packages/c2pa-utilities","slug":"/sdk-repos/c2pa-js/packages/c2pa-utilities/","permalink":"/docs/sdk-repos/c2pa-js/packages/c2pa-utilities/","draft":false,"unlisted":false,"editUrl":"https://github.com/contentauth/c2pa-js/edit/main/packages/c2pa-utilities/README.md","tags":[],"version":"current","frontMatter":{},"sidebar":"docs","previous":{"title":"c2pa-wasm","permalink":"/docs/sdk-repos/c2pa-js/packages/c2pa-wasm/"},"next":{"title":"Supported media formats","permalink":"/docs/sdk-repos/c2pa-js/supported-formats"}}');var i=n(4848),r=n(8453);const c={},a="c2pa-utilities",o={},d=[{value:"Installation",id:"installation",level:2},{value:"API reference documentation",id:"api-reference-documentation",level:2},{value:"Utilities",id:"utilities",level:2},{value:"<code>Context</code> and <code>Settings</code>",id:"context-and-settings",level:3},{value:"Creating a <code>Context</code>",id:"creating-a-context",level:4},{value:"Combining <code>Settings</code>",id:"combining-settings",level:4},{value:"Resolving a <code>Context</code> to JSON",id:"resolving-a-context-to-json",level:4},{value:"Building <code>Settings</code> directly",id:"building-settings-directly",level:4},{value:"Fetch with retry",id:"fetch-with-retry",level:3},{value:"Asset size validation",id:"asset-size-validation",level:3},{value:"Signing algorithm",id:"signing-algorithm",level:3},{value:"Library development",id:"library-development",level:2},{value:"Prerequisites",id:"prerequisites",level:3},{value:"Building",id:"building",level:3},{value:"Testing",id:"testing",level:3}];function l(e){const t={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",ul:"ul",...(0,r.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.header,{children:(0,i.jsx)(t.h1,{id:"c2pa-utilities",children:"c2pa-utilities"})}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"c2pa-utilities"})," is a home for shared utilities and libraries used by both ",(0,i.jsx)(t.code,{children:"c2pa-web"})," and ",(0,i.jsx)(t.code,{children:"c2pa-node"}),". Most clients will get these transitively as a dependency of one of those two packages, but ",(0,i.jsx)(t.code,{children:"@contentauth/c2pa-utilities"})," is also published standalone for cases where you want to reuse a piece of it directly."]}),"\n",(0,i.jsx)(t.h2,{id:"installation",children:"Installation"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-sh",children:"npm install @contentauth/c2pa-utilities\n"})}),"\n",(0,i.jsx)(t.h2,{id:"api-reference-documentation",children:"API reference documentation"}),"\n",(0,i.jsxs)(t.p,{children:["Complete API documentation is generated from TypeScript source using ",(0,i.jsx)(t.a,{href:"https://typedoc.org/",children:"TypeDoc"})," and published to ",(0,i.jsx)(t.a,{href:"https://contentauth.github.io/c2pa-js/modules/_contentauth_c2pa-utilities.html",children:"GitHub Pages"}),"."]}),"\n",(0,i.jsx)(t.h2,{id:"utilities",children:"Utilities"}),"\n",(0,i.jsxs)(t.h3,{id:"context-and-settings",children:[(0,i.jsx)(t.code,{children:"Context"})," and ",(0,i.jsx)(t.code,{children:"Settings"})]}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"Settings"}
1)," is a plain, JSON-serializable object configuring SDK behavior around trust anchors, verification options, and ",(0,i.jsx)(t.code,{children:"Reader"}),"/",(0,i.jsx)(t.code,{children:"Builder"})," options."]}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"Context"})," is a small, immutable wrapper around ",(0,i.jsx)(t.code,{children:"Settings"}),", and is the recommended way to configure a ",(0,i.jsx)(t.code,{children:"Reader"}),"/",(0,i.jsx)(t.code,{children:"Builder"}),". ",(0,i.jsx)(t.code,{children:"Context"})," objects are passed directly to the call that creates a ",(0,i.jsx)(t.code,{children:"Reader"}),"/",(0,i.jsx)(t.code,{children:"Builder"})," instance, so one running SDK instance can freely create many different ",(0,i.jsx)(t.code,{children:"Reader"}),"/",(0,i.jsx)(t.code,{children:"Builder"}),"s, each with its own ",(0,i.jsx)(t.code,{children:"Context"}),". ",(0,i.jsx)(t.code,{children:"Context"})," changes do not propagate, since the ",(0,i.jsx)(t.code,{children:"Context"})," is snapshotted when used to construct a ",(0,i.jsx)(t.code,{children:"Reader"}),"/",(0,i.jsx)(t.code,{children:"Builder"}),". Therefore, it can be safely used to construct multiple instances."]}),"\n",(0,i.jsxs)(t.p,{children:["See ",(0,i.jsxs)(t.a,{href:"/docs/sdk-repos/c2pa-js/packages/c2pa-web/#configuring-behavior-with-context",children:[(0,i.jsx)(t.code,{children:"c2pa-web"}),"'s README"]})," for an example."]}),"\n",(0,i.jsxs)(t.h4,{id:"creating-a-context",children:["Creating a ",(0,i.jsx)(t.code,{children:"Context"})]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-typescript",children:"import { Context } from '@contentauth/c2pa-utilities';\n\nconst context = new Context({\n  verify: {\n    verifyTrust: true\n  },\n  trust: {\n    trustAnchors: 'https://example.com/trust-anchors.pem'\n  }\n});\n"})}),"\n",(0,i.jsxs)(t.h4,{id:"combining-settings",children:["Combining ",(0,i.jsx)(t.code,{children:"Settings"})]}),"\n",(0,i.jsxs)(t.p,{children:["Each ",(0,i.jsx)(t.code,{children:"Context"})," holds whatever single ",(0,i.jsx)(t.code,{children:"Settings"})," object was passed to its constructor and does not handle any merging of settings. To combine more than one ",(0,i.jsx)(t.code,{children:"Settings"})," source, merge them first with ",(0,i.jsx)(t.code,{children:"mergeSettings()"}),", then construct a ",(0,i.jsx)(t.code,{children:"Context"})," from the single, merged result:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-typescript",children:"import { Context, mergeSettings } from '@contentauth/c2pa-utilities';\n\nconst base = { verify: { verifyTrust: true } };\nconst override = { verify: { verifyAfterSign: true } };\n\nconst context = new Context(mergeSettings(base, override));\n// context.settings is { verify: { verifyTrust: true, verifyAfterSign: true } }\n"})}),"\n",(0,i.jsxs)(t.h4,{id:"resolving-a-context-to-json",children:["Resolving a ",(0,i.jsx)(t.code,{children:"Context"})," to JSON"]}),"\n",(0,i.jsxs)(t.p,{children:["Settings are passed across the WASM (",(0,i.jsx)(t.code,{children:"c2pa-web"}),")/native(",(0,i.jsx)(t.code,{children:"c2pa-node"}),") boundary as a JSON string. ",(0,i.jsx)(t.code,{children:"toJson()"})," resolves any trust-anchor URLs embedded in the settings (fetching and validating them) and serializes the result:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-typescript",children:'const contextJson = await context.toJson();\n// \'{"verify":{"verify_trust":true},"trust":{"trust_anchors":"-----BEGIN CERTIFICATE-----..."}}\'\n'})}),"\n",(0,i.jsxs)(t.p,{children:["This step is asynchronous, and can throw if a trust-anchor URL fails to resolve. Bindings call it once, right after building the base ",(0,i.jsx)(t.code,{children:"Context"}),", rather than on every ",(0,i.jsx)(t.code,{children:"Reader"}),"/",(0,i.jsx)(t.code,{children:"Builder"})," call. Internally, ",(0,i.jsx)(t.code,{children:"toJson()"})," is a thin wrapper over ",(0,i.jsx)(t.code,{children:"resolveSettings"}),", below."]}),"\n",(0,i.jsxs)(t.h4,{id:"building-settings-directly",children:["Building ",(0,i.jsx)(t.code,{children:"Settings"})," directly"]}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"Context"})," is built on a handful of lower-level ",(0,i.jsx)(t.code,{children:"Settings"})," helpers, which remain available directly for cases that don't need a ",(0,i.jsx)(t.code,{children:"Context"})," at all:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-typescript",children:"import {\n  createTrustSettings,\n  createVerifySettings,\n  mergeSettings,\n  resolveSettings\n} from '@contentauth/c2pa-utilities';\n\nconst trustSettings = createTrustSettings({\n  trustAnchors: 'https://example.com/anchors.pem'\n});\n\nconst verifySettings = createVerifySettings({\n  verifyTrust: true,\n  verifyAfterReading: true\n});\n\nconst settings = mergeSettings(trustSettings, verifySettings);\n\n// Resolves trust-anchor URLs and serializes to the snake_case JSON string the native SDK expects.\nconst settingsJson = await resolveSettings(settings);\n"})}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"resolveSettings"})," is the entry point most direct callers want. It resolves any ",(0,i.jsx)(t.code,{children:"trust"}),"/",(0,i.jsx)(t.code,{children:"cawgTrust"})," URL fields (fetching and inlining the PEM content, retrying transient failures), and returns the result as a snake_case JSON string. ",(0,i.jsx)(t.code,{children:"settings"})," is merged on top of this package's defaults, so ",(0,i.jsx)(t.code,{children:"resolveSettings"})," always returns a value, even when called with ",(0,i.jsx)(t.code,{children:"undefined"}),". To combine more than one ",(0,i.jsx)(t.code,{children:"Settings"})," object first, merge them with ",(0,i.jsx)(t.code,{children:"mergeSettings()"})," before calling ",(0,i.jsx)(t.code,{children:"resolveSettings"}),", as above."]}),"\n",(0,i.jsx)(t.p,{children:"Other exports:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"createTrustSettings"})," / ",(0,i.jsx)(t.code,{children:"createCawgTrustSettings"})," / ",(0,i.jsx)(t.code,{children:"createVerifySettings"})," \u2014 construct a ",(0,i.jsx)(t.code,{children:"Settings"})," fragment for one section."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"mergeSettings"})," \u2014 deep-merge any number of ",(0,i.jsx)(t.code,{children:"Settings"})," fragments, with later arguments overriding earlier ones. Nested fields are merged rather than overwritten."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"settingsToJson"})," \u2014 serialize a ",(0,i.jsx)(t.code,{children:"Settings"})," object to its snake_case JSON form without resolving trust-anchor URLs."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"snakeCaseify"}
1)," \u2014 the lower-level camelCase-to-snake_case object converter ",(0,i.jsx)(t.code,{children:"settingsToJson"}),"/",(0,i.jsx)(t.code,{children:"resolveSettings"})," use internally."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"loadSettingsFromUrl"})," \u2014 fetch a settings JSON document from a URL, with retry."]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"resolveTrustSettings"})," \u2014 resolve just a ",(0,i.jsx)(t.code,{children:"TrustSettings"})," object's URL fields in place; used internally by ",(0,i.jsx)(t.code,{children:"resolveSettings"}),"."]}),"\n"]}),"\n",(0,i.jsx)(t.h3,{id:"fetch-with-retry",children:"Fetch with retry"}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"fetchWithRetry"})," and ",(0,i.jsx)(t.code,{children:"fetchWithRetryRaw"})," wrap ",(0,i.jsx)(t.code,{children:"fetch"})," with exponential backoff, ",(0,i.jsx)(t.code,{children:"Retry-After"})," handling, and (for ",(0,i.jsx)(t.code,{children:"fetchWithRetry"}),") a response size cap. The retry mechanism is fixed; however, policy details such as retry count, backoff timing, which statuses/errors are retryable, and the maximum honored ",(0,i.jsx)(t.code,{children:"Retry-After"})," delay are configurable per call via ",(0,i.jsx)(t.code,{children:"FetchWithRetryOptions"}),"."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-typescript",children:"import { fetchWithRetry, fetchWithRetryRaw } from '@contentauth/c2pa-utilities';\n\n// GET as text, retrying on network errors, 429, and 5xx, capped at 1 MB by default.\nconst text = await fetchWithRetry('https://example.com/anchors.pem');\n\n// For other methods, headers, or bodies, or to handle the response yourself, use fetchWithRetryRaw.\nconst response = await fetchWithRetryRaw('https://example.com/upload', {\n  method: 'POST',\n  body: payload\n});\n"})}),"\n",(0,i.jsxs)(t.p,{children:["Both functions accept an ",(0,i.jsx)(t.code,{children:"FetchWithRetryOptions"})," object to override the defaults:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-typescript",children:"await fetchWithRetry(url, {\n  maxRetries: 5,\n  initialRetryDelayMs: 500,\n  maxRetryDelayMs: 5_000,\n  maxRetryAfterMs: 60_000,\n  maxResponseBytes: 5 * 1024 * 1024,\n  isRetryableStatus: (status) => status === 429 || status >= 500,\n  isRetryableError: (error) => true,\n  fetch: myFetchImplementation\n});\n"})}),"\n",(0,i.jsxs)(t.p,{children:["An ",(0,i.jsx)(t.code,{children:"AbortError"})," is never retried, and a malformed URL throws immediately rather than being retried."]}),"\n",(0,i.jsx)(t.h3,{id:"asset-size-validation",children:"Asset size validation"}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"validateAssetSize"})," is the shared size check used by both ",(0,i.jsx)(t.code,{children:"Reader"})," implementations before reading an asset."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-typescript",children:"import { validateAssetSize, AssetTooLargeError, DEFAULT_MAX_SIZE_IN_BYTES } from '@contentauth/c2pa-utilities';\n\ntry {\n  validateAssetSize(sizeInBytes, maxSizeInBytes); // pass 0 to use DEFAULT_MAX_SIZE_IN_BYTES\n} catch (e) {\n  if (e instanceof AssetTooLargeError) {\n    // asset exceeds the resolved limit\n  }\n}\n"})}),"\n",(0,i.jsx)(t.h3,{id:"signing-algorithm",children:"Signing algorithm"}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"SigningAlg"})," is the lowercase signing algorithm type accepted/produced by the core native library at the signer construction boundary (e.g. ",(0,i.jsx)(t.code,{children:"Signer.newSigner(cert, key, alg)"}),"). It's derived from the PascalCase ",(0,i.jsx)(t.code,{children:"SigningAlg"})," exported by ",(0,i.jsx)(t.code,{children:"@contentauth/c2pa-types"}),", which describes the casing used when a manifest's ",(0,i.jsx)(t.code,{children:"SignatureInfo.alg"})," is serialized."]}),"\n",(0,i.jsx)(t.h2,{id:"library-development",children:"Library development"}),"\n",(0,i.jsx)(t.h3,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,i.jsx)(t.p,{children:"Ensure the repo-wide prerequisites are installed:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"https://nodejs.org/",children:"Node.js"})," v22.22+"]}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://nx.dev/getting-started/intro",children:"Nx"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"https://pnpm.io/",children:"pnpm"})}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["See the ",(0,i.jsx)(t.a,{href:"/docs/sdk-repos/c2pa-js/#prerequisites",children:"c2pa-js README"})," for details."]}),"\n",(0,i.jsx)(t.h3,{id:"building",children:"Building"}),"\n",(0,i.jsx)(t.p,{children:"To build:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-sh",children:"nx build c2pa-utilities\n"})}),"\n",(0,i.jsx)(t.h3,{id:"testing",children:"Testing"}),"\n",(0,i.jsxs)(t.p,{children:["This library uses ",(0,i.jsx)(t.a,{href:"https://vitest.dev/",children:"Vitest"}),", with ",(0,i.jsx)(t.a,{href:"https://mswjs.io/",children:"msw"})," to mock ",(0,i.jsx)(t.code,{children:"fetch"})," in the settings and fetch-with-retry tests."]}),"\n",(0,i.jsx)(t.p,{children:"To run the tests:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-sh",children:"nx test c2pa-utilities\n"})})]})}function h(e={}){const{wrapper:t}={...(0,r.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(l,{...e})}):l(e)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.