diff --git a/src/core/GltfLoader.ts b/src/core/GltfLoader.ts index acf072f..5516ad9 100644 --- a/src/core/GltfLoader.ts +++ b/src/core/GltfLoader.ts @@ -130,12 +130,20 @@ export interface GltfLoaderOptions { * @security Never perform additional URI decoding or resolution inside this callback. * The loader has already validated and best-effort percent-decoded the URI; * re-decoding may re-introduce path-traversal or SSRF vulnerabilities. - * If percent-decoding fails due to invalid percent-encoding, the original URI string - * (with invalid sequences preserved) may be passed to this callback. * - * Callback invoked to resolve external buffer URIs referenced by the glTF asset. - * Receives a URI string and must return the corresponding binary data. + * Optional async callback invoked to resolve external buffer URIs referenced by the + * glTF asset (non-`data:` references found in `buffers[].uri`). * Required when loading plain `.gltf` files that reference external `.bin` files. + * + * The callback **must** return a `Promise` that resolves to the raw bytes of the + * referenced buffer. Rejections propagate directly from `loadGltf`. + * + * @param uri The raw URI string extracted from the glTF JSON, already validated + * against the loader's URI whitelist and best-effort percent-decoded. If + * percent-decoding fails due to invalid percent-encoding, the original URI string + * (with invalid sequences preserved) may be passed instead. Do **not** perform + * additional URI resolution or decoding inside this callback. + * @returns A `Promise` resolving to the buffer's raw bytes as an `ArrayBuffer`. */ resolveUri?: (uri: string) => Promise; /** diff --git a/tests/gltf.test.ts b/tests/gltf.test.ts index 6e4a4a0..bc2acc0 100644 --- a/tests/gltf.test.ts +++ b/tests/gltf.test.ts @@ -2365,6 +2365,24 @@ describe('GltfLoaderOptions JSDoc', () => { expect(gltfLoaderSource).toContain('percent-decoded'); }); + it('resolveUri JSDoc explicitly states the callback must return a Promise', () => { + expect(gltfLoaderSource).toContain('must** return a `Promise`'); + }); + + it('resolveUri JSDoc documents that rejections propagate from loadGltf', () => { + expect(gltfLoaderSource).toContain('Rejections propagate directly from `loadGltf`'); + }); + + it('resolveUri JSDoc has a @param uri tag', () => { + const resolveUriJsDoc = extractPrecedingJsDoc(gltfLoaderSource, 'resolveUri?:'); + expect(resolveUriJsDoc).toContain('@param uri'); + }); + + it('resolveUri JSDoc has a @returns tag', () => { + const resolveUriJsDoc = extractPrecedingJsDoc(gltfLoaderSource, 'resolveUri?:'); + expect(resolveUriJsDoc).toContain('@returns'); + }); + it('resolveUri JSDoc has @security tag at the top of the comment block', () => { const resolveUriIndex = gltfLoaderSource.indexOf('resolveUri?:'); expect(resolveUriIndex).toBeGreaterThan(-1);