@contentauth/c2pa-web 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +52 -41
  5. package/dist/lib/reader.d.ts +16 -3
  6. package/dist/lib/reader.d.ts.map +1 -1
  7. package/dist/lib/settings.d.ts +1 -0
  8. package/dist/lib/settings.d.ts.map +1 -1
  9. package/dist/lib/worker.d.ts +3 -1
  10. package/dist/lib/worker.d.ts.map +1 -1
  11. package/dist/resources/c2pa_bg.wasm +0 -0
  12. package/package.json +8 -3
  13. package/CHANGELOG.md +0 -20
  14. package/eslint.config.mjs +0 -30
  15. package/project.json +0 -7
  16. package/src/index.ts +0 -15
  17. package/src/lib/c2pa.spec.ts +0 -166
  18. package/src/lib/c2pa.ts +0 -65
  19. package/src/lib/error.ts +0 -26
  20. package/src/lib/reader.ts +0 -150
  21. package/src/lib/settings.ts +0 -52
  22. package/src/lib/supportedFormats.ts +0 -72
  23. package/src/lib/worker/setupWorker.ts +0 -53
  24. package/src/lib/worker/workerManager.ts +0 -64
  25. package/src/lib/worker/workerObjectMap.ts +0 -35
  26. package/src/lib/worker/workerResponse.ts +0 -50
  27. package/src/lib/worker.ts +0 -77
  28. package/test/fixtures/assets/C_with_CAWG_data.jpg +0 -0
  29. package/test/fixtures/assets/C_with_CAWG_data.json +0 -144
  30. package/test/fixtures/assets/C_with_CAWG_data_thumbnail.jpg +0 -0
  31. package/test/fixtures/assets/C_with_CAWG_data_trusted.json +0 -149
  32. package/test/fixtures/assets/C_with_CAWG_data_untrusted.json +0 -157
  33. package/test/fixtures/assets/dash1.m4s +0 -0
  34. package/test/fixtures/assets/dashinit.json +0 -184
  35. package/test/fixtures/assets/dashinit.mp4 +0 -0
  36. package/test/fixtures/assets/no_alg.jpg +0 -0
  37. package/test/fixtures/trust/anchor-correct.pem +0 -15
  38. package/test/fixtures/trust/anchor-incorrect.pem +0 -16
  39. package/tsconfig.json +0 -13
  40. package/tsconfig.lib.json +0 -29
  41. package/tsconfig.spec.json +0 -36
  42. package/vite.config.ts +0 -91
