Skip to content

feat(c2pa-utilities): Introduce Context class - #201

Open
ale-adobe wants to merge 15 commits into
mainfrom
refactor/context-settings-utilities
Open

feat(c2pa-utilities): Introduce Context class#201
ale-adobe wants to merge 15 commits into
mainfrom
refactor/context-settings-utilities

Conversation

@ale-adobe

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

Copy link
Copy Markdown
Collaborator

Summary

This PR introduces the Context class, mirroring the one used in native SDKs, as a wrapper around Settings for configuring Reader and Builder objects. This class is adopted by c2pa-web in #202 and by c2pa-node in #204 as the new way to configure SDK behavior.

Changes

  • Added a Context class wrapping Settings, mirroring Context used in native SDKs.
    • Context is immutable and is meant to supplied when creating a Reader or Builder, customizing that instance's behavior.
  • Simplified resolveSettings by no longer accepting overrides, since the concept of providing per-call overrides no longer applies. Users should create and merge their settings as needed, then pass them into a Context that goes into the Reader or Builder that they wish to configure.
  • Added withDefaultSettings(), a synchronous counterpart to resolveSettings that applies this package's defaults without the async trust-anchor URL resolution, for use in c2pa-node which needs the defaults without supporting that fetch (yet).

Adds a Context class wrapping Settings, mirroring c2pa-rs's own
Context::new().with_settings(...) concept. Context is immutable;
new Context(settings) replaces (not merges) whatever settings a
derived instance holds, matching Context::with_settings's and
c2pa-python's ContextBuilder.with_settings's replace semantics.
Combine multiple Settings sources with mergeSettings() before
constructing a Context.

Simplifies resolveSettings to a single settings argument (its
override-merging second argument had exactly one caller, Context,
which never used it) and fixes a real bug along the way: settings
resolution short-circuited to undefined whenever no settings were
provided at all, silently skipping this package's own defaults
(builder.generateC2paArchive: true) instead of applying them.
resolveSettings/Context.toJson() now always resolve to a value.

Adds withDefaultSettings(), a synchronous counterpart to
resolveSettings that applies this package's defaults without the
async trust-anchor URL resolution, for bindings that need the
defaults without paying for (or supporting) that fetch.

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: 09993e3

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

This PR includes changesets to release 3 packages
Name Type
@contentauth/c2pa-utilities Minor
@contentauth/c2pa-web Patch
@contentauth/c2pa-node Patch

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-utilities): Introduce Context refactor(c2pa-utilities): Introduce Context, deprecate thread-local Settings paths Aug 27, 2026
ale-adobe and others added 3 commits August 27, 2026 11:35
resolveSettings(baseSettings, overrideSettings) became
resolveSettings(settings) in the Context refactor, since Context
never used the second argument. c2pa-web's five call sites (still on
the pre-Context per-call-settings path here) replicate the old
merge-then-resolve behavior explicitly via mergeSettings() so this
package keeps building.

This is a temporary shim: refactor/context-settings-web replaces
these call sites entirely with Context-based settings resolution.
Once that branch's changes land, this commit's diff is superseded,
not needed going forward.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ale-adobe
ale-adobe marked this pull request as ready for review August 27, 2026 19:28
@ale-adobe ale-adobe changed the title refactor(c2pa-utilities): Introduce Context, deprecate thread-local Settings paths refactor(c2pa-utilities): Introduce Context class Sep 2, 2026
@ale-adobe ale-adobe changed the title refactor(c2pa-utilities): Introduce Context class feat(c2pa-utilities): Introduce Context class 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.

See inline comments.

* }
* };
*
* const settingsJson = await resolveSettings(settings, undefined);

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.

https://github.com/contentauth/c2pa-js/blob/main/packages/c2pa-web/src/lib/c2pa.ts#L73

This also has a second undefined param, why not change it there too?

* @returns A JSON-serialized string of the resolved settings.
*/
toJson(options?: FetchWithRetryOptions): Promise<string> {
this._jsonPromise ??= resolveSettings(this.settings, options);

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.

Does this cache only on success? Or would it cache failures if resolving settings fail? Asking because we should be able to retry on failure.

* {@link resolveSettings} so a `Reader`/`Builder` gets sane behavior even with no settings
* provided at all.
*/
export const DEFAULT_SETTINGS: Settings = {

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 make those defaults read-only explicitly? if someone wants to use them as base, they should clone and modify their own version.

@@ -0,0 +1,6 @@
---
'@contentauth/c2pa-utilities': minor

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.

I think actually the resolveSettings change may turn this into a breaking change?

);
});

test('toJson() memoizes: repeated calls return the exact same Promise', async () => {

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 the promise memoized, or its resolution?

import { resolveSettings, type Settings } from './settings.js';

/**
* A `Context` configures the behavior of a single `Reader`/`Builder`.

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.

Suggested change
* A `Context` configures the behavior of a single `Reader`/`Builder`.
* A `Context` configures the behavior of a `Reader`/`Builder`.

It is a bit unclear just from the comment, but a Context could be reused by multiple object.

)
);

const result = await resolveSettings(undefined, {

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.

This does look like a breaking change...

### `Context` and `Settings`

Helpers for building, merging, and serializing the `Settings` object consumed by `c2pa-web`'s and `c2pa-node`'s `Reader`/`Builder` constructors.
`Settings` is a plain, JSON-serializable object configuring SDK behavior around trust anchors, verification options, and `Reader`/`Builder` options. `Context` is a small, immutable wrapper around `Settings`, and is the recommended way to configure a `Reader`/`Builder`. `Context` objects are passed directly to the call that creates a `Reader`/`Builder` instance, so one running SDK instance can freely create many different `Reader`/`Builder`s, each with its own `Context`. See [`c2pa-web`'s README](../c2pa-web/README.md#configuring-behavior-with-context) for an example.

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.

Maybe clarify that if a context changes after a builder/reader is created with that context, the context change does not propagate. It is frozen in time at creation of reader/builder (as far as the native side goes).

async new(settings?: Settings) {
const settingsJson = await resolveSettings(baseSettings, settings);
// TODO: temporary shim for c2pa-utilities' resolveSettings signature change
// (single-argument now); removed once c2pa-web adopts Context in a follow-up PR.

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.

(Repeating it, I know: but that means eventually this is a breaking change).

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.

2 participants