Skip to content

feat(c2pa-web): Use Context to configure Readers/Builders - #202

Open
ale-adobe wants to merge 16 commits into
refactor/context-settings-utilitiesfrom
refactor/context-settings-web
Open

feat(c2pa-web): Use Context to configure Readers/Builders#202
ale-adobe wants to merge 16 commits into
refactor/context-settings-utilitiesfrom
refactor/context-settings-web

Conversation

@ale-adobe

@ale-adobe ale-adobe commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR brings in the Context class introduced in c2pa-utilities (see #201) in c2pa-web, replacing the SDK-wide Settings object and the Reader/Builder factory pattern with static constructors and per-instance Context objects. This mirrors how c2pa-node works (see #204).

In addition, we remove the dead thread-local Settings path using loadSettings from c2pa-wasm, since Settings are now passed explicitly via the Context object.

Changes

C2pa class (c2pa.ts)

  • Renamed C2paSdk to just C2pa.
  • Simplified C2pa class: now functions as a way to start up a web worker instance and load the Wasm binary. The returned handle from createC2pa() is passed in directly when creating a Reader or Builder.

Reader (reader.ts) and Builder (builder.ts)

  • Deprecated the existing ReaderFactory / createReaderFactory and BuilderFactory / createBuilderFactory patterns, but still retain them for now in order to not introduce breaking changes just yet.
  • Added static methods to Reader and Builder for constructing new instances, which accept the C2pa handle above, plus an optional Context to configure their respective behaviors. This is the new, recommended way to create them.

New example usage:

import { createC2pa, Reader, Builder, Context } from '@contentauth/c2pa-web';
import wasmSrc from '@contentauth/c2pa-web/resources/c2pa.wasm?url';

const c2pa = await createC2pa({ wasmSrc });

const context = new Context({
  verify: {
    verifyTrust: true
  },
  trust: {
    trustAnchors: 'https://example.com/trust-anchors.pem'
  }
});

const reader = await Reader.fromBlob(c2pa, blob.type, blob, context);
const builder = await Builder.new(c2pa, context);

Thread-local Settings

  • Removed c2pa-wasm's dead thread-local settings path (loadSettings/Settings::from_string). The one caller (c2pa-web's worker init) is removed along with it.

…ttings

createC2pa's Config gains context?: Context, attached once at
SDK-creation time and shared by every Reader/Builder the SDK
subsequently creates. Settings are resolved to JSON exactly once per
SDK instance instead of on every single Reader/Builder call.

Config.settings and per-call settings overrides on
reader.fromBlob/builder.new/etc. are deprecated but still fully
functional, merging over the base Context via mergeSettings(), to
avoid breaking existing callers before a major version bump.

Also removes c2pa-wasm's dead thread-local settings path
(loadSettings/Settings::from_string): nothing ever read the
thread-local state it wrote, since every Reader/Builder already
built its own explicit per-call Context. The one caller (c2pa-web's
worker init) is removed along with it.

Builds on the Context introduced for c2pa-utilities in
refactor/context-settings-utilities.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: aa2a181

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@contentauth/c2pa-wasm Minor
@contentauth/c2pa-web Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ale-adobe ale-adobe changed the title refactor(c2pa-web): Use Context, deprecate thread-local Settings paths refactor(c2pa-web): Use Context instead of thread-local Settings; switch to static Reader/Builder construction Sep 1, 2026
@ale-adobe ale-adobe changed the title refactor(c2pa-web): Use Context instead of thread-local Settings; switch to static Reader/Builder construction refactor(c2pa-web): Use Context instead of thread-local Settings Sep 2, 2026
Comment thread packages/c2pa-web/src/lib/reader.spec.ts
Comment thread packages/c2pa-web/src/lib/reader.spec.ts
Comment thread packages/c2pa-web/src/lib/builder.spec.ts
Comment thread packages/c2pa-web/src/lib/builder.spec.ts
Comment thread packages/c2pa-web/src/lib/builder.spec.ts
Comment thread packages/c2pa-web/src/lib/builder.spec.ts
Comment thread packages/c2pa-web/src/lib/builder.spec.ts
/**
* @param worker - Worker (via WorkerManager) to be associated with this reader factory.
* @param settings - Optional settings to be used for all builders.
* @deprecated Use `Builder.new`/`Builder.fromDefinition`/`Builder.fromArchive` with a `Context`

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Existing BuilderFactory is still here, but now marked as deprecated.

}

/**
* @deprecated Use `Reader.fromBlob`/`Reader.fromBlobFragment` with a `Context` instead.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Existing ReaderFactory is still here, but now marked as deprecated.

@ale-adobe
ale-adobe marked this pull request as ready for review September 2, 2026 02:06
…entauth/c2pa-js into refactor/context-settings-web
@ale-adobe ale-adobe changed the title refactor(c2pa-web): Use Context instead of thread-local Settings feat(c2pa-web): Use Context instead of thread-local Settings Sep 2, 2026
Comment on lines -13 to -26
/**
* NOTE: we can only return Err(JsString) or Err(JsValue) as error types here, because for some as-of-yet unknown
* reason, wasm-bindgen appears to mishandle JsErrors when created in a Firefox web worker.
*
* See: https://github.com/wasm-bindgen/wasm-bindgen/issues/4961
*/