package/src/lib/c2pa.ts DELETED
@@ -1,65 +0,0 @@
1
- /**
2
- * Copyright 2025 Adobe
3
- * All Rights Reserved.
4
- *
5
- * NOTICE: Adobe permits you to use, modify, and distribute this file in
6
- * accordance with the terms of the Adobe license agreement accompanying
7
- * it.
8
- */
9
- import { createWorkerManager } from './worker/workerManager.js';
10
- import { createReaderFactory, ReaderFactory } from './reader.js';
11
- import { WASM_SRI } from '@contentauth/c2pa-wasm';
12
- import { Settings, settingsToWasmJson } from './settings.js';
13
-
14
- export interface Config {
15
- /**
16
- * URL to fetch the WASM binary or an already-instantiated WASM module.
17
- */
18
- wasmSrc: string | WebAssembly.Module;
19
-
20
- /**
21
- * Settings for the SDK.
22
- */
23
- settings?: Settings;
24
- }
25
-
26
- interface C2paSdk {
27
- reader: ReaderFactory;
28
- dispose: () => void;
29
- }
30
-
31
- /**
32
- * Creates a new instance of c2pa-web by setting up a web worker and preparing a WASM binary.
33
- *
34
- * @param config - SDK configuration object.
35
- * @returns An object providing access to factory methods for creating new reader objects.
36
- *
37
- * @example Creating a new SDK instance and reader:
38
- * ```
39
- * const c2pa = await createC2pa({ wasmSrc: 'url/hosting/wasm/binary' });
40
- *
41
- * const reader = await c2pa.reader.fromBlob(imageBlob.type, imageBlob);
42
- * ```
43
- */
44
- export async function createC2pa(config: Config): Promise<C2paSdk> {
45
- const { wasmSrc, settings } = config;
46
-
47
- const wasm =
48
- typeof wasmSrc === 'string' ? await fetchAndCompileWasm(wasmSrc) : wasmSrc;
49
-
50
- const settingsString = settings ? settingsToWasmJson(settings) : undefined;
51
-
52
- const worker = await createWorkerManager({ wasm, settingsString });
53
-
54
- return {
55
- reader: createReaderFactory(worker),
56
- dispose: worker.terminate,
57
- };
58
- }
59
-
60
- async function fetchAndCompileWasm(src: string) {
61
- const response = await fetch(src, { integrity: WASM_SRI });
62
- const wasm = await WebAssembly.compileStreaming(response);
63
-
64
- return wasm;
65
- }
package/src/lib/error.ts DELETED
@@ -1,26 +0,0 @@
1
- /**
2
- * Copyright 2025 Adobe
3
- * All Rights Reserved.
4
- *
5
- * NOTICE: Adobe permits you to use, modify, and distribute this file in
6
- * accordance with the terms of the Adobe license agreement accompanying
7
- * it.
8
- */
9
-
10
- import { MAX_SIZE_IN_BYTES } from './reader.js';
11
-
12
- export class AssetTooLargeError extends Error {
13
- constructor(size: number) {
14
- super(
15
- `The provided asset was too large. Size: ${size} bytes. Maximum: ${MAX_SIZE_IN_BYTES}.`
16
- );
17
- this.name = 'AssetTooLargeError';
18
- }
19
- }
20
-
21
- export class UnsupportedFormatError extends Error {
22
- constructor(format: string) {
23
- super(`Unsupported format: ${format}.`);
24
- this.name = 'UnsupportedFormatError';
25
- }
26
- }
package/src/lib/reader.ts DELETED
@@ -1,150 +0,0 @@
1
- /**
2
- * Copyright 2025 Adobe
3
- * All Rights Reserved.
4
- *
5
- * NOTICE: Adobe permits you to use, modify, and distribute this file in
6
- * accordance with the terms of the Adobe license agreement accompanying
7
- * it.
8
- */
9
-
10
- import { AssetTooLargeError, UnsupportedFormatError } from './error.js';
11
- import { isSupportedReaderFormat } from './supportedFormats.js';
12
- import type { WorkerManager } from './worker/workerManager.js';
13
-
14
- // 1 GB
15
- export const MAX_SIZE_IN_BYTES = 10 ** 9;
16
-
17
- export interface ReaderFactory {
18
- /**
19
- * Create a Reader from an asset's format and a blob of its bytes.
20
- *
21
- * @param format Asset format
22
- * @param blob Blob of asset bytes
23
- * @returns An object that provides methods for reading C2PA data from the provided asset.
24
- */
25
- fromBlob: (format: string, blob: Blob) => Promise<Reader>;
26
-
27
- fromBlobFragment: (
28
- format: string,
29
- init: Blob,
30
- fragment: Blob
31
- ) => Promise<Reader>;
32
- }
33
-
34
- export interface Reader {
35
- /**
36
- * @returns The asset's full manifest store containing all its manifests, validation statuses, and the URI of the active manifest.
37
- */
38
- manifestStore: () => Promise<any>;
39
-
40
- /**
41
- * @returns The label of the active manifest.
42
- */
43
- activeLabel: () => Promise<string | null>;
44
-
45
- /**
46
- * Resolves a URI reference to a binary object (e.g. a thumbnail) in the resource store.
47
- *
48
- * @param uri URI of the binary object to resolve.
49
- * @returns An array buffer of the resource's bytes.
50
- */
51
- resourceToBuffer: (uri: string) => Promise<ArrayBuffer>;
52
-
53
- /**
54
- * Dispose of this Reader, freeing the memory it occupied and preventing further use. Call this whenever the Reader is no longer needed.
55
- */
56
- free: () => Promise<void>;
57
- }
58
-
59
- /**
60
- *
61
- * @param worker - Worker (via WorkerManager) to be associated with this reader factory
62
- * @returns Object containing reader creation methods
63
- */
64
- export function createReaderFactory(worker: WorkerManager): ReaderFactory {
65
- const registry = new FinalizationRegistry<number>((id) => {
66
- worker.execute({ method: 'reader_free', args: [id] });
67
- });
68
-
69
- return {
70
- async fromBlob(format: string, blob: Blob): Promise<Reader> {
71
- if (!isSupportedReaderFormat(format)) {
72
- throw new UnsupportedFormatError(format);
73
- }
74
-
75
- if (blob.size > MAX_SIZE_IN_BYTES) {
76
- throw new AssetTooLargeError(blob.size);
77
- }
78
-
79
- const readerId = await worker.execute({
80
- method: 'reader_fromBlob',
81
- args: [format, blob],
82
- });
83
-
84
- const unregisterToken = Symbol(readerId);
85
- const reader = createReader(worker, readerId, () => {
86
- registry.unregister(unregisterToken);
87
- });
88
- registry.register(reader, readerId, unregisterToken);
89
-
90
- return reader;
91
- },
92
-
93
- async fromBlobFragment(format: string, init: Blob, fragment: Blob) {
94
- if (!isSupportedReaderFormat(format)) {
95
- throw new UnsupportedFormatError(format);
96
- }
97
-
98
- if (init.size > MAX_SIZE_IN_BYTES) {
99
- throw new AssetTooLargeError(init.size);
100
- }
101
-
102
- const readerId = await worker.execute({
103
- method: 'reader_fromBlobFragment',
104
- args: [format, init, fragment],
105
- });
106
-
107
- const unregisterToken = Symbol(readerId);
108
- const reader = createReader(worker, readerId, () => {
109
- registry.unregister(unregisterToken);
110
- });
111
- registry.register(reader, readerId, unregisterToken);
112
-
113
- return reader;
114
- },
115
- };
116
- }
117
-
118
- function createReader(
119
- worker: WorkerManager,
120
- id: number,
121
- onFree: () => void
122
- ): Reader {
123
- return {
124
- // TODO: manifest type
125
- async manifestStore(): Promise<any> {
126
- const json = await worker.execute({ method: 'reader_json', args: [id] });
127
-
128
- const manifestStore = JSON.parse(json);
129
-
130
- return manifestStore;
131
- },
132
- async activeLabel(): Promise<string | null> {
133
- const label = await worker.execute({
134
- method: 'reader_activeLabel',
135
- args: [id],
136
- });
137
- return label;
138
- },
139
- async resourceToBuffer(uri: string): Promise<ArrayBuffer> {
140
- return worker.execute({
141
- method: 'reader_resourceToBuffer',
142
- args: [id, uri],
143
- });
144
- },
145
- async free(): Promise<void> {
146
- onFree();
147
- return worker.execute({ method: 'reader_free', args: [id] });
148
- },
149
- };
150
- }
@@ -1,52 +0,0 @@
1
- /**
2
- * Copyright 2025 Adobe
3
- * All Rights Reserved.
4
- *
5
- * NOTICE: Adobe permits you to use, modify, and distribute this file in
6
- * accordance with the terms of the Adobe license agreement accompanying
7
- * it.
8
- */
9
-
10
- export interface Settings {
11
- trust?: TrustSettings;
12
- verify?: VerifySettings;
13
- }
14
-
15
- interface TrustSettings {
16
- userAnchors?: string;
17
- trustAnchors?: string;
18
- trustConfig?: string;
19
- allowedList?: string;
20
- }
21
-
22
- interface VerifySettings {
23
- verifyTrust?: boolean;
24
- }
25
-
26
- type SettingsObjectType = {
27
- [k: string]: string | boolean | SettingsObjectType;
28
- };
29
-
30
- /**
31
- * Converts a settings object to a JSON string of the structure expected by c2pa-rs.
32
- */
33
- export function settingsToWasmJson(settings: Settings) {
34
- return JSON.stringify(snakeCaseify(settings as SettingsObjectType));
35
- }
36
-
37
- function snakeCaseify(object: SettingsObjectType): SettingsObjectType {
38
- const formattedObject = Object.entries(object).reduce(
39
- (formattedObject, [key, val]) => {
40
- formattedObject[snakeCase(key)] =
41
- typeof val === 'object' ? snakeCaseify(val) : val;
42
- return formattedObject;
43
- },
44
- {} as SettingsObjectType
45
- );
46
-
47
- return formattedObject;
48
- }
49
-
50
- function snakeCase(str: string): string {
51
- return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
52
- }
@@ -1,72 +0,0 @@
1
- /**
2
- * Copyright 2025 Adobe
3
- * All Rights Reserved.
4
- *
5
- * NOTICE: Adobe permits you to use, modify, and distribute this file in
6
- * accordance with the terms of the Adobe license agreement accompanying
7
- * it.
8
- */
9
-
10
- export const READER_SUPPORTED_FORMATS = [
11
- 'jpg',
12
- 'video/mp4',
13
- 'image/heif',
14
- 'video/x-msvideo',
15
- 'pdf',
16
- 'image/png',
17
- 'application/c2pa',
18
- 'video/quicktime',
19
- 'video/avi',
20
- 'image/gif',
21
- 'application/xml',
22
- 'text/xml',
23
- 'application/xhtml+xml',
24
- 'tiff',
25
- 'audio/wave',
26
- 'mp4',
27
- 'image/avif',
28
- 'image/dng',
29
- 'png',
30
- 'dng',
31
- 'image/svg+xml',
32
- 'image/heic',
33
- 'application/mp4',
34
- 'image/x-nikon-nef',
35
- 'video/msvideo',
36
- 'tif',
37
- 'wav',
38
- 'xml',
39
- 'audio/vnd.wave',
40
- 'xhtml',
41
- 'gif',
42
- 'application/x-troff-msvideo',
43
- 'webp',
44
- 'heic',
45
- 'application/pdf',
46
- 'audio/mpeg',
47
- 'application/x-c2pa-manifest-store',
48
- 'jpeg',
49
- 'image/x-adobe-dng',
50
- 'audio/wav',
51
- 'mp3',
52
- 'mov',
53
- 'image/tiff',
54
- 'audio/mp4',
55
- 'application/svg+xml',
56
- 'arw',
57
- 'c2pa',
58
- 'svg',
59
- 'avi',
60
- 'audio/x-wav',
61
- 'm4a',
62
- 'image/x-sony-arw',
63
- 'image/jpeg',
64
- 'avif',
65
- 'image/webp',
66
- 'nef',
67
- 'heif',
68
- ];
69
-
70
- export function isSupportedReaderFormat(format: string): boolean {
71
- return READER_SUPPORTED_FORMATS.includes(format);
72
- }
@@ -1,53 +0,0 @@
1
- /**
2
- * Copyright 2025 Adobe
3
- * All Rights Reserved.
4
- *
5
- * NOTICE: Adobe permits you to use, modify, and distribute this file in
6
- * accordance with the terms of the Adobe license agreement accompanying
7
- * it.
8
- */
9
-
10
- import { postError, postSuccess } from './workerResponse.js';
11
-
12
- export interface WorkerRequest<T, K> {
13
- method: T;
14
- args: K;
15
- transfer?: Transferable[];
16
- }
17
-
18
- type WorkerRequestPayload = Omit<WorkerRequest<any, any>, 'transfer'>;
19
-
20
- export interface WorkerResponse<T> {
21
- data: T;
22
- transfer?: Transferable[];
23
- }
24
-
25
- export type WorkerFunctions = Record<
26
- string,
27
- (
28
- ...args: any[]
29
- ) => void | Promise<void> | WorkerResponse<any> | Promise<WorkerResponse<any>>
30
- >;
31
-
32
- /**
33
- * Prepares a worker with a list of functions to expose via postMessage and an initialization function
34
- *
35
- * @param functions Map of functions keyed by name
36
- * @param init Initialization function to be called once when the worker is set up
37
- */
38
- export function setupWorker(functions: WorkerFunctions) {
39
- onmessage = async (e: MessageEvent<WorkerRequestPayload>) => {
40
- try {
41
- const { args, method } = e.data;
42
- const result = await functions[method](...args);
43
-
44
- if (result?.data !== undefined) {
45
- postSuccess(result.data, result.transfer);
46
- } else {
47
- postSuccess();
48
- }
49
- } catch (e) {
50
- postError(e);
51
- }
52
- };
53
- }
@@ -1,64 +0,0 @@
1
- /**
2
- * Copyright 2025 Adobe
3
- * All Rights Reserved.
4
- *
5
- * NOTICE: Adobe permits you to use, modify, and distribute this file in
6
- * accordance with the terms of the Adobe license agreement accompanying
7
- * it.
8
- */
9
-
10
- import { WorkerDefinition } from '../worker.js';
11
- import Worker from '../worker?worker&inline';
12
- import { WorkerRequest, WorkerResponse } from './setupWorker.js';
13
- import { handleWorkerResponse } from './workerResponse.js';
14
-
15
- export interface WorkerManager {
16
- execute: <T extends keyof WorkerDefinition, K extends WorkerDefinition[T]>(
17
- request: WorkerRequest<T, Parameters<K>>
18
- ) => Promise<
19
- Awaited<ReturnType<K>> extends WorkerResponse<infer Data>
20
- ? Data
21
- : Awaited<ReturnType<K>>
22
- >;
23
- terminate: () => void;
24
- }
25
-
26
- export interface CreateWorkerManagerConfig {
27
- wasm: WebAssembly.Module;
28
- settingsString?: string;
29
- }
30
-
31
- /**
32
- * Creates a new web worker and performs initialization steps:
33
- * - Compile WASM
34
- * - Load settings (if provided)
35
- *
36
- * @param config - configuration object
37
- * @returns Facade providing convenient control over worker functions
38
- */
39
- export async function createWorkerManager(
40
- config: CreateWorkerManagerConfig
41
- ): Promise<WorkerManager> {
42
- const { wasm, settingsString } = config;
43
-
44
- const worker = new Worker();
45
-
46
- const execute: WorkerManager['execute'] = (request) => {
47
- return new Promise((resolve, reject) => {
48
- handleWorkerResponse(worker, {
49
- onSuccess: (data) => resolve(data),
50
- onError: (error) => reject(error),
51
- });
52
-
53
- const { method, args, transfer } = request;
54
- worker.postMessage({ method, args }, transfer ?? []);
55
- });
56
- };
57
-
58
- await execute({ method: 'initWorker', args: [wasm, settingsString] });
59
-
60
- return {
61
- execute,
62
- terminate: () => worker.terminate(),
63
- };
64
- }
@@ -1,35 +0,0 @@
1
- /**
2
- * Copyright 2025 Adobe
3
- * All Rights Reserved.
4
- *
5
- * NOTICE: Adobe permits you to use, modify, and distribute this file in
6
- * accordance with the terms of the Adobe license agreement accompanying
7
- * it.
8
- */
9
-
10
- export function createWorkerObjectMap<T>() {
11
- let objId = 0;
12
- const objectMap = new Map<number, T>();
13
-
14
- return {
15
- add(object: T): number {
16
- const id = objId++;
17
- objectMap.set(id, object);
18
- return id;
19
- },
20
-
21
- get(id: number): T {
22
- const maybeObject = objectMap.get(id);
23
-
24
- if (!maybeObject) {
25
- throw new Error('Attempted to use an object that has been freed');
26
- }
27
-
28
- return maybeObject;
29
- },
30
-
31
- remove(id: number): boolean {
32
- return objectMap.delete(id);
33
- },
34
- };
35
- }
@@ -1,50 +0,0 @@
1
- /**
2
- * Copyright 2025 Adobe
3
- * All Rights Reserved.
4
- *
5
- * NOTICE: Adobe permits you to use, modify, and distribute this file in
6
- * accordance with the terms of the Adobe license agreement accompanying
7
- * it.
8
- */
9
-
10
- export function postSuccess(payload?: any, transfer?: Transferable[]): void {
11
- const responseObject = {
12
- type: 'success',
13
- ...(payload !== undefined ? { payload } : {}),
14
- };
15
-
16
- postMessage(responseObject, transfer ?? []);
17
- }
18
-
19
- export function postError(error: unknown): void {
20
- postMessage({ type: 'error', error });
21
- }
22
-
23
- export interface ResponseHandlers {
24
- onSuccess: (data?: any) => void;
25
- onError: (error?: any) => void;
26
- }
27
-
28
- export function handleWorkerResponse(
29
- worker: Worker,
30
- responseHandlers: ResponseHandlers
31
- ) {
32
- worker.onmessage = (event) => {
33
- const { data } = event;
34
-
35
- if (data.type === 'success') {
36
- responseHandlers.onSuccess(data?.payload);
37
- } else {
38
- responseHandlers.onError(data?.error);
39
- }
40
- };
41
-
42
- // @TODO: should these have their own error handlers?
43
- worker.onerror = (event) => {
44
- responseHandlers.onError(event.error);
45
- };
46
-
47
- worker.onmessageerror = (event) => {
48
- responseHandlers.onError(event.data);
49
- };
50
- }
package/src/lib/worker.ts DELETED
@@ -1,77 +0,0 @@
1
- /**
2
- * Copyright 2025 Adobe
3
- * All Rights Reserved.
4
- *
5
- * NOTICE: Adobe permits you to use, modify, and distribute this file in
6
- * accordance with the terms of the Adobe license agreement accompanying
7
- * it.
8
- */
9
-
10
- /// <reference lib="webworker" />
11
-
12
- import { WasmReader, initSync, loadSettings } from '@contentauth/c2pa-wasm';
13
- import {
14
- setupWorker,
15
- WorkerFunctions,
16
- WorkerResponse,
17
- } from './worker/setupWorker.js';
18
- import { createWorkerObjectMap } from './worker/workerObjectMap.js';
19
-
20
- const readerMap = createWorkerObjectMap<WasmReader>();
21
-
22
- const workerFunctions = {
23
- async initWorker(module: WebAssembly.Module, settings?: string) {
24
- initSync(module);
25
-
26
- if (settings) {
27
- loadSettings(settings);
28
- }
29
- },
30
-
31
- // Reader creation methods
32
- async reader_fromBlob(
33
- format: string,
34
- blob: Blob
35
- ): Promise<WorkerResponse<number>> {
36
- const reader = await WasmReader.fromBlob(format, blob);
37
- const readerId = readerMap.add(reader);
38
- return { data: readerId };
39
- },
40
-
41
- async reader_fromBlobFragment(
42
- format: string,
43
- init: Blob,
44
- fragment: Blob
45
- ): Promise<WorkerResponse<number>> {
46
- const reader = await WasmReader.fromBlobFragment(format, init, fragment);
47
- const readerId = readerMap.add(reader);
48
- return { data: readerId };
49
- },
50
-
51
- // Reader object methods
52
- reader_json(readerId: number): WorkerResponse<string> {
53
- const reader = readerMap.get(readerId);
54
- return { data: reader.json() };
55
- },
56
- reader_activeLabel(readerId: number): WorkerResponse<string | null> {
57
- const reader = readerMap.get(readerId);
58
- return { data: reader.activeLabel() ?? null };
59
- },
60
- reader_resourceToBuffer(
61
- readerId: number,
62
- uri: string
63
- ): WorkerResponse<ArrayBuffer> {
64
- const reader = readerMap.get(readerId);
65
- const buffer = reader.resourceToBuffer(uri);
66
- return { data: buffer, transfer: [buffer] };
67
- },
68
- reader_free(readerId: number) {
69
- const reader = readerMap.get(readerId);
70
- reader.free();
71
- readerMap.remove(readerId);
72
- },
73
- } satisfies WorkerFunctions;
74
-
75
- export type WorkerDefinition = typeof workerFunctions;
76
-
77
- setupWorker(workerFunctions);