feat(c2pa-utilities): Introduce Context class - #201
Conversation
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 detectedLatest commit: 09993e3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
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 |
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>
…ontext-settings-utilities
| * } | ||
| * }; | ||
| * | ||
| * const settingsJson = await resolveSettings(settings, undefined); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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 = { |
There was a problem hiding this comment.
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 | |||
There was a problem hiding this comment.
I think actually the resolveSettings change may turn this into a breaking change?
| ); | ||
| }); | ||
|
|
||
| test('toJson() memoizes: repeated calls return the exact same Promise', async () => { |
There was a problem hiding this comment.
Is the promise memoized, or its resolution?
| import { resolveSettings, type Settings } from './settings.js'; | ||
|
|
||
| /** | ||
| * A `Context` configures the behavior of a single `Reader`/`Builder`. |
There was a problem hiding this comment.
| * 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, { |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
(Repeating it, I know: but that means eventually this is a breaking change).
Summary
This PR introduces the
Contextclass, mirroring the one used in native SDKs, as a wrapper aroundSettingsfor configuringReaderandBuilderobjects. This class is adopted byc2pa-webin #202 and byc2pa-nodein #204 as the new way to configure SDK behavior.Changes
Contextclass wrappingSettings, mirroringContextused in native SDKs.Contextis immutable and is meant to supplied when creating aReaderorBuilder, customizing that instance's behavior.resolveSettingsby 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 aContextthat goes into theReaderorBuilderthat they wish to configure.c2pa-webto accommodate the signature change; will be removed in feat(c2pa-web): Use Context to configure Readers/Builders #202.withDefaultSettings(), a synchronous counterpart toresolveSettingsthat applies this package's defaults without the async trust-anchor URL resolution, for use inc2pa-nodewhich needs the defaults without supporting that fetch (yet).