/// Accepts a JSON-serialized string to be loaded as c2pa-rs settings.
#[wasm_bindgen(js_name = loadSettings)]
pub fn load_settings(settings: &str) -> Result<(), JsString> {
c2pa::settings::Settings::from_string(settings, "json").map_err(WasmError::other)?;

Ok(())
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gone, since we now pass settings through Context.

@@ -7,7 +7,7 @@
* it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff in this file is a bit scary, but not much has actually changed at a high level.

  • Helper functions are pulled out to the top-level as free functions.
  • Functions that used to be in the createBuilder factory are now static functions on the Builder class.
  • The old BuilderFactory is maintained at the bottom, but marked deprecated.
  • New: Functions for constructing a new Builder accept the C2pa object and the optional Context.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we summarize the prose a little bit maybe, to make it less scary?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can try to trim some of the docs and comments, but most of that was existing from previous AI compliance work that Colin did.

@@ -10,88 +10,164 @@
import { Manifest, ManifestStore } from '@contentauth/c2pa-types';

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff in this file is a bit scary, but not much has actually changed at a high level.

  • Helper functions are pulled out to the top-level as free functions.
  • Functions that used to be in the createReader factory are now static functions on the Reader class.
  • The old ReaderFactory is maintained at the bottom, but marked deprecated.
  • New: Functions for constructing a new Reader accept the C2pa object and the optional Context.

builder: createBuilderFactory(worker, settings),
const c2pa: C2pa = {
worker,
reader: undefined as unknown as ReaderFactory,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A workaround to satisfy the shape of the object before creating the factories below, since we are still trying to keep around the factories as deprecated paths to avoid breaking changes.

@ale-adobe ale-adobe changed the title feat(c2pa-web): Use Context instead of thread-local Settings feat(c2pa-web): Use Context to configure Readers/Builders Sep 2, 2026

@tmathern tmathern left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double-check with the native SDK, but I think you can simplify the Reader format check.

*/

export type * from './lib/c2pa.js';
export type { Config, C2pa } from './lib/c2pa.js';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

export changes may make this a breaking change if exposed types are different

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, correct, alongside the rename to just C2pa.

Comment thread packages/c2pa-web/src/lib/c2pa.ts
// Define browser-to-worker RPC interface
const { createTx, rx } = channel<{
initWorker: (module: WebAssembly.Module, settings?: string) => void;
initWorker: (module: WebAssembly.Module) => void;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking change?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Changesets are marked as minor for the breaking change, but willing to make this the major version bump to go to 1.0.

@@ -7,7 +7,7 @@
* it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we summarize the prose a little bit maybe, to make it less scary?

*/
new: (settings?: Settings) => Promise<Builder>;
static async new(c2pa: C2pa, context: Context = new Context()): Promise<Builder> {
const settingsJson = await context.toJson();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can a context be returned as null if there was an error in context.toJson?

@ale-adobe ale-adobe Sep 4, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not anymore, since I made some changes in #201. It will either resolve into a string or throw if something goes wrong. If an error is thrown, that error will propagate through the constructor here.

) => Promise<Reader | null>;
context: Context = new Context()
): Promise<Reader | null> {
if (!isSupportedReaderFormat(format)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still needed, now that the Rust SDK attempts auto-detect?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not. But I think that's out-of-scope for this PR, so let me revisit that separately.

(I'm inclined to believe that you're right, since this is something we flagged in my proposal originally. Node doesn't do this, and Rust is doing the heavy lifting there as far as I can tell. So I don't see why Web shouldn't do the same thing.)

Regardless, filed https://jira.corp.adobe.com/browse/CAI-13611 this in the epic for me to follow up on later.

@cdmurph32 cdmurph32 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants