diff --git a/CHANGELOG.md b/CHANGELOG.md index 94417f2..a23513e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [5.0.0] - 2024-04-09 + +- Modified + - **[BREAKING CHANGE]** Library has been renamed from `forge-convert-utils` to `svf-utils` + - **[BREAKING CHANGE]** SVF readers and downloaders now expect an `IAuthenticationProvider` interface + for specifying how the requests to the Model Derivative service will be authenticated + - Changed branding from Forge to APS everywhere + - Migrated to the official APS SDKs + ## [4.0.5] - 2023-09-29 - Added diff --git a/LICENSE.md b/LICENSE.md index eef99aa..b21c931 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Petr Broz +Copyright (c) 2024 Autodesk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 20b8e4a..f7b330d 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,27 @@ -# forge-convert-utils +# svf-utils -![Publish to NPM](https://github.com/petrbroz/forge-convert-utils/workflows/Publish%20to%20NPM/badge.svg) -[![npm version](https://badge.fury.io/js/forge-convert-utils.svg)](https://badge.fury.io/js/forge-convert-utils) -![node](https://img.shields.io/node/v/forge-convert-utils.svg) -![npm downloads](https://img.shields.io/npm/dw/forge-convert-utils.svg) +![Publish to NPM](https://github.com/petrbroz/svf-utils/workflows/Publish%20to%20NPM/badge.svg) +[![npm version](https://badge.fury.io/js/svf-utils.svg)](https://badge.fury.io/js/svf-utils) +![node](https://img.shields.io/node/v/svf-utils.svg) +![npm downloads](https://img.shields.io/npm/dw/svf-utils.svg) ![platforms](https://img.shields.io/badge/platform-windows%20%7C%20osx%20%7C%20linux-lightgray.svg) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT) -![Forge & glTF logos](./logo.png) +![APS & glTF logos](./logo.png) -Utilities for converting [Autodesk Forge](https://forge.autodesk.com) SVF file format into +Utilities for converting [Autodesk Platform Services](https://aps.autodesk.com) SVF file format into [glTF 2.0](https://github.com/KhronosGroup/glTF/tree/master/specification/2.0). -> Check out [forge-convert-sqlite](https://github.com/petrbroz/forge-convert-sqlite) with an experimental -> serialization/deserialization of glTF to/from sqlite. - ## Usage ### Command line -- install the package: `npm install --global forge-convert-utils` -- run the `forge-convert` command without parameters for usage info +- install the package: `npm install --global svf-utils` +- run the `svf-to-gltf` command without parameters for usage info - run the command with a path to a local SVF file - run the command with a Model Derivative URN (and optionally viewable GUID) - - to access Forge you must also specify credentials (`FORGE_CLIENT_ID` and `FORGE_CLIENT_SECRET`) - or an authentication token (`FORGE_ACCESS_TOKEN`) as env. variables + - to access APS you must also specify credentials (`APS_CLIENT_ID` and `APS_CLIENT_SECRET`) + or an authentication token (`APS_ACCESS_TOKEN`) as env. variables - this will also download the property database in sqlite format - optionally, use any combination of the following command line args: - `--output-folder ` to change output folder (by default '.') @@ -38,43 +35,43 @@ Utilities for converting [Autodesk Forge](https://forge.autodesk.com) SVF file f #### Unix/macOS ``` -forge-convert --output-folder +svf-to-gltf --output-folder ``` or ``` -export FORGE_CLIENT_ID= -export FORGE_CLIENT_SECRET= -forge-convert --output-folder +export APS_CLIENT_ID= +export APS_CLIENT_SECRET= +svf-to-gltf --output-folder ``` or ``` -export FORGE_ACCESS_TOKEN=> -forge-convert --output-folder +export APS_ACCESS_TOKEN= +svf-to-gltf --output-folder ``` #### Windows ``` -forge-convert --output-folder +svf-to-gltf --output-folder ``` or ``` -set FORGE_CLIENT_ID= -set FORGE_CLIENT_SECRET= -forge-convert --output-folder +set APS_CLIENT_ID= +set APS_CLIENT_SECRET= +svf-to-gltf --output-folder ``` or ``` -set FORGE_ACCESS_TOKEN= -forge-convert --output-folder +set APS_ACCESS_TOKEN= +svf-to-gltf --output-folder ``` ### Node.js @@ -82,81 +79,41 @@ forge-convert --output-folder The library can be used at different levels of granularity. The easiest way to convert an SVF file is to read the entire model into memory -using [SvfReader#read](https://petrbroz.github.io/forge-convert-utils/docs/classes/_svf_reader_.reader.html#read) -method, and save the model into glTF using [GltfWriter#write](https://petrbroz.github.io/forge-convert-utils/docs/classes/_gltf_writer_.writer.html#write): - -```js -const path = require('path'); -const { ModelDerivativeClient, ManifestHelper } = require('forge-server-utils'); -const { SvfReader, GltfWriter } = require('forge-convert-utils'); - -const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET } = process.env; - -async function run(urn, outputDir) { - const auth = { client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET }; - const modelDerivativeClient = new ModelDerivativeClient(auth); - const manifestHelper = new ManifestHelper(await modelDerivativeClient.getManifest(urn)); - const derivatives = manifestHelper.search({ type: 'resource', role: 'graphics' }); - const readerOptions = { - log: console.log - }; - const writerOptions = { - deduplicate: true, - skipUnusedUvs: true, - center: true, - log: console.log, - filter: (dbid) => (dbid >= 100 && dbid <= 200) // only output objects with dbIDs between 100 and 200 - }; - const writer = new GltfWriter(writerOptions); - for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) { - const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, auth); - const scene = await reader.read(readerOptions); - await writer.write(scene, path.join(outputDir, derivative.guid)); - } -} - -run('your model urn', 'path/to/output/folder'); -``` +using [SvfReader#read](https://petrbroz.github.io/svf-utils/docs/classes/_svf_reader_.reader.html#read) +method, and save the model into glTF using [GltfWriter#write](https://petrbroz.github.io/svf-utils/docs/classes/_gltf_writer_.writer.html#write): +[samples/remote-svf-to-gltf.js](./samples/remote-svf-to-gltf.js). If you don't want to read the entire model into memory (for example, when distributing the parsing of an SVF over multiple servers), you can use methods like -[SvfReader#enumerateFragments](https://petrbroz.github.io/forge-convert-utils/docs/classes/_svf_reader_.reader.html#enumeratefragments) -or [SvfReader#enumerateGeometries](https://petrbroz.github.io/forge-convert-utils/docs/classes/_svf_reader_.reader.html#enumerategeometries) +[SvfReader#enumerateFragments](https://petrbroz.github.io/svf-utils/docs/classes/_svf_reader_.reader.html#enumeratefragments) +or [SvfReader#enumerateGeometries](https://petrbroz.github.io/svf-utils/docs/classes/_svf_reader_.reader.html#enumerategeometries) to _asynchronously_ iterate over individual elements: ```js -const { ModelDerivativeClient, ManifestHelper } = require('forge-server-utils'); -const { SvfReader } = require('forge-convert-utils'); - -const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET } = process.env; - -async function run (urn) { - const auth = { client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET }; - const modelDerivativeClient = new ModelDerivativeClient(auth); - const manifestHelper = new ManifestHelper(await modelDerivativeClient.getManifest(urn)); - const derivatives = manifestHelper.search({ type: 'resource', role: 'graphics' }); - for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) { - const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, auth); - for await (const fragment of reader.enumerateFragments()) { - console.log(fragment); - } - } -} +const { SvfReader } = require('svf-utils'); + +// ... -run('your model urn'); +const reader = await SvfReader.FromDerivativeService(urn, guid, authProvider); +for await (const fragment of reader.enumerateFragments()) { + console.log(fragment); +} ``` And finally, if you already have the individual SVF assets in memory, you can parse the binary data -directly using _synchronous_ iterators like [parseMeshes](https://petrbroz.github.io/forge-convert-utils/docs/modules/_svf_meshes_.html#parsemeshes): +directly using _synchronous_ iterators like [parseMeshes](https://petrbroz.github.io/svf-utils/docs/modules/_svf_meshes_.html#parsemeshes): ```js -const { parseMeshes } = require('forge-convert-utils/lib/svf/meshes'); +const { parseMeshes } = require('svf-utils/lib/svf/meshes'); + +// ... + for (const mesh of parseMeshes(buffer)) { console.log(mesh); } ``` -> For additional examples, see the [test](./test) subfolder. +> For additional examples, see the [samples](./samples) subfolder. ### Customization @@ -167,7 +124,7 @@ You can customize the translation by sub-classing the reader and/or the writer c ### Metadata -When converting models from [Model Derivative service](https://forge.autodesk.com/en/docs/model-derivative/v2), +When converting models from [Model Derivative service](https://aps.autodesk.com/en/docs/model-derivative/v2), you can retrieve the model's properties and metadata in form of a sqlite database. The command line tool downloads this database automatically as _properties.sqlite_ file directly in your output folder. If you're using this library in your own Node.js code, you can find the database in the manifest by looking for an asset with type "resource", @@ -213,7 +170,7 @@ See [./samples/local-svf-to-gltf.sh](./samples/local-svf-to-gltf.sh) or - clone the repository - install dependencies: `yarn install` - build the library (transpile TypeScript): `yarn run build` -- run samples in the _test_ subfolder, for example: `FORGE_CLIENT_ID= FORGE_CLIENT_SECRET= node test/remote-svf-to-gltf.js ` +- run samples in the _test_ subfolder, for example: `APS_CLIENT_ID= APS_CLIENT_SECRET= node test/remote-svf-to-gltf.js ` If you're using [Visual Studio Code](https://code.visualstudio.com), you can use the following "task" and "launch" configurations: @@ -252,8 +209,8 @@ In _.vscode/launch.json_: "program": "${workspaceFolder}/test/remote-svf-to-gltf.js", "args": ["", ""], "env": { - "FORGE_CLIENT_ID": "", - "FORGE_CLIENT_SECRET": "" + "APS_CLIENT_ID": "", + "APS_CLIENT_SECRET": "" }, "preLaunchTask": "build" }, diff --git a/bin/forge-convert.js b/bin/svf-to-gltf.js similarity index 55% rename from bin/forge-convert.js rename to bin/svf-to-gltf.js index df83061..5a492b1 100755 --- a/bin/forge-convert.js +++ b/bin/svf-to-gltf.js @@ -2,22 +2,22 @@ const program = require('commander'); const path = require('path'); -const fse = require('fs-extra'); -const { ModelDerivativeClient, ManifestHelper } = require('forge-server-utils'); +const { SdkManagerBuilder } = require('@aps_sdk/autodesk-sdkmanager'); +const { ModelDerivativeClient} = require('@aps_sdk/model-derivative'); +const { Scopes } = require('@aps_sdk/authentication'); +const { SvfReader, GltfWriter, BasicAuthenticationProvider, TwoLeggedAuthenticationProvider } = require('../lib'); -const { SvfReader, GltfWriter } = require('..'); - -const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET, FORGE_ACCESS_TOKEN } = process.env; -let auth = null; -if (FORGE_ACCESS_TOKEN) { - auth = { token: FORGE_ACCESS_TOKEN }; -} else if (FORGE_CLIENT_ID && FORGE_CLIENT_SECRET) { - auth = { client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET }; +const { APS_CLIENT_ID, APS_CLIENT_SECRET, APS_ACCESS_TOKEN } = process.env; +let authenticationProvider = null; +if (APS_ACCESS_TOKEN) { + authenticationProvider = new BasicAuthenticationProvider(APS_ACCESS_TOKEN); +} else if (APS_CLIENT_ID && APS_CLIENT_SECRET) { + authenticationProvider = new TwoLeggedAuthenticationProvider(APS_CLIENT_ID, APS_CLIENT_SECRET); } async function convertRemote(urn, guid, outputFolder, options) { console.log(`Converting urn ${urn}, guid ${guid}`); - const reader = await SvfReader.FromDerivativeService(urn, guid, auth); + const reader = await SvfReader.FromDerivativeService(urn, guid, authenticationProvider); const scene = await reader.read({ log: console.log }); const writer = new GltfWriter(options); await writer.write(scene, path.join(outputFolder, guid)); @@ -59,31 +59,43 @@ program } else { // ID is the Model Derivative URN // Convert input guid or all guids - if (!auth) { - console.warn('Missing environment variables for Autodesk Forge authentication.'); - console.warn('Provide FORGE_CLIENT_ID and FORGE_CLIENT_SECRET, or FORGE_ACCESS_TOKEN.'); + if (!authenticationProvider) { + console.warn('Missing environment variables for APS authentication.'); + console.warn('Provide APS_CLIENT_ID and APS_CLIENT_SECRET, or APS_ACCESS_TOKEN.'); return; } const urn = id; - const client = new ModelDerivativeClient(auth); - const helper = new ManifestHelper(await client.getManifest(urn)); const folder = path.join(program.outputFolder, urn); if (guid) { await convertRemote(urn, guid, folder, options); } else { - const derivatives = helper.search({ type: 'resource', role: 'graphics' }); - for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) { + const sdkManager = SdkManagerBuilder.create().build(); + const modelDerivativeClient = new ModelDerivativeClient(sdkManager); + const accessToken = await authenticationProvider.getToken([Scopes.ViewablesRead]); + const manifest = await modelDerivativeClient.getManifest(accessToken, urn); + const derivatives = []; + function traverse(derivative) { + if (derivative.type === 'resource' && derivative.role === 'graphics' && derivative.mime === 'application/autodesk-svf') { + derivatives.push(derivative); + } + if (derivative.children) { + for (const child of derivative.children) { + traverse(child); + } + } + } + for (const derivative of manifest.derivatives) { + if (derivative.children) { + for (const child of derivative.children) { + traverse(child); + } + } + } + for (const derivative of derivatives) { await convertRemote(urn, derivative.guid, folder, options); } } - - // Store the property database within the subfolder (it is shared by all viewables) - const pdbDerivatives = helper.search({ type: 'resource', role: 'Autodesk.CloudPlatform.PropertyDatabase' }); - if (pdbDerivatives.length > 0) { - const databaseStream = client.getDerivativeChunked(urn, pdbDerivatives[0].urn, 1 << 20); - databaseStream.pipe(fse.createWriteStream(path.join(folder, 'properties.sqlite'))); - } } } catch (err) { console.error(err); diff --git a/logo.png b/logo.png index 740747d..46ed662 100644 Binary files a/logo.png and b/logo.png differ diff --git a/package.json b/package.json index 0ffa951..3c28755 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { - "name": "forge-convert-utils", - "version": "4.0.5", - "description": "Tools for converting Autodesk Forge file formats.", + "name": "svf-utils", + "version": "5.0.0", + "description": "Tools for working with the SVF format used by Autodesk Platform Services.", "main": "lib/index.js", "bin": { - "forge-convert": "./bin/forge-convert.js" + "svf-to-gltf": "./bin/svf-to-gltf.js" }, "engines": { "node": ">=10.15.0" @@ -16,19 +16,18 @@ "author": "Petr Broz ", "license": "MIT", "keywords": [ - "autodesk", - "forge", + "autodesk-platform-services", "gltf", "typescript" ], "repository": { "type": "git", - "url": "git+https://github.com/petrbroz/forge-convert-utils.git" + "url": "git+https://github.com/petrbroz/svf-utils.git" }, "bugs": { - "url": "https://github.com/petrbroz/forge-convert-utils/issues" + "url": "https://github.com/petrbroz/svf-utils/issues" }, - "homepage": "https://github.com/petrbroz/forge-convert-utils#readme", + "homepage": "https://github.com/petrbroz/svf-utils#readme", "devDependencies": { "@types/adm-zip": "^0.4.32", "@types/fs-extra": "^9.0.13", @@ -37,9 +36,12 @@ "typescript": "^4.5.5" }, "dependencies": { + "@aps_sdk/authentication": "^0.1.0-beta.1", + "@aps_sdk/autodesk-sdkmanager": "^0.0.7-beta.1", + "@aps_sdk/model-derivative": "^0.1.0-beta.1", "adm-zip": "^0.5.9", + "axios": "^1.6.8", "commander": "^3.0.2", - "forge-server-utils": "^8.3.5", "fs-extra": "^10.0.0" } } diff --git a/samples/Dockerfile b/samples/Dockerfile index 07c86d2..5c3ca58 100644 --- a/samples/Dockerfile +++ b/samples/Dockerfile @@ -1,7 +1,7 @@ FROM node:lts LABEL maintainer="petr.broz@autodesk.com" -LABEL description="Docker image for experimenting with forge-convert-utils and other glTF tools." +LABEL description="Docker image for experimenting with svf-utils and other glTF tools." # Prepare gltfpack RUN wget https://github.com/zeux/meshoptimizer/releases/download/v0.13/gltfpack-0.13-ubuntu.zip -O /tmp/gltfpack.zip @@ -11,7 +11,7 @@ RUN rm /tmp/gltfpack.zip # Install Node.js dependencies RUN npm install --global gltf-pipeline@^2.0.0 -RUN npm install --global forge-convert-utils@^2.0.0 +RUN npm install --global svf-utils@^5.0.0 # Add test scripts ADD *.sh *.js /tmp/ diff --git a/samples/custom-gltf-attribute.js b/samples/custom-gltf-attribute.js index 9e70d06..4b83015 100644 --- a/samples/custom-gltf-attribute.js +++ b/samples/custom-gltf-attribute.js @@ -2,14 +2,14 @@ * Example: converting an SVF from Model Derivative service into glTF, * embedding object IDs into the COLOR_0 mesh channel. * Usage: - * export FORGE_CLIENT_ID= - * export FORGE_CLIENT_SECRET= + * export APS_CLIENT_ID= + * export APS_CLIENT_SECRET= * node custom-gltf-attribute.js */ const path = require('path'); -const { ModelDerivativeClient, ManifestHelper } = require('forge-server-utils'); -const { SvfReader, GltfWriter } = require('..'); +const { getSvfDerivatives } = require('./shared.js'); +const { SvfReader, GltfWriter, TwoLeggedAuthenticationProvider } = require('..'); /* * Customized glTF writer, outputting meshes with an additional _CUSTOM_INDEX @@ -60,24 +60,20 @@ class CustomGltfWriter extends GltfWriter { } } -const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET } = process.env; +const { APS_CLIENT_ID, APS_CLIENT_SECRET } = process.env; async function run(urn, outputDir) { - const DefaultOptions = { - deduplicate: false, - skipUnusedUvs: false, - center: true, - log: console.log - }; - try { - const auth = { client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET }; - const modelDerivativeClient = new ModelDerivativeClient(auth); - const helper = new ManifestHelper(await modelDerivativeClient.getManifest(urn)); - const derivatives = helper.search({ type: 'resource', role: 'graphics' }); - const writer = new CustomGltfWriter(Object.assign({}, DefaultOptions)); - for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) { - const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, auth); + const derivatives = await getSvfDerivatives(urn, APS_CLIENT_ID, APS_CLIENT_SECRET); + const authenticationProvider = new TwoLeggedAuthenticationProvider(APS_CLIENT_ID, APS_CLIENT_SECRET); + const writer = new CustomGltfWriter({ + deduplicate: false, + skipUnusedUvs: false, + center: true, + log: console.log + }); + for (const derivative of derivatives) { + const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, authenticationProvider); const scene = await reader.read({ log: console.log }); await writer.write(scene, path.join(outputDir, derivative.guid)); } diff --git a/samples/download-svf.js b/samples/download-svf.js index d9cb37c..3e94f52 100644 --- a/samples/download-svf.js +++ b/samples/download-svf.js @@ -1,16 +1,17 @@ /* * Example: downloading SVF assets for all viewables in a Model Derivative URN. * Usage: - * export FORGE_CLIENT_ID= - * export FORGE_CLIENT_SECRET= + * export APS_CLIENT_ID= + * export APS_CLIENT_SECRET= * node download-svf.js */ -const { SvfDownloader } = require('..'); -const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET } = process.env; +const { SvfDownloader, TwoLeggedAuthenticationProvider } = require('..'); +const { APS_CLIENT_ID, APS_CLIENT_SECRET } = process.env; async function run(urn, outputDir = '.') { - const downloader = new SvfDownloader({ client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET }); + const authenticationProvider = new TwoLeggedAuthenticationProvider(APS_CLIENT_ID, APS_CLIENT_SECRET); + const downloader = new SvfDownloader(authenticationProvider); const download = downloader.download(urn, { outputDir, log: console.log }); await download.ready; } diff --git a/samples/filter-by-area.js b/samples/filter-by-area.js index 2534288..5d8599c 100644 --- a/samples/filter-by-area.js +++ b/samples/filter-by-area.js @@ -2,15 +2,15 @@ * Example: converting a subset of SVF (based on a specified area) into glTF. * Usage: * npm install --save gl-matrix - * export FORGE_CLIENT_ID= - * export FORGE_CLIENT_SECRET= + * export APS_CLIENT_ID= + * export APS_CLIENT_SECRET= * node filter-by-area.js */ const path = require('path'); -const { ModelDerivativeClient, ManifestHelper } = require('forge-server-utils'); -const { SvfReader, GltfWriter } = require('../lib'); const { mat4, vec3 } = require('gl-matrix'); +const { getSvfDerivatives } = require('./shared.js'); +const { SvfReader, GltfWriter, TwoLeggedAuthenticationProvider } = require('..'); /* * Customized glTF writer, only outputting meshes completely contained in a specified area. @@ -96,7 +96,7 @@ class AreaFilteredGltfWriter extends GltfWriter { } } -const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET } = process.env; +const { APS_CLIENT_ID, APS_CLIENT_SECRET } = process.env; async function run(urn, outputDir) { const DefaultOptions = { @@ -107,13 +107,11 @@ async function run(urn, outputDir) { }; try { - const auth = { client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET }; - const modelDerivativeClient = new ModelDerivativeClient(auth); - const helper = new ManifestHelper(await modelDerivativeClient.getManifest(urn)); - const derivatives = helper.search({ type: 'resource', role: 'graphics' }); + const derivatives = await getSvfDerivatives(urn, APS_CLIENT_ID, APS_CLIENT_SECRET); + const authenticationProvider = new TwoLeggedAuthenticationProvider(APS_CLIENT_ID, APS_CLIENT_SECRET); const writer = new AreaFilteredGltfWriter(Object.assign({}, DefaultOptions), [-25.0, -25.0, -25.0], [25.0, 25.0, 25.0]); - for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) { - const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, auth); + for (const derivative of derivatives) { + const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, authenticationProvider); const scene = await reader.read({ log: console.log }); await writer.write(scene, path.join(outputDir, derivative.guid)); } diff --git a/samples/local-svf-props.js b/samples/local-svf-props.js index 2ce1bc5..96cfc05 100644 --- a/samples/local-svf-props.js +++ b/samples/local-svf-props.js @@ -15,7 +15,6 @@ function run(dir) { const attrs = fs.readFileSync(path.join(dir, 'objects_attrs.json.gz')); const vals = fs.readFileSync(path.join(dir, 'objects_vals.json.gz')); const db = new PropDbReader(ids, offs, avs, attrs, vals); - const numObjects = offs.length - 1; for (let dbid = 1; dbid < numObjects; dbid++) { console.log(`Properties of #${dbid}`); diff --git a/samples/local-svf-to-gltf.js b/samples/local-svf-to-gltf.js index 13d30d1..bba5e3d 100644 --- a/samples/local-svf-to-gltf.js +++ b/samples/local-svf-to-gltf.js @@ -7,21 +7,14 @@ const path = require('path'); const { SvfReader, GltfWriter } = require('..'); -async function run (filepath, outputDir) { - const defaultOptions = { - deduplicate: false, - skipUnusedUvs: false, - center: true, - log: console.log - }; - +async function run(filepath, outputDir) { try { const reader = await SvfReader.FromFileSystem(filepath); const scene = await reader.read(); let writer; - writer = new GltfWriter(Object.assign({}, defaultOptions)); + writer = new GltfWriter({ deduplicate: false, skipUnusedUvs: false, center: true, log: console.log }); await writer.write(scene, path.join(outputDir, 'gltf-raw')); - writer = new GltfWriter(Object.assign({}, defaultOptions, { deduplicate: true, skipUnusedUvs: true })); + writer = new GltfWriter({ deduplicate: true, skipUnusedUvs: true, center: true, log: console.log }); await writer.write(scene, path.join(outputDir, 'gltf-dedup')); } catch(err) { console.error(err); diff --git a/samples/local-svf-to-gltf.sh b/samples/local-svf-to-gltf.sh index 303c93f..ae25b61 100755 --- a/samples/local-svf-to-gltf.sh +++ b/samples/local-svf-to-gltf.sh @@ -7,10 +7,10 @@ # ./local-svf-to-gltf.sh # Install dependencies -npm install --global forge-convert-utils gltf-pipeline +npm install --global svf-utils gltf-pipeline -# Convert SVF to glTF with [forge-convert-utils](https://github.com/petrbroz/forge-convert-utils) -forge-convert $1 --output-folder $2/gltf --deduplicate --skip-unused-uvs --ignore-lines --ignore-points +# Convert SVF to glTF with [svf-utils](https://github.com/petrbroz/svf-utils) +svf-to-gltf $1 --output-folder $2/gltf --deduplicate --skip-unused-uvs --ignore-lines --ignore-points # Validate glTF using [gltf-validator](https://github.com/KhronosGroup/glTF-Validator), if available if [ -x "$(command -v gltf_validator)" ]; then diff --git a/samples/remote-svf-props.js b/samples/remote-svf-props.js index dd6a000..2a2fd1a 100644 --- a/samples/remote-svf-props.js +++ b/samples/remote-svf-props.js @@ -1,23 +1,21 @@ /* * Example: parsing object properties for all viewables in a Model Derivative URN. * Usage: - * export FORGE_CLIENT_ID= - * export FORGE_CLIENT_SECRET= + * export APS_CLIENT_ID= + * export APS_CLIENT_SECRET= * node remote-svf-props.js */ -const { ModelDerivativeClient, ManifestHelper } = require('forge-server-utils'); -const { SvfReader } = require('..'); +const { getSvfDerivatives } = require('./shared.js'); +const { SvfReader, TwoLeggedAuthenticationProvider } = require('..'); -const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET } = process.env; +const { APS_CLIENT_ID, APS_CLIENT_SECRET } = process.env; -async function run (urn) { - const auth = { client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET }; - const modelDerivativeClient = new ModelDerivativeClient(auth); - const helper = new ManifestHelper(await modelDerivativeClient.getManifest(urn)); - const derivatives = helper.search({ type: 'resource', role: 'graphics' }); - for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) { - const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, auth); +async function run(urn) { + const derivatives = await getSvfDerivatives(urn, APS_CLIENT_ID, APS_CLIENT_SECRET); + const authenticationProvider = new TwoLeggedAuthenticationProvider(APS_CLIENT_ID, APS_CLIENT_SECRET); + for (const derivative of derivatives) { + const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, authenticationProvider); const propdb = await reader.getPropertyDb(); const props = propdb.getProperties(1); for (const name of Object.keys(props)) { diff --git a/samples/remote-svf-to-gltf.js b/samples/remote-svf-to-gltf.js index 43b620a..f5c0ca1 100644 --- a/samples/remote-svf-to-gltf.js +++ b/samples/remote-svf-to-gltf.js @@ -1,46 +1,29 @@ /* - * Example: converting an SVF (incl. property database) from Model Derivative service. + * Example: converting an SVF from Model Derivative service. * Usage: - * export FORGE_CLIENT_ID= - * export FORGE_CLIENT_SECRET= + * export APS_CLIENT_ID= + * export APS_CLIENT_SECRET= * node remote-svf-to-gltf.js */ const path = require('path'); -const fs = require('fs'); -const { ModelDerivativeClient, ManifestHelper } = require('forge-server-utils'); -const { SvfReader, GltfWriter } = require('..'); +const { getSvfDerivatives } = require('./shared.js'); +const { SvfReader, GltfWriter, TwoLeggedAuthenticationProvider } = require('..'); -const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET } = process.env; - -async function run (urn, outputDir) { - const defaultOptions = { - deduplicate: false, - skipUnusedUvs: false, - center: true, - log: console.log - }; +const { APS_CLIENT_ID, APS_CLIENT_SECRET } = process.env; +async function run(urn, outputDir) { try { - const auth = { client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET }; - const modelDerivativeClient = new ModelDerivativeClient(auth); - const helper = new ManifestHelper(await modelDerivativeClient.getManifest(urn)); - const derivatives = helper.search({ type: 'resource', role: 'graphics' }); - const writer0 = new GltfWriter(Object.assign({}, defaultOptions)); - const writer1 = new GltfWriter(Object.assign({}, defaultOptions, { deduplicate: true, skipUnusedUvs: true })); - // Convert individual 3D viewables into glTFs - for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) { - const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, auth); + const derivatives = await getSvfDerivatives(urn, APS_CLIENT_ID, APS_CLIENT_SECRET); + const writer0 = new GltfWriter({ deduplicate: false, skipUnusedUvs: false, center: true, log: console.log }); + const writer1 = new GltfWriter({ deduplicate: true, skipUnusedUvs: true, center: true, log: console.log }); + const authenticationProvider = new TwoLeggedAuthenticationProvider(APS_CLIENT_ID, APS_CLIENT_SECRET); + for (const derivative of derivatives) { + const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, authenticationProvider); const scene = await reader.read({ log: console.log }); await writer0.write(scene, path.join(outputDir, derivative.guid, 'gltf-raw')); await writer1.write(scene, path.join(outputDir, derivative.guid, 'gltf-dedup')); } - // Also download property database in sqlite form - const pdbDerivatives = helper.search({ type: 'resource', role: 'Autodesk.CloudPlatform.PropertyDatabase' }); - if (pdbDerivatives.length > 0) { - const databaseStream = modelDerivativeClient.getDerivativeChunked(urn, pdbDerivatives[0].urn, 1 << 20); - databaseStream.pipe(fs.createWriteStream(path.join(outputDir, 'properties.sqlite'))); - } } catch(err) { console.error(err); process.exit(1); diff --git a/samples/remote-svf-to-gltf.sh b/samples/remote-svf-to-gltf.sh index 1774c43..75fec04 100755 --- a/samples/remote-svf-to-gltf.sh +++ b/samples/remote-svf-to-gltf.sh @@ -3,20 +3,20 @@ # Script converting an SVF (without property database) from Model Derivative service # to glTF, and post-processing it using various 3rd party tools. # -# Usage example with Forge credentials: -# export FORGE_CLIENT_ID= -# export FORGE_CLIENT_SECRET= +# Usage example with APS credentials: +# export APS_CLIENT_ID= +# export APS_CLIENT_SECRET= # ./remote-svf-to-gltf.sh # # Usage example with an existing token: -# export FORGE_ACCESS_TOKEN= +# export APS_ACCESS_TOKEN= # ./remote-svf-to-gltf.sh # Install dependencies -npm install --global forge-convert-utils gltf-pipeline +npm install --global svf-utils gltf-pipeline -# Convert SVF to glTF with [forge-convert-utils](https://github.com/petrbroz/forge-convert-utils) -forge-convert $1 --output-folder $2/gltf --deduplicate --skip-unused-uvs --ignore-lines --ignore-points +# Convert SVF to glTF with [svf-utils](https://github.com/petrbroz/svf-utils) +svf-to-gltf $1 --output-folder $2/gltf --deduplicate --skip-unused-uvs --ignore-lines --ignore-points # Iterate over glTFs generated for all viewables (in / subfolders) for gltf in $(find $2/gltf -name "output.gltf"); do diff --git a/samples/serialize-msgpack.js b/samples/serialize-msgpack.js index 30d2e18..0708609 100644 --- a/samples/serialize-msgpack.js +++ b/samples/serialize-msgpack.js @@ -3,8 +3,8 @@ const fs = require('fs'); const path = require('path'); const { pack } = require('msgpackr'); -const { ModelDerivativeClient, ManifestHelper } = require('forge-server-utils'); -const { SvfReader, GltfWriter } = require('..'); +const { getSvfDerivatives } = require('./shared.js'); +const { SvfReader, GltfWriter, TwoLeggedAuthenticationProvider } = require('..'); class MsgpackGltfWriter extends GltfWriter { serializeManifest(manifest, outputPath) { @@ -13,17 +13,15 @@ class MsgpackGltfWriter extends GltfWriter { } } -const { FORGE_CLIENT_ID, FORGE_CLIENT_SECRET } = process.env; +const { APS_CLIENT_ID, APS_CLIENT_SECRET } = process.env; -async function run (urn, outputDir) { +async function run(urn, outputDir) { try { - const auth = { client_id: FORGE_CLIENT_ID, client_secret: FORGE_CLIENT_SECRET }; - const modelDerivativeClient = new ModelDerivativeClient(auth); - const helper = new ManifestHelper(await modelDerivativeClient.getManifest(urn)); - const derivatives = helper.search({ type: 'resource', role: 'graphics' }); + const derivatives = await getSvfDerivatives(urn, APS_CLIENT_ID, APS_CLIENT_SECRET); + const authenticationProvider = new TwoLeggedAuthenticationProvider(APS_CLIENT_ID, APS_CLIENT_SECRET); const writer = new MsgpackGltfWriter({ deduplicate: true, center: true, log: console.log }); - for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) { - const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, auth); + for (const derivative of derivatives) { + const reader = await SvfReader.FromDerivativeService(urn, derivative.guid, authenticationProvider); const scene = await reader.read({ log: console.log }); await writer.write(scene, path.join(outputDir, derivative.guid)); } diff --git a/samples/shared.js b/samples/shared.js new file mode 100644 index 0000000..91e3d8e --- /dev/null +++ b/samples/shared.js @@ -0,0 +1,46 @@ +const axios = require('axios').default; +const { SdkManagerBuilder } = require('@aps_sdk/autodesk-sdkmanager'); +const { AuthenticationClient, Scopes } = require('@aps_sdk/authentication'); +const { ModelDerivativeClient } = require('@aps_sdk/model-derivative'); + +async function downloadDerivative(urn, derivativeUrn, clientId, clientSecret) { + const sdkManager = SdkManagerBuilder.create().build(); + const authenticationClient = new AuthenticationClient(sdkManager); + const modelDerivativeClient = new ModelDerivativeClient(sdkManager); + const credentials = await authenticationClient.getTwoLeggedToken(clientId, clientSecret, [Scopes.ViewablesRead]); + const downloadInfo = await modelDerivativeClient.getDerivativeUrl(credentials.access_token, derivativeUrn, urn); + const response = await axios.get(downloadInfo.url, { responseType: 'arraybuffer', decompress: false }); + return response.data; +} + +async function getSvfDerivatives(urn, clientId, clientSecret) { + const sdkManager = SdkManagerBuilder.create().build(); + const authenticationClient = new AuthenticationClient(sdkManager); + const modelDerivativeClient = new ModelDerivativeClient(sdkManager); + const credentials = await authenticationClient.getTwoLeggedToken(clientId, clientSecret, [Scopes.ViewablesRead]); + const manifest = await modelDerivativeClient.getManifest(credentials.access_token, urn); + const derivatives = []; + function traverse(derivative) { + if (derivative.type === 'resource' && derivative.role === 'graphics' && derivative.mime === 'application/autodesk-svf') { + derivatives.push(derivative); + } + if (derivative.children) { + for (const child of derivative.children) { + traverse(child); + } + } + } + for (const derivative of manifest.derivatives) { + if (derivative.children) { + for (const child of derivative.children) { + traverse(child); + } + } + } + return derivatives; +} + +module.exports = { + downloadDerivative, + getSvfDerivatives +}; \ No newline at end of file diff --git a/src/common/authentication-provider.ts b/src/common/authentication-provider.ts new file mode 100644 index 0000000..b381cb6 --- /dev/null +++ b/src/common/authentication-provider.ts @@ -0,0 +1,30 @@ +import { AuthenticationClient, ResponseType, Scopes } from "@aps_sdk/authentication"; +import { SdkManager, SdkManagerBuilder } from "@aps_sdk/autodesk-sdkmanager"; + +export interface IAuthenticationProvider { + getToken(scopes: Scopes[]): Promise; +} + +export class BasicAuthenticationProvider implements IAuthenticationProvider { + constructor(protected accessToken: string) {} + + async getToken(scopes: Scopes[]): Promise { + // TODO: check if the hard-coded token has all the needed scopes + return this.accessToken; + } +} + +export class TwoLeggedAuthenticationProvider implements IAuthenticationProvider { + protected sdkManager: SdkManager; + protected authenticationClient: AuthenticationClient; + + constructor(protected clientId: string, protected clientSecret: string) { + this.sdkManager = SdkManagerBuilder.create().build(); + this.authenticationClient = new AuthenticationClient(this.sdkManager); + } + + async getToken(scopes: Scopes[]): Promise { + const credentials = await this.authenticationClient.getTwoLeggedToken(this.clientId, this.clientSecret, scopes); + return credentials.access_token as string; + } +} \ No newline at end of file diff --git a/src/f2d/downloader.ts b/src/f2d/downloader.ts index 4c975e4..9e392d3 100644 --- a/src/f2d/downloader.ts +++ b/src/f2d/downloader.ts @@ -1,8 +1,11 @@ import * as path from 'path'; import * as zlib from 'zlib'; import * as fse from 'fs-extra'; -import { ModelDerivativeClient, ManifestHelper, IDerivativeResourceChild } from 'forge-server-utils'; -import { IAuthOptions } from 'forge-server-utils/dist/common'; +import axios from 'axios'; +import { ManifestDerivativesChildren, ModelDerivativeClient } from '@aps_sdk/model-derivative'; +import { SdkManager, SdkManagerBuilder } from '@aps_sdk/autodesk-sdkmanager'; +import { IAuthenticationProvider } from '../common/authentication-provider'; +import { Scopes } from '@aps_sdk/authentication'; export interface IDownloadOptions { outputDir?: string; @@ -23,10 +26,12 @@ interface IDownloadContext { } export class Downloader { + protected sdkManager: SdkManager; protected modelDerivativeClient: ModelDerivativeClient; - constructor(protected auth: IAuthOptions) { - this.modelDerivativeClient = new ModelDerivativeClient(this.auth); + constructor(protected authenticationProvider: IAuthenticationProvider) { + this.sdkManager = SdkManagerBuilder.create().build(); + this.modelDerivativeClient = new ModelDerivativeClient(this.sdkManager); } download(urn: string, options?: IDownloadOptions): IDownloadTask { @@ -42,12 +47,37 @@ export class Downloader { }; } + private async _downloadDerivative(urn: string, derivativeUrn: string) { + const accessToken = await this.authenticationProvider.getToken([Scopes.ViewablesRead]); + const downloadInfo = await this.modelDerivativeClient.getDerivativeUrl(accessToken, derivativeUrn, urn); + const response = await axios.get(downloadInfo.url as string, { responseType: 'arraybuffer', decompress: false }); + return response.data; + } + private async _download(urn: string, context: IDownloadContext): Promise { context.log(`Downloading derivative ${urn}`); - const helper = new ManifestHelper(await this.modelDerivativeClient.getManifest(urn)); - const derivatives = helper.search({ type: 'resource', role: 'graphics' }) as IDerivativeResourceChild[]; + const accessToken = await this.authenticationProvider.getToken([Scopes.ViewablesRead]); + const manifest = await this.modelDerivativeClient.getManifest(accessToken, urn); + let derivatives: ManifestDerivativesChildren[] = []; + function collectDerivatives(derivative: ManifestDerivativesChildren) { + if (derivative.type === 'resource' && derivative.role === 'graphics' && (derivative as any).mime === 'application/autodesk-f2d') { + derivatives.push(derivative); + } + if (derivative.children) { + for (const child of derivative.children) { + collectDerivatives(child); + } + } + } + for (const derivative of manifest.derivatives) { + if (derivative.children) { + for (const child of derivative.children) { + collectDerivatives(child); + } + } + } const urnDir = path.join(context.outputDir, urn); - for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-f2d')) { + for (const derivative of derivatives) { if (context.cancelled) { return; } @@ -55,9 +85,10 @@ export class Downloader { context.log(`Downloading viewable ${guid}`); const guidDir = path.join(urnDir, guid); fse.ensureDirSync(guidDir); - const baseUrn = derivative.urn.substr(0, derivative.urn.lastIndexOf('/')); - const manifestGzip = await this.modelDerivativeClient.getDerivative(urn, baseUrn + '/manifest.json.gz'); - fse.writeFileSync(path.join(guidDir, 'manifest.json.gz'), new Uint8Array(manifestGzip)); + const derivativeUrn = (derivative as any).urn; + const baseUrn = derivativeUrn.substr(0, derivativeUrn.lastIndexOf('/')); + const manifestGzip = await this._downloadDerivative(urn, baseUrn + '/manifest.json.gz'); + fse.writeFileSync(path.join(guidDir, 'manifest.json.gz'), new Uint8Array(manifestGzip as Buffer)); const manifestGunzip = zlib.gunzipSync(manifestGzip); const manifest = JSON.parse(manifestGunzip.toString()); for (const asset of manifest.assets) { @@ -66,7 +97,7 @@ export class Downloader { } context.log(`Downloading asset ${asset.URI}`); try { - const assetData = await this.modelDerivativeClient.getDerivative(urn, baseUrn + '/' + asset.URI); + const assetData = await this._downloadDerivative(urn, baseUrn + '/' + asset.URI); fse.writeFileSync(path.join(guidDir, asset.URI), new Uint8Array(assetData)); } catch (err) { if (context.failOnMissingAssets) { diff --git a/src/gltf/writer.ts b/src/gltf/writer.ts index 509e8c7..76194f3 100644 --- a/src/gltf/writer.ts +++ b/src/gltf/writer.ts @@ -129,8 +129,8 @@ export class Writer { this.manifest = { asset: { version: '2.0', - generator: 'forge-convert-utils', - copyright: '2019 (c) Autodesk' + generator: 'svf-utils', + copyright: '2024 (c) Autodesk' }, extensionsUsed: [ "KHR_texture_transform" diff --git a/src/index.ts b/src/index.ts index f152c3f..3be34dd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,4 +2,4 @@ export { Reader as SvfReader } from './svf/reader'; export { Downloader as SvfDownloader } from './svf/downloader'; export { Downloader as F2dDownloader } from './f2d/downloader'; export { Writer as GltfWriter } from './gltf/writer'; -export { Region } from 'forge-server-utils/dist/common'; +export { IAuthenticationProvider, BasicAuthenticationProvider, TwoLeggedAuthenticationProvider } from './common/authentication-provider'; \ No newline at end of file diff --git a/src/svf/downloader.ts b/src/svf/downloader.ts index 34f7649..e4bd550 100644 --- a/src/svf/downloader.ts +++ b/src/svf/downloader.ts @@ -1,8 +1,11 @@ import * as path from 'path'; import * as fse from 'fs-extra'; -import { ModelDerivativeClient, ManifestHelper, IDerivativeResourceChild } from 'forge-server-utils'; -import { IAuthOptions, Region } from 'forge-server-utils/dist/common'; +import axios from 'axios'; import { SvfReader } from '..'; +import { SdkManager, SdkManagerBuilder } from '@aps_sdk/autodesk-sdkmanager'; +import { IAuthenticationProvider } from '../common/authentication-provider'; +import { ManifestDerivativesChildren, ModelDerivativeClient } from '@aps_sdk/model-derivative'; +import { Scopes } from '@aps_sdk/authentication'; export interface IDownloadOptions { outputDir?: string; @@ -23,10 +26,12 @@ interface IDownloadContext { } export class Downloader { + protected sdkManager: SdkManager; protected modelDerivativeClient: ModelDerivativeClient; - constructor(protected auth: IAuthOptions, host?: string, region?: Region) { - this.modelDerivativeClient = new ModelDerivativeClient(this.auth, host, region); + constructor(protected authenticationProvider: IAuthenticationProvider, host?: string, region?: string) { + this.sdkManager = SdkManagerBuilder.create().build(); + this.modelDerivativeClient = new ModelDerivativeClient(this.sdkManager); } download(urn: string, options?: IDownloadOptions): IDownloadTask { @@ -42,12 +47,39 @@ export class Downloader { }; } + private async _downloadDerivative(urn: string, derivativeUrn: string) { + const accessToken = await this.authenticationProvider.getToken([Scopes.ViewablesRead]); + const downloadInfo = await this.modelDerivativeClient.getDerivativeUrl(accessToken, derivativeUrn, urn); + const response = await axios.get(downloadInfo.url as string, { responseType: 'arraybuffer', decompress: false }); + return response.data; + } + private async _download(urn: string, context: IDownloadContext): Promise { context.log(`Downloading derivative ${urn}`); - const helper = new ManifestHelper(await this.modelDerivativeClient.getManifest(urn)); - const derivatives = helper.search({ type: 'resource', role: 'graphics' }) as IDerivativeResourceChild[]; + const accessToken = await this.authenticationProvider.getToken([Scopes.ViewablesRead]); + const manifest = await this.modelDerivativeClient.getManifest(accessToken, urn); const urnDir = path.join(context.outputDir || '.', urn); - for (const derivative of derivatives.filter(d => d.mime === 'application/autodesk-svf')) { + + const derivatives: ManifestDerivativesChildren[] = []; + function collectDerivatives(derivative: ManifestDerivativesChildren) { + if (derivative.type === 'resource' && derivative.role === 'graphics' && (derivative as any).mime === 'application/autodesk-svf') { + derivatives.push(derivative); + } + if (derivative.children) { + for (const child of derivative.children) { + collectDerivatives(child); + } + } + } + for (const derivative of manifest.derivatives) { + if (derivative.children) { + for (const child of derivative.children) { + collectDerivatives(child); + } + } + } + + for (const derivative of derivatives) { if (context.cancelled) { return; } @@ -55,9 +87,9 @@ export class Downloader { context.log(`Downloading viewable ${guid}`); const guidDir = path.join(urnDir, guid); fse.ensureDirSync(guidDir); - const svf = await this.modelDerivativeClient.getDerivative(urn, encodeURI(derivative.urn)); + const svf = await this._downloadDerivative(urn, encodeURI((derivative as any).urn)); fse.writeFileSync(path.join(guidDir, 'output.svf'), new Uint8Array(svf)); - const reader = await SvfReader.FromDerivativeService(urn, guid, this.auth); + const reader = await SvfReader.FromDerivativeService(urn, guid, this.authenticationProvider); const manifest = await reader.getManifest(); for (const asset of manifest.assets) { if (context.cancelled) { diff --git a/src/svf/reader.ts b/src/svf/reader.ts index 7a9195e..e3118a3 100644 --- a/src/svf/reader.ts +++ b/src/svf/reader.ts @@ -1,10 +1,11 @@ import * as path from 'path'; import * as fse from 'fs-extra'; import Zip from 'adm-zip'; +import axios from 'axios'; import { isNullOrUndefined } from 'util'; - -import { ModelDerivativeClient, ManifestHelper, IDerivativeResourceChild } from 'forge-server-utils'; -import { IAuthOptions, Region } from 'forge-server-utils/dist/common'; +import { SdkManagerBuilder } from '@aps_sdk/autodesk-sdkmanager'; +import { ManifestDerivativesChildren, ModelDerivativeClient } from '@aps_sdk/model-derivative'; +import { Scopes } from '@aps_sdk/authentication'; import { PropDbReader } from '../common/propdb-reader'; import { parseFragments } from './fragments'; import { parseGeometries } from './geometries'; @@ -12,6 +13,7 @@ import { parseMaterials } from './materials'; import { parseMeshes } from './meshes'; import * as SVF from './schema'; import * as IMF from '../common/intermediate-format'; +import { IAuthenticationProvider } from '../common/authentication-provider'; /** * Entire content of SVF and its assets loaded in memory. @@ -168,13 +170,13 @@ export interface IReaderOptions { * individual SVF objects using methods like {@link readFragments} or {@link enumerateGeometries}. * * @example - * const auth = { client_id: 'forge client id', client_secret: 'forge client secreet' }; - * const reader = await Reader.FromDerivativeService('model urn', 'viewable guid', auth); + * const authProvider = new TwoLeggedAuthenticationProvider(APS_CLIENT_ID, APS_CLIENT_SECRET); + * const reader = await Reader.FromDerivativeService(MODEL_URN, VIEWABLE_GUID, authProvider); * const scene = await reader.read(); // Read entire scene into an intermediate, in-memory representation * console.log(scene); * * @example - * const reader = await Reader.FromFileSystem('path/to/svf'); + * const reader = await Reader.FromFileSystem('path/to/output.svf'); * // Enumerate fragments (without building a list of all of them) * for await (const fragment of reader.enumerateFragments()) { * console.log(fragment); @@ -198,30 +200,56 @@ export class Reader { } /** - * Instantiates new reader for an SVF in Forge Model Derivative service. + * Instantiates new reader for an SVF in APS Model Derivative service. * @async - * @param {string} urn Forge model URN. - * @param {string} guid Forge viewable GUID. The viewable(s) can be found in the manifest + * @param {string} urn APS model URN. + * @param {string} guid APS viewable GUID. The viewable(s) can be found in the manifest * with type: 'resource', role: 'graphics', and mime: 'application/autodesk-svf'. - * @param {IAuthOptions} auth Credentials or access token for accessing the Model Derivative service. - * @param {string} host Optional host URL to be used by all Forge calls. - * @param {Region} region Optional region to be used by all Forge calls. + * @param {IAuthenticationProvider} authenticationProvider Authentication provider for accessing the Model Derivative service. + * @param {string} host Optional host URL to be used by all APS calls. + * @param {string} region Optional region to be used by all APS calls. * @returns {Promise} Reader for the provided SVF. */ - static async FromDerivativeService(urn: string, guid: string, auth: IAuthOptions, host?: string, region?: Region): Promise { + static async FromDerivativeService(urn: string, guid: string, authenticationProvider: IAuthenticationProvider, host?: string, region?: string): Promise { urn = urn.replace(/=/g, ''); - const modelDerivativeClient = new ModelDerivativeClient(auth, host, region); - const helper = new ManifestHelper(await modelDerivativeClient.getManifest(urn)); - const resources = helper.search({ type: 'resource', role: 'graphics', guid }); - if (resources.length === 0) { + const sdkManager = SdkManagerBuilder.create().build(); + const modelDerivativeClient = new ModelDerivativeClient(sdkManager); + const accessToken = await authenticationProvider.getToken([Scopes.ViewablesRead]); + const manifest = await modelDerivativeClient.getManifest(accessToken, urn); + let foundDerivative: ManifestDerivativesChildren | null = null; + function findDerivative(derivative: ManifestDerivativesChildren) { + if (derivative.type === 'resource' && derivative.role === 'graphics' && derivative.guid === guid) { + foundDerivative = derivative; + } + if (derivative.children) { + for (const child of derivative.children) { + findDerivative(child); + } + } + } + for (const derivative of manifest.derivatives) { + if (derivative.children) { + for (const child of derivative.children) { + findDerivative(child); + } + } + } + if (!foundDerivative) { throw new Error(`Viewable '${guid}' not found.`); } - const svfUrn = (resources[0] as IDerivativeResourceChild).urn; - const svf = await modelDerivativeClient.getDerivative(urn, encodeURI(svfUrn)) as Buffer; + + async function downloadDerivative(urn: string, derivativeUrn: string) { + const accessToken = await authenticationProvider.getToken([Scopes.ViewablesRead]); + const downloadInfo = await modelDerivativeClient.getDerivativeUrl(accessToken, derivativeUrn, urn); + const response = await axios.get(downloadInfo.url as string, { responseType: 'arraybuffer', decompress: false }); + return response.data; + } + + const svfUrn = (foundDerivative as any).urn; + const svf = await downloadDerivative(urn, encodeURI(svfUrn)) as Buffer; const baseUri = svfUrn.substr(0, svfUrn.lastIndexOf('/')); const resolve = async (uri: string) => { - const encodedUri = encodeURI(baseUri + '/' + uri); - const buffer = await modelDerivativeClient.getDerivative(urn, encodedUri) as Buffer; + const buffer = await downloadDerivative(urn, encodeURI(path.join(baseUri, uri))); return buffer; }; return new Reader(svf, resolve); diff --git a/test/remote-svf-to-gltf.sh b/test/remote-svf-to-gltf.sh index 3d93031..5f563b3 100755 --- a/test/remote-svf-to-gltf.sh +++ b/test/remote-svf-to-gltf.sh @@ -2,12 +2,12 @@ # Basic SVF-to-glTF conversion test. # Usage: -# export FORGE_CLIENT_ID= -# export FORGE_CLIENT_SECRET= +# export APS_CLIENT_ID= +# export APS_CLIENT_SECRET= # ./remote-svf-to-gltf.sh # Convert SVF to glTF -node ./bin/forge-convert.js $1 --output-folder $2/gltf --deduplicate --skip-unused-uvs --ignore-lines --ignore-points +node ./bin/svf-to-gltf.js $1 --output-folder $2/gltf --deduplicate --skip-unused-uvs --ignore-lines --ignore-points # Iterate over glTFs generated for all viewables (in / subfolders) for gltf in $(find $2/gltf -name "output.gltf"); do diff --git a/yarn.lock b/yarn.lock index 59a731f..74f51b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,135 +2,222 @@ # yarn lockfile v1 +"@aps_sdk/authentication@^0.1.0-beta.1": + version "0.1.0-beta.1" + resolved "https://registry.npmjs.org/@aps_sdk/authentication/-/authentication-0.1.0-beta.1.tgz" + integrity sha512-b09vBx19HqixLeBrMZWqUdqyINAJNK9Ed0PL+qVVHunTGJ+6TxXwM8omjiHPF6a3ksgy4qAsjwjyWMn70784sg== + dependencies: + "@aps_sdk/autodesk-sdkmanager" "^0.0.7-beta.1" + "@types/node" "^20.9.0" + axios "^1.6.1" + +"@aps_sdk/autodesk-sdkmanager@^0.0.7-beta.1": + version "0.0.7-beta.1" + resolved "https://registry.npmjs.org/@aps_sdk/autodesk-sdkmanager/-/autodesk-sdkmanager-0.0.7-beta.1.tgz" + integrity sha512-RqHLuyUb80j+zo2hmvxzg9ievKBieM5bqYf8f2iVMFo41iB6c9PYWxAmWpq4v1cXlj2/VqTGCRxUFaTjA6q4SQ== + dependencies: + axios "^1.4.0" + cockatiel "^3.1.1" + winston "^3.9.0" + +"@aps_sdk/model-derivative@^0.1.0-beta.1": + version "0.1.0-beta.1" + resolved "https://registry.npmjs.org/@aps_sdk/model-derivative/-/model-derivative-0.1.0-beta.1.tgz" + integrity sha512-8b1nlcPw/cDJRLpmp5aYktzANf0JzjAssC8+epSM7pCQnnT5B3gl/06UN0RK43P5fSdWE8EGxsOztCG5mZY71A== + dependencies: + "@aps_sdk/autodesk-sdkmanager" "^0.0.7-beta.1" + "@types/node" "^20.9.0" + axios "^1.6.1" + +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== + +"@dabh/diagnostics@^2.0.2": + version "2.0.3" + resolved "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz" + integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + "@types/adm-zip@^0.4.32": version "0.4.34" - resolved "https://registry.yarnpkg.com/@types/adm-zip/-/adm-zip-0.4.34.tgz#62ac859eb2af6024362a1b3e43527ab79e0c624e" + resolved "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.34.tgz" integrity sha512-8ToYLLAYhkRfcmmljrKi22gT2pqu7hGMDtORP1emwIEGmgUTZOsaDjzWFzW5N2frcFRz/50CWt4zA1CxJ73pmQ== dependencies: "@types/node" "*" -"@types/fs-extra@^8.0.0": - version "8.1.3" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.3.tgz#4807768c0b0a5a5f4746d8fde2f7ab0137076eea" - integrity sha512-7IdV01N0u/CaVO0fuY1YmEg14HQN3+EW8mpNgg6NEfxEl/lzCa5OxlBu3iFsCAdamnYOcTQ7oEi43Xc/67Rgzw== - dependencies: - "@types/node" "*" - "@types/fs-extra@^9.0.13": version "9.0.13" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" + resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz" integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== dependencies: "@types/node" "*" -"@types/node@*": - version "20.7.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.7.1.tgz#06d732ead0bd5ad978ef0ea9cbdeb24dc8717514" - integrity sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg== +"@types/node@*", "@types/node@^20.9.0": + version "20.12.6" + resolved "https://registry.npmjs.org/@types/node/-/node-20.12.6.tgz" + integrity sha512-3KurE8taB8GCvZBPngVbp0lk5CKi8M9f9k1rsADh0Evdz5SzJ+Q+Hx9uHoFGsLnLnd1xmkDQr2hVhlA0Mn0lKQ== + dependencies: + undici-types "~5.26.4" -adm-zip@^0.4.13: - version "0.4.16" - resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" - integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg== +"@types/triple-beam@^1.3.2": + version "1.3.5" + resolved "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== adm-zip@^0.5.9: version "0.5.10" - resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.10.tgz#4a51d5ab544b1f5ce51e1b9043139b639afff45b" + resolved "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz" integrity sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ== +async@^3.2.3: + version "3.2.5" + resolved "https://registry.npmjs.org/async/-/async-3.2.5.tgz" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axios@^0.21.1: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== +axios@^1.4.0, axios@^1.6.1: + version "1.6.8" + resolved "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== dependencies: - follow-redirects "^1.14.0" + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +axios@^1.6.8: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== brace-expansion@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== dependencies: balanced-match "^1.0.0" -combined-stream@^1.0.6: +cockatiel@^3.1.1: + version "3.1.2" + resolved "https://registry.npmjs.org/cockatiel/-/cockatiel-3.1.2.tgz" + integrity sha512-5yARKww0dWyWg2/3xZeXgoxjHLwpVqFptj9Zy7qioJ6+/L0ARM184sgMUrQDjxw7ePJWlGhV998mKhzrxT0/Kg== + +color-convert@^1.9.3: + version "1.9.3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3, color-name@^1.0.0: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.npmjs.org/color/-/color-3.2.1.tgz" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + +combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" commander@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" + resolved "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz" integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -follow-redirects@^1.14.0: - version "1.15.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + +fecha@^4.2.0: + version "4.2.3" + resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== -forge-server-utils@^8.3.5: - version "8.4.0" - resolved "https://registry.yarnpkg.com/forge-server-utils/-/forge-server-utils-8.4.0.tgz#c4e8396677e646f36ac6a25046db7ce277c8e5b9" - integrity sha512-U1QS1spIGxjbZiDgfsFbSaeamrNnu2zvIDMoqO6yNKtfXuQAVQIkziCxBFvkFOIgCFuokoUnOlZQlCDsdWzETQ== - dependencies: - "@types/adm-zip" "^0.4.32" - "@types/fs-extra" "^8.0.0" - adm-zip "^0.4.13" - axios "^0.21.1" - form-data "^2.5.1" - fs-extra "^8.1.0" +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -form-data@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" - combined-stream "^1.0.6" + combined-stream "^1.0.8" mime-types "^2.1.12" fs-extra@^10.0.0: version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== glob@^8.0.3: version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" @@ -141,96 +228,181 @@ glob@^8.0.3: gltf-validator@^2.0.0-dev.3.2: version "2.0.0-dev.3.9" - resolved "https://registry.yarnpkg.com/gltf-validator/-/gltf-validator-2.0.0-dev.3.9.tgz#831cd4d95ce36bc8a2cc176b739c927012119e98" + resolved "https://registry.npmjs.org/gltf-validator/-/gltf-validator-2.0.0-dev.3.9.tgz" integrity sha512-9nPcAgYJwT6sbml7S3/tC+N/BkqTUSL1u8GcmUQLuwToLR0ZH8CF3i/BhVqDwlg7OmKS2GGjjEcnU/oMMeIQUQ== graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@^2.0.3: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + jsonc-parser@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz" integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== - optionalDependencies: - graceful-fs "^4.1.6" - jsonfile@^6.0.1: version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" optionalDependencies: graceful-fs "^4.1.6" +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + +logform@^2.3.2, logform@^2.4.0: + version "2.6.0" + resolved "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz" + integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ== + dependencies: + "@colors/colors" "1.6.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + lunr@^2.3.9: version "2.3.9" - resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" + resolved "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== marked@^4.0.16: version "4.3.0" - resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + resolved "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz" integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== mime-db@1.52.0: version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== mime-types@^2.1.12: version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" minimatch@^5.0.1, minimatch@^5.1.0: version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + once@^1.3.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-stable-stringify@^2.3.1: + version "2.4.3" + resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + shiki@^0.10.1: version "0.10.1" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.10.1.tgz#6f9a16205a823b56c072d0f1a0bcd0f2646bef14" + resolved "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz" integrity sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng== dependencies: jsonc-parser "^3.0.0" vscode-oniguruma "^1.6.1" vscode-textmate "5.2.0" +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz" + integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + +triple-beam@^1.3.0: + version "1.4.1" + resolved "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== + typedoc@^0.22.11: version "0.22.18" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.22.18.tgz#1d000c33b66b88fd8cdfea14a26113a83b7e6591" + resolved "https://registry.npmjs.org/typedoc/-/typedoc-0.22.18.tgz" integrity sha512-NK9RlLhRUGMvc6Rw5USEYgT4DVAUFk7IF7Q6MYfpJ88KnTZP7EneEa4RcP+tX1auAcz7QT1Iy0bUSZBYYHdoyA== dependencies: glob "^8.0.3" @@ -240,31 +412,62 @@ typedoc@^0.22.11: shiki "^0.10.1" typescript@^4.5.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + version "4.7.4" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== universalify@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + vscode-oniguruma@^1.6.1: version "1.7.0" - resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" + resolved "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz" integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== vscode-textmate@5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" + resolved "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz" integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== +winston-transport@^4.7.0: + version "4.7.0" + resolved "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz" + integrity sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@^3.9.0: + version "3.13.0" + resolved "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz" + integrity sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ== + dependencies: + "@colors/colors" "^1.6.0" + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.4.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.7.0" + wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==