@aelionsdk/capability 0.1.0-beta.1 → 1.1.0-rc.1

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.
package/README.md CHANGED
@@ -1,7 +1,28 @@
1
- # @aelionsdk/capability
1
+ # `@aelionsdk/capability`
2
2
 
3
- Browser media capability probing for AelionSDK
3
+ Browser media, graphics, storage and execution capability probing for
4
+ AelionSDK.
4
5
 
5
- Install with `npm install @aelionsdk/capability@next`.
6
+ ## Install
6
7
 
7
- Version 0.1.0-beta.1 is a prerelease and its API may change before the first stable release. This package is part of [AelionSDK](https://github.com/FoyonaCZY/AelionSDK); see the repository README for supported browsers, examples and deployment requirements.
8
+ ```bash
9
+ npm install @aelionsdk/capability@next
10
+ ```
11
+
12
+ `next` currently resolves to `1.1.0-rc.1`. Product applications should normally
13
+ use the capability APIs exposed by `@aelionsdk/sdk`; this package is for custom
14
+ preflight screens and execution hosts.
15
+
16
+ ## Public surface
17
+
18
+ - browser capability probes;
19
+ - capability reports and typed results;
20
+ - fail-closed gates for required features and export profiles.
21
+
22
+ A declared browser API is not proof that a codec configuration works. Run the
23
+ relevant gate with the actual dimensions, frame rate, sample rate and channel
24
+ count before starting a job.
25
+
26
+ See the [capability preflight guide](https://foyonaczy.github.io/AelionSDK/production/capability-preflight/)
27
+ and [API reference](https://foyonaczy.github.io/AelionSDK/api/aelionsdk/capability/overview/).
28
+ Licensed under MIT.
@@ -0,0 +1,62 @@
1
+ import type { Diagnostic } from '@aelionsdk/core';
2
+ /** Whether the codec operation is a decode or an encode. */
3
+ export type CodecOperation = 'decode' | 'encode';
4
+ /** Whether the codec path is video or audio. */
5
+ export type CodecClass = 'video' | 'audio';
6
+ /** The codec execution the SDK is being asked to perform. */
7
+ export interface CodecIdentity {
8
+ readonly class: CodecClass;
9
+ readonly operation: CodecOperation;
10
+ /** WebCodecs codec string, e.g. `avc1.64001f` or `mp4a.40.2`. */
11
+ readonly codec: string;
12
+ }
13
+ /**
14
+ * Contract for a software codec fallback backend (e.g. a WASM decoder).
15
+ *
16
+ * The engine negotiates through this interface but ships no backend in 1.1:
17
+ * an application registers one after the codec strategy is chosen. The
18
+ * interface intentionally mirrors the WebCodecs lifecycle so a future WASM
19
+ * adapter can implement it without reshaping the negotiation path.
20
+ */
21
+ export interface CodecFallbackProvider {
22
+ /** Stable provider id, e.g. `wasm-h264`. */
23
+ readonly id: string;
24
+ /** True when the backend is loaded and ready to accept work. */
25
+ readonly ready: boolean;
26
+ /** Whether this provider can execute the requested codec path. */
27
+ supports(identity: CodecIdentity): boolean;
28
+ }
29
+ /** Negotiated execution path for a single codec operation. */
30
+ export type CodecExecutionPath = {
31
+ readonly path: 'hardware';
32
+ } | {
33
+ readonly path: 'fallback';
34
+ readonly providerId: string;
35
+ } | {
36
+ readonly path: 'unavailable';
37
+ };
38
+ /**
39
+ * Negotiated execution path for a single codec operation plus any diagnostics
40
+ * the negotiation produced (for example a `CAPABILITY_CODEC_FALLBACK_USED`
41
+ * warning or a `CAPABILITY_CODEC_NO_BACKEND` error).
42
+ */
43
+ export type CodecExecutionDecision = CodecExecutionPath & {
44
+ readonly diagnostics: readonly Diagnostic[];
45
+ };
46
+ /**
47
+ * Negotiate how to execute a codec operation: hardware when the platform
48
+ * reports it, otherwise the first ready provider that supports the identity,
49
+ * otherwise fail closed with a diagnostic.
50
+ */
51
+ export declare function selectCodecExecution(identity: CodecIdentity, hardwareSupported: boolean, providers: readonly CodecFallbackProvider[]): CodecExecutionDecision;
52
+ /** Bounded registry of software codec fallback providers. */
53
+ export declare class CodecFallbackRegistry {
54
+ #private;
55
+ register(provider: CodecFallbackProvider): void;
56
+ unregister(id: string): void;
57
+ clear(): void;
58
+ providers(): readonly CodecFallbackProvider[];
59
+ }
60
+ /** The default process-wide registry used when no application instance is passed. */
61
+ export declare const codecFallbackRegistry: CodecFallbackRegistry;
62
+ //# sourceMappingURL=fallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../src/fallback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,4DAA4D;AAC5D,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACjD,gDAAgD;AAChD,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAE3C,6DAA6D;AAC7D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC;CAC5C;AAED,8DAA8D;AAC9D,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAC7B;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAErC;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,kBAAkB,GAAG;IACxD,QAAQ,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,CAAC;CAC7C,CAAC;AAWF;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,aAAa,EACvB,iBAAiB,EAAE,OAAO,EAC1B,SAAS,EAAE,SAAS,qBAAqB,EAAE,GAC1C,sBAAsB,CA4BxB;AAED,6DAA6D;AAC7D,qBAAa,qBAAqB;;IAGzB,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI;IAO/C,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAK5B,KAAK,IAAI,IAAI;IAIb,SAAS,IAAI,SAAS,qBAAqB,EAAE;CAGrD;AAED,qFAAqF;AACrF,eAAO,MAAM,qBAAqB,uBAA8B,CAAC"}
@@ -0,0 +1,57 @@
1
+ function decisionDiagnostic(code, message, recoverable) {
2
+ return {
3
+ code,
4
+ severity: recoverable ? 'warning' : 'error',
5
+ message,
6
+ recoverable,
7
+ };
8
+ }
9
+ /**
10
+ * Negotiate how to execute a codec operation: hardware when the platform
11
+ * reports it, otherwise the first ready provider that supports the identity,
12
+ * otherwise fail closed with a diagnostic.
13
+ */
14
+ export function selectCodecExecution(identity, hardwareSupported, providers) {
15
+ if (hardwareSupported) {
16
+ return { path: 'hardware', diagnostics: [] };
17
+ }
18
+ const provider = providers.find(candidate => candidate.ready && candidate.supports(identity));
19
+ if (provider !== undefined) {
20
+ return {
21
+ path: 'fallback',
22
+ providerId: provider.id,
23
+ diagnostics: [
24
+ decisionDiagnostic('CAPABILITY_CODEC_FALLBACK_USED', `${identity.class} ${identity.operation} for ${identity.codec} runs through the ${provider.id} software fallback`, true),
25
+ ],
26
+ };
27
+ }
28
+ return {
29
+ path: 'unavailable',
30
+ diagnostics: [
31
+ decisionDiagnostic('CAPABILITY_CODEC_NO_BACKEND', `${identity.class} ${identity.operation} for ${identity.codec} has no hardware support and no registered software fallback`, false),
32
+ ],
33
+ };
34
+ }
35
+ /** Bounded registry of software codec fallback providers. */
36
+ export class CodecFallbackRegistry {
37
+ #providers = [];
38
+ register(provider) {
39
+ if (this.#providers.some(existing => existing.id === provider.id)) {
40
+ throw new TypeError(`Codec fallback ${provider.id} is already registered`);
41
+ }
42
+ this.#providers.push(provider);
43
+ }
44
+ unregister(id) {
45
+ const index = this.#providers.findIndex(provider => provider.id === id);
46
+ if (index !== -1)
47
+ this.#providers.splice(index, 1);
48
+ }
49
+ clear() {
50
+ this.#providers.length = 0;
51
+ }
52
+ providers() {
53
+ return [...this.#providers];
54
+ }
55
+ }
56
+ /** The default process-wide registry used when no application instance is passed. */
57
+ export const codecFallbackRegistry = new CodecFallbackRegistry();
package/dist/gate.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import type { Diagnostic } from '@aelionsdk/core';
2
+ import type { CapabilityReport } from './types.js';
3
+ export interface CapabilityGateRequirements {
4
+ /** Exact capability probe ids, for example `h264-decode-1080p30`. */
5
+ readonly codecIds?: readonly string[];
6
+ readonly gpu?: 'webgl2' | 'webgpu';
7
+ readonly audioWorklet?: boolean;
8
+ readonly opfs?: boolean;
9
+ readonly sharedArrayBuffer?: boolean;
10
+ readonly transferableStreams?: boolean;
11
+ readonly secureContext?: boolean;
12
+ readonly crossOriginIsolated?: boolean;
13
+ }
14
+ export interface CapabilityGateResult {
15
+ readonly ok: boolean;
16
+ readonly diagnostics: readonly Diagnostic[];
17
+ }
18
+ /**
19
+ * Evaluates product requirements only from observed capability probes.
20
+ * User-agent and platform strings are intentionally excluded from decisions.
21
+ */
22
+ export declare function evaluateCapabilityGate(report: CapabilityReport, requirements: CapabilityGateRequirements): CapabilityGateResult;
23
+ //# sourceMappingURL=gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,0BAA0B;IACzC,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,CAAC;CAC7C;AAWD;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,gBAAgB,EACxB,YAAY,EAAE,0BAA0B,GACvC,oBAAoB,CAwDtB"}
package/dist/gate.js ADDED
@@ -0,0 +1,52 @@
1
+ function missing(code, message) {
2
+ return {
3
+ code,
4
+ severity: 'error',
5
+ message,
6
+ recoverable: true,
7
+ };
8
+ }
9
+ /**
10
+ * Evaluates product requirements only from observed capability probes.
11
+ * User-agent and platform strings are intentionally excluded from decisions.
12
+ */
13
+ export function evaluateCapabilityGate(report, requirements) {
14
+ const diagnostics = [];
15
+ const codecs = new Map(report.codecs.map(codec => [codec.id, codec]));
16
+ for (const id of requirements.codecIds ?? []) {
17
+ const codec = codecs.get(id);
18
+ if (codec?.supported !== true) {
19
+ diagnostics.push(missing('CAPABILITY_GATE_CODEC_UNSUPPORTED', codec === undefined
20
+ ? `Required codec probe ${id} is absent`
21
+ : `Required codec probe ${id} is unsupported`));
22
+ }
23
+ }
24
+ if (requirements.gpu !== undefined && !report.gpu[requirements.gpu].available) {
25
+ diagnostics.push(missing('CAPABILITY_GATE_GPU_UNSUPPORTED', `Required ${requirements.gpu} backend is unavailable`));
26
+ }
27
+ const booleanRequirements = [
28
+ ['audioWorklet', requirements.audioWorklet, report.audio.audioWorklet.available],
29
+ ['opfs', requirements.opfs, report.storage.opfs.available],
30
+ ['sharedArrayBuffer', requirements.sharedArrayBuffer, report.audio.sharedArrayBuffer.available],
31
+ [
32
+ 'transferableStreams',
33
+ requirements.transferableStreams,
34
+ report.storage.transferableStreams.available,
35
+ ],
36
+ ['secureContext', requirements.secureContext, report.environment.secureContext],
37
+ [
38
+ 'crossOriginIsolated',
39
+ requirements.crossOriginIsolated,
40
+ report.environment.crossOriginIsolated,
41
+ ],
42
+ ];
43
+ for (const [name, required, available] of booleanRequirements) {
44
+ if (required === true && !available) {
45
+ diagnostics.push(missing('CAPABILITY_GATE_REQUIREMENT_UNSUPPORTED', `Required capability ${name} is unavailable`));
46
+ }
47
+ }
48
+ return Object.freeze({
49
+ ok: diagnostics.length === 0,
50
+ diagnostics: Object.freeze(diagnostics),
51
+ });
52
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export * from './fallback.js';
2
+ export * from './gate.js';
1
3
  export * from './probe.js';
2
4
  export * from './types.js';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,4 @@
1
+ export * from './fallback.js';
2
+ export * from './gate.js';
1
3
  export * from './probe.js';
2
4
  export * from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../src/probe.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIV,sBAAsB,EACtB,gBAAgB,EAMjB,MAAM,YAAY,CAAC;AAmdpB,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,gBAAgB,CAAC,CAkD3B"}
1
+ {"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../src/probe.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIV,sBAAsB,EACtB,gBAAgB,EAOjB,MAAM,YAAY,CAAC;AAghBpB,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,gBAAgB,CAAC,CAwD3B"}
package/dist/probe.js CHANGED
@@ -1,4 +1,7 @@
1
1
  import { throwIfAborted } from '@aelionsdk/core';
2
+ function currentNavigator() {
3
+ return Reflect.get(globalThis, 'navigator');
4
+ }
2
5
  function diagnostic(code, message, recoverable, cause) {
3
6
  return {
4
7
  code,
@@ -209,24 +212,33 @@ async function probeGpu(includeAdapterDetails) {
209
212
  try {
210
213
  const canvas = typeof OffscreenCanvas === 'function'
211
214
  ? new OffscreenCanvas(1, 1)
212
- : document.createElement('canvas');
213
- const context = canvas.getContext('webgl2');
214
- webgl2 =
215
- context === null
216
- ? unsupported('CAPABILITY_WEBGL2_UNAVAILABLE', 'WebGL2 context creation failed')
217
- : supported({
218
- renderer: context instanceof WebGL2RenderingContext
219
- ? context.getParameter(context.RENDERER)
220
- : 'offscreen-webgl2',
221
- });
222
- if (context instanceof WebGL2RenderingContext) {
223
- context.getExtension('WEBGL_lose_context')?.loseContext();
215
+ : typeof document === 'object'
216
+ ? document.createElement('canvas')
217
+ : undefined;
218
+ if (canvas === undefined) {
219
+ webgl2 = unsupported('CAPABILITY_WEBGL2_UNAVAILABLE', 'WebGL2 is unavailable');
220
+ }
221
+ else {
222
+ const context = canvas.getContext('webgl2');
223
+ webgl2 =
224
+ context === null
225
+ ? unsupported('CAPABILITY_WEBGL2_UNAVAILABLE', 'WebGL2 context creation failed')
226
+ : supported({
227
+ renderer: typeof WebGL2RenderingContext === 'function' &&
228
+ context instanceof WebGL2RenderingContext
229
+ ? context.getParameter(context.RENDERER)
230
+ : 'offscreen-webgl2',
231
+ });
232
+ if (typeof WebGL2RenderingContext === 'function' &&
233
+ context instanceof WebGL2RenderingContext) {
234
+ context.getExtension('WEBGL_lose_context')?.loseContext();
235
+ }
224
236
  }
225
237
  }
226
238
  catch (cause) {
227
239
  webgl2 = failed('CAPABILITY_WEBGL2_PROBE_FAILED', 'WebGL2 probe failed', cause);
228
240
  }
229
- const gpu = navigator.gpu;
241
+ const gpu = currentNavigator()?.gpu;
230
242
  if (gpu === undefined) {
231
243
  return {
232
244
  worker,
@@ -294,7 +306,10 @@ function probeAudio() {
294
306
  return { audioContext, audioWorklet, sharedArrayBuffer };
295
307
  }
296
308
  function probeStorage() {
297
- const storage = Reflect.get(navigator, 'storage');
309
+ const navigator = currentNavigator();
310
+ const storage = navigator === undefined
311
+ ? undefined
312
+ : Reflect.get(navigator, 'storage');
298
313
  const opfs = storage !== undefined && typeof storage.getDirectory === 'function'
299
314
  ? supported()
300
315
  : unsupported('CAPABILITY_OPFS_UNAVAILABLE', 'OPFS is unavailable');
@@ -307,19 +322,23 @@ function probeStorage() {
307
322
  return { opfs, fileSystemAccess, transferableStreams };
308
323
  }
309
324
  function environment() {
310
- const navigatorWithMemory = navigator;
311
- const userAgentData = Reflect.get(navigator, 'userAgentData');
325
+ const navigator = currentNavigator();
326
+ const userAgentData = navigator === undefined
327
+ ? undefined
328
+ : Reflect.get(navigator, 'userAgentData');
312
329
  return {
313
- userAgent: navigator.userAgent,
330
+ userAgent: navigator?.userAgent ?? 'unavailable',
314
331
  platform: userAgentData?.platform ?? 'unknown',
315
- language: navigator.language,
316
- hardwareConcurrency: Number.isFinite(navigator.hardwareConcurrency) && navigator.hardwareConcurrency > 0
332
+ language: navigator?.language ?? 'unknown',
333
+ hardwareConcurrency: navigator !== undefined &&
334
+ Number.isFinite(navigator.hardwareConcurrency) &&
335
+ navigator.hardwareConcurrency > 0
317
336
  ? navigator.hardwareConcurrency
318
337
  : null,
319
- deviceMemoryGiB: navigatorWithMemory.deviceMemory ?? null,
320
- crossOriginIsolated: globalThis.crossOriginIsolated,
321
- secureContext: globalThis.isSecureContext,
322
- origin: globalThis.location.origin,
338
+ deviceMemoryGiB: navigator?.deviceMemory ?? null,
339
+ crossOriginIsolated: Boolean(globalThis.crossOriginIsolated),
340
+ secureContext: Boolean(globalThis.isSecureContext),
341
+ origin: typeof globalThis.location === 'undefined' ? 'unknown' : globalThis.location.origin,
323
342
  };
324
343
  }
325
344
  function probeColor() {
@@ -343,13 +362,48 @@ function probeColor() {
343
362
  // from display hardware. HDR/10-bit projects fail closed until the renderer
344
363
  // has a float/10-bit surface and matching encoder path.
345
364
  localExecution: {
346
- workingColorSpaces: ['srgb-linear', 'display-p3-linear', 'rec2020-linear'],
347
- transferFunctions: ['srgb', 'gamma22'],
365
+ workingColorSpaces: ['srgb-linear'],
366
+ colorPrimaries: ['bt709'],
367
+ transferFunctions: ['srgb'],
368
+ matrixCoefficients: ['rgb'],
369
+ colorRanges: ['full'],
370
+ chromaSubsamplings: ['rgb', '4:4:4'],
371
+ alphaModes: ['opaque', 'premultiplied'],
372
+ toneMappings: ['none'],
348
373
  bitDepths: [8],
349
374
  hdrPresentation: false,
350
375
  },
351
376
  };
352
377
  }
378
+ async function probeImageFormat(format) {
379
+ const mime = `image/${format}`;
380
+ // ImageDecoder is typed as always-present by the DOM lib, but runtime
381
+ // availability varies; read it through globalThis so the guard is real.
382
+ const imageDecoder = globalThis.ImageDecoder;
383
+ if (imageDecoder === undefined) {
384
+ // No decoder-capability API: report unknown rather than guessing. The
385
+ // createImageBitmap path used by decodeStillImage may still support the
386
+ // format, so claiming unsupported here would be wrong.
387
+ return failed('CAPABILITY_IMAGE_FORMAT_UNKNOWN', `ImageDecoder is unavailable; ${mime} decode support is unknown`, undefined);
388
+ }
389
+ try {
390
+ return (await imageDecoder.isTypeSupported(mime))
391
+ ? supported({ mime })
392
+ : unsupported('CAPABILITY_IMAGE_FORMAT_UNSUPPORTED', `${mime} decode is unsupported`);
393
+ }
394
+ catch (cause) {
395
+ return failed('CAPABILITY_IMAGE_FORMAT_PROBE_FAILED', `Failed to probe ${mime}`, cause);
396
+ }
397
+ }
398
+ async function probeImages() {
399
+ const [avif, jpeg, png, webp] = await Promise.all([
400
+ probeImageFormat('avif'),
401
+ probeImageFormat('jpeg'),
402
+ probeImageFormat('png'),
403
+ probeImageFormat('webp'),
404
+ ]);
405
+ return { avif, jpeg, png, webp };
406
+ }
353
407
  function tier(codecs, gpu, audio) {
354
408
  const hasVideoDecode = codecs.some(codec => codec.kind === 'video-decoder' && codec.supported);
355
409
  if (hasVideoDecode &&
@@ -374,6 +428,7 @@ export async function probeCapabilities(options = {}) {
374
428
  const audio = probeAudio();
375
429
  const storage = probeStorage();
376
430
  const color = probeColor();
431
+ const images = await probeImages();
377
432
  const diagnostics = [
378
433
  ...codecs.flatMap(codec => codec.diagnostics),
379
434
  ...(gpu.webgpu.diagnostics ?? []),
@@ -388,6 +443,10 @@ export async function probeCapabilities(options = {}) {
388
443
  ...(storage.transferableStreams.diagnostics ?? []),
389
444
  ...(color.displayP3Gamut.diagnostics ?? []),
390
445
  ...(color.highDynamicRange.diagnostics ?? []),
446
+ ...(images.avif.diagnostics ?? []),
447
+ ...(images.jpeg.diagnostics ?? []),
448
+ ...(images.png.diagnostics ?? []),
449
+ ...(images.webp.diagnostics ?? []),
391
450
  ];
392
451
  return {
393
452
  schemaVersion: '1.0.0',
@@ -399,6 +458,7 @@ export async function probeCapabilities(options = {}) {
399
458
  audio,
400
459
  storage,
401
460
  color,
461
+ images,
402
462
  wasm: typeof WebAssembly === 'object'
403
463
  ? { available: supported() }
404
464
  : {
package/dist/types.d.ts CHANGED
@@ -35,13 +35,36 @@ export interface StorageCapability {
35
35
  readonly fileSystemAccess: CapabilityProbe;
36
36
  readonly transferableStreams: CapabilityProbe;
37
37
  }
38
+ /**
39
+ * Still-image decode capability for the formats the SDK accepts through
40
+ * {@link ImageDecoder}/`createImageBitmap`. Probes report what the platform can
41
+ * deterministically signal; a probe with `status: 'unknown'` means the platform
42
+ * exposes no decoder-capability API (e.g. no `ImageDecoder`), not that the
43
+ * format is unsupported.
44
+ */
45
+ export interface ImageFormatCapability {
46
+ /** AVIF still decode. */
47
+ readonly avif: CapabilityProbe;
48
+ /** JPEG still decode. */
49
+ readonly jpeg: CapabilityProbe;
50
+ /** PNG still decode. */
51
+ readonly png: CapabilityProbe;
52
+ /** WebP still decode. */
53
+ readonly webp: CapabilityProbe;
54
+ }
38
55
  export interface ColorCapability {
39
56
  readonly displayP3Gamut: CapabilityProbe;
40
57
  readonly highDynamicRange: CapabilityProbe;
41
58
  /** The color contract implemented by the current local renderer/exporter. */
42
59
  readonly localExecution: {
43
60
  readonly workingColorSpaces: readonly string[];
61
+ readonly colorPrimaries: readonly ('bt709' | 'display-p3' | 'bt2020')[];
44
62
  readonly transferFunctions: readonly ('srgb' | 'gamma22' | 'pq' | 'hlg')[];
63
+ readonly matrixCoefficients: readonly ('rgb' | 'bt709' | 'bt2020-ncl')[];
64
+ readonly colorRanges: readonly ('full' | 'limited')[];
65
+ readonly chromaSubsamplings: readonly ('rgb' | '4:4:4' | '4:2:2' | '4:2:0')[];
66
+ readonly alphaModes: readonly ('opaque' | 'premultiplied')[];
67
+ readonly toneMappings: readonly ('none' | 'bt2390' | 'reinhard')[];
45
68
  readonly bitDepths: readonly (8 | 10)[];
46
69
  readonly hdrPresentation: boolean;
47
70
  };
@@ -66,6 +89,7 @@ export interface CapabilityReport {
66
89
  readonly audio: AudioCapability;
67
90
  readonly storage: StorageCapability;
68
91
  readonly color: ColorCapability;
92
+ readonly images: ImageFormatCapability;
69
93
  readonly wasm: {
70
94
  readonly available: CapabilityProbe;
71
95
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aelionsdk/capability",
3
- "version": "0.1.0-beta.1",
3
+ "version": "1.1.0-rc.1",
4
4
  "description": "Browser media capability probing for AelionSDK",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,14 +27,14 @@
27
27
  "!dist/.tsbuildinfo"
28
28
  ],
29
29
  "engines": {
30
- "node": ">=20.19"
30
+ "node": ">=24 <25"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public",
34
34
  "provenance": true
35
35
  },
36
36
  "dependencies": {
37
- "@aelionsdk/core": "0.1.0-beta.1"
37
+ "@aelionsdk/core": "1.1.0-rc.1"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "tsc -b",