@forgeax/engine-codec 0.1.4 → 0.1.7

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.
@@ -5,16 +5,10 @@ import {
5
5
  _basisTranscoderInitCount,
6
6
  _setBasisTranscoderImporter,
7
7
  initBasisTranscoder,
8
- transcodeBasis,
9
8
  transcodeKtx2,
10
9
  } from '../basis-transcoder.js';
11
- import type { Ktx2Parsed } from '../ktx2.js';
12
10
  import { parseKtx2 } from '../ktx2.js';
13
- import type {
14
- BasisEncoderModule,
15
- BasisModuleFactory,
16
- BasisTranscoderModule,
17
- } from '../wasm/basis-types.js';
11
+ import type { BasisEncoderModule, BasisModuleFactory } from '../wasm/basis-types.js';
18
12
 
19
13
  /**
20
14
  * basis-transcoder glue unit tests (w14).
@@ -117,351 +111,6 @@ describe('initBasisTranscoder — lazy-init singleton (D-10, w14)', () => {
117
111
  });
118
112
  });
119
113
 
120
- const TEST_TARGET = 'bc7-rgba-unorm' as const;
121
- const TEST_SENTINEL = 'm112-controlled-wasm-throw';
122
- const TEST_CLOSE_SENTINEL = 'm119-controlled-close-throw';
123
- const TEST_FORMAT = { cTFBC7_RGBA: { value: 7 } };
124
-
125
- function fakeParsed(rawBytes: Uint8Array): Ktx2Parsed {
126
- return { rawBytes, dfd: { colorModel: 166 } } as unknown as Ktx2Parsed;
127
- }
128
-
129
- function createKtx2OperationModule() {
130
- let shouldThrow = true;
131
- let shouldCloseThrow = false;
132
- const state = {
133
- closeCount: 0,
134
- inputBytes: [] as Uint8Array[],
135
- targetEnums: [] as number[],
136
- transcodeLevels: [] as number[],
137
- };
138
-
139
- class FakeKtx2File {
140
- constructor(bytes: Uint8Array) {
141
- state.inputBytes.push(bytes);
142
- }
143
-
144
- close(): void {
145
- state.closeCount++;
146
- if (shouldCloseThrow) throw TEST_CLOSE_SENTINEL;
147
- }
148
-
149
- isValid(): boolean {
150
- return true;
151
- }
152
-
153
- startTranscoding(): number {
154
- return 1;
155
- }
156
-
157
- getLevels(): number {
158
- return 2;
159
- }
160
-
161
- getImageLevelInfo(
162
- level: number,
163
- _layer: number,
164
- _face: number,
165
- ): { origWidth: number; origHeight: number } {
166
- return level === 0 ? { origWidth: 8, origHeight: 4 } : { origWidth: 4, origHeight: 2 };
167
- }
168
-
169
- getImageTranscodedSizeInBytes(
170
- level: number,
171
- _layer: number,
172
- _face: number,
173
- format: number,
174
- ): number {
175
- state.targetEnums.push(format);
176
- return level === 0 ? 16 : 4;
177
- }
178
-
179
- transcodeImage(
180
- dst: Uint8Array,
181
- level: number,
182
- _layer: number,
183
- _face: number,
184
- _format: number,
185
- _getAlphaForOpaqueFormats: number,
186
- _channel0: number,
187
- _channel1: number,
188
- ): number {
189
- state.transcodeLevels.push(level);
190
- if (shouldThrow && level === 1) throw TEST_SENTINEL;
191
- dst.fill(level + 1);
192
- return 1;
193
- }
194
-
195
- getWidth(): number {
196
- return 8;
197
- }
198
-
199
- getHeight(): number {
200
- return 4;
201
- }
202
- }
203
-
204
- const module = {
205
- transcoder_texture_format: TEST_FORMAT,
206
- KTX2File: FakeKtx2File,
207
- } as unknown as BasisTranscoderModule;
208
- return {
209
- module,
210
- state,
211
- repair(): void {
212
- shouldThrow = false;
213
- },
214
- refuseClose(): void {
215
- shouldCloseThrow = true;
216
- },
217
- repairClose(): void {
218
- shouldCloseThrow = false;
219
- },
220
- };
221
- }
222
-
223
- function createBasisOperationModule() {
224
- let shouldThrow = true;
225
- let shouldCloseThrow = false;
226
- const state = {
227
- closeCount: 0,
228
- inputBytes: [] as Uint8Array[],
229
- targetEnums: [] as number[],
230
- transcodeLevels: [] as number[],
231
- };
232
-
233
- class FakeBasisFile {
234
- constructor(bytes: Uint8Array) {
235
- state.inputBytes.push(bytes);
236
- }
237
-
238
- close(): void {
239
- state.closeCount++;
240
- if (shouldCloseThrow) throw TEST_CLOSE_SENTINEL;
241
- }
242
-
243
- getNumImages(): number {
244
- return 1;
245
- }
246
-
247
- getNumLevels(_imageIndex: number): number {
248
- return 2;
249
- }
250
-
251
- getImageWidth(_imageIndex: number, level: number): number {
252
- return level === 0 ? 8 : 4;
253
- }
254
-
255
- getImageHeight(_imageIndex: number, level: number): number {
256
- return level === 0 ? 4 : 2;
257
- }
258
-
259
- startTranscoding(): number {
260
- return 1;
261
- }
262
-
263
- getImageTranscodedSizeInBytes(_imageIndex: number, level: number, format: number): number {
264
- state.targetEnums.push(format);
265
- return level === 0 ? 16 : 4;
266
- }
267
-
268
- transcodeImage(
269
- dst: Uint8Array,
270
- _imageIndex: number,
271
- level: number,
272
- _format: number,
273
- _decodeFlags: number,
274
- ): number {
275
- state.transcodeLevels.push(level);
276
- if (shouldThrow && level === 1) throw TEST_SENTINEL;
277
- dst.fill(level + 1);
278
- return 1;
279
- }
280
- }
281
-
282
- const module = {
283
- transcoder_texture_format: TEST_FORMAT,
284
- KTX2File: class {},
285
- BasisFile: FakeBasisFile,
286
- } as unknown as BasisTranscoderModule;
287
- return {
288
- module,
289
- state,
290
- repair(): void {
291
- shouldThrow = false;
292
- },
293
- refuseClose(): void {
294
- shouldCloseThrow = true;
295
- },
296
- repairClose(): void {
297
- shouldCloseThrow = false;
298
- },
299
- };
300
- }
301
-
302
- function createConstructorThrowingModule(): BasisTranscoderModule {
303
- class ThrowingKtx2File {
304
- constructor(_bytes: Uint8Array) {
305
- throw TEST_SENTINEL;
306
- }
307
- }
308
-
309
- class ThrowingBasisFile {
310
- constructor(_bytes: Uint8Array) {
311
- throw TEST_SENTINEL;
312
- }
313
- }
314
-
315
- return {
316
- transcoder_texture_format: TEST_FORMAT,
317
- KTX2File: ThrowingKtx2File,
318
- BasisFile: ThrowingBasisFile,
319
- } as unknown as BasisTranscoderModule;
320
- }
321
-
322
- describe('transcode throw containment and same-module retry (M112)', () => {
323
- it('contains KTX2 operation throws, closes once, and retries the same input/module deterministically', async () => {
324
- const rawBytes = new Uint8Array([1, 2, 3]);
325
- const parsed = fakeParsed(rawBytes);
326
- const fixture = createKtx2OperationModule();
327
- let importerCalls = 0;
328
- _setBasisTranscoderImporter(() => {
329
- importerCalls++;
330
- return Promise.resolve(fixture.module);
331
- });
332
-
333
- fixture.refuseClose();
334
- const failed = await transcodeKtx2(parsed, TEST_TARGET);
335
- expect(failed.ok).toBe(false);
336
- if (failed.ok) throw new Error('expected operation throw to return a CodecError');
337
- expect(failed.error.code).toBe('transcode-failed');
338
- expect(failed.error.detail).toEqual({
339
- sourceFormat: 'dfd-model-166',
340
- targetFormat: TEST_TARGET,
341
- });
342
- expect(JSON.stringify(failed)).not.toContain(TEST_SENTINEL);
343
- expect(JSON.stringify(failed)).not.toContain(TEST_CLOSE_SENTINEL);
344
- expect(fixture.state.closeCount).toBe(1);
345
- expect(fixture.state.transcodeLevels).toEqual([0, 1]);
346
-
347
- fixture.repair();
348
- const cleanupFailed = await transcodeKtx2(parsed, TEST_TARGET);
349
- expect(cleanupFailed.ok).toBe(false);
350
- if (cleanupFailed.ok) throw new Error('expected close throw to return a CodecError');
351
- expect(cleanupFailed.error.code).toBe('transcode-failed');
352
- expect(cleanupFailed.error.detail).toEqual({
353
- sourceFormat: 'dfd-model-166',
354
- targetFormat: TEST_TARGET,
355
- });
356
- expect(JSON.stringify(cleanupFailed)).not.toContain(TEST_CLOSE_SENTINEL);
357
- expect(fixture.state.closeCount).toBe(2);
358
-
359
- fixture.repairClose();
360
- const repaired = await transcodeKtx2(parsed, TEST_TARGET);
361
- expect(repaired.ok).toBe(true);
362
- if (!repaired.ok) throw new Error('expected repaired KTX2 transcode to succeed');
363
- expect(repaired.value.format).toBe(TEST_TARGET);
364
- expect(repaired.value.width).toBe(8);
365
- expect(repaired.value.height).toBe(4);
366
- expect(
367
- repaired.value.mips.map(({ level, width, height, data }) => ({
368
- level,
369
- width,
370
- height,
371
- data: [...data],
372
- })),
373
- ).toEqual([
374
- { level: 0, width: 8, height: 4, data: new Array(16).fill(1) },
375
- { level: 1, width: 4, height: 2, data: new Array(4).fill(2) },
376
- ]);
377
-
378
- const third = await transcodeKtx2(parsed, TEST_TARGET);
379
- expect(third).toEqual(repaired);
380
- expect(importerCalls).toBe(1);
381
- expect(_basisTranscoderInitCount()).toBe(1);
382
- expect(fixture.state.inputBytes).toHaveLength(4);
383
- expect(fixture.state.inputBytes.every((bytes) => bytes === rawBytes)).toBe(true);
384
- expect(fixture.state.targetEnums).toEqual([7, 7, 7, 7, 7, 7, 7, 7]);
385
- expect(fixture.state.closeCount).toBe(4);
386
- });
387
-
388
- it('contains raw Basis operation throws and retries the same bytes/module deterministically', async () => {
389
- const rawBytes = new Uint8Array([4, 5, 6]);
390
- const fixture = createBasisOperationModule();
391
- let importerCalls = 0;
392
- _setBasisTranscoderImporter(() => {
393
- importerCalls++;
394
- return Promise.resolve(fixture.module);
395
- });
396
-
397
- fixture.refuseClose();
398
- const failed = await transcodeBasis(rawBytes, TEST_TARGET);
399
- expect(failed.ok).toBe(false);
400
- if (failed.ok) throw new Error('expected raw Basis operation throw to return a CodecError');
401
- expect(failed.error.code).toBe('transcode-failed');
402
- expect(failed.error.detail).toEqual({ sourceFormat: 'raw-basis', targetFormat: TEST_TARGET });
403
- expect(JSON.stringify(failed)).not.toContain(TEST_SENTINEL);
404
- expect(JSON.stringify(failed)).not.toContain(TEST_CLOSE_SENTINEL);
405
- expect(fixture.state.closeCount).toBe(1);
406
- expect(fixture.state.transcodeLevels).toEqual([0, 1]);
407
-
408
- fixture.repair();
409
- const cleanupFailed = await transcodeBasis(rawBytes, TEST_TARGET);
410
- expect(cleanupFailed.ok).toBe(false);
411
- if (cleanupFailed.ok) throw new Error('expected close throw to return a CodecError');
412
- expect(cleanupFailed.error.code).toBe('transcode-failed');
413
- expect(cleanupFailed.error.detail).toEqual({
414
- sourceFormat: 'raw-basis',
415
- targetFormat: TEST_TARGET,
416
- });
417
- expect(JSON.stringify(cleanupFailed)).not.toContain(TEST_CLOSE_SENTINEL);
418
- expect(fixture.state.closeCount).toBe(2);
419
-
420
- fixture.repairClose();
421
- const repaired = await transcodeBasis(rawBytes, TEST_TARGET);
422
- expect(repaired.ok).toBe(true);
423
- if (!repaired.ok) throw new Error('expected repaired raw Basis transcode to succeed');
424
- expect(
425
- repaired.value.mips.map(({ level, width, height, data }) => ({
426
- level,
427
- width,
428
- height,
429
- data: [...data],
430
- })),
431
- ).toEqual([
432
- { level: 0, width: 8, height: 4, data: new Array(16).fill(1) },
433
- { level: 1, width: 4, height: 2, data: new Array(4).fill(2) },
434
- ]);
435
-
436
- const third = await transcodeBasis(rawBytes, TEST_TARGET);
437
- expect(third).toEqual(repaired);
438
- expect(importerCalls).toBe(1);
439
- expect(_basisTranscoderInitCount()).toBe(1);
440
- expect(fixture.state.inputBytes).toHaveLength(4);
441
- expect(fixture.state.inputBytes.every((bytes) => bytes === rawBytes)).toBe(true);
442
- expect(fixture.state.targetEnums).toEqual([7, 7, 7, 7, 7, 7, 7, 7]);
443
- expect(fixture.state.closeCount).toBe(4);
444
- });
445
-
446
- it('contains KTX2 and raw Basis constructor throws without attempting close', async () => {
447
- const module = createConstructorThrowingModule();
448
- _setBasisTranscoderImporter(() => Promise.resolve(module));
449
-
450
- const ktx2 = await transcodeKtx2(fakeParsed(new Uint8Array([7])), TEST_TARGET);
451
- expect(ktx2.ok).toBe(false);
452
- if (ktx2.ok) throw new Error('expected KTX2 constructor throw to return a CodecError');
453
- expect(ktx2.error.code).toBe('transcode-failed');
454
- expect(ktx2.error.detail).toEqual({ sourceFormat: 'dfd-model-166', targetFormat: TEST_TARGET });
455
-
456
- const basis = await transcodeBasis(new Uint8Array([8]), TEST_TARGET);
457
- expect(basis.ok).toBe(false);
458
- if (basis.ok) throw new Error('expected Basis constructor throw to return a CodecError');
459
- expect(basis.error.code).toBe('transcode-failed');
460
- expect(basis.error.detail).toEqual({ sourceFormat: 'raw-basis', targetFormat: TEST_TARGET });
461
- expect(JSON.stringify({ ktx2, basis })).not.toContain(TEST_SENTINEL);
462
- });
463
- });
464
-
465
114
  describe.skipIf(!pkgBuilt)('transcodeKtx2 — real UASTC -> BC7 (w14)', () => {
466
115
  let uastcKtx2: Uint8Array;
467
116
 
@@ -18,12 +18,7 @@
18
18
  import type { CodecResult } from './errors.js';
19
19
  import { codecError } from './errors.js';
20
20
  import type { Ktx2Parsed } from './ktx2.js';
21
- import type {
22
- BasisFile,
23
- BasisModuleFactory,
24
- BasisTranscoderModule,
25
- KTX2File,
26
- } from './wasm/basis-types.js';
21
+ import type { BasisFile, BasisModuleFactory, BasisTranscoderModule } from './wasm/basis-types.js';
27
22
 
28
23
  /** How the transcoder WASM module is loaded. Overridable in tests. */
29
24
  type TranscoderImporter = () => Promise<BasisTranscoderModule>;
@@ -217,78 +212,49 @@ export async function transcodeKtx2(
217
212
  });
218
213
  }
219
214
 
220
- let file: KTX2File;
221
- try {
222
- file = new mod.KTX2File(parsed.rawBytes);
223
- } catch {
224
- return codecError('transcode-failed', {
225
- sourceFormat: `dfd-model-${parsed.dfd?.colorModel ?? 'unknown'}`,
226
- targetFormat,
227
- });
228
- }
229
- let result: CodecResult<TranscodedTexture> = codecError('transcode-failed', {
230
- sourceFormat: `dfd-model-${parsed.dfd?.colorModel ?? 'unknown'}`,
231
- targetFormat,
232
- });
215
+ const file = new mod.KTX2File(parsed.rawBytes);
233
216
  try {
234
217
  if (!file.isValid()) {
235
- result = codecError('transcode-failed', {
218
+ return codecError('transcode-failed', {
236
219
  sourceFormat: 'invalid-ktx2-file',
237
220
  targetFormat,
238
221
  });
239
- } else if (file.startTranscoding() === 0) {
240
- result = codecError('transcode-failed', {
222
+ }
223
+ if (file.startTranscoding() === 0) {
224
+ return codecError('transcode-failed', {
241
225
  sourceFormat: 'start-transcoding-failed',
242
226
  targetFormat,
243
227
  });
244
- } else {
245
- const levels = file.getLevels();
246
- const mips: TranscodedMip[] = [];
247
- let failed = false;
248
- for (let level = 0; level < levels; level++) {
249
- const info = file.getImageLevelInfo(level, 0, 0);
250
- const size = file.getImageTranscodedSizeInBytes(level, 0, 0, targetEnum);
251
- const dst = new Uint8Array(size);
252
- const ok = file.transcodeImage(dst, level, 0, 0, targetEnum, 0, -1, -1);
253
- if (ok === 0) {
254
- result = codecError('transcode-failed', {
255
- sourceFormat: `dfd-model-${parsed.dfd?.colorModel ?? 'unknown'}-level-${level}`,
256
- targetFormat,
257
- });
258
- failed = true;
259
- break;
260
- }
261
- mips.push({ level, width: info.origWidth, height: info.origHeight, data: dst });
262
- }
263
- if (!failed) {
264
- result = {
265
- ok: true,
266
- value: {
267
- format: targetFormat,
268
- width: file.getWidth(),
269
- height: file.getHeight(),
270
- mips,
271
- },
272
- };
228
+ }
229
+
230
+ const levels = file.getLevels();
231
+ const mips: TranscodedMip[] = [];
232
+ for (let level = 0; level < levels; level++) {
233
+ const info = file.getImageLevelInfo(level, 0, 0);
234
+ const size = file.getImageTranscodedSizeInBytes(level, 0, 0, targetEnum);
235
+ const dst = new Uint8Array(size);
236
+ const ok = file.transcodeImage(dst, level, 0, 0, targetEnum, 0, -1, -1);
237
+ if (ok === 0) {
238
+ return codecError('transcode-failed', {
239
+ sourceFormat: `dfd-model-${parsed.dfd?.colorModel ?? 'unknown'}-level-${level}`,
240
+ targetFormat,
241
+ });
273
242
  }
243
+ mips.push({ level, width: info.origWidth, height: info.origHeight, data: dst });
274
244
  }
275
- } catch {
276
- result = codecError('transcode-failed', {
277
- sourceFormat: `dfd-model-${parsed.dfd?.colorModel ?? 'unknown'}`,
278
- targetFormat,
279
- });
280
- }
281
- try {
245
+
246
+ return {
247
+ ok: true,
248
+ value: {
249
+ format: targetFormat,
250
+ width: file.getWidth(),
251
+ height: file.getHeight(),
252
+ mips,
253
+ },
254
+ };
255
+ } finally {
282
256
  file.close();
283
- } catch {
284
- if (result.ok) {
285
- result = codecError('transcode-failed', {
286
- sourceFormat: `dfd-model-${parsed.dfd?.colorModel ?? 'unknown'}`,
287
- targetFormat,
288
- });
289
- }
290
257
  }
291
- return result;
292
258
  }
293
259
 
294
260
  /** Transcode a raw `.basis` file; source color remains a Meta author fact. */
@@ -309,72 +275,41 @@ export async function transcodeBasis(
309
275
  targetFormat,
310
276
  });
311
277
  }
312
- let file: BasisFile;
313
- try {
314
- file = new mod.BasisFile(bytes);
315
- } catch {
316
- return codecError('transcode-failed', {
317
- sourceFormat: 'raw-basis',
318
- targetFormat,
319
- });
320
- }
321
- let result: CodecResult<TranscodedBasisTexture> = codecError('transcode-failed', {
322
- sourceFormat: 'raw-basis',
323
- targetFormat,
324
- });
278
+ const file = new mod.BasisFile(bytes);
325
279
  try {
326
280
  if (file.getNumImages() <= 0 || file.getNumLevels(0) <= 0) {
327
- result = codecError('transcode-failed', { sourceFormat: 'invalid-basis-file', targetFormat });
328
- } else if (file.startTranscoding() === 0) {
329
- result = codecError('transcode-failed', {
281
+ return codecError('transcode-failed', { sourceFormat: 'invalid-basis-file', targetFormat });
282
+ }
283
+ if (file.startTranscoding() === 0) {
284
+ return codecError('transcode-failed', {
330
285
  sourceFormat: 'start-transcoding-failed',
331
286
  targetFormat,
332
287
  });
333
- } else {
334
- const mips: TranscodedMip[] = [];
335
- let failed = false;
336
- for (let level = 0; level < file.getNumLevels(0); level++) {
337
- const width = file.getImageWidth(0, level);
338
- const height = file.getImageHeight(0, level);
339
- const size = file.getImageTranscodedSizeInBytes(0, level, targetEnum);
340
- const data = new Uint8Array(size);
341
- if (file.transcodeImage(data, 0, level, targetEnum, 0) === 0) {
342
- result = codecError('transcode-failed', {
343
- sourceFormat: `raw-basis-level-${level}`,
344
- targetFormat,
345
- });
346
- failed = true;
347
- break;
348
- }
349
- mips.push({ level, width, height, data });
350
- }
351
- if (!failed) {
352
- result = {
353
- ok: true,
354
- value: {
355
- format: targetFormat,
356
- width: mips[0]?.width ?? 0,
357
- height: mips[0]?.height ?? 0,
358
- mips,
359
- },
360
- };
288
+ }
289
+ const mips: TranscodedMip[] = [];
290
+ for (let level = 0; level < file.getNumLevels(0); level++) {
291
+ const width = file.getImageWidth(0, level);
292
+ const height = file.getImageHeight(0, level);
293
+ const size = file.getImageTranscodedSizeInBytes(0, level, targetEnum);
294
+ const data = new Uint8Array(size);
295
+ if (file.transcodeImage(data, 0, level, targetEnum, 0) === 0) {
296
+ return codecError('transcode-failed', {
297
+ sourceFormat: `raw-basis-level-${level}`,
298
+ targetFormat,
299
+ });
361
300
  }
301
+ mips.push({ level, width, height, data });
362
302
  }
363
- } catch {
364
- result = codecError('transcode-failed', {
365
- sourceFormat: 'raw-basis',
366
- targetFormat,
367
- });
368
- }
369
- try {
303
+ return {
304
+ ok: true,
305
+ value: {
306
+ format: targetFormat,
307
+ width: mips[0]?.width ?? 0,
308
+ height: mips[0]?.height ?? 0,
309
+ mips,
310
+ },
311
+ };
312
+ } finally {
370
313
  file.close();
371
- } catch {
372
- if (result.ok) {
373
- result = codecError('transcode-failed', {
374
- sourceFormat: 'raw-basis',
375
- targetFormat,
376
- });
377
- }
378
314
  }
379
- return result;
380
315
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=basis-encode-mode-owner.test-d.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"basis-encode-mode-owner.test-d.d.ts","sourceRoot":"","sources":["../../src/__tests__/basis-encode-mode-owner.test-d.ts"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=runtime-validation.unit.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"runtime-validation.unit.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/runtime-validation.unit.test.ts"],"names":[],"mappings":""}
@@ -1,39 +0,0 @@
1
- /// <reference types="node" />
2
-
3
- import { readFileSync } from 'node:fs';
4
- import { describe, expect, expectTypeOf, it } from 'vitest';
5
- import type { BasisEncodeMode } from '../encode/basis-encode.js';
6
- import type { TranscodeModel } from '../transcode.js';
7
-
8
- const basisEncodeSource = readFileSync(
9
- new URL('../encode/basis-encode.ts', import.meta.url),
10
- 'utf8',
11
- );
12
-
13
- describe('BasisEncodeMode owner', () => {
14
- it('keeps the encode and transcode vocabularies bilaterally equal', () => {
15
- expectTypeOf<BasisEncodeMode>().toEqualTypeOf<TranscodeModel>();
16
- expectTypeOf<TranscodeModel>().toEqualTypeOf<BasisEncodeMode>();
17
-
18
- const acceptsEncodeMode = (mode: BasisEncodeMode): BasisEncodeMode => mode;
19
- const acceptsTranscodeModel = (model: TranscodeModel): TranscodeModel => model;
20
- acceptsEncodeMode('etc1s');
21
- acceptsEncodeMode('uastc-ldr');
22
- acceptsEncodeMode('uastc-hdr');
23
- acceptsTranscodeModel('etc1s');
24
- acceptsTranscodeModel('uastc-ldr');
25
- acceptsTranscodeModel('uastc-hdr');
26
- // @ts-expect-error unknown Basis encode modes remain outside the closed owner union.
27
- acceptsEncodeMode('basis-mode-not-real');
28
- // @ts-expect-error unknown transcode models remain outside the closed owner union.
29
- acceptsTranscodeModel('basis-mode-not-real');
30
- });
31
-
32
- it('keeps BasisEncodeMode derived from the public transcode owner', () => {
33
- expect(basisEncodeSource).toContain("import type { TranscodeModel } from '../transcode.js';");
34
- expect(basisEncodeSource).toContain('export type BasisEncodeMode = TranscodeModel;');
35
- expect(basisEncodeSource).not.toContain(
36
- "export type BasisEncodeMode = 'etc1s' | 'uastc-ldr' | 'uastc-hdr';",
37
- );
38
- });
39
- });
@@ -1,19 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { KTX2_IDENTIFIER, parseKtx2 } from '../index';
3
-
4
- describe('codec runtime validation owner', () => {
5
- it('rejects a non-KTX2 artifact with the codec-owned error', async () => {
6
- const result = await parseKtx2(new Uint8Array([0, 1, 2, 3]));
7
-
8
- expect(result.ok).toBe(false);
9
- if (result.ok) return;
10
- expect(result.error.code).toBe('ktx2-parse-failed');
11
- expect(result.error.expected).toContain('valid compressed data');
12
- });
13
-
14
- it('keeps the KTX2 signature as the codec validation boundary', () => {
15
- expect(KTX2_IDENTIFIER).toEqual(
16
- new Uint8Array([0xab, 0x4b, 0x54, 0x58, 0x20, 0x32, 0x30, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a]),
17
- );
18
- });
19
- });