diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000000..67c96a841e --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,133 @@ +const {getESLintConfig} = require('ocular-dev-tools/configuration'); + +// Make any changes to default config here +const config = getESLintConfig({ + overrides: { + // To make import assertions work + parser: '@babel/eslint-parser', + parserOptions: { + project: ['./tsconfig.json'], + requireConfigFile: false, + babelOptions: { + plugins: ['@babel/plugin-syntax-import-assertions'] + } + }, + env: { + browser: true, + es2020: true, + node: true + }, + + rules: { + indent: 0, + 'import/no-unresolved': 0, + 'no-console': 1, + 'no-continue': ['warn'], + 'callback-return': 0, + 'max-depth': ['warn', 4], + complexity: ['warn'], + 'max-statements': ['warn'], + 'default-case': ['warn'], + 'no-eq-null': ['warn'], + eqeqeq: ['warn'], + radix: 0 + // 'accessor-pairs': ['error', {getWithoutSet: false, setWithoutGet: false}] + }, + + overrides: [ + { + files: ['**/*.ts', '**/*.tsx', '**/*.d.ts'], + rules: { + // typescript-eslint 6.0 + '@typescript-eslint/no-unsafe-argument': 0, + '@typescript-eslint/no-redundant-type-constituents': 0, + '@typescript-eslint/no-unsafe-enum-comparison': 1, + '@typescript-eslint/no-duplicate-type-constituents': 1, + '@typescript-eslint/no-base-to-string': 1, + '@typescript-eslint/no-loss-of-precision': 1, + + // For parquet module + '@typescript-eslint/no-non-null-assertion': 0, + '@typescript-eslint/no-non-null-asserted-optional-chain': 0, + '@typescript-eslint/no-floating-promises': 0, + // Gradually enable + '@typescript-eslint/ban-ts-comment': 0, + '@typescript-eslint/ban-types': 0, + '@typescript-eslint/no-unsafe-member-access': 0, + '@typescript-eslint/no-unsafe-assignment': 0, + '@typescript-eslint/no-var-requires': 0, + '@typescript-eslint/no-unused-vars': [ + 'warn', + {vars: 'all', args: 'none', ignoreRestSiblings: false} + ], + // We still have some issues with import resolution + 'import/named': 0, + 'import/no-extraneous-dependencies': 0, // ['warn'], disable for test folder only... + // Warn instead of error + // 'max-params': ['warn'], + // 'no-undef': ['warn'], + // camelcase: ['warn'], + // '@typescript-eslint/no-floating-promises': ['warn'], + // '@typescript-eslint/await-thenable': ['warn'], + // '@typescript-eslint/no-misused-promises': ['warn'], + '@typescript-eslint/no-empty-function': 0, + // We use function hoisting + '@typescript-eslint/no-use-before-define': 0, + // We always want explicit typing, e.g `field: string = ''` + '@typescript-eslint/no-inferrable-types': 0, + '@typescript-eslint/restrict-template-expressions': 0, + '@typescript-eslint/explicit-module-boundary-types': 0, + '@typescript-eslint/require-await': 0, + '@typescript-eslint/no-unsafe-return': 0, + '@typescript-eslint/no-unsafe-call': 0, + '@typescript-eslint/no-empty-interface': 0, + '@typescript-eslint/restrict-plus-operands': 0 + } + }, + { + // scripts use devDependencies + files: ['*worker*.js', '**/worker-utils/**/*.js'], + env: { + browser: true, + es2020: true, + node: true, + worker: true + } + }, + // tests are run with aliases set up in node and webpack. + // This means lint will not find the imported files and generate false warnings + { + // scripts use devDependencies + files: ['**/test/**/*.js', '**/scripts/**/*.js', '*.config.js', '*.config.local.js'], + rules: { + 'import/no-unresolved': 0, + 'import/no-extraneous-dependencies': 0 + } + }, + { + files: ['examples/**/*.js'], + rules: { + 'import/no-unresolved': 0 + } + } + ], + + settings: { + // Ensure eslint finds typescript files + 'import/resolver': { + node: { + extensions: ['.js', '.jsx', '.mjs', '.ts', '.tsx'] + } + } + } + } +}); + +// config.overrides[1].parserOptions = { +// project: ['./tsconfig.json'] +// }; + +// Uncomment to log the eslint config +// console.debug(JSON.stringify(config, null, 2)); + +module.exports = config; diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index a99ddae4a8..0000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,120 +0,0 @@ -const {getESLintConfig, deepMerge} = require('ocular-dev-tools'); - -const defaultConfig = getESLintConfig({react: '16.8.2'}); - -// Make any changes to default config here -const config = deepMerge(defaultConfig, { - parserOptions: { - project: ['./tsconfig.json'] - }, - - env: { - browser: true, - es2020: true, - node: true - }, - - rules: { - indent: 0, - 'import/no-unresolved': 0, - 'no-console': 1, - 'no-continue': ['warn'], - 'callback-return': 0, - 'max-depth': ['warn', 4], - complexity: ['warn'], - 'max-statements': ['warn'], - 'default-case': ['warn'], - 'no-eq-null': ['warn'], - eqeqeq: ['warn'], - radix: 0 - // 'accessor-pairs': ['error', {getWithoutSet: false, setWithoutGet: false}] - }, - - overrides: [ - { - files: ['**/*.ts', '**/*.tsx', '**/*.d.ts'], - rules: { - // For parquet module - '@typescript-eslint/no-non-null-assertion': 0, - '@typescript-eslint/no-non-null-asserted-optional-chain': 0, - '@typescript-eslint/no-floating-promises': 0, - // Gradually enable - '@typescript-eslint/ban-ts-comment': 0, - '@typescript-eslint/ban-types': 0, - '@typescript-eslint/no-unsafe-member-access': 0, - '@typescript-eslint/no-unsafe-assignment': 0, - '@typescript-eslint/no-var-requires': 0, - '@typescript-eslint/no-unused-vars': [ - 'warn', - {vars: 'all', args: 'none', ignoreRestSiblings: false} - ], - // We still have some issues with import resolution - 'import/named': 0, - 'import/no-extraneous-dependencies': 0, // ['warn'], disable for test folder only... - // Warn instead of error - // 'max-params': ['warn'], - // 'no-undef': ['warn'], - // camelcase: ['warn'], - // '@typescript-eslint/no-floating-promises': ['warn'], - // '@typescript-eslint/await-thenable': ['warn'], - // '@typescript-eslint/no-misused-promises': ['warn'], - '@typescript-eslint/no-empty-function': ['warn', {allow: ['arrowFunctions']}], - // We use function hoisting - '@typescript-eslint/no-use-before-define': 0, - // We always want explicit typing, e.g `field: string = ''` - '@typescript-eslint/no-inferrable-types': 0, - '@typescript-eslint/restrict-template-expressions': 0, - '@typescript-eslint/explicit-module-boundary-types': 0, - '@typescript-eslint/require-await': 0, - '@typescript-eslint/no-unsafe-return': 0, - '@typescript-eslint/no-unsafe-call': 0, - '@typescript-eslint/no-empty-interface': 0, - '@typescript-eslint/restrict-plus-operands': 0 - } - }, - { - // scripts use devDependencies - files: ['*worker*.js', '**/worker-utils/**/*.js'], - env: { - browser: true, - es2020: true, - node: true, - worker: true - } - }, - // tests are run with aliases set up in node and webpack. - // This means lint will not find the imported files and generate false warnings - { - // scripts use devDependencies - files: ['**/test/**/*.js', '**/scripts/**/*.js', '*.config.js', '*.config.local.js'], - rules: { - 'import/no-unresolved': 0, - 'import/no-extraneous-dependencies': 0 - } - }, - { - files: ['examples/**/*.js'], - rules: { - 'import/no-unresolved': 0 - } - } - ], - - settings: { - // Ensure eslint finds typescript files - 'import/resolver': { - node: { - extensions: ['.js', '.jsx', '.mjs', '.ts', '.tsx'] - } - } - } -}); - -// config.overrides[1].parserOptions = { -// project: ['./tsconfig.json'] -// }; - -// Uncomment to log the eslint config -// console.debug(config); - -module.exports = config; diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8e1cb85c96..1fc7ee5407 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [16] + node-version: [16, 18, 20] steps: - uses: actions/checkout@v3 @@ -33,7 +33,7 @@ jobs: npm run test ci - name: Coveralls - if: matrix.node-version == 16 + if: matrix.node-version == 18 uses: coverallsapp/github-action@master with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 34cace2602..5601a0513a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # tests test/data/test.png tmp +!modules/zip/test/data/**/*.zip # dists dist/* diff --git a/.ocularrc.cjs b/.ocularrc.cjs deleted file mode 100644 index c3494b0d1c..0000000000 --- a/.ocularrc.cjs +++ /dev/null @@ -1,35 +0,0 @@ -/** @typedef {import('ocular-dev-tools').OcularConfig} OcularConfig */ -const {resolve} = require('path'); - -/** @type {OcularConfig} */ -const config = { - aliases: { - test: resolve(__dirname, 'test') - }, - - browserTest: { - server: {wait: 5000} - }, - - babel: { - // extensions: ['.js', '.jsx', '.mjs', '.ts', '.tsx'] - }, - - lint: { - // TODO - comment out while getting typescript to work - paths: ['dev-docs', 'docs', 'modules'] // 'examples', test', 'website', 'examples'], - // extensions: ['js', 'jsx', 'mjs', 'ts', 'tsx', 'md'] - }, - - webpack: {}, - - entry: { - test: 'test/node.js', - 'test-browser': 'test/browser.js', - bench: 'test/bench/node.js', - 'bench-browser': 'test/bench/browser.js', - size: 'test/size/import-nothing.js' - } -}; - -module.exports = config; diff --git a/.ocularrc.js b/.ocularrc.js new file mode 100644 index 0000000000..fd141f228e --- /dev/null +++ b/.ocularrc.js @@ -0,0 +1,37 @@ +import {resolve} from 'path'; + +export default { + aliases: { + test: resolve('./test') + }, + + typescript: { + project: 'tsconfig.build.json' + }, + + bundle: { + globalName: 'loader', + externals: ['fs', 'path', 'util', 'events', 'stream', 'crypto', 'http', 'https'], + target: ['supports async-functions', 'not dead'], + format: 'umd', + globals: { + '@loaders.gl/*': 'globalThis.loaders' + } + }, + + lint: { + // TODO - comment out while getting typescript to work + paths: ['dev-docs', 'docs', 'modules'] // 'examples', test', 'website', 'examples'], + // extensions: ['js', 'jsx', 'mjs', 'ts', 'tsx', 'md'] + }, + + webpack: {}, + + entry: { + test: 'test/node.ts', + 'test-browser': 'test/browser.ts', + bench: 'test/bench/node.js', + 'bench-browser': 'test/bench/browser.js', + size: 'test/size/import-nothing.js' + } +}; diff --git a/.prettierignore b/.prettierignore index 4dcd12f0fe..c6ae6c21f2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,6 +7,11 @@ node_modules/ .cache public +modules/core/src/iterators/make-stream/make-node-stream.ts + +modules/loader-utils/src/lib/files/node-file-facade.ts +modules/loader-utils/src/lib/filesystems/node-filesystem-facade.ts + modules/3d-tiles/test/lib/classes/tile-3d-batch-table-hierarchy.spec.ts modules/bson/src/bson-writer.ts @@ -15,6 +20,7 @@ modules/bson/src/lib/encoders/encode-bson.ts modules/bson/src/lib/parsers/parse-bson.ts modules/bson/test/data/js-bson/corrupt.ts +modules/parquet/src/polyfills/buffer/buffer.ts modules/parquet/test examples/experimental/gltf-with-raw-webgl/ @@ -35,6 +41,8 @@ modules/mvt/src/lib/geojson-tiler/tile.ts modules/parquet/test +modules/textures/test/basis-loader.spec.ts + modules/xml/test/sax-ts/testcases/attribute-name.spec.ts modules/xml/test/sax-ts/testcases/opentagstart.spec.ts modules/xml/test/sax-ts/testcases/script-close-better.spec.ts diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..bffdbcd710 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +printWidth: 100 +semi: true +singleQuote: true +trailingComma: none +bracketSpacing: false diff --git a/.prettierrc.js b/.prettierrc.js deleted file mode 100644 index 152d41cdf2..0000000000 --- a/.prettierrc.js +++ /dev/null @@ -1,10 +0,0 @@ -const {getPrettierConfig, deepMerge} = require('ocular-dev-tools'); - -const config = getPrettierConfig({react: '16.8.2'}); - -// Make any changes to default config here - -// Uncomment to log the eslint config -// console.debug(config); - -module.exports = config; diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fd33c8370..8db7d7d40b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,278 @@ # CHANGELOG for loaders.gl +## v4.0 + +### v4.0.3 + +- feat(tile-converter): estimation of time remaining (#2774) +- fix: Revert parquet-wasm integration (#2781) +- fix(Arrow): featureIds not correctly parsed from MultiPolygon w/ holes in arrow util (WIP) (#2777) +- fix: Use "latest" version tag when loading from unpkg (#2779) +- docs(arrowjs): Update Arrow docs and release notes (#2778) +- fix(examples): run 'geospatial' locally (#2776) +- chore: Update all dependencies to ^4.0.0 (#2775) +- feat(parquet): Enable Parquet WASM loader (#2773) +- fix(3d-tiles): Enable Tiles3DLoader tests (#2771) +- chore: Dependencies (#2772) +- chore: parseFile accepts `ReadableFile` (#2770) +- chore(excel): Fix batched loader adapter from atomic parse (#2769) +- chore(loader-utils): split Worker/WorkerWithEncoder types (#2768) + +### v4.0.2 + +- test: run workers from source code (#2762) +- feat(schema): makeTableFromBatches (#2767) +- chore: Remove Buffer in test cases (#2766) +- chore(Arrow): add test cases for geoarrow to binary geometries (#2765) +- chore: Adopt namespace style imports for apache-arrow (#2764) +- fix get arrow bound function; add test case (#2763) +- fix(kml): Fix TXCLoader default shape (#2761) +- chore: Improve docs (#2758) +- fix(website): Unbreak website build (#2756) +- chore: fix 4.0 peer dependencies (#2755) + +### v4.0.1 + +- chore(textures): enable tests (#2741) +- feat(gis): Consolidate geo metadata detection and table conversion (#2742) +- fix(zip): cd header zip64 data reading fix (#2710) +- feat(arrow): GeoArrow utilities (#2744) +- Got rid of .toString() usage for ArrayBuffers (#2743) +- chore: Add some javascript API guidelines (#2747) +- Update 3D Tiles Docs (#2749) +- feat(mvt): MVTileSource (#2750) +- chore: improve test coverage (#2751) +- docs: Clean up website links (#2748) +- refactor(tile-converter): refactor creation of Attribute info (#2718) +- feat(tile-converter): conversion progress (#2739) +- chore(shapefile): Improve Shapefile format doc (#2752) +- fix(tile-converter): i3s-server - esm compatibility (#2745) + ## v4.0 Prerelease > The official 4.0 alpha track starts with alpha.6 > The early pre-release track was abandoned due to build incompatibility problems. -release info (#2491)) +> release info (#2491)) + +### v4.0.0-beta.8 + +- Update gltf.md. (#2733) +- fix(website): restore I3S examples (#2734) +- fix: render test import (#2731) +- chore(crypto): Restore crypto tests (#2730) +- chore: Clean up license text (#2729) +- chore(i3s): Export a function customizeColors from i3s lib utils (#2719) +- added test for conversion 64-bit attributes to strings (#2728) + +### v4.0.0-beta.7 + +- fix(i3s): Remove luma.gl dependency (#2727) +- feat(flatgeobuf): Upgrade to latest flatgeobuf (#2684) +- feat(lerc): Break out LERCLoader into its own module (size and bundling issues) (#2724) +- chore(polyfills): Bump deps (#2723) +- feat(polyfills): Add installFilePolyfills on Node.js (#2722) +- fix(i3s): I3SContentLoader regression (#2713) + +### v4.0.0-beta.6 + +- fix(polyfills): Add CJS export for node.js (#2720) +- feat(wms): Restore LERCLoader (#2715) +- chore: Remove deprecated APIs and update docs (#2714) + +### v4.0.0-beta.5 + +- Path fix (#2709) +- fix(gltf, tile-converter): attributeStorageInfo, use class name (#2673) +- chore: Add CI for Node 20 (#2712) +- fix(tile-converter): enable tests (#2708) +- chore: Bump to Node 18 (#2711) +- docs (whats-new): Update whats-new.mdx for 4.0 loaders.gl release (#2702) +- feat(geopackage): Upgrade and modernize (#2704) + +### v4.0.0-beta.4 + +- fix(tile-converter): cli tools (#2707) +- feat(tile-converter): test for conversion arrays to attribute of string type (#2703) +- chore(polyfills): Consolidate node code (#2701) +- fix(i3s): handle search params in I3SLoader (#2692) + +### v4.0.0-beta.6 + +- feat(tile-converter): --analyze-only option (#2694) +- fix(tiles): cartographicToCartesan syntax (#2690) +- chore(website): Restore website (#2689) +- fix(wms): WMS 1.3.0 compatability on GetFeatureInfo (#2680) +- chore: Prep for Node18 support (#2699) +- chore: math.gl@4.0.0 (#2698) +- fix(gltf): fix of getTypedArrayForAccessor in gltf-scenegraph (#2683) +- chore(schema): Move arrow dependencies to arrow module (#2697) +- chore: Upgrade to math.gl@4.0.0-beta.1. Remove gl-matrix (#2696) +- chore: Restore library loading (#2686) +- fix(tiles): convert region to obb (#2685) +- feat: Move to ES modules, upgrade dev-tools (#2681) +- feat(mvt): Add MVTSource (#2674) +- chore(core): Remove writeSync, save and fs dependencies (#2678) +- feat(loader-utils): Refactor FileSystem to be independent of fs (#2676) +- chore: Remove Buffer usage (#2675) +- chore(zip): Refactor zip content hash tables (#2500) +- chore(polyfills): Remove Promise.allSettled polyfill (#2672) + +### v4.0.0-beta.2 + +- fix: getting tile url with empty query params (#2671) +- chore(polyfills): Start moving Node.js code into polyfills (#2669) + +### v4.0.0-beta.1 + +- feat(tile-converter): support of 64-bit int (#2670) +- feat(gltf): added support of arrays to ext-feature-metadata (#2663) +- feat(mvt): Add TileJSONLoader (#2666) +- feat(pmtiles): Create PMTileSource from Blob (#2668) +- feat(wms): Separate WMSSource and WMSService (#2667) +- fix: remove unused ts directive (#2665) +- Move master to 4.0-beta tags (#2661) +- feat(pmtools): Add vector tile support (#2664) +- docs: Improved release notes +- feat(pmtiles): Support for pmtiles format (#2662) +- Website: Geoparquet example (#2660) +- fix(parse-i3s): getting root node url for normalizeTilesetData without nodepages (#2659) + +### v4.0.0-alpha.26 + +- Fixes for deck.gl 8.10 (#2658) +- feat(crypto): Add encoding parameter for hashes (#2657) + +### v4.0.0-alpha.25 + +- fix(gltf): tests for ext-feature-metadata (#2656) +- fix(gltf, converter): make ext-mesh-features independent from ext-structural-metadata (#2655) +- batch types (#2645) +- chore(twkb): Add TWKBLoader tests (#2653) +- feat(tile-converter): select metadata class from EXT_structural_metadata (#2647) +- feat: new geoparquet example (#2646) +- feat(wkt): Add TWKBLoader/Writer (#2028) +- feat(wkb): Auto-detect WKB dialect and encoding (#2184) +- feat(wkb): New HexWKBLoader for hex encoded WKB (#2652) +- chore(worker-utils): Improve version handling (#2651) +- chore: geoparquet prep (#2650) +- feat(wkt): Add WKTCRSLoader/Writer (#2649) +- docs(release-notes): Loaders 4.0 upcoming release notes (#2648) +- docs: Add whats-new and upgrade-guide to arrowjs docs (#2636) +- feat(schema): Make geojson-table compatible with GeoJSON (#2644) +- docs(tile-converter): metadata class selection (#2642) +- chore(tile-converter): rename (#2641) +- chore(parquet): Add Buffer polyfill to parquet to avoid bundler complications (#2643) + +### v4.0.0-alpha.24 + +- fix(tile-converter): geometry attributes reordering performance (#2640) +- fix(tile-converter): EXT_feature_metadata conversion (#2639) +- feat(gltf): EXT_feature_metadata - numeric types support (#2634) +- chore(gltf): 3d-tiles extensions refactoring (#2633) +- chore(draco): Upgrade to draco3d v1.5.6 (#2638) +- Fix browser exclude (#2596) +- docs: Consolidate whats-new (merge duplications) (#2637) +- feat(arrow): upgrade to apache-arrow v13 (#2632) +- feat(arrow): Typed apache arrow loader (#2631) +- chore: More typed loaders (#2630) +- chore(gis): Add typescript types (#2629) +- docs(i3s): fix formats and english (#2628) +- docs(i3s): I3S receipts (#2627) +- chore: Type 3d-tile and I3S loaders. (#2606) + +### v4.0.0-alpha.23 + +- chore: Add loader type parameters (#2626) +- feat(tile-converter): support EXT_mesh_features and EXT_structural_metadata (#2566) +- feat(core): non-specific parse functions return unknown (#2625) +- chore(csv): Ensure tests use typed CSVLoader (#2621) +- docs(core): Typed loaders (#2624) +- chore(zip): Remove zip module dependency on @loaders.gl/core (#2622) +- chore: Clean up module imports, remove default exports in images module (#2617) (#2623) + +### v4.0.0-alpha.22 + +- fix(zip): @loaders.gl/core dependency (#2620) +- feat(tile-converter): support 3tz (#2609) +- chore(core): Reduce use of implicit any, move test files to .ts (#2619) +- chore: Use parseFromContext in subloaders (#2616) +- feat(loader-utils): Type safe context parsers for sub loaders (#2613) +- feat(3d-tiles): some improvements (#2610) + +### v4.0.0-alpha.21 + +- feat(core): parseSync, parseInBatches, load, loadInBatches type inference (#2612) +- feat: More typed loaders (#2607) +- feat(3d-tiles): 3tz loader (#2578) +- feat(zip): ZipFileSystem (#2602) +- chore(i3s): Hash generation moved to @loader.gl/zip (#2599) +- chore(zip): read file classes (#2601) +- chore(zip): Compression method added for local header (#2600) +- chore(compression): Added raw mode for deflate-compresion module (#2598) + +### v4.0.0-alpha.20 + +- chore(i3s): Hash file utility moved to loader-utils (#2595) +- chore(i3s): Zip parse refactoring (#2594) +- fix(core): fetchOptions regression (#2591) +- chore(tile-converter): remove CesiumION tokens (#2592) +- feat(tile-converter): select metadata classes (#2590) +- fix(tile-converter): featureIds + uvRegions (#2588) + +### v4.0.0-alpha.19 + +- fix(tile-converter): CLI startup script (#2587) +- feat(tile-converter): i3s - offline conversion (#2579) +- Handle empty childless tiles in TilesetTraverser (#2584) +- fix(i3s): add to tileset data (#2585) +- fix(tile-converter): fix loading buffers in preprocess-3d-tiles (#2572) + +### v4.0.0-alpha.18 + +- fix(tile-converter): skip failing content (#2576) +- fix: Bump and remove @xmldom/xmldom (input validation issue) (#2582) +- docs(tile-converter): Add documentation for SLPK Extractor (#2567) +- chore(core): Refactor fetchFile to handle Node.js local file loading (#2575) +- chore(tile-converter): 3dtiles - exclude Tile3D and Tileset3D (#2574) + +### v4.0.0-alpha.17 + +- docs(chore): core API documentation improvements (#2573) +- Add triangulate property to geojsonToBinary (#2571) +- fix(obj): Improved vertex colors parsing (#2569) + +### v4.0.0-alpha.16 + +- chore(tile-converter): create SLPK hash during serve (#2565) +- docs(tile-converter): I3S Server (#2564) +- chore(tile-converter): i3s-server tests (#2563) +- chore(deps): bump semver in /test/apps/typescript-test (#2544) +- chore(deps): bump semver from 5.7.1 to 5.7.2 (#2545) +- chore(tile-converter): i3s-server convert to ts (#2562) + +### v4.0.0-alpha.15 + +- chore(tile-converter): bump i3s-server deps (#2561) +- chore(tile-converter): Support for SLPKs larger than 2 Gb (#2547) +- feat(tile-converter): i3s-server bundle (#2555) +- chore(deps): bump semver from 5.7.1 to 5.7.2 in /website (#2546) +- fix(docs): JSONLoader \_rootObjectBatches removed but not mentioned in upgrade guide (#2558) +- chore(deps): bump word-wrap in /test/apps/typescript-test (#2559) +- fix(tile-converter): CesiumION tileset URL (#2560) +- chore: update CHANGELOG.md (#2551) +- chore(tile-converter): update i3s-server manual (#2552) + +### v4.0.0-alpha.14 + +- fix(3d-tiles): implicit tiling v1.1 (#2549) +- fix(tile-converter): i3s->3dtiles regression (#2550) + +### v4.0.0-alpha.13 + +- fix(gltf): 3D tiles extension types & docs (#2542) +- fix(tile-converter): failing test (#2540) +- chore: bump fast-xml-parser (#2538) +- fix(3d-tiles): implicit tiling v1.1 (#2539) ### v4.0.0-alpha.12 @@ -83,6 +351,10 @@ release info (#2491)) ## v3.4 +### v3.4.9 + +- fix(obj): Improved OBJ vertex colors parsing (#2569) + ### 3.4.2 - docs: Upgrade guide for `WMSCapabilities` type, link to CHANGELOG for patch release info diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c950ca677f..fb6ca05cab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ If you consider opening a PR, here is some documentation to get you started: To contribute, you will likely want to clone the loaders.gl repository and start by making sure you can install, build and run tests. -See the [developer guide](https://loaders.gl/docs/dev-env) on the loaders.gl website for more information on how to get your environment set up for loaders.gl development, including for Linux and Windows. +See the [developer guide](https://loaders.gl/docs/developer-guide/dev-env) on the loaders.gl website for more information on how to get your environment set up for loaders.gl development, including for Linux and Windows. ## Community Governance diff --git a/LICENSE b/LICENSE index 5fe2837eff..7208843aec 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,6 @@ -Copyright (c) 2015 Uber Technologies, Inc. - -This software includes parts of PhiloGL (https://github.com/philogb/philogl) -under MIT license. PhiloGL parts Copyright © 2013 Sencha Labs. +loaders.gl is licensed under the MIT license -This software includes adaptations of postprocessing code from THREE.js (https://github.com/mrdoob/three.js/) under MIT license. Additional attribution given in specific source files. THREE.js parts Copyright © 2010-2018 three.js authors. +Copyright (c) vis.gl contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,8 +20,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--- + +Copyright (c) 2015 Uber Technologies, Inc. -loaders.gl includes certain files from Cesium (https://github.com/AnalyticalGraphicsInc/cesium) under the Apache 2 License: +loaders.gl includes certain files from Cesium (https://github.com/AnalyticalGraphicsInc/cesium) +under the Apache 2 License (found in the submodule: modules/3d-tiles):) Copyright 2011-2018 CesiumJS Contributors @@ -38,4 +39,3 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -Cesium-derived code can be found in the submodule: modules/3d-tiles diff --git a/README.md b/README.md index 3bf61551c5..ae46fdfd76 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ loaders.gl is extensively documented on the [loaders.gl](https://loaders.gl) website. +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md). + ## License loaders.gl is licensed under a permissive open source license, using an MIT umbrella license. diff --git a/babel.config.cjs b/babel.config.cjs new file mode 100644 index 0000000000..e8f0890858 --- /dev/null +++ b/babel.config.cjs @@ -0,0 +1,22 @@ +// @ts-ignore +const {getBabelConfig} = require('ocular-dev-tools/configuration'); + +module.exports = getBabelConfig({ + react: true, + plugins: [ + // inject __VERSION__ from package.json + 'version-inline' + ], + ignore: [ + // Don't transpile workers, they are transpiled separately + '**/*.worker.js', + '**/workers/*.js', + // Don't transpile files in libs, we use this folder to store external, + // already transpiled and minified libraries and scripts. + // e.g. draco, basis, las-perf etc. + /src\/libs/, + // babel can't process .d.ts + /\.d\.ts$/ + ], + debug: false +}); diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 8793ebb115..0000000000 --- a/babel.config.js +++ /dev/null @@ -1,26 +0,0 @@ -const {getBabelConfig, deepMerge} = require('ocular-dev-tools'); - -module.exports = (api) => { - const defaultConfig = getBabelConfig(api, {react: true}); - - const config = deepMerge(defaultConfig, { - plugins: [ - // inject __VERSION__ from package.json - 'version-inline' - ], - ignore: [ - // Don't transpile workers, they are transpiled separately - '**/*.worker.js', - '**/workers/*.js', - // Don't transpile files in libs, we use this folder to store external, - // already transpiled and minified libraries and scripts. - // e.g. draco, basis, las-perf etc. - /src\/libs/, - // babel can't process .d.ts - /\.d\.ts$/ - ] - }); - - // console.debug(config); - return config; -}; diff --git a/dev-docs/RFCs/v3.4/data-source-rfc.md b/dev-docs/RFCs/v3.4/data-source-rfc.md new file mode 100644 index 0000000000..52e8ba707a --- /dev/null +++ b/dev-docs/RFCs/v3.4/data-source-rfc.md @@ -0,0 +1,41 @@ +# Data Sources API + +Build a data source API that can encompass services such +- loaded data +- URLS +- tile service +- WMS +- Incremental fetch with range requests etc. +- programmatic data generation +- ... + +### Related + +- deck.gl has a semi-internal data source API. +- + + + +## Main problems + +### Refresh / Dirty state handling. + +How does the application (typically deck.gl)) know when to redraw? + +```typescript +DataSource.setNeedsRefresh(); +DataSource.getNeedsRefresh(clear: boolean = true); +``` + +## Updates + +`DataSource.setProps()` + +Typing is a bit messy when overriding child class definitions. + +## Declarative usage + +Fully declarative usage requires a lifecycle management system, which seems too heavy. + + + diff --git a/docs/README.mdx b/docs/README.mdx index 92da2f1816..72c7f01daa 100644 --- a/docs/README.mdx +++ b/docs/README.mdx @@ -6,18 +6,26 @@

-This documentation describes loaders.gl **v4.0**. Docs for older versions are available on github: +This documentation describes loaders.gl **v4.0**. See our [**release notes**](/docs/whats-new) to learn what is new. + +Docs for older versions are available on github: **[v3.3](https://github.com/visgl/loaders.gl/blob/3.3-release/docs/README.md)**, **[v2.3](https://github.com/visgl/loaders.gl/blob/2.3-release/docs/README.md)**, **[v1.3](https://github.com/visgl/loaders.gl/blob/1.3-release/docs/README.md)**. ## Overview -loaders.gl is a collection of open source loaders and writers for file formats including tabular, geospatial, and 3D formats. It is focused on supporting visualization and analytics of big data. +loaders.gl is a collection of open source loaders and writers for various file formats, +primarily focused on supporting visualization and analytics of big data. +Tabular, geospatial, and 3D file formats are well covered. -loaders.gl is packaged and published as a suite of composable loader modules offering consistent APIs and features across file formats, and offers advanced features such as running loaders on workers and incremental parsing, and all loaders work in both the browser and in Node.js. +Published as a suite of composable loader modules with consistent APIs and features, +offering advanced features such as worker thread and incremental parsing, +loaders.gl aims to be a trusted companion when you need to load data into your application. -By design, other [vis.gl frameworks](https://vis.gl/frameworks) such as [deck.gl](https://deck.gl) and [luma.gl](https://luma.gl) integrate seamlessly with loaders.gl, however loaders.gl itself has no dependencies on those frameworks, and all loaders and writers can be used with any JavaScript application or framework. +While loaders.gl can be used with any JavaScript application or framework, +[vis.gl frameworks](https://vis.gl/frameworks) such as [**deck.gl**](https://deck.gl) +come pre-integrated with loaders.gl. ## Loaders @@ -34,7 +42,8 @@ loaders.gl provides a wide selection of loaders organized into categories: ## Code Examples -loaders.gl provides a small core API module with common functions to load and save data, and a range of optional modules that provide loaders and writers for specific file formats. +loaders.gl provides a small core API module with common functions to load and save data, +and a range of optional modules that provide loaders and writers for specific file formats. A minimal example using the `load` function and the `CSVLoader` to load a CSV formatted table into a JavaScript array: @@ -66,53 +75,50 @@ To quickly get up to speed on how the loaders.gl API works, please see [Get Star ## Supported Platforms -loaders.gl provides consistent support for both browsers and Node.js. The following platforms are supported: +loaders.gl supports both browsers and Node.js: -- **Evergreen Browsers** loaders.gl supports recent versions of the major evergreen browsers (e.g. Chrome, Firefox, Safari) on both desktop and mobile. -- **Node.js** LTS (Long-Term Support) [releases](https://nodejs.org/en/about/releases/) are also supported. Note that the `@loaders.gl/polyfills` module should be imported under Node.js. It installs the required Node.js polyfills for `fetch` etc. -- **IE11** is no longer officially supported from v3.0, however loaders.gl 2.3 is known to run on IE11. - - To run on IE11, both `@loaders.gl/polyfills` and additional appropriate polyfills (e.g. babel polyfills) need to be installed which will increase your application bundle size. - - Note that because of lack of regular testing on IE11, regressions can occur, so pinning your loaders.gl versions in package.json is advisable. - - For IE11, additional transpilation of loaders.gl packages in your `node_modules` folder may also be required. -## Design Goals +- **Evergreen Browsers** recent versions of major evergreen browsers (e.g. Chrome, Firefox, Safari) are supported on both desktop and mobile. +- **Node.js** All current [LTS releases](https://nodejs.org/en/about/previous-releases) are supported. -**Framework Agnostic** - Files are parsed into clearly documented data structures (objects + typed arrays) that can be used with any JavaScript framework. +## Design Goals -**Streaming Support** - Several loaders can parse in batches from both node and browser `Stream`s, allowing "larger than memory" files to be processed, and initial results to be available while the remainder of a file is still loading. +**Framework Agnostic** - Files are parsed into clearly documented plain data structures (objects + typed arrays) that can be used with any JavaScript framework. -**Browser Support** - loaders.gl supports recent versions of evergreen browsers. +**Browser Support** - supports recent versions of evergreen browsers, and ensures that loaders are easy to bundle. -**Worker Support** - Many loaders.gl loaders are automatically run in web workers, keeping the main thread free for other tasks while parsing completes. +**Node Support** - loaders.gl can be used when writing backend and cloud services, and you can confidently run your unit tests under Node. -**Node Support** - All loaders work under Node.js and can be used when writing backend and cloud services, and when running your unit tests under Node. +**Worker Support** - Many loaders.gl loaders come with pre-built web workers, keeping the main thread free for other tasks while parsing completes. -**Loader Categories** - loaders.gl groups similar data formats into "categories". loaders in the same category return parsed data in "standardized" form, making it easier to build applications that can handle multiple similar file formats. +**Loader Categories** - loaders.gl groups similar data formats into "categories" that return parsed data in "standardized" form. This makes it easier to build applications that can handle multiple similar file formats. **Format Autodection** - Applications can specify multiple loaders when parsing a file, and loaders.gl will automatically pick the right loader for a given file based on a combination of file/url extensions, MIME types and initial data bytes. -**Bundle Size Reduction** - Loaders for each file format are published in independent npm modules to allow applications to cherry-pick only the loaders it needs. In addition, modules are optimized for tree-shaking, and many larger loader libraries and web workers are loaded from CDN on use and not included in your application bundle. +**Streaming Support** - Many loaders can parse in batches from both node and WhatWG streams, allowing "larger than memory" files to be processed, and initial results to be available while the remainder of a file is still loading. -**Modern JavaScript** - loaders.gl is written in standard ES2018 and the API emphasizes modern, portable JavaScript constructs, e.g. async iterators instead of streams, `ArrayBuffer` instead of `Buffer`, etc. +**Composability and Bundle-Size Optimization** - Loaders for each file format are published in independent npm modules to allow applications to cherry-pick only the loaders it needs. In addition, modules are optimized for tree-shaking, and many larger loader libraries and web workers are loaded from CDN on use and not included in your application bundle. **Binary Data** - loaders.gl is optimized to load into compact memory representations and use with WebGL frameworks (e.g. by returning typed arrays whenever possible). Note that in spite of the `.gl` naming, loaders.gl has no any actual WebGL dependencies and loaders can be used without restrictions in non-WebGL applications. -**Multi-Asset Loading** - Some formats like glTF, Shapefile, or mip mapped / cube textures can require dozens of separate loads to resolve all linked assets (external buffers, images etc). Tracking all the resulting async loads can cause complications for applications. By default, loaders.gl loads all linked assets before resolving the returned `Promise`. +**Multi-Asset Loading** - Formats like glTF, Shapefile, or mip mapped / cube textures can require dozens of separate loads to resolve all linked assets (external buffers, images etc). loaders.gl loads all linked assets before resolving the returned `Promise`. + +**Modern JavaScript** - loaders.gl is written in TypeScript 5.0 and standard ES2018, is packaged as ECMAScript modules, and the API emphasizes modern, portable JavaScript constructs, e.g. async iterators instead of streams, `ArrayBuffer` instead of `Buffer`, etc. ## Licenses -loaders.gl itself is MIT licensed but various modules contain code under several permissive open source licenses, currently MIT, BSD and Apache licenses. Each loader module comes with its own license, so if the distinction matters to you, please check the documentation for each module and decide accordingly, however loaders.gl will never include code with non-permissive, commercial or copyLeft licenses. +loaders.gl itself is MIT licensed, however various modules contain forked code under several permissive, compatible open source licenses, such as ISC, BSD and Apache licenses. Each loader module provides some license notes, so if the distinction matters to you, please check the documentation for each module and decide accordingly. We guarantee that loaders.gl will never include code with non-permissive, commercial or copy-left licenses. ## Credits and Attributions -loaders.gl is maintained by a group of organizations collaborating through open governance under the Linux Foundation. +loaders.gl is maintained by a group of organizations collaborating through open governance under the OpenJS and Linux Foundations. -While loaders.gl contains a lot of original code, it is also partly a repackaging of superb work done by others in the open source community. We try to be as explicit as we can about the origins and attributions of each piece of code, both in the documentation page for each module and in the preservation of comments relating to authorship and contributions inside forked source code. +While loaders.gl contains substantial amounts of original code, it also repackages lots of superb work done by others in the open source community. We try to be as explicit as we can about the origins and attributions of each piece of code, both in the documentation page for each module and in the preservation of comments relating to authorship and contributions inside forked source code. Even so, we can make mistakes, and we may not have the full history of the code we are reusing. If you think that we have missed something, or that we could do better in regard to attribution, please let us know. -### Primary maintainers +## Primary maintainers -The organizations and individuals that contribute most significantly to the development and maintenance of loaders.gl are: +Organizations that currently contribute most significantly to the development and maintenance of loaders.gl:

diff --git a/docs/arrowjs/api-reference/builder.md b/docs/arrowjs/api-reference/builder.md new file mode 100644 index 0000000000..ab5280ae9c --- /dev/null +++ b/docs/arrowjs/api-reference/builder.md @@ -0,0 +1,87 @@ +# Builders + + + The `makeBuilder()` function creates a `Builder` instance that is set up to build + a columnar vector of the supplied `DataType`. + + A `Builder` is responsible for writing arbitrary JavaScript values + to ArrayBuffers and/or child Builders according to the Arrow specification + or each DataType, creating or resizing the underlying ArrayBuffers as necessary. + + The `Builder` for each Arrow `DataType` handles converting and appending + values for a given `DataType`. + + Once created, `Builder` instances support both appending values to the end + of the `Builder`, and random-access writes to specific indices + `builder.append(value)` is a convenience method for + builder.set(builder.length, value)`). Appending or setting values beyond the + uilder's current length may cause the builder to grow its underlying buffers + r child Builders (if applicable) to accommodate the new values. + + After enough values have been written to a `Builder`, `builder.flush()` + ill commit the values to the underlying ArrayBuffers (or child Builders). The + nternal Builder state will be reset, and an instance of `Data` is returned. + lternatively, `builder.toVector()` will flush the `Builder` and return + n instance of `Vector` instead. + + When there are no more values to write, use `builder.finish()` to + inalize the `Builder`. This does not reset the internal state, so it is + ecessary to call `builder.flush()` or `toVector()` one last time + f there are still values queued to be flushed. + + Note: calling `builder.finish()` is required when using a `DictionaryBuilder`, + ecause this is when it flushes the values that have been enqueued in its internal + ictionary's `Builder`, and creates the `dictionaryVector` for the `Dictionary` `DataType`. + + + ## Usage + +Creating a utf8 array + + ```ts + import { Builder, Utf8 } from 'apache-arrow'; + + const utf8Builder = makeBuilder({ + type: new Utf8(), + nullValues: [null, 'n/a'] + }); + + utf8Builder + .append('hello') + .append('n/a') + .append('world') + .append(null); + + const utf8Vector = utf8Builder.finish().toVector(); + + console.log(utf8Vector.toJSON()); + // > ["hello", null, "world", null] + ``` + +## makeBuilder + +```ts +function makeBuilder(options: BuilderOptions): Builder; +``` + +```ts +type BuilderOptions { + type: T; + nullValues?: TNull[] | ReadonlyArray | null; + children?: { [key: string]: BuilderOptions } | BuilderOptions[]; +} +``` + +- `type` - the data type of the column. This can be an arbitrarily nested data type with children (`List`, `Struct` etc). +- `nullValues?` - The javascript values which will be considered null-values. +- `children?` - `BuilderOptions` for any nested columns. + +- `type T` - The `DataType` of this `Builder`. +- `type TNull` - The type(s) of values which will be considered null-value sentinels. + + +## Builder + +`makeBuilder()` returns `Builder` which is a base class for the various that Arrow JS builder subclasses that +construct Arrow Vectors from JavaScript values. + diff --git a/docs/arrowjs/api-reference/data-frame.md b/docs/arrowjs/api-reference/data-frame.md deleted file mode 100644 index af7853236a..0000000000 --- a/docs/arrowjs/api-reference/data-frame.md +++ /dev/null @@ -1,23 +0,0 @@ -# DataFrame - -> This documentation reflects Arrow JS v4.0. Needs to be updated for the new Arrow API in v9.0 +. - -Extends `Table` - -## Methods - -### filter(predicate: Predicate) : FilteredDataFrame - -Returns: A `FilteredDataFrame` which is a subclass of `DataFrame`, allowing you to chain additional data frame operations, including applying additional filters. - -Note that this operation just registers filter predicates and is this very cheap to call. No actual filtering is done until iteration starts. - -### scan(next: Function, bind?: Function) - -Performantly iterates over all non-filtered rows in the data frame. - -* `next` `(idx: number, batch: RecordBatch) => void` - -* `bind` `(batch: RecordBatch) => void` - Optional, typically used to generate high-performance per-batch accessor functions for `next`. - -### countBy(name: Col | String) : CountByResult - diff --git a/docs/arrowjs/api-reference/predicates.md b/docs/arrowjs/api-reference/predicates.md deleted file mode 100644 index e1165c1e47..0000000000 --- a/docs/arrowjs/api-reference/predicates.md +++ /dev/null @@ -1,33 +0,0 @@ -# Predicates - -> This documentation reflects Arrow JS v4.0. Needs to be updated for the new Arrow API in v9.0 +. - -## Value - -## Literal - -## Col - -The Col predicate gets the value of the specified column - -### bind(batch : RecordBatch) : Function - -Returns a more efficient accessor for the column values in this batch, taking local indices. - -Note: These accessors are typically created in the `DataFrame.scan` bind method, and then used in the the `DataFrame.next` method. - -## ComparisonPredicate - -## And - -## Or - -## Equals - -## LTEq - -## GTEq - -## Not - -## CustomPredicate diff --git a/docs/arrowjs/api-reference/record-batch-reader.md b/docs/arrowjs/api-reference/record-batch-reader.md index dd77938472..9614babe0c 100644 --- a/docs/arrowjs/api-reference/record-batch-reader.md +++ b/docs/arrowjs/api-reference/record-batch-reader.md @@ -11,7 +11,7 @@ The JavaScript API supports streaming multiple arrow tables over a single socket To read all batches from all tables in a data source: -```js +```typescript const readers = RecordBatchReader.readAll(fetch(path, {credentials: 'omit'})); for await (const reader of readers) { for await (const batch of reader) { @@ -22,7 +22,7 @@ for await (const reader of readers) { If you only have one table (the normal case), then there'll only be one RecordBatchReader/the outer loop will only execute once. You can also create just one reader via -```js +```typescript const reader = await RecordBatchReader.from(fetch(path, {credentials: 'omit'})); ``` diff --git a/docs/arrowjs/api-reference/record-batch-writer.md b/docs/arrowjs/api-reference/record-batch-writer.md index 5f470583c8..6c0267f2ed 100644 --- a/docs/arrowjs/api-reference/record-batch-writer.md +++ b/docs/arrowjs/api-reference/record-batch-writer.md @@ -35,7 +35,7 @@ Returns: A Node.js Duplex stream Example: -```js +```typescript const fs = require('fs'); const { PassThrough, finished } = require('stream'); diff --git a/docs/arrowjs/api-reference/table.md b/docs/arrowjs/api-reference/table.md index ee2844cd4f..d8c660a4f1 100644 --- a/docs/arrowjs/api-reference/table.md +++ b/docs/arrowjs/api-reference/table.md @@ -17,7 +17,7 @@ A Table’s columns are instances of `Column`, which is a container for one or m `Table.new()` accepts an `Object` of `Columns` or `Vectors`, where the keys will be used as the field names for the `Schema`: -```js +```typescript const i32s = Int32Vector.from([1, 2, 3]); const f32s = Float32Vector.from([.1, .2, .3]); const table = Table.new({ i32: i32s, f32: f32s }); diff --git a/docs/arrowjs/api-reference/chunked.md b/docs/arrowjs/api-reference/vector-chunked.md similarity index 99% rename from docs/arrowjs/api-reference/chunked.md rename to docs/arrowjs/api-reference/vector-chunked.md index 971c74a3f5..95b7f04c3f 100644 --- a/docs/arrowjs/api-reference/chunked.md +++ b/docs/arrowjs/api-reference/vector-chunked.md @@ -10,7 +10,7 @@ Holds a "chunked array" that allows a number of array fragments (represented by Create a new contiguous typed array from a `Chunked` instance (note that this creates a new typed array unless only one chunk) -```js +```typescript const typedArray = chunked.toArray(); ``` diff --git a/docs/arrowjs/api-reference/column.md b/docs/arrowjs/api-reference/vector-column.md similarity index 92% rename from docs/arrowjs/api-reference/column.md rename to docs/arrowjs/api-reference/vector-column.md index 0eaf86bbba..dd3e96450e 100644 --- a/docs/arrowjs/api-reference/column.md +++ b/docs/arrowjs/api-reference/vector-column.md @@ -7,17 +7,17 @@ An immutable column data structure consisting of a field (type metadata) and a c ## Usage Copy a column -```js +```typescript const typedArray = column.slice(); ``` Get a contiguous typed array from a `Column` (creates a new typed array unless only one chunk) -```js +```typescript const typedArray = column.toArray(); ``` columns are iterable -```js +```typescript let max = column.get(0); let min = max; for (const value of column) { @@ -29,9 +29,6 @@ for (const value of column) { ## Inheritance -Column extends [`Chunked`](/docs/arrowjs/api-reference/chunked) - - ## Fields In addition to fields inherited from `Chunked`, Colum also defines diff --git a/docs/arrowjs/api-reference/vector.md b/docs/arrowjs/api-reference/vector.md index 8933d3b0c9..13adc38558 100644 --- a/docs/arrowjs/api-reference/vector.md +++ b/docs/arrowjs/api-reference/vector.md @@ -1,6 +1,14 @@ -# Vector +# Vectors -> This documentation reflects Arrow JS v4.0. Needs to be updated for the new Arrow API in v9.0 +. +A `Vector` is an Array-like data structure. Use `makeVector` and `vectorFromArray` to create vectors. + +### makeVector + + +### vectorFromArray + + +### Vector Also referred to as `BaseVector`. An abstract base class for vector types. @@ -8,36 +16,34 @@ Also referred to as `BaseVector`. An abstract base class for vector types. * ... * TBD -## Inheritance +## Fields +### `type: DataType` -## Fields +The Arrow `DataType` that describes the elements in this Vector. -### data: `Data` (readonly) +### `data: Data (readonly)` The underlying Data instance for this Vector. -### numChildren: number (readonly) +### `numChildren: number (readonly)` The number of logical Vector children. Only applicable if the DataType of the Vector is one of the nested types (List, FixedSizeList, Struct, or Map). -### type : T - -The DataType that describes the elements in the Vector -### typeId : T['typeId'] +### `typeId: T['typeId']` The `typeId` enum value of the `type` instance -### length : number +### `length: number` Number of elements in the `Vector` -### offset : number +### `offset: number` Offset to the first element in the underlying data. -### stride : number +### `stride: number` Stride between successive elements in the the underlying data. @@ -49,59 +55,68 @@ The number of elements in the underlying data buffer that constitute a single lo - For `FixedSizeList` types, the stride is the `listSize` property of the `FixedSizeList` instance. - For `FixedSizeBinary` types, the stride is the `byteWidth` property of the `FixedSizeBinary` instance. -### nullCount : Number +### `nullCount: number` Number of `null` values in this `Vector` instance (`null` values require a null map to be present). -### VectorName : String +### `VectorName: string` Returns the name of the Vector -### ArrayType : TypedArrayConstructor | ArrayConstructor +### `ArrayType: TypedArrayConstructor | ArrayConstructor` Returns the constructor of the underlying typed array for the values buffer as determined by this Vector's DataType. -### values : T['TArray'] +### `values: T['TArray']` Returns the underlying data buffer of the Vector, if applicable. -### typeIds : Int8Array | null +### `typeIds: Int8Array | null` Returns the underlying typeIds buffer, if the Vector DataType is Union. -### nullBitmap : Uint8Array | null +### `nullBitmap: Uint8Array | null` Returns the underlying validity bitmap buffer, if applicable. Note: Since the validity bitmap is a Uint8Array of bits, it is _not_ sliced when you call `vector.slice()`. Instead, the `vector.offset` property is updated on the returned Vector. Therefore, you must factor `vector.offset` into the bit position if you wish to slice or read the null positions manually. See the implementation of `BaseVector.isValid()` for an example of how this is done. -### valueOffsets : Int32Array | null +### `valueOffsets: Int32Array | null` Returns the underlying valueOffsets buffer, if applicable. Only the List, Utf8, Binary, and DenseUnion DataTypes will have valueOffsets. ## Methods -### clone(data: `Data`, children): `Vector` +### `clone(data: Data, children): Vector` Returns a clone of the current Vector, using the supplied Data and optional children for the new clone. Does not copy any underlying buffers. -### concat(...others: `Vector[]`) +### `concat(...others: Vector[])` Returns a `Chunked` vector that concatenates this Vector with the supplied other Vectors. Other Vectors must be the same type as this Vector. -### slice(begin?: number, end?: number) +### `slice(begin?: number, end?: number)` Returns a zero-copy slice of this Vector. The begin and end arguments are handled the same way as JS' `Array.prototype.slice`; they are clamped between 0 and `vector.length` and wrap around when negative, e.g. `slice(-1, 5)` or `slice(5, -1)` -### isValid(index: number): boolean +### `isValid()` + +```ts +vector.isValid(index: number): boolean +``` + +Returns `true` the supplied index is valid in the underlying validity bitmap. + -Returns whether the supplied index is valid in the underlying validity bitmap. +### `getChildAt()` -### getChildAt``(index: number): `Vector` | null +```ts +vector.getChildAt(index: number): Vector | null +``` -Returns the inner Vector child if the DataType is one of the nested types (Map or Struct). +Returns the inner Vector child if the DataType is one of the nested types such as Map or Struct. -### toJSON(): any +### `toJSON()` Returns a dense JS Array of the Vector values, with null sentinels in-place. diff --git a/docs/arrowjs/arrow-sidebar.json b/docs/arrowjs/arrow-sidebar.json index a3f1a3c7d6..9642233281 100644 --- a/docs/arrowjs/arrow-sidebar.json +++ b/docs/arrowjs/arrow-sidebar.json @@ -8,6 +8,7 @@ "items": [ "arrowjs/README", "arrowjs/whats-new", + "arrowjs/upgrade-guide", "arrowjs/contributing" ] }, @@ -23,16 +24,15 @@ "type": "category", "label": "Developer Guide", "items": [ - "arrowjs/developer-guide/big-ints", - "arrowjs/developer-guide/converting-data", - "arrowjs/developer-guide/data-frame-operations", - "arrowjs/developer-guide/data-sources", "arrowjs/developer-guide/data-types", - "arrowjs/developer-guide/memory-management", - "arrowjs/developer-guide/predicates", - "arrowjs/developer-guide/reading-and-writing", + "arrowjs/developer-guide/schemas", "arrowjs/developer-guide/tables", - "arrowjs/developer-guide/typescript" + "arrowjs/developer-guide/builders", + "arrowjs/developer-guide/converting-data", + "arrowjs/developer-guide/memory-management", + "arrowjs/developer-guide/big-ints", + "arrowjs/developer-guide/data-sources", + "arrowjs/developer-guide/reading-and-writing" ] }, { @@ -40,13 +40,9 @@ "label": "API Reference", "items": [ "arrowjs/api-reference/README", - "arrowjs/api-reference/chunked", - "arrowjs/api-reference/column", - "arrowjs/api-reference/data-frame", "arrowjs/api-reference/data", "arrowjs/api-reference/dictionary", "arrowjs/api-reference/field", - "arrowjs/api-reference/predicates", "arrowjs/api-reference/record-batch-reader", "arrowjs/api-reference/record-batch-writer", "arrowjs/api-reference/record-batch", diff --git a/docs/arrowjs/developer-guide/big-ints.md b/docs/arrowjs/developer-guide/big-ints.md index 72efee60df..b689c08714 100644 --- a/docs/arrowjs/developer-guide/big-ints.md +++ b/docs/arrowjs/developer-guide/big-ints.md @@ -22,7 +22,7 @@ One of the added methods is an implementation of [`[Symbol.toPrimitive]`](https: The implementation of these methods is [bifurcated](https://github.com/apache/arrow/blob/3eb07b7ed173e2ecf41d689b0780dd103df63a00/js/src/util/bn.ts#L125), so if you're in an environment with `BigInt` support we use the native type, but if not, we'll make a best-effort attempt to return something meaningful (usually the unsigned decimal representation of the number as a string, though we'd appreciate help if someone knows how to compute the signed decimal representation). Examples: -```js +```typescript import { Int64Vector } from 'apache-arrow'; import assert from 'assert'; diff --git a/docs/arrowjs/developer-guide/builders.md b/docs/arrowjs/developer-guide/builders.md new file mode 100644 index 0000000000..5a9efe515c --- /dev/null +++ b/docs/arrowjs/developer-guide/builders.md @@ -0,0 +1,30 @@ +# Building columns and tables + +Many JavaScript application may only need to be able to load and iterate of the data in existing Apache Arrow files creating outside of JavaScript. + +However a JS application may also want to create its own Arrow tables from scratch. + +For this situation, Apache Arrow JS provides the `makeBuilder()` function that returns `Builder` instances that can be used to build columns of specific data types. + +However, creating arrow-compatible binary data columns for complex, potentially nullable data types can be quite tricky. + + ```ts + import { Builder, Utf8 } from 'apache-arrow'; + + const utf8Builder = makeBuilder({ + type: new Utf8(), + nullValues: [null, 'n/a'] + }); + + utf8Builder + .append('hello') + .append('n/a') + .append('world') + .append(null); + + const utf8Vector = utf8Builder.finish().toVector(); + + console.log(utf8Vector.toJSON()); + // > ["hello", null, "world", null] + ``` + diff --git a/docs/arrowjs/developer-guide/converting-data.md b/docs/arrowjs/developer-guide/converting-data.md index ed367e0843..0327c0b59e 100644 --- a/docs/arrowjs/developer-guide/converting-data.md +++ b/docs/arrowjs/developer-guide/converting-data.md @@ -14,7 +14,7 @@ Many arrow classes support the following methods: You can get a temporary object representing a row in a table. -```js +```typescript const row = table.get(0); ``` @@ -24,13 +24,13 @@ Note that the `row` does not retain the schema, so you'll either need to know th More efficient is to get a column. -```js +```typescript const column = table.getColumn('data'); ``` The column can be chunked, so to get a contiguous (typed) array, call -```js +```typescript const array = table.getColumn('columnName').toArray(); ``` diff --git a/docs/arrowjs/developer-guide/data-frame-operations.md b/docs/arrowjs/developer-guide/data-frame-operations.md deleted file mode 100644 index 94348f824c..0000000000 --- a/docs/arrowjs/developer-guide/data-frame-operations.md +++ /dev/null @@ -1,87 +0,0 @@ -# Data Frame Operations - -Part of the power of data frame operations is that they typically do not actually perform any modifications (copying etc) of the underlying data, and ultimately only impact how iteration over that data is done, and what "view" of the data is presented. This allows data frame operations to be extremely performant, even when applied on very big (multi-gigabyte) data aset. - -Note that the Arrow JS `Table` class inherits from the `DataFrame` class which is why the examples in this section can use `DataFrame` methods to `Table` instances. - -Also, most of the data frame operations do not modify the original `Table` or `DataFrame`, but rather return a new similar object with new filtering or "iteration constraints" applied. So memory is usually not changed or modified during these operations. - -References: -* Much of the text in this section is adapted from Brian Hulette's [Introduction to Apache Arrow](https://observablehq.com/@theneuralbit/introduction-to-apache-arrow) - - -## Removing Rows - -A simplest way to remove rows from a data frame mey be use `Table.slice(start, end)`. As usual, rather than actually modifying memory, this operation returns a new `Table`/`DataFrame` with iteration constrained to a sub set of the rows in the original frame. - - -## Removing Columns - -The `Table.select(keys: String[])` method drops all columns except the columns with names that match the supplied `keys`. - -```js -table.select(['name', 'age']); // Drop all colums except name and age -```` - - -## Filtering Rows - -Another way to "remove" rows from data frames is to apply filters. Filters effectively "removes" rows that don't fullfill the predicates in the filter. For details see the note below. - -```js -const selectedName = 'myname'; -// Remove all rows with name === 'myname' -const dataFrame = table.filter(arrow.predicate.col('name').eq(selectedName)); -``` - -The predicates classes provided by arrow allows for the comparison of column values against literals or javascript values (equality, greater or equal than, less or equal than) as well as the creation of composite logical expressions (`and`, `or` and `not`) out of individual column comparisons. - -It is also possible to write custom predicates by supplying an arbitrary JavaScript function to filter a row, however performance is usually best when using the built-in comparison predicates. - -> Note that calling `filter()` on a `DataFrame` doesn't actually remove any rows from the underlying data store (it just stores the predicates). It's not until you iterate over the date, e.g. by calling `countBy()` or `scan()` that we actually apply the filter on the rows. - - -## Counting Rows - -To count the number of times different values appear in a table, use `countBy()`. - -```js -const newTable = table.countBy('column_name'); -``` - -Note that `countBy()` does not return a modified data frame or table, but instead returns a new `Table` that contains two columns, `value` and `count`. Each distinct value in the specified column in the original table is listed once in `value`, and the corresponding `count` field in the same row indicates how many times it was present in the original table. - -Note that the results are not sorted. - -## Sorting - -DataFrames do not currently support sorting. To sort you need to move the data back to JavaScript arrays. - -## Iterating over a DataFrame (Scanning) - -The `DataFrame.scan()` method lets you define a custom function that will be called for each (non-filtered) record in the `DataFrame`. - -Note: For simpler use cases, it is recommended to use the Arrow API provided predicates etc rather than writing a custom scan function, as performance will often be better. - - -### Writing a `next` callback for `scan()` - -In order to be more efficient, Arrow data is broken up into batches of records (which is what makes it possible to do concatenations despite the columnar layout, and `DataFrame.scan()` does not hide this implementation detail from you. - - -### Optimizing `scan()` performance with `bind()` callbacks - -In addition to the `next` callback, you can supply a `bind` function for scan to call each time it starts reading from a new `RecordBatch`. `scan` will call these functions as illustrated in the following pseudo-code: - -```js -for (const batch of batches) { - bind(batch); - for (const index in batch) { - next(index, batch); - } -} -``` - -Note: -* The `index` passed to next only applies to the current RecordBatch, it is not a global index. -* The current `RecordBatch` is passed to `next`, so it is possible to access data without writing a bind function, but there will be a performance penalty if your data has a lot of batches. diff --git a/docs/arrowjs/developer-guide/data-types.md b/docs/arrowjs/developer-guide/data-types.md index 8a7a088131..ff1757cb1e 100644 --- a/docs/arrowjs/developer-guide/data-types.md +++ b/docs/arrowjs/developer-guide/data-types.md @@ -7,12 +7,14 @@ Arrow supports a rich set of data types: * Nested types: list, struct, and union * Dictionary type: An encoded categorical type +## Data Type Descriptor Objects + ### Converting Dates Apache Arrow Timestamp is a 64-bit int of milliseconds since the epoch, represented as two 32-bit ints in JS to preserve precision. The fist number is the "low" int and the second number is the "high" int. -```js +```typescript function toDate(timestamp) { return new Date((timestamp[1] * Math.pow(2, 32) + timestamp[0])/1000); } diff --git a/docs/arrowjs/developer-guide/introduction.md b/docs/arrowjs/developer-guide/introduction.md new file mode 100644 index 0000000000..e21e254e0a --- /dev/null +++ b/docs/arrowjs/developer-guide/introduction.md @@ -0,0 +1,54 @@ +# Introduction + +Apache Arrow is a binary specification and set of libraries for representing Tables and Columns of strongly-typed fixed-width, variable-width, and nested data structures in-memory and over-the-wire. + +Arrow represents columns of values in sets of contiguous buffers. This is in contrast to a row-oriented representation, where the values for each row are stored in a contiguous buffer. The columnar representation makes it easier to take advantage of SIMD instruction sets in modern CPUs and GPUs, and can lead to dramatic performance improvements processing large amounts of data. + +## Components + +The Arrow library is organized into separate components responsible for creating, reading, writing, serializing, deserializing, or manipulating Tables or Columns. + +* [Data Types](/docs/arrowjs/developer-guide/introduction#arrow-data-types) - Classes that define the fixed-width, variable-width, and composite data types Arrow can represent +* [Vectors](/docs/arrowjs/developer-guide/introduction#arrow-vectors) - Classes to read and decode JavaScript values from the underlying buffers or Vectors for each data type +* [Builders](/docs/arrowjs/developer-guide/introduction#arrow-builders) - Classes to write and encode JavaScript values into the underlying buffers or Vectors for each data type +* [Visitors](/docs/arrowjs/developer-guide/introduction#arrow-visitors) - Classes to traverse, manipulate, read, write, or aggregate values from trees of Arrow Vectors or DataTypes +* [IPC Readers and Writers](/docs/arrowjs/developer-guide/introduction#arrow-ipc-primitives) - Classes to read and write the Arrow IPC (inter-process communication) binary file and stream formats +* [Fields, Schemas, RecordBatches, Tables, and Columns](/docs/arrowjs/developer-guide/introduction#fields-schemas-recordbatches-tables-and-columns) - Classes to describe, manipulate, read, and write groups of strongly-typed Vectors or Columns + +## [Data Types](/docs/arrowjs/developer-guide/data-types) + +At the heart of Arrow is set of well-known logical [data types](/docs/arrowjs/developer-guide/data-types), ensuring each Column in an Arrow Table is strongly-typed. These data types define how a Column's underlying buffers should be constructed and read, and includes configurable (and custom) metadata fields for further annotating a Column. A Schema describing each Column's name and data type is encoded alongside each Column's data buffers, allowing you to consume an Arrow data source without knowing the data types or column layout beforehand. + +Each data type falls into one of three rough categories: Fixed-width types, variable-width types, or composite types that contain other Arrow data types. All data types can represent null values, which are stored in a separate validity [bitmask](https://en.wikipedia.org/wiki/Mask_(computing)). Follow the links below for a more detailed description of each data type. + +### [Fixed-width Data Types](/docs/arrowjs/developer-guide/data-types#fixed-width-data-types) + +Fixed-width data types describe physical primitive values (bytes or bits of some fixed size), or logical values that can be represented as primitive values. In addition to an optional [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) validity bitmask, these data types have a physical data buffer (a [`TypedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#TypedArray_objects) corresponding to the data type's physical element width). + + * [Null](/docs/arrowjs/developer-guide/data-types#null) - A column of NULL values having no physical storage + * [Bool](/docs/arrowjs/developer-guide/data-types#bool) - Booleans as either 0 or 1 (bit-packed, LSB-ordered) + * [Int](/docs/arrowjs/developer-guide/data-types#int) - Signed or unsigned 8, 16, 32, or 64-bit little-endian integers + * [Float](/docs/arrowjs/developer-guide/data-types#float) - 2, 4, or 8-byte floating point values + * [Decimal](/docs/arrowjs/developer-guide/data-types#decimal) - Precision-and-scale-based 128-bit decimal values + * [FixedSizeBinary](/docs/arrowjs/developer-guide/data-types#fixedsizebinary) - A list of fixed-size binary sequences, where each value occupies the same number of bytes + * [Date](/docs/arrowjs/developer-guide/data-types#date) - Date as signed 32-bit integer days or 64-bit integer milliseconds since the UNIX epoch + * [Time](/docs/arrowjs/developer-guide/data-types#time) - Time as signed 32 or 64-bit integers, representing either seconds, millisecond, microseconds, or nanoseconds since midnight (00:00:00) + * [Timestamp](/docs/arrowjs/developer-guide/data-types#timestamp) - Exact timestamp as signed 64-bit integers, representing either seconds, milliseconds, microseconds, or nanoseconds since the UNIX epoch + * [Interval](/docs/arrowjs/developer-guide/data-types#interval) - Time intervals as pairs of either (year, month) or (day, time) in SQL style + * [FixedSizeList](/docs/arrowjs/developer-guide/data-types#fixedsizelist) - Fixed-size sequences of another logical Arrow data type + +### [Variable-width Data Types](/docs/arrowjs/developer-guide/data-types#variable-width-data-types) + +Variable-width types describe lists of values with different widths, including binary blobs, Utf8 code-points, or slices of another underlying Arrow data type. These types store the values contiguously in memory, and have a physical [`Int32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array) of offsets that describe the start and end indicies of each list element. + + * [List](/docs/arrowjs/developer-guide/data-types#list) - Variable-length sequences of another logical Arrow data type + * [Utf8](/docs/arrowjs/developer-guide/data-types#utf8) - Variable-length byte sequences of UTF8 code-points (strings) + * [Binary](/docs/arrowjs/developer-guide/data-types#binary) - Variable-length byte sequences (no guarantee of UTF8-ness) + +### [Composite Data Types](/docs/arrowjs/developer-guide/data-types#composite-data-types) + +Composite types don't have physical data buffers of their own. They contain other Arrow data types and delegate work to them. + + * [Union](/docs/arrowjs/developer-guide/data-types#union) - Union of logical child data types + * [Map](/docs/arrowjs/developer-guide/data-types#map) - Map of named logical child data types + * [Struct](/docs/arrowjs/developer-guide/data-types#struct) - Struct of ordered logical child data types diff --git a/docs/arrowjs/developer-guide/predicates.md b/docs/arrowjs/developer-guide/predicates.md deleted file mode 100644 index d3aee210a3..0000000000 --- a/docs/arrowjs/developer-guide/predicates.md +++ /dev/null @@ -1,18 +0,0 @@ -# Using Predicates - - -The Arrow API provides standard predicates that allow for the comparison of column values against literals (equality, greater or equal than, less or eqial than) as well as the creation of composite logical expressions (`and`, `or` and `not`) out of individual column comparisons. - -It is of course also possible to write custom predicates, however the performance is best when using the built-ins. Note that for performance reasons, filters are specified using "predicates" rather than custom JavaScript functions. - -## Filtering using Predicates - -> Note that calling `filter()` on a `DataFrame` doesn't actually do anything (other than store the predicates). It's not until you call `countBy()` or `scan()` on the resulting object that Arrow actually scans through all of the data. - -```js -table = table.filter(arrow.predicate.col('winnername').eq(winner)); - -for (const row of table) { - // only returns rows that match criteria -} -``` diff --git a/docs/arrowjs/developer-guide/reading-and-writing.md b/docs/arrowjs/developer-guide/reading-and-writing.md index eae29458cf..d13418e336 100644 --- a/docs/arrowjs/developer-guide/reading-and-writing.md +++ b/docs/arrowjs/developer-guide/reading-and-writing.md @@ -23,7 +23,7 @@ To read Arrow tables incrementally, you use the `RecordBatchReader` class. If you only have one table in your file (the normal case), then you'll only need one `RecordBatchReader`: -```js +```typescript const reader = await RecordBatchReader.from(fetch(path, {credentials: 'omit'})); for await (const batch of reader) { console.log(batch.length); @@ -34,7 +34,7 @@ for await (const batch of reader) { The JavaScript Arrow API supports arrow data streams that contain multiple tables (this is an "extension" to the arrow spec). Naturally, each Table comes with its own set of record batches, so to read all batches from all tables in the data source you will need a double loop: -```js +```typescript const readers = RecordBatchReader.readAll(fetch(path, {credentials: 'omit'})); for await (const reader of readers) { for await (const batch of reader) { @@ -57,7 +57,7 @@ The `RecordStreamWriter` class allows you to write Arrow `Table` and `RecordBatc A more complicated example of using Arrow to go from node -> python -> node: -```js +```typescript const { AsyncIterable } = require('ix'); const { child } = require('event-stream'); const { fork } = require('child_process'); diff --git a/docs/arrowjs/developer-guide/schemas.md b/docs/arrowjs/developer-guide/schemas.md new file mode 100644 index 0000000000..05326f764a --- /dev/null +++ b/docs/arrowjs/developer-guide/schemas.md @@ -0,0 +1,36 @@ +# Schemas + +The `Schema` class stores a list of `Field` instances that provide +information about the columns in a table: name, data type and nullability. + +A `Schema` can also contain metadata, both on the table level and on each Field. + +Every `Table` and `RecordBatch` contains a `Schema` instance. + +:::info +Note that since Arrow allows for composite columns (`List`, `Struct`, `Map_` etc), +data types can contain nested `Field` objects. +::: + +### Create a new Schema + + + + +### Working with Arrow Schemas + +Get the names of the columns in a table. + +```typescript +const fieldNames = table.schema.fields.map(f => f.name); +// Array(3) ["Latitude", "Longitude", "Date"] +``` + +```typescript +const fieldTypes = schema.fields.map(f => f.type) +// Array(3) [Float, Float, Timestamp] + +const fieldTypeNames = ...; +// Array(3) ["Float64", "Float64", "Timestamp"] +``` + diff --git a/docs/arrowjs/developer-guide/tables.md b/docs/arrowjs/developer-guide/tables.md index f1769631cc..90eff81392 100644 --- a/docs/arrowjs/developer-guide/tables.md +++ b/docs/arrowjs/developer-guide/tables.md @@ -8,7 +8,7 @@ References: Applications often start with loading some Arrow formatted data. The Arrow API provides several ways to do this, but in many cases, the simplest approach is to use `Table.from()`. -```js +```typescript import {Table} from 'apache-arrow'; const response = await fetch(dataUrl); const arrayBuffer = await response.arrayBuffer(); @@ -17,18 +17,18 @@ const dataTable = arrow.Table.from(new Uint8Array(arrayBuffer)); ## Getting Records Count -```js +```typescript const count = table.count(); ``` ### Getting Arrow Schema Metadata -```js +```typescript const fieldNames = table.schema.fields.map(f => f.name); // Array(3) ["Latitude", "Longitude", "Date"] ``` -```js +```typescript const fieldTypes = tables.schema.fields.map(f => f.type) // Array(3) [Float, Float, Timestamp] @@ -38,7 +38,7 @@ const fieldTypeNames = ...; ### Accessing Arrow Table Row Data -```js +```typescript const firstRow = tables.get(0) // 1st row data const lastRow = tables.get(rowCount-1) ``` @@ -47,7 +47,7 @@ const lastRow = tables.get(rowCount-1) It is easy to converting Rows to JSON/Arrays/Strings: -```js +```typescript toJSON = Array(3) [41.890751259, -87.71617311899999, Int32Array(2)] toArray = Array(3) [41.933659084, -87.72369064600001, Int32Array(2)] ``` @@ -66,7 +66,7 @@ range = ƒ(start, end, step) ### Iterating over Rows and Cells -```js +```typescript for (let row of dataFrame) { for (let cell of row) { if ( Array.isArray(cell) ) { @@ -86,7 +86,7 @@ for (let row of dataFrame) { Apache Arrow Timestamp is a 64-bit int of milliseconds since the epoch, represented as two 32-bit ints in JS to preserve precision. The fist number is the "low" int and the second number is the "high" int. -```js +```typescript function toDate(timestamp) { return new Date((timestamp[1] * Math.pow(2, 32) + timestamp[0])/1000); } @@ -105,7 +105,7 @@ timestamps = Array(10) [2017-01-01, 2017-01-01, 2017-01-01, 2017-01-01, 2017-01- ### Filtering Timestamped Data -```js +```typescript function filterByDate(startDate, endDate) { const dateFilter = arrow.predicate.custom(i => { const arrowDate = table.getColumn('Date').get(i); diff --git a/docs/arrowjs/developer-guide/typescript.md b/docs/arrowjs/developer-guide/typescript.md deleted file mode 100644 index ea85ffe6be..0000000000 --- a/docs/arrowjs/developer-guide/typescript.md +++ /dev/null @@ -1,11 +0,0 @@ -# Using with Typescript - -This documentation does not include advanced type definitions in the interest of simplicity and making the documentation accessible to more JavaScript developers. If you are working with Typescript in your application and would benefit from documentation that includes the Typescript definitions, you can refer to the auto generated JSDocs for the API. - -## Considerations when Using Typescript - -To ensure that type information "flows" correctly from the types of function/constructor arguments to the types of returned objects, some special methods are provided (effectively working around limitations in Typescript). - -A key example is the availability of static `new()` methods on a number of classes that are intended to be used instead of calling `new` on the constructor. Accordingly, `Table.new()` is an alternative to `new Table()`, that provides stronger type inference on the returned Table. - -You may want to leverage this syntax if your application is written in Typescript. diff --git a/docs/arrowjs/get-started/README.md b/docs/arrowjs/get-started/README.md index 2d29730a45..3e61a111f2 100644 --- a/docs/arrowjs/get-started/README.md +++ b/docs/arrowjs/get-started/README.md @@ -15,6 +15,6 @@ yarn add apache-arrow You should now be able to import arrow into your projects -```js +```typescript import {Table} from 'apache-arrow'; ``` diff --git a/docs/arrowjs/get-started/examples.md b/docs/arrowjs/get-started/examples.md index 119a1ce09a..6b51aac93e 100644 --- a/docs/arrowjs/get-started/examples.md +++ b/docs/arrowjs/get-started/examples.md @@ -4,7 +4,7 @@ Some short examples ### Get a table from an Arrow file on disk (in IPC format) -```js +```typescript import { readFileSync } from 'fs'; import { Table } from 'apache-arrow'; @@ -25,7 +25,7 @@ null, null, null ### Create a Table when the Arrow file is split across buffers -```js +```typescript import { readFileSync } from 'fs'; import { Table } from 'apache-arrow'; @@ -48,7 +48,7 @@ console.log(table.toString()); ### Create a Table from JavaScript arrays -```js +```typescript import { Table, FloatVector, @@ -73,7 +73,7 @@ const rainfall = Table.new( ### Load data with `fetch` -```js +```typescript import { Table } from "apache-arrow"; const table = await Table.from(fetch(("/simple.arrow"))); @@ -83,7 +83,7 @@ console.log(table.toString()); ### Columns look like JS Arrays -```js +```typescript import { readFileSync } from 'fs'; import { Table } from 'apache-arrow'; diff --git a/docs/arrowjs/paul-drafts/introduction.md b/docs/arrowjs/paul-drafts/introduction.md deleted file mode 100644 index dcaac801a0..0000000000 --- a/docs/arrowjs/paul-drafts/introduction.md +++ /dev/null @@ -1,54 +0,0 @@ -# Introduction - -Apache Arrow is a binary specification and set of libraries for representing Tables and Columns of strongly-typed fixed-width, variable-width, and nested data structures in-memory and over-the-wire. - -Arrow represents columns of values in sets of contiguous buffers. This is in contrast to a row-oriented representation, where the values for each row are stored in a contiguous buffer. The columnar representation makes it easier to take advantage of SIMD instruction sets in modern CPUs and GPUs, and can lead to dramatic performance improvements processing large amounts of data. - -## Components - -The Arrow library is organized into separate components responsible for creating, reading, writing, serializing, deserializing, or manipulating Tables or Columns. - -* [Data Types](/docs/arrowjs/paul-drafts/introduction#arrow-data-types) - Classes that define the fixed-width, variable-width, and composite data types Arrow can represent -* [Vectors](/docs/arrowjs/paul-drafts/introduction#arrow-vectors) - Classes to read and decode JavaScript values from the underlying buffers or Vectors for each data type -* [Builders](/docs/arrowjs/paul-drafts/introduction#arrow-builders) - Classes to write and encode JavaScript values into the underlying buffers or Vectors for each data type -* [Visitors](/docs/arrowjs/paul-drafts/introduction#arrow-visitors) - Classes to traverse, manipulate, read, write, or aggregate values from trees of Arrow Vectors or DataTypes -* [IPC Readers and Writers](/docs/arrowjs/paul-drafts/introduction#arrow-ipc-primitives) - Classes to read and write the Arrow IPC (inter-process communication) binary file and stream formats -* [Fields, Schemas, RecordBatches, Tables, and Columns](/docs/arrowjs/paul-drafts/introduction#fields-schemas-recordbatches-tables-and-columns) - Classes to describe, manipulate, read, and write groups of strongly-typed Vectors or Columns - -## [Data Types](/docs/arrowjs/paul-drafts/data-types/introduction) - -At the heart of Arrow is set of well-known logical [data types](/docs/arrowjs/paul-drafts/data-types/introduction), ensuring each Column in an Arrow Table is strongly-typed. These data types define how a Column's underlying buffers should be constructed and read, and includes configurable (and custom) metadata fields for further annotating a Column. A Schema describing each Column's name and data type is encoded alongside each Column's data buffers, allowing you to consume an Arrow data source without knowing the data types or column layout beforehand. - -Each data type falls into one of three rough categories: Fixed-width types, variable-width types, or composite types that contain other Arrow data types. All data types can represent null values, which are stored in a separate validity [bitmask](https://en.wikipedia.org/wiki/Mask_(computing)). Follow the links below for a more detailed description of each data type. - -### [Fixed-width Data Types](/docs/arrowjs/paul-drafts/data-types/introduction#fixed-width-data-types) - -Fixed-width data types describe physical primitive values (bytes or bits of some fixed size), or logical values that can be represented as primitive values. In addition to an optional [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) validity bitmask, these data types have a physical data buffer (a [`TypedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#TypedArray_objects) corresponding to the data type's physical element width). - - * [Null](/docs/arrowjs/paul-drafts/data-types/introduction#null) - A column of NULL values having no physical storage - * [Bool](/docs/arrowjs/paul-drafts/data-types/introduction#bool) - Booleans as either 0 or 1 (bit-packed, LSB-ordered) - * [Int](/docs/arrowjs/paul-drafts/data-types/introduction#int) - Signed or unsigned 8, 16, 32, or 64-bit little-endian integers - * [Float](/docs/arrowjs/paul-drafts/data-types/introduction#float) - 2, 4, or 8-byte floating point values - * [Decimal](/docs/arrowjs/paul-drafts/data-types/introduction#decimal) - Precision-and-scale-based 128-bit decimal values - * [FixedSizeBinary](/docs/arrowjs/paul-drafts/data-types/introduction#fixedsizebinary) - A list of fixed-size binary sequences, where each value occupies the same number of bytes - * [Date](/docs/arrowjs/paul-drafts/data-types/introduction#date) - Date as signed 32-bit integer days or 64-bit integer milliseconds since the UNIX epoch - * [Time](/docs/arrowjs/paul-drafts/data-types/introduction#time) - Time as signed 32 or 64-bit integers, representing either seconds, millisecond, microseconds, or nanoseconds since midnight (00:00:00) - * [Timestamp](/docs/arrowjs/paul-drafts/data-types/introduction#timestamp) - Exact timestamp as signed 64-bit integers, representing either seconds, milliseconds, microseconds, or nanoseconds since the UNIX epoch - * [Interval](/docs/arrowjs/paul-drafts/data-types/introduction#interval) - Time intervals as pairs of either (year, month) or (day, time) in SQL style - * [FixedSizeList](/docs/arrowjs/paul-drafts/data-types/introduction#fixedsizelist) - Fixed-size sequences of another logical Arrow data type - -### [Variable-width Data Types](/docs/arrowjs/paul-drafts/data-types/introduction#variable-width-data-types) - -Variable-width types describe lists of values with different widths, including binary blobs, Utf8 code-points, or slices of another underlying Arrow data type. These types store the values contiguously in memory, and have a physical [`Int32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array) of offsets that describe the start and end indicies of each list element. - - * [List](/docs/arrowjs/paul-drafts/data-types/list) - Variable-length sequences of another logical Arrow data type - * [Utf8](/docs/arrowjs/paul-drafts/data-types/utf8) - Variable-length byte sequences of UTF8 code-points (strings) - * [Binary](/docs/arrowjs/paul-drafts/data-types/binary) - Variable-length byte sequences (no guarantee of UTF8-ness) - -### [Composite Data Types](/docs/arrowjs/paul-drafts/data-types/introduction#composite-data-types) - -Composite types don't have physical data buffers of their own. They contain other Arrow data types and delegate work to them. - - * [Union](/docs/arrowjs/paul-drafts/data-types/union) - Union of logical child data types - * [Map](/docs/arrowjs/paul-drafts/data-types/map) - Map of named logical child data types - * [Struct](/docs/arrowjs/paul-drafts/data-types/struct) - Struct of ordered logical child data types diff --git a/docs/arrowjs/paul-drafts/ipc/index.md b/docs/arrowjs/paul-drafts/ipc/index.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs/arrowjs/paul-drafts/tables/index.md b/docs/arrowjs/paul-drafts/tables/index.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs/arrowjs/paul-drafts/vectors/index.md b/docs/arrowjs/paul-drafts/vectors/index.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs/arrowjs/paul-drafts/visitors/index.md b/docs/arrowjs/paul-drafts/visitors/index.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs/arrowjs/upgrade-guide.md b/docs/arrowjs/upgrade-guide.md new file mode 100644 index 0000000000..8aae35ca54 --- /dev/null +++ b/docs/arrowjs/upgrade-guide.md @@ -0,0 +1,79 @@ +# Upgrade Guide + +Unfortunately for JavaScript users, Apache Arrow JS does not publish detailed ugrade guides notes beyond the common [Apache Arrow release notes](https://arrow.apache.org/release/). + +Also Apache Arrow JS follows a common cross-language versioning number scheme which leads to frequent major release bumps, that confusingly do not contain any significant JavaScript changes (sometimes a major version bump has no JavaScript changes at all). + +The biggest changes were made in Apache Arrow JS Version 9.0 (based on feedback from loaders.gl users). + +## Upgrading to v13.0 + +- No significant changes in Apache Arrow JS +- [Apache Arrow 13.0.0](https://arrow.apache.org/release/12.0.0.html) + +## Upgrading to v12.0 + +- Under the hood, Apache Arrow JS removed "big int" fallback handling (big ints are now supported by all current browsers and Node.js versions). +- Bug found: Can break table reads in rare cases, e.g when dicts have big int keys. +- [Apache Arrow 12.0.0](https://arrow.apache.org/release/12.0.0.html) + +## Upgrading to v11.0 + +- No significant changes in Apache Arrow JS +- [Apache Arrow 11.0.0](https://arrow.apache.org/release/11.0.0.html) + +## Upgrading to v10.0 + +- No significant changes in Apache Arrow JS +- [Apache Arrow 10.0.0](https://arrow.apache.org/release/10.0.0.html) + +## Upgrading to v9.0 + + +In case it is helpful, changes made to loaders.gl can be found in this [PR](https://github.com/visgl/loaders.gl/pull/2276/files) + +## Upgrading to v7.0 / v8.0 / v9.0 + +- [Apache Arrow 7.0.0](https://arrow.apache.org/release/7.0.0.html) +- [Apache Arrow 8.0.0](https://arrow.apache.org/release/8.0.0.html) +- [Apache Arrow 9.0.0](https://arrow.apache.org/release/9.0.0.html) + +These releases made a series of breaking changes to Apache Arrow JS to transform it into a lean, tree-shakeable "core" library. + +The good news is that Apache Arrow v9.0 resolves a big concern around the size of the ArrowJS library. The size issue was creating resistance against full-scale Arrow JS adoption in some code bases. For instance, in loaders.gl, even trivial usage of the loaders.gl `ArrowLoader` would lead to ~250KB of Apache Arrow dependencies being bundled before v9.0. + +The downside is that upgrading through Arrow JS v7.0-v9.0 tends to require a big effort for most older applications. This is made more difficult since Apache Arrow does not have good release notes. The following are observations from upgrading applications: + +**Removed core classes** + +| Removed Feature | Alternative | Comment | +| --------------- | -------------- | --------------------------------------------------------------------------------- | +| `Column` class | `Vector` class | The `Vector` class now supports chunking, removing the need for a `Column` class. | + + +**Removed static constructors** + +| Removed Feature | Alternative | Comment | +| ------------------------------- | ----------------------- | --------------------------------------------------------------------------------- | +| `Data` static factory methods | `makeData()` function | Referencing the Data class doesn't automatically pull in static constructor code. | +| `Column` static factory methods | `makeVector()` function | +| `Table` static factory methods | `makeTable()` function | +| `Schema` static factory methods | `makeSchema()` function | + + +**DataFrame removal** - A number of pre-9.0 features didn’t really fit into Arrow core functionality. These features were really a library on top of Arrow, and in the trade-off of keeping the Arrow JS core lean, they were removed. + +| Removed Feature | Alternative | Comment | +| ------------------- | ----------- | --------- | +| `DataFrame` | N/A | See below | +| `FilteredDataFrame` | N/A | See below | +| Predicates | N/A | See below | +| `Table.filter` | N/A | See below | + +While there are no alternatives for the removed features inside Apache Arrow JS v9.0+, applications can implement similar logic on top of Arrow JS. There are also high-quality independent libraries such as [Arquero](https://github.com/uwdata/arquero) that provide support for filtering and processing of Arrow JS tables. + +Finally, in case it is helpful, changes made to loaders.gl can be found in this [PR](https://github.com/visgl/loaders.gl/pull/1931/files) + +## Upgrading to v6.0 and earlier + +Unfortunately we don't have any release notes for these releases. diff --git a/docs/arrowjs/whats-new.md b/docs/arrowjs/whats-new.md index 37adf03b10..aeffa3a693 100644 --- a/docs/arrowjs/whats-new.md +++ b/docs/arrowjs/whats-new.md @@ -1,20 +1,85 @@ # What's New -# v0.4.1 +Unfortunately for JavaScript users, Apache Arrow JS does not publish detailed release notes beyond the common [Apache Arrow release notes](https://arrow.apache.org/release/). -TBA +:::caution +Apache Arrow JS follows the versioning number scheme for the cross-language Apache Arrow repository releases, +which results in frequent major release bumps, without any significant JavaScript changes being introduced. +This can be an inconvenience for JavaScript applications that rely on [semantic versioning](https://semver.org) to restrict dependencies to compatible package. Therefore some extra attention around versions may be required, especially if your app uses multiple JavaScript packages dependent on arrow. You may end up bundling two different arrow js versions or the build may break due to version requirement incompatibility. +::: -# v0.4.0 -TBA +## v13.0 +August 23, 2023 -# v0.3.0 +- [Apache Arrow 13.0.0](https://arrow.apache.org/release/13.0.0.html) +- Apache Arrow JS: No significant changes. -TBA +## v12.0 +- [Apache Arrow 12.0.0](https://arrow.apache.org/release/12.0.0.html) +- Apache Arrow JS: No significant changes. +- Apache Arrow JS: removed "big int" fallback handling, as browser support is now ubiquitous. -# v0.3.0 +## v11.0 -TBA +- [Apache Arrow 11.0.0](https://arrow.apache.org/release/11.0.0.html) +- Apache Arrow JS: No significant changes. + +## v10.0 + +- [Apache Arrow 10.0.0](https://arrow.apache.org/release/10.0.0.html) +- Apache Arrow JS: No significant changes. + +## v9.0 + +- [Apache Arrow 9.0.0](https://arrow.apache.org/release/9.0.0.html) +- Apache Arrow JS: Breaking API changes, see [Upgrade Guide](./upgrade-guide). +- Apache Arrow JS: Smaller, focused, tree-shakeable API. +- Apache Arrow JS: Removes non-core functionality from the Arrow JS library. + +## v8.0 + +- [Apache Arrow 8.0.0](https://arrow.apache.org/release/8.0.0.html) +- Apache Arrow JS: Breaking API changes, see [Upgrade Guide](./upgrade-guide). +- Apache Arrow JS: Smaller, focused, tree-shakeable API. +- Apache Arrow JS: Removes non-core functionality from the Arrow JS library. + +## v7.0 + +- [Apache Arrow 7.0.0](https://arrow.apache.org/release/7.0.0.html) +- Apache Arrow JS: Breaking API changes, see [Upgrade Guide](./upgrade-guide). +- Apache Arrow JS: Smaller, focused, tree-shakeable API. +- Apache Arrow JS: Removes non-core functionality from the Arrow JS library. + +## v6.0 + +- [Apache Arrow 6.0.0](https://arrow.apache.org/release/6.0.0.html) +- Changes in Apache Arrow JS: N/A + +## v5.0 + +- [Apache Arrow 5.0.0](https://arrow.apache.org/release/5.0.0.html) +- Changes in Apache Arrow JS: N/A + +## v4.0 + +- [Apache Arrow 4.0.0](https://arrow.apache.org/release/4.0.0.html) +- Changes in Apache Arrow JS: N/A + +## v3.0 + +- [Apache Arrow 3.0.0](https://arrow.apache.org/release/3.0.0.html) +- Changes in Apache Arrow JS: N/A + +## v2.0 + +- [Apache Arrow 2.0.0](https://arrow.apache.org/release/2.0.0.html) +- Changes in Apache Arrow JS: N/A + +## v1.0 + +- [Apache Arrow 1.0.0](https://arrow.apache.org/release/1.0.0.html) +- Initial version diff --git a/docs/developer-guide/composite-loaders.md b/docs/developer-guide/composite-loaders.md new file mode 100644 index 0000000000..7860b3a4b6 --- /dev/null +++ b/docs/developer-guide/composite-loaders.md @@ -0,0 +1,44 @@ +# Composite Loaders + +loaders.gl enables loaders to call other loaders (referred to as "sub-loaders" in this section). +This enables loaders for "composite formats" to be "composed" out of loaders for the primitive parts. + +An example of sub-loaders can be seen in the `GLTFLoader`, which delegates: +- Draco mesh decoding to the `DracoLoader` +- image decoding to the various `ImageLoaders` and the `BasisLoader`. + +Naturally, composite loaders can call other composite loaders, which is for instance used by +the `Tiles3DLoader` which uses the `GLTFLoader` to parse embedded glTF data in certain tiles. + +## Calling loaders inside loaders + +To call another loader from the parse function of your loader, use the appropriate `parse*WithContext()` function provided by `@loaders.gl/loader-utils`. +Make sure to supply in the `context` parameter . + +A conceptual example of a 3D Tiles loader calling the `GLTFLoader` with some additional options. + +```typescript +import {parseWithContext} from '@loaders.gl/loader-utils'; + +export async function parse3DTile(arrayBuffer, options, context) { + const tile = {}; + // Extract embedded GLB (if present) into `tile.gltfArrayBuffer` + ... + if (tile.gltfArrayBuffer) { + tile.gltf = await parseWithContext(tile.gltfArrayBuffer, GLTFLoader, {gltf: {...}}, context); + } +} +``` + +:::caution +While loaders could import `@loaders.gl/core` and use the core `parse*()` functions to call sub-loaders, +it is strongly discouraged for multiple reasons. +Most importantly it prevents loaders.gl from properly handling certain use cases +such as allowing worker-loaders to call other loaders. +The `parse*WithContext()` functions exported by `@loaders.gl/loader-utils` are the right tool for the job. +::: + +- +## LoaderContext + +When a loader is being called (i.e. one of its `parse*()` functions is being called), a `LoaderContext` object is supplied. diff --git a/docs/developer-guide/concepts/async-iterators.md b/docs/developer-guide/concepts/async-iterators.md index d864438cde..c23cf356e3 100644 --- a/docs/developer-guide/concepts/async-iterators.md +++ b/docs/developer-guide/concepts/async-iterators.md @@ -1,20 +1,53 @@ # AsyncIterators -Streaming functionality in loaders.gl is built on the ES2018 `AsyncIterator` concept. This page gives some background on AsyncIterator since it is a recently introduced concept (at least as part of the JavaScript standard). +Streaming functionality in loaders.gl is built on the ES2018 `AsyncIterator` concept. +This page gives some background on `AsyncIterator`. ## Availability -`AsyncIterator` is a standard JavaScript ES2018 feature and is well supported by recent evergreen browsers and Node.js versions. +`AsyncIterator` and the `for await of` iteration syntax are standard JavaScript ES2018 features and are supported by all recent evergreen browsers and Node.js versions as well as e.g. the babel transpiler. -The `for await of` iteration syntax is supported as well as the babel transpiler. +## Iterators and TypeScript + +There are multiple similar-sounding types supporting the type safe use of iterators which can be a source of confusion to users, so some information is provided here: + +| Type | Async Type | Type Parameters | + Description | +| ------------------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| `Iterator` | `AsyncIterator` | `<...>` | An iterator has `next()`, ... methods | +| `Iterable` | `AsyncIterable` | `<...>` | An iterable is a class that has a `[Symbol.iterator]` or `[Symbol.asyncIterator]` property that returns an `Iterator` or `AsyncIterator` | +| `Generator` | `AsyncGenerator` | `<...>` | A generator is a function that takes some parameters and when called returns an `Iterator` or `AsyncIterator` | +| `IterableIterator` | `AsyncIterableIterator` | `<...>` | It is convenient to define `Iterator`s that are also `Iterable`. Most built in container classes return this type. | + +An `IterableIterator` can +- be used in a for..of loop +- be spread into an array +- be spread into a parameter list +- be used in APIs that accept iterables like `Array.from()`, `new Set()`, `new Map()` + +```typescript +interface Iterable { + [Symbol.iterator]() : Iterator; +} +interface Iterator { + next() : IteratorResult; + return?(value? : any) : IteratorResult; +} +interface IteratorResult { + value: any; + done: boolean; +} +``` + +https://exploringjs.com/es6/ch_iteration.html#sec_implementing-iterables ## Batched Parsing and Endcoding using AsyncIterators The input and output from streaming loaders and writers can both be expressed in terms of async iterators. -## Using AsyncIterator +## Using AsyncIterators -Remember tyhat an async iterator can be consumed (iterated over) via the for-await construct: +Remember that an `AsyncIterator` or `AsyncIterable` can be consumed (iterated over) via the for-await construct: ```typescript for await (const x of asyncIterable) { @@ -33,12 +66,14 @@ for await (const buf of fs.createReadStream('foo.txt')) { ## Creating AsyncIterators -Remember that any object in JavaScript that implements the `[Symbol.asyncIterator]()` method is an `AsyncIterable`. And the async generator syntax can be used to generate new async iterators +Remember that any object in JavaScript that implements the `[Symbol.asyncIterator]()` method is an `AsyncIterable`. + +And the async **generator** syntax `async function *` can be used to generate new async iterators ```typescript -async function* asyncIterator() { +async function * makeAsyncIterator() { yield new Promise(...) } -for await (const x of asyncIterator()) {} // Notice parens after 'asyncIterator' +for await (const x of makeAsyncIterator()) {} // Notice parens after 'makeAsyncIterator' ``` diff --git a/docs/developer-guide/concepts/javascript-apis.md b/docs/developer-guide/concepts/javascript-apis.md new file mode 100644 index 0000000000..f3546beb2a --- /dev/null +++ b/docs/developer-guide/concepts/javascript-apis.md @@ -0,0 +1,50 @@ +# Preferred JavaScript APIs + +loaders.gl supports input and output of data from JavaScript/TypeScript programs. To do this it is necessary to use platform APIs for +- loading data from files and URLs +- writing data to files +- manipulating binary data +- parsing images +- etc + +Over the years, a number of different JavaScript APIs have emerged. Depending on the version of JavaScript supported by a browser or the version of Node.js being used. In addition, the set of available APIs move + +## Loading data with `fetch()` + +loaders.gl standardizes on the `fetch()` API. The result of a `fetch` operation is a `Response` object which can be passed to many loaders.gl functions, meaning that the application can call `fetch()` itself to fully control the requests. + +:::information +The `fetch()` API emerged in browsers, but is now also supported natively on Node, starting with Node.js v18. +For older Node.js versions, `@loaders.gl/polyfills` installs polyfills of `fetch`, `Response` and `Headers` classes. +::: + +## Local file access + +loaders.gl offers a `FileSystem`, `ReadableFile` and `WritableFile` interfaces, and various implementations of these. + +For local file access in the browser, the `File` class (a derivate of `Blob`, see below) is the tool of choice. +It is not clear if a counterpart to the `File` class will eventually be supported by Node.js. + +> Note that reading local files in the browser has limitations. Actual file paths are obscured and files can only be created as a result of an interactive file selection or file drop action by the user. + +## Saving data + +Saving data from a browser is either done by POST requests to a server, or via local downloads. + +## Binary data APIs + +The choice of binary data API in JavaScript usually comes down to either using Node.js `Buffer` class or a combination of `ArrayBuffer`, `TextEncoder`/`TextDecoder` classes. + +The `Buffer` class in Node.js is not supported by browsers. Polyfills are available, but they can add considerable size (~50KB) to an application, and can cause small but frustrating bundling issues. + +:::caution +Therefore loaders.gl tries to avoid use of the `Buffer` class in its core libraries and loaders, preferring to use `ArrayBuffer`, typed arrays and `Blob`s. +::: + +The `Blob` (and `File`) classes in the browser have some unique advantages. They leverage an efficient blob storage mechanism in the browser, and they also enable partial, random-access reads from large blobs in that storage or from local files. `Blob`s are available in Node starting with Node.js v18. For lower versions, a polyfill will be installed by `@laoders.gl/polyfills`. + +## Image APIs + +The preferred image platform API is the `ImageBitmap`. It is currently only supported on modern browsers, not in Node.js. + +At this stage loaders.gl does not provide an ImageBitmap polyfill, and it is not clear if future versions of Node.js would support something similar natively. diff --git a/docs/developer-guide/creating-loaders-and-writers.md b/docs/developer-guide/creating-loaders-and-writers.md index 96289d7bfb..85f34c23de 100644 --- a/docs/developer-guide/creating-loaders-and-writers.md +++ b/docs/developer-guide/creating-loaders-and-writers.md @@ -36,39 +36,9 @@ In general, it is recommended that loaders are "standalone" and avoid importing ## Creating Composite Loaders -loaders.gl enables loaders to call other loaders (referred to as "sub-loaders" in this section). This enables loaders for "composite formats" to be "composed" out of loaders for the primitive parts. - -Good examples of sub-loaders are the `GLTFLoader` which can delegate Draco mesh decoding to the `DracoLoader` and image decoding to the various `ImageLoaders` and the `BasisLoader`. - -Naturally, Composite loaders can call other composite loaders, which is for instance used by the `Tiles3DLoader` which uses the `GLTFLoader` to parse embedded glTF data in certain tiles. - -## Calling loaders inside loaders - -To call another loader, a loader should use the appropriate `parse` function provided in the `context` parameter. - -A conceptual example of a 3D Tiles loader calling the `GLTFLoader` with some additional options. - -```typescript -export async function parse3DTile(arrayBuffer, options, context) { - const tile = {}; - // Extract embedded GLB (if present) into `tile.gltfArrayBuffer` - ... - if (tile.gltfArrayBuffer) { - const {parse} = context; - tile.gltf = await parse(tile.gltfArrayBuffer, GLTFLoader, { - gltf: {...} - }); - } -} -``` - -Remarks: - -- While a loader could potentially import `parse` from `@loaders.gl/core` to invoke a sub-loader, it is discouraged, not only from a dependency management reasons, but it prevents loaders.gl from properly handling parameters and allow worker-loaders to call other loaders. - -## LoaderContext - -When a loader is being called (i.e. one of its `parse*()` functions is being called), a `LoaderContext` object is supplied. +loaders.gl enables loaders to call other loaders (referred to as "sub-loaders" in this section). +This enables loaders for "composite formats" to be "composed" out of loaders for the primitive parts. +For more information see [Composite Loaders](./composite-loaders). ## Accessing the Response object diff --git a/docs/developer-guide/node.md b/docs/developer-guide/node.md new file mode 100644 index 0000000000..3eb90e13a3 --- /dev/null +++ b/docs/developer-guide/node.md @@ -0,0 +1,27 @@ +# Node.js support + +Firstly, to run loaders.gl on Node.js you want to import the `@loaders.gl/polyfills` module. + +Also it is good to understand that loaders.gl avoids using Node.js specific APIs (such as Buffer, path, util, fs, streams etc) instead favoring browser compatible equivalents. loaders.gl is optimized for cross-platform compatible APIs. + +However, if your goal is to write Node.js-idiomatic code rather than browser-portable code, you may find that working with loaders.gl can require some extra work. + +## Polyfills + +To install these polyfills, just `import` the polyfills module before start using loaders.gl. + +```typescript +import '@loaders.gl/polyfills'; +import {parse} from '@loaders.gl/core'; +``` + +## Combining with other Polyfills + +loaders.gl only installs polyfills if the corresponding global symbol is `undefined`. This means that if another polyfill is already installed when `@loaders.gl/polyfills` is imported, the other polyfill will remain in effect. Since most polyfill libraries work this way, applications can mix and match polyfills by ordering the polyfill import statements appropriately (but see the remarks below for a possible caveat). + +## Provided Polyfills + +See [API Reference](/docs/modules/polyfills/api-reference). + +## Remarks + diff --git a/docs/developer-guide/polyfills.md b/docs/developer-guide/polyfills.md deleted file mode 100644 index f9e20112c3..0000000000 --- a/docs/developer-guide/polyfills.md +++ /dev/null @@ -1,28 +0,0 @@ -# Polyfills - -Older browsers (mainly Edge and IE11) as well as Node.js do not provide certain APIs (`TextEncoder`, `fetch` etc) that loaders.gl depends on. - -The good news is that these APIs can be provided by the application using the [polyfill]() technique. - -While there are many good polyfill modules for these classes available on `npm`, to make the search for a version that is guaranteed to work with loaders.gl a little easier, the `@loaders.gl/polyfills` module is provided. - -To install these polyfills, just `import` the polyfills module before start using loaders.gl. - -```typescript -import '@loaders.gl/polyfills'; -import {parse} from '@loaders.gl/core'; -``` - -## Combining with other Polyfills - -loaders.gl only installs polyfills if the corresponding global symbol is `undefined`. This means that if another polyfill is already installed when `@loaders.gl/polyfills` is imported, the other polyfill will remain in effect. Since most polyfill libraries work this way, applications can mix and match polyfills by ordering the polyfill import statements appropriately (but see the remarks below for a possible caveat). - -## Provided Polyfills - -See [API Reference](/docs/modules/polyfills/api-reference). - -## Remarks - -Applications should typically only install this module if they need to run under older environments. While the polyfills are only installed at runtime if the platform does not already support them, they will still be included in your application bundle, i.e. importing the polyfill module will increase your application's bundle size. - -When importing polyfills for the same symbol from different libraries, the import can depend on how the other polyfill is written. to control the order of installation, you may want to use `require` rather than `import` when importing `@loaders.gl/polyfills`. As a general rule, `import` statements execute before `require` statments. diff --git a/docs/developer-guide/standards.md b/docs/developer-guide/standards.md index 963f49ea20..db130b4cfd 100644 --- a/docs/developer-guide/standards.md +++ b/docs/developer-guide/standards.md @@ -38,4 +38,4 @@ Developing standards | Format | | ------------------------------------------------------------------------------------ | ----------------------- | | Shapefile | `@loaders.gl/shapefile` | -| [**LERC**](/docs/modules/wms/formats/lerc) (Limited Error Raster Compression) format | `@loaders.gl/wms` | . | +| [**LERC**](/docs/modules/lerc/formats/lerc) (Limited Error Raster Compression) format | `@loaders.gl/wms` | . | diff --git a/docs/developer-guide/using-loaders.md b/docs/developer-guide/using-loaders.md index 5490478cac..82d173526b 100644 --- a/docs/developer-guide/using-loaders.md +++ b/docs/developer-guide/using-loaders.md @@ -19,7 +19,7 @@ data = await load(url, CSVLoader); ... ``` -## Specifying and Registering Loaders +## Specifying Loaders As seen above can be specified directly in a call to `load` or any of the `parse` functions: @@ -34,7 +34,49 @@ const pointCloud = await load(url, [PCDLoader, LASLoader]); ... ``` -Loaders can also be registered globally. To register a loader, use `registerLoaders`: +### Loaders and TypeScript + +Since v4.0, all loaders are typed, meaning that loaders.gl can infer types for returned data and loader options from the supplied loader + + +Note that type inference only works when single loader is provided: + +```typescript +import {load} from '@loaders.gl/core'; +import {PCDLoader} from '@loaders.gl/pcd'; +import {LASLoader} from '@loaders.gl/las'; + +// Single loader infers type +const pcdPointCloud = await load(url, PCDLoader); // => type PCDMesh +const lasPointCloud = await load(url, LASLoader); // => type LASMesh + +const pointCloud = await load(url, [PCDLoader, LASLoader]); // => type unknown +``` + +Note that you can use selectLoader and a switch statement to remain typed + +```typescript +import {load} from '@loaders.gl/core'; +import {PCDLoader} from '@loaders.gl/pcd'; +import {LASLoader} from '@loaders.gl/las'; + +const loader = await selectLoader(url, [PCDLoader, LASLoader]); +switch (loader.id) { + case: 'pcd': + const pcdPointCloud = await load(url, PCDLoader); // => type PCDMesh + break; + case 'las': + const lasPointCloud = await load(url, LASLoader); // => type LASMesh + break; +} +``` + +### Registering Loaders + +Loaders can also be registered globally. To register a loader, use `registerLoaders()`. +Registered loaders will be included in loader selection if you call any form of +`parse()` or `load()` that does not specify a single loader. + ```typescript import {registerLoaders, load} from '@loaders.gl/core'; @@ -45,6 +87,15 @@ registerLoaders([CSVLoader]); data = await load('url.csv'); // => CSVLoader selected from pre-registered loaders ``` +Note that in this case the loader type is not known and the return type will be unknown. + +:::caution +Relying on global state (such as set by `registerLoaders()`) is not a +recommended application development practice. +It sometimes causes problems later, as it tends to create unexpected dependencies between distant parts of the code. +The mechanism is provided but the choice to use it is yours. +::: + ## Selecting Loaders The loader selection algorithm is exposed to applications via `selectLoader`: diff --git a/docs/developer-guide/using-worker-loaders.md b/docs/developer-guide/using-worker-loaders.md index ef13b625a3..5c818a0e84 100644 --- a/docs/developer-guide/using-worker-loaders.md +++ b/docs/developer-guide/using-worker-loaders.md @@ -88,7 +88,7 @@ This can be useful e.g. when building applications that cannot access CDNs or wh ## Composite Loaders and Workers (Advanced) -loaders.gl supports sub-loader invocation from worker loaders. This is somewhat experimental +loaders.gl supports sub-loader invocation from worker loaders. A worker loader starts a seperate thread with a javascript bundle that only contains the code for that loader, so a worker loader needs to call the main thread (and indirectly, potentially another worker thread with another worrker loader) to parse using a sub-loader, properly transferring data into and back from the other thread. diff --git a/docs/developer-guide/using-writers.md b/docs/developer-guide/using-writers.md index 4985e3f648..6211243572 100644 --- a/docs/developer-guide/using-writers.md +++ b/docs/developer-guide/using-writers.md @@ -4,9 +4,6 @@ Writers allow applications to encoded data for a number of the formats supported For a detailed specification of the writer object format see the [API reference](docs/specifications/writer-object-format). -> Writers and the `encode` functions are available for use, however they are considered experimental. -Writer support is still in development, and have issues. Also not all formats have writers. - ## Usage As an example, to Draco-compress a mesh using the `DracoWriter`: diff --git a/docs/docs-sidebar.json b/docs/docs-sidebar.json index dab2d6de03..bca57083f5 100644 --- a/docs/docs-sidebar.json +++ b/docs/docs-sidebar.json @@ -22,7 +22,7 @@ "developer-guide/loader-categories", "developer-guide/using-worker-loaders", "developer-guide/using-streaming-loaders", - "developer-guide/polyfills" + "developer-guide/node" ] }, { @@ -31,6 +31,7 @@ "items": [ "developer-guide/dependencies", "developer-guide/creating-loaders-and-writers", + "developer-guide/composite-loaders", "developer-guide/error-handling", "developer-guide/dev-env" ] @@ -39,6 +40,7 @@ "type": "category", "label": "Concepts", "items": [ + "developer-guide/concepts/javascript-apis", "developer-guide/concepts/binary-data", "developer-guide/concepts/streaming", "developer-guide/concepts/async-iterators", @@ -47,6 +49,20 @@ } ] }, + { + "type": "category", + "label": "Tutorials", + "items": [ + { + "type": "category", + "label": "I3S", + "items": [ + "modules/i3s/recipes/building-scene-layer", + "modules/i3s/recipes/attribute-driven-colorization" + ] + } + ] + }, { "type": "category", "label": "Formats", @@ -56,10 +72,20 @@ "modules/csv/formats/csv", "modules/pcd/formats/pcd", "modules/ply/formats/ply", + "modules/json/formats/geojson", + "modules/json/formats/geojson-geometry", "modules/gltf/formats/glb", "modules/gltf/formats/gltf", + "modules/geopackage/formats/geopackage", + "modules/kml/formats/kml", + "modules/kml/formats/gpx", + "modules/kml/formats/tcx", + "modules/las/formats/las", + "modules/mvt/formats/mvt", + "modules/mvt/formats/tilejson", "modules/parquet/formats/parquet", "modules/parquet/formats/geoparquet", + "modules/pmtiles/formats/pmtiles", "modules/shapefile/formats/shapefile", "modules/textures/formats/compressed-textures", "modules/wms/formats/csw", @@ -67,7 +93,10 @@ "modules/wms/formats/wmts", "modules/wms/formats/wfs", "modules/wms/formats/gml", - "modules/wms/formats/lerc", + "modules/lerc/formats/lerc", + "modules/wkt/formats/wkt", + "modules/wkt/formats/wkb", + "modules/wkt/formats/wkt-crs", "modules/xml/formats/xml", "modules/zip/formats/zip" ] @@ -89,6 +118,13 @@ "specifications/category-3d-tiles" ] }, + { + "type": "category", + "label": "Sources and Services", + "items": [ + "modules/pmtiles/api-reference/pmtiles-source" + ] + }, "modules/3d-tiles/api-reference/tiles-3d-loader", "modules/3d-tiles/api-reference/cesium-ion-loader", "modules/arrow/api-reference/arrow-loader", @@ -110,12 +146,14 @@ "modules/json/api-reference/json-loader", "modules/json/api-reference/ndjson-loader", "modules/json/api-reference/geojson-loader", + "modules/json/api-reference/geojson-writer", "modules/json/api-reference/ndgeojson-loader", "modules/kml/api-reference/gpx-loader", "modules/kml/api-reference/kml-loader", "modules/kml/api-reference/tcx-loader", "modules/las/api-reference/las-loader", "modules/mvt/api-reference/mvt-loader", + "modules/mvt/api-reference/tilejson-loader", "modules/obj/api-reference/obj-loader", "modules/parquet/api-reference/parquet-loader", "modules/pcd/api-reference/pcd-loader", @@ -130,10 +168,14 @@ "modules/textures/api-reference/crunch-loader", "modules/textures/api-reference/npy-loader", "modules/video/api-reference/video-loader", - "modules/wkt/api-reference/wkb-loader", - "modules/wkt/api-reference/wkb-writer", "modules/wkt/api-reference/wkt-loader", "modules/wkt/api-reference/wkt-writer", + "modules/wkt/api-reference/wkb-loader", + "modules/wkt/api-reference/wkb-writer", + "modules/wkt/api-reference/twkb-loader", + "modules/wkt/api-reference/twkb-writer", + "modules/wkt/api-reference/wkt-crs-loader", + "modules/wkt/api-reference/wkt-crs-writer", "modules/wms/api-reference/wms-capabilities-loader", "modules/xml/api-reference/xml-loader", "modules/zip/api-reference/zip-loader", @@ -165,11 +207,19 @@ "modules/core/api-reference/set-path-prefix", "modules/core/api-reference/binary-utilities", "modules/core/api-reference/iterator-utilities", - "modules/loader-utils/api-reference/request-scheduler", "specifications/loader-object-format", "specifications/writer-object-format" ] }, + { + "type": "category", + "label": "@loaders.gl/loader-utils", + "items": [ + "modules/loader-utils/README", + "modules/loader-utils/api-reference/request-scheduler", + "modules/loader-utils/api-reference/parse-with-context" + ] + }, { "type": "category", "label": "@loaders.gl/3d-tiles", @@ -261,6 +311,11 @@ "label": "@loaders.gl/las", "items": ["modules/las/README"] }, + { + "type": "category", + "label": "@loaders.gl/lerc", + "items": ["modules/lerc/README"] + }, { "type": "category", "label": "@loaders.gl/mvt", @@ -286,6 +341,11 @@ "label": "@loaders.gl/ply", "items": ["modules/ply/README"] }, + { + "type": "category", + "label": "@loaders.gl/pmtiles", + "items": ["modules/pmtiles/README"] + }, { "type": "category", "label": "@loaders.gl/polyfills", @@ -371,6 +431,8 @@ "label": "Command-Line Reference", "items": [ "modules/tile-converter/cli-reference/tile-converter", + "modules/tile-converter/cli-reference/i3s-server", + "modules/tile-converter/cli-reference/slpk-extractor", "modules/tile-converter/cli-reference/supported-features" ] } diff --git a/docs/modules/3d-tiles/README.md b/docs/modules/3d-tiles/README.md index ebb76438b3..1f7baa6689 100644 --- a/docs/modules/3d-tiles/README.md +++ b/docs/modules/3d-tiles/README.md @@ -35,7 +35,7 @@ To handle the complex dynamic tile selection and loading required to performantl Basic API usage is illustrated in the following snippet. Create a `Tileset3D` instance, point it a valid tileset URL, set up callbacks, and keep feeding in new camera positions: -```js +```typescript import {load} from '@loaders.gl/core'; import {Tiles3DLoader} from '@loaders.gl/3d-tiles'; import {Tileset3D} from '@loaders.gl/tiles'; @@ -52,7 +52,7 @@ const tileset3d = new Tileset3D(tilesetJson, { tileset3d.update(viewport); // Viewport changes (pan zoom etc) -tileset3d.update(viewport); +tileset3d.selectTiles(viewport); // Visible tiles const visibleTiles = tileset3d.tiles.filter((tile) => tile.selected); diff --git a/docs/modules/3d-tiles/api-reference/cesium-ion-loader.md b/docs/modules/3d-tiles/api-reference/cesium-ion-loader.md index 96736ace0f..2fa64642ea 100644 --- a/docs/modules/3d-tiles/api-reference/cesium-ion-loader.md +++ b/docs/modules/3d-tiles/api-reference/cesium-ion-loader.md @@ -9,7 +9,7 @@ Parse [3D tile](https://github.com/AnalyticalGraphicsInc/3d-tiles) fetched from Load a tileset file from Cesium ion server. -```js +```typescript import {load} from '@loaders.gl/core'; import {CesiumIonLoader} from '@loaders.gl/3d-tiles'; import {WebMercatorViewport} from '@deck.gl/core'; @@ -31,7 +31,7 @@ const tilesetJson = await load(tilesetUrl, CesiumIonLoader, { }); const viewport = new WebMercatorViewport({latitude, longitude, zoom}); -tileset3d.update(viewport); +tileset3d.selectTiles(viewport); // visible tiles const visibleTiles = tileset3d.tiles.filter((tile) => tile.selected); diff --git a/docs/modules/3d-tiles/api-reference/tiles-3d-loader.md b/docs/modules/3d-tiles/api-reference/tiles-3d-loader.md index eaae53f212..4c83437469 100644 --- a/docs/modules/3d-tiles/api-reference/tiles-3d-loader.md +++ b/docs/modules/3d-tiles/api-reference/tiles-3d-loader.md @@ -25,7 +25,7 @@ As a tileset contains multiple file formats, `Tiles3DLoader` is needed to be exp Load a tileset file. -```js +```typescript import {load} from '@loaders.gl/core'; import {Tiles3DLoader} from '@loaders.gl/3d-tiles'; const tilesetUrl = 'https://assets.ion.cesium.com/43978/tileset.json'; @@ -34,7 +34,7 @@ const tilesetJson = await load(tilesetUrl, Tiles3DLoader); To decompress tiles containing Draco compressed glTF models or Draco compressed point clouds: -```js +```typescript import {load, registerLoaders} from '@loaders.gl/core'; import {Tiles3DLoader} from '@loaders.gl/3d-tiles'; const tileUrl = 'https://assets.ion.cesium.com/43978/1.pnts'; @@ -43,7 +43,7 @@ const tile = await load(tileUrl, Tiles3DLoader, {decompress: true}); Load a tileset and dynamically load/unload tiles based on viewport with helper class `Tileset3D` (`@loaders.gl/tiles`) -```js +```typescript import {load} from '@loaders.gl/core'; import {Tileset3D} from '@loaders.gl/tiles'; import {Tiles3DLoader} from '@loaders.gl/3d-tiles'; @@ -56,16 +56,28 @@ const tilesetJson = await load(tilesetUrl, Tiles3DLoader); const tilesetJson = await load(tilesetUrl, Tiles3DLoader, {'3d-tiles': {isTileset: true}}); const tileset3d = new Tileset3D(tilesetJson, { + throttleRequests: false, onTileLoad: tile => console.log(tile) }); -const viewport = new WebMercatorViewport({latitude, longitude, zoom, ...}); -tileset3d.update(viewport); +const viewport = new WebMercatorViewport({ + width: 600, + height: 400, + latitude: 40.7067584, + longitude: -74.0115413, + zoom: 17 +}); +tileset3d.selectTiles(viewport); // visible tiles const visibleTiles = tileset3d.tiles.filter(tile => tile.selected); // Note that visibleTiles will likely not immediately include all tiles // tiles will keep loading and file `onTileLoad` callbacks + +// To fully load all tiles in a given view, repeatedly select tiles until the tileset is loaded +while (!tileset3d.isLoaded()) { + await tileset3d.selectTiles(viewport); +} ``` ## Options diff --git a/docs/modules/arrow/api-reference/arrow-loader.md b/docs/modules/arrow/api-reference/arrow-loader.md index 0552b22255..076fd3d198 100644 --- a/docs/modules/arrow/api-reference/arrow-loader.md +++ b/docs/modules/arrow/api-reference/arrow-loader.md @@ -18,7 +18,7 @@ The `ArrowLoader` parses the Apache Arrow columnar table format. ## Usage -```js +```typescript import {ArrowLoader} from '@loaders.gl/arrow'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/arrow/api-reference/arrow-writer.md b/docs/modules/arrow/api-reference/arrow-writer.md index 903129c1fb..2eb265160d 100644 --- a/docs/modules/arrow/api-reference/arrow-writer.md +++ b/docs/modules/arrow/api-reference/arrow-writer.md @@ -18,7 +18,7 @@ The `ArrowWriter` encodes a set of arrays into an ArrayBuffer of Apach Arrow col ## Usage -```js +```typescript import {encodeSync} from '@loaders.gl/core'; import {ArrowWriter, VECTOR_TYPES} from '@loaders.gl/arrow'; diff --git a/docs/modules/arrow/formats/arrow.md b/docs/modules/arrow/formats/arrow.md index 1bf72890c9..620dba0a03 100644 --- a/docs/modules/arrow/formats/arrow.md +++ b/docs/modules/arrow/formats/arrow.md @@ -2,4 +2,4 @@ ![arrow-logo](../images/apache-arrow-small.png) -For more information, see [ArrowJS](/docs/arrowjs/README) documentation. +For more information, see [ArrowJS](/docs/arrowjs) documentation. diff --git a/docs/modules/bson/api-reference/bson-loader.md b/docs/modules/bson/api-reference/bson-loader.md index 9c4213eb06..6e936b46b0 100644 --- a/docs/modules/bson/api-reference/bson-loader.md +++ b/docs/modules/bson/api-reference/bson-loader.md @@ -23,7 +23,7 @@ Streaming loader for BSON encoded files. For simple usage, you can load and parse a BSON file atomically: -```js +```typescript import {BSONLoader} from '@loaders.gl/bson'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/bson/api-reference/bson-writer.md b/docs/modules/bson/api-reference/bson-writer.md index 22030d3d40..d461fc24d9 100644 --- a/docs/modules/bson/api-reference/bson-writer.md +++ b/docs/modules/bson/api-reference/bson-writer.md @@ -19,7 +19,7 @@ Writer for BSON files. ## Usage -```js +```typescript import {BSONWriter} from '@loaders.gl/json'; import {encode} from '@loaders.gl/core'; diff --git a/docs/modules/bson/formats/bson.md b/docs/modules/bson/formats/bson.md index 955de7ef22..fe5ff42d28 100644 --- a/docs/modules/bson/formats/bson.md +++ b/docs/modules/bson/formats/bson.md @@ -50,7 +50,7 @@ Implementations are available in a variety of languages such as C, C++, C#, D, D A JSON "document" such as -```json +```typescripton {"hello": "world"} ``` diff --git a/docs/modules/core/README.md b/docs/modules/core/README.md index 88b52db113..ef7ad09f27 100644 --- a/docs/modules/core/README.md +++ b/docs/modules/core/README.md @@ -2,13 +2,13 @@ The `@loaders.gl/core` module contains the core API of loaders.gl -The core API offers functions to parse data in various ways using loaders +The core API offers functions to parse loaded data in various ways using loaders - [`parse`](/docs/modules/core/api-reference/parse) - [`parseSync`](/docs/modules/core/api-reference/parse-sync) - [`parseInBatches`](/docs/modules/core/api-reference/parse-in-batches) -To fetch data +To fetch data, use the built-in `fetch` or the API compatible but more capable `fetchFile` - [`fetchFile`](/docs/modules/core/api-reference/fetch-file) diff --git a/docs/modules/core/api-reference/binary-utilities.md b/docs/modules/core/api-reference/binary-utilities.md index b6a34a852c..e811cbe22a 100644 --- a/docs/modules/core/api-reference/binary-utilities.md +++ b/docs/modules/core/api-reference/binary-utilities.md @@ -4,7 +4,7 @@ loaders.gl provides a set of functions to simplify working with binary data. The ## Usage -```js +```typescript import {toArrayBuffer} from '@loaders.gl/core'; ``` diff --git a/docs/modules/core/api-reference/encode.md b/docs/modules/core/api-reference/encode.md index 929a4ce5d9..30154b818d 100644 --- a/docs/modules/core/api-reference/encode.md +++ b/docs/modules/core/api-reference/encode.md @@ -1,8 +1,10 @@ # encode -## Functions +Encodes data using the provided _writer_. -### encode(fileData : ArrayBuffer | String, writer : Object | Array [, options : Object [, url : String]]) : Promise.Any +```typescript +encode(fileData: ArrayBuffer | string, writer: Writer, options: WriterOptions, url?: string): Promise +``` Encodes data asynchronously using the provided writer. @@ -13,7 +15,9 @@ Encodes data asynchronously using the provided writer. - `options.log`=`console` Any object with methods `log`, `info`, `warn` and `error`. By default set to `console`. Setting log to `null` will turn off logging. -### encodeSync(fileData : ArrayBuffer | String, writer : Object | Array, [, options : Object [, url : String]]) : any +```typescript +encodeSync(fileData: ArrayBuffer | string, writer: Writer, options?: WriterOptions, rl?: string): unknown +``` Encodes data synchronously using the provided writer, if possible. If not, returns `null`, in which case asynchronous loading is required. diff --git a/docs/modules/core/api-reference/fetch-file.md b/docs/modules/core/api-reference/fetch-file.md index bb65c5a032..dda7e6a485 100644 --- a/docs/modules/core/api-reference/fetch-file.md +++ b/docs/modules/core/api-reference/fetch-file.md @@ -1,12 +1,66 @@ # fetchFile -The `fetchFile` function is a wrapper around `fetch` which provides support for path prefixes and some additional loading capabilities. +The `fetchFile()` function is an alternative to the built-in +[`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) function. +`fetchFile` provides additional functionality to support Node.js and testing use cases: + +- load from local file system under Node.js +- path prefix resolution +- alias resolution + +```typescript +fetchFile(url: string | Blob, options?: RequestInit) : Promise +``` + +A wrapper around the platform function with some additions: + + +Returns: + +- A promise that resolves into a fetch [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object + - `headers`: `Headers` - A [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object. + - `arrayBuffer()`: Promise.ArrayBuffer`- Loads the file as an`ArrayBuffer`. + - `text()`: Promise.String` - Loads the file and decodes it into text. + - `json()`: Promise.String` - Loads the file and decodes it into JSON. + - `body` : ReadableStream` - A stream that can be used to incrementally read the contents of the file. + +::info +Use of `fetchFile` is optional. loaders.gl `parse()` function can be used with data loaded via any mechanism the application prefers, e.g. directly using `fetch`, `XMLHttpRequest` etc. +:: + +## Node.js local file system support + +

+ From-v4.0 +

+ +The `fetchFile()` function supports fetching data from the local file system under Node.js. + +`fetchFile` will delegate any url that starts with `http://` `https://` or `data://` +to the built-in `fetch` function. Other URLs will be interpreted as local files. + +::caution +loaders.gl v4.0+ applications no longer need to install the `@loaders.gl/polyfills` +module to get `fetch` support under Node.js v18+. +:: + +## Path prefix resolution + +`fetchFile()` injects any prefix set by the `setPathPrefix()` API. +Note that the path prefix mechanism is mainly intended to help small example applications +to load data from the right place. It is not intended to support general application use cases, so use sparingly. + +## Alias resolution + +`fetchFile()` also resolves any aliases set by the `_addAliases` function. This internal +functionality is mainly intended for and used by loaders.gl own test cases, to allow paths +to test data file to be specified in terms of which loaders.gl module they are located in. ## Usage Use the `fetchFile` function as follows: -```js +```typescript import {fetchFile} from '@loaders.gl/core'; const response = await fetchFile(url); @@ -24,7 +78,7 @@ const arrayBuffer = await response.arrayBuffer(); The `Response` object from `fetchFile` is usually passed to `parse` as follows: -```js +```typescript import {fetchFile, parse} from '@loaders.gl/core'; import {OBJLoader} from '@loaders.gl/obj'; @@ -33,7 +87,7 @@ const data = await parse(fetchFile(url), OBJLoader); Note that if you don't need the extra features in `fetchFile`, you can just use the browsers built-in `fetch` method. -```js +```typescript import {parse} from '@loaders.gl/core'; import {OBJLoader} from '@loaders.gl/obj'; @@ -42,27 +96,12 @@ const data = await parse(fetch(url), OBJLoader); ## Functions -### `fetchFile(url: string | Blob, options?: RequestInit) : Promise` - -A wrapper around the platform [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) function with some additions: - -- Supports `setPathPrefix`: If path prefix has been set, it will be appended if `url` is relative (e.g. does not start with a `/`). -- Supports `File` and `Blob` objects on the browser (and returns "mock" fetch response objects). - -Returns: - -- A promise that resolves into a fetch [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object, with the following methods/fields: - - `headers`: `Headers` - A [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object. - - `arrayBuffer()`: Promise.ArrayBuffer`- Loads the file as an`ArrayBuffer`. - - `text()`: Promise.String` - Loads the file and decodes it into text. - - `json()`: Promise.String` - Loads the file and decodes it into JSON. - - `body` : ReadableStream` - A stream that can be used to incrementally read the contents of the file. ## Remarks +- `fetch` is supported in Node.js from v18+. - For `string` URLs - `fetchFile` will delegate to `fetch` after resolving the URL. - For `File`/`Blob` - a `Response` object will be returned. Any `RequestInit` options are ignored in this case. -- Under Node.js, `fetchFile` (and `fetch`) works and returns a polyfilled `Response` object if `@loaders.gl/polyfills` has been installed, and `RequestInit` options are used. - `Response.headers` (`Content-Length` and `Content-Type`) are populated (on a best effort basis for `File`, `Blob` and under Node.js). -- Use of `fetchFile` is completely optional. loaders.gl can be used with data loaded via any mechanism the application prefers, e.g. directly using `fetch`, `XMLHttpRequest` etc. -- The `setPathPrefix()` mechanism is intended to help test cases to load data from the right place, but is not intended to support general application use cases, so use with care. +- Supports `setPathPrefix`: If path prefix has been set, it will be appended if `url` is relative (e.g. does not start with a `/`). +- Supports `File` and `Blob` objects on the browser (and returns "mock" fetch response objects). diff --git a/docs/modules/core/api-reference/fetch-progress.md b/docs/modules/core/api-reference/fetch-progress.md index 601d215242..95b50dc6c8 100644 --- a/docs/modules/core/api-reference/fetch-progress.md +++ b/docs/modules/core/api-reference/fetch-progress.md @@ -1,4 +1,4 @@ -# fetchProgress +# fetchProgress 🚧 > This function is still experimental @@ -6,7 +6,7 @@ A function that tracks a fetch response object and calls `onProgress` callbacks. ## Usage -```js +```typescript import {_fetchProgress} from '@loaders.gl/core'; function onProgress(percent, {loadedBytes, totalBytes}) { diff --git a/docs/modules/core/api-reference/load-in-batches.md b/docs/modules/core/api-reference/load-in-batches.md index ad9f197ea1..8e7fb3f8b4 100644 --- a/docs/modules/core/api-reference/load-in-batches.md +++ b/docs/modules/core/api-reference/load-in-batches.md @@ -1,5 +1,12 @@ # loadInBatches +```typescript +loadInBatches(url: string | File | ... , loaders: Loader, options?: LoaderOptions]): Promise> +loadInBatches(url: string | File | ... , loaders: Loader[], options?: LoaderOptions]): Promise> +loadInBatches(files: (string | File | ...)[] | FileList, loaders: Loader, options?: LoaderOptions]): Promise> +loadInBatches(files: (string | File | ...)[] | FileList, loaders: Loader[], options?: LoaderOptions]): Promise> +``` + `loadInBatches` opens a `url` as a stream and passes it and options to `parseInBatches`. See the documentation of `load` and `parseInBatches` for more details. Starting with [![Website shields.io](https://img.shields.io/badge/v2.3-blue.svg?style=flat-square)](http://shields.io), `loadInBatches` can also load and parse multiple files from a list of `File` objects or urls. @@ -10,7 +17,7 @@ More importantly, when called with multiple files, `loadInBatches` makes all the ### Usage -```js +```typescript const iteratorPromises = await loadInBatches([file1, file2], OBJLoader); for await (const iterator of iteratorPromises) { for await (const batch of iterator) { @@ -20,7 +27,7 @@ for await (const iterator of iteratorPromises) { } ``` -```js +```typescript import {fetchFile, parseFilesInBatches} from '@loaders.gl/core'; import {ShapefileLoader} from '@loaders.gl/shapefile'; @@ -39,9 +46,6 @@ for (const batchIterator of batchIterators) { } ``` -### `loadInBatches(url: string | File | ... , loaders: object | object[], options?: object]): Promise>` - -### `loadInBatches(files: (string | File | ...)[] | FileList, loaders: object | object[], options?: object]): Promise>` Loads data in batches from a stream, releasing each batch to the application while the stream is still being read. diff --git a/docs/modules/core/api-reference/load.md b/docs/modules/core/api-reference/load.md index f67de99e7a..087086c77c 100644 --- a/docs/modules/core/api-reference/load.md +++ b/docs/modules/core/api-reference/load.md @@ -1,12 +1,14 @@ # load -The `load` function can be used with any _loader object_. They takes a `url` and one or more _loader objects_, checks what type of data that loader prefers to work on (e.g. text, JSON, binary, stream, ...), loads the data in the appropriate way, and passes it to the loader. +```typescript +load(url: string | File, loaders: Loader, options?: LoaderOptions]): Promise +load(url: string | File, loaders: Loader[], options?: LoaderOptions]): Promise +load(url: string | File, options?: LoaderOptions): Promise +``` -### load(url : String | File, loaders : Object | Object[], options?: object]): Promise.Response +The `load()` function is used to load and parse data with a specific _loader object_. An array of loader objects can be provided, in which case `load` will attempt to autodetect which loader is appropriate for the file. -### load(url : String | File, options?: Object): Promise.Response - -The `load` function is used to load and parse data with a specific _loader object_. An array of loader objects can be provided, in which case `load` will attempt to autodetect which loader is appropriate for the file. +The `load()` function can also be used with multiple _loaders_. `load()` takes a `url` and one or more _loader objects_, checks what type of data that loader prefers to work on (e.g. text, JSON, binary, stream, ...), loads the data in the appropriate way, and passes it to the loader. The `loaders` parameter can also be omitted, in which case any _loader objects_ previously registered with [`registerLoaders`](/docs/modules/core/api-reference/register-loaders) will be used. @@ -16,6 +18,7 @@ The `loaders` parameter can also be omitted, in which case any _loader objects_ Returns: +- If `options.fetch` is not overridden with a new function. - Return value depends on the _loader category_. Notes: diff --git a/docs/modules/core/api-reference/loader-options.md b/docs/modules/core/api-reference/loader-options.md index dbdca86588..abcbf71695 100644 --- a/docs/modules/core/api-reference/loader-options.md +++ b/docs/modules/core/api-reference/loader-options.md @@ -36,7 +36,7 @@ Top level options are interpreted by the core API and apply to all loaders. | `options.reuseWorkers` | `boolean` | `true` | By default, worker threads are kept in memory and reused. But if `reuseWorkers` is `false` workers will be automatically terminated after job completion and reloaded for each job. | | `options..workerUrl` | `string` | per-loader | If the corresponding loader can parse on a worker, the url to the worker script can be controller with this option. | | `options.modules` | `object` | - | Supply bundled modules (like draco3d) instead of loading from CDN. | -| `options.CDN` (experimental) | `string` | - | Controls certain script loading from CDN. `true` loads from `unpkg.com/@loaders.gl`. `false` load from local urls. `string` alternate CDN url. | +| `options.CDN` (🚧 experimental) | `string` | - | Controls certain script loading from CDN. `true` loads from `unpkg.com/@loaders.gl`. `false` load from local urls. `string` alternate CDN url. | ## Batched parsing options diff --git a/docs/modules/core/api-reference/parse-in-batches.md b/docs/modules/core/api-reference/parse-in-batches.md index fd1d657c60..60e3bde887 100644 --- a/docs/modules/core/api-reference/parse-in-batches.md +++ b/docs/modules/core/api-reference/parse-in-batches.md @@ -6,11 +6,15 @@ Batched parsing is only supported by a subset of loaders. Check documentation of From [![Website shields.io](https://img.shields.io/badge/v2.3-blue.svg?style=flat-square)](http://shields.io) `parseInBatches` can be used with all loaders. Non-supporting loaders will wait until all data has arrived, and emit a single batch containing the parsed data for the entire input (effectively behave as if `parse` had been called). +:::caution +When calling parse from a loader to invoke a sub-loader, do not use this function. Use the `parseInBatchesWithContext` counterparts in `@loaders.gl/loader-utils`` +::: + ## Usage Parse CSV in batches (emitting a batch of rows every time data arrives from the network): -```js +```typescript import {fetchFile, parseInBatches} from '@loaders.gl/core'; import {CSVLoader} from '@loaders.gl/obj'; @@ -22,7 +26,7 @@ for await (const batch of batchIterator) { Parse CSV in batches, requesting an initial metadata batch: -```js +```typescript import {fetchFile, parseInBatches} from '@loaders.gl/core'; import {CSVLoader} from '@loaders.gl/obj'; diff --git a/docs/modules/core/api-reference/parse-sync.md b/docs/modules/core/api-reference/parse-sync.md index 5dae68c573..cd0744ce4d 100644 --- a/docs/modules/core/api-reference/parse-sync.md +++ b/docs/modules/core/api-reference/parse-sync.md @@ -1,12 +1,37 @@ # parseSync -> Synchronous parsing is not supported by all loaders. Refer to the documentation for each loader. +::caution +Synchronous parsing is not supported by all loaders. Refer to the documentation for each loader. +:: -For supporting loaders, the synchronous `parseSync` function works on already loaded data. +:::caution +When calling parse from a loader to invoke a sub-loader, do not use this function. Use `parseSyncWithContext` counterparts in `@loaders.gl/loader-utils`` +::: + +The `parseSync()` function parses data synchronously using the provided loader, if possible. + +```typescript +parseSync(data: ArrayBuffer | string, loaders: Loader, options?: LoaderOptions, url?: string]]) : unknown +parseSync(data: ArrayBuffer | string, loaders: Loader[], options?: LoaderOptions, url?: string]]) : unknown +``` + +- `data`: already loaded data, either in binary or text format. This parameter can be any of the following types: + - `Response`: `fetch` response object returned by `fetchFile` or `fetch`. + - `ArrayBuffer`: Parse from binary data in an array buffer + - `string`: Parse from text data in a string. (Only works for loaders that support textual input). + - `Iterator`: Iterator that yeilds binary (`ArrayBuffer`) chunks or string chunks (string chunks only work for loaders that support textual input). + can also be supplied. +- `loaders`: can be a single loader or an array of loaders. If ommitted, will use the list of registered loaders (see `registerLoaders`) +- `options`: See [`LoaderOptions`](./loader-options). +- `url`: optional, assists in the autoselection of a loader if multiple loaders are supplied to `loader`. + +Returns: + +- Return value depends on the _loader object_ category, or `null`, in which case asynchronous parsing is required. ## Usage -```js +```typescript import {fetchFile, parseSync} from '@loaders.gl/core'; import {OBJLoader} from '@loaders.gl/obj'; @@ -20,30 +45,10 @@ data = parseSync(arraybuffer, OBJLoader); Handling errors -```js +```typescript try { const data = await parseSync(data); } catch (error) { console.log(error); } ``` - -## Functions - -### `parseSync(data: ArrayBuffer | String, loaders: Loader | Loader[], options?: LoaderOptions, url?: String]]) : any` - -Parses data synchronously using the provided loader, if possible. If not, returns `null`, in which case asynchronous parsing is required. - -- `data`: already loaded data, either in binary or text format. This parameter can be any of the following types: - - `Response`: `fetch` response object returned by `fetchFile` or `fetch`. - - `ArrayBuffer`: Parse from binary data in an array buffer - - `String`: Parse from text data in a string. (Only works for loaders that support textual input). - - `Iterator`: Iterator that yeilds binary (`ArrayBuffer`) chunks or string chunks (string chunks only work for loaders that support textual input). - can also be supplied. -- `loaders`: can be a single loader or an array of loaders. If ommitted, will use the list of registered loaders (see `registerLoaders`) -- `options`: See [`LoaderOptions`](./loader-options). -- `url`: optional, assists in the autoselection of a loader if multiple loaders are supplied to `loader`. - -Returns: - -- Return value depends on the _loader object_ category diff --git a/docs/modules/core/api-reference/parse.md b/docs/modules/core/api-reference/parse.md index 73c202ef78..840c93a345 100644 --- a/docs/modules/core/api-reference/parse.md +++ b/docs/modules/core/api-reference/parse.md @@ -4,11 +4,15 @@ This function "atomically" parses data (i.e. parses the entire data set in one o In contrast to `load`, `parse` does not accept URLs (it treats strings as data to be parsed) however it does read data from `Response` objects (which can involve loading data from a source). `Response` objects are returned by `fetch` but can also be manually created to wrap other data types, which makes `parse` quite flexible. +:::caution +When calling parse from a loader to invoke a sub-loader, do not use this function. Use the `parseWithContext` counterparts in `@loaders.gl/loader-utils`` +::: + ## Usage The return value from `fetch` or `fetchFile` is a `Promise` that resolves to the fetch `Response` object and can be passed directly to the non-sync parser functions: -```js +```typescript import {fetchFile, parse} from '@loaders.gl/core'; import {OBJLoader} from '@loaders.gl/obj'; @@ -19,7 +23,7 @@ data = await parse(fetchFile(url), OBJLoader); Batched (streaming) parsing is supported by some loaders -```js +```typescript import {fetchFile, parseInBatches} from '@loaders.gl/core'; import {CSVLoader} from '@loaders.gl/obj'; @@ -31,7 +35,7 @@ for await (const batch of batchIterator) { Handling errors -```js +```typescript try { const response = await fetch(url); // fetch can throw in case of network errors const data = await parse(response); // parse will throw if server reports an error diff --git a/docs/modules/core/api-reference/register-loaders.md b/docs/modules/core/api-reference/register-loaders.md index fa55e5df8d..c1010acd2d 100644 --- a/docs/modules/core/api-reference/register-loaders.md +++ b/docs/modules/core/api-reference/register-loaders.md @@ -8,7 +8,7 @@ Applications can then make all those imported loaders available (via format auto Sample application initialization code that imports and registers loaders: -```js +```typescript import {registerLoaders} from '@loaders.gl/core'; import {CSVLoader} from '@loaders.gl/csv'; @@ -17,7 +17,7 @@ registerLoaders(CSVLoader); Some other file that needs to load CSV: -```js +```typescript import {load} from '@loaders.gl/core'; // The pre-registered CSVLoader gets auto selected based on file extension... diff --git a/docs/modules/core/api-reference/select-loader.md b/docs/modules/core/api-reference/select-loader.md index 0f4026ba67..1af8e5db3c 100644 --- a/docs/modules/core/api-reference/select-loader.md +++ b/docs/modules/core/api-reference/select-loader.md @@ -23,7 +23,7 @@ unless `options.ignoreRegisteredLoaders` is `true`. Select a loader from a list of provided loaders: -```js +```typescript import {selectLoaderSync} from '@loaders.gl/core'; import {ArrowLoader} from '@loaders.gl/arrow'; import {CSVLoader} from '@loaders.gl/csv'; @@ -33,7 +33,7 @@ selectLoaderSync('filename.csv', [ArrowLoader, CSVLoader]); // => CSVLoader Select a loader from pre-registered loaders in the loader registry: -```js +```typescript import {registerLoaders, selectLoader} from '@loaders.gl/core'; import {ArrowLoader} from '@loaders.gl/arrow'; import {CSVLoader} from '@loaders.gl/csv'; @@ -45,7 +45,7 @@ await selectLoader('filename.csv'); // => CSVLoader Select a loader by specifying MIME type (using unregistered MIME types, see below) -```js +```typescript const data = new Blob([string], {type: 'application/x.csv'}); await selectLoader(blob); // => CSVLoader ``` @@ -53,7 +53,7 @@ await selectLoader(blob); // => CSVLoader The async `selectLoader` function can identify loaders without extension and mimeType by content sniffing `Blob` and `File` objects (useful when user drags and drops files into your application). -```js +```typescript const data = new Blob(['DRACO...'] /* Binary Draco files start with these characters */]); await selectLoader(blob, DracoLoader); // => DracoLoader ``` diff --git a/docs/modules/core/api-reference/set-loader-options.md b/docs/modules/core/api-reference/set-loader-options.md index e9d1038843..328e01d4d5 100644 --- a/docs/modules/core/api-reference/set-loader-options.md +++ b/docs/modules/core/api-reference/set-loader-options.md @@ -6,7 +6,7 @@ Set or get the supplied options onto the current global options object Bundling the entire `draco3d` library (instead of loading it on-demand from CDN): -```js +```typescript import draco from 'draco3d'; import {setLoaderOptions} from '@loaders.gl/core'; setLoaderOptions({ diff --git a/docs/modules/core/api-reference/write-file.md b/docs/modules/core/api-reference/write-file.md index eee8a48dc5..52c98295d6 100644 --- a/docs/modules/core/api-reference/write-file.md +++ b/docs/modules/core/api-reference/write-file.md @@ -4,7 +4,7 @@ A file save utilities that (attempts to) work consistently across browser and no ## Usage -```js +```typescript import {writeFile} from '@loaders.gl/core'; import {DracoWriter} from '@loaders.gl/draco'; diff --git a/docs/modules/crypto/README.md b/docs/modules/crypto/README.md index 6e0a73bf9b..2c291929f4 100644 --- a/docs/modules/crypto/README.md +++ b/docs/modules/crypto/README.md @@ -6,10 +6,25 @@ The `@loaders.gl/crypto` module provides a selection of optional cryptographic hash plugins for loaders.gl. -## Cryptographic Formats +Terminology: +- A **hash** is an + +## Cryptographic Algorithmgs MD5, SHA256 and many more, see [crypto-js](https://github.com/brix/crypto-js) +## Encoding + +A hash algorithm takes input data and generates a digest. The digest is a number, usually containing a fixed number of bits. +Since the number of bits involved is often large (e.g. SHA256 generates a 256 bit digest), digests are typically encoded as strings instead of numbers. + +The two most common string encodings of numbers are *hex* and *base64*. +Which one you want to use often depends on what you are planning to do with the digest. +If you are calling an existing API (perhaps a cloud storage service) +you will want to generate the encoding that matches or is required by that API. + +All hash functions in the `@loaders.gl/crypto` module take an `encoding` parameter that lets you specify the encoding. + ## Cryptographic Hash API The API offers "transforms" that can calculate a cryptographic hash incrementally on data as it comes in on a stream. @@ -25,7 +40,7 @@ The API offers "transforms" that can calculate a cryptographic hash incrementall The `@loaders.gl/crypto` libraries exports transform that can be used to incrementally calculate a cryptographic hash as data is being loaded and parsed: -```js +```typescript import {loadInBatches} from '@loaders.gl/core'; import {CRC32Hash} from '@loaders.gl/crypto'; diff --git a/docs/modules/crypto/api-reference/hash.md b/docs/modules/crypto/api-reference/hash.md index 962056a05b..2647c9da3d 100644 --- a/docs/modules/crypto/api-reference/hash.md +++ b/docs/modules/crypto/api-reference/hash.md @@ -16,23 +16,39 @@ The name of the hash algorithm ## Methods -#### `preload(): Promise` +#### `preload()` + +`preload(): Promise` Asynchronously loads required libraries. For some hash classes this must be completed before `hashSync()` is available. -#### `hash(data: ArrayBuffer): Promise` +#### `hash()` + +```typescript + hash.hash(data: ArrayBuffer, encoding: 'hex' | 'base64'): Promise +``` Asynchronously hashes data. -#### `hashSync(data: ArrayBuffer): ArrayBuffer` +#### `hashSync()` + +```typescript + hash.hashSync(data: ArrayBuffer, encoding: 'hex' | 'base64'): ArrayBuffer +``` Synchronously hashes data. -For some hashions `preload()` must have been called and completed before +:::caution +For some hash sub classes, `preload()` must have been called and completed before synchronous operations are available. +::: + +#### `hashInBactches()` -#### `hashBatches(data: AsyncIterable): AsyncIterable` +```typescript + hash.hashBatches(data: AsyncIterable, encoding: 'hex' | 'base64'): AsyncIterable +``` Asynchronously hashes data in batches. diff --git a/docs/modules/csv/api-reference/csv-writer.md b/docs/modules/csv/api-reference/csv-writer.md index a5683c7669..15fb69ece3 100644 --- a/docs/modules/csv/api-reference/csv-writer.md +++ b/docs/modules/csv/api-reference/csv-writer.md @@ -1,5 +1,9 @@ # CSVWriter +

+ From-v4.0 +

+ Writes tabular data into comma-separated value and [delimiter-separated value](https://en.wikipedia.org/wiki/Delimiter-separated_values) encoding. | Loader | Characteristic | diff --git a/docs/modules/draco/README.md b/docs/modules/draco/README.md index 7f809c57b6..76d77881d4 100644 --- a/docs/modules/draco/README.md +++ b/docs/modules/draco/README.md @@ -28,7 +28,7 @@ Draco support requires the Draco libraries, which are quite big (see table below Bundling the entire `draco3d` library: -```js +```typescript import draco from 'draco3d'; import {setLoaderOptions} from '@loaders.gl/core'; setLoaderOptions({ @@ -40,7 +40,7 @@ setLoaderOptions({ Bundling only the WebAssembly decoder -```js +```typescript import {setLoaderOptions} from '@loaders.gl/core'; setLoaderOptions({ modules: { diff --git a/docs/modules/draco/api-reference/draco-loader.md b/docs/modules/draco/api-reference/draco-loader.md index 359f26679a..b8ed5bdf3a 100644 --- a/docs/modules/draco/api-reference/draco-loader.md +++ b/docs/modules/draco/api-reference/draco-loader.md @@ -23,7 +23,7 @@ Features: ## Usage -```js +```typescript import {DracoLoader} from '@loaders.gl/draco'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/draco/api-reference/draco-writer.md b/docs/modules/draco/api-reference/draco-writer.md index 05a3ccf07a..0b595e5273 100644 --- a/docs/modules/draco/api-reference/draco-writer.md +++ b/docs/modules/draco/api-reference/draco-writer.md @@ -14,7 +14,7 @@ The `DracoWriter` encodes a mesh or point cloud using [Draco](https://google.git ## Usage -```js +```typescript import {DracoWriter} from '@loaders.gl/draco'; import {encode} from '@loaders.gl/core'; diff --git a/docs/modules/draco/images/draco-writer.md b/docs/modules/draco/images/draco-writer.md index 7b3c03de5c..1ff8737576 100644 --- a/docs/modules/draco/images/draco-writer.md +++ b/docs/modules/draco/images/draco-writer.md @@ -14,7 +14,7 @@ The `DracoWriter` encodes a mesh or point cloud (maps of attributes) using [Drac ## Usage -```js +```typescript import {DracoWriter} from '@loaders.gl/draco'; import {encode} from '@loaders.gl/core'; diff --git a/docs/modules/excel/api-reference/excel-loader.md b/docs/modules/excel/api-reference/excel-loader.md index 48b297b5b0..96f5d3f6a3 100644 --- a/docs/modules/excel/api-reference/excel-loader.md +++ b/docs/modules/excel/api-reference/excel-loader.md @@ -12,7 +12,7 @@ ## Usage -```js +```typescript import {ExcelLoader} from '@loaders.gl/excel'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/flatgeobuf/api-reference/flatgeobuf-loader.md b/docs/modules/flatgeobuf/api-reference/flatgeobuf-loader.md index a446a4e2ed..571f9633fe 100644 --- a/docs/modules/flatgeobuf/api-reference/flatgeobuf-loader.md +++ b/docs/modules/flatgeobuf/api-reference/flatgeobuf-loader.md @@ -1,4 +1,4 @@ -# FlatGeobufLoader +# FlatGeobufLoader 🚧

From-v3.1 @@ -18,7 +18,7 @@ Loader for the [FlatGeobuf](http://flatgeobuf.org/) format, a binary FlatBuffers ## Usage -```js +```typescript import {FlatGeobufLoader} from '@loaders.gl/flatgeobuf'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/geopackage/README.md b/docs/modules/geopackage/README.md index ddf31b3af7..18598c78d7 100644 --- a/docs/modules/geopackage/README.md +++ b/docs/modules/geopackage/README.md @@ -1,4 +1,4 @@ -# Overview +# Overview 🚧 ![ogc-logo](../../images/logos/ogc-logo-60.png) diff --git a/docs/modules/geopackage/api-reference/geopackage-loader.md b/docs/modules/geopackage/api-reference/geopackage-loader.md index a8b54f02d0..dff6dae211 100644 --- a/docs/modules/geopackage/api-reference/geopackage-loader.md +++ b/docs/modules/geopackage/api-reference/geopackage-loader.md @@ -1,4 +1,6 @@ -# GeoPackageLoader +# GeoPackageLoader 🚧 + +![ogc-logo](../../../images/logos/ogc-logo-60.png)

From-v3.0 @@ -18,40 +20,45 @@ GeoPackage loader ## Usage -```js +To load all tables in a geopackage file as GeoJSON: + +```typescript import {GeoPackageLoader, GeoPackageLoaderOptions} from '@loaders.gl/geopackage'; import {load} from '@loaders.gl/core'; import {Tables, ObjectRowTable, Feature} from '@loaders.gl/schema'; const optionsAsTable: GeoPackageLoaderOptions = { geopackage: { + shape: 'tables', sqlJsCDN: 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.5.0/' - }, - gis: { - format: 'tables' } }; -const tablesData: Tables = await load(url, GeoPackageLoader, optionsAsTable); +const tablesData: Tables = await load(url, GeoPackageLoader, optionsAsTable); +``` +To load a specific table named `feature_table` in a geopackage file as GeoJSON: + +```typescript const optionsAsGeoJson: GeoPackageLoaderOptions = { geopackage: { - sqlJsCDN: 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.5.0/' - }, - gis: { - format: 'geojson' + shape: 'geojson', + table: 'feature_table', + sqlJsCDN: 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.5.0/', } }; -const geoJsonData: Record = await load(url, GeoPackageLoader, optionsAsGeoJson); + +const geoJsonData: GeoJSONTable = await load(url, GeoPackageLoader, optionsAsGeoJson); + ``` ## Options | Option | Type | Default | Description | | --------------------- | ------ | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `options.shape` | String | `'tables'` \| '`geojson-table'` | Output format. | +| `options.table` | String | N/A | name of table to load | Output format. | | `geopackage.sqlJsCDN` | String | `'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.5.0/'` | CDN from which to load the SQL.js bundle. This is loaded asynchronously when the GeoPackageLoader is called on a file. | -| `options.gis.format` | String | `'tables'` | Output format for data. If set to `geojson` | -- `geopackage.sqlJsCDN`: As of March 2022, SQL.js versions 1.6.0, 1.6.1, and 1.6.2 were tested as not working. Therefore this library pins to use of SQL.js version 1.5.0, and requires the WASM bundle from the same version. ## Output @@ -65,7 +72,11 @@ The `GeoPackageLoader` currently loads all features from all vector tables. Returns `Record`, an object mapping from table name to an array of GeoJSON features. The `Feature` type is defined in `@loaders.gl/schema`. +## Remarks + +- `options.geopackage.sqlJsCDN`: As of March 2022, SQL.js versions 1.6.0, 1.6.1, and 1.6.2 were tested as not working. + ## Future Work -- Select a single vector layer/table to load. Right now all vector tables are loaded. This would fit well in the two-stage loader process, where the first stage reads metadata from the file (i.e. the list of available layers) and the second stage loads one or more +- Select a single vector layer/table to load. This could would fit well in the two-stage loader process, where the first stage reads metadata from the file (i.e. the list of available layers) and the second stage loads one or more tables. - Binary and GeoJSON output. Right now the output is GeoJSON-only, and is contained within an object mapping table names to geometry data. This is the same problem as we discussed previously for MVT, where with GeoPackage especially it's decently likely to only desire a portion of the layers contained in the file. diff --git a/docs/modules/geopackage/formats/geopackage.md b/docs/modules/geopackage/formats/geopackage.md new file mode 100644 index 0000000000..533c736675 --- /dev/null +++ b/docs/modules/geopackage/formats/geopackage.md @@ -0,0 +1,5 @@ +# Geopackage + +![ogc-logo](../../../images/logos/ogc-logo-60.png) + +The `@loaders.gl/geopackage` module handles the OGC [GeoPackage](https://www.geopackage.org/) format. diff --git a/docs/modules/gis/api-reference/binary-to-geojson.md b/docs/modules/gis/api-reference/binary-to-geojson.md index 6fdb321387..f190c8a97b 100644 --- a/docs/modules/gis/api-reference/binary-to-geojson.md +++ b/docs/modules/gis/api-reference/binary-to-geojson.md @@ -5,7 +5,7 @@ a valid GeoJSON Geometry, Feature or array of Features. ## Usage -```js +```typescript import {load} from '@loaders.gl/core'; import {JSONLoader} from '@loaders.gl/json'; import {geojsonToBinary, binaryToGeojson} from '@loaders.gl/gis'; diff --git a/docs/modules/gis/api-reference/geojson-to-binary.md b/docs/modules/gis/api-reference/geojson-to-binary.md index a9a409db77..1c3303525c 100644 --- a/docs/modules/gis/api-reference/geojson-to-binary.md +++ b/docs/modules/gis/api-reference/geojson-to-binary.md @@ -7,22 +7,14 @@ main process. ## Usage -```js +```typescript import {load} from '@loaders.gl/core'; import {JSONLoader} from '@loaders.gl/json'; import {geojsonToBinary} from '@loaders.gl/gis'; const geoJSONfeatures = await load('data.geojson', JSONLoader); -/* - * Default options are: - * - * { - * fixRingWinding: true - * numericPropKeys: derived from data - * PositionDataType: Float32Array - * } - */ +// See table below for full list of options const options = {PositionDataType: Float32Array}; const binaryFeatures = geojsonToBinary(geoJSONfeatures, options); ``` @@ -38,13 +30,13 @@ non-numeric property objects of the given geometry type. Each `TypedArray` is wrapped inside an _accessor object_, where `.value` contains the raw numeric data, and `.size` gives the number of values per vertex. For example, -```js +```typescript positions: {value: Float32Array, size: 3} ``` corresponds to 3D coordinates, where each vertex is defined by three numbers. -```js +```typescript { points: { // Array of x, y or x, y, z positions @@ -95,8 +87,8 @@ corresponds to 3D coordinates, where each vertex is defined by three numbers. polygonIndices: {value: Uint16Array | Uint32Array, size: 1}, // Indices within positions of the start of each primitive Polygon/ring primitivePolygonIndices: {value: Uint16Array | Uint32Array, size: 1}, - // Triangle indices. Allows deck.gl to skip performing costly triangulation on main thread - triangles: {value: Uint32Array, size: 1}, + // Triangle indices. Allows deck.gl to skip performing costly triangulation on main thread. Not present if `options.triangulate` is `false` + triangles?: {value: Uint32Array, size: 1}, // Array of original feature indexes by vertex globalFeatureIds: {value: Uint16Array | Uint32Array, size: 1}, // Array of Polygon feature indexes by vertex @@ -121,10 +113,13 @@ corresponds to 3D coordinates, where each vertex is defined by three numbers. | Option | Type | Default | Description | | ---------------- | --------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -| fixRingWinding | `Boolean` | `true` | Whether the fix incorrect ring winding for polygons. Valid `GeoJSON` polygons have the outer ring coordinates in CCW order and with holes in CW order | +| fixRingWinding | `Boolean` | `true` | Whether to fix incorrect ring winding for polygons. Valid `GeoJSON` polygons have the outer ring coordinates in CCW order and with holes in CW order | | numericPropKeys | `Array` | Derived from data | Names of feature properties that should be converted to numeric `TypedArray`s. Passing `[]` will force all properties to be returned as normal objects. | | PositionDataType | `class` | `Float32Array` | Data type used for positions arrays. | +| triangulate | `Boolean` | `true` | Whether polygons are broken into triangles as part of the conversion (generally required for GPU rendering) | ## Notes In the case of the source geoJson features having an object as a property, it would not be deep cloned, so it would be linked from the output object (be careful on further mutations). + +Triangulation of polygons can be time consuming. If not needed, set the `triangulate` option to `false`. diff --git a/docs/modules/gltf/README.md b/docs/modules/gltf/README.md index d362629a91..1a58f94a07 100644 --- a/docs/modules/gltf/README.md +++ b/docs/modules/gltf/README.md @@ -23,7 +23,7 @@ To simplify traversing and building glTF data objects, the [`GLTFScenegraph`](/d A glTF data object can also be built programmatically using the GLTFScenegraph's "fluent API": -```js +```typescript import {encode} from '@loaders.gl/gltf'; import {GLTFScenegraph, GLTFWriter} from '@loaders.gl/gltf'; const gltfScenegraph = new GLTFScenegraph() @@ -55,7 +55,7 @@ Certain glTF extensions are fully or partially supported by the glTF classes. Fo Draco encoding and decoding is supported by the `GLTFBuilder` and `GLTFParser` classes but requires the DracoWriter and DracoLoader dependencies to be "injected" by the application. -```js +```typescript import {GLTFBuilder} from '@loaders.gl/gltf'; import {DracoWriter, DracoLoader} from '@loaders.gl/draco'; diff --git a/docs/modules/gltf/api-reference/glb-loader.md b/docs/modules/gltf/api-reference/glb-loader.md index d97879a11d..de892e88d7 100644 --- a/docs/modules/gltf/api-reference/glb-loader.md +++ b/docs/modules/gltf/api-reference/glb-loader.md @@ -17,7 +17,7 @@ Note: applications that want to parse GLB-formatted glTF files would normally us ## Usage -```js +```typescript import {load} from '@loaders.gl/core'; import {GLBLoader} from '@loaders.gl/gltf'; const gltf = await load(url, GLBLoader); @@ -39,7 +39,7 @@ Remarks: Returns -```json +```typescripton { "header": { "byteLength": number, diff --git a/docs/modules/gltf/api-reference/glb-writer.md b/docs/modules/gltf/api-reference/glb-writer.md index 46327baadc..0f8a2f3379 100644 --- a/docs/modules/gltf/api-reference/glb-writer.md +++ b/docs/modules/gltf/api-reference/glb-writer.md @@ -14,7 +14,7 @@ Note: applications that want to encode GLB-formatted glTF files should normally ## Usage -```js +```typescript import {GLBWriter} from '@loaders.gl/gltf'; import {encodeSync} from '@loaders.gl/core'; diff --git a/docs/modules/gltf/api-reference/gltf-loader.md b/docs/modules/gltf/api-reference/gltf-loader.md index c93ed7c9b2..d22a87633e 100644 --- a/docs/modules/gltf/api-reference/gltf-loader.md +++ b/docs/modules/gltf/api-reference/gltf-loader.md @@ -40,7 +40,7 @@ The GLTF Loader returns an object with a `json` field containing the glTF Sceneg Optionally, the loaded gltf can be "post processed", which lightly annotates and transforms the loaded JSON structure to make it easier to use. Refer to [postProcessGLTF](post-process-gltf) for details. -In addition, certain glTF extensions, in particular Draco mesh encoding, can be fully or partially processed during loading. When possible (and extension processing is enabled), such extensions will be resolved/decompressed and replaced with standards conformant representations. See [glTF Extensions](gltf-extensions) for more information. +In addition, certain glTF extensions, in particular Draco mesh encoding, can be fully or partially processed during loading. When possible (and extension processing is enabled), such extensions will be resolved/decompressed and replaced with standards conformant representations. Note: while supported, synchronous parsing of glTF (e.g. using `parseSync()`) has significant limitations. When parsed asynchronously (using `await parse()` or `await load()`), the following additional capabilities are enabled: @@ -78,7 +78,7 @@ The data format returned by the `GLTFLoader` is the unmodified glTF JSON extract The standard glTF JSON structure will be available in the `json` field. -```json +```typescripton { json: { scenes: [...], @@ -92,7 +92,7 @@ The standard glTF JSON structure will be available in the `json` field. However, the objects inside these arrays will have been pre-processed to simplify usage. For details on changes and extra fields added to the various glTF objects, see [post processing](post-process-gltf). -```json +```typescripton { // The base URI used to load this glTF, if any. For resolving relative uris to linked resources. baseUri: String, diff --git a/docs/modules/gltf/api-reference/gltf-scenegraph.md b/docs/modules/gltf/api-reference/gltf-scenegraph.md index e68d2dba94..6bea6d5562 100644 --- a/docs/modules/gltf/api-reference/gltf-scenegraph.md +++ b/docs/modules/gltf/api-reference/gltf-scenegraph.md @@ -8,7 +8,7 @@ The `GLTFScenegraph` class provides an API for accessing and modifying glTF data ### Accessing -```js +```typescript import {GLTFLoader, GLTFScenegraph} from '@loaders.gl/gltf'; import {load} from '@loaders.gl/core'; @@ -42,7 +42,7 @@ const scenegraph = gltf.getScene(2); ### Modifying -```js +```typescript import {load, encode} from '@loaders.gl/core'; import {ImageWriter} from '@loaders.gl/images'; import {GLTFLoader, GLTFWriter, GLTFScenegraph} from '@loaders.gl/gltf'; diff --git a/docs/modules/gltf/api-reference/gltf-writer.md b/docs/modules/gltf/api-reference/gltf-writer.md index 147e1106ad..c2217f96a2 100644 --- a/docs/modules/gltf/api-reference/gltf-writer.md +++ b/docs/modules/gltf/api-reference/gltf-writer.md @@ -12,7 +12,7 @@ The `GLTFWriter` is a writer for glTF scenegraphs. ## Usage -```js +```typescript import {GLTFWriter} from '@loaders.gl/gltf'; import {encodeSync} from '@loaders.gl/core'; diff --git a/docs/modules/gltf/api-reference/post-process-gltf.md b/docs/modules/gltf/api-reference/post-process-gltf.md index 240cb0b954..8541f2a2a6 100644 --- a/docs/modules/gltf/api-reference/post-process-gltf.md +++ b/docs/modules/gltf/api-reference/post-process-gltf.md @@ -7,7 +7,7 @@ For details see below. ## Usage -```js +```typescript import {load} from '@loaders.gl.core'; import {GLTFLoader, postProcessGLTF} from '@loaders.gl/gltf'; diff --git a/docs/modules/gltf/formats/gltf.md b/docs/modules/gltf/formats/gltf.md index 30f87e1f85..ad42436a0a 100644 --- a/docs/modules/gltf/formats/gltf.md +++ b/docs/modules/gltf/formats/gltf.md @@ -24,7 +24,7 @@ A glTF file uses one of two possible file extensions: .gltf (JSON/ASCII) or .glb ## glTF Extensions -glTF extensions can be present in glTF files, and will be present in the parsed JSON. glTF extensions can supported by applications by inspecting the `extensions` fields inside glTF objects, and it is up to each application to handle or ignore them. +glTF extensions can be present in glTF files, and will be present in the parsed JSON. glTF extensions can be supported by applications by inspecting the `extensions` fields inside glTF objects, and it is up to each application to handle or ignore them. loaders.gl aims to provide support for glTF extensions that can be handled completely or partially during loading, and article describes glTF extensions that are fully or partially processed by the `@loaders.gl/gltf` classes. @@ -37,7 +37,8 @@ Note that many glTF extensions affect aspects that are firmly outside of the sco | [KHR_texture_basisu](#khr_texture_basisu) | Y | Adds the ability to specify textures using KTX v2 | | [KHR_texture_transform](#khr_texture_transform) | Y | Adds transformation properties (translation, rotation, scale) for TEXCOORD\_ mesh attribute | | KHR_texture_webp | Y | -| [EXT_mesh_features](#ext_mesh_features) | N | (In progress) 3D tiles extension | +| [EXT_mesh_features](#ext_mesh_features) | Y | 3D tiles extension | +| [EXT_structural_metadata](#ext_structural_metadata) | Y | 3D tiles extension | | [KHR_lights_punctual](#khr_lights_punctual) | Y\* | Deprecated | | [KHR_materials_unlit](#khr_materials_unlit) | Y\* | Deprecated | | [EXT_feature_metadata](#ext_feature_metadata) | Y\* | Deprecated. 3D tiles extension | @@ -120,3 +121,9 @@ The `GLTFLoader` by default fully decompresses meshopt compressed geometries, re 3D tiles extension by Cesium. This extension defines a means of assigning identifiers to geometry and subcomponents of geometry within a glTF 2.0 asset. [EXT_mesh_features](https://github.com/CesiumGS/glTF/tree/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_mesh_features) + +### EXT_structural_metadata + +3D tiles extension by Cesium. This extension defines a means of storing structured metadata within a glTF 2.0 asset. + +[EXT_structural_metadata](https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata) diff --git a/docs/modules/i3s/README.md b/docs/modules/i3s/README.md index 3ab18eeeb4..27195649b5 100644 --- a/docs/modules/i3s/README.md +++ b/docs/modules/i3s/README.md @@ -27,8 +27,6 @@ To handle the complex dynamic tile selection and loading required to performantl - [`Tileset3D`](/docs/modules/tiles/api-reference/tileset-3d) to work with the loaded tileset. - [`Tile3D`](/docs/modules/tiles/api-reference/tile-3d) to access data for a specific tile. -## Remarks - -`@loaders.gl/3s` is still under experimental, and mainly support decoding `MeshPyramids` (3D Object and Integrated Mesh) profiles. - ## Attribution + +MIT license, code is written for loaders.gl. \ No newline at end of file diff --git a/docs/modules/i3s/api-reference/i3s-loader.md b/docs/modules/i3s/api-reference/i3s-loader.md index 82ac58cd12..7d68d30c3c 100644 --- a/docs/modules/i3s/api-reference/i3s-loader.md +++ b/docs/modules/i3s/api-reference/i3s-loader.md @@ -17,36 +17,36 @@ A loader for loading an [Indexed 3d Scene (I3S) layer](https://github.com/Esri/i ## I3S Layer type support -| Layer Type | Supported | I3S Spec Link | -| -------------------- | ------------ | ------------------------------------------------------------------------------ | -| 3DObject | yes | https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3Dobject_ReadMe.md | -| Integrated Mesh | yes | https://github.com/Esri/i3s-spec/blob/master/docs/1.7/IntegratedMesh_ReadMe.md | -| Points | no | https://github.com/Esri/i3s-spec/blob/master/docs/1.7/Point_ReadMe.md | -| PointClouds | no | https://github.com/Esri/i3s-spec/blob/master/docs/2.0/pcsl_ReadMe.md | -| Building Scene Layer | experimental | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/BSL_ReadMe.md | +| Layer Type | Supported | I3S Spec Link | +| -------------------- | -------------- | ------------------------------------------------------------------------------ | +| 3DObject | ✅ | https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3Dobject_ReadMe.md | +| Integrated Mesh | ✅ | https://github.com/Esri/i3s-spec/blob/master/docs/1.7/IntegratedMesh_ReadMe.md | +| Points | ❌ | https://github.com/Esri/i3s-spec/blob/master/docs/1.7/Point_ReadMe.md | +| PointClouds | ❌ | https://github.com/Esri/i3s-spec/blob/master/docs/2.0/pcsl_ReadMe.md | +| Building Scene Layer | 🚧 experimental | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/BSL_ReadMe.md | ## I3S Aspects support | Aspect | Supported | I3S Spec Link | | --------------------- | --------- | -------------------------------------------------------------------------------------------- | -| Node pages | yes | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/nodePage.cmn.md | -| Compressed attributes | yes | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/compressedAttributes.cmn.md | -| PBR materials | yes | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/pbrMetallicRoughness.cmn.md | -| Feature attributes | yes | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/attributeStorageInfo.cmn.md | -| Texture Atlas | yes | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/texture.cmn.md#atlas-usage-and-regions | +| Node pages | ✅ | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/nodePage.cmn.md | +| Compressed attributes | ✅ | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/compressedAttributes.cmn.md | +| PBR materials | ✅ | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/pbrMetallicRoughness.cmn.md | +| Feature attributes | ✅ | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/attributeStorageInfo.cmn.md | +| Texture Atlas | ✅ | https://github.com/Esri/i3s-spec/blob/master/docs/1.8/texture.cmn.md#atlas-usage-and-regions | ## Texture formats I3S textures specification - https://github.com/Esri/i3s-spec/blob/master/docs/1.8/texture.cmn.md -| Texture | Supported | -| ---------------------------------------------- | ---------- | -| JPEG | yes | -| PNG | yes | -| .dds with DXT1 (no alpha) | yes | -| .dds with DXT5 (alpha channel) | yes | -| ktx-etc2 | not tested | -| Basis Universal Texture format in Khronos KTX2 | yes | +| Texture | Supported | +| ---------------------------------------------- | ------------ | +| JPEG | ✅ | +| PNG | ✅ | +| .dds with DXT1 (no alpha) | ✅ | +| .dds with DXT5 (alpha channel) | ✅ | +| ktx-etc2 | 🚧 not tested | +| Basis Universal Texture format in Khronos KTX2 | ✅ | ## Terms @@ -72,7 +72,7 @@ A simple react app uses [`I3SLoader`](https://github.com/visgl/loaders.gl/blob/m [Example Codepen](https://codepen.io/belom88/pen/JjamLvx) -```js +```typescript import React, {Component} from 'react'; import {StaticMap} from 'react-map-gl'; @@ -157,7 +157,7 @@ A more complex example can be found [here](https://github.com/visgl/loaders.gl/t Basic API usage is illustrated in the following snippet. Create a `Tileset3D` instance, point it a valid tileset URL, set up callbacks, and keep feeding in new camera positions: -```js +```typescript import {load} from '@loaders.gl/core'; import {I3SLoader} from '@loaders.gl/i3s'; import {Tileset3D} from '@loaders.gl/tiles'; @@ -178,7 +178,7 @@ const viewport = new WebMercatorViewport({latitude, longitude, zoom, ...}) tileset3d.update(viewport); // Viewport changes (pan zoom etc) -tileset3d.update(viewport); +tileset3d.selectTiles(viewport); // Visible tiles const visibleTiles = tileset3d.tiles.filter(tile => tile.selected); @@ -237,15 +237,15 @@ The following fields are guaranteed. Additionally, the loaded tile object will c After content is loaded, the following fields are guaranteed. But different tiles may have different extra content fields. -| Field | Type | Contents | -| -------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------- | -| `cartesianOrigin` | `Number[3]` | "Center" of tile geometry in WGS84 fixed frame coordinates | -| `cartographicOrigin` | `Number[3]` | "Origin" in lng/lat (center of tile's bounding volume) | -| `modelMatrix` | `Number[16]` | Transforms tile geometry positions to fixed frame coordinates | -| `vertexCount` | `Number` | Transforms tile geometry positions to fixed frame coordinates | +| Field | Type | Contents | +| -------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- | +| `cartesianOrigin` | `Number[3]` | "Center" of tile geometry in WGS84 fixed frame coordinates | +| `cartographicOrigin` | `Number[3]` | "Origin" in lng/lat (center of tile's bounding volume) | +| `modelMatrix` | `Number[16]` | Transforms tile geometry positions to fixed frame coordinates | +| `vertexCount` | `Number` | Transforms tile geometry positions to fixed frame coordinates | | `attributes` | `Object` | Each attribute follows luma.gl [accessor](https://github.com/visgl/luma.gl/blob/master/docs/api-reference/webgl/README.md) properties | -| `texture` | `Object` | Loaded texture by [`loaders.gl/image`](https://loaders.gl/modules/images/docs/api-reference/image-loader) | -| `featureData` | `Object` | Loaded feature data for parsing the geometies (Will be deprecated in 2.x) | +| `texture` | `Object` | Loaded texture by [`loaders.gl/image`](https://loaders.gl/modules/images/docs/api-reference/image-loader) | +| `featureData` | `Object` | Loaded feature data for parsing the geometies (Will be deprecated in 2.x) | `attributes` contains following fields diff --git a/docs/modules/i3s/recipes/attribute-driven-colorization.md b/docs/modules/i3s/recipes/attribute-driven-colorization.md new file mode 100644 index 0000000000..8de999e8a1 --- /dev/null +++ b/docs/modules/i3s/recipes/attribute-driven-colorization.md @@ -0,0 +1,118 @@ +# Attribute-driven colorization + +

+ From-v3.4 +

+ +In I3S, a feature represents a real-world object within a node. For example, a building within a 3D object scene layer. Node resources such as geometry buffer and attributes can belong to a feature and can be accessed by an object-ID. + +In terms of geometry, every vertex of a mesh is associated with some feature. At the same time, every feature is associated with feature attribute values. For example, there might be `HEIGHT` attribute that stores roof height information about every building. + +All that means that it is possible to make some visual effects related to attribute value. It might be text labels, colors, opacity etc. + +From 3.4 version loaders.gl provides attributes colorization capability that allows colorization of node mesh by some attribute value. + +The complete case of attributes colorization is done in [I3S Explorer](https://i3s.loaders.gl/viewer?tileset=new-york). It is an open source ReactJS application. See source code on [GitHub](https://github.com/visgl/loaders.gl-showcases). + +![I3S New York dataset colorized by height roof](i3s-new-york-colorize-by-heightroof.png) + +## Find out layer's attributes + +It is necessary to pick some attribute to colorize a layer by. So it is necessary to load the layer JSON: + +```javascript +import {load} from '@loaders.gl/core'; +import {I3SLoader} from '@loaders.gl/i3s'; + +const i3sLayer = await load(url, I3SBuildingSceneLayerLoader); +``` + +List and types of attributes might be taken from [`i3sLayer.fields`](https://github.com/Esri/i3s-spec/blob/master/docs/1.9/field.cmn.md) and [`i3sLayer.attributeStorageInfo`](https://github.com/Esri/i3s-spec/blob/master/docs/1.8/attributeStorageInfo.cmn.md) properties. + +## Setup colorization scale + +Attributes colorization capability applies linear color gradient. To create this gradient attribute values range is required. + +To get minimum and maximum attribute values [statistics](https://github.com/Esri/i3s-spec/blob/master/docs/1.9/statisticsInfo.cmn.md) can be used. The [statistics info JSON](https://github.com/Esri/i3s-spec/blob/master/docs/1.9/statsInfo.cmn.md) has min and max values. Usage of those values allows setting true attribute values range not clamping extremum values. + +As soon as statistics info is stored in separate resources, it has to be loaded in a separate request. Statistics is just a JSON data and can be loaded with JSONLoader: + +```javascript +import {load} from '@loaders.gl/core'; +import {JSONLoader} from '@loaders.gl/loader-utils'; + +const attributeStats = await load(`${url}/statistics/f_5/0`, JSONLoader); +``` + +## Use I3SLoader with `colorsByAttribute` option + +To colorize a dataset I3SLoader has to be used with `colorsByAttribute` option: + +``` + import { StaticMap } from "react-map-gl"; + import DeckGL from "@deck.gl/react"; + import { Tile3DLayer } from "@deck.gl/geo-layers"; + import { I3SLoader } from "@loaders.gl/i3s"; + + function renderLayer() { + const loadOptions = { + i3s: { + colorsByAttribute: { + attributeName, + minValue: statistics.min, + maxValue: statistics.max, + minColor: [146, 146, 252, 255], // #9292FC + maxColor: [44, 44, 175, 255], // #2C2CAF + }, + }, + }; + return new Tile3DLayer({ + id: `tile3d-layer-${layer.id}`, + data: url, + loader: I3SLoader, + loadOptions, + }); + } + + + + +``` + +## Colorization mode + +Attributes colorization capability can work in 2 modes: 'replace' and 'multiply'. + +`replace` mode. Attribute-based colors replace geometry vertex colors. This is the default mode. + +`multiply` mode. Attribute-based colors multiply with geometry vertex colors. + +Usage example: + +```javascript +function renderLayer() { + const loadOptions = { + i3s: { + colorsByAttribute: { + attributeName, + minValue: statistics.min, + maxValue: statistics.max, + minColor: [146, 146, 252, 255], // #9292FC + maxColor: [44, 44, 175, 255], // #2C2CAF + mode: 'multiply' // or 'replace' + } + } + }; + return new Tile3DLayer({ + id: `tile3d-layer-${layer.id}`, + data: url, + loader: I3SLoader, + loadOptions + }); +} +``` diff --git a/docs/modules/i3s/recipes/bsl-explorer.png b/docs/modules/i3s/recipes/bsl-explorer.png new file mode 100644 index 0000000000..9e3487ca9b Binary files /dev/null and b/docs/modules/i3s/recipes/bsl-explorer.png differ diff --git a/docs/modules/i3s/recipes/building-scene-layer.md b/docs/modules/i3s/recipes/building-scene-layer.md new file mode 100644 index 0000000000..b2bcb43fa7 --- /dev/null +++ b/docs/modules/i3s/recipes/building-scene-layer.md @@ -0,0 +1,96 @@ +# I3S Building Scene Layer (BSL) + +

+ From-v3.2 +

+ +A building scene layer can represent 3D model content based on BIM structuring disciplines such as architectural or structural, and categories such as windows or walls. In I3S [specification](https://github.com/Esri/i3s-spec) BSL is a layer type. + +BSL doesn't have content resources. It is a composite layer that consists of multiple sublayers. Every sublayer can be: `group`, `3DObject` or `Point`. + +`group` sublayer is also just a composite layer. It contains further levels of sublayers and provides a nested structure for BSL. As a result, BSL has a tree-like sublayers structure with `3DObject` and `Point` layers; + +`3DObject` and `Point` sublayers are content layers. Those layer types are separated types of layers in I3S spec and can be used independently. Loaders.gl supports `3DObject` layer types and doesn't support `Point`. + +## BSL visualization with deck.gl + +I3S `3DObject` layer is shown in deck.gl with [Tile3DLayer](https://deck.gl/docs/api-reference/geo-layers/tile-3d-layer). The complete case of BSL visualization is done in [I3S Explorer](https://i3s.loaders.gl/viewer?tileset=turanga-library). It is an open source ReactJS application. See source code on [GitHub](https://github.com/visgl/loaders.gl-showcases). + +![I3S Explorer BSL visualization](i3s-explorer-building.png) + +### Load BSL + +For BSL it is necessary to do preparation steps to obtain data for `Tile3DLayer`. + +Loaders.gl has `I3SBuildingSceneLayerLoader` that is used to load BSL JSON metadata and pick `3DObject` sublayers. + +```javascript +import {load} from '@loaders.gl/core'; +import {I3SBuildingSceneLayerLoader} from '@loaders.gl/i3s'; + +const {header, sublayers} = await load(tilesetData.url, I3SBuildingSceneLayerLoader); +``` + +The loader returns a flattened array called `sublayers` that contains only `3DObject` layers from the BSL sublayers tree. + +### Show sublayers in deck.gl + +```jsx +import {StaticMap} from 'react-map-gl'; +import DeckGL from '@deck.gl/react'; +import {Tile3DLayer} from '@deck.gl/geo-layers'; +import {COORDINATE_SYSTEM} from '@deck.gl/core'; +import {I3SLoader} from '@loaders.gl/i3s'; + +function renderLayer() { + const tile3dLayers = sublayers.map((layer) => { + if (!layer.visibility) { + return null; + } + const loadOptions = { + i3s: {coordinateSystem: COORDINATE_SYSTEM.LNGLAT_OFFSETS} + }; + if (token) { + loadOptions.i3s.token = token; + } + return new Tile3DLayer({ + id: `tile3d-layer-${layer.id}`, + data: layer.url, + loader: I3SLoader, + loadOptions + }); + }); +} + + + +; +``` + +Take a look at sublayer properties: + +- `id` - unique identifier required to set deck.gl layer id; +- `url` - `3DObject` layer url; +- `visibility` - BSL has an initial visibility state for all sublayers. Use this property to control sublayers' visibility. + +### Control sublayers' visibility + +`I3SBuildingSceneLayerLoader` returns also a `header` object. `header.sublayers` is a JSON nested tree that can be used to visualize BSL explorer panel. + +![BSL Explorer visualization](bsl-explorer.png) + +With such a panel it is possible to change a sublayer visibility property: + +```javascript + function onChangeHandler(treeItem) => { + const sublayer = sublayers.find((sublayer) => sublayer.id === treeItem.id); + if (sublayer) { + sublayer.visibility = !sublayer.visibility; + } + } +``` diff --git a/docs/modules/i3s/recipes/i3s-explorer-building.png b/docs/modules/i3s/recipes/i3s-explorer-building.png new file mode 100644 index 0000000000..ad335e8ea0 Binary files /dev/null and b/docs/modules/i3s/recipes/i3s-explorer-building.png differ diff --git a/docs/modules/i3s/recipes/i3s-new-york-colorize-by-heightroof.png b/docs/modules/i3s/recipes/i3s-new-york-colorize-by-heightroof.png new file mode 100644 index 0000000000..c6165587a6 Binary files /dev/null and b/docs/modules/i3s/recipes/i3s-new-york-colorize-by-heightroof.png differ diff --git a/docs/modules/images/api-reference/binary-image-api.md b/docs/modules/images/api-reference/binary-image-api.md index 31984dc3df..04077849db 100644 --- a/docs/modules/images/api-reference/binary-image-api.md +++ b/docs/modules/images/api-reference/binary-image-api.md @@ -13,7 +13,7 @@ The format is reported using MIME types strings. Supported binary formats and th ## Usage -```js +```typescript const response = await fetchFile(imageUrl); const arrayBuffer = await response.arrayBuffer(); @@ -33,7 +33,7 @@ Parameters: Returns a metadata object describing the image. Returns `null` if the binary data does not represent a known binary image format. -```js +```typescript { mimeType: string; width: number; diff --git a/docs/modules/images/api-reference/image-loader.md b/docs/modules/images/api-reference/image-loader.md index 63a98e590e..76be5a104e 100644 --- a/docs/modules/images/api-reference/image-loader.md +++ b/docs/modules/images/api-reference/image-loader.md @@ -14,7 +14,7 @@ An image loader that works under both Node.js (requires `@loaders.gl/polyfills`) ## Usage -```js +```typescript import '@loaders.gl/polyfills'; // only needed if using under Node import {ImageLoader} from '@loaders.gl/images'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/images/api-reference/image-writer.md b/docs/modules/images/api-reference/image-writer.md index 36e7334b56..c6e0adadc0 100644 --- a/docs/modules/images/api-reference/image-writer.md +++ b/docs/modules/images/api-reference/image-writer.md @@ -14,7 +14,7 @@ The `ImageWriter` class can encode an image into `ArrayBuffer` both under browse ## Usage -```js +```typescript import '@loaders.gl/polyfill'; // only if using under Node import {ImageWriter} from '@loaders.gl/images'; import {encode} from '@loaders.gl/core'; diff --git a/docs/modules/images/api-reference/parsed-image-api.md b/docs/modules/images/api-reference/parsed-image-api.md index 5c5faee0a7..79eab94a10 100644 --- a/docs/modules/images/api-reference/parsed-image-api.md +++ b/docs/modules/images/api-reference/parsed-image-api.md @@ -8,7 +8,7 @@ Background: The image returned by the [`ImageLoader`](/docs/modules/images/api-r E.g., the `getImageData` method enables the application to get width, height and pixel data from an image returned by the `ImageLoader` in a platform independent way: -```js +```typescript import {ImageLoader, getImageSize, getImageData} from `@loaders.gl/images`; import {load} from `@loaders.gl/core`; diff --git a/docs/modules/json/README.md b/docs/modules/json/README.md index 48ff165310..ba81ca9b2b 100644 --- a/docs/modules/json/README.md +++ b/docs/modules/json/README.md @@ -24,9 +24,7 @@ npm install @loaders.gl/core @loaders.gl/json | [`GeoJSONLoader`](/docs/modules/json/api-reference/geojson-loader) | | [`NDGeoJSONLoader`](/docs/modules/json/api-reference/ndgeojson-loader) | | [`JSONWriter`](/docs/modules/json/api-reference/json-writer) | -| [`NDJSONWriter`](/docs/modules/json/api-reference/ndjson-writer) | | [`GeoJSONWriter`](/docs/modules/json/api-reference/geojson-writer) | -| [`NDGeoJSONWriter`](/docs/modules/json/api-reference/ndgeojson-writer) | ## Additional APIs diff --git a/docs/modules/json/api-reference/geojson-loader.md b/docs/modules/json/api-reference/geojson-loader.md index 1eb61d4e1d..5cfcafb862 100644 --- a/docs/modules/json/api-reference/geojson-loader.md +++ b/docs/modules/json/api-reference/geojson-loader.md @@ -17,7 +17,7 @@ Streaming loader for GeoJSON encoded files. For simple usage, you can load and parse a JSON file atomically: -```js +```typescript import {GeoJSONLoader} from '@loaders.gl/json'; import {load} from '@loaders.gl/core'; @@ -27,7 +27,7 @@ const data = await load(url, GeoJSONLoader, {json: options}); For larger files, GeoJSONLoader supports streaming JSON parsing, in which case it will yield "batches" of rows from one array. To parse a stream of GeoJSON, the user can specify the `options.json.jsonpaths` to stream the `features` array. -```js +```typescript import {GeoJSONLoader} from '@loaders.gl/json'; import {loadInBatches} from '@loaders.gl/core'; @@ -50,7 +50,7 @@ When batch parsing an embedded JSON array as a table, it is possible to get acce The loader will yield an initial and a final batch with `batch.container` providing the container object and `batch.batchType` set to `partial-result` and `final-result` respectively. -```js +```typescript import {GeoJSONLoader} from '@loaders.gl/json'; import {loadInBatches} from '@loaders.gl/core'; diff --git a/docs/modules/json/api-reference/geojson-writer.md b/docs/modules/json/api-reference/geojson-writer.md index c23c699109..9c76f05087 100644 --- a/docs/modules/json/api-reference/geojson-writer.md +++ b/docs/modules/json/api-reference/geojson-writer.md @@ -1,6 +1,10 @@ # GeoJSONWriter -Streaming loader for GeoJSON encoded files. +

+ From-v4.0 +

+ +Streaming writer for GeoJSON encoded files. | Loader | Characteristic | | -------------- | ---------------------------------------------------- | @@ -17,22 +21,22 @@ Streaming loader for GeoJSON encoded files. For simple usage, you can encode a table into a JSON "file" atomically: -```js -import {GeoJSONLoader} from '@loaders.gl/json'; +```typescript +import {GeoJSONWriter} from '@loaders.gl/json'; import {encode} from '@loaders.gl/core'; -const data = await encode(url, GeoJSONLoader, {json: options}); +const data = await encode(url, GeoJSONWriter, {json: options}); ``` ### Streaming and JSON paths -For larger files, GeoJSONLoader supports streaming JSON parsing, in which case it will yield "batches" of rows from one array. +For larger files, GeoJSONWriter supports streaming JSON parsing, in which case it will yield "batches" of rows from one array. -```js -import {GeoJSONLoader} from '@loaders.gl/json'; +```typescript +import {GeoJSONWriter} from '@loaders.gl/json'; import {encodeInBatches} from '@loaders.gl/core'; -const batches = await encodeInBatches('geojson.json', GeoJSONLoader, {json: {jsonpaths: ['$.features']}}); +const batches = await encodeInBatches('geojson.json', GeoJSONWriter, {json: {jsonpaths: ['$.features']}}); for await (const batch of batches) { // batch.data will contain a number of rows diff --git a/docs/modules/json/api-reference/json-loader.md b/docs/modules/json/api-reference/json-loader.md index 423dc310ec..daa7ec8eb1 100644 --- a/docs/modules/json/api-reference/json-loader.md +++ b/docs/modules/json/api-reference/json-loader.md @@ -15,7 +15,7 @@ Streaming loader for JSON encoded files. For simple usage, you can load and parse a JSON file atomically: -```js +```typescript import {JSONLoader} from '@loaders.gl/json'; import {load} from '@loaders.gl/core'; @@ -25,7 +25,7 @@ const data = await load(url, JSONLoader, {json: options}); For larger files, JSONLoader supports streaming JSON parsing, in which case it will yield "batches" of rows from one array. To parse a stream of GeoJSON, the user can specify the `options.json.jsonpaths` to stream the `features` array. -```js +```typescript import {JSONLoader} from '@loaders.gl/json'; import {loadInBatches} from '@loaders.gl/core'; @@ -48,7 +48,7 @@ When batch parsing an embedded JSON array as a table, it is possible to get acce The loader will yield an initial and a final batch with `batch.container` providing the container object and `batch.batchType` set to `partial-result` and `final-result` respectively. -```js +```typescript import {JSONLoader} from '@loaders.gl/json'; import {loadInBatches} from '@loaders.gl/core'; diff --git a/docs/modules/json/api-reference/json-writer.md b/docs/modules/json/api-reference/json-writer.md new file mode 100644 index 0000000000..92a68e03f6 --- /dev/null +++ b/docs/modules/json/api-reference/json-writer.md @@ -0,0 +1,79 @@ +# JSONWriter + +

+ From-v4.0 +

+ +Streaming writer for GeoJSON encoded files. + +| Loader | Characteristic | +| -------------- | ---------------------------------------------------- | +| File Extension | `.geojson` | +| Media Type | `application/geo+json` | +| File Type | Text | +| File Format | JSON | +| Data Format | [Classic Table](/docs/specifications/category-table) | +| Supported APIs | `encode`, `encodeSync`, `encodeÓInBatches` | + +## Usage + +For simple usage, you can encode a table into a JSON "file" atomically: + +```typescript +import {JSONWriter} from '@loaders.gl/json'; +import {encode} from '@loaders.gl/core'; + +const data = await encode(url, JSONWriter, {json: options}); +``` + +### Streaming and JSON paths + +For larger files, JSONWriter supports streaming JSON parsing, in which case it will yield "batches" of rows from one array. + +```typescript +import {JSONWriter} from '@loaders.gl/json'; +import {encodeInBatches} from '@loaders.gl/core'; + +const batches = await encodeInBatches('geojson.json', JSONWriter, {json: {jsonpaths: ['$.features']}}); + +for await (const batch of batches) { + // batch.data will contain a number of rows + for (const feature of batch.data) { + switch (feature.geometry.type) { + case 'Polygon': + ... + } + } +} +``` + +To parse a stream of GeoJSON, the user can specify the `options.json.jsonpaths` to stream the `features` array. + +If no JSONPath is specified the loader will stream the first array it encounters in the JSON payload. + + +## Data Format + +Encoded batches are array buffers or strings + +## Options + +Supports table category options such as `batchType` and `batchSize`. + +| Option | From | Type | Default | Description | +| ---------------- | ------------------------------------------------------------------------------------- | ---------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `json.table` | [![Website shields.io](https://img.shields.io/badge/v2.0-blue.svg?style=flat-square)] | `boolean` | `false` | Parses non-streaming JSON as table, i.e. return the first embedded array in the JSON. Always `true` during batched/streaming parsing. | +| `json.jsonpaths` | [![Website shields.io](https://img.shields.io/badge/v2.2-blue.svg?style=flat-square)] | `string[]` | `[]` | A list of JSON paths (see below) indicating the array that can be streamed. | + +## JSONPaths + +A minimal subset of the JSONPath syntax is supported, to specify which array in a JSON object should be streamed as batchs. + +`$.component1.component2.component3` + +- No support for wildcards, brackets etc. Only paths starting with `$` (JSON root) are supported. +- Regardless of the paths provided, only arrays will be streamed. + +## Attribution + +This loader is based on a fork of dscape's [`clarinet`](https://github.com/dscape/clarinet) under BSD 2-clause license. diff --git a/docs/modules/json/api-reference/ndjson-loader.md b/docs/modules/json/api-reference/ndjson-loader.md index 484f9be12a..7bae462964 100644 --- a/docs/modules/json/api-reference/ndjson-loader.md +++ b/docs/modules/json/api-reference/ndjson-loader.md @@ -18,7 +18,7 @@ Streaming loader for NDJSON encoded files and related formats (LDJSON and JSONL) ## Usage -```js +```typescript import {NDJSONLoader} from '@loaders.gl/json'; import {load} from '@loaders.gl/core'; @@ -27,7 +27,7 @@ const data = await load(url, NDJSONLoader, {ndjson: options}); The NDJSONLoader supports streaming NDJSON parsing, in which case it will yield "batches" of rows, where each row is a parsed line from the NDJSON stream. -```js +```typescript import {NDJSONLoader} from '@loaders.gl/json'; import {loadInBatches} from '@loaders.gl/core'; diff --git a/docs/modules/json/formats/geojson-geometry.md b/docs/modules/json/formats/geojson-geometry.md new file mode 100644 index 0000000000..fc2bc2db90 --- /dev/null +++ b/docs/modules/json/formats/geojson-geometry.md @@ -0,0 +1,47 @@ +# GeoJSON Geometry + +GeoJSON geometries can sometimes be useful independently of GeoJSON. + +## Examples + +```json + { + "type": "Point", + "coordinates": [102.0, 0.5] + }, +``` + +```json + { + "type": "LineString", + "coordinates": [ + [102.0, 0.0], + [103.0, 1.0], + [104.0, 0.0], + [105.0, 1.0] + ] + }, +``` + +```json + { + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ] + ] + } +``` + +## Alternatives + +| Format | Notes | +| ------------ | ------------------------------------------------------------------- | +| WKB | Binary, more compact | +| WKT | Text based, slightly more compact, a bit harder to parse (not JSON) | +| GML Geometry | XML based, even more verbose, more complex to parse | diff --git a/docs/modules/json/formats/geojson.md b/docs/modules/json/formats/geojson.md new file mode 100644 index 0000000000..4311851ad9 --- /dev/null +++ b/docs/modules/json/formats/geojson.md @@ -0,0 +1,16 @@ +# GeoJSON + +- [IETF standard](https://datatracker.ietf.org/doc/html/rfc7946) +- [geojson.org](https://geojson.org/) + +GeoJSON is a format for encoding a variety of geographic data structures together with properties. + +## Geometries + +Since GeoJSON geometries can be independently useful they are described on a [separate page](./geojson-geometry). + +## Alternatives + +| Format | Support | Description | +| -------- | ------- | --------------------------------------------- | +| TopoJSON | ❌ | A more compact version that encodes topology. | diff --git a/docs/modules/kml/api-reference/gpx-loader.md b/docs/modules/kml/api-reference/gpx-loader.md index 647cb1eee8..9c491b49d5 100644 --- a/docs/modules/kml/api-reference/gpx-loader.md +++ b/docs/modules/kml/api-reference/gpx-loader.md @@ -6,11 +6,6 @@ The `GPXLoader` parses [GPX files][gpx_wikipedia] into GeoJSON. From Wikipedia: -> GPX, or GPS Exchange Format, is an XML schema designed as a common GPS data -> format for software applications. It can be used to describe waypoints, -> tracks, and routes. ... Location data (and optionally elevation, time, and -> other information) is stored in tags and can be interchanged between GPS -> devices and software. | Loader | Characteristic | | --------------------- | ------------------------------------------ | @@ -26,7 +21,7 @@ The `GPXLoader` parses [GPX files][gpx_wikipedia] into GeoJSON. From Wikipedia: ## Usage -```js +```typescript import {GPXLoader} from '@loaders.gl/kml'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/kml/api-reference/kml-loader.md b/docs/modules/kml/api-reference/kml-loader.md index af6a040fe3..8ad5def182 100644 --- a/docs/modules/kml/api-reference/kml-loader.md +++ b/docs/modules/kml/api-reference/kml-loader.md @@ -1,14 +1,8 @@ # KMLLoader -The `KMLLoader` parses [KML files][kml_wikipedia] into GeoJSON. From Wikipedia: - -> Keyhole Markup Language (KML) is an XML notation for expressing geographic -> annotation and visualization within two-dimensional maps and three-dimensional -> Earth browsers. +![ogc-logo](../../../images/logos/ogc-logo-60.png) -KML is now an [Open Geospatial Consortium standard][kml_ogc_standard]. - -[kml_ogc_standard]: https://www.ogc.org/standards/kml +The `KMLLoader` parses [KML files][kml_wikipedia] into GeoJSON. From Wikipedia: | Loader | Characteristic | | --------------------- | ------------------------------------------ | @@ -24,7 +18,7 @@ KML is now an [Open Geospatial Consortium standard][kml_ogc_standard]. ## Usage -```js +```typescript import {KMLLoader} from '@loaders.gl/kml'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/kml/api-reference/tcx-loader.md b/docs/modules/kml/api-reference/tcx-loader.md index ba70b171f6..6e634f3496 100644 --- a/docs/modules/kml/api-reference/tcx-loader.md +++ b/docs/modules/kml/api-reference/tcx-loader.md @@ -6,13 +6,6 @@ The `TCXLoader` parses [TCX files][tcx_wikipedia] into GeoJSON. From Wikipedia: -> Training Center XML (TCX) is a data exchange format introduced in 2007 as part -> of Garmin's Training Center product. The XML is similar to GPX since it -> exchanges GPS tracks, but treats a track as an Activity rather than simply a -> series of GPS points. TCX provides standards for transferring heart rate, -> running cadence, bicycle cadence, calories in the detailed track. It also -> provides summary data in the form of laps. - | Loader | Characteristic | | --------------------- | ------------------------------------------ | | File Extension | `.tcx` | @@ -27,7 +20,7 @@ The `TCXLoader` parses [TCX files][tcx_wikipedia] into GeoJSON. From Wikipedia: ## Usage -```js +```typescript import {TCXLoader} from '@loaders.gl/kml'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/kml/formats/gpx.md b/docs/modules/kml/formats/gpx.md new file mode 100644 index 0000000000..934e4a9550 --- /dev/null +++ b/docs/modules/kml/formats/gpx.md @@ -0,0 +1,9 @@ +# GPX + +- [GPX - Wikipedia](https://en.wikipedia.org/wiki/GPS_Exchange_Format) + +GPX, or GPS Exchange Format, is an XML schema designed as a common GPS data +format for software applications. It can be used to describe waypoints, +tracks, and routes. ... Location data (and optionally elevation, time, and +other information) is stored in tags and can be interchanged between GPS +devices and software. \ No newline at end of file diff --git a/docs/modules/kml/formats/kml.md b/docs/modules/kml/formats/kml.md new file mode 100644 index 0000000000..185f2ab520 --- /dev/null +++ b/docs/modules/kml/formats/kml.md @@ -0,0 +1,13 @@ +# KML + +![ogc-logo](../../../images/logos/ogc-logo-60.png) + +- [KML Tutorial - Google](https://developers.google.com/kml/documentation/kml_tut) + +Keyhole Markup Language (KML) is an XML notation for expressing geographic +annotation and visualization within two-dimensional maps and three-dimensional +Earth browsers. + +KML is now an [Open Geospatial Consortium standard][kml_ogc_standard]. + +[kml_ogc_standard]: https://www.ogc.org/standards/kml diff --git a/docs/modules/kml/formats/tcx.md b/docs/modules/kml/formats/tcx.md new file mode 100644 index 0000000000..f0ed60149a --- /dev/null +++ b/docs/modules/kml/formats/tcx.md @@ -0,0 +1,10 @@ +# TCX + +- [TCX - Wikipedia](https://en.wikipedia.org/wiki/Training_Center_XML) + +Training Center XML (TCX) is a data exchange format introduced in 2007 as part +of Garmin's Training Center product. The XML is similar to GPX since it +exchanges GPS tracks, but treats a track as an Activity rather than simply a +series of GPS points. TCX provides standards for transferring heart rate, +running cadence, bicycle cadence, calories in the detailed track. It also +provides summary data in the form of laps. diff --git a/docs/modules/las/api-reference/las-loader.md b/docs/modules/las/api-reference/las-loader.md index 9012c366fc..59ed17ee78 100644 --- a/docs/modules/las/api-reference/las-loader.md +++ b/docs/modules/las/api-reference/las-loader.md @@ -14,7 +14,7 @@ The `LASLoader` parses a point cloud in the LASER file format. ## Usage -```js +```typescript import {LASLoader} from '@loaders.gl/las'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/las/formats/las.md b/docs/modules/las/formats/las.md new file mode 100644 index 0000000000..1347004b3c --- /dev/null +++ b/docs/modules/las/formats/las.md @@ -0,0 +1,3 @@ +# LAS / LAZ + +The `@loaders.gl/las` module handles the [LASER file format](https://www.asprs.org/divisions-committees/lidar-division/laser-las-file-format-exchange-activities) (LAS) or its compressed version (LAZ), a public format for the interchange of 3-dimensional point cloud data data, developed for LIDAR mapping purposes. diff --git a/docs/modules/lerc/README.md b/docs/modules/lerc/README.md new file mode 100644 index 0000000000..4804ca0a72 --- /dev/null +++ b/docs/modules/lerc/README.md @@ -0,0 +1,5 @@ +# Overview + +

+ From-v4.0 +

diff --git a/docs/modules/wms/api-reference/lerc-loader.md b/docs/modules/lerc/api-reference/lerc-loader.md similarity index 99% rename from docs/modules/wms/api-reference/lerc-loader.md rename to docs/modules/lerc/api-reference/lerc-loader.md index 54faf49145..1325cb609a 100644 --- a/docs/modules/wms/api-reference/lerc-loader.md +++ b/docs/modules/lerc/api-reference/lerc-loader.md @@ -24,7 +24,7 @@ There are two major versions, known as "Lerc1" and "Lerc2". ## Usage -```js +```typescript import {LERCLoader} from '@loaders.gl/wms'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/wms/formats/lerc.md b/docs/modules/lerc/formats/lerc.md similarity index 100% rename from docs/modules/wms/formats/lerc.md rename to docs/modules/lerc/formats/lerc.md diff --git a/docs/modules/loader-utils/README.md b/docs/modules/loader-utils/README.md new file mode 100644 index 0000000000..9e008035f2 --- /dev/null +++ b/docs/modules/loader-utils/README.md @@ -0,0 +1,3 @@ +# Overview + +The `@loaders.gl/loader-utils` contains utilities for creating loaders. diff --git a/docs/modules/loader-utils/api-reference/parse-with-context.md b/docs/modules/loader-utils/api-reference/parse-with-context.md new file mode 100644 index 0000000000..2d51da7515 --- /dev/null +++ b/docs/modules/loader-utils/api-reference/parse-with-context.md @@ -0,0 +1,25 @@ +# parseWithContext + +Use when invoking a sub-loader from a loader, to parse embedded data or perhaps an associated resource. + +## Usage + +```typescript +import {parseWithContext} from '@loaders.gl/loader-utils'; +import {OBJLoader} from '@loaders.gl/obj'; + +parse(data: ArrayBuffer, options: LoaderOptions, context?: LoaderContext) { + const subData = data.slice(100, 200); + data = await parseWithContext(subData, OBJLoader, options, context); +} +... +``` + +## Functions + +### parse + +### parseSyncWithContext + +### parseInBatchesWithContext + diff --git a/docs/modules/loader-utils/api-reference/request-scheduler.md b/docs/modules/loader-utils/api-reference/request-scheduler.md index dd682cd5cc..06f9340c38 100644 --- a/docs/modules/loader-utils/api-reference/request-scheduler.md +++ b/docs/modules/loader-utils/api-reference/request-scheduler.md @@ -18,7 +18,7 @@ A primary use case is to let the app reprioritize or cancel requests if circumst To schedule a request so that it can be issued at a time when it can be immediately processed. -```js +```typescript const URL = '...'; const requestToken = await requestScheduler.scheduleRequest(URL); if (requestToken) { diff --git a/docs/modules/math/api-reference/gl-type.md b/docs/modules/math/api-reference/gl-type.md index 89caaa3b25..14616f92e6 100644 --- a/docs/modules/math/api-reference/gl-type.md +++ b/docs/modules/math/api-reference/gl-type.md @@ -16,7 +16,7 @@ Helper functions to work with WebGL data type constants. ## Usage -```js +```typescript import {GL, GLType} from '@loaders.gl/math'; // Returns Int8Array.BYTES_PER_ELEMENT var size = GLType.getSizeInBytes(GL.BYTE); diff --git a/docs/modules/mvt/README.md b/docs/modules/mvt/README.md index bd1fdaae64..940695052c 100644 --- a/docs/modules/mvt/README.md +++ b/docs/modules/mvt/README.md @@ -1,6 +1,6 @@ # Overview -The `@loaders.gl/mvt` module handles the [Mapbox Vector Tile](https://github.com/mapbox/vector-tile-spec) format, a protobuf-encoded format that defines geospatial geometries. +The `@loaders.gl/mvt` module handles the [Mapbox Vector Tile](/docs/modules/mvt/formats/mvt) format, a protobuf-encoded format that defines geospatial geometries. The modules also provides a `GeoJSONTiler` class that can serve up equivalent parsed tiles from an in-memory `GeoJSON` file. diff --git a/docs/modules/mvt/api-reference/mvt-loader.md b/docs/modules/mvt/api-reference/mvt-loader.md index e4c625d36b..43e4018efe 100644 --- a/docs/modules/mvt/api-reference/mvt-loader.md +++ b/docs/modules/mvt/api-reference/mvt-loader.md @@ -12,7 +12,7 @@ Loader for the [Mapbox Vector Tile](https://docs.mapbox.com/vector-tiles/specifi ## Usage -```js +```typescript import {MVTLoader} from '@loaders.gl/mvt'; import {load} from '@loaders.gl/core'; @@ -40,7 +40,7 @@ const geoJSONfeatures = await load(url, MVTLoader, loaderOptions); The parser will return an array of [GeoJSON objects](https://tools.ietf.org/html/rfc7946) with WGS84 coordinates and feature properties from MVT if `coordinates` property is set to `wgs84` and `tileIndex` properties are present. -```js +```typescript import {MVTLoader} from '@loaders.gl/mvt'; import {load} from '@loaders.gl/core'; @@ -66,7 +66,7 @@ Even though tile coordinates go from 0 to 1, there can be some negative (or grea Note that local coordinates are relative to tile origin, which is in the top left. -```js +```typescript import {MVTLoader} from '@loaders.gl/mvt'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/mvt/api-reference/tilejson-loader.md b/docs/modules/mvt/api-reference/tilejson-loader.md new file mode 100644 index 0000000000..a1c584a7ec --- /dev/null +++ b/docs/modules/mvt/api-reference/tilejson-loader.md @@ -0,0 +1,27 @@ +# PMTilesLoader + +The `PMTilesLoader` parses header/metadata from a pmtiles archive + +| Loader | Characteristic | +| --------------------- | -------------------------------------------------- | +| File Extension | `.json` | +| File Type | Text | +| File Format | [TileJSON](/docs/modules/mvt/formats/tilejson) | +| Data Format | TileJSON | +| Decoder Type | Synchronous | +| Worker Thread Support | No | +| Streaming Support | No | + +## Usage + +```typescript +import {TileJSONLoader} from '@loaders.gl/pmtiles'; +import {load} from '@loaders.gl/core'; + +const tileJSON = await load(url, TileJSONLoader, options); +``` + +## Options + +| Option | Type | Default | Description | +| ------ | ---- | ------- | ----------- | diff --git a/docs/modules/mvt/formats/mvt.md b/docs/modules/mvt/formats/mvt.md new file mode 100644 index 0000000000..665589668b --- /dev/null +++ b/docs/modules/mvt/formats/mvt.md @@ -0,0 +1,15 @@ +# Mapbox Vector Tile + +- *[Mapbox Vector Tile Specification](https://github.com/mapbox/vector-tile-spec)* + + +A specification for encoding tiled vector data. + +MVT is a protobuf-encoded format that defines geospatial geometries. + +## Metadata + +It is often useful to have global metadata about a tileset. A common complementary format for encoding tileset metadata is [TileJSON](./tilejson). + + + diff --git a/docs/modules/mvt/formats/tilejson.md b/docs/modules/mvt/formats/tilejson.md new file mode 100644 index 0000000000..5c29814728 --- /dev/null +++ b/docs/modules/mvt/formats/tilejson.md @@ -0,0 +1,30 @@ +# TileJSON / Tilestats + +- *[TileJSON specification](https://github.com/mapbox/tilejson-spec/blob/master/3.0.0/README.md)* +- *[Tilestats information](https://github.com/mapbox/mapbox-geostats) +- *[Tilestats generation](https://github.com/mapbox/mapbox-geostats#output-the-stats) + +## TileJSON + +Metadata about a tileset. + +for representing metadata about multiple types of web-based map layers, to aid clients in configuration and browsing. + +As the name suggests, TileJSON is JSON encoded. + +## Tilestats + +Tilestats is a valuable inofficial "extension" to TileJSON. It provides column statistics, notably: +- the data type of each column +- min/max values for numeric columns (enabling e.g. global color scale calculations). +- a sample of values for each column + +Tilestats are not always available so applications must be prepared to work in their absence. +However, tilestats is output by major tilers such as [tippecanoe](https://github.com/mapbox/mapbox-geostats#output-the-stats). + + +## Fields + +| Data | TileJSON | tilestats | Description | +| --- | --- | --- | --- | +| \ No newline at end of file diff --git a/docs/modules/obj/README.md b/docs/modules/obj/README.md index c665495bb3..88cbcf6147 100644 --- a/docs/modules/obj/README.md +++ b/docs/modules/obj/README.md @@ -9,6 +9,12 @@ npm install @loaders.gl/obj npm install @loaders.gl/core ``` +## Loaders and Writers + +| Loader | +| -------------------------------------------------------- | +| [`OBJLoader`](/docs/modules/obj/api-reference/obj-loader) | + ## Attribution OBJLoader is a port of [three.js](https://github.com/mrdoob/three.js)'s OBJLoader under MIT License. diff --git a/docs/modules/obj/api-reference/obj-loader.md b/docs/modules/obj/api-reference/obj-loader.md index 80d4178d03..9f97232553 100644 --- a/docs/modules/obj/api-reference/obj-loader.md +++ b/docs/modules/obj/api-reference/obj-loader.md @@ -14,7 +14,7 @@ The `OBJLoader` parses the OBJ half of the classic Wavefront OBJ/MTL format. ## Usage -```js +```typescript import {OBJLoader} from '@loaders.gl/obj'; import {load} from '@loaders.gl/core'; @@ -25,3 +25,11 @@ const data = await load(url, OBJLoader, options); | Option | Type | Default | Description | | ------ | ---- | ------- | ----------- | + +Remarks: + +- vertex colors are parsed as a `COLOR_0` attribute when red, green and blue values are included after x y and z (this precludes specifying w). The color values range from 0 to 1. + +## Attribution + +OBJLoader is a port of [three.js](https://github.com/mrdoob/three.js)'s OBJLoader under MIT License. \ No newline at end of file diff --git a/docs/modules/parquet/README.md b/docs/modules/parquet/README.md index e14c3e7b80..2397958f32 100644 --- a/docs/modules/parquet/README.md +++ b/docs/modules/parquet/README.md @@ -1,4 +1,4 @@ -# @loaders.gl/parquet +# @loaders.gl/parquet 🚧

From-v3.1 diff --git a/docs/modules/parquet/api-reference/parquet-loader.md b/docs/modules/parquet/api-reference/parquet-loader.md index be37e07927..e44b611cce 100644 --- a/docs/modules/parquet/api-reference/parquet-loader.md +++ b/docs/modules/parquet/api-reference/parquet-loader.md @@ -1,4 +1,4 @@ -# ParquetLoader +# ParquetLoader 🆕 🚧

From-v3.1 diff --git a/docs/modules/parquet/api-reference/parquet-writer.md b/docs/modules/parquet/api-reference/parquet-writer.md index ac4d488106..da8d9f81ec 100644 --- a/docs/modules/parquet/api-reference/parquet-writer.md +++ b/docs/modules/parquet/api-reference/parquet-writer.md @@ -1,4 +1,4 @@ -# ParquetWriter +# ParquetWriter 🆕 🚧

From-v3.1 @@ -10,4 +10,4 @@ The Parquet format supports a large set of features (data types, encodings, compressions, encryptions etc) it require time and contributions for the loaders.gl implementation to provide support for all variations. -Please refer to the detailed information about which [Parquet format features](/docs/modules/parquet/format/parquet) are supported. +Please refer to the detailed information about which [Parquet format features](/docs/modules/parquet/formats/parquet) are supported. diff --git a/docs/modules/parquet/formats/geoparquet.md b/docs/modules/parquet/formats/geoparquet.md index 851957ef3a..6665b0584f 100644 --- a/docs/modules/parquet/formats/geoparquet.md +++ b/docs/modules/parquet/formats/geoparquet.md @@ -7,9 +7,11 @@ Geoparquet is a set of conventions for storing geospatial data in Parquet files. Standardization is happening at [geoparquet.org](https://geoparquet.org). -GeoParquet is similar to GeoArrow, as both a binary columnar formats with a high degree of similarity. +GeoParquet file is a Parquet file that additionally follows these conventions: -Essentially a GeoParquet file is a Parquet file that follows these conventions: +- Geospatial metadata describing any geospatial columns is stored in the Parquet file's schema metadata (as stringified JSON). +- Geometry columns are [WKB](/docs/modules/wkt/formats/wkb) encoded (additional encodings will likely be added)/ -- JSON encoded metadata stored in the Parquet file's schema metadata. -- WKB encoded geometry columns +## Alternatives + +GeoParquet can be compared to GeoArrow, as both are binary columnar formats with a high degree of similarity. diff --git a/docs/modules/parquet/formats/parquet.md b/docs/modules/parquet/formats/parquet.md index e70d8c8e08..2154dc93eb 100644 --- a/docs/modules/parquet/formats/parquet.md +++ b/docs/modules/parquet/formats/parquet.md @@ -7,15 +7,14 @@ Parquet is a binary columnar format optimized for compact storage on disk. The GitHUB specification of [Apache Parquet](https://github.com/apache/parquet-format/blob/master/README.md). -## Column encodings +## Pages -Some encodings are intended to improve successive column compression by organizing data so that it is less random. +columns can be divided into pages (similar to Apache Arrow record batches) so that partial columns covering a range of rows can be read without reading the entire file. -## Compared to similar formats +## Alternatives In contrast to Arrow which is designed to minimize serialization and deserialization, Parquet is optimized for storage on disk. - ## Compression Since Parquet is designed for read-write access, compression is applied per column chunk. @@ -24,28 +23,30 @@ A wide range of compression codecs are supported. Internal parquet compression f | Type | Read | Write | | -------------- | ---- | ----- | -| `UNCOMPRESSED` | YES | YES | -| `GZIP` | YES | YES | -| `SNAPPY` | YES | YES | | -| `BROTLI` | YES | No | | -| `LZO` | NO | NO | There is currently no readily available browser-based LZO module for JS | -| `LZ4` | YES | YES | -| `LZ4_RAW` | YES | YES | -| `ZSTD` | YES | YES | | +| `UNCOMPRESSED` | ✅ | ✅ | +| `GZIP` | ✅ | ✅ | +| `SNAPPY` | ✅ | ✅ | | +| `BROTLI` | ✅ | No | | +| `LZO` | ❌ | ❌ | There is currently no readily available browser-based LZO module for JS | +| `LZ4` | ✅ | ✅ | +| `LZ4_RAW` | ✅ | ✅ | +| `ZSTD` | ✅ | ✅ | | ## Encoding +Some encodings are intended to improve successive column compression by organizing data so that it is less random. + The following Parquet encodings are supported: | Encoding | Read | Write | Types | | ------------------------- | ---- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `PLAIN` | YES | YES | All | -| `PLAIN_DICTIONARY` | YES | YES | All | -| `RLE_DICTIONARY` | YES | NO | All | -| `DELTA_BINARY_PACKED` | NO | NO | `INT32`, `INT64`, `INT_8`, `INT_16`, `INT_32`, `INT_64`, `UINT_8`, `UINT_16`, `UINT_32`, `UINT_64`, `TIME_MILLIS`, `TIME_MICROS`, `TIMESTAMP_MILLIS`, `TIMESTAMP_MICROS` | -| `DELTA_BYTE_ARRAY` | NO | NO | `BYTE_ARRAY`, `UTF8` | -| `DELTA_LENGTH_BYTE_ARRAY` | NO | NO | `BYTE_ARRAY`, `UTF8` | +| `PLAIN` | ✅ | ✅ | All | +| `PLAIN_DICTIONARY` | ✅ | ✅ | All | +| `RLE_DICTIONARY` | ✅ | ❌ | All | +| `DELTA_BINARY_PACKED` | ❌ | ❌ | `INT32`, `INT64`, `INT_8`, `INT_16`, `INT_32`, `INT_64`, `UINT_8`, `UINT_16`, `UINT_32`, `UINT_64`, `TIME_MILLIS`, `TIME_MICROS`, `TIMESTAMP_MILLIS`, `TIMESTAMP_MICROS` | +| `DELTA_BYTE_ARRAY` | ❌ | ❌ | `BYTE_ARRAY`, `UTF8` | +| `DELTA_LENGTH_BYTE_ARRAY` | ❌ | ❌ | `BYTE_ARRAY`, `UTF8` | ## Repetition @@ -53,9 +54,9 @@ There are three repetition types in Parquet: | Repetition | Supported | | ---------- | --------- | -| `REQUIRED` | YES | -| `OPTIONAL` | YES | -| `REPEATED` | YES | +| `REQUIRED` | ✅ | +| `OPTIONAL` | ✅ | +| `REPEATED` | ✅ | ### Record Shredding diff --git a/docs/modules/pcd/api-reference/pcd-loader.md b/docs/modules/pcd/api-reference/pcd-loader.md index d9716cb23e..b63885c919 100644 --- a/docs/modules/pcd/api-reference/pcd-loader.md +++ b/docs/modules/pcd/api-reference/pcd-loader.md @@ -16,7 +16,7 @@ Note: Currently supports `ascii`, `binary` and compressed binary files. ## Usage -```js +```typescript import {PCDLoader} from '@loaders.gl/pcd'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/ply/api-reference/ply-loader.md b/docs/modules/ply/api-reference/ply-loader.md index b6d2d5707a..2e84ef6e10 100644 --- a/docs/modules/ply/api-reference/ply-loader.md +++ b/docs/modules/ply/api-reference/ply-loader.md @@ -14,7 +14,7 @@ The `PLYLoader` parses simple meshes in the Polygon File Format or the Stanford ## Usage -```js +```typescript import {PLYLoader} from '@loaders.gl/ply'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/pmtiles/README.md b/docs/modules/pmtiles/README.md new file mode 100644 index 0000000000..bcce3fdf12 --- /dev/null +++ b/docs/modules/pmtiles/README.md @@ -0,0 +1,3 @@ +# @loaders.gl/pmtiles + +Support for loading [pmtiles](/docs/modules/pmtiles/formats/pmtiles) format tiles. diff --git a/docs/modules/pmtiles/api-reference/pmtiles-source.md b/docs/modules/pmtiles/api-reference/pmtiles-source.md new file mode 100644 index 0000000000..ec1d342cae --- /dev/null +++ b/docs/modules/pmtiles/api-reference/pmtiles-source.md @@ -0,0 +1,28 @@ +# PMTilesSource 🆕 + +The `PMTilesSource` reads individual tiles from a PMTiles archive file. + +| Loader | Characteristic | +| --------------------- | ----------------------------------------------- | +| File Extension | `.pmtiles` | +| File Type | Binary Archive | +| File Format | [PMTiles](/docs/modules/pmtiles/formats/pmtiles) | +| Data Format | Metadata | +| Decoder Type | Asynchronous | +| Worker Thread Support | No | +| Streaming Support | No | + +## Usage + +```typescript +import {PMTilesSource} from '@loaders.gl/pmtiles'; +import {load} from '@loaders.gl/core'; + +const source = new PMTilesSource({url}); +const tile = await source.getTile(...); +``` + +## Options + +| Option | Type | Default | Description | +| ------ | ---- | ------- | ----------- | diff --git a/docs/modules/pmtiles/formats/pmtiles.md b/docs/modules/pmtiles/formats/pmtiles.md new file mode 100644 index 0000000000..a71c97e939 --- /dev/null +++ b/docs/modules/pmtiles/formats/pmtiles.md @@ -0,0 +1,164 @@ +# PMTiles + +PMTiles is a single-file archive format for tiled data designed to enable individual tiles to be loaded via HTTP range request access. A PMTiles archive can be hosted on a commodity storage platform such as Amazon S3. + +- *[PMTiles](https://github.com/protomaps/PMTiles)* + +## Overview + +TBA + +## Versions + +## Version 3 + +- File Structure +97% smaller overhead - Spec version 2 would always issue a 512 kilobyte initial request; version 3 reduces this to 16 kilobytes. What remains the same is that nearly any map tile can be retrieved in at most two additional requests. +- Unlimited metadata - version 2 had a hard cap on the amount of JSON metadata of about 300 kilobytes; version 3 removes this limit. This is essential for tools like tippecanoe to store detailed column statistics. Essential archive information, such as tile type and compression methods, are stored in a binary header separate from application metadata. +- Hilbert tile IDs - tiles internally are addressed by a single 64-bit Hilbert tile ID instead of Z/X/Y. See the blog post on Tile IDs for details. +- Archive ordering - An optional clustered mode enforces that tile contents are laid out in Tile ID order. +- Compressed directories and metadata - Directories used to fetch offsets of tile data consume about 10% the space of those in version 2. See the blog post on compressed directories for details. +- JavaScript +Compression - The TypeScript pmtiles library now includes a decompressor - fflate - to allow reading compressed vector tile archives directly in the browser. This reduces the size and latency of vector tiles by as much as 70%. +- Tile Cancellation - All JavaScript plugins now support tile cancellation, meaning quick zooming across many levels will interrupt the loading of tiles that are never shown. This has a significant effect on the perceived user experience, as tiles at the end of a animation will appear earlier. +- ETag support - clients can detect when files change on static storage by reading the ETag HTTP header. This means that PMTiles-based map applications can update datasets in place at low frequency without running into caching problems. + +## Version 3 Specification + +### File structure + +A PMTiles archive is a single-file archive of square tiles with five main sections: + +1. A fixed-size, 127-byte **Header** starting with `PMTiles` and then the spec version - currently `3` - that contains offsets to the next sections. +2. A root **Directory**, described below. The Header and Root combined must be less than 16,384 bytes. +3. JSON metadata. +4. Optionally, a section of **Leaf Directories**, encoded the same way as the root. +5. The tile data. + +### Entries + +A Directory is a list of `Entries`, in ascending order by `TileId`: + + Entry = (TileId uint64, Offset uint64, Length uint32, RunLength uint32) + +* `TileId` starts at 0 and corresponds to a cumulative position on the series of square Hilbert curves starting at z=0. +* `Offset` is the position of the tile in the file relative to the start of the data section. +* `Length` is the size of the tile in bytes. +* `RunLength` is how many times this tile is repeated: the `TileId=5,RunLength=2` means that tile is present at IDs 5 and 6. +* If `RunLength=0`, the offset/length points to a Leaf Directory where `TileId` is the first entry. + +### Directory Serialization + +Entries are stored in memory as integers, but serialized to disk using these compression steps: + +1. A little-endian varint indicating the # of entries +2. Delta encoding of `TileId` +3. Zeroing of `Offset`: + * `0` if it is equal to the `Offset` + `Length` of the previous entry + * `Offset+1` otherwise +4. Varint encoding of all numbers +5. Columnar ordering: all `TileId`s, all `RunLength`s, all `Length`s, then all `Offset`s +6. Finally, general purpose compression as described by the `Header`'s `InternalCompression` field + +##3 Directory Hierarchy + +* The number of entries in the root directory and leaf directories is up to the implementation. +* However, the compressed size of the header plus root directory is required in v3 to be under **16,384 bytes**. This is to allow latency-optimized clients to prefetch the root directory and guarantee it is complete. A sophisticated writer might need several attempts to optimize this. +* Root size, leaf sizes and depth should be configurable by the user to optimize for different trade-offs: cost, bandwidth, latency. + +### Header Design + +*Certain fields belonging to metadata in v2 are promoted to fixed-size header fields. This allows a map container to be initialized to the desired extent or center without blocking on the JSON metadata, and allows proxies to return well-defined HTTP headers.* + +The `Header` is 127 bytes, with little-endian integer values: + +| offset | description | width | +| ------ | ----------------------------------------------------------------------------------------- | ----- | +| 0 | magic number `PMTiles` | 7 | +| 7 | spec version, currently `3` | 1 | +| 8 | offset of root directory | 8 | +| 16 | length of root directory | 8 | +| 24 | offset of JSON metadata, possibly compressed by `InternalCompression` | 8 | +| 32 | length of JSON metadata | 8 | +| 40 | offset of leaf directories | 8 | +| 48 | length of leaf directories | 8 | +| 56 | offset of tile data | 8 | +| 64 | length of tile data | 8 | +| 72 | # of addressed tiles, 0 if unknown | 8 | +| 80 | # of tile entries, 0 if unknown | 8 | +| 88 | # of tile contents, 0 if unknown | 8 | +| 96 | boolean clustered flag, `0x1` if true | 1 | +| 97 | `InternalCompression` enum (0 = Unknown, 1 = None, 2 = Gzip, 3 = Brotli, 4 = Zstd) | 1 | +| 98 | `TileCompression` enum | 1 | +| 99 | tile type enum (0 = Unknown/Other, 1 = MVT (PBF Vector Tile), 2 = PNG, 3 = JPEG, 4 = WEBP | 1 | +| 100 | min zoom | 1 | +| 101 | max zoom | 1 | +| 102 | min longitude (signed 32-bit integer: longitude * 10,000,000) | 4 | +| 106 | min latitude | 4 | +| 110 | max longitude | 4 | +| 114 | max latitude | 4 | +| 118 | center zoom | 1 | +| 119 | center longitude | 4 | +| 123 | center latitude | 4 | + +### Notes + +* **# of addressed tiles**: the total number of tiles before run-length encoding, i.e. `Sum(RunLength)` over all entries. +* **# of tile entries**: the total number of entries across all directories where `RunLength > 0`. +* **# # of tile contents**: the number of referenced blobs in the tile section, or the unique # of offsets. If the archive is completely deduplicated, this is equal to the # of unique tile contents. If there is no deduplication, this is equal to the number of tile entries above. +* **boolean clustered flag**: if true, blobs in the data section are ordered by Hilbert `TileId`. When writing with deduplication, this means that offsets are either contiguous with the previous offset+length, or refer to a lesser offset. +* **compression enum**: Mandatory, tells the client how to decompress contents as well as provide correct `Content-Encoding` headers to browsers. +* **tile type**: A hint as to the tile contents. Clients and proxies may use this to: + * Automatically determine a visualization method + * provide a conventional MIME type `Content-Type` HTTP header + * Enforce a canonical extension e.g. `.mvt`, `png`, `jpeg`, `.webp` to prevent duplication in caches + +### Organization + +In most cases, the archive should be in the order `Header`, Root Directory, JSON Metadata, Leaf Directories, Tile Data. It is possible to relocate sections other than `Header` arbitrarily, but no current writers/readers take advantage of this. A future design may allow for reverse-ordered archives to enable single-pass writing. + + +## Version 2 + +*Note: this is deprecated in favor of spec version 3.* + +PMTiles is a binary serialization format designed for two main access patterns: over the network, via HTTP 1.1 Byte Serving (`Range:` requests), or via memory-mapped files on disk. **All integer values are little-endian.** + +A PMTiles archive is composed of: +* a fixed-size 512,000 byte header section +* Followed by any number of tiles in arbitrary format +* Optionally followed by any number of *leaf directories* + +### Header +* The header begins with a 2-byte magic number, "PM" +* Followed by 2 bytes, the PMTiles specification version (currently 2). +* Followed by 4 bytes, the length of metadata (M bytes) +* Followed by 2 bytes, the number of entries in the *root directory* (N entries) +* Followed by M bytes of metadata, which **must be a JSON string with bounds, minzoom and maxzoom properties (new in v2)** +* Followed by N * 17 bytes, the root directory. + +### Directory structure + +A directory is a contiguous sequence of 17 byte entries. A directory can have at most 21,845 entries. **A directory must be sorted by Z, X and then Y order (new in v2).** + +An entry consists of: +* 1 byte: the zoom level (Z) of the entry, with the top bit set to 1 instead of 0 to indicate the offset/length points to a leaf directory and not a tile. +* 3 bytes: the X (column) of the entry. +* 3 bytes: the Y (row) of the entry. +* 6 bytes: the offset of where the tile begins in the archive. +* 4 bytes: the length of the tile, in bytes. + +**All leaf directory entries follow non-leaf entries. All leaf directories in a single directory must have the same Z value. (new in v2).** + +### Notes +* A full directory of 21,845 entries holds exactly a complete pyramid with 8 levels, or 1+4+16+64+256+1024+4096+16384. +* A PMTiles archive with less than 21,845 tiles should have a root directory and no leaf directories. +* Multiple tile entries can point to the same offset; this is useful for de-duplicating certain tiles, such as an empty "ocean" tile. +* Analogously, multiple leaf directory entries can point to the same offset; this can avoid inefficiently-packed small leaf directories. +* The tentative media type for PMTiles archives is `application/vnd.pmtiles`. + +### Implementation suggestions + +* PMTiles is designed to make implementing a writer simple. Reserve 512KB, then write all tiles, recording their entry information; then write all leaf directories; finally, rewind to 0 and write the header. +* The order of tile data in the archive is unspecified; an optimized implementation should arrange tiles on a 2D space-filling curve. +* PMTiles readers should cache directory entries by byte offset, not by Z/X/Y. This means that deduplicated leaf directories result in cache hits. \ No newline at end of file diff --git a/docs/modules/polyfills/api-reference/README.md b/docs/modules/polyfills/api-reference/README.md index 2e096116e0..0dfc331766 100644 --- a/docs/modules/polyfills/api-reference/README.md +++ b/docs/modules/polyfills/api-reference/README.md @@ -1,10 +1,8 @@ # Overview -The optional `@loaders.gl/polyfills` module installs support for Node.js and older browsers. +The `@loaders.gl/polyfills` module installs support for Node.js. This module should be imported before you call any loaders.gl functionality under Node.js -loaders.gl is based on the HTML5 API provided by modern, evergreen browsers. Older browsers (mainly Edge and IE11) as well as versions of Node.js prior to v12 do not provide certain classes that loaders.gl depends on. - -Note that while `@loaders.gl/polyfills` is designed to work seamlessly with other loaders.gl modules, using it is not a requirement. There are other good polyfill modules available on `npm` that can be used in its place. +loaders.gl is based on the HTML5 API provided by modern, evergreen browsers. ## Installation @@ -16,33 +14,28 @@ npm install @loaders.gl/polyfills Just import `@loaders.gl/polyfills` before you start using other loaders.gl modules. -```js +```typescript import '@loaders.gl/polyfills'; import '@loaders.gl/core'; ``` -To use the experimental `Blob` and `File` polyfills +## Features -```js -import {installFilePolyfills} from '@loaders.gl/polyfills'; -installFilePolyfills(); -``` +The polyfills module installs the following capabilities. -## Included Polyfills +- fetching files from Node file system +- Node Filesystem implementation +- Node ReadableFile and WritableFile implementations +- Node Crypto class +- Node Stream support +- Node library loading +- Image parsing and encoding under Node.js + | +## Deprecated polyfills -| Polyfill | Node | Browser | Comments | -| ------------------------------- | ------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `TextEncoder`/`TextDecoder` | Node.js < 11 | Yes (Older browsers) | Only UTF8 is guaranteed to be supported | -| `atob`/`btoa` | All versions | No | Note: these functions are [not unicode safe](https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem), but OK to use for test cases. | -| `fetch` | All versions | No | A subset of the fetch API is supported, see below. | -| `Response` | All versions | No | A subset of the `Response` API is supported, see below. | -| `Headers` | All versions | No | A subset of the fetch API is supported, see below. | -| `Blob` (Experimental) | All versions | No | A subset of the fetch API is supported, see below. | -| `File` (Experimental) | All versions | No | A subset of the fetch API is supported, see below. | -| `FileReader` (Experimental) | All versions | No | A subset of the fetch API is supported, see below. | -| `ReadableStream` (Experimental) | All versions | No | A subset of the ReadableStream API is supported. | +Before Node v18, `fetch` needed to be polyfilled. The `@loaders.gl/polyfills` module still conditionally installs a fetch polyfill on Node 16, but this is expected to be removed in next major release. -## fetch Polyfill +### fetch Polyfill The Node.js `fetch`, `Response` and `Headers` polyfills supports a large subset of the browser fetch API, including: @@ -55,18 +48,9 @@ The Node.js `fetch`, `Response` and `Headers` polyfills supports a large subset The Node.js `fetch` is able to follow 30X redirect: if `Response` has status 300-399 and `location` header is set, the `fetch` polyfill re-requests data from `location`. -# TextEncoder and TextDecoder Polyfills - -`TextEncoder` and `TextDecoder` polyfills are provided to ensure these APIs are always available. In modern browsers these will evaluate to the built-in objects of the same name, however under Node.js polyfills are transparently installed. - -Note: The provided polyfills only guarantee UTF8 support. - ## Remarks -- Applications should only install this module if they need to run under older environments. While the polyfills are only installed at runtime if the platform does not already support them, importing this module will increase the application's bundle size. -- Refer to browser documentation for the usage of these classes, e.g. MDN. -- In the browser, overhead of using these imports is not as high, as most polyfills are only bundled under Node.js. -- If working under older browsers, e.g. IE11, you may need to install your own TextEncoder/TextDecoder polyfills before loading this library +- The polyfills module can safely be imported in the browser. It is designed to be a no-op in this case, though if you are using new cutting-edge bundlers, they may not respect this configuration. ## Attribution diff --git a/docs/modules/schema/README.md b/docs/modules/schema/README.md index 9dc2df7e0d..baf6c9744e 100644 --- a/docs/modules/schema/README.md +++ b/docs/modules/schema/README.md @@ -11,11 +11,11 @@ The table API is modelled after a subset of the Apache Arrow API: -| Class | Arrow Counterpart | Description | -| ------------------------------------------------------------------ | ----------------- | ------------ | -| [`Table`](/docs/modules/schema/api-reference/table) | Table | Table | -| [`TableSchema`](/docs/modules/schema/api-reference/table-schema) | `Schema` | Table schema | -| [`TableBatch`](/docs/modules/schema/api-reference/table-batch) | `RecordBatch` | Table batch | +| Class | Arrow Counterpart | Description | +| --------------------------------------------------------- | ----------------- | ----------- | +| [`Table`](/docs/modules/schema/api-reference/table) | `Table` | Table | +| [`Schema`](/docs/modules/schema/api-reference/schema) | `Schema` | Schema | +| [`Batch`](/docs/modules/schema/api-reference/table-batch) | `RecordBatch` | Batch | ## Determining shape of loaded data @@ -44,12 +44,12 @@ processTile(tile.data); ### Table Category -| Shape | Category | Types / Description | -| --- | --- | --- | -| `table` | `Table` | -| `array-row-table` | `ArrayRowTable` | +| Shape | Category | Types / Description | +| ------------------ | ---------------- | ------------------- | +| `table` | `Table` | +| `array-row-table` | `ArrayRowTable` | | `object-row-table` | `ObjectRowTable` | -| `columnar-table` | `ColumnarTable` | +| `columnar-table` | `ColumnarTable` | - Tables can be - row-oriented, i.e. organized as an array of rows @@ -59,7 +59,7 @@ Rows can contain either - an array of values, where the column name is found in the schema. - object with key-value pairs, where the key is the column name -```json +```typescripton { "shape": , "data": @@ -69,10 +69,10 @@ Rows can contain either ## GIS Category -| Shape | Category | Types / Description | -| --- | --- | --- | -| `geojson` | `GeoJSON` | GeoJSON is a `features` array wrapped at the top level | -| `array-row-table` | `ArrayRowTable` | +| Shape | Category | Types / Description | +| ------------------ | ---------------- | ---------------------------------------------------------------- | +| `geojson` | `GeoJSON` | GeoJSON is a `features` array wrapped at the top level | +| `array-row-table` | `ArrayRowTable` | | `object-row-table` | `ObjectRowTable` | -| `geojson-table` | `GeojsonTable` | GeoJSON table essentially contains the `features` array from the +| `geojson-table` | `GeojsonTable` | GeoJSON table essentially contains the `features` array from the | diff --git a/docs/modules/schema/api-reference/apache-arrow.md b/docs/modules/schema/api-reference/apache-arrow.md new file mode 100644 index 0000000000..ff37b1100c --- /dev/null +++ b/docs/modules/schema/api-reference/apache-arrow.md @@ -0,0 +1,4 @@ +# Apache Arrow + +loaders.gl aims to provide strong support for and interoperability with Apache Arrow. + diff --git a/docs/modules/schema/api-reference/geometries.md b/docs/modules/schema/api-reference/geometries.md new file mode 100644 index 0000000000..ed7514f50b --- /dev/null +++ b/docs/modules/schema/api-reference/geometries.md @@ -0,0 +1,27 @@ +# Geometries + + +## GeoJSONTable + +The `GeoJSONTable` is one of the standard data return formats from loaders.gl loaders. +It is a GeoJSON FeatureCollection with two extra fields (`shape` and `schema`) + + +## Binary Geometries + +loaders.gl defines a Binary Geometry Format. + +The format is designed to work directly with the binary support in deck.gl layers. + +This format is currently described in more detail in the `@loaders.gl/gis` module documentation. + +## + +## Tesselation + +Some loaders can perform tesselation. + +This is typically done with earcut, which is a very fast polygon tesselator. The drawback with + +There can also be problems if polygons have very large numbers of holes. + diff --git a/docs/modules/schema/api-reference/schema.md b/docs/modules/schema/api-reference/schema.md new file mode 100644 index 0000000000..35e39c9d5f --- /dev/null +++ b/docs/modules/schema/api-reference/schema.md @@ -0,0 +1,19 @@ +# Schema + +loaders.gl provides a simple serializable schema class to help describe tables and table like data. +The Schema is modelled after Arrow. + + +## Schema Deduction + +Schemas can be deduced, but unless the data format is binary, this can lead to mistakes. + +For instance, should a column with zip codes in a CSV be treated as strings or numbers? (Most auto detection systems would classify the type as numbers, but most users would prefer for that column to be classified as string, to avoid potential dropping of leading zeroes among other things.) + +## Schema Serialization + +.. + +## Apache Arrow Schemas + +... diff --git a/docs/modules/schema/api-reference/table-schema.md b/docs/modules/schema/api-reference/table-schema.md deleted file mode 100644 index ab2b1b22ec..0000000000 --- a/docs/modules/schema/api-reference/table-schema.md +++ /dev/null @@ -1 +0,0 @@ -# TableSchema diff --git a/docs/modules/schema/api-reference/table.md b/docs/modules/schema/api-reference/table.md index f91505acf6..55b81ecc8f 100644 --- a/docs/modules/schema/api-reference/table.md +++ b/docs/modules/schema/api-reference/table.md @@ -1 +1,30 @@ # Table + +loaders.gl defines a number of table types. + +- `ObjectRowTable` +- `ArrayRowTable` +- `GeoJSONTable` +- `ColumnarTable` +- `ArrowTable` + +These all have a `shape` field on the top level. + +(If you are an advanced TypeScript programmer, you will appreciate that this lets typescript treat table types as a "discriminated union", meaning that once the type has been checked in an if or switch statement, the typing of the table is implied). + + +## Table Schemas + +Each table has an optional `schema` field. If it is present, it contains a list of fields (name, type and metadata for each field), as well as metadata for the table itself. + +There are also utilities for deducing schemas. + +## Table Utilities + +A set of utilities are provided to work with tables independently of which of the supported representations they are in. + +- `tableLength`` +- ... + + + diff --git a/docs/modules/shapefile/api-reference/dbf-loader.md b/docs/modules/shapefile/api-reference/dbf-loader.md index 0eb6a21180..1ad7075ae3 100644 --- a/docs/modules/shapefile/api-reference/dbf-loader.md +++ b/docs/modules/shapefile/api-reference/dbf-loader.md @@ -22,7 +22,7 @@ Note: Most applications will want to use the `ShapefileLoader` instead of this l The `DBFLoader` parses feature attributes from the Shapefile format. -```js +```typescript import {DBFLoader} from '@loaders.gl/shapefile'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/shapefile/api-reference/shapefile-loader.md b/docs/modules/shapefile/api-reference/shapefile-loader.md index 71fd86ff5d..ab8dceb65a 100644 --- a/docs/modules/shapefile/api-reference/shapefile-loader.md +++ b/docs/modules/shapefile/api-reference/shapefile-loader.md @@ -19,7 +19,7 @@ Shapefile loader ## Usage -```js +```typescript import {ShapefileLoader} from '@loaders.gl/shapefile'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/shapefile/api-reference/shp-loader.md b/docs/modules/shapefile/api-reference/shp-loader.md index 9ed16ea946..97b72faf9f 100644 --- a/docs/modules/shapefile/api-reference/shp-loader.md +++ b/docs/modules/shapefile/api-reference/shp-loader.md @@ -21,7 +21,7 @@ Note: Most applications will want to use the `ShapefileLoader` instead of this l ## Usage -```js +```typescript import {SHPLoader} from '@loaders.gl/shapefile'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/shapefile/formats/shapefile.md b/docs/modules/shapefile/formats/shapefile.md index f1b793d757..c50c288111 100644 --- a/docs/modules/shapefile/formats/shapefile.md +++ b/docs/modules/shapefile/formats/shapefile.md @@ -1,23 +1,24 @@ # Shapefile -- *[`@loaders.gl/shapefile`](/docs/modules/shapefile/formats/shapefile)* -- *https://www.clicketyclick.dk/databases/xbase/format/data_types.html* -- *http://www.dbase.com/Knowledgebase/INT/db7_file_fmt.htm* -- *http://webhelp.esri.com/arcgisdesktop/9.3/index.cfm?TopicName=Geoprocessing_considerations_for_shapefile_output* -- *https://www.loc.gov/preservation/digital/formats/fdd/fdd000326.shtml* -- *https://support.esri.com/en/technical-article/000013192* +ESRI Shapefiles is a file format for storing geospatial vector data. -ESRI Shapefiles are a popular file format for storing geospatial vector data. +- *[`@loaders.gl/shapefile`](/docs/modules/shapefile)* +- *[Wikipedia](https://en.wikipedia.org/wiki/Shapefile)* - *[ESRI Shapefile Whitepaper](https://www.esri.com/content/dam/esrisites/sitecore-archive/Files/Pdfs/library/whitepapers/pdfs/shapefile.pdf)* - *[Notes on Shapefile usage](http://webhelp.esri.com/arcgisdesktop/9.3/index.cfm?TopicName=Geoprocessing_considerations_for_shapefile_output)* +- *[DBF header](http://www.dbase.com/Knowledgebase/INT/db7_file_fmt.htm)* - *[data types](https://www.clicketyclick.dk/databases/xbase/format/data_types.html_)* - *[code pages](https://support.esri.com/en/technical-article/000013192)* - *[implementation notes](https://www.loc.gov/preservation/digital/formats/fdd/fdd000326.shtml)* -## Multi-file Summary +*Note that Shapefiles are falling out of favor in modern usage (likely due to the significant inconvenience of having to deal with multiple files). However, a lot of valuable geospatial data available is still provided in Shapefile format, and sometimes only in this format. +Additional information and some strong opinions can be found at [switchfromshapefile.org](http://switchfromshapefile.org/).* -The format consists of a number of files that must be stored together -(in the same directory, and with the same file name but different extensions). -Files with extensions `.shp`, `.shx`, `.dbf` must exist; -additional files with other extensions such as `.prj` and `.cpg` may exist. +## A multi-file format -A common problem with shapefiles is that the user only opens the shp file but not the dbf. +A Shapefile consists of a number of files that must be read and written together. +Because of this, they are typically stored together with the same file name but different extensions. +These related files are usually stored in the same directory or inside a common zip archive. +While it is possible to just load the geometries from a `.shp` file, files with extensions `.shp`, `.shx`, `.dbf` are often expected to exist, +and additional files with other extensions such as `.prj` and `.cpg` may also exist, if needed. + +A common problem with shapefiles is that the user only opens the `.shp` file but not the accompanying files such as `.dbf`. | File | Type | Contents | | ------ | ------ | -------------------------------------------------------------------------------------------------------------- | @@ -26,3 +27,48 @@ A common problem with shapefiles is that the user only opens the shp file but no | `.shx` | Binary | The index (technically required, however it is sometimes possible to open shapefiles without the index) | | `.prj` | Text | A small usually single line text file containing a WKT-CRS style projection. WGS84 is assumed if not present. | | `.cpg` | Text | A small text file containing a text encoding name for the DBF text fields. `latin1` is assumed if not present. | + +### Coordinate Systems + +Arbitrary coordinate reference systems are supported for Shapefiles. + +Such coordinate systems are reprojected to WGS84 on import. + +### Encodings + +The optional "code page" file (`.cpg`) specifies the encoding of any text data in the Shapefile (or more precisely, in the sidecar `.dbf` file). If no `.cpg` file is provided, `latin1` encoding is assumed. + +### Geometries + +A Shapefile always encodes a single type of geometries. The following geometries are supported: + +| Shape type | GeoJSON | loaders.gl | Value | Fields | +| ------------- | ------------ | ---------- | ----- | --------------------------------------------------------------------------------------------------------------- | +| `Null` shape | `null` | ✅ | 0 | None | +| `Point` | `Point` | ✅ | 1 | X, Y | +| `Polyline` | `LineString` | ✅ | 3 | MBR, Number of parts, Number of points, Parts, Points | +| `Polygon` | `Polygon` | ✅ | 5 | MBR, Number of parts, Number of points, Parts, Points | +| `MultiPoint` | `MultiPoint` | ✅ | 8 | MBR, Number of points, Points | +| `PointZ` | `Point` | ✅ | 11 | X, Y, Z Optional: M | +| `PolylineZ` | `LineString` | ✅ | 13 | MBR, Number of parts, Number of points, Parts, Points, Z range, Z array Optional: M range, M array | +| `PolygonZ` | `Polygon` | ✅ | 15 | MBR, Number of parts, Number of points, Parts, Points, Z range, Z array Optional: M range, M array | +| `MultiPointZ` | `MultiPoint` | ✅ | 18 | MBR, Number of points, Points, Z range, Z array Optional: M range, M array | +| `PointM` | `Point` | ✅ | 21 | X, Y, M | +| `PolylineM` | `LineString` | ✅ | 23 | MBR, Number of parts, Number of points, Parts, Points Optional: M range, M array | +| `PolygonM` | `Polygon` | ✅ | 25 | MBR, Number of parts, Number of points, Parts, Points Optional: M range, M array | +| `MultiPointM` | `MultiPoint` | ✅ | 28 | MBR, Number of points, Points Optional Fields: M range, M array | +| `MultiPatch` | | ❌ | 31 | MBR , Number of parts, Number of points, Parts, Part types, Points, Z range, Z array Optional: M range, M array | + +- `value` is the internal shapefile encoding + +### Version History + +- The shapefile format was introduced with ArcView GIS version 2 in the early 1990s. + +### Troubleshooting + +- No data columns: The most common problem with shapefile is probably that they user only opens the main `.shp` file. In this case only the geometry is included, but no data columns are present. +- Geometry projection issues: geometry may fail to load or be visualized incorrectly without the associated `.prj` file. +- Incorrect strings: Encodings may not be correct without the `.cpg` file. + +Also note that there is a very large number of possible projections and it is hard to test that every possible projection is supported. If your data is old or known to be problematic, it may be worth double checking that things look correct after importing. diff --git a/docs/modules/terrain/api-reference/quantized-mesh-loader.md b/docs/modules/terrain/api-reference/quantized-mesh-loader.md index 6220ff99f5..d1f9367942 100644 --- a/docs/modules/terrain/api-reference/quantized-mesh-loader.md +++ b/docs/modules/terrain/api-reference/quantized-mesh-loader.md @@ -22,7 +22,7 @@ mesh][quantized_mesh] format. ## Usage -```js +```typescript import {QuantizedMeshLoader} from '@loaders.gl/terrain'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/terrain/api-reference/terrain-loader.md b/docs/modules/terrain/api-reference/terrain-loader.md index dbf7eae558..6a683c2d24 100644 --- a/docs/modules/terrain/api-reference/terrain-loader.md +++ b/docs/modules/terrain/api-reference/terrain-loader.md @@ -15,7 +15,7 @@ The `TerrainLoader` reconstructs mesh surfaces from height map images, e.g. [Map ## Usage -```js +```typescript import {ImageLoader} from '@loaders.gl/images'; import {TerrainLoader} from '@loaders.gl/terrain'; import {load, registerLoaders} from '@loaders.gl/core'; diff --git a/docs/modules/textures/api-reference/basis-loader.md b/docs/modules/textures/api-reference/basis-loader.md index 70053cddeb..69b91a5f39 100644 --- a/docs/modules/textures/api-reference/basis-loader.md +++ b/docs/modules/textures/api-reference/basis-loader.md @@ -16,7 +16,7 @@ A loader for Basis Universal "supercompressed" GPU textures. Extracts supercompr ## Usage -```js +```typescript import {BasisLoader} from '@loaders.gl/textures'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/textures/api-reference/compressed-texture-loader.md b/docs/modules/textures/api-reference/compressed-texture-loader.md index 0d52effbf1..25613aa9ee 100644 --- a/docs/modules/textures/api-reference/compressed-texture-loader.md +++ b/docs/modules/textures/api-reference/compressed-texture-loader.md @@ -16,7 +16,7 @@ Loader for compressed textures in the PVR file format ## Usage -```js +```typescript import {CompressedTextureLoader} from '@loaders.gl/textures'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/textures/api-reference/compressed-texture-writer.md b/docs/modules/textures/api-reference/compressed-texture-writer.md index 5a03450839..56d26d7e44 100644 --- a/docs/modules/textures/api-reference/compressed-texture-writer.md +++ b/docs/modules/textures/api-reference/compressed-texture-writer.md @@ -1,4 +1,4 @@ -# CompressedTextureWriter +# CompressedTextureWriter 🚧

From-v3.0 @@ -19,7 +19,7 @@ ## Usage -```js +```typescript import '@loaders.gl/polyfill'; // only if using under Node import {encodeURLtoURL} from '@loaders.gl/core'; import {CompressedTextureWriter} from '@loaders.gl/textures'; diff --git a/docs/modules/textures/api-reference/crunch-loader.md b/docs/modules/textures/api-reference/crunch-loader.md index 905920b394..0ffe64c5d1 100644 --- a/docs/modules/textures/api-reference/crunch-loader.md +++ b/docs/modules/textures/api-reference/crunch-loader.md @@ -16,7 +16,7 @@ Loader for compressed textures in the Crunch file format ## Usage -```js +```typescript import {CrunchWorkerLoader} from '@loaders.gl/textures'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/textures/api-reference/ktx2-basis-texture-writer.md b/docs/modules/textures/api-reference/ktx2-basis-texture-writer.md index 231809d150..09f8413742 100644 --- a/docs/modules/textures/api-reference/ktx2-basis-texture-writer.md +++ b/docs/modules/textures/api-reference/ktx2-basis-texture-writer.md @@ -1,4 +1,4 @@ -# KTX2BasisWriter +# KTX2BasisWriter 🚧

From-v3.1 @@ -19,7 +19,7 @@ ## Usage -```js +```typescript import '@loaders.gl/polyfill'; // only if using under Node import {load, encode} from '@loaders.gl/core'; import {KTX2BasisUniversalTextureWriter} from '@loaders.gl/textures'; diff --git a/docs/modules/textures/api-reference/load-image-array.md b/docs/modules/textures/api-reference/load-image-array.md index 6f05e44f07..41c8c439b8 100644 --- a/docs/modules/textures/api-reference/load-image-array.md +++ b/docs/modules/textures/api-reference/load-image-array.md @@ -9,7 +9,7 @@ A function that loads an array of images. Primarily intended for loading: Loading an array of images -```js +```typescript import '@loaders.gl/polyfills'; // only needed for Node.js support import {loadImageArray} from `@loaders.gl/images`; @@ -20,7 +20,7 @@ for (const image of images) { } ``` -```js +```typescript import '@loaders.gl/polyfills'; // only needed for Node.js support import {loadImageArray} from `@loaders.gl/images`; diff --git a/docs/modules/textures/api-reference/load-image-cube.md b/docs/modules/textures/api-reference/load-image-cube.md index b99b4610d6..8f54b736f3 100644 --- a/docs/modules/textures/api-reference/load-image-cube.md +++ b/docs/modules/textures/api-reference/load-image-cube.md @@ -6,7 +6,7 @@ A function that loads 6 images representing the faces of a cube. Primarily inten Load images for a cubemap with one image per face -```js +```typescript import '@loaders.gl/polyfills'; // only needed for Node.js support import {loadImageCube} from `@loaders.gl/images`; @@ -19,7 +19,7 @@ for (const face in imageCube) { Load images for a cubemap with an array of mip images per face -```js +```typescript import '@loaders.gl/polyfills'; // only needed for Node.js support import {loadImageCube} from `@loaders.gl/images`; diff --git a/docs/modules/textures/api-reference/load-image.md b/docs/modules/textures/api-reference/load-image.md index 679cc1e13b..88d3975771 100644 --- a/docs/modules/textures/api-reference/load-image.md +++ b/docs/modules/textures/api-reference/load-image.md @@ -2,14 +2,14 @@ ## Usage -```js +```typescript import '@loaders.gl/polyfills'; // only needed if using under Node import {loadImage} from `@loaders.gl/images`; const image = await loadImage(url); ``` -```js +```typescript import '@loaders.gl/polyfills'; // only needed if using under Node import {loadImage} from `@loaders.gl/images`; diff --git a/docs/modules/textures/api-reference/npy-loader.md b/docs/modules/textures/api-reference/npy-loader.md index cc6365cc1c..481090dd8e 100644 --- a/docs/modules/textures/api-reference/npy-loader.md +++ b/docs/modules/textures/api-reference/npy-loader.md @@ -21,7 +21,7 @@ The `NPYLoader` parses an array from the [NPY format][npy-spec], a lightweight e ## Usage -```js +```typescript import {_NPYLoader} from '@loaders.gl/textures'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/textures/formats/compressed-textures.md b/docs/modules/textures/formats/compressed-textures.md index a1a245161f..a222639fc7 100644 --- a/docs/modules/textures/formats/compressed-textures.md +++ b/docs/modules/textures/formats/compressed-textures.md @@ -100,7 +100,7 @@ Data returned by any loaders.gl "image" category loader (including texture loade To use compressed textures in WebGL -```js +```typescript const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); @@ -143,7 +143,7 @@ Support for compressed textures is a work in progress in the [WebGPU standard](h At the time of writing, only S3 texture compression has been specified: -```js +```typescript // BC compressed formats usable if "texture-compression-bc" is both // supported by the device/user agent and enabled in requestDevice. "bc1-rgba-unorm", diff --git a/docs/modules/tile-converter/api-reference/3d-tiles-converter.md b/docs/modules/tile-converter/api-reference/3d-tiles-converter.md index f63287cd02..b8e5cd2d61 100644 --- a/docs/modules/tile-converter/api-reference/3d-tiles-converter.md +++ b/docs/modules/tile-converter/api-reference/3d-tiles-converter.md @@ -8,7 +8,7 @@ The `Tiles3DConverter` class converts an I3S layer. It converts between the OGC ## Usage -```js +```typescript import {Tiles3DConverter} from '@loaders.gl/tile-converter'; const TILESET_URL = diff --git a/docs/modules/tile-converter/api-reference/i3s-converter.md b/docs/modules/tile-converter/api-reference/i3s-converter.md index b4ff40a300..da8b68b954 100644 --- a/docs/modules/tile-converter/api-reference/i3s-converter.md +++ b/docs/modules/tile-converter/api-reference/i3s-converter.md @@ -8,7 +8,7 @@ The `I3SConverter` class converts a 3D Tiles tileset to I3S layer. ## Usage -```js +```typescript import {I3SConverter} from '@loaders.gl/tile-converter'; const converter = new I3SConverter(); @@ -45,7 +45,9 @@ Converts a tileset to I3S format - `options.generateTextures: boolean` Whether the converter should generate additional texture of another format. For non-compressed source texture format (JPG, PNG) the converter creates additional KTX2 texture. For compressed source texture (KTX2) the converter creates additional JPG texture. To encode and decode KTX2 [Basis Universal Supercompressed GPU Texture Codec](https://github.com/BinomialLLC/basis_universal) is used. - `options.generateBoundingVolumes: boolean` Whether the converter generate new bounding volumes from the mesh vertices. The default behavior is convertion bounding volumes (box, sphere or region) from 3DTiles tileset data. If this option is set `true` the converter will ignore source bounding volume and generate new bounding volume (oriented bounding box and minimal bounding sphere) from the geometry POSITION attribute. - `options.instantNodeWriting: boolean` Whether the converter should keep JSON resources ([3DNodeIndexDocuments](https://github.com/Esri/i3s-spec/blob/master/docs/1.8/3DNodeIndexDocument.cmn) and [nodePages](https://github.com/Esri/i3s-spec/blob/master/docs/1.8/nodePage.cmn)) on disk during conversion. The default behavior is the converter keeps JSON resources in memory till the end of conversion. Those resources need to be updated during conversion (adding child nodes and neighbor nodes). If this option is set `true` the converter will keep JSON resources on disk all the time. Use this option for large datasets when the nodes tree is large and "memory overflow" error occurs. Instant node writing saves memory usage in cost of conversion speed (>2 times slower). -- `options.validate` Enable Validation +- `options.metadataClass: string` One of the list of feature metadata classes, detected by converter on "analyze" stage +- `options.analyze: boolean` Analyze the input tileset content without conversion. +- `options.validate: boolean` Enable Validation. ### Validation diff --git a/docs/modules/tile-converter/cli-reference/i3s-server.md b/docs/modules/tile-converter/cli-reference/i3s-server.md new file mode 100644 index 0000000000..790c783501 --- /dev/null +++ b/docs/modules/tile-converter/cli-reference/i3s-server.md @@ -0,0 +1,81 @@ +# I3S Server + +

+ From-v4.0 + + npm version + +

+ +I3S Server is a NodeJS HTTP service built on top of [Express](https://expressjs.com). It can serve I3S data from output path of tile-converter or from SLPK file container. + +## Installation + +The i3s-server is published as a part of `@loaders.gl/tile-converter` library. + +Create a new folder: + +```bash +mkdir tmp +cd tmp +``` + +Install `@loaders.gl/tile-converter` package: + +```bash +npm i @loaders.gl/tile-converter +``` + +## Serve the output data of `tile-converter` + +Convert 3DTiles tileset to I3S without `--slpk` option: + +```bash +npx tile-converter --install-dependencies +npx tile-converter --input-type 3DTILES --tileset /path/to/tileset.json --name NewTileset +``` + +### Start HTTP server +```bash +PORT=8080 HTTPS_PORT=4443 I3sLayerPath="./data" DEBUG=i3s-server:* npx i3s-server +``` + +#### The layer should be available on URLs +- `http://localhost:8080/NewTileset/SceneServer/layers/0` +- `https://localhost:4443/NewTileset/SceneServer/layers/0` + +#### Open in ArcGIS + +`https://www.arcgis.com/home/webscene/viewer.html?url=http://localhost:8080/NewTileset/SceneServer` + +#### Open in I3S Explorer + +`https://i3s.loaders.gl/viewer?tileset=http://localhost:8080/NewTileset/SceneServer/layers/0` + +## Serve SLPK + +Example for path `../datasets/Rancho_Mesh_mesh_v17_1.slpk`: + +### Start the server + +```bash +PORT=8080 HTTPS_PORT=4443 I3sLayerPath="../datasets/Rancho_Mesh_mesh_v17_1.slpk" DEBUG=i3s-server:* npx i3s-server +``` +#### The layer should be available on URLs + +- `http://localhost:8080/SceneServer/layers/0/...` +- `https://localhost:4443/SceneServer/layers/0/...` + +#### Open in ArcGIS + +`https://www.arcgis.com/home/webscene/viewer.html?url=http://localhost:8080/SceneServer` + +#### Open in I3S Explorer + +`https://i3s.loaders.gl/viewer?tileset=http://localhost:8080/SceneServer/layers/0` + +## ENV variables + +- `I3sLayerPath` - path to converted data or SLPK file. +- `PORT` - HTTP port. Eg for `PORT = 8080 npx i3s-server` the server will work on host `http://localhost:8080/...`. Default value is `80`; +- `HTTPS_PORT` - HTTPS port. Eg for `PORT = 4443 npx i3s-server` the server will work on host `https://localhost:4443/...`. Default value is `443` \ No newline at end of file diff --git a/docs/modules/tile-converter/cli-reference/metadata-class-selection-promt.png b/docs/modules/tile-converter/cli-reference/metadata-class-selection-promt.png new file mode 100644 index 0000000000..4093e32156 Binary files /dev/null and b/docs/modules/tile-converter/cli-reference/metadata-class-selection-promt.png differ diff --git a/docs/modules/tile-converter/cli-reference/slpk-extractor.md b/docs/modules/tile-converter/cli-reference/slpk-extractor.md new file mode 100644 index 0000000000..374359a0e3 --- /dev/null +++ b/docs/modules/tile-converter/cli-reference/slpk-extractor.md @@ -0,0 +1,53 @@ +# SLPK extractor + +

+ From-v4.0 + + npm version + +

+ +SLPK extractor is utility that helps to extract slpk to a dataset that can be served via i3s-server + +## Installation + +The slpk-extractor is published as a part of `@loaders.gl/tile-converter` + +Create a new folder: + +```bash +mkdir tmp +cd tmp +``` + +Install `@loaders.gl/tile-converter` package: + +```bash +npm i @loaders.gl/tile-converter +``` + +## Extraction + +Extract .slpk to the `./data` folder: + +```bash +npx slpk-extractor --tileset="./path/to/the/file.slpk" --output="./data//SceneServer/layers/0" +``` +Then you can start serving dataset + +### Start HTTP server +```bash +PORT=8080 HTTPS_PORT=4443 I3sLayerPath="./data" DEBUG=i3s-server:* npx i3s-server +``` + +#### The layer should be available on URLs +- `http://localhost:8080/SceneServer/layers/0` +- `https://localhost:4443/SceneServer/layers/0` + +#### Open in ArcGIS + +`https://www.arcgis.com/home/webscene/viewer.html?url=http://localhost:8080//SceneServer` + +#### Open in I3S Explorer + +`https://i3s.loaders.gl/viewer?tileset=http://localhost:8080/SceneServer/layers/0` diff --git a/docs/modules/tile-converter/cli-reference/supported-features.md b/docs/modules/tile-converter/cli-reference/supported-features.md index 37e127f594..b58976f1c9 100644 --- a/docs/modules/tile-converter/cli-reference/supported-features.md +++ b/docs/modules/tile-converter/cli-reference/supported-features.md @@ -18,12 +18,12 @@ The tile-converter is capable to convert 3D tiles data of formats [3DTiles](http ## Input data source types -| Specification | Data source type | Status | -| ------------- | ------------------------ | ------------- | -| `I3S` | SLPK | Not supported | -| `I3S` | HTTP REST service | Supported | -| `3DTiles` | Local file system folder | Supported | -| `3DTiles` | Cesium ION URL | Supported | +| Specification | Data source type | Status | +| ------------- | ------------------------ | ------------------------------- | +| `I3S` | SLPK | Supported as local HTTP service | +| `I3S` | HTTP REST service | Supported | +| `3DTiles` | Local file system folder | Supported | +| `3DTiles` | Cesium ION URL | Supported | ## Versions @@ -46,9 +46,9 @@ Some 3DTiles vNext extensions are supported as input data. | `3DTiles` | `3DTILES_implicit_tiling` | Supported | | `3DTiles` | `3DTILES_bounding_volume_S2` | Supported | | `3DTIles` | `3DTILES_metadata` | Not applicable for `I3S` | -| `glTF` | `EXT_mesh_features` | In progress | -| `glTF` | `EXT_feature_metadata` | In progress | -| `glTF` | `EXT_structural_metadata` | In progress | +| `glTF` | `EXT_mesh_features` | Supported | +| `glTF` | `EXT_feature_metadata` | Supported | +| `glTF` | `EXT_structural_metadata` | Supported | ## Internal data types diff --git a/docs/modules/tile-converter/cli-reference/tile-converter.md b/docs/modules/tile-converter/cli-reference/tile-converter.md index 062fbefd68..c2262ecd60 100644 --- a/docs/modules/tile-converter/cli-reference/tile-converter.md +++ b/docs/modules/tile-converter/cli-reference/tile-converter.md @@ -1,4 +1,4 @@ -# Tile converter +# Tile Converter

From-v3.0 @@ -10,110 +10,159 @@

-The `tile-converter` is a command line utility (CLI) for two-way batch conversion between [I3S](https://www.ogc.org/standards/i3s) and [3D Tiles](https://www.ogc.org/standards/3DTiles), both an OGC community standard. It can load tilesets to be converted directly from an URL or file based formats. I3S and 3DTiles are large formats that include different layer types and data formats. See ["Supported features" page](/docs/modules/tile-converter/cli-reference/supported-features) that describes what the tile-converter supports. +The `tile-converter` is a command line utility (CLI) for two-way batch conversion between [I3S](https://www.ogc.org/standards/i3s) and [3D Tiles](https://www.ogc.org/standards/3DTiles), both an OGC community standard. It can load tilesets to be converted directly from an URL or file based formats. I3S and 3DTiles are large formats that include different layer types and data formats. See [Supported Features](/docs/modules/tile-converter/cli-reference/supported-features) page that describes what the tile-converter supports. ## Installation The tile-converter is published as an npm module and as a docker image. -Installing `@loaders.gl/tile-converter` from npm makes the `tile-converter` command line tool available. It can be run using `npx`. +Installing `@loaders.gl/tile-converter` from npm makes the `tile-converter` command line tool available. ```bash -$ npm i @loaders.gl/tile-converter +npm i @loaders.gl/tile-converter ``` +## Usage + +Tile Converter can be run using `npx`. + +Aknowledge available options: + ```bash -$ npx tile-converter --install-dependencies +npx tile-converter --help ``` +Install dependencies: + ```bash -$ npx tile-converter --input-type \ --tileset \ --name [--output ] [--draco] [--max-depth 4] [--slpk] [--7zExe ] [--token ] [--egm ] +npx tile-converter --install-dependencies ``` -Alternatively, you can use syntax with the equal sign: +Run conversion: ```bash -$ npx tile-converter --input-type=\ --tileset= --splk=true|false +npx tile-converter --input-type \ --tileset \ --name [--output ] [--no-draco] [--max-depth 4] [--slpk] [--7zExe ] [--token ] [--egm ] [--split-nodes] [--instant-node-writing] [--generate-textures] [--generate-bounding-volumes] ``` +Alternatively, you can use syntax with the equal sign: + ```bash -$ npx tile-converter --help +npx tile-converter --input-type=\ --tileset= --splk=true|false ... ``` -Alternatively, to download the `tile-converter` docker image, run: +Alternatively, there is a docker image to run: ```bash -$ docker pull visgl/tile-converter +docker pull visgl/tile-converter ``` +See more details [here](#docker-image) + ## Supported Platforms -Works only on NodeJS. +Operationg Systems: Windows 8.1 or higher, Ubuntu 20.04 or higher + +NodeJS 14 or higher is required. ## Options | Option | 3DTiles to I3S conversion | I3S to 3DTiles conversion | Description | | ------------------------- | ------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| install-dependencies | | | Run the script for installing dependencies. Run this options separate from others. Now "\*.pgm" file installation is implemented | +| help | \* | \* | Show the converter tool options list | +| install-dependencies | | | Run the script for installing dependencies. Run this options separate from others. It installs "\*.pgm" Earth Gravity Model, loader workers, sharp and join-images npm packages | | input-type | \* | \* | "I3S" - for I3S to 3DTiles conversion, "3DTILES" for 3DTiles to I3S conversion | | tileset | \* | \* | "tileset.json" file (3DTiles) / "http://..../SceneServer/layers/0" resource (I3S) | -| output | \* | \* | Output folder. This folder will be created by converter if doesn't exist. It is relative to the converter path. Default: "data" folder | -| name | \* | \* | Tileset name. This option is used for naming in resulting json resouces and for resulting path/\*.slpk file naming | -| max-depth | \* | \* | Maximal depth of the hierarchical tiles tree traversal, default: infinite | -| slpk | \* | | Whether the converter generates \*.slpk (Scene Layer Package) I3S output file | +| output | \* | \* | Output folder. This folder will be created by converter if doesn't exist. It is relative to the converter path. Default: "./data" folder | +| name | \* | \* | Tileset name. This option is required for naming in the output json resouces and for the output `path/\*.slpk` file naming | +| max-depth | \* | \* | Maximal depth of the hierarchical tiles tree traversal, default: infinity | +| slpk | \* | | Whether the converter generates \*.slpk (Scene Layer Package) I3S output files | | 7zExe | \* | | location of 7z.exe archiver to create slpk on Windows OS, default: "C:\\Program Files\\7-Zip\\7z.exe" | | egm | \* | \* | location of the Earth Gravity Model (\*.pgm) file to convert heights from ellipsoidal to gravity-related format, default: "./deps/egm2008-5.pgm". A model file can be loaded from GeographicLib https://geographiclib.sourceforge.io/html/geoid.html | | token | \* | | Token for Cesium ION tileset authentication. | | no-draco | \* | | Disable draco compression for geometry. Default: not set | | instant-node-writing | \* | | Whether the converter should keep JSON resources ([3DNodeIndexDocuments](https://github.com/Esri/i3s-spec/blob/master/docs/1.8/3DNodeIndexDocument.cmn.md) and [nodePages](https://github.com/Esri/i3s-spec/blob/master/docs/1.8/nodePage.cmn.md)) on disk during conversion. The default behavior is the converter keeps JSON resources in memory till the end of conversion. Those resources need to be updated during conversion (adding child nodes and neighbor nodes). If this option is set `true` the converter will keep JSON resources on disk all the time. Use this option for large datasets when the nodes tree is large and "memory overflow" error occurs. Instant node writing saves memory usage in cost of conversion speed (>2 times slower). | -| split-nodes | \* | | Prevent to merge similar materials that could lead to incorrect visualization. By default, the converter tries to merge PBR materials to create one node for all primitives in a glTF file. Using this option the converter will generate new node for every PBR material in the glTF file. With this option the resulting layer usually has refinement issues because "leaf" nodes are generated in the middle of the nodes tree] | -| generate-textures | \* | | Enable KTX2 textures generation if only one of (JPG, PNG) texture is provided or generate JPG texture if only KTX2 is provided | -| generate-bounding-volumes | \* | | Will generate obb and mbs bounding volumes from geometry | -| help | \* | \* | Show the converter tool options list | -| validate | \* | | Perform counting of all tiles and tiles with "ADD" type of refinement. Check whether a particular child node fits into the parent one or not. If not, warn about it. | +| split-nodes | \* | | The converter will generate new node for every PBR material in the glTF file. Prevent merge of similar materials that could lead to incorrect visualization. By default, the converter tries to merge PBR materials to create one node for all primitives in a glTF file. Note can cause refinement issues because "leaf" nodes are generated in the middle of the nodes tree tree | +| generate-textures | \* | | Create compressed KTX2 textures if non-compressed (JPG, PNG) texture is presented in the input tileset or generate JPG texture if compressed KTX2 is presented in the input tileset | +| generate-bounding-volumes | \* | | Create new OBB and MBS bounding volumes from geometry instead of conversion it from the source tile bounding volume | +| metadata-class | \* | | Set metadata class related to EXT_feature_metadata or EXT_structural_meatadata. See [Schema class](#schema-class-selection) extensions | +| analyze | \* | | Analyze the input tileset content without conversion | +| validate | \* | | Perform counting of all tiles. Check whether a particular child node fits into the parent one or not. If not, warn about it. | + +## Metadata class selection + +_This topic is applicable only for source type "3DTILES"._ + +An input glTF resource may contain [EXT_feature_metadata](https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata/README.md) or [EXT_structural_metadata](https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/README.md) extensions. + +Those extensions provide the structural metadata storage. Metadata - represented as entities and properties - may be closely associated with parts of 3D content, with data representations appropriate for large, distributed datasets. For the most detailed use cases, properties allow vertex- and texel-level associations; higher-level property associations are also supported. + +One glTF resource might include more than one metadata class. That means that parts of a mesh might be associated with different sets of properties. +For example, the glTF might have `bridges` and `buildings` classes. In that case, one part of the mesh is related to `bridges` properties (eg. `construction_year`, `type`) and another part of the mesh is related to `buildings` properties (eg. `construction_year`, `height`, `number_of_floors`, `ownership`). -## Running local server to handle i3s layer. +On another side there is an output I3S layer that doesn't support structural metadata and multiple classes. I3S has [feature attributes](https://github.com/Esri/i3s-spec/blob/master/docs/1.9/attributeStorageInfo.cmn.md) metadata that is the same for every node in the layer. So I3S can consume only one set of properties. -After conversion there are new i3s layers in output ("data" in example) directory. +In case when the input 3DTiles dataset has multiple metadata classes, the tile-converter provides a promt to select one class from the list: -Run it with the local web server from project directory: +![Metadata class selection](metadata-class-selection-promt.png) + +If the wanted class is known before the conversion it is possible to skip the prompt with setting `metadata-class` option. For example: + +```bash +npx tile-converter --input-type 3DTILES --tileset ..... --metadata-class bridges +``` + +## Running local server to handle I3S layer + +After conversion without `--slpk` option a new I3S layer is created in output ("data" by default) directory. + +Run it with the local web server: ```bash -$ I3sLayerPath="./data/CairoLayer" DEBUG=i3s-server:* npx i3s-server +PORT=8080 HTTPS_PORT=4443 I3sLayerPath="./data" DEBUG=i3s-server:* npx i3s-server +``` + +See more details [here](/docs/modules/tile-converter/cli-reference/i3s-server) + +### Show converted layer on a map + +``` +open https://loaders.gl/examples/i3s?url=http://localhost:8080/NewLayerName/SceneServer/layers/0 ``` ## Docker image -The tile converter is available as a docker image in the [visgl/tile-converter](https://hub.docker.com/r/visgl/tile-converter/tags) dockerhub repo. +The tile converter is available as a docker image in the [visgl/tile-converter](https://hub.docker.com/r/visgl/tile-converter/tags) dockerhub repo. The advantage of docker image is that it has dependencies pre-installed so it is not necessary to call `npx tile-converter --install-dependencies`. To download the tile-converter docker image, run: ```bash -$ docker pull visgl/tile-converter +docker pull visgl/tile-converter ``` To use converter run: ```bash -$ docker run +docker run \ + --rm \ -v /path/to/output_folder:/loaders-bundle/data \ - --rm \ + -v /path/to/input-tileset:/loaders-bundle/input-data \ visgl/tile-converter \ - --input-type ... \ + --input-type 3dtiles \ --token ... \ - --tileset ... \ + --tileset input-data/tileset.json \ --name ... \ - --output ... \ - --max-depth ... + --output data \ + --max-depth 3 \ + ... ``` Docker run arguments: --v - Create docker volume, linked to internal data folder +`-v` - Create docker volume, linked to internal data folder ---rm - Remove container after conversion +`--rm` - Remove container after conversion -visgl/tile-converter - Image name +`visgl/tile-converter` - Image name To build your own tile-converter docker image: @@ -121,14 +170,8 @@ To build your own tile-converter docker image: - In root folder of the project run: ```bash - $ yarn bootstrap - $ docker build -t [docker_image_name] -f modules/tile-converter/Dockerfile . + yarn bootstrap + docker build -t [docker_image_name] -f modules/tile-converter/Dockerfile . ``` - Push docker image to your docker hub - -## Show converted layer on a map. - -``` -$ open https://loaders.gl/examples/i3s?url=http://localhost/SceneServer/layers/0 -``` diff --git a/docs/modules/tiles/api-reference/tile-3d.md b/docs/modules/tiles/api-reference/tile-3d.md index 658e595840..bfaab63d8c 100644 --- a/docs/modules/tiles/api-reference/tile-3d.md +++ b/docs/modules/tiles/api-reference/tile-3d.md @@ -4,7 +4,7 @@ ## Constructor -```js +```typescript new Tile3D(tileset, header, parentHeader); ``` diff --git a/docs/modules/tiles/api-reference/tileset-3d.md b/docs/modules/tiles/api-reference/tileset-3d.md index 4512b1dbdf..d13aad7f67 100644 --- a/docs/modules/tiles/api-reference/tileset-3d.md +++ b/docs/modules/tiles/api-reference/tileset-3d.md @@ -13,7 +13,7 @@ References Loading a tileset and instantiating a `Tileset3D` instance. -```js +```typescript import {load} from '@loaders.gl/core'; import {Tileset3D} from '@loaders.gl/tiles'; import {Tiles3DLoader} from '@loaders.gl/3d-tiles'; @@ -27,7 +27,7 @@ const tileset3d = new Tileset3D(tilesetJson, { Loading a tileset and dynamically load/unload with viewport. -```js +```typescript import {load} from '@loaders.gl/core'; import {Tileset3D} from '@loaders.gl/tiles'; import {I3SLoader} from '@loaders.gl/i3s'; @@ -46,7 +46,7 @@ tileset3d.update(viewport); Since `Tileset3D's update` is a synchronized call, which selects the tiles qualified for rendering based on current viewport and available tiles, user can trigger another `update` when new tiles are loaded. -```js +```typescript import {Tileset3D} from '@loaders.gl/tiles'; const viewport = new WebMercatorViewport({latitude, longitude, zoom}); @@ -58,7 +58,7 @@ const tileset3d = new Tileset3D(tilesetJson, { ## Constructor -```js +```typescript new Tileset3D(tilesetJson, { onTileLoad: (tile) => console.log(tile) }); @@ -169,13 +169,13 @@ See the [properties schema reference](https://github.com/AnalyticalGraphicsInc/3 ### maximumScreenSpaceError : Number -The maximum screen space error used to drive level of detail refinement. This value helps determine when a tile refines to its descendants, and therefore plays a major role in balancing performance with visual quality. +Threshold that controls the level of detail of loaded tiles. A higher value means tile traversal stops early, displaying lower quality tiles (but much faster load times & less bandwidth used), because we're using a high "error tolerance". A lower value means lower tolerance for error, so traversal goes deeper in the tree and displays higher quality tiles. A tile's screen space error is roughly equivalent to the number of pixels wide that would be drawn if a sphere with a radius equal to the tile's geometric error were rendered at the tile's position. If this value exceeds `maximumScreenSpaceError` the tile refines to its descendants. -Depending on the tileset, `maximumScreenSpaceError` may need to be tweaked to achieve the right balance. Higher values provide better performance but lower visual quality. \* +Depending on the tileset, `maximumScreenSpaceError` may need to be tweaked to achieve the right balance between performance with visual quality. \* ### maximumMemoryUsage : Number @@ -209,7 +209,7 @@ The root tile header. The tileset's bounding sphere. -```js +```typescript var tileset = viewer.scene.primitives.add( new Tileset3D({ url: 'http://localhost:8002/tilesets/Seattle/tileset.json' diff --git a/docs/modules/video/api-reference/gif-builder.md b/docs/modules/video/api-reference/gif-builder.md index f8a957c5b2..d3f981e278 100644 --- a/docs/modules/video/api-reference/gif-builder.md +++ b/docs/modules/video/api-reference/gif-builder.md @@ -1,4 +1,4 @@ -# GIFBuilder +# GIFBuilder 🚧

From-v2.2 @@ -19,7 +19,7 @@ The `GIFBuilder` class creates a base64 encoded GIF image from either: Build a GIF from images -```js +```typescript import {load} from '@loaders.gl/core'; import {ImageLoader} from '@loaders.gl/images'; import {GIFBuilder} from '@loaders.gl/video'; @@ -39,7 +39,7 @@ gifBuilder.build(); Build a GIF from image URLs (Experimental) -```js +```typescript import {GIFBuilder} from '@loaders.gl/video'; const gifBuilder = new GIFBuilder({source: 'images', width: 400, height: 400}); @@ -51,7 +51,7 @@ gifBuilder.build(); Build a GIF from image URLs, with frame-specific Text (Experimental) -```js +```typescript import {GIFBuilder} from '@loaders.gl/video'; const gifBuilder = new GIFBuilder({source: 'images', width: 400, height: 400}); @@ -63,7 +63,7 @@ gifBuilder.build(); Build a GIF from the webcam (Experimental) -```js +```typescript import {GIFBuilder} from '@loaders.gl/video'; const gifBuilder = new GIFBuilder({source: webcam, width: 400, height: 400}); gifBuilder.build(); diff --git a/docs/modules/video/api-reference/video-loader.md b/docs/modules/video/api-reference/video-loader.md index d502ef6bf7..16e835de90 100644 --- a/docs/modules/video/api-reference/video-loader.md +++ b/docs/modules/video/api-reference/video-loader.md @@ -18,7 +18,7 @@ A basic Video element loader. Only works in the browser. ## Usage -```js +```typescript import '@loaders.gl/polyfills'; // only needed if using under Node import {VideoLoader} from '@loaders.gl/video'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/wkt/README.md b/docs/modules/wkt/README.md index 87ed4b3c4f..57cd38924b 100644 --- a/docs/modules/wkt/README.md +++ b/docs/modules/wkt/README.md @@ -2,36 +2,30 @@ ![ogc-logo](../../images/logos/ogc-logo-60.png) -The `@loaders.gl/wkt` module handles the [Well Known Text (WKT)](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) format, an ASCII format that defines geospatial geometries; and the [Well Known Binary](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) format, WKT's binary equivalent. +## Formats -## Installation +The `@loaders.gl/wkt` module handles the following formats: -```bash -npm install @loaders.gl/wkt -npm install @loaders.gl/core -``` +| Format | Description | +| ---------------------------------------------- | -------------------------------- | +| [`Well Known Text (WKT)`](/docs/modules/wkt/formats/wkt) | ASCII format for geometry features | +| [`Well Known Binary (WKB)`](/docs/modules/wkt/formats/wkb) | Binary format for geometry features | +| [`Well Known Text Coordinate Reference System (WKT-CRS)`](/docs/modules/wkt/formats/wkt-crs) | Text format for spatial reference systems | ## Loaders and Writers -| Loader | -| -------------------------------------------------------- | -| [`WKBLoader`](/docs/modules/wkt/api-reference/wkb-loader) | -| [`WKBWriter`](/docs/modules/wkt/api-reference/wkb-writer) | -| [`WKTLoader`](/docs/modules/wkt/api-reference/wkt-loader) | -| [`WKTWriter`](/docs/modules/wkt/api-reference/wkt-writer) | +| Loader | +| ---------------------------------------------------------------- | +| [`WKBLoader`](/docs/modules/wkt/api-reference/wkb-loader) | +| [`WKBWriter`](/docs/modules/wkt/api-reference/wkb-writer) | +| [`WKTLoader`](/docs/modules/wkt/api-reference/wkt-loader) | +| [`WKTWriter`](/docs/modules/wkt/api-reference/wkt-writer) | +| [`WKTCRSLoader`](/docs/modules/wkt/api-reference/wkt-crs-loader) | +| [`WKTCRSWriter`](/docs/modules/wkt/api-reference/wkt-crs-writer) | -## Format Notes - -A number of variants of WKT and WKB exist - -| Format | Support | Description | -| --- | --- | --- | -| WKT | Y | Text representation | -| WKB | Y | Binary representation | -| EWKT | N | WKT that starts with a spatial reference id (SRID) | -| TWKB | N | WKB variant that uses varints, precision truncation and zigzag point encoding to reduce uncompressed binary size ~2x (compressed size reduction is less). | ## Attribution The `WKTLoader` is based on a fork of the Mapbox [`wellknown`](https://github.com/mapbox/wellknown) module under the ISC license (MIT/BSD 2-clause equivalent). The `WKBLoader` and `WKBWriter` are forked from https://github.com/cschwarz/wkx under MIT license, Copyright (c) 2013 Christian Schwarz. +The `WKTCRSLoader` and `WKTCRSWriter` are based on a fork of https://github.com/DanielJDufour/wkt-crs under Creative Commons CC0 1.0 license. diff --git a/docs/modules/wkt/api-reference/hex-wkb-loader.md b/docs/modules/wkt/api-reference/hex-wkb-loader.md new file mode 100644 index 0000000000..e6a9a5638a --- /dev/null +++ b/docs/modules/wkt/api-reference/hex-wkb-loader.md @@ -0,0 +1,71 @@ +# HexWKBLoader 🆕 🚧 + + +![ogc-logo](../../../images/logos/ogc-logo-60.png) + +

+ From-v2.2 +

+ +Loader for hex encoded [Well-known binary][wkb] format for representation of geometry. + +[wkb]: https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary + +| Loader | Characteristic | +| --------------------- | --------------------------------------------- | +| File Extension | `.wkb`, | +| File Type | Binary | +| File Format | [Well Known Binary][wkb] | +| Data Format | [Geometry](/docs/specifications/category-gis) | +| Supported APIs | `load`, `parse`, `parseSync` | +| Decoder Type | Synchronous | +| Worker Thread Support | Yes | + +## Installation + +```bash +npm install @loaders.gl/wkt +npm install @loaders.gl/core +``` + +## Usage + +```typescript +import {HexWKBLoader} from '@loaders.gl/wkt'; +import {parseSync} from '@loaders.gl/core'; + +// prettier-ignore +const data = parseSync(data, HexWKBLoader); +// => { positions: { value: Float64Array(2) [ 1, 2 ], size: 2 } } +``` + +```typescript +import {HexWKBLoader} from '@loaders.gl/wkt'; +import {load} from '@loaders.gl/core'; + +const data = await load(url, HexWKBLoader); +``` + +## Options + +N/A + +## Format Summary + +Well-known binary (WKB) is a binary geometry encoding to store geometries (it +doesn't store attributes). It's used in databases such as PostGIS and as the +internal storage format of Shapefiles. It's also being discussed as the internal +storage format for a ["GeoArrow"](https://github.com/geopandas/geo-arrow-spec) +specification. WKB is defined starting on page 62 of the [OGC Simple Features +specification](http://portal.opengeospatial.org/files/?artifact_id=25355). + +It's essentially a binary representation of WKT. For common geospatial types +including (Multi) `Point`, `Line`, and `Polygon`, there's a 1:1 correspondence +between WKT/WKB and GeoJSON. WKT and WKB also support extended geometry types, +such as `Curve`, `Surface`, and `TIN`, which don't have a correspondence to +GeoJSON. + +- Coordinates can be 2-4 dimensions and are interleaved. +- Positions stored as double precision + +![image](https://user-images.githubusercontent.com/15164633/83707157-90413b80-a5d6-11ea-921c-b04208942e79.png) diff --git a/docs/modules/wkt/api-reference/twkb-loader.md b/docs/modules/wkt/api-reference/twkb-loader.md new file mode 100644 index 0000000000..87b83347c8 --- /dev/null +++ b/docs/modules/wkt/api-reference/twkb-loader.md @@ -0,0 +1,74 @@ +# TWKBLoader 🆕 🚧 + + +

+ From-v2.2 +

+ +Loader for the [Well-known binary][wkb] format for representation of geometry. + +[wkb]: https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary + +| Loader | Characteristic | +| --------------------- | --------------------------------------------- | +| File Extension | `.wkb`, | +| File Type | Binary | +| File Format | [Tiny Well Known Binary][twkb] | +| Data Format | [Geometry](/docs/specifications/category-gis) | +| Supported APIs | `load`, `parse`, `parseSync` | +| Decoder Type | Synchronous | +| Worker Thread Support | Yes | + +## Installation + +```bash +npm install @loaders.gl/wkt +npm install @loaders.gl/core +``` + +## Usage + +```typescript +import {TWKBLoader} from '@loaders.gl/wkt'; +import {parseSync} from '@loaders.gl/core'; + +// prettier-ignore +const buffer = new Uint8Array([ + 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 240, 63, 0, + 0, 0, 0, 0, 0, 0, 64 +]).buffer; +const data = parseSync(buffer, TWKBLoader); +// => { positions: { value: Float64Array(2) [ 1, 2 ], size: 2 } } +``` + +```typescript +import {TWKBLoader} from '@loaders.gl/wkt'; +import {load} from '@loaders.gl/core'; + +const data = await load(url, TWKBLoader); +``` + +## Options + +N/A + +## Format Summary + +Well-known binary (WKB) is a binary geometry encoding to store geometries (it +doesn't store attributes). It's used in databases such as PostGIS and as the +internal storage format of Shapefiles. It's also being discussed as the internal +storage format for a ["GeoArrow"](https://github.com/geopandas/geo-arrow-spec) +specification. WKB is defined starting on page 62 of the [OGC Simple Features +specification](http://portal.opengeospatial.org/files/?artifact_id=25355). + +It's essentially a binary representation of WKT. For common geospatial types +including (Multi) `Point`, `Line`, and `Polygon`, there's a 1:1 correspondence +between WKT/WKB and GeoJSON. WKT and WKB also support extended geometry types, +such as `Curve`, `Surface`, and `TIN`, which don't have a correspondence to +GeoJSON. + +- Coordinates can be 2-4 dimensions and are interleaved. +- Positions stored as double precision + +![image](https://user-images.githubusercontent.com/15164633/83707157-90413b80-a5d6-11ea-921c-b04208942e79.png) diff --git a/docs/modules/wkt/api-reference/twkb-writer.md b/docs/modules/wkt/api-reference/twkb-writer.md new file mode 100644 index 0000000000..757fe5ea7f --- /dev/null +++ b/docs/modules/wkt/api-reference/twkb-writer.md @@ -0,0 +1,59 @@ +# TWKBWriter 🆕 🚧 + + +

+ From-v3.1 +

+ +Writer for the [Tiny Well-known binary][twkb] format for representation of geometry. + +[twkb]: https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary + +| Loader | Characteristic | +| --------------------- | --------------------------------------------- | +| File Extension | `.wkb`, | +| File Type | Binary | +| File Format | [Tiny Well Known Binary][twkb] | +| Data Format | [Geometry](/docs/specifications/category-gis) | +| Supported APIs | `encode`, `encodeSync` | +| Encoder Type | Synchronous | +| Worker Thread Support | Yes | + +## Installation + +```bash +npm install @loaders.gl/wkt +npm install @loaders.gl/core +``` + +## Usage + +```typescript +import {TWKBWriter} from '@loaders.gl/wkt'; +import {encodeSync} from '@loaders.gl/core'; + +const geometry = { + type: "Polygon", + coordinates: [[[1, 2], [3, 4], [5, 6], [1, 2]]] +} +const arrayBuffer = encodeSync(geometry, TWKBWriter, {wkt: {hasZ: false, hasM: false}}) +``` + +## Options + +- `hasZ`: Should be `true` if the GeoJSON input has Z values. These values are expected to be the third coordinate position. +- `hasM`: Should be `true` if the GeoJSON input has M values. Thes are expected to be the third coordinate position if Z values do not exist, or fourth if Z values do exist. + +## Format Summary + +Well-known binary (TWKB) is a binary geometry encoding to store geometries (it +doesn't store attributes). It's used in databases such as PostGIS and as the +internal storage format of Shapefiles. It's also being discussed as the internal +storage format for a ["GeoArrow"](https://github.com/geopandas/geo-arrow-spec) +specification. TWKB is defined starting on page 62 of the [OGC Simple Features +specification](http://portal.opengeospatial.org/files/?artifact_id=25355). + +- Coordinates can be 2-4 dimensions and are interleaved. +- Positions stored as double precision + +![image](https://user-images.githubusercontent.com/15164633/83707157-90413b80-a5d6-11ea-921c-b04208942e79.png) diff --git a/docs/modules/wkt/api-reference/wkb-loader.md b/docs/modules/wkt/api-reference/wkb-loader.md index ea9333c06a..db36773817 100644 --- a/docs/modules/wkt/api-reference/wkb-loader.md +++ b/docs/modules/wkt/api-reference/wkb-loader.md @@ -1,5 +1,7 @@ # WKBLoader +![ogc-logo](../../../images/logos/ogc-logo-60.png) +

From-v2.2

@@ -18,9 +20,16 @@ Loader for the [Well-known binary][wkb] format for representation of geometry. | Decoder Type | Synchronous | | Worker Thread Support | Yes | +## Installation + +```bash +npm install @loaders.gl/wkt +npm install @loaders.gl/core +``` + ## Usage -```js +```typescript import {WKBLoader} from '@loaders.gl/wkt'; import {parseSync} from '@loaders.gl/core'; @@ -34,7 +43,7 @@ const data = parseSync(buffer, WKBLoader); // => { positions: { value: Float64Array(2) [ 1, 2 ], size: 2 } } ``` -```js +```typescript import {WKBLoader} from '@loaders.gl/wkt'; import {load} from '@loaders.gl/core'; diff --git a/docs/modules/wkt/api-reference/wkb-writer.md b/docs/modules/wkt/api-reference/wkb-writer.md index 1db5d9237f..1085b43514 100644 --- a/docs/modules/wkt/api-reference/wkb-writer.md +++ b/docs/modules/wkt/api-reference/wkb-writer.md @@ -1,5 +1,7 @@ # WKBWriter +![ogc-logo](../../../images/logos/ogc-logo-60.png) +

From-v3.1

@@ -18,9 +20,16 @@ Writer for the [Well-known binary][wkb] format for representation of geometry. | Encoder Type | Synchronous | | Worker Thread Support | Yes | +## Installation + +```bash +npm install @loaders.gl/wkt +npm install @loaders.gl/core +``` + ## Usage -```js +```typescript import {WKBWriter} from '@loaders.gl/wkt'; import {encodeSync} from '@loaders.gl/core'; diff --git a/docs/modules/wkt/api-reference/wkt-crs-loader.md b/docs/modules/wkt/api-reference/wkt-crs-loader.md new file mode 100644 index 0000000000..656c83e889 --- /dev/null +++ b/docs/modules/wkt/api-reference/wkt-crs-loader.md @@ -0,0 +1,151 @@ +# WKTCRSLoader 🆕 🚧 + + +![ogc-logo](../../../images/logos/ogc-logo-60.png) + +

+ From-v4.0 +

+ +Parses WKT-CRS ([Well-known text representation of coordinate reference systems](../formats/wkt-crs)). + +## Installation + +```bash +npm install @loaders.gl/wkt +npm install @loaders.gl/core +``` + +# Usage + +```typescript +// you can skip this line if you loaded via + diff --git a/examples/website/tiles/package.json b/examples/website/tiles/package.json new file mode 100644 index 0000000000..36fbd87b92 --- /dev/null +++ b/examples/website/tiles/package.json @@ -0,0 +1,35 @@ +{ + "private": true, + "name": "geospatial-loaders-example", + "description": "Minimal example of using loaders.gl with vite.", + "version": "0.0.0", + "license": "MIT", + "scripts": { + "start": "vite", + "build": "tsc && vite build", + "serve": "vite preview" + }, + "dependencies": { + "@deck.gl/core": "^8.9.28", + "@deck.gl/layers": "^8.9.28", + "@deck.gl/geo-layers": "^8.9.28", + "@deck.gl/mesh-layers": "^8.9.28", + "@deck.gl/extensions": "8.9.28", + "@deck.gl/react": "^8.9.28", + "@loaders.gl/core": "^4.0.0", + "@loaders.gl/loader-utils": "^4.0.0", + "@loaders.gl/mvt": "^4.0.0", + "@loaders.gl/pmtiles": "^4.0.0", + "@monaco-editor/react": "^4.5.0", + "mapbox-gl": "npm:empty-npm-package@^1.0.0", + "maplibre-gl": "^2.4.0", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-map-gl": "^7.0.0", + "styled-components": "^4.2.0" + }, + "devDependencies": { + "typescript": "^5.0.4", + "vite": "^4.4.9" + } +} diff --git a/examples/website/tiles/tsconfig.json b/examples/website/tiles/tsconfig.json new file mode 100644 index 0000000000..a7ca1cd93a --- /dev/null +++ b/examples/website/tiles/tsconfig.json @@ -0,0 +1,21 @@ +{ + "include": [".", "../../../modules/*/src"], + "compilerOptions": { + "root": ["."], + // "target": "ESNext", + // "useDefineForClassFields": true, + // "lib": ["DOM", "DOM.Iterable", "ESNext"], + // "allowJs": false, + // "skipLibCheck": false, + // "esModuleInterop": false, + // "allowSyntheticDefaultImports": true, + // "strict": true, + // "forceConsistentCasingInFileNames": true, + // "module": "ESNext", + // "moduleResolution": "Node", + // "resolveJsonModule": true, + // "isolatedModules": true, + // "noEmit": true, + "jsx": "react-jsx" + } +} diff --git a/examples/website/tiles/vite.config.ts b/examples/website/tiles/vite.config.ts new file mode 100644 index 0000000000..2eb3c50302 --- /dev/null +++ b/examples/website/tiles/vite.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from 'vite'; +import fs from 'fs'; + +/** Run against local source */ +const getAliases = async (frameworkName, frameworkRootDir) => { + const modules = await fs.promises.readdir(`${frameworkRootDir}/modules`) + const aliases = {} + modules.forEach(module => { + aliases[`${frameworkName}/${module}`] = `${frameworkRootDir}/modules/${module}/src`; + }) + console.log(aliases); + return aliases +} + +// https://vitejs.dev/config/ +export default defineConfig(async () => ({ + resolve: {alias: await getAliases('@loaders.gl', `${__dirname}/../../..`)}, + server: {open: true} +})) + diff --git a/examples/website/wms/app.tsx b/examples/website/wms/app.tsx index 2b9da5c561..b897f5d1f8 100644 --- a/examples/website/wms/app.tsx +++ b/examples/website/wms/app.tsx @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import React, {PureComponent} from 'react'; import {createRoot} from 'react-dom/client'; @@ -67,6 +68,7 @@ export default class App extends PureComponent { loading: true; metadata: string | ''; error: string | ''; + featureInfo: any; } = { // CURRENT VIEW POINT / CAMERA POSITIO viewState: INITIAL_VIEW_STATE, @@ -174,7 +176,7 @@ export default class App extends PureComponent { onError={(error: Error) => this.setState({error: error.message})} controller={{type: MapController, maxPitch: 85}} getTooltip={({object}) => - this.state.featureInfo && { + this.state?.featureInfo && { html: `

Feature Info

${this.state.featureInfo}
`, style: { color: '#EEE', diff --git a/examples/website/wms/examples.ts b/examples/website/wms/examples.ts index 3301a9e68e..992f1fd767 100644 --- a/examples/website/wms/examples.ts +++ b/examples/website/wms/examples.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors export const LOADERS_URI = 'https://raw.githubusercontent.com/visgl/loaders.gl/master'; diff --git a/examples/website/wms/package.json b/examples/website/wms/package.json index 2f3199dd86..61c807f7af 100644 --- a/examples/website/wms/package.json +++ b/examples/website/wms/package.json @@ -1,19 +1,19 @@ { + "private": true, "name": "wms-loaders-example", "description": "WMS loaders.", "version": "0.0.0", "license": "MIT", - "private": true, "scripts": { "start": "vite", "build": "tsc && vite build", "serve": "vite preview" }, "dependencies": { - "@deck.gl/core": "^8.9.7", - "@deck.gl/geo-layers": "^8.9.7", - "@loaders.gl/core": "^4.0.0-alpha.8", - "@loaders.gl/wms": "^4.0.0-alpha.8", + "@deck.gl/core": "^8.9.28", + "@deck.gl/geo-layers": "^8.9.28", + "@loaders.gl/core": "^4.0.0", + "@loaders.gl/wms": "^4.0.0", "@monaco-editor/react": "^4.5.0", "deck.gl": "^8.9.0", "mapbox-gl": "npm:empty-npm-package@1.0.0", diff --git a/index.html b/index.html new file mode 100644 index 0000000000..a588e946f0 --- /dev/null +++ b/index.html @@ -0,0 +1,10 @@ + + + + + loaders.gl unit tests + + + + + diff --git a/lerna.json b/lerna.json index f54f8040db..ccc5ecb6ec 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "2.9.1", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "command": { "publish": {}, "bootstrap": {} diff --git a/modules/3d-tiles/package.json b/modules/3d-tiles/package.json index 0fa2cbfc3f..bf15231589 100644 --- a/modules/3d-tiles/package.json +++ b/modules/3d-tiles/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/3d-tiles", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "3D Tiles, an open standard for streaming massive heterogeneous 3D geospatial datasets.", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -22,8 +23,15 @@ "pointcloud" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -31,22 +39,23 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { - "@loaders.gl/draco": "4.0.0-alpha.13", - "@loaders.gl/gltf": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/math": "4.0.0-alpha.13", - "@loaders.gl/tiles": "4.0.0-alpha.13", - "@math.gl/core": "^3.5.1", - "@math.gl/geospatial": "^3.5.1", + "@loaders.gl/draco": "4.0.3", + "@loaders.gl/gltf": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/math": "4.0.3", + "@loaders.gl/tiles": "4.0.3", + "@loaders.gl/zip": "4.0.3", + "@math.gl/core": "^4.0.0", + "@math.gl/geospatial": "^4.0.0", "@probe.gl/log": "^4.0.4", "long": "^5.2.1" }, "peerDependencies": { - "@loaders.gl/core": "^4.0.0-alpha.8" + "@loaders.gl/core": "^4.0.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/3d-tiles/src/3d-tiles-archive-loader.ts b/modules/3d-tiles/src/3d-tiles-archive-loader.ts new file mode 100644 index 0000000000..201107d29b --- /dev/null +++ b/modules/3d-tiles/src/3d-tiles-archive-loader.ts @@ -0,0 +1,47 @@ +import type {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; +import {DataViewFile} from '@loaders.gl/loader-utils'; +import {parse3DTilesArchive as parse3DTilesArchiveFromProvider} from './3d-tiles-archive/3d-tiles-archive-parser'; + +// __VERSION__ is injected by babel-plugin-version-inline +// @ts-ignore TS2304: Cannot find name '__VERSION__'. +const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; + +/** options to load data from 3tz */ +export type Tiles3DArchiveFileLoaderOptions = LoaderOptions & { + '3d-tiles-archive'?: { + /** path inside the 3tz archive */ + path?: string; + }; +}; + +/** + * Loader for 3tz packages + */ +export const Tiles3DArchiveFileLoader: LoaderWithParser< + ArrayBuffer, + never, + Tiles3DArchiveFileLoaderOptions +> = { + name: '3tz', + id: '3tz', + module: '3d-tiles', + version: VERSION, + mimeTypes: ['application/octet-stream', 'application/vnd.maxar.archive.3tz+zip'], + parse: parse3DTilesArchive, + extensions: ['3tz'], + options: {} +}; + +/** + * returns a single file from the 3tz archive + * @param data 3tz archive data + * @param options options + * @returns requested file + */ +async function parse3DTilesArchive( + data: ArrayBuffer, + options: Tiles3DArchiveFileLoaderOptions = {} +): Promise { + const archive = await parse3DTilesArchiveFromProvider(new DataViewFile(new DataView(data))); + return archive.getFile(options['3d-tiles-archive']?.path ?? ''); +} diff --git a/modules/3d-tiles/src/3d-tiles-archive/3d-tiles-archive-archive.ts b/modules/3d-tiles/src/3d-tiles-archive/3d-tiles-archive-archive.ts new file mode 100644 index 0000000000..681b9b7bba --- /dev/null +++ b/modules/3d-tiles/src/3d-tiles-archive/3d-tiles-archive-archive.ts @@ -0,0 +1,86 @@ +import {FileProvider} from '@loaders.gl/loader-utils'; +import {MD5Hash} from '@loaders.gl/crypto'; +import {DeflateCompression, NoCompression} from '@loaders.gl/compression'; +import {parseZipLocalFileHeader} from '@loaders.gl/zip'; + +type CompressionHandler = (compressedFile: ArrayBuffer) => Promise; + +/** + * Handling different compression types in zip + */ +const COMPRESSION_METHODS: {[key: number]: CompressionHandler} = { + /** No compression */ + 0: (data) => new NoCompression().decompress(data), + /** Deflation */ + 8: (data) => new DeflateCompression({raw: true}).decompress(data) +}; + +/** + * Class for handling information about 3tz file + */ +export class Tiles3DArchive { + /** FileProvider with whe whole file */ + private fileProvider: FileProvider; + /** hash info */ + private hashTable: Record; + + /** + * creates Tiles3DArchive handler + * @param fileProvider - FileProvider with the whole file + * @param hashTable - hash info + */ + constructor(fileProvider: FileProvider, hashTable: Record) { + this.fileProvider = fileProvider; + this.hashTable = hashTable; + } + + /** + * Returns file with the given path from 3tz archive + * @param path - path inside the 3tz + * @returns buffer with ready to use file + */ + async getFile(path: string): Promise { + // sometimes paths are not in lower case when hash file is created, + // so first we're looking for lower case file name and then for original one + let data = await this.getFileBytes(path.toLocaleLowerCase()); + if (!data) { + data = await this.getFileBytes(path); + } + if (!data) { + throw new Error(`No such file in the archive: ${path}`); + } + + return data; + } + + /** + * Trying to get raw file data by adress + * @param path - path inside the archive + * @returns buffer with the raw file data + */ + private async getFileBytes(path: string): Promise { + const arrayBuffer = new TextEncoder().encode(path).buffer; + const nameHash = await new MD5Hash().hash(arrayBuffer, 'hex'); + const byteOffset = this.hashTable[nameHash]; + if (byteOffset === undefined) { + return null; + } + + const localFileHeader = await parseZipLocalFileHeader(byteOffset, this.fileProvider); + if (!localFileHeader) { + return null; + } + + const compressedFile = await this.fileProvider.slice( + localFileHeader.fileDataOffset, + localFileHeader.fileDataOffset + localFileHeader.compressedSize + ); + + const compressionMethod = COMPRESSION_METHODS[localFileHeader.compressionMethod]; + if (!compressionMethod) { + throw Error('Only Deflation compression is supported'); + } + + return compressionMethod(compressedFile); + } +} diff --git a/modules/3d-tiles/src/3d-tiles-archive/3d-tiles-archive-parser.ts b/modules/3d-tiles/src/3d-tiles-archive/3d-tiles-archive-parser.ts new file mode 100644 index 0000000000..49906d8497 --- /dev/null +++ b/modules/3d-tiles/src/3d-tiles-archive/3d-tiles-archive-parser.ts @@ -0,0 +1,52 @@ +import {FileProvider} from '@loaders.gl/loader-utils'; +import { + cdSignature as cdHeaderSignature, + makeHashTableFromZipHeaders, + parseHashTable, + parseZipCDFileHeader, + parseZipLocalFileHeader, + searchFromTheEnd +} from '@loaders.gl/zip'; +import {Tiles3DArchive} from './3d-tiles-archive-archive'; + +/** + * Creates 3tz file handler from raw file + * @param fileProvider raw file data + * @param cb is called with information message during parsing + * @returns 3tz file handler + */ +export const parse3DTilesArchive = async ( + fileProvider: FileProvider, + cb?: (msg: string) => void +): Promise => { + const hashCDOffset = await searchFromTheEnd(fileProvider, cdHeaderSignature); + + const cdFileHeader = await parseZipCDFileHeader(hashCDOffset, fileProvider); + + let hashTable: Record; + if (cdFileHeader?.fileName !== '@3dtilesIndex1@') { + hashTable = await makeHashTableFromZipHeaders(fileProvider); + cb?.( + '3tz doesnt contain hash file, hash info has been composed according to zip archive headers' + ); + } else { + // cb?.('3tz contains hash file'); + const localFileHeader = await parseZipLocalFileHeader( + cdFileHeader.localHeaderOffset, + fileProvider + ); + if (!localFileHeader) { + throw new Error('corrupted 3tz zip archive'); + } + + const fileDataOffset = localFileHeader.fileDataOffset; + const hashFile = await fileProvider.slice( + fileDataOffset, + fileDataOffset + localFileHeader.compressedSize + ); + + hashTable = parseHashTable(hashFile); + } + + return new Tiles3DArchive(fileProvider, hashTable); +}; diff --git a/modules/3d-tiles/src/bundle.ts b/modules/3d-tiles/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/3d-tiles/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/3d-tiles/src/cesium-ion-loader.ts b/modules/3d-tiles/src/cesium-ion-loader.ts index 3cb231d607..005542b18e 100644 --- a/modules/3d-tiles/src/cesium-ion-loader.ts +++ b/modules/3d-tiles/src/cesium-ion-loader.ts @@ -1,4 +1,4 @@ -import type {LoaderWithParser} from '@loaders.gl/loader-utils'; +import type {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; import {Tiles3DLoader} from './tiles-3d-loader'; import {getIonTilesetMetadata} from './lib/ion/ion'; @@ -18,7 +18,7 @@ async function preload(url, options = {}) { /** * Loader for 3D tiles from Cesium ION */ -export const CesiumIonLoader: LoaderWithParser = { +export const CesiumIonLoader: LoaderWithParser = { ...Tiles3DLoader, id: 'cesium-ion', name: 'Cesium Ion', @@ -33,7 +33,6 @@ export const CesiumIonLoader: LoaderWithParser = { }, options: { 'cesium-ion': { - // @ts-expect-error ...Tiles3DLoader.options['3d-tiles'], accessToken: null } diff --git a/modules/3d-tiles/src/index.ts b/modules/3d-tiles/src/index.ts index 3131278b59..5224b838d2 100644 --- a/modules/3d-tiles/src/index.ts +++ b/modules/3d-tiles/src/index.ts @@ -2,6 +2,9 @@ export {Tiles3DLoader} from './tiles-3d-loader'; export {CesiumIonLoader} from './cesium-ion-loader'; export {Tile3DSubtreeLoader} from './tile-3d-subtree-loader'; +export type {Tiles3DArchiveFileLoaderOptions} from './3d-tiles-archive-loader'; +export {Tiles3DArchiveFileLoader} from './3d-tiles-archive-loader'; +export {Tiles3DArchiveFileSystem} from './lib/filesystems/tiles-3d-archive-file-system'; // WRITERS export {Tile3DWriter} from './tile-3d-writer'; diff --git a/modules/3d-tiles/src/lib/filesystems/tiles-3d-archive-file-system.ts b/modules/3d-tiles/src/lib/filesystems/tiles-3d-archive-file-system.ts new file mode 100644 index 0000000000..4756f289f2 --- /dev/null +++ b/modules/3d-tiles/src/lib/filesystems/tiles-3d-archive-file-system.ts @@ -0,0 +1,96 @@ +import {FileProvider} from '@loaders.gl/loader-utils'; +import { + ZipFileSystem, + cdSignature as cdHeaderSignature, + searchFromTheEnd, + parseZipCDFileHeader, + parseHashTable, + parseZipLocalFileHeader +} from '@loaders.gl/zip'; +import {Tiles3DArchive} from '../../3d-tiles-archive/3d-tiles-archive-archive'; + +/** + * FileSystem adapter for a 3tz (3D tiles archive format) file + * Holds FileProvider object that provides random access to archived files. + * The difference from ZipFileSystem is usage of `@3dtilesIndex1@` index file that increases + * access speed to archived files + * @see https://github.com/erikdahlstrom/3tz-specification/blob/master/Specification.md + */ +export class Tiles3DArchiveFileSystem extends ZipFileSystem { + hashTable?: Record | null; + + /** + * Constructor + * @param file - instance of FileProvider or file path string + */ + constructor(file: FileProvider | string) { + super(file); + } + + /** + * Implementation of fetch against this file system. + * It tries to take `@3dtilesIndex1@` file from the archive and use it + * for faster access to archived files + * @param filename - name of a file + * @returns - Response with file data + */ + async fetch(filename: string): Promise { + const fileProvider = this.fileProvider; + if (!fileProvider) { + throw new Error('No data detected in the zip archive'); + } + await this.parseHashTable(); + if (this.hashTable) { + const archive = new Tiles3DArchive(fileProvider, this.hashTable); + + const fileData = await archive.getFile(filename); + const response = new Response(fileData); + Object.defineProperty(response, 'url', {value: `${this.fileName || ''}/${filename}`}); + return response; + } + return super.fetch(filename); + } + + /** + * Try to get and parse '@3dtilesIndex1@' file, that allows to get direct access + * to files inside the archive + * @returns void + */ + private async parseHashTable(): Promise { + if (this.hashTable !== undefined) { + return; + } + + const fileProvider = this.fileProvider; + if (!fileProvider) { + throw new Error('No data detected in the zip archive'); + } + + const hashCDOffset = await searchFromTheEnd(fileProvider, cdHeaderSignature); + + const cdFileHeader = await parseZipCDFileHeader(hashCDOffset, fileProvider); + + // '@3dtilesIndex1@' is index file that must be the last in the archive. It allows + // to improve load and read performance when the archive contains a very large number + // of files. + if (cdFileHeader?.fileName === '@3dtilesIndex1@') { + const localFileHeader = await parseZipLocalFileHeader( + cdFileHeader.localHeaderOffset, + fileProvider + ); + if (!localFileHeader) { + throw new Error('corrupted 3tz'); + } + + const fileDataOffset = localFileHeader.fileDataOffset; + const hashFile = await fileProvider.slice( + fileDataOffset, + fileDataOffset + localFileHeader.compressedSize + ); + + this.hashTable = parseHashTable(hashFile); + } else { + this.hashTable = null; + } + } +} diff --git a/modules/3d-tiles/src/lib/ion/ion.ts b/modules/3d-tiles/src/lib/ion/ion.ts index cd5575983f..5c71bfa354 100644 --- a/modules/3d-tiles/src/lib/ion/ion.ts +++ b/modules/3d-tiles/src/lib/ion/ion.ts @@ -34,7 +34,7 @@ export async function getIonAssets(accessToken) { assert(accessToken); const url = CESIUM_ION_URL; const headers = {Authorization: `Bearer ${accessToken}`}; - const response = await fetchFile(url, {fetch: {headers}}); + const response = await fetchFile(url, {headers}); if (!response.ok) { throw new Error(response.statusText); } @@ -49,7 +49,7 @@ export async function getIonAssetMetadata(accessToken, assetId) { const url = `${CESIUM_ION_URL}/${assetId}`; // https://cesium.com/docs/rest-api/#operation/getAsset // Retrieves metadata information about a specific asset. - let response = await fetchFile(`${url}`, {fetch: {headers}}); + let response = await fetchFile(`${url}`, {headers}); if (!response.ok) { throw new Error(response.statusText); } @@ -57,7 +57,7 @@ export async function getIonAssetMetadata(accessToken, assetId) { // https://cesium.com/docs/rest-api/#operation/getAssetEndpoint // Retrieves information and credentials that allow you to access the tiled asset data for visualization and analysis. - response = await fetchFile(`${url}/endpoint`, {fetch: {headers}}); + response = await fetchFile(`${url}/endpoint`, {headers}); if (!response.ok) { throw new Error(response.statusText); } diff --git a/modules/3d-tiles/src/lib/parsers/helpers/parse-3d-implicit-tiles.ts b/modules/3d-tiles/src/lib/parsers/helpers/parse-3d-implicit-tiles.ts index 9bf60b3018..0223ab8633 100644 --- a/modules/3d-tiles/src/lib/parsers/helpers/parse-3d-implicit-tiles.ts +++ b/modules/3d-tiles/src/lib/parsers/helpers/parse-3d-implicit-tiles.ts @@ -7,6 +7,8 @@ import {getS2CellIdFromToken, getS2ChildCellId, getS2TokenFromCellId} from '../. import type {S2VolumeInfo} from '../../utils/obb/s2-corners-to-obb'; import {convertS2BoundingVolumetoOBB} from '../../utils/obb/s2-corners-to-obb'; import Long from 'long'; +import {Tiles3DLoaderOptions} from '../../../tiles-3d-loader'; +import {ImplicitOptions} from '../parse-3d-tile-header'; const QUADTREE_DEVISION_COUNT = 4; const OCTREE_DEVISION_COUNT = 8; @@ -84,15 +86,16 @@ function getChildS2VolumeBox( // eslint-disable-next-line max-statements export async function parseImplicitTiles(params: { subtree: Subtree; - options: any; + implicitOptions: ImplicitOptions; parentData?: {mortonIndex: number; x: number; y: number; z: number}; childIndex?: number; level?: number; globalData?: {level: number; mortonIndex: number; x: number; y: number; z: number}; s2VolumeBox?: S2VolumeBox; + loaderOptions: Tiles3DLoaderOptions; }) { const { - options, + implicitOptions, parentData = { mortonIndex: 0, x: 0, @@ -107,7 +110,8 @@ export async function parseImplicitTiles(params: { y: 0, z: 0 }, - s2VolumeBox + s2VolumeBox, + loaderOptions } = params; let {subtree, level = 0} = params; const { @@ -117,44 +121,56 @@ export async function parseImplicitTiles(params: { contentUrlTemplate, subtreesUriTemplate, basePath - } = options; - + } = implicitOptions; const tile = {children: [], lodMetricValue: 0, contentUrl: ''}; + if (!maximumLevel) { + // eslint-disable-next-line no-console + log.once( + `Missing 'maximumLevel' or 'availableLevels' property. The subtree ${contentUrlTemplate} won't be loaded...` + ); + return tile; + } + + const lev = level + globalData.level; + if (lev > maximumLevel) { + return tile; + } + const childrenPerTile = SUBDIVISION_COUNT_MAP[subdivisionScheme]; + const bitsPerTile = Math.log2(childrenPerTile); - // childIndex is in range [0, 7] + // childIndex is in range [0,4] for quadtrees and [0, 7] for octrees const childX = childIndex & 0b01; // Get first bit for X const childY = (childIndex >> 1) & 0b01; // Get second bit for Y const childZ = (childIndex >> 2) & 0b01; // Get third bit for Z const levelOffset = (childrenPerTile ** level - 1) / (childrenPerTile - 1); - let childTileMortonIndex = concatBits(parentData.mortonIndex, childIndex); + let childTileMortonIndex = concatBits(parentData.mortonIndex, childIndex, bitsPerTile); let tileAvailabilityIndex = levelOffset + childTileMortonIndex; // Local tile coordinates - let childTileX = concatBits(parentData.x, childX); - let childTileY = concatBits(parentData.y, childY); - let childTileZ = concatBits(parentData.z, childZ); + let childTileX = concatBits(parentData.x, childX, 1); + let childTileY = concatBits(parentData.y, childY, 1); + let childTileZ = concatBits(parentData.z, childZ, 1); let isChildSubtreeAvailable = false; - if (level + 1 > subtreeLevels) { + if (level >= subtreeLevels) { isChildSubtreeAvailable = getAvailabilityResult( subtree.childSubtreeAvailability, childTileMortonIndex ); } - const x = concatBits(globalData.x, childTileX); - const y = concatBits(globalData.y, childTileY); - const z = concatBits(globalData.z, childTileZ); - const lev = level + globalData.level; + const x = concatBits(globalData.x, childTileX, level * bitsPerTile); + const y = concatBits(globalData.y, childTileY, level * bitsPerTile); + const z = concatBits(globalData.z, childTileZ, level * bitsPerTile); if (isChildSubtreeAvailable) { const subtreePath = `${basePath}/${subtreesUriTemplate}`; const childSubtreeUrl = replaceContentUrlTemplate(subtreePath, lev, x, y, z); - const childSubtree = await load(childSubtreeUrl, Tile3DSubtreeLoader); + const childSubtree = await load(childSubtreeUrl, Tile3DSubtreeLoader, loaderOptions); subtree = childSubtree; @@ -174,7 +190,7 @@ export async function parseImplicitTiles(params: { const isTileAvailable = getAvailabilityResult(subtree.tileAvailability, tileAvailabilityIndex); - if (!isTileAvailable || level > maximumLevel) { + if (!isTileAvailable) { return tile; } @@ -200,11 +216,12 @@ export async function parseImplicitTiles(params: { // Recursive calling... const childTileParsed = await parseImplicitTiles({ subtree, - options, + implicitOptions, + loaderOptions, parentData: pData, childIndex: index, level: childTileLevel, - globalData, + globalData: {...globalData}, s2VolumeBox: childS2VolumeBox }); @@ -215,7 +232,7 @@ export async function parseImplicitTiles(params: { childTileParsed, globalLevel, childCoordinates, - options, + implicitOptions, s2VolumeBox ); // @ts-ignore @@ -274,7 +291,7 @@ function formatTileData( tile, level: number, childCoordinates: {childTileX: number; childTileY: number; childTileZ: number}, - options: any, + options: ImplicitOptions, s2VolumeBox?: S2VolumeBox ) { const { @@ -363,11 +380,12 @@ function calculateBoundingVolumeForChildTile( /** * Do binary concatenation - * @param first - * @param second + * @param higher - number to put to higher part of result + * @param lower - number to put to lower part of result + * @param shift - number of bits to shift lower number */ -function concatBits(first: number, second: number): number { - return parseInt(first.toString(2) + second.toString(2), 2); +function concatBits(higher: number, lower: number, shift: number): number { + return (higher << shift) + lower; } /** diff --git a/modules/3d-tiles/src/lib/parsers/helpers/parse-3d-tile-gltf-view.ts b/modules/3d-tiles/src/lib/parsers/helpers/parse-3d-tile-gltf-view.ts index 2e315ecc05..f03f4e7f31 100644 --- a/modules/3d-tiles/src/lib/parsers/helpers/parse-3d-tile-gltf-view.ts +++ b/modules/3d-tiles/src/lib/parsers/helpers/parse-3d-tile-gltf-view.ts @@ -8,7 +8,7 @@ // - Also, should we have hard dependency on gltf module or use injection or auto-discovery for gltf parser? import {GLTFLoader, postProcessGLTF, _getMemoryUsageGLTF} from '@loaders.gl/gltf'; -import {LoaderContext, sliceArrayBuffer} from '@loaders.gl/loader-utils'; +import {LoaderContext, sliceArrayBuffer, parseFromContext} from '@loaders.gl/loader-utils'; import {Tiles3DTileContent} from '../../../types'; import {Tiles3DLoaderOptions} from '../../../tiles-3d-loader'; @@ -74,15 +74,20 @@ export async function extractGLTF( if (!context) { return; } - const {parse, fetch} = context; if (tile.gltfUrl) { + const {fetch} = context; const response = await fetch(tile.gltfUrl, options); tile.gltfArrayBuffer = await response.arrayBuffer(); tile.gltfByteOffset = 0; } if (tile.gltfArrayBuffer) { // TODO - Should handle byteOffset... However, not used now... - const gltfWithBuffers = await parse(tile.gltfArrayBuffer, GLTFLoader, options, context); + const gltfWithBuffers = await parseFromContext( + tile.gltfArrayBuffer, + GLTFLoader, + options, + context + ); tile.gltf = postProcessGLTF(gltfWithBuffers); tile.gpuMemoryUsageInBytes = _getMemoryUsageGLTF(tile.gltf); delete tile.gltfArrayBuffer; diff --git a/modules/3d-tiles/src/lib/parsers/parse-3d-tile-composite.ts b/modules/3d-tiles/src/lib/parsers/parse-3d-tile-composite.ts index 233074760c..c37d180f77 100644 --- a/modules/3d-tiles/src/lib/parsers/parse-3d-tile-composite.ts +++ b/modules/3d-tiles/src/lib/parsers/parse-3d-tile-composite.ts @@ -38,7 +38,7 @@ export async function parseComposite3DTile( // extract each tile from the byte stream tile.tiles = []; while (tile.tiles.length < tile.tilesLength && (tile.byteLength || 0) - byteOffset > 12) { - const subtile = {}; + const subtile: Tiles3DTileContent = {shape: 'tile3d'}; tile.tiles.push(subtile); byteOffset = await parse3DTile(arrayBuffer, byteOffset, options, context, subtile); // TODO - do we need to add any padding in between tiles? diff --git a/modules/3d-tiles/src/lib/parsers/parse-3d-tile-gltf.ts b/modules/3d-tiles/src/lib/parsers/parse-3d-tile-gltf.ts index 3844c853fb..82ea4166cd 100644 --- a/modules/3d-tiles/src/lib/parsers/parse-3d-tile-gltf.ts +++ b/modules/3d-tiles/src/lib/parsers/parse-3d-tile-gltf.ts @@ -1,6 +1,9 @@ -import type {LoaderContext} from '@loaders.gl/loader-utils'; -import type {Tiles3DLoaderOptions} from '../../tiles-3d-loader'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {parseFromContext, LoaderContext} from '@loaders.gl/loader-utils'; import {_getMemoryUsageGLTF, GLTFLoader, postProcessGLTF} from '@loaders.gl/gltf'; +import type {Tiles3DLoaderOptions} from '../../tiles-3d-loader'; import {Tiles3DTileContent} from '../../types'; export async function parseGltf3DTile( @@ -8,26 +11,25 @@ export async function parseGltf3DTile( arrayBuffer: ArrayBuffer, options?: Tiles3DLoaderOptions, context?: LoaderContext -): Promise { +): Promise { // Set flags // glTF models need to be rotated from Y to Z up // https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification#y-up-to-z-up tile.rotateYtoZ = true; // Save gltf up axis - tile.gltfUpAxis = - options?.['3d-tiles'] && options['3d-tiles'].assetGltfUpAxis - ? options['3d-tiles'].assetGltfUpAxis - : 'Y'; + tile.gltfUpAxis = options?.['3d-tiles']?.assetGltfUpAxis + ? options['3d-tiles'].assetGltfUpAxis + : 'Y'; if (options?.['3d-tiles']?.loadGLTF) { if (!context) { - return; + return arrayBuffer.byteLength; } - const {parse} = context; - const gltfWithBuffers = await parse(arrayBuffer, GLTFLoader, options, context); + const gltfWithBuffers = await parseFromContext(arrayBuffer, GLTFLoader, options, context); tile.gltf = postProcessGLTF(gltfWithBuffers); tile.gpuMemoryUsageInBytes = _getMemoryUsageGLTF(tile.gltf); } else { tile.gltfArrayBuffer = arrayBuffer; } + return arrayBuffer.byteLength; } diff --git a/modules/3d-tiles/src/lib/parsers/parse-3d-tile-header.ts b/modules/3d-tiles/src/lib/parsers/parse-3d-tile-header.ts index 8fd71f4f36..c0b5cac9a6 100644 --- a/modules/3d-tiles/src/lib/parsers/parse-3d-tile-header.ts +++ b/modules/3d-tiles/src/lib/parsers/parse-3d-tile-header.ts @@ -1,11 +1,13 @@ import type {Tiles3DLoaderOptions} from '../../tiles-3d-loader'; import type {LoaderOptions} from '@loaders.gl/loader-utils'; +import {path} from '@loaders.gl/loader-utils'; import {Tile3DSubtreeLoader} from '../../tile-3d-subtree-loader'; import {load} from '@loaders.gl/core'; import {LOD_METRIC_TYPE, TILE_REFINEMENT, TILE_TYPE} from '@loaders.gl/tiles'; import { ImplicitTilingExensionData, Subtree, + Tile3DBoundingVolume, Tiles3DTileContentJSON, Tiles3DTileJSON, Tiles3DTileJSONPostprocessed, @@ -16,6 +18,34 @@ import {parseImplicitTiles, replaceContentUrlTemplate} from './helpers/parse-3d- import type {S2VolumeInfo} from '../utils/obb/s2-corners-to-obb'; import {convertS2BoundingVolumetoOBB} from '../utils/obb/s2-corners-to-obb'; +/** Options for recursive loading implicit subtrees */ +export type ImplicitOptions = { + /** Template of the full url of the content template */ + contentUrlTemplate: string; + /** Template of the full url of the subtree */ + subtreesUriTemplate: string; + /** Implicit subdivision scheme */ + subdivisionScheme: 'QUADTREE' | 'OCTREE' | string; + /** Levels per subtree */ + subtreeLevels: number; + /** Maximum implicit level through all subtrees */ + maximumLevel?: number; + /** 3DTiles refine method (add/replace) */ + refine?: string; + /** Tileset base path */ + basePath: string; + /** 3DTiles LOD metric type */ + lodMetricType: LOD_METRIC_TYPE.GEOMETRIC_ERROR; + /** Root metric value of the root tile of the implicit subtrees */ + rootLodMetricValue: number; + /** Bounding volume of the root tile of the implicit subtrees */ + rootBoundingVolume: Tile3DBoundingVolume; + /** Function that detects TILE_TYPE by tile metadata and content URL */ + getTileType: (tile: Tiles3DTileJSON, tileContentUrl?: string) => TILE_TYPE | string; + /** Function that converts string refine method to enum value */ + getRefine: (refine?: string) => TILE_REFINEMENT | string | undefined; +}; + function getTileType(tile: Tiles3DTileJSON, tileContentUrl: string = ''): TILE_TYPE | string { if (!tileContentUrl) { return TILE_TYPE.EMPTY; @@ -60,7 +90,7 @@ function resolveUri(uri: string = '', basePath: string): string { return uri; } - return `${basePath}/${uri}`; + return path.resolve(basePath, uri); } export function normalizeTileData( @@ -158,6 +188,7 @@ export async function normalizeImplicitTileHeaders( const { subdivisionScheme, maximumLevel, + availableLevels, subtreeLevels, subtrees: {uri: subtreesUriTemplate} } = implicitTilingExtension; @@ -179,12 +210,12 @@ export async function normalizeImplicitTileHeaders( const rootBoundingVolume = tile.boundingVolume; - const implicitOptions = { + const implicitOptions: ImplicitOptions = { contentUrlTemplate, subtreesUriTemplate, subdivisionScheme, subtreeLevels, - maximumLevel, + maximumLevel: Number.isFinite(availableLevels) ? availableLevels - 1 : maximumLevel, refine, basePath, lodMetricType: LOD_METRIC_TYPE.GEOMETRIC_ERROR, @@ -194,7 +225,7 @@ export async function normalizeImplicitTileHeaders( getRefine }; - return await normalizeImplicitTileData(tile, basePath, subtree, implicitOptions); + return await normalizeImplicitTileData(tile, basePath, subtree, implicitOptions, options); } /** @@ -208,7 +239,8 @@ export async function normalizeImplicitTileData( tile: Tiles3DTileJSON, basePath: string, rootSubtree: Subtree, - options: any + implicitOptions: ImplicitOptions, + loaderOptions: Tiles3DLoaderOptions ): Promise { if (!tile) { return null; @@ -216,7 +248,8 @@ export async function normalizeImplicitTileData( const {children, contentUrl} = await parseImplicitTiles({ subtree: rootSubtree, - options + implicitOptions, + loaderOptions }); let tileContentUrl: string | undefined; diff --git a/modules/3d-tiles/src/lib/parsers/parse-3d-tile-point-cloud.ts b/modules/3d-tiles/src/lib/parsers/parse-3d-tile-point-cloud.ts index e554287967..fda1447b11 100644 --- a/modules/3d-tiles/src/lib/parsers/parse-3d-tile-point-cloud.ts +++ b/modules/3d-tiles/src/lib/parsers/parse-3d-tile-point-cloud.ts @@ -2,6 +2,7 @@ // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md import {DracoLoader} from '@loaders.gl/draco'; +import {LoaderContext, parseFromContext} from '@loaders.gl/loader-utils'; import {GL} from '@loaders.gl/math'; import {Vector3} from '@math.gl/core'; @@ -13,7 +14,6 @@ import {normalize3DTileColorAttribute} from './helpers/normalize-3d-tile-colors' import {normalize3DTileNormalAttribute} from './helpers/normalize-3d-tile-normals'; import {normalize3DTilePositionAttribute} from './helpers/normalize-3d-tile-positions'; import {Tiles3DLoaderOptions} from '../../tiles-3d-loader'; -import {LoaderContext} from '@loaders.gl/loader-utils'; import {Tiles3DTileContent} from '../../types'; export async function parsePointCloud3DTile( @@ -262,7 +262,6 @@ export async function loadDraco( if (!context) { return; } - const {parse} = context; const dracoOptions = { ...options, draco: { @@ -274,17 +273,20 @@ export async function loadDraco( // The entire tileset might be included, too expensive to serialize delete dracoOptions['3d-tiles']; - const data = await parse(dracoData.buffer, DracoLoader, dracoOptions); + const data = await parseFromContext(dracoData.buffer, DracoLoader, dracoOptions, context); const decodedPositions = data.attributes.POSITION && data.attributes.POSITION.value; const decodedColors = data.attributes.COLOR_0 && data.attributes.COLOR_0.value; const decodedNormals = data.attributes.NORMAL && data.attributes.NORMAL.value; const decodedBatchIds = data.attributes.BATCH_ID && data.attributes.BATCH_ID.value; + // @ts-expect-error const isQuantizedDraco = decodedPositions && data.attributes.POSITION.value.quantization; + // @ts-expect-error const isOctEncodedDraco = decodedNormals && data.attributes.NORMAL.value.quantization; if (isQuantizedDraco) { // Draco quantization range == quantized volume scale - size in meters of the quantized volume // Internal quantized range is the range of values of the quantized data, e.g. 255 for 8-bit, 1023 for 10-bit, etc + // @ts-expect-error This doesn't look right const quantization = data.POSITION.data.quantization; const range = quantization.range; tile.quantizedVolumeScale = new Vector3(range, range, range); @@ -293,6 +295,7 @@ export async function loadDraco( tile.isQuantizedDraco = true; } if (isOctEncodedDraco) { + // @ts-expect-error This doesn't look right tile.octEncodedRange = (1 << data.NORMAL.data.quantization.quantizationBits) - 1.0; tile.isOctEncodedDraco = true; } @@ -308,9 +311,13 @@ export async function loadDraco( } tile.attributes = { + // @ts-expect-error positions: decodedPositions, + // @ts-expect-error colors: normalize3DTileColorAttribute(tile, decodedColors, undefined), + // @ts-expect-error normals: decodedNormals, + // @ts-expect-error batchIds: decodedBatchIds, ...batchTableAttributes }; diff --git a/modules/3d-tiles/src/lib/parsers/parse-3d-tile.ts b/modules/3d-tiles/src/lib/parsers/parse-3d-tile.ts index ba3f65f518..173dd80575 100644 --- a/modules/3d-tiles/src/lib/parsers/parse-3d-tile.ts +++ b/modules/3d-tiles/src/lib/parsers/parse-3d-tile.ts @@ -19,8 +19,8 @@ export async function parse3DTile( byteOffset = 0, options: Tiles3DLoaderOptions | undefined, context: LoaderContext | undefined, - tile: Tiles3DTileContent = {} -) { + tile: Tiles3DTileContent = {shape: 'tile3d'} +): Promise { tile.byteOffset = byteOffset; tile.type = getMagicString(arrayBuffer, byteOffset); diff --git a/modules/3d-tiles/src/lib/utils/s2/s2-token-functions.ts b/modules/3d-tiles/src/lib/utils/s2/s2-token-functions.ts index 70563f933e..3d3f2e5aab 100644 --- a/modules/3d-tiles/src/lib/utils/s2/s2-token-functions.ts +++ b/modules/3d-tiles/src/lib/utils/s2/s2-token-functions.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import Long from 'long'; diff --git a/modules/3d-tiles/src/tile-3d-subtree-loader.ts b/modules/3d-tiles/src/tile-3d-subtree-loader.ts index 6c9df443f9..8bee9c15ac 100644 --- a/modules/3d-tiles/src/tile-3d-subtree-loader.ts +++ b/modules/3d-tiles/src/tile-3d-subtree-loader.ts @@ -1,12 +1,12 @@ -import type {LoaderWithParser} from '@loaders.gl/loader-utils'; +import type {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; +import type {Subtree} from './types'; import parse3DTilesSubtree from './lib/parsers/helpers/parse-3d-tile-subtree'; import {VERSION} from './lib/utils/version'; /** * Loader for 3D Tiles Subtree - * */ -export const Tile3DSubtreeLoader: LoaderWithParser = { +export const Tile3DSubtreeLoader: LoaderWithParser = { id: '3d-tiles-subtree', name: '3D Tiles Subtree', module: '3d-tiles', diff --git a/modules/3d-tiles/src/tile-3d-writer.ts b/modules/3d-tiles/src/tile-3d-writer.ts index b36f92614c..32795eca6d 100644 --- a/modules/3d-tiles/src/tile-3d-writer.ts +++ b/modules/3d-tiles/src/tile-3d-writer.ts @@ -1,22 +1,23 @@ -import type {Writer} from '@loaders.gl/loader-utils'; +import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import {VERSION} from './lib/utils/version'; import encode3DTile from './lib/encoders/encode-3d-tile'; /** * Exporter for 3D Tiles */ -export const Tile3DWriter: Writer = { +export const Tile3DWriter: WriterWithEncoder = { name: '3D Tile', id: '3d-tiles', module: '3d-tiles', version: VERSION, extensions: ['cmpt', 'pnts', 'b3dm', 'i3dm'], mimeTypes: ['application/octet-stream'], - encodeSync, binary: true, options: { ['3d-tiles']: {} - } + }, + encode: async (tile, options) => encodeSync(tile, options), + encodeSync }; function encodeSync(tile, options) { diff --git a/modules/3d-tiles/src/tiles-3d-loader.ts b/modules/3d-tiles/src/tiles-3d-loader.ts index 9e3e986b24..34fd189c7c 100644 --- a/modules/3d-tiles/src/tiles-3d-loader.ts +++ b/modules/3d-tiles/src/tiles-3d-loader.ts @@ -8,7 +8,7 @@ import {TILESET_TYPE, LOD_METRIC_TYPE} from '@loaders.gl/tiles'; import {VERSION} from './lib/utils/version'; import {parse3DTile} from './lib/parsers/parse-3d-tile'; import {normalizeTileHeaders} from './lib/parsers/parse-3d-tile-header'; -import {Tiles3DTilesetJSON, Tiles3DTilesetJSONPostprocessed} from './types'; +import {Tiles3DTilesetJSON, Tiles3DTileContent, Tiles3DTilesetJSONPostprocessed} from './types'; export type Tiles3DLoaderOptions = LoaderOptions & // GLTFLoaderOptions & - TODO not yet exported @@ -29,7 +29,11 @@ export type Tiles3DLoaderOptions = LoaderOptions & /** * Loader for 3D Tiles */ -export const Tiles3DLoader: LoaderWithParser = { +export const Tiles3DLoader: LoaderWithParser< + any, // Tiles3DTileContent | Tiles3DTilesetJSONPostprocessed, + never, + Tiles3DLoaderOptions +> = { id: '3d-tiles', name: '3D Tiles', module: '3d-tiles', @@ -49,7 +53,11 @@ export const Tiles3DLoader: LoaderWithParser = { }; /** Parses a tileset or tile */ -async function parse(data, options: Tiles3DLoaderOptions = {}, context?: LoaderContext) { +async function parse( + data, + options: Tiles3DLoaderOptions = {}, + context?: LoaderContext +): Promise { // auto detect file type const loaderOptions = options['3d-tiles'] || {}; let isTileset; @@ -75,6 +83,7 @@ async function parseTileset( const normalizedRoot = await normalizeTileHeaders(tilesetJson, basePath, options || {}); const tilesetJsonPostprocessed: Tiles3DTilesetJSONPostprocessed = { ...tilesetJson, + shape: 'tileset3d', loader: Tiles3DLoader, url: tilesetUrl, queryString: context?.queryString || '', @@ -92,14 +101,17 @@ async function parseTile( arrayBuffer: ArrayBuffer, options?: Tiles3DLoaderOptions, context?: LoaderContext -) { +): Promise { const tile = { content: { + shape: 'tile3d', featureIds: null } }; const byteOffset = 0; + // @ts-expect-error await parse3DTile(arrayBuffer, byteOffset, options, context, tile.content); + // @ts-expect-error return tile.content; } diff --git a/modules/3d-tiles/src/types.ts b/modules/3d-tiles/src/types.ts index b5ca5fdfd8..1e9b9b8aa3 100644 --- a/modules/3d-tiles/src/types.ts +++ b/modules/3d-tiles/src/types.ts @@ -1,12 +1,10 @@ -import type {GLTFPostprocessed} from '@loaders.gl/gltf'; +import type {GLTFPostprocessed, FeatureTableJson} from '@loaders.gl/gltf'; +export type {FeatureTableJson}; + import {LoaderWithParser} from '@loaders.gl/loader-utils'; import {Matrix4, Vector3} from '@math.gl/core'; import {TILESET_TYPE, LOD_METRIC_TYPE, TILE_TYPE, TILE_REFINEMENT} from '@loaders.gl/tiles'; -export type FeatureTableJson = { - [key: string]: any[]; -}; - export type B3DMContent = { batchTableJson?: FeatureTableJson; byteLength: number; @@ -42,6 +40,7 @@ export type GLTFHeader = { * https://github.com/CesiumGS/3d-tiles/tree/main/specification#property-reference */ export type Tiles3DTilesetJSON = { + shape: 'tileset3d'; /** Metadata about the entire tileset. * https://github.com/CesiumGS/3d-tiles/tree/main/specification#asset */ @@ -75,10 +74,7 @@ export type Tiles3DTilesetJSON = { /** TilesetJSON postprocessed by Tiles3DLoader */ export type Tiles3DTilesetJSONPostprocessed = Omit & { - /** - * Loader used - * @deprecated - */ + /** @deprecated Loader used */ loader: LoaderWithParser; /** URL used to load a tileset resource */ url: string; @@ -202,6 +198,8 @@ export type TilesetProperty = { }; export type Tiles3DTileContent = { + shape: 'tile3d'; + /** Common properties */ byteOffset?: number; type?: string; @@ -300,9 +298,9 @@ export type Tiles3DTileContent = { */ export type Subtree = { /** An array of buffers. */ - buffers: Buffer[]; + buffers: GLTFStyleBuffer[]; /** An array of buffer views. */ - bufferViews: BufferView[]; + bufferViews: GLTFStyleBufferView[]; /** The availability of tiles in the subtree. The availability bitstream is a 1D boolean array where tiles are ordered by their level in the subtree and Morton index * within that level. A tile's availability is determined by a single bit, 1 meaning a tile exists at that spatial index, and 0 meaning it does not. * The number of elements in the array is `(N^subtreeLevels - 1)/(N - 1)` where N is 4 for subdivision scheme `QUADTREE` and 8 for `OCTREE`. @@ -355,14 +353,14 @@ export type ExplicitBitstream = Uint8Array; */ export type SubdivisionScheme = 'QUADTREE' | 'OCTREE'; -type Buffer = { +type GLTFStyleBuffer = { name: string; uri?: string; byteLength: number; }; /** Subtree buffer view */ -export type BufferView = { +export type GLTFStyleBufferView = { buffer: number; byteOffset: number; byteLength: number; @@ -372,6 +370,10 @@ export type BufferView = { * Spec - https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_implicit_tiling */ export type ImplicitTilingExensionData = ImplicitTilingData & { + /** This property is not part of the schema + * https://github.com/CesiumGS/3d-tiles/blob/main/extensions/3DTILES_implicit_tiling/schema/tile.3DTILES_implicit_tiling.schema.json + * But it can be seen in some test datasets. It is handled as substitute of `availableLevels` + */ maximumLevel?: number; }; diff --git a/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Elevation_1_1.json b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Elevation_1_1.json new file mode 100644 index 0000000000..dbf6021dca --- /dev/null +++ b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Elevation_1_1.json @@ -0,0 +1,29 @@ +{ + "asset": { + "version": "1.1" + }, + "geometricError": 300000.0, + "root": { + "boundingVolume": { + "region": [ + 0.767944870877505, 0.20943951023931956, 0.8028514559173916, 0.24434609527920614, 0.0, 0.0 + ] + }, + "geometricError": 300000.0, + "refine": "ADD", + "children": [ + { + "boundingVolume": { + "region": [ + 0.767944870877505, 0.20943951023931956, 0.7853981633974483, 0.22689280275926285, 0.0, + 0.0 + ] + }, + "geometricError": 300000.0, + "content": { + "uri": "Tiles/N12/E044/Elevation/1_1/N12E044_D001_S001_T001.json" + } + } + ] + } +} diff --git a/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/GSModels_1_1.json b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/GSModels_1_1.json new file mode 100644 index 0000000000..b9b3aaa600 --- /dev/null +++ b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/GSModels_1_1.json @@ -0,0 +1,22 @@ +{ + "asset": { + "version": "1.1" + }, + "geometricError": 300000.0, + "root": { + "boundingVolume": { + "region": [0.7853981633974483, 0.20943951023931956, 0.8028514559173916, 0.22689280275926285, 0.0, 0.0] + }, + "geometricError": 300000.0, + "refine": "ADD", + "children": [{ + "boundingVolume": { + "region": [0.7853981633974483, 0.20943951023931956, 0.8028514559173916, 0.22689280275926285, 0.0, 0.0] + }, + "geometricError": 300000.0, + "content": { + "uri": "Tiles/N12/E045/GSModels/1_1/N12E045_D300_S001_T001.json" + } + }] + } +} \ No newline at end of file diff --git a/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E044/Elevation/1_1/N12E044_D001_S001_T001.json b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E044/Elevation/1_1/N12E044_D001_S001_T001.json new file mode 100644 index 0000000000..194cf5927f --- /dev/null +++ b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E044/Elevation/1_1/N12E044_D001_S001_T001.json @@ -0,0 +1,19 @@ +{ + "asset": { + "version": "1.1" + }, + "geometricError": 300000.0, + "root": { + "boundingVolume": { + "region": [ + 0.767944870877505, 0.20943951023931956, 0.7853981633974483, 0.22689280275926285, + -16.97454071044922, 783.8671875 + ] + }, + "geometricError": 300000.0, + "refine": "REPLACE", + "content": { + "uri": "N12E044_D001_S001_T001_LC10_U0_R0.glb" + } + } +} diff --git a/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E044/Elevation/1_1/N12E044_D001_S001_T001_LC10_U0_R0.glb b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E044/Elevation/1_1/N12E044_D001_S001_T001_LC10_U0_R0.glb new file mode 100644 index 0000000000..c2894ebbfe Binary files /dev/null and b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E044/Elevation/1_1/N12E044_D001_S001_T001_LC10_U0_R0.glb differ diff --git a/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E045/GSModels/1_1/N12E045_D300_S001_T001.json b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E045/GSModels/1_1/N12E045_D300_S001_T001.json new file mode 100644 index 0000000000..58602f79cf --- /dev/null +++ b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E045/GSModels/1_1/N12E045_D300_S001_T001.json @@ -0,0 +1,73 @@ +{ + "asset": { + "version": "1.1" + }, + "geometricError": 300000.0, + "root": { + "boundingVolume": { + "region": [ + 0.7853981633974483, 0.20943951023931956, 0.8028514559173916, 0.22689280275926285, 0.0, 0.0 + ] + }, + "geometricError": 300000.0, + "refine": "ADD", + "children": [ + { + "boundingVolume": { + "region": [ + 0.7853981633974483, 0.20943951023931956, 0.8028514559173916, 0.22689280275926285, 0.0, + 0.0 + ] + }, + "geometricError": 150000.0, + "children": [ + { + "boundingVolume": { + "region": [ + 0.7853981633974483, 0.20943951023931956, 0.8028514559173916, 0.22689280275926285, + 0.0, 0.0 + ] + }, + "geometricError": 75000.0, + "children": [ + { + "boundingVolume": { + "region": [ + 0.7853981633974483, 0.20943951023931956, 0.8028514559173916, + 0.22689280275926285, 0.0, 0.0 + ] + }, + "geometricError": 37500.0, + "children": [ + { + "boundingVolume": { + "region": [ + 0.7853981633974483, 0.20943951023931956, 0.8028514559173916, + 0.22689280275926285, 0.0, 0.0 + ] + }, + "geometricError": 18750.0, + "children": [ + { + "boundingVolume": { + "region": [ + 0.7853981633974483, 0.20943951023931956, 0.8028514559173916, + 0.22689280275926285, 0.0, 0.0 + ] + }, + "geometricError": 9375.0, + "content": { + "uri": "N12E045_D300_S001_T001_LC5_U0_R0.glb" + } + } + ] + } + ] + } + ] + } + ] + } + ] + } +} diff --git a/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E045/GSModels/1_1/N12E045_D300_S001_T001_LC5_U0_R0.glb b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E045/GSModels/1_1/N12E045_D300_S001_T001_LC5_U0_R0.glb new file mode 100644 index 0000000000..559f86e0b8 Binary files /dev/null and b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/Tiles/N12/E045/GSModels/1_1/N12E045_D300_S001_T001_LC5_U0_R0.glb differ diff --git a/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/tileset.json b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/tileset.json new file mode 100644 index 0000000000..e6d91b390d --- /dev/null +++ b/modules/3d-tiles/test/data/VNext/cdb-yemen-cut/tileset.json @@ -0,0 +1,41 @@ +{ + "asset": { + "version": "1.1" + }, + "geometricError": 300000.0, + "root": { + "boundingVolume": { + "region": [ + 0.767944870877505, 0.20943951023931956, 0.8028514559173916, 0.24434609527920614, 0.0, 0.0 + ] + }, + "geometricError": 300000.0, + "refine": "ADD", + "children": [ + { + "boundingVolume": { + "region": [ + 0.767944870877505, 0.20943951023931956, 0.8028514559173916, 0.24434609527920614, 0.0, + 0.0 + ] + }, + "geometricError": 300000.0, + "content": { + "uri": "Elevation_1_1.json" + } + }, + { + "boundingVolume": { + "region": [ + 0.7853981633974483, 0.20943951023931956, 0.8028514559173916, 0.22689280275926285, 0.0, + 0.0 + ] + }, + "geometricError": 300000.0, + "content": { + "uri": "GSModels_1_1.json" + } + } + ] + } +} diff --git a/modules/3d-tiles/test/data/test.3tz b/modules/3d-tiles/test/data/test.3tz new file mode 100644 index 0000000000..e3c8e40957 Binary files /dev/null and b/modules/3d-tiles/test/data/test.3tz differ diff --git a/modules/3d-tiles/test/index.ts b/modules/3d-tiles/test/index.ts index 1024e3657a..6973961edd 100644 --- a/modules/3d-tiles/test/index.ts +++ b/modules/3d-tiles/test/index.ts @@ -6,12 +6,15 @@ import './lib/parsers/instanced-model-3d-tile.spec'; import './lib/parsers/point-cloud-3d-tile.spec'; import './lib/parsers/composite-3d-tile.spec'; import './lib/parsers/parse-3d-tile-header.spec'; -import './lib/parsers/helpers/gpu-memory-usage.spec'; +// TODO v4.0 restore these tests +// import './lib/parsers/helpers/gpu-memory-usage.spec'; import './lib/parsers/helpers/normalize-3d-tile-colors.spec'; // import './styles/expression.spec'; // import './styles/conditions-expression.spec'; // import './styles/tile-3d-style.spec'; +// TODO v4.0 restore these tests import './tiles-3d-loader.spec'; import './tile-3d-subtree-loader.spec'; +import './tiles-3d-archive-loaders.spec'; diff --git a/modules/3d-tiles/test/lib/parsers/helpers/normalize-3d-tile-colors.spec.ts b/modules/3d-tiles/test/lib/parsers/helpers/normalize-3d-tile-colors.spec.ts index 540338cd38..db336aad36 100644 --- a/modules/3d-tiles/test/lib/parsers/helpers/normalize-3d-tile-colors.spec.ts +++ b/modules/3d-tiles/test/lib/parsers/helpers/normalize-3d-tile-colors.spec.ts @@ -2,17 +2,18 @@ import test from 'tape-promise/tape'; import {GL} from '@loaders.gl/math'; import {normalize3DTileColorAttribute} from '../../../../src/lib/parsers/helpers/normalize-3d-tile-colors'; +import {Tiles3DTileContent} from '@loaders.gl/3d-tiles'; -const TEST_CASES = [ +const TEST_CASES: {tile: Tiles3DTileContent; colors; batchTable; expected; message: string}[] = [ { - tile: {}, + tile: {shape: 'tile3d'}, colors: null, batchTable: null, expected: null, message: 'Should return null when no tile / colors' }, { - tile: {pointCount: 1}, + tile: {shape: 'tile3d', pointCount: 1}, colors: new Uint8ClampedArray([250, 150, 50]), batchTable: null, expected: { @@ -24,7 +25,7 @@ const TEST_CASES = [ message: 'Size should be 3 for RGB format' }, { - tile: {pointCount: 1}, + tile: {shape: 'tile3d', pointCount: 1}, colors: new Uint8ClampedArray([250, 150, 50, 255]), batchTable: null, expected: { diff --git a/modules/3d-tiles/test/lib/parsers/instanced-model-3d-tile.spec.ts b/modules/3d-tiles/test/lib/parsers/instanced-model-3d-tile.spec.ts index d2fc6e4652..ddb7524caf 100644 --- a/modules/3d-tiles/test/lib/parsers/instanced-model-3d-tile.spec.ts +++ b/modules/3d-tiles/test/lib/parsers/instanced-model-3d-tile.spec.ts @@ -78,7 +78,11 @@ test('instanced model tile#throws with empty gltf', async (t) => { type: TILE3D_TYPE.INSTANCED_3D_MODEL }; const arrayBuffer = encodeSync(TILE, Tile3DWriter); - await t.rejects(parse(arrayBuffer, Tiles3DLoader), /valid loader/, 'throws with empty gltf'); + await t.rejects( + parse(arrayBuffer, Tiles3DLoader), + // /valid loader/, + 'throws with empty gltf' + ); t.end(); }); @@ -94,7 +98,7 @@ test('instanced model tile#throws on invalid url', async (t) => { const arrayBuffer = encodeSync(TILE, Tile3DWriter); await t.rejects( parse(arrayBuffer, Tiles3DLoader), - /No valid loader found/, + // /No valid loader found/, 'throws on invalid url' ); t.end(); diff --git a/modules/3d-tiles/test/lib/parsers/point-cloud-3d-tile.spec.ts b/modules/3d-tiles/test/lib/parsers/point-cloud-3d-tile.spec.ts index d35c2c9837..c52750e32d 100644 --- a/modules/3d-tiles/test/lib/parsers/point-cloud-3d-tile.spec.ts +++ b/modules/3d-tiles/test/lib/parsers/point-cloud-3d-tile.spec.ts @@ -99,9 +99,9 @@ test('loadDraco# Pass options to draco loader properly', async (t) => { worker: true, reuseWorkers: true }; - const tile = {}; + const context: LoaderContext = { - parse: async (buffer, loader, resultOptions) => { + _parse: async (buffer, loader, resultOptions) => { t.deepEqual(resultOptions, resultObject); t.equal(resultOptions?.['3d-tiles'], undefined); t.end(); @@ -117,7 +117,7 @@ test('loadDraco# Pass options to draco loader properly', async (t) => { worker: true, reuseWorkers: true }; - await loadDraco(tile, dracoData, options, context); + await loadDraco({shape: 'tile3d'}, dracoData, options, context); }); /* diff --git a/modules/3d-tiles/test/lib/utils/load-utils.ts b/modules/3d-tiles/test/lib/utils/load-utils.ts index 21d810854e..521d201fa3 100644 --- a/modules/3d-tiles/test/lib/utils/load-utils.ts +++ b/modules/3d-tiles/test/lib/utils/load-utils.ts @@ -4,6 +4,7 @@ import {fetchFile, load} from '@loaders.gl/core'; import {Tiles3DLoader} from '@loaders.gl/3d-tiles'; import {Tileset3D} from '@loaders.gl/tiles'; +import type {Tile3D} from '@loaders.gl/tiles'; /** @typedef {import('@loaders.gl/tiles').Tile3D} Tile3D */ diff --git a/modules/3d-tiles/test/tiles-3d-archive-loaders.spec.js b/modules/3d-tiles/test/tiles-3d-archive-loaders.spec.js new file mode 100644 index 0000000000..6bd6f13c4d --- /dev/null +++ b/modules/3d-tiles/test/tiles-3d-archive-loaders.spec.js @@ -0,0 +1,13 @@ +import test from 'tape-promise/tape'; +import {load} from '@loaders.gl/core'; +import {Tiles3DArchiveFileLoader} from '../src'; + +const testUrl = '@loaders.gl/3d-tiles/test/data/test.3tz'; + +test('Tiles3DArchiveFileLoader#load uncompressed file', async (t) => { + const uncompressedFile = await load(testUrl, Tiles3DArchiveFileLoader, { + '3d-tiles-archive': {path: 'tileset.json'} + }); + t.deepEqual(uncompressedFile.byteLength, 2339, 'tileset.json has the correct length'); + t.end(); +}); diff --git a/modules/3d-tiles/test/tiles-3d-loader.spec.ts b/modules/3d-tiles/test/tiles-3d-loader.spec.ts index 505efac95a..af152676a2 100644 --- a/modules/3d-tiles/test/tiles-3d-loader.spec.ts +++ b/modules/3d-tiles/test/tiles-3d-loader.spec.ts @@ -59,7 +59,9 @@ test('Tiles3DLoader#Tile with GLB w/ Draco bufferviews', async (t) => { const response = await fetchFile(TILE_B3DM_WITH_DRACO_URL); const tile = await parse(response, [Tiles3DLoader, DracoLoader]); t.ok(tile); + // @ts-expect-error type Tiles3DLoader t.ok(tile.gltf); + // @ts-expect-error type Tiles3DLoader t.equals(tile.type, 'b3dm', 'Should parse the correct tiles type.'); t.end(); }); @@ -168,7 +170,7 @@ test('Tiles3DLoader#Implicit Octree Tileset with bitstream availability and subt t.equal(tileset.root.children[0].children[0].children.length, 1); // children level 3 - t.equal(tileset.root.children[0].children[0].children[0].content.uri, 'content/3/2/0/1.pnts'); + t.equal(tileset.root.children[0].children[0].children[0].content.uri, 'content/3/8/0/1.pnts'); t.equal(tileset.root.children[0].children[0].children[0].lodMetricValue, 625); t.equal(tileset.root.children[0].children[0].children[0].refine, 1); t.equal(tileset.root.children[0].children[0].children[0].type, 'pointcloud'); diff --git a/modules/3d-tiles/tsconfig.json b/modules/3d-tiles/tsconfig.json index a3b4ccc969..a613ab4d3c 100644 --- a/modules/3d-tiles/tsconfig.json +++ b/modules/3d-tiles/tsconfig.json @@ -9,10 +9,12 @@ }, "references": [ {"path": "../core"}, + {"path": "../compression"}, {"path": "../draco"}, {"path": "../gltf"}, {"path": "../loader-utils"}, {"path": "../math"}, - {"path": "../tiles"} + {"path": "../tiles"}, + {"path": "../zip"} ] } diff --git a/modules/3d-tiles/wip/tileset-3d-full.md b/modules/3d-tiles/wip/tileset-3d-full.md index bb933a7fb5..bb2e81b04d 100644 --- a/modules/3d-tiles/wip/tileset-3d-full.md +++ b/modules/3d-tiles/wip/tileset-3d-full.md @@ -71,15 +71,13 @@ The base path that non-absolute paths in tileset JSON file are relative to. ### maximumScreenSpaceError -The maximum screen space error used to drive level of detail refinement. This value helps determine when a tile refines to its descendants, and therefore plays a major role in balancing performance with visual quality. - +Threshold that controls the level of detail of loaded tiles. A higher value means tile traversal stops early, displaying lower quality tiles (but much faster load times & less bandwidth used), because we're using a high "error tolerance". A lower value means lower tolerance for error, so traversal goes deeper in the tree and displays higher quality tiles. A tile's screen space error is roughly equivalent to the number of pixels wide that would be drawn if a sphere with a radius equal to the tile's geometric error were rendered at the tile's position. If this value exceeds `maximumScreenSpaceError` the tile refines to its descendants. -Depending on the tileset, `maximumScreenSpaceError` may need to be tweaked to achieve the right balance. Higher values provide better performance but lower visual quality. - * +Depending on the tileset, `maximumScreenSpaceError` may need to be tweaked to achieve the right balance between performance with visual quality. ### maximumMemoryUsage : Number diff --git a/modules/arrow/package.json b/modules/arrow/package.json index 6ac782bb66..90fa4d24a3 100644 --- a/modules/arrow/package.json +++ b/modules/arrow/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/arrow", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Simple columnar table loader for the Apache Arrow format", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "PLY" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -32,17 +40,17 @@ "fs": false }, "scripts": { - "pre-build": "npm run build-worker && npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js --platform=browser --external:{stream}", + "pre-build": "npm run build-worker && npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/arrow-worker.ts --bundle --outfile=dist/arrow-worker.js --platform=browser --external:{stream} --define:__VERSION__=\\\"$npm_package_version\\\"", "pre-build2": "cp fixed-package.json ../../node_modules/apache-arrow/package.json && npm run build-bundle && npm run build-worker", - "build-bundle2": "esbuild src/bundle.ts --bundle --outfile=dist/bundle.js --platform=browser --external:{stream}", "build-worker2": "esbuild src/workers/arrow-worker.ts --bundle --outfile=dist/arrow-worker.js --platform=browser --external:{stream}" }, "dependencies": { - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", - "apache-arrow": "^9.0.0" + "@loaders.gl/gis": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "apache-arrow": "^13.0.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/arrow/src/arrow-loader.ts b/modules/arrow/src/arrow-loader.ts index fd75ef35fc..e755ba8e97 100644 --- a/modules/arrow/src/arrow-loader.ts +++ b/modules/arrow/src/arrow-loader.ts @@ -1,6 +1,8 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {Loader, LoaderOptions} from '@loaders.gl/loader-utils'; -import type {ArrowTable} from '@loaders.gl/schema'; +import type {ArrowTable} from './lib/arrow-table'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. @@ -34,5 +36,3 @@ export const ArrowLoader: Loader = { } } }; - -export const _typecheckArrowLoader: Loader = ArrowLoader; diff --git a/modules/arrow/src/arrow-writer.ts b/modules/arrow/src/arrow-writer.ts index 7ffa8a65e8..ed63db63be 100644 --- a/modules/arrow/src/arrow-writer.ts +++ b/modules/arrow/src/arrow-writer.ts @@ -1,5 +1,6 @@ // import type {} from '@loaders.gl/loader-utils'; -import type {Writer, WriterOptions} from '@loaders.gl/loader-utils'; + +import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import {ColumnarTable} from './lib/encode-arrow'; import {encodeArrowSync} from './lib/encode-arrow'; @@ -12,7 +13,7 @@ type ArrowWriterOptions = WriterOptions & { }; /** Apache Arrow writer */ -export const ArrowWriter: Writer = { +export const ArrowWriter: WriterWithEncoder = { name: 'Apache Arrow', id: 'arrow', module: 'arrow', @@ -23,9 +24,12 @@ export const ArrowWriter: Writer = { 'application/vnd.apache.arrow.stream', 'application/octet-stream' ], - encodeSync(data, options?) { + binary: true, + options: {}, + encode: async function encodeArrow(data, options?): Promise { return encodeArrowSync(data); }, - binary: true, - options: {} + encodeSync(data, options?) { + return encodeArrowSync(data); + } }; diff --git a/modules/arrow/src/bundle.ts b/modules/arrow/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/arrow/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/arrow/src/geoarrow/convert-geoarrow-to-binary-geometry.ts b/modules/arrow/src/geoarrow/convert-geoarrow-to-binary-geometry.ts new file mode 100644 index 0000000000..03f44da20e --- /dev/null +++ b/modules/arrow/src/geoarrow/convert-geoarrow-to-binary-geometry.ts @@ -0,0 +1,262 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import * as arrow from 'apache-arrow'; +import {BinaryFeatureCollection as BinaryFeatures} from '@loaders.gl/schema'; +import {GeoArrowEncoding} from '@loaders.gl/gis'; +import {updateBoundsFromGeoArrowSamples} from './get-arrow-bounds'; + +/** + * Binary data from geoarrow column and can be used by e.g. deck.gl GeojsonLayer + */ +export type BinaryDataFromGeoArrow = { + binaryGeometries: BinaryFeatures[]; + bounds: [number, number, number, number]; + featureTypes: {polygon: boolean; point: boolean; line: boolean}; +}; + +type BinaryGeometryContent = { + featureIds: Uint32Array; + flatCoordinateArray: Float64Array; + nDim: number; + geomOffset: Int32Array; + geometryIndicies: Uint16Array; +}; + +// binary geometry template, see deck.gl BinaryGeometry +export const BINARY_GEOMETRY_TEMPLATE = { + globalFeatureIds: {value: new Uint32Array(0), size: 1}, + positions: {value: new Float32Array(0), size: 2}, + properties: [], + numericProps: {}, + featureIds: {value: new Uint32Array(0), size: 1} +}; + +/** + * get binary geometries from geoarrow column + * + * @param geoColumn the geoarrow column, e.g. arrowTable.getChildAt(geoColumnIndex) + * @param geoEncoding the geo encoding of the geoarrow column, e.g. getGeoArrowEncoding(arrowTable.schema, geoColumnName) + * @returns BinaryDataFromGeoArrow + */ +export function getBinaryGeometriesFromArrow( + geoColumn: arrow.Vector, + geoEncoding: GeoArrowEncoding +): BinaryDataFromGeoArrow { + const featureTypes = { + polygon: geoEncoding === 'geoarrow.multipolygon' || geoEncoding === 'geoarrow.polygon', + point: geoEncoding === 'geoarrow.multipoint' || geoEncoding === 'geoarrow.point', + line: geoEncoding === 'geoarrow.multilinestring' || geoEncoding === 'geoarrow.linestring' + }; + + const chunks = geoColumn.data; + let bounds: [number, number, number, number] = [Infinity, Infinity, -Infinity, -Infinity]; + let globalFeatureIdOffset = 0; + const binaryGeometries: BinaryFeatures[] = []; + + chunks.forEach((chunk) => { + const {featureIds, flatCoordinateArray, nDim, geomOffset} = getBinaryGeometriesFromChunk( + chunk, + geoEncoding + ); + + const globalFeatureIds = new Uint32Array(featureIds.length); + for (let i = 0; i < featureIds.length; i++) { + globalFeatureIds[i] = featureIds[i] + globalFeatureIdOffset; + } + + const binaryContent = { + globalFeatureIds: {value: globalFeatureIds, size: 1}, + positions: { + value: flatCoordinateArray, + size: nDim + }, + featureIds: {value: featureIds, size: 1}, + properties: [...Array(chunk.length).keys()].map((i) => ({ + index: i + globalFeatureIdOffset + })) + }; + + // TODO: check if chunks are sequentially accessed + globalFeatureIdOffset += chunk.length; + + // NOTE: deck.gl defines the BinaryFeatures structure must have points, lines, polygons even if they are empty + binaryGeometries.push({ + shape: 'binary-feature-collection', + points: { + type: 'Point', + ...BINARY_GEOMETRY_TEMPLATE, + ...(featureTypes.point ? binaryContent : {}) + }, + lines: { + type: 'LineString', + ...BINARY_GEOMETRY_TEMPLATE, + ...(featureTypes.line ? binaryContent : {}), + pathIndices: {value: featureTypes.line ? geomOffset : new Uint16Array(0), size: 1} + }, + polygons: { + type: 'Polygon', + ...BINARY_GEOMETRY_TEMPLATE, + ...(featureTypes.polygon ? binaryContent : {}), + polygonIndices: { + // TODO why deck.gl's tessellatePolygon performance is not good when using geometryIndicies + // even when there is no hole in any polygon + value: featureTypes.polygon ? geomOffset : new Uint16Array(0), + size: 1 + }, + primitivePolygonIndices: { + value: featureTypes.polygon ? geomOffset : new Uint16Array(0), + size: 1 + } + } + }); + + bounds = updateBoundsFromGeoArrowSamples(flatCoordinateArray, nDim, bounds); + }); + + return {binaryGeometries, bounds, featureTypes}; +} + +/** + * get binary geometries from geoarrow column + * @param chunk one chunk/batch of geoarrow column + * @param geoEncoding geo encoding of the geoarrow column + * @returns BinaryGeometryContent + */ +function getBinaryGeometriesFromChunk( + chunk: arrow.Data, + geoEncoding: GeoArrowEncoding +): BinaryGeometryContent { + switch (geoEncoding) { + case 'geoarrow.point': + case 'geoarrow.multipoint': + return getBinaryPointsFromChunk(chunk, geoEncoding); + case 'geoarrow.linestring': + case 'geoarrow.multilinestring': + return getBinaryLinesFromChunk(chunk, geoEncoding); + case 'geoarrow.polygon': + case 'geoarrow.multipolygon': + return getBinaryPolygonsFromChunk(chunk, geoEncoding); + default: + throw Error('invalid geoarrow encoding'); + } +} + +/** + * get binary polygons from geoarrow polygon column + * @param chunk one chunk of geoarrow polygon column + * @param geoEncoding the geo encoding of the geoarrow polygon column + * @returns BinaryGeometryContent + */ +function getBinaryPolygonsFromChunk(chunk: arrow.Data, geoEncoding: string): BinaryGeometryContent { + const isMultiPolygon = geoEncoding === 'geoarrow.multipolygon'; + + const polygonData = isMultiPolygon ? chunk.children[0] : chunk; + const polygonOffset = polygonData.valueOffsets; + const partData = isMultiPolygon + ? chunk.valueOffsets.map((i) => polygonOffset.at(i) || i) + : chunk.valueOffsets; + const ringData = polygonData.children[0]; + const pointData = ringData.children[0]; + const coordData = pointData.children[0]; + const nDim = pointData.stride; + const geomOffset = ringData.valueOffsets; + const flatCoordinateArray = coordData.values; + + const geometryIndicies = new Uint16Array(polygonOffset.length); + for (let i = 0; i < polygonOffset.length; i++) { + geometryIndicies[i] = geomOffset[polygonOffset[i]]; + } + + const numOfVertices = flatCoordinateArray.length / nDim; + const featureIds = new Uint32Array(numOfVertices); + for (let i = 0; i < partData.length - 1; i++) { + const startIdx = geomOffset[partData[i]]; + const endIdx = geomOffset[partData[i + 1]]; + for (let j = startIdx; j < endIdx; j++) { + featureIds[j] = i; + } + } + + return { + featureIds, + flatCoordinateArray, + nDim, + geomOffset, + geometryIndicies + }; +} + +/** + * get binary lines from geoarrow line column + * @param chunk one chunk/batch of geoarrow column + * @param geoEncoding the geo encoding of the geoarrow column + * @returns BinaryGeometryContent + */ +function getBinaryLinesFromChunk(chunk: arrow.Data, geoEncoding: string): BinaryGeometryContent { + const isMultiLineString = geoEncoding === 'geoarrow.multilinestring'; + + const lineData = isMultiLineString ? chunk.children[0] : chunk; + const pointData = lineData.children[0]; + const coordData = pointData.children[0]; + + const nDim = pointData.stride; + const geomOffset = lineData.valueOffsets; + const flatCoordinateArray = coordData.values; + + // geometryIndicies is not needed for line string + const geometryIndicies = new Uint16Array(0); + + const numOfVertices = flatCoordinateArray.length / nDim; + const featureIds = new Uint32Array(numOfVertices); + for (let i = 0; i < chunk.length; i++) { + const startIdx = geomOffset[i]; + const endIdx = geomOffset[i + 1]; + for (let j = startIdx; j < endIdx; j++) { + featureIds[j] = i; + } + } + + return { + featureIds, + flatCoordinateArray, + nDim, + geomOffset, + geometryIndicies + }; +} + +/** + * get binary points from geoarrow point column + * @param chunk one chunk/batch of geoarrow column + * @param geoEncoding geo encoding of the geoarrow column + * @returns BinaryGeometryContent + */ +function getBinaryPointsFromChunk(chunk: arrow.Data, geoEncoding: string): BinaryGeometryContent { + const isMultiPoint = geoEncoding === 'geoarrow.multipoint'; + + const pointData = isMultiPoint ? chunk.children[0] : chunk; + const coordData = pointData.children[0]; + + const nDim = pointData.stride; + const flatCoordinateArray = coordData.values; + + // geometryIndices is not needed for point + const geometryIndicies = new Uint16Array(0); + // geomOffset is not needed for point + const geomOffset = new Int32Array(0); + + const numOfVertices = flatCoordinateArray.length / nDim; + const featureIds = new Uint32Array(numOfVertices); + for (let i = 0; i < chunk.length; i++) { + featureIds[i] = i; + } + + return { + featureIds, + flatCoordinateArray, + nDim, + geomOffset, + geometryIndicies + }; +} diff --git a/modules/arrow/src/geoarrow/convert-geoarrow-to-geojson.ts b/modules/arrow/src/geoarrow/convert-geoarrow-to-geojson.ts new file mode 100644 index 0000000000..2855823d8e --- /dev/null +++ b/modules/arrow/src/geoarrow/convert-geoarrow-to-geojson.ts @@ -0,0 +1,192 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import * as arrow from 'apache-arrow'; +import { + Feature, + MultiPolygon, + Position, + Polygon, + MultiPoint, + Point, + MultiLineString, + LineString +} from '@loaders.gl/schema'; +import type {GeoArrowEncoding} from '@loaders.gl/gis'; + +type RawArrowFeature = { + encoding?: GeoArrowEncoding; + data: any; +}; + +/** + * parse geometry from arrow data that is returned from processArrowData() + * NOTE: this function could be duplicated with the binaryToFeature() in deck.gl, + * it is currently only used for picking because currently deck.gl returns only the index of the feature + * So the following functions could be deprecated once deck.gl returns the feature directly for binary geojson layer + * + * @param rawData the raw geometry data returned from processArrowData, which is an object with two properties: encoding and data + * @see processArrowData + * @returns Feature or null + */ +export function parseGeometryFromArrow(rawData: RawArrowFeature): Feature | null { + const encoding = rawData.encoding?.toLowerCase(); + const data = rawData.data; + if (!encoding || !data) { + return null; + } + + let geometry; + + switch (encoding) { + case 'geoarrow.multipolygon': + geometry = arrowMultiPolygonToFeature(data); + break; + case 'geoarrow.polygon': + geometry = arrowPolygonToFeature(data); + break; + case 'geoarrow.multipoint': + geometry = arrowMultiPointToFeature(data); + break; + case 'geoarrow.point': + geometry = arrowPointToFeature(data); + break; + case 'geoarrow.multilinestring': + geometry = arrowMultiLineStringToFeature(data); + break; + case 'geoarrow.linestring': + geometry = arrowLineStringToFeature(data); + break; + default: { + throw Error(`GeoArrow encoding not supported ${encoding}`); + } + } + return { + type: 'Feature', + geometry, + properties: {} + }; +} + +/** + * convert Arrow MultiPolygon to geojson Feature + */ +function arrowMultiPolygonToFeature(arrowMultiPolygon: arrow.Vector): MultiPolygon { + const multiPolygon: Position[][][] = []; + for (let m = 0; m < arrowMultiPolygon.length; m++) { + const arrowPolygon = arrowMultiPolygon.get(m); + const polygon: Position[][] = []; + for (let i = 0; arrowPolygon && i < arrowPolygon?.length; i++) { + const arrowRing = arrowPolygon?.get(i); + const ring: Position[] = []; + for (let j = 0; arrowRing && j < arrowRing.length; j++) { + const arrowCoord = arrowRing.get(j); + const coord: Position = Array.from(arrowCoord); + ring.push(coord); + } + polygon.push(ring); + } + multiPolygon.push(polygon); + } + const geometry: MultiPolygon = { + type: 'MultiPolygon', + coordinates: multiPolygon + }; + return geometry; +} + +/** + * convert Arrow Polygon to geojson Feature + */ +function arrowPolygonToFeature(arrowPolygon: arrow.Vector): Polygon { + const polygon: Position[][] = []; + for (let i = 0; arrowPolygon && i < arrowPolygon.length; i++) { + const arrowRing = arrowPolygon.get(i); + const ring: Position[] = []; + for (let j = 0; arrowRing && j < arrowRing.length; j++) { + const arrowCoord = arrowRing.get(j); + const coords: Position = Array.from(arrowCoord); + ring.push(coords); + } + polygon.push(ring); + } + const geometry: Polygon = { + type: 'Polygon', + coordinates: polygon + }; + return geometry; +} + +/** + * convert Arrow MultiPoint to geojson MultiPoint + */ +function arrowMultiPointToFeature(arrowMultiPoint: arrow.Vector): MultiPoint { + const multiPoint: Position[] = []; + for (let i = 0; arrowMultiPoint && i < arrowMultiPoint.length; i++) { + const arrowPoint = arrowMultiPoint.get(i); + if (arrowPoint) { + const coord: Position = Array.from(arrowPoint); + multiPoint.push(coord); + } + } + const geometry: MultiPoint = { + type: 'MultiPoint', + coordinates: multiPoint + }; + return geometry; +} + +/** + * convert Arrow Point to geojson Point + */ +function arrowPointToFeature(arrowPoint: arrow.Vector): Point { + const point: Position = Array.from(arrowPoint); + const geometry: Point = { + type: 'Point', + coordinates: point + }; + return geometry; +} + +/** + * convert Arrow MultiLineString to geojson MultiLineString + */ +function arrowMultiLineStringToFeature(arrowMultiLineString: arrow.Vector): MultiLineString { + const multiLineString: Position[][] = []; + for (let i = 0; arrowMultiLineString && i < arrowMultiLineString.length; i++) { + const arrowLineString = arrowMultiLineString.get(i); + const lineString: Position[] = []; + for (let j = 0; arrowLineString && j < arrowLineString.length; j++) { + const arrowCoord = arrowLineString.get(j); + if (arrowCoord) { + const coords: Position = Array.from(arrowCoord); + lineString.push(coords); + } + } + multiLineString.push(lineString); + } + const geometry: MultiLineString = { + type: 'MultiLineString', + coordinates: multiLineString + }; + return geometry; +} + +/** + * convert Arrow LineString to geojson LineString + */ +function arrowLineStringToFeature(arrowLineString: arrow.Vector): LineString { + const lineString: Position[] = []; + for (let i = 0; arrowLineString && i < arrowLineString.length; i++) { + const arrowCoord = arrowLineString.get(i); + if (arrowCoord) { + const coords: Position = Array.from(arrowCoord); + lineString.push(coords); + } + } + const geometry: LineString = { + type: 'LineString', + coordinates: lineString + }; + return geometry; +} diff --git a/modules/arrow/src/geoarrow/get-arrow-bounds.ts b/modules/arrow/src/geoarrow/get-arrow-bounds.ts new file mode 100644 index 0000000000..9428b2ad8b --- /dev/null +++ b/modules/arrow/src/geoarrow/get-arrow-bounds.ts @@ -0,0 +1,40 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +/** + * Update bounds from geoarrow sample data + * + * @param flatCoords the flattend coordinates array from one chunk of geoarrow column + * @param nDim the number of dimensions of the coordinates + * @param bounds the bounds to be updated + * @param sampleSize how many samples to be used to update the bounds, default is 1000 per chunk + * @returns the updated bounds + */ +export function updateBoundsFromGeoArrowSamples( + flatCoords: Float64Array, + nDim: number, + bounds: [number, number, number, number], + sampleSize: number = 100 +): [number, number, number, number] { + const numberOfFeatures = flatCoords.length / nDim; + const sampleStep = Math.max(Math.floor(numberOfFeatures / sampleSize), 1); + + const newBounds: [number, number, number, number] = [...bounds]; + for (let i = 0; i < numberOfFeatures; i += sampleStep) { + const lng = flatCoords[i * nDim]; + const lat = flatCoords[i * nDim + 1]; + if (lng < newBounds[0]) { + newBounds[0] = lng; + } + if (lat < newBounds[1]) { + newBounds[1] = lat; + } + if (lng > newBounds[2]) { + newBounds[2] = lng; + } + if (lat > newBounds[3]) { + newBounds[3] = lat; + } + } + return newBounds; +} diff --git a/modules/arrow/src/index.ts b/modules/arrow/src/index.ts index b0568c69de..0378cbfdca 100644 --- a/modules/arrow/src/index.ts +++ b/modules/arrow/src/index.ts @@ -1,16 +1,40 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {LoaderWithParser} from '@loaders.gl/loader-utils'; import type {ArrowLoaderOptions} from './arrow-loader'; +import type {ArrowTableBatch, ColumnarTable, ObjectRowTable} from '@loaders.gl/schema'; +import type {ArrowTable} from './lib/arrow-table'; + +import {TableBatchBuilder} from '@loaders.gl/schema'; import {ArrowLoader as ArrowWorkerLoader} from './arrow-loader'; import parseSync from './lib/parse-arrow-sync'; import {parseArrowInBatches} from './lib/parse-arrow-in-batches'; -import {TableBatchBuilder} from '@loaders.gl/schema'; -import ArrowTableBatchAggregator from './lib/arrow-table-batch'; +import {ArrowTableBatchAggregator} from './lib/arrow-table-batch'; // Make the ArrowBatch type available TableBatchBuilder.ArrowBatch = ArrowTableBatchAggregator; +// TYPES + +export {getArrowType} from './schema/arrow-type-utils'; + +// SCHEMA + +export { + serializeArrowSchema, + deserializeArrowSchema, + serializeArrowMetadata, + deserializeArrowMetadata, + serializeArrowField, + deserializeArrowField, + serializeArrowType, + deserializeArrowType +} from './schema/convert-arrow-schema'; + // Types +export type {ArrowTable, ArrowTableBatch} from './lib/arrow-table'; export {VECTOR_TYPES} from './types'; // Arrow writer @@ -23,7 +47,11 @@ export type {ArrowLoaderOptions}; export {ArrowWorkerLoader}; /** ArrowJS table loader */ -export const ArrowLoader: LoaderWithParser = { +export const ArrowLoader: LoaderWithParser< + ArrowTable | ColumnarTable | ObjectRowTable, + ArrowTableBatch, + ArrowLoaderOptions +> = { ...ArrowWorkerLoader, parse: async (arraybuffer: ArrayBuffer, options?: ArrowLoaderOptions) => parseSync(arraybuffer, options), @@ -31,4 +59,17 @@ export const ArrowLoader: LoaderWithParser = { parseInBatches: parseArrowInBatches }; -export const _typecheckArrowLoader: LoaderWithParser = ArrowLoader; +// Arrow Utils +export type {GeoArrowEncoding} from '@loaders.gl/gis'; +// getGeometryColumnsFromArrowTable, +// getGeoArrowEncoding + +export type {BinaryDataFromGeoArrow} from './geoarrow/convert-geoarrow-to-binary-geometry'; +export { + BINARY_GEOMETRY_TEMPLATE, + getBinaryGeometriesFromArrow +} from './geoarrow/convert-geoarrow-to-binary-geometry'; + +export {parseGeometryFromArrow} from './geoarrow/convert-geoarrow-to-geojson'; + +export {updateBoundsFromGeoArrowSamples} from './geoarrow/get-arrow-bounds'; diff --git a/modules/arrow/src/lib/arrow-table-batch.ts b/modules/arrow/src/lib/arrow-table-batch.ts index 012e817da7..9ad7d4eb14 100644 --- a/modules/arrow/src/lib/arrow-table-batch.ts +++ b/modules/arrow/src/lib/arrow-table-batch.ts @@ -1,18 +1,9 @@ -import type {ArrowTableBatch} from '@loaders.gl/schema'; -import { - Schema, - Field, - RecordBatch, - Struct, - makeVector, - makeData, - Vector, - Float32 -} from 'apache-arrow'; import {ColumnarTableBatchAggregator} from '@loaders.gl/schema'; +import type {ArrowTableBatch} from './arrow-table'; +import * as arrow from 'apache-arrow'; -export default class ArrowTableBatchAggregator extends ColumnarTableBatchAggregator { - arrowSchema: Schema | null; +export class ArrowTableBatchAggregator extends ColumnarTableBatchAggregator { + arrowSchema: arrow.Schema | null; constructor(schema, options) { super(schema, options); @@ -29,10 +20,10 @@ export default class ArrowTableBatchAggregator extends ColumnarTableBatchAggrega const arrowVectors = getArrowVectors(this.arrowSchema, batch.data); // Create the record batch - const recordBatch = new RecordBatch( + const recordBatch = new arrow.RecordBatch( this.arrowSchema, - makeData({ - type: new Struct(this.arrowSchema.fields), + arrow.makeData({ + type: new arrow.Struct(this.arrowSchema.fields), children: arrowVectors.map(({data}) => data[0]) }) ); @@ -40,7 +31,7 @@ export default class ArrowTableBatchAggregator extends ColumnarTableBatchAggrega return { shape: 'arrow-table', batchType: 'data', - data: recordBatch, + data: new arrow.Table([recordBatch]), length: batch.length }; } @@ -50,15 +41,15 @@ export default class ArrowTableBatchAggregator extends ColumnarTableBatchAggrega } // Convert from a simple loaders.gl schema to an Arrow schema -function getArrowSchema(schema): Schema { - const arrowFields: Field[] = []; +function getArrowSchema(schema): arrow.Schema { + const arrowFields: arrow.Field[] = []; for (const key in schema) { const field = schema[key]; if (field.type === Float32Array) { // TODO - just store the original field as metadata? const metadata = new Map(); // field; - // arrow: new Field(name, nullable, metadata) - const arrowField = new Field(field.name, new Float32(), field.nullable, metadata); + // arrow: new arrow.Field(name, nullable, metadata) + const arrowField = new arrow.Field(field.name, new arrow.Float32(), field.nullable, metadata); arrowFields.push(arrowField); } } @@ -66,16 +57,16 @@ function getArrowSchema(schema): Schema { throw new Error('No arrow convertible fields'); } - return new Schema(arrowFields); + return new arrow.Schema(arrowFields); } // Convert from simple loaders.gl arrays to arrow vectors -function getArrowVectors(arrowSchema, data): Vector[] { +function getArrowVectors(arrowSchema, data): arrow.Vector[] { const arrowVectors: any[] = []; for (const field of arrowSchema.fields) { const vector = data[field.name]; if (vector instanceof Float32Array) { - const arrowVector = makeVector(vector); + const arrowVector = arrow.makeVector(vector); arrowVectors.push(arrowVector); } } diff --git a/modules/arrow/src/lib/arrow-table.ts b/modules/arrow/src/lib/arrow-table.ts new file mode 100644 index 0000000000..b64e75a81f --- /dev/null +++ b/modules/arrow/src/lib/arrow-table.ts @@ -0,0 +1,27 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {Batch, Schema} from '@loaders.gl/schema'; +import type * as arrow from 'apache-arrow'; + +/** + * A table organized as an Apache Arrow table + * @note This is a variant of the type from loaders.gl/schema + */ +export type ArrowTable = { + shape: 'arrow-table'; + schema?: Schema; + data: arrow.Table; +}; + +/** + * Batch for a table organized as an Apache Arrow table + * @note This is a variant of the type from loaders.gl/schema + */ +export type ArrowTableBatch = Batch & { + shape: 'arrow-table'; + schemaType?: 'explicit' | 'deduced'; + schema?: Schema; + data: arrow.Table; // ApacheRecordBatch; + length: number; +}; diff --git a/modules/arrow/src/lib/encode-arrow.ts b/modules/arrow/src/lib/encode-arrow.ts index 36c6c306a1..3a5c1f7f7a 100644 --- a/modules/arrow/src/lib/encode-arrow.ts +++ b/modules/arrow/src/lib/encode-arrow.ts @@ -1,4 +1,4 @@ -import {Table, Vector, tableToIPC, vectorFromArray} from 'apache-arrow'; +import * as arrow from 'apache-arrow'; import {AnyArrayType, VECTOR_TYPES} from '../types'; export type ColumnarTable = { @@ -15,28 +15,28 @@ export type ColumnarTable = { * @returns - encoded ArrayBuffer */ export function encodeArrowSync(data: ColumnarTable): ArrayBuffer { - const vectors: Record = {}; + const vectors: Record = {}; for (const arrayData of data) { const arrayVector = createVector(arrayData.array, arrayData.type); vectors[arrayData.name] = arrayVector; } - const table = new Table(vectors); - const arrowBuffer = tableToIPC(table); + const table = new arrow.Table(vectors); + const arrowBuffer = arrow.tableToIPC(table); return arrowBuffer; } /** - * Create Arrow Vector from given data and vector type + * Create Arrow arrow.Vector from given data and vector type * @param array {import('../types').AnyArrayType} - columns data * @param type {number} - the writer options * @return a vector of one of vector's types defined in the Apache Arrow library */ -function createVector(array, type): Vector { +function createVector(array, type): arrow.Vector { switch (type) { case VECTOR_TYPES.DATE: - return vectorFromArray(array); + return arrow.vectorFromArray(array); case VECTOR_TYPES.FLOAT: default: - return vectorFromArray(array); + return arrow.vectorFromArray(array); } } diff --git a/modules/arrow/src/lib/parse-arrow-in-batches.ts b/modules/arrow/src/lib/parse-arrow-in-batches.ts index 765b12f35c..5d941ec044 100644 --- a/modules/arrow/src/lib/parse-arrow-in-batches.ts +++ b/modules/arrow/src/lib/parse-arrow-in-batches.ts @@ -1,13 +1,14 @@ // TODO - this import defeats the sophisticated typescript checking in ArrowJS -import {RecordBatchReader} from 'apache-arrow'; +import type {ArrowTableBatch} from './arrow-table'; +import * as arrow from 'apache-arrow'; // import {isIterable} from '@loaders.gl/core'; /** */ export function parseArrowInBatches( asyncIterator: AsyncIterable | Iterable -): AsyncIterable { - // Creates the appropriate RecordBatchReader subclasses from the input +): AsyncIterable { + // Creates the appropriate arrow.RecordBatchReader subclasses from the input // This will also close the underlying source in case of early termination or errors // As an optimization, return a non-async iterator @@ -25,25 +26,32 @@ export function parseArrowInBatches( } */ - async function* makeArrowAsyncIterator() { - const readers = RecordBatchReader.readAll(asyncIterator); + async function* makeArrowAsyncIterator(): AsyncIterator { + // @ts-ignore + const readers = arrow.RecordBatchReader.readAll(asyncIterator); for await (const reader of readers) { - for await (const batch of reader) { - yield processBatch(batch); + for await (const recordBatch of reader) { + const arrowTabledBatch: ArrowTableBatch = { + shape: 'arrow-table', + batchType: 'data', + data: new arrow.Table([recordBatch]), + length: recordBatch.data.length + }; + // processBatch(recordBatch); + yield arrowTabledBatch; } break; // only processing one stream of batches } } - return makeArrowAsyncIterator(); -} -function processBatch(batch) { - const values = { - metadata: batch.schema.metadata, - length: batch.length - }; - batch.schema.fields.forEach(({name}, index) => { - values[name] = batch.getChildAt(index).toArray(); - }); - return values; + return makeArrowAsyncIterator() as any; // as AsyncIterator; } + +// function processBatch(batch: RecordBatch): ArrowTableBatch { +// const values = {}; +// batch.schema.fields.forEach(({name}, index) => { +// values[name] = batch.getChildAt(index)?.toArray(); +// }); +// return { +// }; +// } diff --git a/modules/arrow/src/lib/parse-arrow-sync.ts b/modules/arrow/src/lib/parse-arrow-sync.ts index 151767c15d..a21f66e868 100644 --- a/modules/arrow/src/lib/parse-arrow-sync.ts +++ b/modules/arrow/src/lib/parse-arrow-sync.ts @@ -1,46 +1,35 @@ +import type {ColumnarTable, ObjectRowTable} from '@loaders.gl/schema'; +import type {ArrowTable} from './arrow-table'; +import {convertTable} from '@loaders.gl/schema'; +import * as arrow from 'apache-arrow'; import type {ArrowLoaderOptions} from '../arrow-loader'; -import {tableFromIPC} from 'apache-arrow'; +import { + convertApacheArrowToArrowTable, + convertArrowToColumnarTable +} from '../tables/convert-arrow-to-table'; // Parses arrow to a columnar table -export default function parseArrowSync(arrayBuffer, options?: ArrowLoaderOptions) { - const arrowTable = tableFromIPC([new Uint8Array(arrayBuffer)]); +export default function parseArrowSync( + arrayBuffer, + options?: ArrowLoaderOptions +): ArrowTable | ColumnarTable | ObjectRowTable { + const apacheArrowTable = arrow.tableFromIPC([new Uint8Array(arrayBuffer)]); + const arrowTable = convertApacheArrowToArrowTable(apacheArrowTable); - // Extract columns - - // TODO - avoid calling `getColumn` on columns we are not interested in? - // Add options object? - const columnarTable = {}; - - for (const field of arrowTable.schema.fields) { - // This (is intended to) coalesce all record batches into a single typed array - const arrowColumn = arrowTable.getChild(field.name); - const values = arrowColumn?.toArray(); - columnarTable[field.name] = values; - } - - switch (options?.arrow?.shape) { + const shape = options?.arrow?.shape || 'arrow-table'; + switch (shape) { case 'arrow-table': return arrowTable; - case 'object-row-table': - return convertColumnarToRowFormatTable(columnarTable); + case 'columnar-table': - default: - return columnarTable; - } -} + return convertArrowToColumnarTable(arrowTable); -function convertColumnarToRowFormatTable(columnarTable) { - const tableKeys = Object.keys(columnarTable); - const tableRowsCount = columnarTable[tableKeys[0]].length; - const rowFormatTable: {}[] = []; + case 'object-row-table': + const columnarTable = convertArrowToColumnarTable(arrowTable); + return convertTable(columnarTable, 'object-row-table'); - for (let index = 0; index < tableRowsCount; index++) { - const tableItem = {}; - for (let keyIndex = 0; keyIndex < tableKeys.length; keyIndex++) { - const fieldName = tableKeys[keyIndex]; - tableItem[fieldName] = columnarTable[fieldName][index]; - } - rowFormatTable.push(tableItem); + default: + // TODO + throw new Error(shape); } - return rowFormatTable; } diff --git a/modules/schema/src/lib/table/arrow/arrow-type-utils.ts b/modules/arrow/src/schema/arrow-type-utils.ts similarity index 59% rename from modules/schema/src/lib/table/arrow/arrow-type-utils.ts rename to modules/arrow/src/schema/arrow-type-utils.ts index 45cb1f079d..72aa27b4b1 100644 --- a/modules/schema/src/lib/table/arrow/arrow-type-utils.ts +++ b/modules/arrow/src/schema/arrow-type-utils.ts @@ -1,45 +1,28 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import type {TypedArray} from '../../../types/types'; -import { - DataType, - Float32, - Float64, - Int16, - Int32, - Int8, - Uint16, - Uint32, - Uint8 - // Int8Vector, - // Uint8Vector, - // Int16Vector, - // Uint16Vector, - // Int32Vector, - // Uint32Vector, - // Float32Vector, - // Float64Vector -} from 'apache-arrow'; -// import {AbstractVector} from 'apache-arrow/vector'; +import type {TypedArray} from '@loaders.gl/schema'; +import * as arrow from 'apache-arrow'; -export function getArrowType(array: TypedArray): DataType { +/** Return an Apache Arrow Type instance that corresponds to the type of the elements in the supplied Typed Array */ +export function getArrowType(array: TypedArray): arrow.DataType { switch (array.constructor) { case Int8Array: - return new Int8(); + return new arrow.Int8(); case Uint8Array: - return new Uint8(); + return new arrow.Uint8(); case Int16Array: - return new Int16(); + return new arrow.Int16(); case Uint16Array: - return new Uint16(); + return new arrow.Uint16(); case Int32Array: - return new Int32(); + return new arrow.Int32(); case Uint32Array: - return new Uint32(); + return new arrow.Uint32(); case Float32Array: - return new Float32(); + return new arrow.Float32(); case Float64Array: - return new Float64(); + return new arrow.Float64(); default: throw new Error('array type not supported'); } diff --git a/modules/arrow/src/schema/convert-arrow-schema.ts b/modules/arrow/src/schema/convert-arrow-schema.ts new file mode 100644 index 0000000000..dd73ec43c7 --- /dev/null +++ b/modules/arrow/src/schema/convert-arrow-schema.ts @@ -0,0 +1,290 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {DataType, Field, Schema, SchemaMetadata} from '@loaders.gl/schema'; +import * as arrow from 'apache-arrow'; + +/** Convert Apache Arrow Schema (class instance) to a serialized Schema (plain data) */ +export function serializeArrowSchema(arrowSchema: arrow.Schema): Schema { + return { + fields: arrowSchema.fields.map((arrowField) => serializeArrowField(arrowField)), + metadata: serializeArrowMetadata(arrowSchema.metadata) + }; +} + +/** Convert a serialized Schema (plain data) to an Apache Arrow Schema (class instance) */ +export function deserializeArrowSchema(schema: Schema): arrow.Schema { + return new arrow.Schema( + schema.fields.map((field) => deserializeArrowField(field)), + deserializeArrowMetadata(schema.metadata) + ); +} + +/** Convert Apache Arrow Schema metadata (Map) to serialized metadata (Record */ +export function serializeArrowMetadata(arrowMetadata: Map): SchemaMetadata { + return Object.fromEntries(arrowMetadata); +} + +/** Convert serialized metadata (Record to Apache Arrow Schema metadata (Map) to */ +export function deserializeArrowMetadata(metadata?: SchemaMetadata): Map { + return metadata ? new Map(Object.entries(metadata)) : new Map(); +} + +/** Convert Apache Arrow Field (class instance) to serialized Field (plain data) */ +export function serializeArrowField(field: arrow.Field): Field { + return { + name: field.name, + type: serializeArrowType(field.type), + nullable: field.nullable, + metadata: serializeArrowMetadata(field.metadata) + }; +} + +/** Convert a serialized Field (plain data) to an Apache Arrow Field (class instance)*/ +export function deserializeArrowField(field: Field): arrow.Field { + return new arrow.Field( + field.name, + deserializeArrowType(field.type), + field.nullable, + deserializeArrowMetadata(field.metadata) + ); +} + +/** Converts a serializable loaders.gl data type to hydrated arrow data type */ +// eslint-disable-next-line complexity +export function serializeArrowType(arrowType: arrow.DataType): DataType { + switch (arrowType.constructor) { + case arrow.Null: + return 'null'; + case arrow.Binary: + return 'binary'; + case arrow.Bool: + return 'bool'; + case arrow.Int: + const intType = arrowType as arrow.Int; + return `${intType.isSigned ? 'u' : ''}int${intType.bitWidth}`; + case arrow.Int8: + return 'int8'; + case arrow.Int16: + return 'int16'; + case arrow.Int32: + return 'int32'; + case arrow.Int64: + return 'int64'; + case arrow.Uint8: + return 'uint8'; + case arrow.Uint16: + return 'uint16'; + case arrow.Uint32: + return 'uint32'; + case arrow.Uint64: + return 'uint64'; + case arrow.Float: + const precision = (arrowType as arrow.Float).precision; + // return `float(precision + 1) * 16`; + switch (precision) { + case arrow.Precision.HALF: + return 'float16'; + case arrow.Precision.SINGLE: + return 'float32'; + case arrow.Precision.DOUBLE: + return 'float64'; + default: + return 'float16'; + } + case arrow.Float16: + return 'float16'; + case arrow.Float32: + return 'float32'; + case arrow.Float64: + return 'float64'; + case arrow.Utf8: + return 'utf8'; + case arrow.Decimal: + const decimal = arrowType as arrow.Decimal; + return { + type: 'decimal', + bitWidth: decimal.bitWidth, + precision: decimal.precision, + scale: decimal.scale + }; + case arrow.Date_: + const dateUnit = (arrowType as arrow.Date_).unit; + return dateUnit === arrow.DateUnit.DAY ? 'date-day' : 'date-millisecond'; + case arrow.DateDay: + return 'date-day'; + case arrow.DateMillisecond: + return 'date-millisecond'; + case arrow.Time: + const timeUnit = (arrowType as arrow.Time).unit; + switch (timeUnit) { + case arrow.TimeUnit.SECOND: + return 'time-second'; + case arrow.TimeUnit.MILLISECOND: + return 'time-millisecond'; + case arrow.TimeUnit.MICROSECOND: + return 'time-microsecond'; + case arrow.TimeUnit.NANOSECOND: + return 'time-nanosecond'; + default: + return 'time-second'; + } + case arrow.TimeMillisecond: + return 'time-millisecond'; + case arrow.TimeSecond: + return 'time-second'; + case arrow.TimeMicrosecond: + return 'time-microsecond'; + case arrow.TimeNanosecond: + return 'time-nanosecond'; + case arrow.Timestamp: + const timeStampUnit = (arrowType as arrow.Timestamp).unit; + switch (timeStampUnit) { + case arrow.TimeUnit.SECOND: + return 'timestamp-second'; + case arrow.TimeUnit.MILLISECOND: + return 'timestamp-millisecond'; + case arrow.TimeUnit.MICROSECOND: + return 'timestamp-microsecond'; + case arrow.TimeUnit.NANOSECOND: + return 'timestamp-nanosecond'; + default: + return 'timestamp-second'; + } + case arrow.TimestampSecond: + return 'timestamp-second'; + case arrow.TimestampMillisecond: + return 'timestamp-millisecond'; + case arrow.TimestampMicrosecond: + return 'timestamp-microsecond'; + case arrow.TimestampNanosecond: + return 'timestamp-nanosecond'; + case arrow.Interval: + const intervalUnit = (arrowType as arrow.Interval).unit; + switch (intervalUnit) { + case arrow.IntervalUnit.DAY_TIME: + return 'interval-daytime'; + case arrow.IntervalUnit.YEAR_MONTH: + return 'interval-yearmonth'; + default: + return 'interval-daytime'; + } + case arrow.IntervalDayTime: + return 'interval-daytime'; + case arrow.IntervalYearMonth: + return 'interval-yearmonth'; + case arrow.Map_: + const mapType = arrowType as arrow.Map_; + return { + type: 'map', + keysSorted: mapType.keysSorted, + children: mapType.children.map((arrowField) => serializeArrowField(arrowField)) + }; + case arrow.List: + const listType = arrowType as arrow.List; + const listField = listType.valueField; + return { + type: 'list', + children: [serializeArrowField(listField)] + }; + case arrow.FixedSizeList: + const fixedSizeList = arrowType as arrow.FixedSizeList; + return { + type: 'fixed-size-list', + listSize: fixedSizeList.listSize, + children: [serializeArrowField(fixedSizeList.children[0])] + }; + case arrow.Struct: + const structType = arrowType as arrow.Struct; + return { + type: 'struct', + children: structType.children.map((arrowField) => serializeArrowField(arrowField)) + }; + default: + throw new Error(`arrow type not supported: ${arrowType.constructor.name}`); + } +} + +/** Converts a serializable loaders.gl data type to hydrated arrow data type */ +// eslint-disable-next-line complexity +export function deserializeArrowType(dataType: DataType): arrow.DataType { + if (typeof dataType === 'object') { + switch (dataType.type) { + case 'decimal': + return new arrow.Decimal(dataType.precision, dataType.scale, dataType.bitWidth); + case 'map': + let children = dataType.children.map((arrowField) => deserializeArrowField(arrowField)); + return new arrow.Map_(children as any, dataType.keysSorted); + case 'list': + const field = deserializeArrowField(dataType.children[0]); + return new arrow.List(field); + case 'fixed-size-list': + const child = deserializeArrowField(dataType.children[0]); + return new arrow.FixedSizeList(dataType.listSize, child); + case 'struct': + children = dataType.children.map((arrowField) => deserializeArrowField(arrowField)); + return new arrow.Struct(children); + default: + throw new Error('array type not supported'); + } + } + + switch (dataType) { + case 'null': + return new arrow.Null(); + case 'binary': + return new arrow.Binary(); + case 'bool': + return new arrow.Bool(); + case 'int8': + return new arrow.Int8(); + case 'int16': + return new arrow.Int16(); + case 'int32': + return new arrow.Int32(); + case 'int64': + return new arrow.Int64(); + case 'uint8': + return new arrow.Uint8(); + case 'uint16': + return new arrow.Uint16(); + case 'uint32': + return new arrow.Uint32(); + case 'uint64': + return new arrow.Uint64(); + case 'float16': + return new arrow.Float16(); + case 'float32': + return new arrow.Float32(); + case 'float64': + return new arrow.Float64(); + case 'utf8': + return new arrow.Utf8(); + case 'date-day': + return new arrow.DateDay(); + case 'date-millisecond': + return new arrow.DateMillisecond(); + case 'time-second': + return new arrow.TimeSecond(); + case 'time-millisecond': + return new arrow.TimeMillisecond(); + case 'time-microsecond': + return new arrow.TimeMicrosecond(); + case 'time-nanosecond': + return new arrow.TimeNanosecond(); + case 'timestamp-second': + return new arrow.TimestampSecond(); + case 'timestamp-millisecond': + return new arrow.TimestampMillisecond(); + case 'timestamp-microsecond': + return new arrow.TimestampMicrosecond(); + case 'timestamp-nanosecond': + return new arrow.TimestampNanosecond(); + case 'interval-daytime': + return new arrow.IntervalDayTime(); + case 'interval-yearmonth': + return new arrow.IntervalYearMonth(); + default: + throw new Error('array type not supported'); + } +} diff --git a/modules/arrow/src/tables/convert-arrow-to-table.ts b/modules/arrow/src/tables/convert-arrow-to-table.ts new file mode 100644 index 0000000000..492e4e613e --- /dev/null +++ b/modules/arrow/src/tables/convert-arrow-to-table.ts @@ -0,0 +1,68 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {ColumnarTable, ObjectRowTable} from '@loaders.gl/schema'; +import type * as arrow from 'apache-arrow'; +import type {ArrowTable} from '../lib/arrow-table'; + +/** + * Wrap an apache arrow table in a loaders.gl table wrapper. + * From this additional conversions are available. + * @param arrowTable + * @returns + */ +export function convertApacheArrowToArrowTable(arrowTable: arrow.Table): ArrowTable { + return { + shape: 'arrow-table', + data: arrowTable + }; +} + +/** + * Convert an Apache Arrow table to a ColumnarTable + * @note Currently does not convert schema + */ +export function convertArrowToColumnarTable(table: ArrowTable): ColumnarTable { + // TODO - avoid calling `getColumn` on columns we are not interested in? + // Add options object? + + const arrowTable = table.data; + const columnarTable = {}; + + for (const field of arrowTable.schema.fields) { + // This (is intended to) coalesce all record batches into a single typed array + const arrowColumn = arrowTable.getChild(field.name); + const values = arrowColumn?.toArray(); + columnarTable[field.name] = values; + } + + return { + shape: 'columnar-table', + data: columnarTable + }; +} + +/** + * + * @note - should be part of schema module + */ +export function convertColumnarToRowFormatTable(columnarTable: ColumnarTable): ObjectRowTable { + const tableKeys = Object.keys(columnarTable); + const tableRowsCount = columnarTable[tableKeys[0]].length; + + const rowFormatTable: {}[] = []; + + for (let index = 0; index < tableRowsCount; index++) { + const tableItem = {}; + for (let keyIndex = 0; keyIndex < tableKeys.length; keyIndex++) { + const fieldName = tableKeys[keyIndex]; + tableItem[fieldName] = columnarTable[fieldName][index]; + } + rowFormatTable.push(tableItem); + } + + return { + shape: 'object-row-table', + data: rowFormatTable + }; +} diff --git a/modules/schema/src/lib/table/arrow/convert-table-to-arrow.ts b/modules/arrow/src/tables/convert-table-to-arrow.ts similarity index 98% rename from modules/schema/src/lib/table/arrow/convert-table-to-arrow.ts rename to modules/arrow/src/tables/convert-table-to-arrow.ts index 2135909a55..26ec5a80e1 100644 --- a/modules/schema/src/lib/table/arrow/convert-table-to-arrow.ts +++ b/modules/arrow/src/tables/convert-table-to-arrow.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // import { // Table as ApacheArrowTable, diff --git a/modules/arrow/src/types.ts b/modules/arrow/src/types.ts index f9c203e0a6..eda3750981 100644 --- a/modules/arrow/src/types.ts +++ b/modules/arrow/src/types.ts @@ -1,12 +1,7 @@ -type TypedIntArray = - | Int8Array - | Uint8Array - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Int32Array - | Uint32Array; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +type TypedIntArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array; type TypedFloatArray = Float32Array | Float64Array; diff --git a/modules/arrow/src/workers/arrow-worker.ts b/modules/arrow/src/workers/arrow-worker.ts index ef229f3e84..65dbc761c8 100644 --- a/modules/arrow/src/workers/arrow-worker.ts +++ b/modules/arrow/src/workers/arrow-worker.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import {createLoaderWorker} from '@loaders.gl/loader-utils'; import {ArrowLoader} from '../index'; diff --git a/modules/arrow/test/arrow-loader.spec.js b/modules/arrow/test/arrow-loader.spec.ts similarity index 66% rename from modules/arrow/test/arrow-loader.spec.js rename to modules/arrow/test/arrow-loader.spec.ts index 23f3f49fa7..d25bf13b0d 100644 --- a/modules/arrow/test/arrow-loader.spec.js +++ b/modules/arrow/test/arrow-loader.spec.ts @@ -1,6 +1,11 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import test from 'tape-promise/tape'; import {validateLoader} from 'test/common/conformance'; +import * as fs from 'fs'; + import {ArrowLoader} from '@loaders.gl/arrow'; import {isBrowser, makeIterator, resolvePath} from '@loaders.gl/core'; import {setLoaderOptions, fetchFile, parse, parseInBatches} from '@loaders.gl/core'; @@ -25,11 +30,14 @@ test('ArrowLoader#loader conformance', (t) => { }); test('ArrowLoader#parseSync(simple.arrow)', async (t) => { - const columns = await parse(fetchFile(ARROW_SIMPLE), ArrowLoader, {worker: false}); + const arrowTable = await parse(fetchFile(ARROW_SIMPLE), ArrowLoader, {worker: false}); // Check loader specific results - t.ok(columns.bar, 'bar column loaded'); - t.ok(columns.baz, 'baz column loaded'); - t.ok(columns.foo, 'foo column loaded'); + t.equal(arrowTable.shape, 'columnar-table'); + if (arrowTable.shape === 'columnar-table') { + t.ok(arrowTable.data.bar, 'bar column loaded'); + t.ok(arrowTable.data.baz, 'baz column loaded'); + t.ok(arrowTable.data.foo, 'foo column loaded'); + } t.end(); }); @@ -40,23 +48,32 @@ test('ArrowLoader#parseSync(simple.arrow) type="object-row-table"', async (t) => shape: 'object-row-table' } }); - t.ok(rowFormatTable, 'Row based table loaded'); - t.equal(rowFormatTable.length, 5); - t.deepEqual(rowFormatTable[0], {foo: 1, bar: 1, baz: 'aa'}); + t.equal(rowFormatTable.shape, 'object-row-table'); + if (rowFormatTable.shape === 'object-row-table') { + t.ok(rowFormatTable, 'Row based table loaded'); + t.equal(rowFormatTable.data.length, 5); + t.deepEqual(rowFormatTable.data[0], {foo: 1, bar: 1, baz: 'aa'}); + } t.end(); }); -test('ArrowLoader#parseSync(dictionary.arrow)', async (t) => { - const columns = await parse(fetchFile(ARROW_DICTIONARY), ArrowLoader); +// This table triggers an arrow bug in apache-arrow v12, v13 +// https://github.com/visgl/loaders.gl/pull/2632#issuecomment-1712001480 +// https://github.com/apache/arrow/blob/f1d2fc92f9d898fc067d46a0d032d9b117a2d7fc/js/src/ipc/metadata/message.ts#L389 +test.skip('ArrowLoader#parseSync(dictionary.arrow)', async (t) => { + const columnarTable = await parse(fetchFile(ARROW_DICTIONARY), ArrowLoader); // Check loader specific results - t.ok(columns['example-csv'], 'example-csv loaded'); + t.ok(columnarTable.data['example-csv'], 'example-csv loaded'); t.end(); }); test('ArrowLoader#parse(fetchFile(struct).arrow)', async (t) => { const columns = await parse(fetchFile(ARROW_STRUCT), ArrowLoader); // Check loader specific results - t.ok(columns.struct_nullable, 'struct_nullable loaded'); + t.equal(columns.shape, 'columnar-table'); + if (columns.shape === 'columnar-table') { + t.ok(columns.data.struct_nullable, 'struct_nullable loaded'); + } t.end(); }); @@ -91,7 +108,6 @@ test('ArrowLoader#parseInBatches(Stream)', async (t) => { t.end(); return; } - const fs = require('fs'); const stream = fs.createReadStream(resolvePath(ARROW_BIOGRID_NODES)); const asyncIterator = await parseInBatches(makeIterator(stream), ArrowLoader); for await (const batch of asyncIterator) { diff --git a/modules/arrow/test/arrow-writer.spec.js b/modules/arrow/test/arrow-writer.spec.ts similarity index 79% rename from modules/arrow/test/arrow-writer.spec.js rename to modules/arrow/test/arrow-writer.spec.ts index d512da3734..7f801a97dd 100644 --- a/modules/arrow/test/arrow-writer.spec.js +++ b/modules/arrow/test/arrow-writer.spec.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import test from 'tape-promise/tape'; import {validateWriter} from 'test/common/conformance'; @@ -31,10 +34,10 @@ test('ArrowWriter#encode', async (t) => { const table = parseSync(arrayBuffer, ArrowLoader); t.ok(table); - t.ok(table.date); - t.ok(table.precipitation); - t.equals(table.date.length, LENGTH); - t.equals(table.precipitation.length, LENGTH); - + t.equals(table.shape, 'columnar-table'); + if (table.shape === 'columnar-table') { + t.ok(table.data.precipitation); + t.equals(table.data.precipitation.length, LENGTH); + } t.end(); }); diff --git a/modules/arrow/test/data/line.arrow b/modules/arrow/test/data/line.arrow new file mode 100644 index 0000000000..2db7920191 Binary files /dev/null and b/modules/arrow/test/data/line.arrow differ diff --git a/modules/arrow/test/data/multiline.arrow b/modules/arrow/test/data/multiline.arrow new file mode 100644 index 0000000000..2d5728a465 Binary files /dev/null and b/modules/arrow/test/data/multiline.arrow differ diff --git a/modules/arrow/test/data/multipoint.arrow b/modules/arrow/test/data/multipoint.arrow new file mode 100644 index 0000000000..7123a43432 Binary files /dev/null and b/modules/arrow/test/data/multipoint.arrow differ diff --git a/modules/arrow/test/data/multipolygon.arrow b/modules/arrow/test/data/multipolygon.arrow new file mode 100644 index 0000000000..b51e678082 Binary files /dev/null and b/modules/arrow/test/data/multipolygon.arrow differ diff --git a/modules/arrow/test/data/multipolygon_hole.arrow b/modules/arrow/test/data/multipolygon_hole.arrow new file mode 100644 index 0000000000..1c7048ed4a Binary files /dev/null and b/modules/arrow/test/data/multipolygon_hole.arrow differ diff --git a/modules/arrow/test/data/point.arrow b/modules/arrow/test/data/point.arrow new file mode 100644 index 0000000000..0b1835023b Binary files /dev/null and b/modules/arrow/test/data/point.arrow differ diff --git a/modules/arrow/test/data/polygon.arrow b/modules/arrow/test/data/polygon.arrow new file mode 100644 index 0000000000..01a963978a Binary files /dev/null and b/modules/arrow/test/data/polygon.arrow differ diff --git a/modules/arrow/test/geoarrow/convert-geoarrow-to-binary-geometry.spec.ts b/modules/arrow/test/geoarrow/convert-geoarrow-to-binary-geometry.spec.ts new file mode 100644 index 0000000000..5d095a595a --- /dev/null +++ b/modules/arrow/test/geoarrow/convert-geoarrow-to-binary-geometry.spec.ts @@ -0,0 +1,257 @@ +import test, {Test} from 'tape-promise/tape'; + +import {Table as ApacheArrowTable} from 'apache-arrow'; +import {getGeometryColumnsFromSchema} from '@loaders.gl/gis'; +import {fetchFile, parse} from '@loaders.gl/core'; +import { + BINARY_GEOMETRY_TEMPLATE, + ArrowLoader, + getBinaryGeometriesFromArrow, + serializeArrowSchema +} from '@loaders.gl/arrow'; + +import { + POINT_ARROW_FILE, + MULTIPOINT_ARROW_FILE, + LINE_ARROW_FILE, + MULTILINE_ARROW_FILE, + POLYGON_ARROW_FILE, + MULTIPOLYGON_ARROW_FILE +} from './convert-geoarrow-to-geojson.spec'; + +const expectedPointBinaryGeometry = { + binaryGeometries: [ + { + shape: 'binary-feature-collection', + points: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Point', + globalFeatureIds: {value: new Uint32Array([0]), size: 1}, + positions: {value: new Float64Array([1, 1]), size: 2}, + properties: [{index: 0}], + featureIds: {value: new Uint32Array([0]), size: 1} + }, + lines: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'LineString', + pathIndices: {value: new Uint16Array(0), size: 1} + }, + polygons: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Polygon', + polygonIndices: {value: new Uint16Array(0), size: 1}, + primitivePolygonIndices: {value: new Uint16Array(0), size: 1} + } + } + ], + bounds: [1, 1, 1, 1], + featureTypes: {polygon: false, point: true, line: false} +}; + +const expectedMultiPointBinaryGeometry = { + binaryGeometries: [ + { + shape: 'binary-feature-collection', + points: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Point', + globalFeatureIds: {value: new Uint32Array([0, 0]), size: 1}, + positions: {value: new Float64Array([1, 1, 2, 2]), size: 2}, + properties: [{index: 0}], + featureIds: {value: new Uint32Array([0, 0]), size: 1} + }, + lines: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'LineString', + pathIndices: {value: new Uint16Array(0), size: 1} + }, + polygons: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Polygon', + polygonIndices: {value: new Uint16Array(0), size: 1}, + primitivePolygonIndices: {value: new Uint16Array(0), size: 1} + } + } + ], + bounds: [1, 1, 2, 2], + featureTypes: {polygon: false, point: true, line: false} +}; + +const expectedLineBinaryGeometry = { + binaryGeometries: [ + { + shape: 'binary-feature-collection', + points: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Point' + }, + lines: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'LineString', + globalFeatureIds: {value: new Uint32Array([0, 0]), size: 1}, + positions: {value: new Float64Array([0, 0, 1, 1]), size: 2}, + properties: [{index: 0}], + featureIds: {value: new Uint32Array([0, 0]), size: 1}, + pathIndices: {value: new Int32Array([0, 2]), size: 1} + }, + polygons: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Polygon', + polygonIndices: {value: new Uint16Array(0), size: 1}, + primitivePolygonIndices: {value: new Uint16Array(0), size: 1} + } + } + ], + bounds: [0, 0, 1, 1], + featureTypes: {polygon: false, point: false, line: true} +}; + +const expectedMultiLineBinaryGeometry = { + binaryGeometries: [ + { + shape: 'binary-feature-collection', + points: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Point' + }, + lines: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'LineString', + globalFeatureIds: {value: new Uint32Array([0, 0, 0, 0]), size: 1}, + positions: {value: new Float64Array([1, 1, 2, 2, 3, 3, 4, 4]), size: 2}, + properties: [{index: 0}], + featureIds: {value: new Uint32Array([0, 0, 0, 0]), size: 1}, + pathIndices: {value: new Int32Array([0, 2, 4]), size: 1} + }, + polygons: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Polygon', + polygonIndices: {value: new Uint16Array(0), size: 1}, + primitivePolygonIndices: {value: new Uint16Array(0), size: 1} + } + } + ], + bounds: [1, 1, 4, 4], + featureTypes: {polygon: false, point: false, line: true} +}; + +const expectedPolygonBinaryGeometry = { + binaryGeometries: [ + { + shape: 'binary-feature-collection', + points: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Point' + }, + lines: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'LineString', + pathIndices: {value: new Uint16Array(0), size: 1} + }, + polygons: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Polygon', + globalFeatureIds: { + value: new Uint32Array([0, 0, 0, 0]), + size: 1 + }, + positions: { + value: new Float64Array([0, 0, 1, 1, 2, 2, 0, 0]), + size: 2 + }, + properties: [{index: 0}], + featureIds: {value: new Uint32Array([0, 0, 0, 0]), size: 1}, + polygonIndices: {value: new Int32Array([0, 4]), size: 1}, + primitivePolygonIndices: {value: new Int32Array([0, 4]), size: 1} + } + } + ], + bounds: [0, 0, 2, 2], + featureTypes: {polygon: true, point: false, line: false} +}; + +const expectedMultiPolygonBinaryGeometry = { + binaryGeometries: [ + { + shape: 'binary-feature-collection', + points: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Point' + }, + lines: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'LineString', + pathIndices: {value: new Uint16Array(0), size: 1} + }, + polygons: { + ...BINARY_GEOMETRY_TEMPLATE, + type: 'Polygon', + globalFeatureIds: { + value: new Uint32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + size: 1 + }, + positions: { + value: new Float64Array([0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2]), + size: 2 + }, + properties: [{index: 0}], + featureIds: { + value: new Uint32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + size: 1 + }, + polygonIndices: {value: new Int32Array([0, 5, 10]), size: 1}, + primitivePolygonIndices: {value: new Int32Array([0, 5, 10]), size: 1} + } + } + ], + bounds: [0, 0, 3, 3], + featureTypes: {polygon: true, point: false, line: false} +}; + +test('ArrowUtils#getBinaryGeometriesFromArrow', (t) => { + const testCases = [ + [POINT_ARROW_FILE, expectedPointBinaryGeometry], + [MULTIPOINT_ARROW_FILE, expectedMultiPointBinaryGeometry], + [LINE_ARROW_FILE, expectedLineBinaryGeometry], + [MULTILINE_ARROW_FILE, expectedMultiLineBinaryGeometry], + [POLYGON_ARROW_FILE, expectedPolygonBinaryGeometry], + [MULTIPOLYGON_ARROW_FILE, expectedMultiPolygonBinaryGeometry] + ]; + + testCases.forEach((testCase) => { + testGetBinaryGeometriesFromArrow(t, testCase[0], testCase[1]); + }); + + t.end(); +}); + +async function testGetBinaryGeometriesFromArrow( + t: Test, + arrowFile, + expectedBinaryGeometries +): Promise { + const arrowTable = await parse(fetchFile(arrowFile), ArrowLoader, { + worker: false, + arrow: { + shape: 'arrow-table' + } + }); + + t.equal(arrowTable.shape, 'arrow-table'); + + const table = arrowTable.data as ApacheArrowTable; + const geoColumn = table.getChild('geometry'); + t.notEqual(geoColumn, null, 'geoColumn is not null'); + + const schema = serializeArrowSchema(table.schema); + const geometryColumns = getGeometryColumnsFromSchema(schema); + const encoding = geometryColumns.geometry.encoding; + + t.notEqual(encoding, undefined, 'encoding is not undefined'); + if (geoColumn && encoding) { + const binaryData = getBinaryGeometriesFromArrow(geoColumn, encoding); + t.deepEqual(binaryData, expectedBinaryGeometries, 'binary geometries are correct'); + } + + return Promise.resolve(); +} diff --git a/modules/arrow/test/geoarrow/convert-geoarrow-to-geojson.spec.ts b/modules/arrow/test/geoarrow/convert-geoarrow-to-geojson.spec.ts new file mode 100644 index 0000000000..9d15bb4113 --- /dev/null +++ b/modules/arrow/test/geoarrow/convert-geoarrow-to-geojson.spec.ts @@ -0,0 +1,340 @@ +import test, {Test} from 'tape-promise/tape'; + +import {tableFromIPC} from 'apache-arrow'; +import {fetchFile} from '@loaders.gl/core'; +import {FeatureCollection} from '@loaders.gl/schema'; +import {serializeArrowSchema, parseGeometryFromArrow} from '@loaders.gl/arrow'; +import {getGeometryColumnsFromSchema} from '@loaders.gl/gis'; + +export const POINT_ARROW_FILE = '@loaders.gl/arrow/test/data/point.arrow'; +export const MULTIPOINT_ARROW_FILE = '@loaders.gl/arrow/test/data/multipoint.arrow'; +export const LINE_ARROW_FILE = '@loaders.gl/arrow/test/data/line.arrow'; +export const MULTILINE_ARROW_FILE = '@loaders.gl/arrow/test/data/multiline.arrow'; +export const POLYGON_ARROW_FILE = '@loaders.gl/arrow/test/data/polygon.arrow'; +export const MULTIPOLYGON_ARROW_FILE = '@loaders.gl/arrow/test/data/multipolygon.arrow'; +export const MULTIPOLYGON_HOLE_ARROW_FILE = '@loaders.gl/arrow/test/data/multipolygon_hole.arrow'; + +/** Array containing all encodings */ +const GEOARROW_ENCODINGS = [ + 'geoarrow.multipolygon', + 'geoarrow.polygon', + 'geoarrow.multilinestring', + 'geoarrow.linestring', + 'geoarrow.multipoint', + 'geoarrow.point', + 'geoarrow.wkb', + 'geoarrow.wkt' +]; + +// a simple geojson contains one point +const expectedPointGeojson: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + id: 2, + name: 'name2' + }, + geometry: { + type: 'Point', + coordinates: [1, 1] + } + } + ] +}; + +// a simple geojson contains one linestring +const expectedLineStringGeoJson: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + id: 1, + name: 'name1' + }, + geometry: { + type: 'LineString', + coordinates: [ + [0, 0], + [1, 1] + ] + } + } + ] +}; + +// a simple geojson contains one polygon +const expectedPolygonGeojson: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + id: 1, + name: 'name1' + }, + geometry: { + type: 'Polygon', + coordinates: [ + [ + [0, 0], + [1, 1], + [2, 2], + [0, 0] + ] + ] + } + } + ] +}; + +// a simple geojson contains one MultiPoint +const expectedMultiPointGeoJson: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + id: 2, + name: 'name2' + }, + geometry: { + type: 'MultiPoint', + coordinates: [ + [1, 1], + [2, 2] + ] + } + } + ] +}; + +// a simple geojson contains one MultiLinestring +const expectedMultiLineStringGeoJson: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + id: 2, + name: 'name2' + }, + geometry: { + type: 'MultiLineString', + coordinates: [ + [ + [1, 1], + [2, 2] + ], + [ + [3, 3], + [4, 4] + ] + ] + } + } + ] +}; + +// a simple geojson contains one MultiPolygon +const expectedMultiPolygonGeojson: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + id: 1, + name: 'name1' + }, + geometry: { + type: 'MultiPolygon', + coordinates: [ + [ + [ + [0, 0], + [0, 1], + [1, 1], + [1, 0], + [0, 0] + ] + ], + [ + [ + [2, 2], + [2, 3], + [3, 3], + [3, 2], + [2, 2] + ] + ] + ] + } + } + ] +}; + +// a simple geojson contains one MultiPolygon with a whole in it +/* +const expectedMultiPolygonWithHoleGeojson: FeatureCollection = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + id: 1, + name: 'name1' + }, + geometry: { + type: 'MultiPolygon', + coordinates: [ + [ + [ + [0, 0], + [0, 1], + [1, 1], + [1, 0], + [0, 0] + ], + [ + [0.25, 0.25], + [0.25, 0.75], + [0.75, 0.75], + [0.75, 0.25], + [0.25, 0.25] + ] + ], + [ + [ + [2, 2], + [2, 3], + [3, 3], + [3, 2], + [2, 2] + ] + ] + ] + } + }, + { + type: 'Feature', + properties: { + id: 2, + name: 'name2' + }, + geometry: { + type: 'MultiPolygon', + coordinates: [ + [ + [ + [10, 10], + [10, 11], + [11, 11], + [11, 10], + [10, 10] + ], + [ + [10.25, 10.25], + [10.25, 10.75], + [10.75, 10.75], + [10.75, 10.25], + [10.25, 10.25] + ] + ], + [ + [ + [12, 12], + [12, 13], + [13, 13], + [13, 12], + [12, 12] + ] + ] + ] + } + } + ] +}; +*/ + +test('ArrowUtils#parseGeometryFromArrow', (t) => { + const testCases: [string, FeatureCollection][] = [ + [POINT_ARROW_FILE, expectedPointGeojson], + [MULTIPOINT_ARROW_FILE, expectedMultiPointGeoJson], + [LINE_ARROW_FILE, expectedLineStringGeoJson], + [MULTILINE_ARROW_FILE, expectedMultiLineStringGeoJson], + [POLYGON_ARROW_FILE, expectedPolygonGeojson], + [MULTIPOLYGON_ARROW_FILE, expectedMultiPolygonGeojson] + // [MULTIPOLYGON_HOLE_ARROW_FILE, expectedMultiPolygonWithHoleGeojson] + ]; + + testCases.forEach((testCase) => { + testParseFromArrow(t, testCase[0], testCase[1]); + }); + + t.end(); +}); + +async function testParseFromArrow( + t: Test, + arrowFile: string, + expectedGeojson: FeatureCollection +): Promise { + // TODO: use the following code instead of apache-arrow to load arrow table + // const arrowTable = await parse(fetchFile(arrowFile), ArrowLoader, {worker: false}); + const response = await fetchFile(arrowFile); + const arrayBuffer = await response.arrayBuffer(); + const arrowTable = tableFromIPC(new Uint8Array(arrayBuffer)); + + t.comment(arrowFile); + + // check if the arrow table is loaded correctly + t.equal( + arrowTable.numRows, + expectedGeojson.features.length, + `arrow table has ${expectedGeojson.features.length} row` + ); + + const colNames = [...Object.keys(expectedGeojson.features[0].properties || {}), 'geometry']; + t.equal(arrowTable.numCols, colNames.length, `arrow table has ${colNames.length} columns`); + + // check fields exist in arrow table schema + arrowTable.schema.fields.map((field) => + t.equal(colNames.includes(field.name), true, `arrow table has ${field.name} column`) + ); + + const schema = serializeArrowSchema(arrowTable.schema); + const geometryColumns = getGeometryColumnsFromSchema(schema); + + // check 'geometry' is in geometryColumns (geometryColumns is a Map object) + t.equal(Boolean(geometryColumns.geometry), true, 'geometryColumns has geometry column'); + + // get encoding from geometryColumns['geometry'] + const encoding = geometryColumns.geometry.encoding; + + // check encoding is one of GEOARROW_ENCODINGS + t.ok( + Object.values(GEOARROW_ENCODINGS).includes(encoding!), + 'encoding is one of GEOARROW_ENCODINGS' + ); + + // get first geometry from arrow geometry column + const firstArrowGeometry = arrowTable.getChild('geometry')?.get(0); + const firstArrowGeometryObject = { + encoding, + data: firstArrowGeometry + }; + + // parse arrow geometry to geojson feature + const firstFeature = parseGeometryFromArrow(firstArrowGeometryObject); + + // check if geometry in firstFeature is equal to the original geometry in expectedPointGeojson + t.deepEqual( + firstFeature?.geometry, + expectedGeojson.features[0].geometry, + 'firstFeature.geometry is equal to expectedGeojson.features[0].geometry' + ); + + return Promise.resolve(); +} diff --git a/modules/arrow/test/geoarrow/get-arrow-bounds.spec.ts b/modules/arrow/test/geoarrow/get-arrow-bounds.spec.ts new file mode 100644 index 0000000000..c4dd35da6a --- /dev/null +++ b/modules/arrow/test/geoarrow/get-arrow-bounds.spec.ts @@ -0,0 +1,58 @@ +import test from 'tape-promise/tape'; + +import {updateBoundsFromGeoArrowSamples} from '@loaders.gl/arrow'; + +// fix a bug that map bounds are not updated correctly from arrow samples +test('ArrowUtils#updateBoundsFromGeoArrowSamples', (t) => { + const testCases = [ + { + coords: [0, 0, 1, 1, 2, 2], + nDim: 2, + bound: [0, 0, 2, 2], + boundSample2: [0, 0, 2, 2] + }, + { + coords: [0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 6, 6], + nDim: 2, + bound: [0, 0, 6, 6], + boundSample2: [0, 0, 4, 4] + }, + { + coords: [0, 0, 0, 1, 1, 1, 2, 2, 2], + nDim: 3, + bound: [0, 0, 2, 2], + boundSample2: [0, 0, 2, 2] + }, + { + coords: [0, 0, 0, 1, 1, 1, 2, 2, 2, 4, 4, 4, 5, 5, 5, 6, 6, 6], + nDim: 3, + bound: [0, 0, 6, 6], + boundSample2: [0, 0, 4, 4] + } + ]; + + testCases.forEach((testCase) => { + const initBound: [number, number, number, number] = [Infinity, Infinity, -Infinity, -Infinity]; + + const updatedBound = updateBoundsFromGeoArrowSamples( + new Float64Array(testCase.coords), + testCase.nDim, + initBound + ); + t.deepEqual(updatedBound, testCase.bound, 'bounds updated correctly'); + + const sampleSize = 2; + const updateBoundWith2Samples = updateBoundsFromGeoArrowSamples( + new Float64Array(testCase.coords), + testCase.nDim, + initBound, + sampleSize + ); + t.deepEqual( + updateBoundWith2Samples, + testCase.boundSample2, + 'bounds updated correctly with 2 samples' + ); + }); + t.end(); +}); diff --git a/modules/arrow/test/index.js b/modules/arrow/test/index.js deleted file mode 100644 index 60ea1563f1..0000000000 --- a/modules/arrow/test/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import './arrow-loader.spec'; -import './arrow-writer.spec'; diff --git a/modules/arrow/test/index.ts b/modules/arrow/test/index.ts new file mode 100644 index 0000000000..aaa35907c2 --- /dev/null +++ b/modules/arrow/test/index.ts @@ -0,0 +1,9 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import './arrow-loader.spec'; +import './arrow-writer.spec'; + +import './geoarrow/convert-geoarrow-to-binary-geometry.spec'; +import './geoarrow/convert-geoarrow-to-geojson.spec'; +import './geoarrow/get-arrow-bounds.spec'; diff --git a/modules/arrow/tsconfig.json b/modules/arrow/tsconfig.json index 014fcad457..003021f3af 100644 --- a/modules/arrow/tsconfig.json +++ b/modules/arrow/tsconfig.json @@ -9,6 +9,7 @@ }, "references": [ {"path": "../core"}, + {"path": "../gis"}, {"path": "../loader-utils"}, {"path": "../schema"} ] diff --git a/modules/bson/package.json b/modules/bson/package.json index 6ffec87e56..090eb302f4 100644 --- a/modules/bson/package.json +++ b/modules/bson/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/bson", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loader for JSON and streaming JSON formats", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -22,8 +23,15 @@ "JSON async iterator" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -31,13 +39,13 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { - "@loaders.gl/gis": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", + "@loaders.gl/gis": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", "@types/bson": "4.2.0", "bson": "4.2.0" }, diff --git a/modules/bson/src/bson-loader.ts b/modules/bson/src/bson-loader.ts index 6e0494bdc9..71eb3662ce 100644 --- a/modules/bson/src/bson-loader.ts +++ b/modules/bson/src/bson-loader.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; import type {ParseBSONOptions} from './lib/parsers/parse-bson'; import {parseBSONSync} from './lib/parsers/parse-bson'; @@ -15,10 +16,6 @@ export type BSONLoaderOptions = LoaderOptions & { bson?: ParseBSONOptions; }; -const DEFAULT_BSON_LOADER_OPTIONS = { - bson: {} -}; - export const BSONLoader: LoaderWithParser, never, BSONLoaderOptions> = { name: 'BSON', id: 'bson', @@ -30,15 +27,17 @@ export const BSONLoader: LoaderWithParser, never, BSONLo binary: true, parse, parseSync, - options: DEFAULT_BSON_LOADER_OPTIONS + options: { + bson: {} + } }; async function parse(arrayBuffer: ArrayBuffer, options?: BSONLoaderOptions) { - const bsonOptions = {...DEFAULT_BSON_LOADER_OPTIONS.bson, ...options?.bson}; + const bsonOptions = {...BSONLoader.options.bson, ...options?.bson}; return parseBSONSync(arrayBuffer, bsonOptions); } function parseSync(arrayBuffer: ArrayBuffer, options?: BSONLoaderOptions) { - const bsonOptions = {...DEFAULT_BSON_LOADER_OPTIONS.bson, ...options?.bson}; + const bsonOptions = {...BSONLoader.options.bson, ...options?.bson}; return parseBSONSync(arrayBuffer, bsonOptions); } diff --git a/modules/bson/src/bson-writer.ts b/modules/bson/src/bson-writer.ts index a2cbe749a5..ebfbb8ded9 100644 --- a/modules/bson/src/bson-writer.ts +++ b/modules/bson/src/bson-writer.ts @@ -1,6 +1,7 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import type {Writer, WriterOptions} from '@loaders.gl/loader-utils'; +import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import type {EncodeBSONOptions} from './lib/encoders/encode-bson'; import {encodeBSONSync} from './lib/encoders/encode-bson'; @@ -12,7 +13,7 @@ export type BSONWriterOptions = WriterOptions & { bson?: EncodeBSONOptions } -export const BSONWriter: Writer, never, BSONWriterOptions> = { +export const BSONWriter: WriterWithEncoder, never, BSONWriterOptions> = { name: 'BSON', id: 'bson', module: 'bson', diff --git a/modules/bson/src/bundle.ts b/modules/bson/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/bson/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/bson/src/index.ts b/modules/bson/src/index.ts index 2b8c0d8230..0d1485283e 100644 --- a/modules/bson/src/index.ts +++ b/modules/bson/src/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors export type {BSONLoaderOptions} from './bson-loader'; export {BSONLoader} from './bson-loader'; diff --git a/modules/bson/src/lib/encoders/encode-bson.ts b/modules/bson/src/lib/encoders/encode-bson.ts index 31e7be569a..9b82958662 100644 --- a/modules/bson/src/lib/encoders/encode-bson.ts +++ b/modules/bson/src/lib/encoders/encode-bson.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {SerializeOptions} from 'bson'; import {serialize} from 'bson'; diff --git a/modules/bson/src/lib/parsers/parse-bson.ts b/modules/bson/src/lib/parsers/parse-bson.ts index 05e7ce1bd2..a7091d2a64 100644 --- a/modules/bson/src/lib/parsers/parse-bson.ts +++ b/modules/bson/src/lib/parsers/parse-bson.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {DeserializeOptions} from 'bson'; import {deserialize} from 'bson'; diff --git a/modules/bson/test/bson-loader.bench.ts b/modules/bson/test/bson-loader.bench.ts index 13fc90f696..9c1b38ee78 100644 --- a/modules/bson/test/bson-loader.bench.ts +++ b/modules/bson/test/bson-loader.bench.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {BSONLoader} from '@loaders.gl/bson'; import {load} from '@loaders.gl/core'; diff --git a/modules/bson/test/bson-loader.spec.js b/modules/bson/test/bson-loader.spec.js deleted file mode 100644 index 49204608e6..0000000000 --- a/modules/bson/test/bson-loader.spec.js +++ /dev/null @@ -1,35 +0,0 @@ -import test from 'tape-promise/tape'; -import {load, parseSync} from '@loaders.gl/core'; -import {BSONLoader} from '@loaders.gl/bson'; -import corruptScenarios from './data/js-bson/corrupt'; - -const TAGS_BSON_URL = '@loaders.gl/bson/test/data/js-bson/mongodump.airpair.tags.bson'; -const MINI_BSON_URL = '@loaders.gl/bson/test/data/js-bson/test.bson'; - -test('BSONLoader#load(test.bson)', async (t) => { - const data = await load(MINI_BSON_URL, BSONLoader); - t.comment(JSON.stringify(data)); - t.ok(data, 'Data received'); - t.end(); -}); - -// This seems to be a corrupt test file? -test.skip('BSONLoader#load(mongodump.airpair.tags.bson)', async (t) => { - const data = await load(TAGS_BSON_URL, BSONLoader); - t.ok(data, 'data received'); - t.end(); -}); - -test('BSON Compliance - corrupt scenarips', async (t) => { - for (let i = 0; i < corruptScenarios.documents.length; i++) { - const doc = corruptScenarios.documents[i]; - if (!doc.skip) { - // Create a buffer containing the payload - const buffer = Buffer.from(doc.encoded, 'hex'); - // Attempt to deserialize - t.throws(() => parseSync(buffer, BSONLoader), `Throws ${doc.error}`); - } - } - - t.end(); -}); diff --git a/modules/bson/test/bson-loader.spec.ts b/modules/bson/test/bson-loader.spec.ts index 4390c4e98b..19f423618f 100644 --- a/modules/bson/test/bson-loader.spec.ts +++ b/modules/bson/test/bson-loader.spec.ts @@ -1,16 +1,17 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; -import {load, parseSync} from '@loaders.gl/core'; +import {load} from '@loaders.gl/core'; import {BSONLoader} from '@loaders.gl/bson'; -import corruptScenarios from './data/js-bson/corrupt'; +// import corruptScenarios from './data/js-bson/corrupt'; const TAGS_BSON_URL = '@loaders.gl/bson/test/data/js-bson/mongodump.airpair.tags.bson'; const MINI_BSON_URL = '@loaders.gl/bson/test/data/js-bson/test.bson'; test('BSONLoader#load(test.bson)', async (t) => { const data = await load(MINI_BSON_URL, BSONLoader); - t.comment(JSON.stringify(data)); + // t.comment(JSON.stringify(data)); t.ok(data, 'Data received'); t.end(); }); @@ -22,16 +23,17 @@ test.skip('BSONLoader#load(mongodump.airpair.tags.bson)', async (t) => { t.end(); }); -test('BSON Compliance - corrupt scenarios', async (t) => { - for (let i = 0; i < corruptScenarios.documents.length; i++) { - const doc = corruptScenarios.documents[i]; - if (!doc.skip) { - // Create a buffer containing the payload - const buffer = Buffer.from(doc.encoded, 'hex'); - // Attempt to deserialize - t.throws(() => parseSync(buffer, BSONLoader), `Throws ${doc.error}`); - } - } +// TODO - skip because of Node.js Bbuffer dependency +// test('BSON Compliance - corrupt scenarios', async (t) => { +// for (let i = 0; i < corruptScenarios.documents.length; i++) { +// const doc = corruptScenarios.documents[i]; +// if (!doc.skip) { +// // Create a buffer containing the payload +// const buffer = Buffer.from(doc.encoded, 'hex'); +// // Attempt to deserialize +// t.throws(() => parseSync(buffer, BSONLoader), `Throws ${doc.error}`); +// } +// } - t.end(); -}); +// t.end(); +// }); diff --git a/modules/bson/test/data/js-bson/corrupt.ts b/modules/bson/test/data/js-bson/corrupt.ts index 5e6b959712..c9b675ee72 100644 --- a/modules/bson/test/data/js-bson/corrupt.ts +++ b/modules/bson/test/data/js-bson/corrupt.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors export default { description: 'Corrupted BSON', documents: [ diff --git a/modules/bson/tsconfig.json b/modules/bson/tsconfig.json index 5a85c0ea92..4e11b7c942 100644 --- a/modules/bson/tsconfig.json +++ b/modules/bson/tsconfig.json @@ -8,7 +8,6 @@ "outDir": "dist" }, "references": [ - {"path": "../gis"}, {"path": "../loader-utils"}, {"path": "../schema"} ] diff --git a/modules/compression/package.json b/modules/compression/package.json index 906e61a08b..a5e018147c 100644 --- a/modules/compression/package.json +++ b/modules/compression/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/compression", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Decompression and compression plugins for loaders.gl", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -19,8 +20,15 @@ "point cloud" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -33,15 +41,15 @@ "util": false }, "scripts": { - "pre-build": "npm run build-bundle && npm run build-worker && npm run build-worker-node", - "build-bundle": "esbuild src/bundle.ts --outfile=dist/dist.min.js --bundle --minify --sourcemap --external:{fs,path,crypto}", - "build-worker": "esbuild src/workers/worker.ts --outfile=dist/compression-worker.js --target=esnext --bundle --minify --sourcemap --external:{fs,path,crypto} --define:__VERSION__=\\\"$npm_package_version\\\"", - "build-worker-node": "esbuild src/workers/worker.ts --outfile=dist/compression-worker-node.js --platform=node --target=node16 --bundle --minify --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-worker && npm run build-worker-node", + "build-bundle": "ocular-bundle ./src/index.ts", + "build-worker": "esbuild src/workers/compression-worker.ts --outfile=dist/compression-worker.js --target=esnext --bundle --minify --sourcemap --external:{fs,path,crypto} --define:__VERSION__=\\\"$npm_package_version\\\"", + "build-worker-node": "esbuild src/workers/compression-worker-node.ts --outfile=dist/compression-worker-node.js --platform=node --target=node16 --bundle --minify --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { "@babel/runtime": "^7.3.1", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/worker-utils": "4.0.0-alpha.13", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/worker-utils": "4.0.3", "@types/brotli": "^1.3.0", "@types/pako": "^1.0.1", "fflate": "0.7.4", diff --git a/modules/compression/src/bundle.ts b/modules/compression/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/compression/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/compression/src/compression-worker.ts b/modules/compression/src/compress-on-worker.ts similarity index 84% rename from modules/compression/src/compression-worker.ts rename to modules/compression/src/compress-on-worker.ts index b3022a5167..f995b76552 100644 --- a/modules/compression/src/compression-worker.ts +++ b/modules/compression/src/compress-on-worker.ts @@ -1,4 +1,4 @@ -import type {WorkerObject} from '@loaders.gl/worker-utils'; +// import type {WorkerObject} from '@loaders.gl/worker-utils'; import {processOnWorker} from '@loaders.gl/worker-utils'; // __VERSION__ is injected by babel-plugin-version-inline @@ -30,5 +30,3 @@ export function compressOnWorker( ): Promise { return processOnWorker(CompressionWorker, data, options); } - -export const _typecheckCompressionWorker: WorkerObject = CompressionWorker; diff --git a/modules/compression/src/index.ts b/modules/compression/src/index.ts index 35af6cb16d..70061c2482 100644 --- a/modules/compression/src/index.ts +++ b/modules/compression/src/index.ts @@ -11,5 +11,5 @@ export {LZ4Compression} from './lib/lz4-compression'; export {ZstdCompression} from './lib/zstd-compression'; export {LZOCompression} from './lib/lzo-compression'; -export type {CompressionWorkerOptions} from './compression-worker'; -export {CompressionWorker, compressOnWorker} from './compression-worker'; +export type {CompressionWorkerOptions} from './compress-on-worker'; +export {CompressionWorker, compressOnWorker} from './compress-on-worker'; diff --git a/modules/compression/src/lib/deflate-compression.ts b/modules/compression/src/lib/deflate-compression.ts index 5068485b09..2ad8ab6449 100644 --- a/modules/compression/src/lib/deflate-compression.ts +++ b/modules/compression/src/lib/deflate-compression.ts @@ -8,6 +8,8 @@ import {promisify1} from '@loaders.gl/loader-utils'; export type DeflateCompressionOptions = CompressionOptions & { deflate?: pako.InflateOptions & pako.DeflateOptions & {useZlib?: boolean}; + /** creates raw data, without wrapper (header and adler32 crc). */ + raw?: boolean; }; /** @@ -58,7 +60,8 @@ export class DeflateCompression extends Compression { } const pakoOptions: pako.DeflateOptions = this.options?.deflate || {}; const inputArray = new Uint8Array(input); - return pako.deflate(inputArray, pakoOptions).buffer; + const deflate = this.options?.raw ? pako.deflateRaw : pako.deflate; + return deflate(inputArray, pakoOptions).buffer; } decompressSync(input: ArrayBuffer): ArrayBuffer { @@ -69,7 +72,8 @@ export class DeflateCompression extends Compression { } const pakoOptions: pako.InflateOptions = this.options?.deflate || {}; const inputArray = new Uint8Array(input); - return pako.inflate(inputArray, pakoOptions).buffer; + const inflate = this.options?.raw ? pako.inflateRaw : pako.inflate; + return inflate(inputArray, pakoOptions).buffer; } async *compressBatches( diff --git a/modules/compression/src/workers/compression-worker-node.ts b/modules/compression/src/workers/compression-worker-node.ts new file mode 100644 index 0000000000..b953925b64 --- /dev/null +++ b/modules/compression/src/workers/compression-worker-node.ts @@ -0,0 +1 @@ +import './compression-worker'; diff --git a/modules/compression/src/workers/worker.ts b/modules/compression/src/workers/compression-worker.ts similarity index 100% rename from modules/compression/src/workers/worker.ts rename to modules/compression/src/workers/compression-worker.ts diff --git a/modules/compression/test/compression.spec.js b/modules/compression/test/compression.spec.js index f34af8aeaa..46ddedee0e 100644 --- a/modules/compression/test/compression.spec.js +++ b/modules/compression/test/compression.spec.js @@ -158,7 +158,6 @@ test('compression#batched', async (t) => { }); // WORKER TESTS - test('gzip#worker', async (t) => { const {binaryData} = getData(); diff --git a/modules/core/package.json b/modules/core/package.json index bd89c36fea..779e3aa2e4 100644 --- a/modules/core/package.json +++ b/modules/core/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/core", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "The core API for working with loaders.gl loaders and writers", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -19,17 +20,22 @@ "point cloud" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "browser": { "fs": false, "stream": false, "./src/iterators/make-stream/make-node-stream.ts": "./src/iterators/make-stream/make-dom-stream.ts", "./src/iterators/make-stream/make-node-stream.js": "./src/iterators/make-stream/make-dom-stream.js", - "./dist/iterators/make-stream/make-node-stream.js": "./dist/iterators/make-stream/make-dom-stream.js", - "./dist/es5/iterators/make-stream/make-node-stream.js": "./dist/es5/iterators/make-stream/make-dom-stream.js", - "./dist/esm/iterators/make-stream/make-node-stream.js": "./dist/esm/iterators/make-stream/make-dom-stream.js" + "./dist/iterators/make-stream/make-node-stream.js": "./dist/iterators/make-stream/make-dom-stream.js" }, "files": [ "src", @@ -37,15 +43,15 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle && npm run build-worker && npm run build-worker-node", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-worker && npm run build-worker-node", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/null-worker.ts --outfile=dist/null-worker.js --bundle --target=esnext --define:__VERSION__=\\\"$npm_package_version\\\"", "build-worker-node": "esbuild src/workers/null-worker.ts --outfile=dist/null-worker-node.js --bundle --platform=node --target=node16 --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { "@babel/runtime": "^7.3.1", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/worker-utils": "4.0.0-alpha.13", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/worker-utils": "4.0.3", "@probe.gl/log": "^4.0.2" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" diff --git a/modules/core/src/bundle.ts b/modules/core/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/core/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/core/src/core-addons/write-file-browser.ts b/modules/core/src/core-addons/write-file-browser.ts index 745a41d40d..beb1294a80 100644 --- a/modules/core/src/core-addons/write-file-browser.ts +++ b/modules/core/src/core-addons/write-file-browser.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + // A browser implementation of the Node.js `fs` module's `fs.writeFile` method. // NOTE: WAS COMMENTED OUT TO GET NODE 8 TESTS RUNNING @@ -8,9 +11,9 @@ import {isBrowser} from '@loaders.gl/core'; // TODO hack - trick filesaver.js to skip loading under node const savedNavigatorExists = 'navigator' in global; -const savedNavigator = global.navigator; +const savedNavigator = globalThis.navigator; if (!isBrowser) { - global.navigator = {userAgent: 'MSIE 9.'}; + globalThis.navigator = {userAgent: 'MSIE 9.'}; } // Need to use `require` to ensure our modification of global code above happens first @@ -18,9 +21,9 @@ const saveAs = require('filesaver.js'); if (!isBrowser) { if (savedNavigatorExists) { - global.navigator = savedNavigator; + globalThis.navigator = savedNavigator; } else { - delete global.navigator; + delete globalThis.navigator; } } // END hack diff --git a/modules/core/src/index.ts b/modules/core/src/index.ts index 8502c97356..9757d71991 100644 --- a/modules/core/src/index.ts +++ b/modules/core/src/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // TYPES export type { @@ -11,16 +12,19 @@ export type { DataType, SyncDataType, BatchableDataType, - IFileSystem, - IRandomAccessReadFileSystem + ReadableFile, + WritableFile, + Stat, + FileSystem, + RandomAccessFileSystem } from '@loaders.gl/loader-utils'; // FILE READING AND WRITING export {fetchFile} from './lib/fetch/fetch-file'; export {readArrayBuffer} from './lib/fetch/read-array-buffer'; -export {readFileSync} from './lib/fetch/read-file'; -export {writeFile, writeFileSync} from './lib/fetch/write-file'; +// export {readFileSync} from './lib/fetch/read-file'; +// export {writeFile, writeFileSync} from './lib/fetch/write-file'; // CONFIGURATION export {setLoaderOptions, getLoaderOptions} from './lib/api/loader-options'; @@ -37,8 +41,8 @@ export {loadInBatches} from './lib/api/load-in-batches'; // ENCODING (ENCODING AND WRITING) export {encodeTable, encodeTableAsText, encodeTableInBatches} from './lib/api/encode-table'; -export {encode, encodeSync, encodeInBatches, encodeText, encodeURLtoURL} from './lib/api/encode'; -export {save, saveSync} from './lib/api/save'; +export {encode, encodeSync, encodeInBatches, encodeURLtoURL} from './lib/api/encode'; +export {encodeText, encodeTextSync} from './lib/api/encode'; // CORE UTILS SHARED WITH LOADERS (RE-EXPORTED FROM LOADER-UTILS) export {setPathPrefix, getPathPrefix, resolvePath} from '@loaders.gl/loader-utils'; @@ -46,15 +50,15 @@ export {RequestScheduler} from '@loaders.gl/loader-utils'; // ITERATOR UTILS export {makeIterator} from './iterators/make-iterator/make-iterator'; -export {makeStream} from './iterators/make-stream/make-node-stream'; +export {makeStream} from './iterators/make-stream/make-stream'; // CORE LOADERS export {NullWorkerLoader, NullLoader} from './null-loader'; export {JSONLoader} from '@loaders.gl/loader-utils'; // EXPERIMENTAL -export {default as _fetchProgress} from './lib/progress/fetch-progress'; -export {default as _BrowserFileSystem} from './lib/filesystems/browser-filesystem'; +export {fetchProgress as _fetchProgress} from './lib/progress/fetch-progress'; +export {BrowserFileSystem as _BrowserFileSystem} from './lib/filesystems/browser-filesystem'; // FOR TESTING export {_unregisterLoaders} from './lib/api/register-loaders'; diff --git a/modules/core/src/iterators/batch-iterators/timed-batch-iterator.ts b/modules/core/src/iterators/batch-iterators/timed-batch-iterator.ts index 653c28b807..851eaeb84a 100644 --- a/modules/core/src/iterators/batch-iterators/timed-batch-iterator.ts +++ b/modules/core/src/iterators/batch-iterators/timed-batch-iterator.ts @@ -1,7 +1,13 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + /** * "Debounces" batches and returns them in groups */ -export async function* timedBatchIterator(batchIterator: AsyncIterable, timeout) { +export async function* timedBatchIterator( + batchIterator: AsyncIterable, + timeout: number +): AsyncIterable { let start = Date.now(); let batches: Batch[] = []; for await (const batch of batchIterator) { diff --git a/modules/core/src/iterators/make-iterator/make-array-buffer-iterator.ts b/modules/core/src/iterators/make-iterator/make-array-buffer-iterator.ts index 77c29f6a79..2749cdc5bc 100644 --- a/modules/core/src/iterators/make-iterator/make-array-buffer-iterator.ts +++ b/modules/core/src/iterators/make-iterator/make-array-buffer-iterator.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {IteratorOptions} from './make-iterator'; const DEFAULT_CHUNK_SIZE = 256 * 1024; diff --git a/modules/core/src/iterators/make-iterator/make-blob-iterator.ts b/modules/core/src/iterators/make-iterator/make-blob-iterator.ts index f1e6788b25..4053122326 100644 --- a/modules/core/src/iterators/make-iterator/make-blob-iterator.ts +++ b/modules/core/src/iterators/make-iterator/make-blob-iterator.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {IteratorOptions} from './make-iterator'; const DEFAULT_CHUNK_SIZE = 1024 * 1024; // 1MB — biggest value that keeps UI responsive diff --git a/modules/core/src/iterators/make-iterator/make-iterator.ts b/modules/core/src/iterators/make-iterator/make-iterator.ts index 690c8d0beb..5ea4139b70 100644 --- a/modules/core/src/iterators/make-iterator/make-iterator.ts +++ b/modules/core/src/iterators/make-iterator/make-iterator.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {ReadStream} from 'fs'; import {makeStringIterator} from './make-string-iterator'; diff --git a/modules/core/src/iterators/make-iterator/make-stream-iterator.ts b/modules/core/src/iterators/make-iterator/make-stream-iterator.ts index 09c48ee0cb..09559e03e7 100644 --- a/modules/core/src/iterators/make-iterator/make-stream-iterator.ts +++ b/modules/core/src/iterators/make-iterator/make-stream-iterator.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {Readable} from 'stream'; import {isBrowser, toArrayBuffer} from '@loaders.gl/loader-utils'; diff --git a/modules/core/src/iterators/make-iterator/make-string-iterator.ts b/modules/core/src/iterators/make-iterator/make-string-iterator.ts index 48187c3813..b0e9cfea0e 100644 --- a/modules/core/src/iterators/make-iterator/make-string-iterator.ts +++ b/modules/core/src/iterators/make-iterator/make-string-iterator.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {IteratorOptions} from './make-iterator'; const DEFAULT_CHUNK_SIZE = 256 * 1024; diff --git a/modules/core/src/iterators/make-stream/make-dom-stream.ts b/modules/core/src/iterators/make-stream/make-stream.ts similarity index 83% rename from modules/core/src/iterators/make-stream/make-dom-stream.ts rename to modules/core/src/iterators/make-stream/make-stream.ts index dc8915e0cc..8aa62d5585 100644 --- a/modules/core/src/iterators/make-stream/make-dom-stream.ts +++ b/modules/core/src/iterators/make-stream/make-stream.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + export type MakeStreamOptions = { /** Stream allocates an arrayBuffer. Enables use of a default reader. */ autoAllocateChunkSize?: number; @@ -15,7 +18,12 @@ export function makeStream( source: Iterable | AsyncIterable, options?: MakeStreamOptions ): ReadableStream { - const iterator = source[Symbol.asyncIterator] + if (globalThis.loaders.makeNodeStream) { + return globalThis.loaders.makeNodeStream(source, options); + } + + // TODO - add AsyncGenerator to parameter types? + const iterator = (source as AsyncGenerator)[Symbol.asyncIterator] ? (source as AsyncIterable)[Symbol.asyncIterator]() : (source as Iterable)[Symbol.iterator](); @@ -24,6 +32,7 @@ export function makeStream( // Create a byte stream (enables `Response(stream).arrayBuffer()`) // Only supported on Chrome // See: https://developer.mozilla.org/en-US/docs/Web/API/ReadableByteStreamController + // @ts-ignore type: 'bytes', async pull(controller) { diff --git a/modules/core/src/javascript-utils/is-type.ts b/modules/core/src/javascript-utils/is-type.ts index 7996ac1bde..66a642f991 100644 --- a/modules/core/src/javascript-utils/is-type.ts +++ b/modules/core/src/javascript-utils/is-type.ts @@ -1,17 +1,21 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {Readable} from 'stream'; /** A DOM or Node readable stream */ export type ReadableStreamType = ReadableStream | Readable; -const isBoolean: (x: any) => boolean = (x) => typeof x === 'boolean'; -const isFunction: (x: any) => boolean = (x) => typeof x === 'function'; +const isBoolean: (x: unknown) => boolean = (x) => typeof x === 'boolean'; +const isFunction: (x: unknown) => boolean = (x) => typeof x === 'function'; -export const isObject: (x: any) => boolean = (x) => x !== null && typeof x === 'object'; +export const isObject: (x: unknown) => boolean = (x) => x !== null && typeof x === 'object'; export const isPureObject: (x: any) => boolean = (x) => isObject(x) && x.constructor === {}.constructor; export const isPromise: (x: any) => boolean = (x) => isObject(x) && isFunction(x.then); -export const isIterable: (x: any) => boolean = (x) => x && typeof x[Symbol.iterator] === 'function'; +export const isIterable: (x: any) => boolean = (x) => + Boolean(x) && typeof x[Symbol.iterator] === 'function'; export const isAsyncIterable: (x: any) => boolean = (x) => x && typeof x[Symbol.asyncIterator] === 'function'; export const isIterator: (x: any) => boolean = (x) => x && isFunction(x.next); @@ -20,8 +24,10 @@ export const isResponse: (x: any) => boolean = (x) => (typeof Response !== 'undefined' && x instanceof Response) || (x && x.arrayBuffer && x.text && x.json); -export const isFile: (x: any) => boolean = (x) => typeof File !== 'undefined' && x instanceof File; -export const isBlob: (x: any) => boolean = (x) => typeof Blob !== 'undefined' && x instanceof Blob; +export const isFile: (x: unknown) => boolean = (x) => + typeof File !== 'undefined' && x instanceof File; +export const isBlob: (x: unknown) => boolean = (x) => + typeof Blob !== 'undefined' && x instanceof Blob; /** Check for Node.js `Buffer` without triggering bundler to include buffer polyfill */ export const isBuffer: (x: any) => boolean = (x) => x && typeof x === 'object' && x.isBuffer; @@ -38,7 +44,7 @@ export const isWritableNodeStream: (x: any) => boolean = (x) => isObject(x) && isFunction(x.end) && isFunction(x.write) && isBoolean(x.writable); export const isReadableNodeStream: (x: any) => boolean = (x) => isObject(x) && isFunction(x.read) && isFunction(x.pipe) && isBoolean(x.readable); -export const isReadableStream: (x: any) => boolean = (x) => +export const isReadableStream: (x: unknown) => boolean = (x) => isReadableDOMStream(x) || isReadableNodeStream(x); -export const isWritableStream: (x: any) => boolean = (x) => +export const isWritableStream: (x: unknown) => boolean = (x) => isWritableDOMStream(x) || isWritableNodeStream(x); diff --git a/modules/core/src/lib/api/encode-table.ts b/modules/core/src/lib/api/encode-table.ts index 2b098a66f0..72e9f52f10 100644 --- a/modules/core/src/lib/api/encode-table.ts +++ b/modules/core/src/lib/api/encode-table.ts @@ -1,11 +1,16 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Copyright 2022 Foursquare Labs, Inc /* global TextEncoder, TextDecoder */ -import {concatenateArrayBuffers, Writer, WriterOptionsType} from '@loaders.gl/loader-utils'; +import { + concatenateArrayBuffers, + WriterOptionsType, + WriterWithEncoder +} from '@loaders.gl/loader-utils'; import {Table} from '@loaders.gl/schema'; -export async function encodeTable( +export async function encodeTable( data: Table, writer: WriterT, options?: WriterOptionsType @@ -35,7 +40,7 @@ export async function encodeTable( throw new Error('Writer could not encode data'); } -export async function encodeTableAsText( +export async function encodeTableAsText( data: Table, writer: WriterT, options?: WriterOptionsType @@ -44,14 +49,14 @@ export async function encodeTableAsText( return await writer.encodeText(data, options); } - if (writer.text && (writer.encode || writer.encodeInBatches)) { + if (writer.text) { const arrayBuffer = await encodeTable(data, writer, options); return new TextDecoder().decode(arrayBuffer); } - throw new Error('Writer could not encode data as text'); + throw new Error(`Writer ${writer.name} could not encode data as text`); } -export function encodeTableInBatches( +export function encodeTableInBatches( data: Table, writer: WriterT, options?: WriterOptionsType @@ -65,7 +70,7 @@ export function encodeTableInBatches( throw new Error('Writer could not encode data in batches'); } -function getIterator(data) { - const dataIterator = [{table: data, start: 0, end: data.length}]; +function getIterator(data: any): Iterable<{start: number; end: number}> { + const dataIterator = [{...data, start: 0, end: data.length}]; return dataIterator; } diff --git a/modules/core/src/lib/api/encode.ts b/modules/core/src/lib/api/encode.ts index 6c23537681..bd0568c31c 100644 --- a/modules/core/src/lib/api/encode.ts +++ b/modules/core/src/lib/api/encode.ts @@ -1,8 +1,10 @@ -import {Writer, WriterOptions, canEncodeWithWorker} from '@loaders.gl/loader-utils'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {WriterOptions, WriterWithEncoder, canEncodeWithWorker} from '@loaders.gl/loader-utils'; +import {concatenateArrayBuffers, resolvePath, NodeFile} from '@loaders.gl/loader-utils'; import {processOnWorker} from '@loaders.gl/worker-utils'; -import {concatenateArrayBuffers, resolvePath} from '@loaders.gl/loader-utils'; import {isBrowser} from '@loaders.gl/loader-utils'; -import {writeFile} from '../fetch/write-file'; import {fetchFile} from '../fetch/fetch-file'; import {getLoaderOptions} from './loader-options'; @@ -10,8 +12,8 @@ import {getLoaderOptions} from './loader-options'; * Encode loaded data into a binary ArrayBuffer using the specified Writer. */ export async function encode( - data: any, - writer: Writer, + data: unknown, + writer: WriterWithEncoder, options?: WriterOptions ): Promise { const globalOptions = getLoaderOptions() as WriterOptions; @@ -40,7 +42,7 @@ export async function encode( const batches = encodeInBatches(data, writer, options); // Concatenate the output - const chunks: any[] = []; + const chunks: unknown[] = []; for await (const batch of batches) { chunks.push(batch); } @@ -51,7 +53,8 @@ export async function encode( if (!isBrowser && writer.encodeURLtoURL) { // TODO - how to generate filenames with correct extensions? const tmpInputFilename = getTemporaryFilename('input'); - await writeFile(tmpInputFilename, data); + const file = new NodeFile(tmpInputFilename, 'w'); + await file.write(data as ArrayBuffer); const tmpOutputFilename = getTemporaryFilename('output'); @@ -72,7 +75,11 @@ export async function encode( /** * Encode loaded data into a binary ArrayBuffer using the specified Writer. */ -export function encodeSync(data: any, writer: Writer, options?: WriterOptions): ArrayBuffer { +export function encodeSync( + data: unknown, + writer: WriterWithEncoder, + options?: WriterOptions +): ArrayBuffer { if (writer.encodeSync) { return writer.encodeSync(data, options); } @@ -86,28 +93,51 @@ export function encodeSync(data: any, writer: Writer, options?: WriterOptions): * @throws if the writer does not generate text output */ export async function encodeText( - data: any, - writer: Writer, + data: unknown, + writer: WriterWithEncoder, options?: WriterOptions ): Promise { if (writer.text && writer.encodeText) { return await writer.encodeText(data, options); } - if (writer.text && (writer.encode || writer.encodeInBatches)) { + if (writer.text) { const arrayBuffer = await encode(data, writer, options); return new TextDecoder().decode(arrayBuffer); } - throw new Error('Writer could not encode data as text'); + throw new Error(`Writer ${writer.name} could not encode data as text`); +} + +/** + * Encode loaded data to text using the specified Writer + * @note This is a convenience function not intended for production use on large input data. + * It is not optimized for performance. Data maybe converted from text to binary and back. + * @throws if the writer does not generate text output + */ +export function encodeTextSync( + data: unknown, + writer: WriterWithEncoder, + options?: WriterOptions +): string { + if (writer.text && writer.encodeTextSync) { + return writer.encodeTextSync(data, options); + } + + if (writer.text && writer.encodeSync) { + const arrayBuffer = encodeSync(data, writer, options); + return new TextDecoder().decode(arrayBuffer); + } + + throw new Error(`Writer ${writer.name} could not encode data as text`); } /** * Encode loaded data into a sequence (iterator) of binary ArrayBuffers using the specified Writer. */ export function encodeInBatches( - data: any, - writer: Writer, + data: unknown, + writer: WriterWithEncoder, options?: WriterOptions ): AsyncIterable { if (writer.encodeInBatches) { @@ -124,10 +154,10 @@ export function encodeInBatches( * @note Node.js only. This function enables using command-line converters as "writers". */ export async function encodeURLtoURL( - inputUrl, - outputUrl, - writer: Writer, - options + inputUrl: string, + outputUrl: string, + writer: WriterWithEncoder, + options?: WriterOptions ): Promise { inputUrl = resolvePath(inputUrl); outputUrl = resolvePath(outputUrl); @@ -141,8 +171,8 @@ export async function encodeURLtoURL( /** * @todo TODO - this is an unacceptable hack!!! */ -function getIterator(data) { - const dataIterator = [{table: data, start: 0, end: data.length}]; +function getIterator(data: any): Iterable<{table: any; start: number; end: number}> { + const dataIterator = [{...data, start: 0, end: data.length}]; return dataIterator; } diff --git a/modules/core/src/lib/api/load-in-batches.ts b/modules/core/src/lib/api/load-in-batches.ts index fd4e78b376..4f0e0d45bb 100644 --- a/modules/core/src/lib/api/load-in-batches.ts +++ b/modules/core/src/lib/api/load-in-batches.ts @@ -1,4 +1,14 @@ -import type {LoaderWithParser, LoaderOptions, LoaderContext} from '@loaders.gl/loader-utils'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type { + LoaderWithParser, + LoaderOptions, + LoaderContext, + FetchLike, + BatchableDataType +} from '@loaders.gl/loader-utils'; +import type {LoaderBatchType, LoaderOptionsType} from '@loaders.gl/loader-utils'; import {isLoaderObject} from '../loader-utils/normalize-loader'; import {getFetchFunction} from '../loader-utils/get-fetch-function'; @@ -6,6 +16,19 @@ import {parseInBatches} from './parse-in-batches'; type FileType = string | File | Blob | Response | (string | File | Blob | Response)[] | FileList; +/** + * Parses `data` synchronously using a specified loader + */ +export async function loadInBatches< + LoaderT extends LoaderWithParser, + OptionsT extends LoaderOptions = LoaderOptionsType +>( + files: FileType, + loader: LoaderT, + options?: OptionsT, + context?: LoaderContext +): Promise>>; + /** * Parses `data` using a specified loader * @param data @@ -18,21 +41,29 @@ export function loadInBatches( loaders?: LoaderWithParser | LoaderWithParser[] | LoaderOptions, options?: LoaderOptions, context?: LoaderContext -): Promise>; +): Promise>; export function loadInBatches( files: FileType[] | FileList, loaders?: LoaderWithParser | LoaderWithParser[] | LoaderOptions, options?: LoaderOptions, context?: LoaderContext -): Promise>; +): Promise>[]; -export function loadInBatches(files, loaders, options, context) { +export function loadInBatches( + files: FileType | FileType[] | FileList, + loaders?: LoaderWithParser | LoaderWithParser[] | LoaderOptions, + options?: LoaderOptions, + context?: LoaderContext +): Promise> | Promise>[] { + let loadersArray: LoaderWithParser | LoaderWithParser[] | undefined; // Signature: load(url, options) if (!Array.isArray(loaders) && !isLoaderObject(loaders)) { context = undefined; // context not supported in short signature - options = loaders; - loaders = null; + options = loaders as LoaderOptions; + loadersArray = undefined; + } else { + loadersArray = loaders as LoaderWithParser | LoaderWithParser[] | undefined; } // Select fetch function @@ -40,21 +71,34 @@ export function loadInBatches(files, loaders, options, context) { // Single url/file if (!Array.isArray(files)) { - return loadOneFileInBatches(files, loaders, options, fetch); + return loadOneFileInBatches(files, loadersArray!, options || {}, fetch); } // Multiple URLs / files - const promises = files.map((file) => loadOneFileInBatches(file, loaders, options, fetch)); + const promises = files.map((file) => + loadOneFileInBatches(file, loadersArray!, options || {}, fetch) + ); // No point in waiting here for all responses before starting to stream individual streams? return promises; } -async function loadOneFileInBatches(file, loaders, options, fetch) { +async function loadOneFileInBatches( + file: FileType, + loaders: LoaderWithParser | LoaderWithParser[], + options: LoaderOptions, + fetch: FetchLike +): Promise> { if (typeof file === 'string') { const url = file; const response = await fetch(url); - return await parseInBatches(response, loaders, options); + // pick right overload + return Array.isArray(loaders) + ? await parseInBatches(response, loaders, options) + : await parseInBatches(response, loaders, options); } - return await parseInBatches(file, loaders, options); + // pick right overload + return Array.isArray(loaders) + ? await parseInBatches(file as BatchableDataType, loaders, options) + : await parseInBatches(file as BatchableDataType, loaders, options); } diff --git a/modules/core/src/lib/api/load.ts b/modules/core/src/lib/api/load.ts index e1db05e019..ee2b35e081 100644 --- a/modules/core/src/lib/api/load.ts +++ b/modules/core/src/lib/api/load.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {DataType, Loader, LoaderContext, LoaderOptions} from '@loaders.gl/loader-utils'; import type {LoaderOptionsType, LoaderReturnType} from '@loaders.gl/loader-utils'; @@ -18,37 +19,45 @@ import {parse} from './parse'; * @param context */ -export async function load( +export async function load< + LoaderT extends Loader, + OptionsT extends LoaderOptions = LoaderOptionsType +>( url: string | DataType, loader: LoaderT, - options?: LoaderOptionsType, + options?: OptionsT, context?: LoaderContext ): Promise>; -export async function load< - LoaderT extends Loader, // eslint-disable-line @typescript-eslint/no-unused-vars - LoaderOptionsT extends LoaderOptions = LoaderOptions ->( +export async function load( url: string | DataType, - loaders: Loader[] | LoaderOptions, - options?: LoaderOptionsT, + loaders: Loader[], + options?: LoaderOptions, context?: LoaderContext -): Promise; +): Promise; + +export async function load( + url: string | DataType, + loaders?: LoaderOptions, + context?: LoaderContext +): Promise; + +export async function load(url: string | DataType, loaders: LoaderOptions): Promise; // implementation signature -export async function load( +export async function load( url: string | DataType, loaders?: Loader[] | LoaderOptions, - options?: LoaderOptionsT, + options?: LoaderOptions, context?: LoaderContext -): Promise { +): Promise { let resolvedLoaders: Loader | Loader[]; - let resolvedOptions: LoaderOptionsT | undefined; + let resolvedOptions: LoaderOptions | undefined; // Signature: load(url, options) if (!Array.isArray(loaders) && !isLoaderObject(loaders)) { resolvedLoaders = []; - resolvedOptions = loaders as LoaderOptionsT; + resolvedOptions = loaders as LoaderOptions; context = undefined; // context not supported in short signature } else { resolvedLoaders = loaders as Loader | Loader[]; diff --git a/modules/core/src/lib/api/loader-options.ts b/modules/core/src/lib/api/loader-options.ts index a5c785c044..17055f513e 100644 --- a/modules/core/src/lib/api/loader-options.ts +++ b/modules/core/src/lib/api/loader-options.ts @@ -1,2 +1,5 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + export {setGlobalOptions as setLoaderOptions} from '../loader-utils/option-utils'; export {getGlobalLoaderOptions as getLoaderOptions} from '../loader-utils/option-utils'; diff --git a/modules/core/src/lib/api/parse-in-batches.ts b/modules/core/src/lib/api/parse-in-batches.ts index 9c1928a92a..585b8c9d10 100644 --- a/modules/core/src/lib/api/parse-in-batches.ts +++ b/modules/core/src/lib/api/parse-in-batches.ts @@ -1,12 +1,11 @@ -import type {Batch} from '@loaders.gl/schema'; -import type { - BatchableDataType, - Loader, - LoaderWithParser, - LoaderContext, - LoaderOptions -} from '@loaders.gl/loader-utils'; -import {assert, concatenateArrayBuffersAsync} from '@loaders.gl/loader-utils'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {isTable, makeBatchFromTable, type Batch} from '@loaders.gl/schema'; +import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; +import type {LoaderContext, BatchableDataType} from '@loaders.gl/loader-utils'; +import type {LoaderBatchType, LoaderOptionsType} from '@loaders.gl/loader-utils'; +import {concatenateArrayBuffersAsync} from '@loaders.gl/loader-utils'; import {isLoaderObject} from '../loader-utils/normalize-loader'; import {normalizeOptions} from '../loader-utils/option-utils'; import {getLoaderContext} from '../loader-utils/loader-context'; @@ -17,6 +16,37 @@ import {selectLoader} from './select-loader'; // Ensure `parse` is available in context if loader falls back to `parse` import {parse} from './parse'; +/** + * Parses `data` synchronously using a specified loader + */ +export async function parseInBatches< + LoaderT extends Loader, + OptionsT extends LoaderOptions = LoaderOptionsType +>( + data: BatchableDataType, + loader: LoaderT, + options?: OptionsT, + context?: LoaderContext +): Promise>>; + +/** + * Parses `data` using one of the supplied loaders + */ +export async function parseInBatches( + data: BatchableDataType, + loaders: Loader[], + options?: LoaderOptions, + context?: LoaderContext +): Promise>; + +/** + * Parses `data` in batches by selecting a pre-registered loader + */ +export async function parseInBatches( + data: BatchableDataType, + options?: LoaderOptions +): Promise>; + /** * Parses `data` using a specified loader * @param data @@ -29,9 +59,7 @@ export async function parseInBatches( loaders?: Loader | Loader[] | LoaderOptions, options?: LoaderOptions, context?: LoaderContext -): Promise> { - assert(!context || typeof context === 'object'); // parseInBatches no longer accepts final url - +): Promise | Iterable> { const loaderArray = Array.isArray(loaders) ? loaders : undefined; // Signature: parseInBatches(data, options, url) - Uses registered loaders @@ -52,14 +80,13 @@ export async function parseInBatches( const loader = await selectLoader(data as ArrayBuffer, loaders as Loader | Loader[], options); // Note: if options.nothrow was set, it is possible that no loader was found, if so just return null if (!loader) { - // @ts-ignore - return null; + return []; } // Normalize options options = normalizeOptions(options, loader, loaderArray, url); context = getLoaderContext( - {url, parseInBatches, parse, loaders: loaderArray}, + {url, _parseInBatches: parseInBatches, _parse: parse, loaders: loaderArray}, options, context || null ); @@ -75,7 +102,7 @@ async function parseWithLoaderInBatches( data: BatchableDataType, options: LoaderOptions, context: LoaderContext -): Promise> { +): Promise> { const outputIterator = await parseToOutputIterator(loader, data, options, context); // Generate metadata batch if requested @@ -94,7 +121,9 @@ async function parseWithLoaderInBatches( bytesUsed: 0 }; - async function* makeMetadataBatchIterator(iterator) { + async function* makeMetadataBatchIterator( + iterator: Iterable | AsyncIterable + ): AsyncIterable { yield metadataBatch; yield* iterator; } @@ -112,7 +141,7 @@ async function parseToOutputIterator( data: BatchableDataType, options: LoaderOptions, context: LoaderContext -): Promise> { +): Promise> { // Get an iterator from the input const inputIterator = await getAsyncIterableFromData(data, options); @@ -124,30 +153,49 @@ async function parseToOutputIterator( return loader.parseInBatches(transformedIterator, options, context); } - // Fallback: load atomically using `parse` concatenating input iterator into single chunk - async function* parseChunkInBatches() { - const arrayBuffer = await concatenateArrayBuffersAsync(transformedIterator); - // Call `parse` instead of `loader.parse` to ensure we can call workers etc. - const parsedData = await parse( - arrayBuffer, - loader, - // TODO - Hack: supply loaders MIME type to ensure we match it - {...options, mimeType: loader.mimeTypes[0]}, - context - ); - // yield a single batch, the output from loader.parse() - // TODO - run through batch builder to apply options etc... - const batch: Batch = { - mimeType: loader.mimeTypes[0], - shape: Array.isArray(parsedData) ? 'row-table' : 'unknown', - batchType: 'data', - data: parsedData, - length: Array.isArray(parsedData) ? parsedData.length : 1 - }; - yield batch; - } + return parseChunkInBatches(transformedIterator, loader, options, context); +} + +// Fallback: load atomically using `parse` concatenating input iterator into single chunk +async function* parseChunkInBatches( + transformedIterator: Iterable | AsyncIterable, + loader: Loader, + options: LoaderOptions, + context: LoaderContext +): AsyncIterable { + const arrayBuffer = await concatenateArrayBuffersAsync(transformedIterator); + // Call `parse` instead of `loader.parse` to ensure we can call workers etc. + const parsedData = await parse( + arrayBuffer, + loader, + // TODO - Hack: supply loaders MIME type to ensure we match it + {...options, mimeType: loader.mimeTypes[0]}, + context + ); - return parseChunkInBatches(); + // yield a single batch, the output from loader.parse() repackaged as a batch + const batch = convertDataToBatch(parsedData, loader); + + yield batch; +} + +/** + * Convert parsed data into a single batch + * @todo run through batch builder to apply options etc... + */ +function convertDataToBatch(parsedData: unknown, loader: Loader): Batch { + const batch: Batch = isTable(parsedData) + ? makeBatchFromTable(parsedData) + : { + shape: 'unknown', + batchType: 'data', + data: parsedData, + length: Array.isArray(parsedData) ? parsedData.length : 1 + }; + + batch.mimeType = loader.mimeTypes[0]; + + return batch; } type TransformBatches = ( diff --git a/modules/core/src/lib/api/parse-sync.ts b/modules/core/src/lib/api/parse-sync.ts index 9e4c0f4c43..8125b64c73 100644 --- a/modules/core/src/lib/api/parse-sync.ts +++ b/modules/core/src/lib/api/parse-sync.ts @@ -1,11 +1,9 @@ -import type { - SyncDataType, - Loader, - LoaderWithParser, - LoaderContext, - LoaderOptions -} from '@loaders.gl/loader-utils'; -import {assert} from '@loaders.gl/loader-utils'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; +import type {LoaderContext, SyncDataType} from '@loaders.gl/loader-utils'; +import type {LoaderOptionsType, LoaderReturnType} from '@loaders.gl/loader-utils'; import {selectLoaderSync} from './select-loader'; import {isLoaderObject} from '../loader-utils/normalize-loader'; import {normalizeOptions} from '../loader-utils/option-utils'; @@ -13,21 +11,45 @@ import {getArrayBufferOrStringFromDataSync} from '../loader-utils/get-data'; import {getLoaderContext, getLoadersFromContext} from '../loader-utils/loader-context'; import {getResourceUrl} from '../utils/resource-utils'; +// OVERLOADS + +/** + * Parses `data` synchronously using the specified loader + */ +export function parseSync< + LoaderT extends Loader, + OptionsT extends LoaderOptions = LoaderOptionsType +>( + data: SyncDataType, + loader: LoaderT, + options?: OptionsT, + context?: LoaderContext +): LoaderReturnType; + +/** + * Parses `data` synchronously by matching one of the supplied loaders + */ +export function parseSync( + data: SyncDataType, + loaders: Loader[], + options?: LoaderOptions, + context?: LoaderContext +): unknown; + +/** + * Parses `data` synchronously by matching a pre=registered loader + */ +export function parseSync(data: SyncDataType, options?: LoaderOptions): unknown; + /** * Parses `data` synchronously using a specified loader - * @param data - * @param loaders - * @param options - * @param context */ export function parseSync( data: SyncDataType, loaders?: Loader | Loader[] | LoaderOptions, options?: LoaderOptions, context?: LoaderContext -): any { - assert(!context || typeof context === 'object'); // parseSync no longer accepts final url - +): unknown { // Signature: parseSync(data, options) // Uses registered loaders if (!Array.isArray(loaders) && !isLoaderObject(loaders)) { @@ -49,7 +71,7 @@ export function parseSync( } // Normalize options - options = normalizeOptions(options, loader, candidateLoaders); + options = normalizeOptions(options, loader, candidateLoaders as Loader[] | undefined); // Extract a url for auto detection const url = getResourceUrl(data); @@ -58,7 +80,7 @@ export function parseSync( throw new Error('parseSync called parse (which is async'); }; context = getLoaderContext( - {url, parseSync, parse, loaders: loaders as Loader[]}, + {url, _parseSync: parse, _parse: parse, loaders: loaders as Loader[]}, options, context || null ); diff --git a/modules/core/src/lib/api/parse.ts b/modules/core/src/lib/api/parse.ts index fdbb01488d..ed169f3f62 100644 --- a/modules/core/src/lib/api/parse.ts +++ b/modules/core/src/lib/api/parse.ts @@ -1,12 +1,15 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import type {DataType, Loader, LoaderContext, LoaderOptions} from '@loaders.gl/loader-utils'; +import type {Loader, LoaderContext, LoaderOptions} from '@loaders.gl/loader-utils'; +import type {DataType, LoaderWithParser} from '@loaders.gl/loader-utils'; import type {LoaderOptionsType, LoaderReturnType} from '@loaders.gl/loader-utils'; -import {assert, validateWorkerVersion} from '@loaders.gl/worker-utils'; import {parseWithWorker, canParseWithWorker} from '@loaders.gl/loader-utils'; +import {assert, validateWorkerVersion} from '@loaders.gl/worker-utils'; import {isLoaderObject} from '../loader-utils/normalize-loader'; import {isResponse} from '../../javascript-utils/is-type'; import {normalizeOptions} from '../loader-utils/option-utils'; +import {mergeLoaderOptions} from '@loaders.gl/loader-utils'; import {getArrayBufferOrStringFromData} from '../loader-utils/get-data'; import {getLoaderContext, getLoadersFromContext} from '../loader-utils/loader-context'; import {getResourceUrl} from '../utils/resource-utils'; @@ -14,6 +17,9 @@ import {selectLoader} from './select-loader'; // type LoaderArrayType = T extends (infer Loader)[] ? LoaderOptionsType : T +/** + * Parses `data` asynchronously using the supplied loader + */ export async function parse< LoaderT extends Loader, OptionsT extends LoaderOptions = LoaderOptionsType @@ -24,17 +30,23 @@ export async function parse< context?: LoaderContext ): Promise>; +/** + * Parses `data` asynchronously by matching one of the supplied loader + */ export async function parse( data: DataType | Promise, loaders: Loader[], options?: LoaderOptions, context?: LoaderContext -): Promise; +): Promise; +/** + * Parses data asynchronously by matching a pre-registered loader + */ export async function parse( data: DataType | Promise, options?: LoaderOptions -): Promise; +): Promise; /** * Parses `data` using a specified loader @@ -49,9 +61,7 @@ export async function parse( loaders?: Loader | Loader[] | LoaderOptions, options?: LoaderOptions, context?: LoaderContext -): Promise { - assert(!context || typeof context === 'object'); // parse no longer accepts final url - +): Promise { // Signature: parse(data, options, context | url) // Uses registered loaders if (loaders && !Array.isArray(loaders) && !isLoaderObject(loaders)) { @@ -78,33 +88,48 @@ export async function parse( } // Normalize options + // @ts-expect-error options = normalizeOptions(options, loader, candidateLoaders, url); // Could be invalid... // Get a context (if already present, will be unchanged) - context = getLoaderContext({url, parse, loaders: candidateLoaders}, options, context || null); + context = getLoaderContext( + // @ts-expect-error + {url, _parse: parse, loaders: candidateLoaders}, + options, + context || null + ); return await parseWithLoader(loader, data, options, context); } // TODO: support progress and abort // TODO - should accept loader.parseAsyncIterator and concatenate. -async function parseWithLoader(loader, data, options, context) { +async function parseWithLoader( + loader: Loader, + data, + options: LoaderOptions, + context: LoaderContext +): Promise { validateWorkerVersion(loader); + options = mergeLoaderOptions(loader.options, options); + if (isResponse(data)) { // Serialize to support passing the response to web worker const response = data as Response; const {ok, redirected, status, statusText, type, url} = response; const headers = Object.fromEntries(response.headers.entries()); + // @ts-expect-error TODO - fix this context.response = {headers, ok, redirected, status, statusText, type, url}; } data = await getArrayBufferOrStringFromData(data, loader, options); + const loaderWithParser = loader as LoaderWithParser; + // First check for synchronous text parser, wrap results in promises - if (loader.parseTextSync && typeof data === 'string') { - options.dataType = 'text'; - return loader.parseTextSync(data, options, context, loader); + if (loaderWithParser.parseTextSync && typeof data === 'string') { + return loaderWithParser.parseTextSync(data, options, context); } // If we have a workerUrl and the loader can parse the given options efficiently in a worker @@ -113,16 +138,16 @@ async function parseWithLoader(loader, data, options, context) { } // Check for asynchronous parser - if (loader.parseText && typeof data === 'string') { - return await loader.parseText(data, options, context, loader); + if (loaderWithParser.parseText && typeof data === 'string') { + return await loaderWithParser.parseText(data, options, context); } - if (loader.parse) { - return await loader.parse(data, options, context, loader); + if (loaderWithParser.parse) { + return await loaderWithParser.parse(data, options, context); } // This should not happen, all sync loaders should also offer `parse` function - assert(!loader.parseSync); + assert(!loaderWithParser.parseSync); // TBD - If asynchronous parser not available, return null throw new Error(`${loader.id} loader - no parser found and worker is disabled`); diff --git a/modules/core/src/lib/api/register-loaders.ts b/modules/core/src/lib/api/register-loaders.ts index 0bb6f8c142..9b3d12f802 100644 --- a/modules/core/src/lib/api/register-loaders.ts +++ b/modules/core/src/lib/api/register-loaders.ts @@ -1,9 +1,14 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import {Loader} from '@loaders.gl/loader-utils'; import {normalizeLoader} from '../loader-utils/normalize-loader'; import {getGlobalLoaderState} from '../loader-utils/option-utils'; -// Store global registered loaders on the global object to increase chances of cross loaders-version interoperability -// This use case is not reliable but can help when testing new versions of loaders.gl with existing frameworks +/** + * Store global registered loaders on the global object to increase chances of cross loaders-version interoperability + * This use case is not reliable but can help when testing new versions of loaders.gl with existing frameworks + */ const getGlobalLoaderRegistry = () => { const state = getGlobalLoaderState(); state.loaderRegistry = state.loaderRegistry || []; diff --git a/modules/core/src/lib/api/save.ts b/modules/core/src/lib/api/save.ts deleted file mode 100644 index 2a6e5c8fb3..0000000000 --- a/modules/core/src/lib/api/save.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type {Writer, WriterOptions} from '@loaders.gl/loader-utils'; -import {encode, encodeSync} from './encode'; -import {writeFile, writeFileSync} from '../fetch/write-file'; - -export async function save(data, url, writer: Writer, options: WriterOptions) { - const encodedData = await encode(data, writer, options); - return await writeFile(url, encodedData); -} - -export function saveSync(data, url, writer, options) { - const encodedData = encodeSync(data, writer, options); - return writeFileSync(url, encodedData); -} diff --git a/modules/core/src/lib/api/select-loader.ts b/modules/core/src/lib/api/select-loader.ts index 30dbe3ce49..8b52913c62 100644 --- a/modules/core/src/lib/api/select-loader.ts +++ b/modules/core/src/lib/api/select-loader.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {LoaderContext, LoaderOptions, Loader} from '@loaders.gl/loader-utils'; import {compareArrayBuffers, path} from '@loaders.gl/loader-utils'; import {normalizeLoader} from '../loader-utils/normalize-loader'; @@ -6,6 +9,7 @@ import {getResourceUrl, getResourceMIMEType} from '../utils/resource-utils'; import {getRegisteredLoaders} from './register-loaders'; import {isBlob} from '../../javascript-utils/is-type'; import {stripQueryString} from '../utils/url-utils'; +import {TypedArray} from '@loaders.gl/schema'; const EXT_PATTERN = /\.([^.]+)$/; @@ -135,12 +139,16 @@ function selectLoaderInternal( reason = reason || (loader ? `matched MIME type ${type}` : ''); // Look for loader via initial bytes (Note: not always accessible (e.g. Response, stream, async iterator) + // @ts-ignore Blob | Response loader = loader || findLoaderByInitialBytes(loaders, data); + // @ts-ignore Blob | Response reason = reason || (loader ? `matched initial data ${getFirstCharacters(data)}` : ''); // Look up loader by fallback mime type - loader = loader || findLoaderByMIMEType(loaders, options?.fallbackMimeType); - reason = reason || (loader ? `matched fallback MIME type ${type}` : ''); + if (options?.fallbackMimeType) { + loader = loader || findLoaderByMIMEType(loaders, options?.fallbackMimeType); + reason = reason || (loader ? `matched fallback MIME type ${type}` : ''); + } if (reason) { log.log(1, `selectLoader selected ${loader?.name}: ${reason}.`); @@ -150,7 +158,7 @@ function selectLoaderInternal( } /** Check HTTP Response */ -function validHTTPResponse(data: any): boolean { +function validHTTPResponse(data: unknown): boolean { // HANDLE HTTP status if (data instanceof Response) { // 204 - NO CONTENT. This handles cases where e.g. a tile server responds with 204 for a missing tile @@ -162,7 +170,7 @@ function validHTTPResponse(data: any): boolean { } /** Generate a helpful message to help explain why loader selection failed. */ -function getNoValidLoaderMessage(data): string { +function getNoValidLoaderMessage(data: string | ArrayBuffer | Response | Blob): string { const url = getResourceUrl(data); const type = getResourceMIMEType(data); @@ -170,6 +178,7 @@ function getNoValidLoaderMessage(data): string { message += url ? `${path.filename(url)}, ` : 'no url provided, '; message += `MIME type: ${type ? `"${type}"` : 'not provided'}, `; // First characters are only accessible when called on data (string or arrayBuffer). + // @ts-ignore Blob | Response const firstCharacters: string = data ? getFirstCharacters(data) : ''; message += firstCharacters ? ` first bytes: "${firstCharacters}"` : 'first bytes: not available'; message += ')'; @@ -204,7 +213,7 @@ function findLoaderByExtension(loaders: Loader[], extension: string): Loader | n return null; } -function findLoaderByMIMEType(loaders, mimeType) { +function findLoaderByMIMEType(loaders: Loader[], mimeType: string): Loader | null { for (const loader of loaders) { if (loader.mimeTypes && loader.mimeTypes.includes(mimeType)) { return loader; @@ -219,7 +228,7 @@ function findLoaderByMIMEType(loaders, mimeType) { return null; } -function findLoaderByInitialBytes(loaders, data) { +function findLoaderByInitialBytes(loaders: Loader[], data: string | ArrayBuffer): Loader | null { if (!data) { return null; } @@ -245,27 +254,32 @@ function findLoaderByInitialBytes(loaders, data) { return null; } -function testDataAgainstText(data, loader) { +function testDataAgainstText(data: string, loader: Loader): boolean { if (loader.testText) { return loader.testText(data); } const tests = Array.isArray(loader.tests) ? loader.tests : [loader.tests]; - return tests.some((test) => data.startsWith(test)); + return tests.some((test) => data.startsWith(test as string)); } -function testDataAgainstBinary(data, byteOffset, loader) { +function testDataAgainstBinary(data: ArrayBuffer, byteOffset: number, loader: Loader): boolean { const tests = Array.isArray(loader.tests) ? loader.tests : [loader.tests]; return tests.some((test) => testBinary(data, byteOffset, loader, test)); } -function testBinary(data, byteOffset, loader, test) { +function testBinary( + data: ArrayBuffer, + byteOffset: number, + loader: Loader, + test?: ArrayBuffer | string | ((b: ArrayBuffer) => boolean) +): boolean { if (test instanceof ArrayBuffer) { return compareArrayBuffers(test, data, test.byteLength); } switch (typeof test) { case 'function': - return test(data, loader); + return test(data); case 'string': // Magic bytes check: If `test` is a string, check if binary data starts with that strings @@ -277,7 +291,7 @@ function testBinary(data, byteOffset, loader, test) { } } -function getFirstCharacters(data, length: number = 5) { +function getFirstCharacters(data: string | ArrayBuffer | TypedArray, length: number = 5) { if (typeof data === 'string') { return data.slice(0, length); } else if (ArrayBuffer.isView(data)) { @@ -290,7 +304,7 @@ function getFirstCharacters(data, length: number = 5) { return ''; } -function getMagicString(arrayBuffer, byteOffset, length) { +function getMagicString(arrayBuffer: ArrayBuffer, byteOffset: number, length: number): string { if (arrayBuffer.byteLength < byteOffset + length) { return ''; } diff --git a/modules/core/src/lib/common.ts b/modules/core/src/lib/common.ts index 777359a7b0..6c3639fde9 100644 --- a/modules/core/src/lib/common.ts +++ b/modules/core/src/lib/common.ts @@ -1 +1,4 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + export type {LoaderWithParser} from '@loaders.gl/loader-utils'; diff --git a/modules/core/src/lib/fetch/fetch-error-message.ts b/modules/core/src/lib/fetch/fetch-error-message.ts index e791ffe95d..9e06734328 100644 --- a/modules/core/src/lib/fetch/fetch-error-message.ts +++ b/modules/core/src/lib/fetch/fetch-error-message.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + export function getErrorMessageFromResponseSync(response: Response): string { return `Failed to fetch resource ${response.url}(${response.status}): ${response.statusText} `; } diff --git a/modules/core/src/lib/fetch/fetch-file.ts b/modules/core/src/lib/fetch/fetch-file.ts index 388dd7881e..7e709b8166 100644 --- a/modules/core/src/lib/fetch/fetch-file.ts +++ b/modules/core/src/lib/fetch/fetch-file.ts @@ -1,30 +1,47 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import {resolvePath} from '@loaders.gl/loader-utils'; import {makeResponse} from '../utils/response-utils'; -// import {getErrorMessageFromResponse} from './fetch-error-message'; + +export function isNodePath(url: string): boolean { + return !isRequestURL(url) && !isDataURL(url); +} + +export function isRequestURL(url: string): boolean { + return url.startsWith('http:') || url.startsWith('https:'); +} + +export function isDataURL(url: string): boolean { + return url.startsWith('data:'); +} /** - * fetch compatible function - * Reads file data from: - * - http/http urls - * - data urls - * - File/Blob objects - * Leverages `@loaders.gl/polyfills` for Node.js support - * Respects pathPrefix and file aliases + * fetch API compatible function + * - Supports fetching from Node.js local file system paths + * - Respects pathPrefix and file aliases */ export async function fetchFile( - url: string | Blob, - options?: RequestInit & {fetch?: RequestInit | Function} + urlOrData: string | Blob, + fetchOptions?: RequestInit ): Promise { - if (typeof url === 'string') { - url = resolvePath(url); + if (typeof urlOrData === 'string') { + const url = resolvePath(urlOrData); - let fetchOptions: RequestInit = options as RequestInit; - if (options?.fetch && typeof options?.fetch !== 'function') { - fetchOptions = options.fetch; + // Support fetching from local file system + if (isNodePath(url)) { + if (globalThis.loaders?.fetchNode) { + return globalThis.loaders?.fetchNode(url, fetchOptions); + } + // throw new Error( + // 'fetchFile: globalThis.loaders.fetchNode not defined. Install @loaders.gl/polyfills' + // ); } + // Call global fetch return await fetch(url, fetchOptions); } - return await makeResponse(url); + // TODO - should we still call fetch on non-URL inputs? + return await makeResponse(urlOrData); } diff --git a/modules/core/src/lib/fetch/read-array-buffer.ts b/modules/core/src/lib/fetch/read-array-buffer.ts index 76384c9ecd..d132ce6495 100644 --- a/modules/core/src/lib/fetch/read-array-buffer.ts +++ b/modules/core/src/lib/fetch/read-array-buffer.ts @@ -1,5 +1,5 @@ -// -import {fs} from '@loaders.gl/loader-utils'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors /** * Reads a chunk from a random access file @@ -9,13 +9,10 @@ import {fs} from '@loaders.gl/loader-utils'; * @returns */ export async function readArrayBuffer( - file: Blob | ArrayBuffer | string | number, + file: Blob | ArrayBuffer | string, start: number, length: number ): Promise { - if (typeof file === 'number') { - return await fs._readToArrayBuffer(file, start, length); - } // TODO - we can do better for ArrayBuffer and string if (!(file instanceof Blob)) { file = new Blob([file]); diff --git a/modules/core/src/lib/fetch/read-file.ts b/modules/core/src/lib/fetch/read-file.ts deleted file mode 100644 index 6830c304dd..0000000000 --- a/modules/core/src/lib/fetch/read-file.ts +++ /dev/null @@ -1,31 +0,0 @@ -// File read -import {isBrowser, resolvePath, fs, toArrayBuffer} from '@loaders.gl/loader-utils'; -import {assert} from '@loaders.gl/loader-utils'; - -// TODO - this is not tested -// const isDataURL = (url) => url.startsWith('data:'); - -/** - * In a few cases (data URIs, node.js) "files" can be read synchronously - */ -export function readFileSync(url: string, options: object = {}) { - url = resolvePath(url); - - // Only support this if we can also support sync data URL decoding in browser - // if (isDataURL(url)) { - // return decodeDataUri(url); - // } - - if (!isBrowser) { - const buffer = fs.readFileSync(url, options); - return typeof buffer !== 'string' ? toArrayBuffer(buffer) : buffer; - } - - // @ts-ignore - if (!options.nothrow) { - // throw new Error('Cant load URI synchronously'); - assert(false); - } - - return null; -} diff --git a/modules/core/src/lib/fetch/write-file.ts b/modules/core/src/lib/fetch/write-file.ts deleted file mode 100644 index 10502aae6a..0000000000 --- a/modules/core/src/lib/fetch/write-file.ts +++ /dev/null @@ -1,27 +0,0 @@ -// file write -import {isBrowser, assert, resolvePath} from '@loaders.gl/loader-utils'; -import {fs, toBuffer} from '@loaders.gl/loader-utils'; - -export async function writeFile( - filePath: string, - arrayBufferOrString: ArrayBuffer | string, - options? -): Promise { - filePath = resolvePath(filePath); - if (!isBrowser) { - await fs.writeFile(filePath, toBuffer(arrayBufferOrString), {flag: 'w'}); - } - assert(false); -} - -export function writeFileSync( - filePath: string, - arrayBufferOrString: ArrayBuffer | string, - options? -): void { - filePath = resolvePath(filePath); - if (!isBrowser) { - fs.writeFileSync(filePath, toBuffer(arrayBufferOrString), {flag: 'w'}); - } - assert(false); -} diff --git a/modules/core/src/lib/filesystems/browser-filesystem.ts b/modules/core/src/lib/filesystems/browser-filesystem.ts index e704657603..a333a6b7ed 100644 --- a/modules/core/src/lib/filesystems/browser-filesystem.ts +++ b/modules/core/src/lib/filesystems/browser-filesystem.ts @@ -1,4 +1,8 @@ -import type {FileSystem} from './filesystem'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {FileSystem, ReadableFile} from '@loaders.gl/loader-utils'; +import {BlobFile} from '@loaders.gl/loader-utils'; type BrowserFileSystemOptions = { fetch?: typeof fetch; @@ -8,7 +12,7 @@ type BrowserFileSystemOptions = { * FileSystem adapter for a browser FileList. * Holds a list of browser 'File' objects. */ -export default class BrowserFileSystem implements FileSystem { +export class BrowserFileSystem implements FileSystem { private _fetch: typeof fetch; private files: {[filename: string]: File} = {}; private lowerCaseFiles: {[filename: string]: File} = {}; @@ -109,18 +113,30 @@ export default class BrowserFileSystem implements FileSystem { // implements IRandomAccessFileSystem // RANDOM ACCESS - async open(pathname: string, flags, mode?): Promise { - return this.files[pathname]; + async openReadableFile(pathname: string, flags: unknown): Promise { + return new BlobFile(this.files[pathname]); } - /** + // PRIVATE + + // Supports case independent paths, and file usage tracking + _getFile(path: string, used: boolean): File { + // Prefer case match, but fall back to case independent. + const file = this.files[path] || this.lowerCaseFiles[path]; + if (file && used) { + this.usedFiles[path] = true; + } + return file; + } +} +/* * Read a range into a buffer * @todo - handle position memory * @param buffer is the buffer that the data (read from the fd) will be written to. * @param offset is the offset in the buffer to start writing at. * @param length is an integer specifying the number of bytes to read. * @param position is an argument specifying where to begin reading from in the file. If position is null, data will be read from the current file position, and the file position will be updated. If position is an integer, the file position will remain unchanged. - */ + * async read( fd: any, buffer: ArrayBuffer, @@ -140,16 +156,4 @@ export default class BrowserFileSystem implements FileSystem { } // fstat(fd: number): Promise; // Stat - - // PRIVATE - - // Supports case independent paths, and file usage tracking - _getFile(path, used) { - // Prefer case match, but fall back to case indepent. - const file = this.files[path] || this.lowerCaseFiles[path]; - if (file && used) { - this.usedFiles[path] = true; - } - return file; - } -} + */ diff --git a/modules/core/src/lib/filesystems/filesystem.ts b/modules/core/src/lib/filesystems/filesystem.ts deleted file mode 100644 index 6ac1c9de98..0000000000 --- a/modules/core/src/lib/filesystems/filesystem.ts +++ /dev/null @@ -1,49 +0,0 @@ -export type ReadOptions = {}; - -export type Stat = { - size: number; - isDirectory: () => boolean; -}; - -/** - * A FileSystem interface can encapsulate various file sources, - * a FileList, a ZipFile, a GoogleDrive etc. - */ -export interface FileSystem { - /** - * Return a list of file names - * @param dirname directory name. file system root directory if omitted - */ - readdir(dirname?: string, options?: {recursive?: boolean}): Promise; - - /** - * Gets information from a local file from the filesystem - * @param filename file name to stat - * @param options currently unused - * @throws if filename is not in local filesystem - */ - stat(filename: string, options?: object): Promise<{size: number}>; - - /** - * Fetches a local file from the filesystem (or a URL) - * @param filename - * @param options - */ - fetch(filename: RequestInfo, options?: RequestInit): Promise; -} - -/** - * A random access file system - */ -export interface RandomAccessReadFileSystem extends FileSystem { - open(path: string, flags, mode?): Promise; - close(fd: any): Promise; - fstat(fd: any): Promise; - read( - fd: any, - buffer: ArrayBuffer | ArrayBufferView, - offset?: number, - length?: number, - position?: number - ): Promise<{bytesRead: number; buffer: ArrayBuffer}>; -} diff --git a/modules/core/src/lib/filesystems/read-array-buffer.ts b/modules/core/src/lib/filesystems/read-array-buffer.ts index 6ac17582cd..5583415e8a 100644 --- a/modules/core/src/lib/filesystems/read-array-buffer.ts +++ b/modules/core/src/lib/filesystems/read-array-buffer.ts @@ -1,5 +1,12 @@ -// Random-Access read +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +/** + * Read a slice of a Blob or File, without loading the entire file into memory + * The trick when reading File objects is to read successive "slices" of the File + * Per spec https://w3c.github.io/FileAPI/, slicing a File only updates the start and end fields + * @param file to read + */ export async function readArrayBuffer( file: Blob | ArrayBuffer | any, start: number, @@ -11,21 +18,3 @@ export async function readArrayBuffer( } return await file.read(start, start + length); } - -/** - * Read a slice of a Blob or File, without loading the entire file into memory - * The trick when reading File objects is to read successive "slices" of the File - * Per spec https://w3c.github.io/FileAPI/, slicing a File only updates the start and end fields - * Actually reading from file happens in `readAsArrayBuffer` - * @param blob to read - export async function readBlob(blob: Blob): Promise { - return await new Promise((resolve, reject) => { - const fileReader = new FileReader(); - fileReader.onload = (event: ProgressEvent) => - resolve(event?.target?.result as ArrayBuffer); - // TODO - reject with a proper Error - fileReader.onerror = (error: ProgressEvent) => reject(error); - fileReader.readAsArrayBuffer(blob); - }); -} -*/ diff --git a/modules/core/src/lib/init.ts b/modules/core/src/lib/init.ts index 2dd383e490..edc79bd473 100644 --- a/modules/core/src/lib/init.ts +++ b/modules/core/src/lib/init.ts @@ -1,4 +1,6 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import {log} from './utils/log'; // @ts-ignore TS2304: Cannot find name '__VERSION__'. @@ -8,6 +10,7 @@ const version = typeof __VERSION__ !== 'undefined' ? __VERSION__ : ''; if (!globalThis.loaders) { log.log(1, `loaders.gl ${version}`)(); + // @ts-ignore TS2339: Property 'loaders' does not exist on type 'Window & typeof globalThis'. globalThis.loaders = Object.assign(globalThis.loaders || {}, { VERSION: version, log diff --git a/modules/core/src/lib/loader-utils/check-errors.ts b/modules/core/src/lib/loader-utils/check-errors.ts index 07243ccdab..6299d43101 100644 --- a/modules/core/src/lib/loader-utils/check-errors.ts +++ b/modules/core/src/lib/loader-utils/check-errors.ts @@ -1,4 +1,11 @@ -export async function checkFetchResponseStatus(response) { +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +/** + * Check reponse status, if not OK extract error message and throw error + * @param response + */ +export async function checkFetchResponseStatus(response: Response): Promise { if (!response.ok) { let errorMessage = `fetch failed ${response.status} ${response.statusText}`; try { @@ -13,14 +20,25 @@ export async function checkFetchResponseStatus(response) { } } -export function checkFetchResponseStatusSync(response) { +/** + * Check response status synchronously, if not OK extract error message and throw error + * Not able to extract as good an error message as the async version + * @param response + */ +export function checkFetchResponseStatusSync(response: Response): void { if (!response.ok) { throw new Error(`fetch failed ${response.status}`); } } -function getErrorText(text) { +/** + * Ad-hoc error message extractor + * @todo Handle XML, JSON, etc + * @param text + * @returns + */ +function getErrorText(text: string): string { // Look for HTML error texts - const matches = text.match('
(.*)
'); + const matches = /
(.*)<\/pre>/.exec(text);
   return matches ? matches[1] : ` ${text.slice(0, 10)}...`;
 }
diff --git a/modules/core/src/lib/loader-utils/get-data.ts b/modules/core/src/lib/loader-utils/get-data.ts
index e9fbde7ffe..4e0872bf1e 100644
--- a/modules/core/src/lib/loader-utils/get-data.ts
+++ b/modules/core/src/lib/loader-utils/get-data.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import type {
   DataType,
   SyncDataType,
@@ -127,7 +130,7 @@ export async function getAsyncIterableFromData(
   }
 
   if (isAsyncIterable(data)) {
-    return data[Symbol.asyncIterator]();
+    return data as AsyncIterable;
   }
 
   return getIterableFromData(data);
diff --git a/modules/core/src/lib/loader-utils/get-fetch-function.ts b/modules/core/src/lib/loader-utils/get-fetch-function.ts
index 4184ccaad5..95833c8f74 100644
--- a/modules/core/src/lib/loader-utils/get-fetch-function.ts
+++ b/modules/core/src/lib/loader-utils/get-fetch-function.ts
@@ -1,6 +1,7 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
 
-import type {LoaderContext, LoaderOptions} from '@loaders.gl/loader-utils';
+import type {LoaderContext, LoaderOptions, FetchLike} from '@loaders.gl/loader-utils';
 import {isObject} from '../../javascript-utils/is-type';
 import {fetchFile} from '../fetch/fetch-file';
 import {getGlobalLoaderOptions} from './option-utils';
@@ -13,19 +14,19 @@ import {getGlobalLoaderOptions} from './option-utils';
 export function getFetchFunction(
   options?: LoaderOptions,
   context?: Omit & Partial>
-) {
+): FetchLike {
   const globalOptions = getGlobalLoaderOptions();
 
-  const fetchOptions = options || globalOptions;
+  const loaderOptions = options || globalOptions;
 
   // options.fetch can be a function
-  if (typeof fetchOptions.fetch === 'function') {
-    return fetchOptions.fetch;
+  if (typeof loaderOptions.fetch === 'function') {
+    return loaderOptions.fetch;
   }
 
   // options.fetch can be an options object
-  if (isObject(fetchOptions.fetch)) {
-    return (url) => fetchFile(url, fetchOptions as RequestInit);
+  if (isObject(loaderOptions.fetch)) {
+    return (url) => fetchFile(url, loaderOptions.fetch as RequestInit);
   }
 
   // else refer to context (from parent loader) if available
diff --git a/modules/core/src/lib/loader-utils/loader-context.ts b/modules/core/src/lib/loader-utils/loader-context.ts
index a7be113e9d..23c73357c0 100644
--- a/modules/core/src/lib/loader-utils/loader-context.ts
+++ b/modules/core/src/lib/loader-utils/loader-context.ts
@@ -1,8 +1,14 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import type {Loader, LoaderOptions, LoaderContext} from '@loaders.gl/loader-utils';
 import {getFetchFunction} from './get-fetch-function';
 import {extractQueryString, stripQueryString} from '../utils/url-utils';
 import {path} from '@loaders.gl/loader-utils';
 
+/** Properties for creating an updated context */
+type LoaderContextProps = Omit & Partial>;
+
 /**
  * "sub" loaders invoked by other loaders get a "context" injected on `this`
  * The context will inject core methods like `parse` and contain information
@@ -13,7 +19,7 @@ import {path} from '@loaders.gl/loader-utils';
  * @param previousContext
  */
 export function getLoaderContext(
-  context: Omit & Partial>,
+  context: LoaderContextProps,
   options: LoaderOptions,
   parentContext: LoaderContext | null
 ): LoaderContext {
@@ -49,9 +55,9 @@ export function getLoaderContext(
 export function getLoadersFromContext(
   loaders: Loader[] | Loader | undefined,
   context?: LoaderContext
-) {
-  // A single non-array loader is force selected, but only on top-level (context === null)
-  if (!context && loaders && !Array.isArray(loaders)) {
+): Loader | Loader[] | undefined {
+  // A single loader (non-array) indicates no selection desired. Force select.
+  if (loaders && !Array.isArray(loaders)) {
     return loaders;
   }
 
@@ -65,5 +71,5 @@ export function getLoadersFromContext(
     candidateLoaders = candidateLoaders ? [...candidateLoaders, ...contextLoaders] : contextLoaders;
   }
   // If no loaders, return null to look in globally registered loaders
-  return candidateLoaders && candidateLoaders.length ? candidateLoaders : null;
+  return candidateLoaders && candidateLoaders.length ? candidateLoaders : undefined;
 }
diff --git a/modules/core/src/lib/loader-utils/loggers.ts b/modules/core/src/lib/loader-utils/loggers.ts
index ac28920e7b..eb3839a55f 100644
--- a/modules/core/src/lib/loader-utils/loggers.ts
+++ b/modules/core/src/lib/loader-utils/loggers.ts
@@ -1,20 +1,26 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 // probe.gl Log compatible loggers
+
 import {Log} from '@probe.gl/log';
 
 export const probeLog = new Log({id: 'loaders.gl'});
 
+type LogFunction = () => void;
+
 // Logs nothing
 export class NullLog {
-  log() {
+  log(): LogFunction {
     return () => {};
   }
-  info() {
+  info(): LogFunction {
     return () => {};
   }
-  warn() {
+  warn(): LogFunction {
     return () => {};
   }
-  error() {
+  error(): LogFunction {
     return () => {};
   }
 }
@@ -26,16 +32,16 @@ export class ConsoleLog {
   constructor() {
     this.console = console; // eslint-disable-line
   }
-  log(...args) {
+  log(...args: unknown[]): LogFunction {
     return this.console.log.bind(this.console, ...args);
   }
-  info(...args) {
+  info(...args: unknown[]): LogFunction {
     return this.console.info.bind(this.console, ...args);
   }
-  warn(...args) {
+  warn(...args: unknown[]): LogFunction {
     return this.console.warn.bind(this.console, ...args);
   }
-  error(...args) {
+  error(...args: unknown[]): LogFunction {
     return this.console.error.bind(this.console, ...args);
   }
 }
diff --git a/modules/core/src/lib/loader-utils/normalize-loader.ts b/modules/core/src/lib/loader-utils/normalize-loader.ts
index c4d901e105..e2c869ffd4 100644
--- a/modules/core/src/lib/loader-utils/normalize-loader.ts
+++ b/modules/core/src/lib/loader-utils/normalize-loader.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import type {Loader} from '@loaders.gl/loader-utils';
 import {assert} from '@loaders.gl/loader-utils';
 
diff --git a/modules/core/src/lib/loader-utils/option-defaults.ts b/modules/core/src/lib/loader-utils/option-defaults.ts
index 91910bf3c3..85928598e4 100644
--- a/modules/core/src/lib/loader-utils/option-defaults.ts
+++ b/modules/core/src/lib/loader-utils/option-defaults.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import type {LoaderOptions} from '@loaders.gl/loader-utils';
 import {isBrowser} from '@loaders.gl/loader-utils';
 import {ConsoleLog} from './loggers';
@@ -8,6 +11,7 @@ export const DEFAULT_LOADER_OPTIONS: LoaderOptions = {
   mimeType: undefined,
   nothrow: false,
   log: new ConsoleLog(), // A probe.gl compatible (`log.log()()` syntax) that just logs to console
+  useLocalLibraries: false,
 
   CDN: 'https://unpkg.com/@loaders.gl',
   worker: true, // By default, use worker if provided by loader.
diff --git a/modules/core/src/lib/loader-utils/option-utils.ts b/modules/core/src/lib/loader-utils/option-utils.ts
index 452cf6800c..db7f1afb08 100644
--- a/modules/core/src/lib/loader-utils/option-utils.ts
+++ b/modules/core/src/lib/loader-utils/option-utils.ts
@@ -1,4 +1,5 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
 
 import type {Loader, LoaderOptions} from '@loaders.gl/loader-utils';
 import {isPureObject, isObject} from '../../javascript-utils/is-type';
@@ -6,7 +7,7 @@ import {probeLog, NullLog} from './loggers';
 import {DEFAULT_LOADER_OPTIONS, REMOVED_LOADER_OPTIONS} from './option-defaults';
 
 /**
- * Global state for loaders.gl. Stored on `global.loaders._state`
+ * Global state for loaders.gl. Stored on `globalThis.loaders._state`
  */
 type GlobalLoaderState = {
   loaderRegistry: Loader[];
@@ -33,12 +34,12 @@ export function getGlobalLoaderState(): GlobalLoaderState {
  * NOTE: This use case is not reliable but can help when testing new versions of loaders.gl with existing frameworks
  * @returns global loader options merged with default loader options
  */
-export const getGlobalLoaderOptions = (): LoaderOptions => {
+export function getGlobalLoaderOptions(): LoaderOptions {
   const state = getGlobalLoaderState();
   // Ensure all default loader options from this library are mentioned
   state.globalOptions = state.globalOptions || {...DEFAULT_LOADER_OPTIONS};
   return state.globalOptions;
-};
+}
 
 /**
  * Set global loader options
@@ -47,6 +48,7 @@ export const getGlobalLoaderOptions = (): LoaderOptions => {
 export function setGlobalOptions(options: LoaderOptions): void {
   const state = getGlobalLoaderState();
   const globalOptions = getGlobalLoaderOptions();
+  // @ts-expect-error First param looks incorrect
   state.globalOptions = normalizeOptionsInternal(globalOptions, options);
 }
 
@@ -77,12 +79,15 @@ export function normalizeOptions(
  * @param options
  * @param loaders
  */
-function validateOptions(options: LoaderOptions, loaders: Loader[]) {
+function validateOptions(options: LoaderOptions, loaders: Loader[]): void {
   // Check top level options
   validateOptionsObject(options, null, DEFAULT_LOADER_OPTIONS, REMOVED_LOADER_OPTIONS, loaders);
   for (const loader of loaders) {
     // Get the scoped, loader specific options from the user supplied options
-    const idOptions = (options && options[loader.id]) || {};
+    const idOptions: Record = ((options && options[loader.id]) || {}) as Record<
+      string,
+      unknown
+    >;
 
     // Get scoped, loader specific default and deprecated options from the selected loader
     const loaderOptions = (loader.options && loader.options[loader.id]) || {};
@@ -90,18 +95,19 @@ function validateOptions(options: LoaderOptions, loaders: Loader[]) {
       (loader.deprecatedOptions && loader.deprecatedOptions[loader.id]) || {};
 
     // Validate loader specific options
+    // @ts-ignore
     validateOptionsObject(idOptions, loader.id, loaderOptions, deprecatedOptions, loaders);
   }
 }
 
 // eslint-disable-next-line max-params, complexity
 function validateOptionsObject(
-  options,
+  options: LoaderOptions,
   id: string | null,
-  defaultOptions,
-  deprecatedOptions,
+  defaultOptions: Record,
+  deprecatedOptions: Record,
   loaders: Loader[]
-) {
+): void {
   const loaderName = id || 'Top level';
   const prefix = id ? `${id}.` : '';
 
@@ -127,7 +133,7 @@ function validateOptionsObject(
   }
 }
 
-function findSimilarOption(optionKey, loaders) {
+function findSimilarOption(optionKey: string, loaders: Loader[]): string {
   const lowerCaseOptionKey = optionKey.toLowerCase();
   let bestSuggestion = '';
   for (const loader of loaders) {
@@ -146,7 +152,11 @@ function findSimilarOption(optionKey, loaders) {
   return bestSuggestion;
 }
 
-function normalizeOptionsInternal(loader, options, url?: string) {
+function normalizeOptionsInternal(
+  loader: Loader,
+  options: LoaderOptions,
+  url?: string
+): LoaderOptions {
   const loaderDefaultOptions = loader.options || {};
 
   const mergedOptions = {...loaderDefaultOptions};
@@ -165,7 +175,7 @@ function normalizeOptionsInternal(loader, options, url?: string) {
 }
 
 // Merge nested options objects
-function mergeNestedFields(mergedOptions, options) {
+function mergeNestedFields(mergedOptions: LoaderOptions, options: LoaderOptions): void {
   for (const key in options) {
     // Check for nested options
     // object in options => either no key in defaultOptions or object in defaultOptions
@@ -173,8 +183,8 @@ function mergeNestedFields(mergedOptions, options) {
       const value = options[key];
       if (isPureObject(value) && isPureObject(mergedOptions[key])) {
         mergedOptions[key] = {
-          ...mergedOptions[key],
-          ...options[key]
+          ...(mergedOptions[key] as object),
+          ...(options[key] as object)
         };
       } else {
         mergedOptions[key] = options[key];
@@ -192,7 +202,7 @@ function mergeNestedFields(mergedOptions, options) {
  * TODO - extract query parameters?
  * TODO - should these be injected on context instead of options?
  */
-function addUrlOptions(options, url?: string) {
+function addUrlOptions(options: LoaderOptions, url?: string): void {
   if (url && !('baseUri' in options)) {
     options.baseUri = url;
   }
diff --git a/modules/core/src/lib/progress/fetch-progress.ts b/modules/core/src/lib/progress/fetch-progress.ts
index 51601b0a06..8d1f6fe842 100644
--- a/modules/core/src/lib/progress/fetch-progress.ts
+++ b/modules/core/src/lib/progress/fetch-progress.ts
@@ -1,9 +1,12 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 // Forked from github AnthumChris/fetch-progress-indicators under MIT license
 
 /**
  * Intercepts the Response stream and creates a new Response
  */
-export default async function fetchProgress(
+export async function fetchProgress(
   response: Response | Promise,
   onProgress: any, // TODO better callback types
   onDone = () => {},
@@ -43,7 +46,15 @@ export default async function fetchProgress(
 // Forward to original streams controller
 // TODO - this causes a crazy deep "async stack"... rewrite as async iterator?
 // eslint-disable-next-line max-params
-async function read(controller, reader, loadedBytes, totalBytes, onProgress, onDone, onError) {
+async function read(
+  controller: any,
+  reader: any,
+  loadedBytes: number,
+  totalBytes: number,
+  onProgress: Function,
+  onDone: Function,
+  onError: Function
+): Promise {
   try {
     const {done, value} = await reader.read();
     if (done) {
diff --git a/modules/core/src/lib/utils/log.ts b/modules/core/src/lib/utils/log.ts
index 959bef73b7..e13b874d00 100644
--- a/modules/core/src/lib/utils/log.ts
+++ b/modules/core/src/lib/utils/log.ts
@@ -1,4 +1,6 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import {Log} from '@probe.gl/log';
 
 export const log = new Log({id: 'loaders.gl'});
diff --git a/modules/core/src/lib/utils/mime-type-utils.ts b/modules/core/src/lib/utils/mime-type-utils.ts
index ff316e3df2..f7cc73d479 100644
--- a/modules/core/src/lib/utils/mime-type-utils.ts
+++ b/modules/core/src/lib/utils/mime-type-utils.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 // TODO - build/integrate proper MIME type parsing
 // https://mimesniff.spec.whatwg.org/
 
diff --git a/modules/core/src/lib/utils/resource-utils.ts b/modules/core/src/lib/utils/resource-utils.ts
index 14131bb79a..dc69377701 100644
--- a/modules/core/src/lib/utils/resource-utils.ts
+++ b/modules/core/src/lib/utils/resource-utils.ts
@@ -1,4 +1,5 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
 
 import {isResponse, isBlob} from '../../javascript-utils/is-type';
 import {parseMIMEType, parseMIMETypeFromURL} from './mime-type-utils';
diff --git a/modules/core/src/lib/utils/response-utils.ts b/modules/core/src/lib/utils/response-utils.ts
index 8be9b48276..b7aefd01d6 100644
--- a/modules/core/src/lib/utils/response-utils.ts
+++ b/modules/core/src/lib/utils/response-utils.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import {isResponse} from '../../javascript-utils/is-type';
 import {getResourceContentLength, getResourceUrl, getResourceMIMEType} from './resource-utils';
 
@@ -7,9 +10,9 @@ import {getResourceContentLength, getResourceUrl, getResourceMIMEType} from './r
  *
  * @param resource
  */
-export async function makeResponse(resource: any): Promise {
+export async function makeResponse(resource: unknown): Promise {
   if (isResponse(resource)) {
-    return resource;
+    return resource as Response;
   }
 
   // Add content-length header if possible
@@ -42,7 +45,7 @@ export async function makeResponse(resource: any): Promise {
   }
 
   // Attempt to create a Response from the resource, adding headers and setting url
-  const response = new Response(resource, {headers});
+  const response = new Response(resource as any, {headers});
   // We can't control `Response.url` via constructor, use a property override to record URL.
   Object.defineProperty(response, 'url', {value: url});
   return response;
@@ -73,12 +76,12 @@ export function checkResponseSync(response: Response): void {
 
 // HELPERS
 
-async function getResponseError(response): Promise {
+async function getResponseError(response: Response): Promise {
   let message = `Failed to fetch resource ${response.url} (${response.status}): `;
   try {
     const contentType = response.headers.get('Content-Type');
     let text = response.statusText;
-    if (contentType.includes('application/json')) {
+    if (contentType?.includes('application/json')) {
       text += ` ${await response.text()}`;
     }
     message += text;
@@ -89,7 +92,9 @@ async function getResponseError(response): Promise {
   return message;
 }
 
-async function getInitialDataUrl(resource): Promise {
+async function getInitialDataUrl(
+  resource: string | Blob | ArrayBuffer | unknown
+): Promise {
   const INITIAL_DATA_LENGTH = 5;
   if (typeof resource === 'string') {
     return `data:,${resource.slice(0, INITIAL_DATA_LENGTH)}`;
@@ -111,7 +116,7 @@ async function getInitialDataUrl(resource): Promise {
 }
 
 // https://stackoverflow.com/questions/9267899/arraybuffer-to-base64-encoded-string
-function arrayBufferToBase64(buffer) {
+function arrayBufferToBase64(buffer: ArrayBuffer): string {
   let binary = '';
   const bytes = new Uint8Array(buffer);
   for (let i = 0; i < bytes.byteLength; i++) {
diff --git a/modules/core/src/lib/utils/url-utils.ts b/modules/core/src/lib/utils/url-utils.ts
index bc88a29699..f910813d68 100644
--- a/modules/core/src/lib/utils/url-utils.ts
+++ b/modules/core/src/lib/utils/url-utils.ts
@@ -1,4 +1,5 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
 
 const QUERY_STRING_PATTERN = /\?.*/;
 
diff --git a/modules/core/src/null-loader.ts b/modules/core/src/null-loader.ts
index 59c6da3926..a9b4d9d791 100644
--- a/modules/core/src/null-loader.ts
+++ b/modules/core/src/null-loader.ts
@@ -1,13 +1,21 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 // __VERSION__ is injected by babel-plugin-version-inline
 // @ts-ignore TS2304: Cannot find name '__VERSION__'.
 const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest';
 
-import {Loader, LoaderWithParser} from '@loaders.gl/loader-utils';
+import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils';
+import {LoaderContext} from '@loaders.gl/loader-utils';
+
+export type NullLoaderOptions = LoaderOptions & {
+  null?: {};
+};
 
 /**
  * Loads any data and returns null (or optionally passes through data unparsed)
  */
-export const NullWorkerLoader: Loader = {
+export const NullWorkerLoader: Loader = {
   name: 'Null loader',
   id: 'null',
   module: 'core',
@@ -21,27 +29,18 @@ export const NullWorkerLoader: Loader = {
   }
 };
 
-/**
- * Returns arguments passed to the parse API in a format that can be transfered to a
- * web worker. The `context` parameter is stripped using JSON.stringify & parse.
- */
-function parseSync(arrayBuffer, options, context) {
-  if (!options.null.echoParameters) return null;
-  context = context && JSON.parse(JSON.stringify(context));
-  return {arrayBuffer, options, context};
-}
-
 /**
  * Loads any data and returns null (or optionally passes through data unparsed)
  */
-export const NullLoader: LoaderWithParser = {
+export const NullLoader: LoaderWithParser = {
   name: 'Null loader',
   id: 'null',
   module: 'core',
   version: VERSION,
   mimeTypes: ['application/x.empty'],
   extensions: ['null'],
-  parse: async (arrayBuffer, options, context) => parseSync(arrayBuffer, options, context),
+  parse: async (arrayBuffer: ArrayBuffer, options?: NullLoaderOptions, context?: LoaderContext) =>
+    parseSync(arrayBuffer, options || {}, context),
   parseSync,
   parseInBatches: async function* generator(asyncIterator, options, context) {
     for await (const batch of asyncIterator) {
@@ -50,8 +49,18 @@ export const NullLoader: LoaderWithParser = {
   },
   tests: [() => false],
   options: {
-    null: {
-      echoParameters: false
-    }
+    null: {}
   }
 };
+
+/**
+ * Returns arguments passed to the parse API in a format that can be transferred to a
+ * web worker. The `context` parameter is stripped using JSON.stringify & parse.
+ */
+function parseSync(
+  arrayBuffer: ArrayBuffer,
+  options?: NullLoaderOptions,
+  context?: LoaderContext
+): null {
+  return null;
+}
diff --git a/modules/core/src/workers/null-worker-node.ts b/modules/core/src/workers/null-worker-node.ts
new file mode 100644
index 0000000000..dff34387f9
--- /dev/null
+++ b/modules/core/src/workers/null-worker-node.ts
@@ -0,0 +1,7 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
+import {createLoaderWorker} from '@loaders.gl/loader-utils';
+import {NullLoader} from '../null-loader';
+
+createLoaderWorker(NullLoader);
diff --git a/modules/core/src/workers/null-worker.ts b/modules/core/src/workers/null-worker.ts
index febacbf452..dff34387f9 100644
--- a/modules/core/src/workers/null-worker.ts
+++ b/modules/core/src/workers/null-worker.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import {createLoaderWorker} from '@loaders.gl/loader-utils';
 import {NullLoader} from '../null-loader';
 
diff --git a/modules/core/test/core.bench.js b/modules/core/test/core.bench.ts
similarity index 91%
rename from modules/core/test/core.bench.js
rename to modules/core/test/core.bench.ts
index 1e0c24f1dd..0dddea44d9 100644
--- a/modules/core/test/core.bench.js
+++ b/modules/core/test/core.bench.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 const LENGTH = 1e3;
 const ARRAY = new Array(LENGTH).fill(0);
 
diff --git a/modules/core/test/index-node.ts b/modules/core/test/index-node.ts
new file mode 100644
index 0000000000..87e14ea355
--- /dev/null
+++ b/modules/core/test/index-node.ts
@@ -0,0 +1,4 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
+import './lib/api/load.spec.node';
diff --git a/modules/core/test/index.js b/modules/core/test/index.ts
similarity index 87%
rename from modules/core/test/index.js
rename to modules/core/test/index.ts
index 49c24c20b6..8b6464d78c 100644
--- a/modules/core/test/index.js
+++ b/modules/core/test/index.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import './javascript-utils/is-type.spec';
 import './javascript-utils/text-encoder.spec';
 
@@ -16,7 +19,8 @@ import './lib/loader-utils/get-data.spec';
 import './lib/fetch/fetch-error-message.spec';
 import './lib/fetch/fetch-file.spec';
 import './lib/fetch/fetch-file.browser.spec';
-import './lib/fetch/read-file.spec';
+import './lib/fetch/fetch-file.node.spec';
+// import './lib/fetch/read-file.spec';
 
 import './lib/api/set-loader-options.spec';
 import './lib/api/register-loaders.spec';
diff --git a/modules/core/test/iterators/make-stream.spec.ts b/modules/core/test/iterators/make-stream.spec.ts
index 2ae263c281..6a1975df29 100644
--- a/modules/core/test/iterators/make-stream.spec.ts
+++ b/modules/core/test/iterators/make-stream.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 /* eslint-disable no-invalid-this, import/no-extraneous-dependencies */
 import test from 'tape-promise/tape';
 import {isBrowser, makeStream, makeIterator} from '@loaders.gl/core';
@@ -10,7 +13,6 @@ test('asyncIteratorToStream#fetch from asyncIteratorStream', async (t) => {
     const concatenatedData = concatenateArrayBuffers(...data);
 
     const stream = makeStream(data);
-    // @ts-expect-error stream can be either Node or DOM stream
     const response = new Response(stream);
     const arrayBuffer = await response.arrayBuffer();
 
@@ -25,7 +27,6 @@ test('asyncIteratorToStream#makeIterator(iteratorToStream())', async (t) => {
   const concatenatedData = concatenateArrayBuffers(...data);
 
   const stream = makeStream(data);
-  // @ts-expect-error stream can be either Node or DOM stream
   const streamIterator = makeIterator(stream);
 
   const chunks = await concatenateArrayBuffersAsync(streamIterator);
diff --git a/modules/core/test/javascript-utils/is-type.spec.js b/modules/core/test/javascript-utils/is-type.spec.ts
similarity index 94%
rename from modules/core/test/javascript-utils/is-type.spec.js
rename to modules/core/test/javascript-utils/is-type.spec.ts
index 0d37a29512..f5f5ec6da6 100644
--- a/modules/core/test/javascript-utils/is-type.spec.js
+++ b/modules/core/test/javascript-utils/is-type.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 
 import {
diff --git a/modules/core/test/javascript-utils/text-encoder.spec.js b/modules/core/test/javascript-utils/text-encoder.spec.ts
similarity index 84%
rename from modules/core/test/javascript-utils/text-encoder.spec.js
rename to modules/core/test/javascript-utils/text-encoder.spec.ts
index 9698e347ed..10cc104641 100644
--- a/modules/core/test/javascript-utils/text-encoder.spec.js
+++ b/modules/core/test/javascript-utils/text-encoder.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 /* eslint-disable max-len */
 import test from 'tape-promise/tape';
 
diff --git a/modules/core/test/lib/api/load-in-batches.spec.js b/modules/core/test/lib/api/load-in-batches.spec.ts
similarity index 57%
rename from modules/core/test/lib/api/load-in-batches.spec.js
rename to modules/core/test/lib/api/load-in-batches.spec.ts
index 77da2c6f61..72907e6a90 100644
--- a/modules/core/test/lib/api/load-in-batches.spec.js
+++ b/modules/core/test/lib/api/load-in-batches.spec.ts
@@ -1,12 +1,16 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {loadInBatches, fetchFile, isBrowser} from '@loaders.gl/core';
 import {CSVLoader} from '@loaders.gl/csv';
 import {OBJLoader} from '@loaders.gl/obj';
 import {KMLLoader} from '@loaders.gl/kml';
+import {ObjectRowTableBatch} from '@loaders.gl/schema';
 
 const CSV_SAMPLE_VERY_LONG_URL = '@loaders.gl/csv/test/data/sample-very-long.csv';
 const OBJ_ASCII_URL = '@loaders.gl/obj/test/data/bunny.obj';
-const KML_URL = '@loaders.gl/kml/test/data/KML_Samples.kml';
+const KML_URL = '@loaders.gl/kml/test/data/kml/KML_Samples.kml';
 
 test('loadInBatches#FileList', async (t) => {
   if (isBrowser) {
@@ -15,6 +19,7 @@ test('loadInBatches#FileList', async (t) => {
 
     const iteratorPromises = await loadInBatches([blob, blob], OBJLoader);
     for await (const iterator of iteratorPromises) {
+      // @ts-ignore
       for await (const batch of iterator) {
         // Just the one batch...
         t.equal(batch.data.mode, 4, 'mode is TRIANGLES (4)');
@@ -25,31 +30,36 @@ test('loadInBatches#FileList', async (t) => {
   t.end();
 });
 
-test('loadInBatches#non-batched loader (mesh)', async (t) => {
-  const batches = await loadInBatches(OBJ_ASCII_URL, OBJLoader);
-  for await (const batch of batches) {
-    // Just the one batch...
-    t.equal(batch.data.mode, 4, 'OBJ mode is TRIANGLES (4)');
-  }
+test.skip('loadInBatches#non-batched loader (mesh)', async (t) => {
+  // This masquerades an atomic loader as batches
+  // const batches = await loadInBatches(OBJ_ASCII_URL, OBJLoader);
+  // for await (const batch of batches) {
+  //   // Just the one batch...
+  //   t.equal(batch?.data.mode, 4, 'OBJ mode is TRIANGLES (4)');
+  // }
   t.end();
 });
 
 test('loadInBatches#non-batched loader (gis)', async (t) => {
-  const batches = await loadInBatches(KML_URL, KMLLoader, {kml: {type: 'object-row-table'}});
+  const batches = (await loadInBatches(KML_URL, KMLLoader, {
+    kml: {shape: 'object-row-table'}
+  })) as AsyncIterableIterator;
   for await (const batch of batches) {
     // Just the one batch...
-    t.equal(batch.data.data.length, 20, 'KML length of data features table is correct');
+    // @ts-ignore TODO - check returned types
+    t.equal(batch.data.length, 20, 'KML length of data features table is correct');
   }
   t.end();
 });
 
 test('loadInBatches(options.limit)', async (t) => {
-  // @ts-expect-error
+  // @ts-ignore
   const iterator = await loadInBatches(CSV_SAMPLE_VERY_LONG_URL, CSVLoader, {
     limit: 100
   });
-  const rows = [];
+  const rows: unknown[] = [];
   for await (const batch of iterator) {
+    // @ts-ignore CSVLoader types are not made available here due to potential circular dependency in tsconfigs
     rows.push(...batch.data);
   }
   t.is(rows.length, 100, 'Got the correct table size with options.limit');
diff --git a/modules/core/test/lib/api/load.spec.node.ts b/modules/core/test/lib/api/load.spec.node.ts
new file mode 100644
index 0000000000..9f81f7694d
--- /dev/null
+++ b/modules/core/test/lib/api/load.spec.node.ts
@@ -0,0 +1,24 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
+import test from 'tape-promise/tape';
+import {isBrowser, load, resolvePath} from '@loaders.gl/core';
+import {JSONLoader} from '@loaders.gl/json';
+
+const JSON_URL = '@loaders.gl/core/test/data/files/basic.json';
+
+test('load#Node stream - NODE ONLY', async (t) => {
+  if (isBrowser) {
+    t.comment('Skipping load(Node stream) tests in Node.js');
+    t.end();
+    return;
+  }
+
+  const fs = await import('fs');
+  const stream = fs.createReadStream(resolvePath(JSON_URL));
+  // @ts-ignore TODO remove this ts-ignore
+  const data = await load(stream, JSONLoader);
+  t.equals(typeof data, 'object', 'load(Node stream) returned data');
+
+  t.end();
+});
diff --git a/modules/core/test/lib/api/load.spec.js b/modules/core/test/lib/api/load.spec.ts
similarity index 81%
rename from modules/core/test/lib/api/load.spec.js
rename to modules/core/test/lib/api/load.spec.ts
index 435f32f58c..df49446e89 100644
--- a/modules/core/test/lib/api/load.spec.js
+++ b/modules/core/test/lib/api/load.spec.ts
@@ -1,4 +1,8 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
+import {parseFromContext} from '@loaders.gl/loader-utils';
 import {isBrowser, load, fetchFile, registerLoaders, resolvePath} from '@loaders.gl/core';
 import {NullWorkerLoader} from '@loaders.gl/core';
 import {JSONLoader} from '@loaders.gl/json';
@@ -44,7 +48,7 @@ test('load#auto detect loader', (t) => {
     parse: async (arrayBuffer, options, context) => {
       t.ok(arrayBuffer instanceof ArrayBuffer, 'Got ArrayBuffer');
       t.deepEquals(options.JSON, {option: true}, 'Option is passed through');
-      t.ok(context.parse, 'context is populated');
+      t.ok(context._parse, 'context is populated');
       t.end();
     }
   };
@@ -67,7 +71,7 @@ test('load#load retrieve Response', async (t) => {
     extensions: ['json'],
     parse: async (arrayBuffer, options, context) => {
       t.ok(arrayBuffer instanceof ArrayBuffer, 'Got ArrayBuffer');
-      const data = await context.parse(arrayBuffer, JSONLoader);
+      const data = await parseFromContext(arrayBuffer, JSONLoader, {}, context);
       t.ok(data, 'Read response data');
 
       const {response} = context;
@@ -90,14 +94,12 @@ test('load#load retrieve Response from worker - BROWSER ONLY', async (t) => {
     return;
   }
 
-  const {context} = await load(JSON_URL, NullWorkerLoader, {
-    null: {echoParameters: true},
+  const result = await load(JSON_URL, NullWorkerLoader, {
     _workerType: 'test',
     reuseWorkers: false
   });
 
-  t.ok(context, 'Context passed through by NullWorkerLoader');
-  checkResponse(t, context.response);
+  t.equal(result, null, 'null passed through by NullWorkerLoader');
 
   t.end();
 });
@@ -127,19 +129,3 @@ test('load#stream', async (t) => {
   t.equals(typeof data, 'object', 'load(stream) returned data');
   t.end();
 });
-
-test('load#Node stream - NODE ONLY', async (t) => {
-  if (isBrowser) {
-    t.comment('Skipping load(Node stream) tests in Node.js');
-    t.end();
-    return;
-  }
-
-  const fs = require('fs');
-  const stream = fs.createReadStream(resolvePath(JSON_URL));
-  // @ts-ignore TODO remove this ts-ignore
-  const data = await load(stream, JSONLoader);
-  t.equals(typeof data, 'object', 'load(Node stream) returned data');
-
-  t.end();
-});
diff --git a/modules/core/test/lib/api/parse-in-batches.spec.js b/modules/core/test/lib/api/parse-in-batches.spec.ts
similarity index 90%
rename from modules/core/test/lib/api/parse-in-batches.spec.js
rename to modules/core/test/lib/api/parse-in-batches.spec.ts
index 8fca51400f..d134ab7568 100644
--- a/modules/core/test/lib/api/parse-in-batches.spec.js
+++ b/modules/core/test/lib/api/parse-in-batches.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {parseInBatches} from '@loaders.gl/core';
 
@@ -24,7 +27,7 @@ test('parseInBatches', async (t) => {
   // @ts-ignore
   batches = await parseInBatches([1, 2], NoOpLoader, {metadata: true});
 
-  const values = [];
+  const values: unknown[] = [];
   metadata = false;
   for await (const batch of batches) {
     if (batch.batchType === 'metadata') {
diff --git a/modules/core/test/lib/api/parse.spec.js b/modules/core/test/lib/api/parse.spec.ts
similarity index 94%
rename from modules/core/test/lib/api/parse.spec.js
rename to modules/core/test/lib/api/parse.spec.ts
index 04fb691d0f..a1b7813684 100644
--- a/modules/core/test/lib/api/parse.spec.js
+++ b/modules/core/test/lib/api/parse.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 
 import {isBrowser} from '@loaders.gl/core';
diff --git a/modules/core/test/lib/api/register-loaders.spec.js b/modules/core/test/lib/api/register-loaders.spec.ts
similarity index 94%
rename from modules/core/test/lib/api/register-loaders.spec.js
rename to modules/core/test/lib/api/register-loaders.spec.ts
index 58cbe464dc..b18a410732 100644
--- a/modules/core/test/lib/api/register-loaders.spec.js
+++ b/modules/core/test/lib/api/register-loaders.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {registerLoaders} from '@loaders.gl/core';
 import {getRegisteredLoaders} from '@loaders.gl/core/lib/api/register-loaders';
diff --git a/modules/core/test/lib/api/select-loader.spec.js b/modules/core/test/lib/api/select-loader.spec.ts
similarity index 98%
rename from modules/core/test/lib/api/select-loader.spec.js
rename to modules/core/test/lib/api/select-loader.spec.ts
index c83eb6172f..95fdac3b4b 100644
--- a/modules/core/test/lib/api/select-loader.spec.js
+++ b/modules/core/test/lib/api/select-loader.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {fetchFile, selectLoader, selectLoaderSync, isBrowser} from '@loaders.gl/core';
 import {ImageLoader} from '@loaders.gl/images';
@@ -6,7 +9,7 @@ import {LASLoader} from '@loaders.gl/las';
 import {Tiles3DLoader} from '@loaders.gl/3d-tiles';
 import {KMLLoader} from '@loaders.gl/kml';
 
-const KML_URL = '@loaders.gl/kml/test/data/KML_Samples.kml';
+const KML_URL = '@loaders.gl/kml/test/data/kml/KML_Samples.kml';
 
 const DRACO_URL = '@loaders.gl/draco/test/data/bunny.drc';
 const TILE_3D_URL = '@loaders.gl/3d-tiles/test/data/PointCloud/PointCloudRGB/pointCloudRGB.pnts';
diff --git a/modules/core/test/lib/api/set-loader-options.spec.js b/modules/core/test/lib/api/set-loader-options.spec.ts
similarity index 87%
rename from modules/core/test/lib/api/set-loader-options.spec.js
rename to modules/core/test/lib/api/set-loader-options.spec.ts
index 0e98930d86..3cd8d3047d 100644
--- a/modules/core/test/lib/api/set-loader-options.spec.js
+++ b/modules/core/test/lib/api/set-loader-options.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {setLoaderOptions, getLoaderOptions} from '@loaders.gl/core';
 
diff --git a/modules/core/test/lib/fetch/fetch-error-message.spec.js b/modules/core/test/lib/fetch/fetch-error-message.spec.ts
similarity index 87%
rename from modules/core/test/lib/fetch/fetch-error-message.spec.js
rename to modules/core/test/lib/fetch/fetch-error-message.spec.ts
index d49df5d9b1..372f99db2e 100644
--- a/modules/core/test/lib/fetch/fetch-error-message.spec.js
+++ b/modules/core/test/lib/fetch/fetch-error-message.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 
 import {
diff --git a/modules/core/test/lib/fetch/fetch-file.browser.spec.js b/modules/core/test/lib/fetch/fetch-file.browser.spec.ts
similarity index 98%
rename from modules/core/test/lib/fetch/fetch-file.browser.spec.js
rename to modules/core/test/lib/fetch/fetch-file.browser.spec.ts
index e6e697d8ee..59b808645f 100644
--- a/modules/core/test/lib/fetch/fetch-file.browser.spec.js
+++ b/modules/core/test/lib/fetch/fetch-file.browser.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 
 import {isBrowser, fetchFile} from '@loaders.gl/core';
diff --git a/modules/core/test/lib/fetch/fetch-file.node.spec.ts b/modules/core/test/lib/fetch/fetch-file.node.spec.ts
new file mode 100644
index 0000000000..41a2019c11
--- /dev/null
+++ b/modules/core/test/lib/fetch/fetch-file.node.spec.ts
@@ -0,0 +1,145 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
+import test from 'tape-promise/tape';
+import {isBrowser, fetchFile} from '@loaders.gl/core';
+
+const GITHUB_MASTER = 'https://raw.githubusercontent.com/visgl/loaders.gl/master/modules/';
+const PLY_CUBE_ATT_URL = `${GITHUB_MASTER}ply/test/data/cube_att.ply`;
+const PLY_CUBE_ATT_SIZE = 853;
+const TEXT_URL = '@loaders.gl/polyfills/test/data/data.txt';
+const TEXT_URL_GZIPPED = '@loaders.gl/polyfills/test/data/data.txt.gz';
+// Request of this url returns location like "/@loaders.gl/textures@[VERSION]/dist/libs/basis_encoder.js"
+// So we get an error when trying to fetch such redirect url without protocol and origin.
+const TEXT_URL_WITH_REDIRECT =
+  'https://unpkg.com/@loaders.gl/textures@beta/dist/libs/basis_encoder.js';
+
+// This type of links on github works via 302 redirect
+// ("https://github.com/repository/raw/branch-name/path/to/file/file-name.extension")
+const REDIRECT_URL =
+  'https://github.com/visgl/deck.gl-data/raw/master/3d-tiles/RoyalExhibitionBuilding/1/1.pnts';
+
+test('fetchFile() (NODE)', async (t) => {
+  if (!isBrowser) {
+    const response = await fetchFile(PLY_CUBE_ATT_URL);
+    t.ok(response.headers, 'fetchFile successfully returned headers under Node.js');
+    const data = await response.arrayBuffer();
+    t.ok(data, 'fetchFile successfully loaded data under Node.js');
+  }
+  t.end();
+});
+
+test('fetchFile() ignores url query params when loading file (NODE)', async (t) => {
+  if (!isBrowser) {
+    const response = await fetchFile(`${PLY_CUBE_ATT_URL}?v=1.2.3`);
+    const data = await response.text();
+    t.ok(response.headers, 'fetchFile successfully returned headers under Node.js');
+    t.ok(data, 'fetchFile successfully loaded data under Node.js');
+  }
+  t.end();
+});
+
+test.skip('fetchFile() error handling (NODE)', async (t) => {
+  if (!isBrowser) {
+    let response = await fetchFile('non-existent-file');
+    t.comment(response.statusText);
+    t.ok(response.statusText.includes('ENOENT'), 'fetch statusText forwards node ENOENT error');
+    t.notOk(response.ok, 'fetchFile fails cleanly on non-existent file');
+    t.ok(response.arrayBuffer(), 'Response.arrayBuffer() does not throw');
+
+    response = await fetchFile('.');
+    t.comment(response.statusText);
+    t.ok(response.statusText.includes('EISDIR'), 'fetch statusText forwards node error');
+    t.notOk(response.ok, 'fetchFile fails cleanly on directory');
+    t.ok(response.arrayBuffer(), 'Response.arrayBuffer() does not throw');
+  }
+  t.end();
+});
+
+test('fetchFile() able to handle "Accept-Encoding: gzip" (NODE)', async (t) => {
+  if (!isBrowser) {
+    // Github will serve the desired compression
+    const headers = {
+      'Accept-Encoding': 'gzip'
+    };
+    // Test will pass even if server will refuse to encode into 'gzip' and just return plaintext
+    // In case of GitHub URL, it's honoring gzip and properly returning compressed data
+    const response = await fetchFile(PLY_CUBE_ATT_URL, {headers});
+    const data = await response.text();
+    t.equal(data.length, PLY_CUBE_ATT_SIZE, 'fetchFile data size as expected');
+    t.ok(data, 'fetchFile successfully loaded data under Node.js with "gzip" encoding');
+  }
+  t.end();
+});
+
+test('fetchFile() able to handle "Accept-Encoding: br" (NODE)', async (t) => {
+  if (!isBrowser) {
+    // Github will serve the desired compression
+    const headers = {
+      'Accept-Encoding': 'br'
+    };
+    // Test will pass even if server will refuse to encode into 'br' and just return plaintext
+    const response = await fetchFile(PLY_CUBE_ATT_URL, {headers});
+    const data = await response.text();
+    t.ok(data.length === PLY_CUBE_ATT_SIZE, 'fetchFile data size as expected');
+    t.ok(data, 'fetchFile successfully loaded data under Node.js with "br" encoding');
+  }
+  t.end();
+});
+
+test('fetchFile() able to handle "Accept-Encoding: deflate"', async (t) => {
+  if (!isBrowser) {
+    // Github will serve the desired compression
+    const headers = {
+      'Accept-Encoding': 'deflate'
+    };
+    // Test will pass even if server will refuse to encode into 'deflate' and just return plaintext
+    const response = await fetchFile(PLY_CUBE_ATT_URL, {headers});
+    const data = await response.text();
+    t.ok(data.length === PLY_CUBE_ATT_SIZE, 'fetchFile data size as expected');
+    t.ok(data, 'fetchFile successfully loaded data under Node.js with "deflate" encoding');
+  }
+  t.end();
+});
+
+test.skip('fetchFile() able to decompress .gz extension (NODE)', async (t) => {
+  let response = await fetchFile(TEXT_URL);
+  t.ok(response.ok, response.statusText);
+  let data = await response.text();
+  t.equal(data, '123456', 'fetchFile correctly read text file');
+
+  if (!isBrowser) {
+    response = await fetchFile(TEXT_URL_GZIPPED);
+    t.ok(response.ok, response.statusText);
+    data = await response.text();
+    t.equal(data, '123456', 'fetchFile correctly decompressed gzipped ".gz" file');
+  }
+  t.end();
+});
+
+test('fetchFile() should follow redirect if `followRedirect` option is true', async (t) => {
+  if (!isBrowser) {
+    const defaultFetchResponse = await fetchFile(REDIRECT_URL);
+    t.equal(defaultFetchResponse.status, 200);
+
+    const defaultResponse = await fetchFile(REDIRECT_URL, {});
+    t.equal(defaultResponse.status, 200);
+
+    // @ts-ignore - TODO/ActionEngine
+    const successResponse = await fetchFile(REDIRECT_URL, {followRedirect: true});
+    t.equal(successResponse.status, 200);
+
+    // TODO/ActionEngine - restore
+    // const failedResponse = await fetchFile(REDIRECT_URL, {followRedirect: false});
+    // t.equal(failedResponse.status, 302);
+  }
+  t.end();
+});
+
+test('fetchFile() should follow redirect if header location doesn`t have protocol and origin', async (t) => {
+  if (!isBrowser) {
+    const defaultFetchResponse = await fetchFile(TEXT_URL_WITH_REDIRECT);
+    t.equal(defaultFetchResponse.status, 200);
+  }
+  t.end();
+});
diff --git a/modules/core/test/lib/fetch/fetch-file.spec.js b/modules/core/test/lib/fetch/fetch-file.spec.ts
similarity index 95%
rename from modules/core/test/lib/fetch/fetch-file.spec.js
rename to modules/core/test/lib/fetch/fetch-file.spec.ts
index fef463f3b5..9b6a9a9db6 100644
--- a/modules/core/test/lib/fetch/fetch-file.spec.js
+++ b/modules/core/test/lib/fetch/fetch-file.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 
 import {isBrowser, fetchFile} from '@loaders.gl/core';
diff --git a/modules/core/test/lib/fetch/read-file.spec.js b/modules/core/test/lib/fetch/read-file.spec.js
deleted file mode 100644
index bf70098292..0000000000
--- a/modules/core/test/lib/fetch/read-file.spec.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import test from 'tape-promise/tape';
-
-import {isBrowser, readFileSync} from '@loaders.gl/core';
-
-const DATA_URL = 'data:,important content!';
-const BINARY_URL = '@loaders.gl/core/test/data/files/binary-data.bin';
-const TEXT_URL = '@loaders.gl/core/test/data/files/hello-world.txt';
-
-test('readFile#imports', (t) => {
-  t.ok(readFileSync, 'readFileSync defined');
-  t.end();
-});
-
-// Only support this if we can also support sync data URL decoding in browser
-test.skip('readFileSync#dataUrl', (t) => {
-  if (isBrowser) {
-    t.comment('Skip readFileSync in browser');
-    t.end();
-    return;
-  }
-
-  const data = readFileSync(DATA_URL);
-  t.ok(data, 'readFileSync loaded data url');
-  t.end();
-});
-
-test('readFileSync#file (BINARY)', (t) => {
-  if (isBrowser) {
-    t.comment('Skip file read in browser');
-    t.end();
-    return;
-  }
-
-  const data = readFileSync(BINARY_URL);
-  t.ok(data instanceof ArrayBuffer, 'readFileSync loaded local file into ArrayBuffer');
-  // @ts-expect-error
-  t.equals(data.byteLength, 4, 'readFileSync loaded local file length correctly');
-  t.end();
-});
-
-test('readFileSync#file (TEXT)', (t) => {
-  if (isBrowser) {
-    t.comment('Skip readFileSync in browser');
-    t.end();
-    return;
-  }
-
-  const data = readFileSync(TEXT_URL, {encoding: 'utf8'});
-  t.equals(typeof data, 'string', 'fetchFile loaded local file into string');
-  t.equals(data, 'Hello world!', 'fetchFile loaded local file data correctly');
-  t.end();
-});
diff --git a/modules/core/test/lib/filesystems/browser-filesystem.spec.js b/modules/core/test/lib/filesystems/browser-filesystem.spec.ts
similarity index 81%
rename from modules/core/test/lib/filesystems/browser-filesystem.spec.js
rename to modules/core/test/lib/filesystems/browser-filesystem.spec.ts
index f60599713b..e9d2c43089 100644
--- a/modules/core/test/lib/filesystems/browser-filesystem.spec.js
+++ b/modules/core/test/lib/filesystems/browser-filesystem.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {fetchFile, isBrowser} from '@loaders.gl/core';
 import {_BrowserFileSystem as BrowserFileSystem} from '@loaders.gl/core';
@@ -13,14 +16,16 @@ test('BrowserFileSystem#fetch', async (t) => {
   if (isBrowser) {
     const fileList = await loadImagesAsFiles();
     const fileSystem = new BrowserFileSystem(fileList);
+    // eslint-disable-next-line @typescript-eslint/unbound-method
     const {fetch} = fileSystem;
+
     for (const url of IMAGE_URLS) {
       const response = await fetch(url);
       t.ok(response.ok, `fetching file from browser file system: ${url}`);
     }
 
     const response = await fetch('bogus.txt');
-    t.notOk(response.ok, `fetching non-existent file from browser file system fails`);
+    t.notOk(response.ok, 'fetching non-existent file from browser file system fails');
   }
   t.end();
 });
@@ -29,7 +34,7 @@ test('BrowserFileSystem#fetch', async (t) => {
 
 const readFile = (url) => fetchFile(url).then((response) => response.arrayBuffer());
 
-let imagesPromise = null;
+let imagesPromise: Promise | null = null;
 
 /**
  * @returns {Promise}
diff --git a/modules/core/test/lib/init.spec.js b/modules/core/test/lib/init.spec.ts
similarity index 87%
rename from modules/core/test/lib/init.spec.js
rename to modules/core/test/lib/init.spec.ts
index 336e0ad7c3..b7e6d92ead 100644
--- a/modules/core/test/lib/init.spec.js
+++ b/modules/core/test/lib/init.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 // /* global loaders */
 // @ts-nocheck
 
diff --git a/modules/core/test/lib/loader-utils/auto-parse.spec.js b/modules/core/test/lib/loader-utils/auto-parse.spec.ts
similarity index 71%
rename from modules/core/test/lib/loader-utils/auto-parse.spec.js
rename to modules/core/test/lib/loader-utils/auto-parse.spec.ts
index d4415ba8ba..7812f4d05c 100644
--- a/modules/core/test/lib/loader-utils/auto-parse.spec.js
+++ b/modules/core/test/lib/loader-utils/auto-parse.spec.ts
@@ -1,10 +1,12 @@
-/* eslint-disable max-len */
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {load} from '@loaders.gl/core';
 import {OBJLoader} from '@loaders.gl/obj';
 import {KMLLoader} from '@loaders.gl/kml';
 
-const KML_URL = '@loaders.gl/kml/test/data/KML_Samples.kml';
+const KML_URL = '@loaders.gl/kml/test/data/kml/KML_Samples.kml';
 
 const LOADERS = [OBJLoader, KMLLoader];
 
diff --git a/modules/core/test/lib/loader-utils/get-data.spec.js b/modules/core/test/lib/loader-utils/get-data.spec.ts
similarity index 59%
rename from modules/core/test/lib/loader-utils/get-data.spec.js
rename to modules/core/test/lib/loader-utils/get-data.spec.ts
index cb5935d115..91f9ed6f79 100644
--- a/modules/core/test/lib/loader-utils/get-data.spec.js
+++ b/modules/core/test/lib/loader-utils/get-data.spec.ts
@@ -1,10 +1,13 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {
   getArrayBufferOrStringFromDataSync,
   getAsyncIterableFromData
 } from '@loaders.gl/core/lib/loader-utils/get-data';
 
-import {isBrowser, isIterator, JSONLoader} from '@loaders.gl/core';
+import {isIterator, JSONLoader} from '@loaders.gl/core';
 
 const BinaryLoader = {
   ...JSONLoader,
@@ -30,7 +33,7 @@ test('parseWithLoader#getArrayBufferOrStringFromDataSync', (t) => {
   t.end();
 });
 
-test('parseWithLoader#getArrayBufferOrStringFromDataSync(embedded arrays/buffers)', (t) => {
+test('parseWithLoader#getArrayBufferOrStringFromDataSync(embedded arrays)', (t) => {
   const string = 'line 1\nline 2';
   const embeddedString = `}}}${string}{{{`;
 
@@ -38,7 +41,7 @@ test('parseWithLoader#getArrayBufferOrStringFromDataSync(embedded arrays/buffers
   const typedArrayWithOffset = new Uint8Array(typedArray.buffer, 3, string.length);
 
   // Check that our offset array is correctly set up
-  let extractedString = new TextDecoder().decode(typedArrayWithOffset);
+  const extractedString = new TextDecoder().decode(typedArrayWithOffset);
   t.equals(extractedString, string);
 
   let result = getArrayBufferOrStringFromDataSync(typedArrayWithOffset, JSONLoader, {});
@@ -52,27 +55,42 @@ test('parseWithLoader#getArrayBufferOrStringFromDataSync(embedded arrays/buffers
     'typedArrayWithOffset to ArrayBuffer returns correct result'
   );
 
-  if (!isBrowser) {
-    const nodeBufferWithOffset = Buffer.from(typedArray.buffer, 3, string.length);
+  t.end();
+});
 
-    // Check that our offset array is correctly set up
-    extractedString = nodeBufferWithOffset.toString();
-    t.equals(extractedString, string);
+// TODO - skip because of Node.js Bbuffer dependency
+// test('parseWithLoader#getArrayBufferOrStringFromDataSync(embedded buffers)', (t) => {
+//   if (!isBrowser) {
+//     const string = 'line 1\nline 2';
+//     const embeddedString = `}}}${string}{{{`;
 
-    result = getArrayBufferOrStringFromDataSync(nodeBufferWithOffset, JSONLoader, {});
-    t.equals(result, string, 'BufferWithOffset to string returns correct result');
+//     const typedArray = new TextEncoder().encode(embeddedString);
+//     const typedArrayWithOffset = new Uint8Array(typedArray.buffer, 3, string.length);
 
-    result = getArrayBufferOrStringFromDataSync(nodeBufferWithOffset, BinaryLoader, {});
-    t.deepEquals(
-      // @ts-ignore
-      new Uint8Array(result),
-      typedArrayWithOffset,
-      'BufferWithOffset to ArrayBuffer returns correct result'
-    );
-  }
+//     // Check that our offset array is correctly set up
+//     let extractedString = new TextDecoder().decode(typedArrayWithOffset);
+//     t.equals(extractedString, string);
 
-  t.end();
-});
+//     const nodeBufferWithOffset = Buffer.from(typedArray.buffer, 3, string.length);
+
+//     // Check that our offset array is correctly set up
+//     extractedString = nodeBufferWithOffset.toString();
+//     t.equals(extractedString, string);
+
+//     let result = getArrayBufferOrStringFromDataSync(nodeBufferWithOffset, JSONLoader, {});
+//     t.equals(result, string, 'BufferWithOffset to string returns correct result');
+
+//     result = getArrayBufferOrStringFromDataSync(nodeBufferWithOffset, BinaryLoader, {});
+//     t.deepEquals(
+//       // @ts-ignore
+//       new Uint8Array(result),
+//       typedArrayWithOffset,
+//       'BufferWithOffset to ArrayBuffer returns correct result'
+//     );
+//   }
+
+//   t.end();
+// });
 
 test('parseWithLoader#getAsyncIterableFromData', async (t) => {
   const TESTS = [
diff --git a/modules/core/test/lib/loader-utils/loggers.spec.js b/modules/core/test/lib/loader-utils/loggers.spec.ts
similarity index 90%
rename from modules/core/test/lib/loader-utils/loggers.spec.js
rename to modules/core/test/lib/loader-utils/loggers.spec.ts
index 45ab7c381b..b8d947209e 100644
--- a/modules/core/test/lib/loader-utils/loggers.spec.js
+++ b/modules/core/test/lib/loader-utils/loggers.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {NullLog, ConsoleLog} from '@loaders.gl/core/lib/loader-utils/loggers';
 
diff --git a/modules/core/test/lib/loader-utils/normalize-loader.spec.js b/modules/core/test/lib/loader-utils/normalize-loader.spec.ts
similarity index 97%
rename from modules/core/test/lib/loader-utils/normalize-loader.spec.js
rename to modules/core/test/lib/loader-utils/normalize-loader.spec.ts
index 15769537d8..df0c877e49 100644
--- a/modules/core/test/lib/loader-utils/normalize-loader.spec.js
+++ b/modules/core/test/lib/loader-utils/normalize-loader.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 /* eslint-disable max-len */
 import test from 'tape-promise/tape';
 import {isLoaderObject, normalizeLoader} from '@loaders.gl/core/lib/loader-utils/normalize-loader';
diff --git a/modules/core/test/lib/loader-utils/option-utils.spec.js b/modules/core/test/lib/loader-utils/option-utils.spec.ts
similarity index 92%
rename from modules/core/test/lib/loader-utils/option-utils.spec.js
rename to modules/core/test/lib/loader-utils/option-utils.spec.ts
index 557a517016..21e3237118 100644
--- a/modules/core/test/lib/loader-utils/option-utils.spec.js
+++ b/modules/core/test/lib/loader-utils/option-utils.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 /* eslint-disable max-len */
 import test from 'tape-promise/tape';
 import {normalizeOptions} from '@loaders.gl/core/lib/loader-utils/option-utils';
diff --git a/modules/core/test/lib/progress/fetch-progress.spec.js b/modules/core/test/lib/progress/fetch-progress.spec.ts
similarity index 90%
rename from modules/core/test/lib/progress/fetch-progress.spec.js
rename to modules/core/test/lib/progress/fetch-progress.spec.ts
index 31737e4e71..5d2713e77c 100644
--- a/modules/core/test/lib/progress/fetch-progress.spec.js
+++ b/modules/core/test/lib/progress/fetch-progress.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {fetchFile, _fetchProgress} from '@loaders.gl/core';
 
diff --git a/modules/core/test/lib/utils/mime-type-utils.spec.js b/modules/core/test/lib/utils/mime-type-utils.spec.ts
similarity index 92%
rename from modules/core/test/lib/utils/mime-type-utils.spec.js
rename to modules/core/test/lib/utils/mime-type-utils.spec.ts
index 2d54f7b5a4..8af8524596 100644
--- a/modules/core/test/lib/utils/mime-type-utils.spec.js
+++ b/modules/core/test/lib/utils/mime-type-utils.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {parseMIMEType, parseMIMETypeFromURL} from '@loaders.gl/core/lib/utils/mime-type-utils';
 
diff --git a/modules/core/test/lib/utils/resource-utils.spec.js b/modules/core/test/lib/utils/resource-utils.spec.ts
similarity index 97%
rename from modules/core/test/lib/utils/resource-utils.spec.js
rename to modules/core/test/lib/utils/resource-utils.spec.ts
index 12af4624ec..d2b5c5b7fd 100644
--- a/modules/core/test/lib/utils/resource-utils.spec.js
+++ b/modules/core/test/lib/utils/resource-utils.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {isBrowser} from '@loaders.gl/core';
 import {
diff --git a/modules/core/test/lib/utils/response-utils.spec.js b/modules/core/test/lib/utils/response-utils.spec.ts
similarity index 85%
rename from modules/core/test/lib/utils/response-utils.spec.js
rename to modules/core/test/lib/utils/response-utils.spec.ts
index f1e9c08dfa..815fa45fe6 100644
--- a/modules/core/test/lib/utils/response-utils.spec.js
+++ b/modules/core/test/lib/utils/response-utils.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {isBrowser} from '@loaders.gl/core';
 import {
@@ -14,7 +17,11 @@ test('Response', async (t) => {
 });
 
 test('makeResponse', async (t) => {
-  const response = await makeResponse('abc');
+  const responseInput = new Response('abc');
+  let response = await makeResponse(responseInput);
+  t.equal(response, responseInput, 'makeResponse() returns response argument');
+
+  response = await makeResponse('abc');
   t.equal(response.headers.get('content-length'), '3', 'content-length was set by makeResponse');
   const text = await response.text();
   t.equal(text, 'abc', 'could be read as text');
@@ -27,6 +34,7 @@ test('makeResponse(File)', async (t) => {
     type: 'text/plain'
   });
   const response = await makeResponse(file);
+  // t.comment(JSON.stringify(response.headers, null, 2));
   t.equal(
     response.headers.get('content-length'),
     '3',
diff --git a/modules/crypto/package.json b/modules/crypto/package.json
index fa8a34f611..d6e988889d 100644
--- a/modules/crypto/package.json
+++ b/modules/crypto/package.json
@@ -1,8 +1,9 @@
 {
   "name": "@loaders.gl/crypto",
-  "version": "4.0.0-alpha.13",
+  "version": "4.0.3",
   "description": "Cryptographic/hashing plugins for loaders.gl",
   "license": "MIT",
+  "type": "module",
   "publishConfig": {
     "access": "public",
     "registry": "https://npm.pkg.github.com"
@@ -19,8 +20,15 @@
     "point cloud"
   ],
   "types": "dist/index.d.ts",
-  "main": "dist/es5/index.js",
-  "module": "dist/esm/index.js",
+  "main": "dist/index.cjs",
+  "module": "dist/index.js",
+  "exports": {
+    ".": {
+      "import": "./dist/index.js",
+      "require": "./dist/index.cjs",
+      "types": "./dist/index.d.ts"
+    }
+  },
   "sideEffects": false,
   "files": [
     "src",
@@ -28,15 +36,15 @@
     "README.md"
   ],
   "scripts": {
-    "pre-build": "npm run build-bundle && npm run build-worker && npm run build-worker-node",
-    "build-bundle": "esbuild src/bundle.ts --outfile=dist/dist.min.js --bundle --minify --sourcemap",
-    "build-worker": "esbuild src/workers/worker.ts --outfile=dist/crypto-worker.js --target=esnext --bundle --minify --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"",
-    "build-worker-node": "esbuild src/workers/worker.ts --outfile=dist/crypto-worker-node.js --platform=node --target=esnext,node16 --bundle --minify --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\""
+    "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-worker && npm run build-worker-node",
+    "build-bundle": "ocular-bundle ./src/index.ts",
+    "build-worker": "esbuild src/workers/crypto-worker.ts --outfile=dist/crypto-worker.js --target=esnext --bundle --minify --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"",
+    "build-worker-node": "esbuild src/workers/crypto-worker-node.ts --outfile=dist/crypto-worker-node.js --platform=node --target=esnext,node16 --bundle --minify --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\""
   },
   "dependencies": {
     "@babel/runtime": "^7.3.1",
-    "@loaders.gl/loader-utils": "4.0.0-alpha.13",
-    "@loaders.gl/worker-utils": "4.0.0-alpha.13",
+    "@loaders.gl/loader-utils": "4.0.3",
+    "@loaders.gl/worker-utils": "4.0.3",
     "@types/crypto-js": "^4.0.2"
   },
   "devDependencies": {
diff --git a/modules/crypto/src/bundle.ts b/modules/crypto/src/bundle.ts
deleted file mode 100644
index 0db0c48b55..0000000000
--- a/modules/crypto/src/bundle.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// @ts-nocheck
-const moduleExports = require('./index');
-globalThis.loaders = globalThis.loaders || {};
-module.exports = Object.assign(globalThis.loaders, moduleExports);
diff --git a/modules/crypto/src/index.ts b/modules/crypto/src/index.ts
index 6f8aaabe45..80a39aa1a7 100644
--- a/modules/crypto/src/index.ts
+++ b/modules/crypto/src/index.ts
@@ -1,4 +1,11 @@
-// import type {WorkerObject} from '@loaders.gl/worker-utils';
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
+// __VERSION__ is injected by babel-plugin-version-inline
+// @ts-ignore TS2304: Cannot find name '__VERSION__'.
+const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest';
+
+export {Hash} from './lib/hash';
 
 export {CRC32Hash} from './lib/crc32-hash';
 export {CRC32CHash} from './lib/crc32c-hash';
@@ -8,12 +15,6 @@ export {SHA256Hash} from './lib/sha256-hash';
 export {CryptoHash} from './lib/crypto-hash';
 export {NodeHash} from './lib/node-hash';
 
-export {hexToBase64 as _hexToBase64, toHex as _toHex} from './lib/utils/digest-utils';
-
-// __VERSION__ is injected by babel-plugin-version-inline
-// @ts-ignore TS2304: Cannot find name '__VERSION__'.
-const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest';
-
 /**
  * Small, fast worker for CRC32, CRC32c and MD5 Hashes
  */
@@ -40,3 +41,8 @@ export const CryptoJSWorker = {
     cryptojs: {}
   }
 };
+
+// EXPERIMENTAL
+
+export {encodeNumber, encodeHex, encodeBase64} from './lib/utils/digest-utils';
+export {asciiToBase64, base64ToAscii} from './lib/utils/base64-utils';
diff --git a/modules/crypto/src/lib/algorithms/crc32.ts b/modules/crypto/src/lib/algorithms/crc32.ts
index c24e64c485..79c001fedd 100644
--- a/modules/crypto/src/lib/algorithms/crc32.ts
+++ b/modules/crypto/src/lib/algorithms/crc32.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 // Inspired by https://gist.github.com/wqli78/1330293/6d85cc967f32cccfcbad94ae7d088a3dcfc14bd9
 
 /**
diff --git a/modules/crypto/src/lib/algorithms/crc32c.ts b/modules/crypto/src/lib/algorithms/crc32c.ts
index 5019011d01..0c8a6d6247 100644
--- a/modules/crypto/src/lib/algorithms/crc32c.ts
+++ b/modules/crypto/src/lib/algorithms/crc32c.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 // From: https://gist.github.com/wqli78/1330293/6d85cc967f32cccfcbad94ae7d088a3dcfc14bd9
 // CRC32 doesn't appear to be supported natively by crypto-js
 
diff --git a/modules/crypto/src/lib/algorithms/md5-wasm.ts b/modules/crypto/src/lib/algorithms/md5-wasm.ts
index b0a42069f4..ffab8db8f2 100644
--- a/modules/crypto/src/lib/algorithms/md5-wasm.ts
+++ b/modules/crypto/src/lib/algorithms/md5-wasm.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 /* eslint-disable */
 // @ts-nocheck
 /* !
@@ -326,7 +329,7 @@ function makeMD5WA() {
     setX = exports.setX;
     memView = mView;
     digestbytes = crypt.wordsToBytes(md5WA(message));
-    return options && options.asBytes ? digestbytes : crypt.bytesToHex(digestbytes);
+    return options && options.asBytes ? digestbytes : crypt.bytesconvertNumberToHex(digestbytes);
   };
 }
 
@@ -482,7 +485,8 @@ function makeMD5JS() {
 
   return function (message, options) {
     var digestbytes = crypt.wordsToBytes(md5JS(message, options)),
-      result = options && options.asBytes ? digestbytes : crypt.bytesToHex(digestbytes);
+      result =
+        options && options.asBytes ? digestbytes : crypt.bytesconvertNumberToHex(digestbytes);
     return result;
   };
 }
@@ -534,7 +538,7 @@ function makeCrypt() {
       return bytes;
     },
 
-    bytesToHex: function (bytes) {
+    bytesconvertNumberToHex: function (bytes) {
       for (var hex = [], i = 0; i < bytes.length; i++) {
         hex.push((bytes[i] >>> 4).toString(16));
         hex.push((bytes[i] & 0xf).toString(16));
diff --git a/modules/crypto/src/lib/crc32-hash.ts b/modules/crypto/src/lib/crc32-hash.ts
index 932efd7f5f..25bf76eba1 100644
--- a/modules/crypto/src/lib/crc32-hash.ts
+++ b/modules/crypto/src/lib/crc32-hash.ts
@@ -1,7 +1,9 @@
-// CRC32
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import {Hash} from './hash';
 import CRC32 from './algorithms/crc32';
-import {toHex, hexToBase64} from './utils/digest-utils';
+import {encodeNumber} from './utils/digest-utils';
 
 /**
  * Calculates CRC32 Cryptographic Hash
@@ -23,28 +25,26 @@ export class CRC32Hash extends Hash {
    * Atomic hash calculation
    * @returns base64 encoded hash
    */
-  async hash(input: ArrayBuffer): Promise {
-    return this.hashSync(input);
+  async hash(input: ArrayBuffer, encoding: 'hex' | 'base64'): Promise {
+    return this.hashSync(input, encoding);
   }
 
-  hashSync(input: ArrayBuffer): string {
+  hashSync(input: ArrayBuffer, encoding: 'hex' | 'base64'): string {
     this._hash.update(input);
-    const hashValue = this._hash.finalize();
-    const hex = toHex(hashValue);
-    const hash = hexToBase64(hex);
-    return hash;
+    const digest = this._hash.finalize();
+    return encodeNumber(digest, encoding);
   }
 
   async *hashBatches(
-    asyncIterator: AsyncIterable | Iterable
+    asyncIterator: AsyncIterable | Iterable,
+    encoding: 'hex' | 'base64' = 'base64'
   ): AsyncIterable {
     for await (const chunk of asyncIterator) {
       this._hash.update(chunk);
       yield chunk;
     }
-    const hashValue = this._hash.finalize();
-    const hex = toHex(hashValue);
-    const hash = hexToBase64(hex);
+    const digest = this._hash.finalize();
+    const hash = encodeNumber(digest, encoding);
     this.options.crypto?.onEnd?.({hash});
   }
 }
diff --git a/modules/crypto/src/lib/crc32c-hash.ts b/modules/crypto/src/lib/crc32c-hash.ts
index c50e79c552..9a623a04e2 100644
--- a/modules/crypto/src/lib/crc32c-hash.ts
+++ b/modules/crypto/src/lib/crc32c-hash.ts
@@ -1,7 +1,9 @@
-// CRC32c
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import {Hash} from './hash';
 import CRC32C from './algorithms/crc32c';
-import {toHex, hexToBase64} from './utils/digest-utils';
+import {encodeNumber} from './utils/digest-utils';
 
 /**
  * A transform that calculates CRC32c Hash
@@ -26,30 +28,28 @@ export class CRC32CHash extends Hash {
    * Atomic hash calculation
    * @returns base64 encoded hash
    */
-  async hash(input: ArrayBuffer): Promise {
-    return this.hashSync(input);
+  async hash(input: ArrayBuffer, encoding: 'hex' | 'base64'): Promise {
+    return this.hashSync(input, encoding);
   }
 
-  hashSync(input: ArrayBuffer): string {
+  hashSync(input: ArrayBuffer, encoding: 'hex' | 'base64'): string {
     this._hash.update(input);
-    const hashValue = this._hash.finalize();
-    const hex = toHex(hashValue);
-    const hash = hexToBase64(hex);
-    return hash;
+    const digest = this._hash.finalize();
+    return encodeNumber(digest, encoding);
   }
 
   // runInBatches inherited
 
   async *hashBatches(
-    asyncIterator: AsyncIterable | Iterable
+    asyncIterator: AsyncIterable | Iterable,
+    encoding: 'hex' | 'base64' = 'base64'
   ): AsyncIterable {
     for await (const chunk of asyncIterator) {
       this._hash.update(chunk);
       yield chunk;
     }
-    const hashValue = this._hash.finalize();
-    const hex = toHex(hashValue);
-    const hash = hexToBase64(hex);
+    const digest = this._hash.finalize();
+    const hash = encodeNumber(digest, encoding);
     this.options.crypto?.onEnd?.({hash});
   }
 }
diff --git a/modules/crypto/src/lib/crypto-hash.ts b/modules/crypto/src/lib/crypto-hash.ts
index 19f27a52e1..98f58171fd 100644
--- a/modules/crypto/src/lib/crypto-hash.ts
+++ b/modules/crypto/src/lib/crypto-hash.ts
@@ -1,16 +1,14 @@
-// This dependency is too big, application must provide it
-import type * as CryptoJSNamespace from 'crypto-js';
 import {Hash} from './hash';
 
 type CryptoHashOptions = {
-  modules: {[moduleName: string]: any};
+  modules?: {[moduleName: string]: any};
   crypto: {
     algorithm: string;
     onEnd?: (result: {hash: string}) => any;
   };
 };
 
-let CryptoJS: typeof CryptoJSNamespace;
+let CryptoJS: any;
 
 /**
  * A transform that calculates Cryptographic Hash using Crypto JS library
@@ -36,9 +34,9 @@ export class CryptoHash extends Hash {
   async preload(): Promise {
     if (!CryptoJS) {
       CryptoJS = this.options?.modules?.CryptoJS;
-      if (!CryptoJS) {
-        throw new Error(this.name);
-      }
+    }
+    if (!CryptoJS) {
+      throw new Error(this.name);
     }
     if (!this._hash) {
       const algo = CryptoJS.algo[this._algorithm];
@@ -53,28 +51,31 @@ export class CryptoHash extends Hash {
    * Atomic hash calculation
    * @returns base64 encoded hash
    */
-  async hash(input: ArrayBuffer): Promise {
+  async hash(input: ArrayBuffer, encoding: 'hex' | 'base64'): Promise {
     await this.preload();
     // arrayBuffer is accepted, even though types and docs say no
     // https://stackoverflow.com/questions/25567468/how-to-decrypt-an-arraybuffer
-    // @ts-expect-error
     const typedWordArray = CryptoJS.lib.WordArray.create(input);
-    return this._hash.update(typedWordArray).finalize().toString(CryptoJS.enc.Base64);
+    // Map our encoding constant to Crypto library
+    const enc = encoding === 'base64' ? CryptoJS.enc.Base64 : CryptoJS.enc.Hex;
+    return this._hash.update(typedWordArray).finalize().toString(enc);
   }
 
   async *hashBatches(
-    asyncIterator: AsyncIterable | Iterable
+    asyncIterator: AsyncIterable | Iterable,
+    encoding: 'hex' | 'base64' = 'base64'
   ): AsyncIterable {
     await this.preload();
     for await (const chunk of asyncIterator) {
       // arrayBuffer is accepted, even though types and docs say no
       // https://stackoverflow.com/questions/25567468/how-to-decrypt-an-arraybuffer
-      // @ts-expect-error
       const typedWordArray = CryptoJS.lib.WordArray.create(chunk);
       this._hash.update(typedWordArray);
       yield chunk;
     }
-    const hash = this._hash.finalize().toString(CryptoJS.enc.Base64);
-    this.options?.crypto?.onEnd?.({hash});
+    // Map our encoding constant to Crypto library
+    const enc = encoding === 'base64' ? CryptoJS.enc.Base64 : CryptoJS.enc.Hex;
+    const digest = this._hash.finalize().toString(enc);
+    this.options?.crypto?.onEnd?.({hash: digest});
   }
 }
diff --git a/modules/crypto/src/lib/hash.ts b/modules/crypto/src/lib/hash.ts
index e351c4e400..2b4eb17a62 100644
--- a/modules/crypto/src/lib/hash.ts
+++ b/modules/crypto/src/lib/hash.ts
@@ -1,4 +1,6 @@
-// Hash
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import {concatenateArrayBuffersAsync} from '@loaders.gl/loader-utils';
 
 type HashOptions = {
@@ -21,10 +23,11 @@ export abstract class Hash {
     return;
   }
 
-  abstract hash(arrayBuffer: ArrayBuffer): Promise;
+  abstract hash(arrayBuffer: ArrayBuffer, encoding: 'hex' | 'base64'): Promise;
 
   async *hashBatches(
-    asyncIterator: AsyncIterable | Iterable
+    asyncIterator: AsyncIterable | Iterable,
+    encoding: 'hex' | 'base64' = 'base64'
   ): AsyncIterable {
     const arrayBuffers: ArrayBuffer[] = [];
     for await (const arrayBuffer of asyncIterator) {
@@ -32,7 +35,7 @@ export abstract class Hash {
       yield arrayBuffer;
     }
     const output = await this.concatenate(arrayBuffers);
-    const hash = await this.hash(output);
+    const hash = await this.hash(output, encoding);
     this.options.crypto?.onEnd?.({hash});
   }
 
diff --git a/modules/crypto/src/lib/md5-hash.ts b/modules/crypto/src/lib/md5-hash.ts
index 8078efd142..6545c97cd0 100644
--- a/modules/crypto/src/lib/md5-hash.ts
+++ b/modules/crypto/src/lib/md5-hash.ts
@@ -1,7 +1,10 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 // Fork of https://github.com/briantbutton/md5-wasm under MIT license
 import {Hash} from './hash';
 import md5WASM from './algorithms/md5-wasm';
-import {hexToBase64} from './utils/digest-utils';
+import {encodeHex} from './utils/digest-utils';
 
 /**
  * A transform that calculates MD5 hashes, passing data through
@@ -19,12 +22,12 @@ export class MD5Hash extends Hash {
    * Atomic hash calculation
    * @returns base64 encoded hash
    */
-  async hash(input: ArrayBuffer): Promise {
+  async hash(input: ArrayBuffer, encoding: 'hex' | 'base64'): Promise {
     const md5Promise = new Promise((resolve, reject) =>
       // @ts-expect-error
       md5WASM(input).then(resolve).catch(reject)
     );
     const hex = await md5Promise;
-    return hexToBase64(hex);
+    return encodeHex(hex, encoding);
   }
 }
diff --git a/modules/crypto/src/lib/node-hash.ts b/modules/crypto/src/lib/node-hash.ts
index 74457ce604..e2432f0988 100644
--- a/modules/crypto/src/lib/node-hash.ts
+++ b/modules/crypto/src/lib/node-hash.ts
@@ -1,8 +1,9 @@
-// This dependency is too big, application must provide it
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import {Hash} from './hash';
-import {createHash} from 'crypto'; // Node.js builtin
 
-type CryptoHashOptions = {
+type NodeHashOptions = {
   crypto: {
     algorithm: string;
     onEnd?: (result: {hash: string}) => any;
@@ -10,48 +11,27 @@ type CryptoHashOptions = {
 };
 
 /**
- * Calculates Cryptographic Hash using Node.js crypto library
- * @deprecated Warning, experimental class
+ * A transform that calculates Cryptographic Hash using Node's Crypto library
+ * @deprecated Only available in Node.js
  */
 export class NodeHash extends Hash {
-  readonly name = 'crypto-node';
-
-  options: CryptoHashOptions;
-  // @ts-ignore
-  private _algorithm;
-  // @ts-ignore
-  private _hash;
+  readonly name;
+  readonly options: NodeHashOptions;
 
-  constructor(options: CryptoHashOptions) {
+  constructor(options: NodeHashOptions) {
     super();
     this.options = options;
-    if (!this.options?.crypto?.algorithm) {
-      throw new Error(this.name);
+    if (!globalThis.loaders.NodeHash) {
+      throw new Error('install @loaders.gl/crypto on Node.js to use NodeHash');
     }
+    return new globalThis.loaders.NodeHash(options);
   }
 
   /**
    * Atomic hash calculation
    * @returns base64 encoded hash
    */
-  async hash(input: ArrayBuffer): Promise {
-    await this.preload();
-    const hash = createHash(this.options?.crypto?.algorithm?.toLowerCase());
-    const inputArray = new Uint8Array(input);
-    return hash.update(inputArray).digest('base64');
-  }
-
-  async *hashBatches(
-    asyncIterator: AsyncIterable | Iterable
-  ): AsyncIterable {
-    await this.preload();
-    const hash = createHash(this.options?.crypto?.algorithm?.toLowerCase());
-    for await (const chunk of asyncIterator) {
-      // https://stackoverflow.com/questions/25567468/how-to-decrypt-an-arraybuffer
-      const inputArray = new Uint8Array(chunk);
-      hash.update(inputArray);
-      yield chunk;
-    }
-    this.options?.crypto?.onEnd?.({hash: hash.digest('base64')});
+  async hash(input: ArrayBuffer, encoding: 'hex' | 'base64'): Promise {
+    throw new Error('Not implemented');
   }
 }
diff --git a/modules/crypto/src/lib/sha256-hash.ts b/modules/crypto/src/lib/sha256-hash.ts
index dc6ff94fe3..f82ef4177b 100644
--- a/modules/crypto/src/lib/sha256-hash.ts
+++ b/modules/crypto/src/lib/sha256-hash.ts
@@ -1,4 +1,6 @@
-//
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import {CryptoHash} from './crypto-hash';
 
 type CryptoHashOptions = {
diff --git a/modules/crypto/src/lib/utils/base64-utils.ts b/modules/crypto/src/lib/utils/base64-utils.ts
index c6bf5c6884..3c5a5a5859 100644
--- a/modules/crypto/src/lib/utils/base64-utils.ts
+++ b/modules/crypto/src/lib/utils/base64-utils.ts
@@ -1,8 +1,11 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 /**
  * `btoa()` polyfill as defined by the HTML and Infra specs, which mostly just references
  * RFC 4648.
  */
-export function toBase64(string: string): string | null {
+export function asciiToBase64(string: string): string | null {
   // String conversion as required by Web IDL.
   string = `${string}`;
   // "The btoa() method must throw an "InvalidCharacterError" DOMException if
@@ -38,6 +41,102 @@ export function toBase64(string: string): string | null {
   return out;
 }
 
+/**
+ * Implementation of atob() according to the HTML and Infra specs, except that
+ * instead of throwing INVALID_CHARACTER_ERR we return null.
+ *
+ * @note Forked from https://github.com/jsdom/abab under BSD 3 clause license
+ */
+export function base64ToAscii(data: string): string {
+  // Web IDL requires DOMStrings to just be converted using ECMAScript
+  // ToString, which in our case amounts to using a template literal.
+  data = `${data}`;
+  // "Remove all ASCII whitespace from data."
+  data = data.replace(/[ \t\n\f\r]/g, '');
+  // "If data's code point length divides by 4 leaving no remainder, then: if data ends
+  // with one or two U+003D (=) code points, then remove them from data."
+  if (data.length % 4 === 0) {
+    data = data.replace(/[=]=?$/, '');
+  }
+  // "If data's code point length divides by 4 leaving a remainder of 1, then return
+  // failure."
+  //
+  // "If data contains a code point that is not one of
+  //
+  // U+002B (+)
+  // U+002F (/)
+  // ASCII alphanumeric
+  //
+  // then return failure."
+  if (data.length % 4 === 1 || /[^+/0-9A-Za-z]/.test(data)) {
+    return '';
+  }
+  // "Let output be an empty byte sequence."
+  let output = '';
+  // "Let buffer be an empty buffer that can have bits appended to it."
+  //
+  // We append bits via left-shift and or.  accumulatedBits is used to track
+  // when we've gotten to 24 bits.
+  let buffer = 0;
+  let accumulatedBits = 0;
+  // "Let position be a position variable for data, initially pointing at the
+  // start of data."
+  //
+  // "While position does not point past the end of data:"
+  for (let i = 0; i < data.length; i++) {
+    // "Find the code point pointed to by position in the second column of
+    // Table 1: The Base 64 Alphabet of RFC 4648. Let n be the number given in
+    // the first cell of the same row.
+    //
+    // "Append to buffer the six bits corresponding to n, most significant bit
+    // first."
+    //
+    // atobLookup() implements the table from RFC 4648.
+    buffer <<= 6;
+    // @ts-expect-error
+    buffer |= atobLookup(data[i]);
+    accumulatedBits += 6;
+    // "If buffer has accumulated 24 bits, interpret them as three 8-bit
+    // big-endian numbers. Append three bytes with values equal to those
+    // numbers to output, in the same order, and then empty buffer."
+    if (accumulatedBits === 24) {
+      output += String.fromCharCode((buffer & 0xff0000) >> 16);
+      output += String.fromCharCode((buffer & 0xff00) >> 8);
+      output += String.fromCharCode(buffer & 0xff);
+      buffer = accumulatedBits = 0;
+    }
+    // "Advance position by 1."
+  }
+  // "If buffer is not empty, it contains either 12 or 18 bits. If it contains
+  // 12 bits, then discard the last four and interpret the remaining eight as
+  // an 8-bit big-endian number. If it contains 18 bits, then discard the last
+  // two and interpret the remaining 16 as two 8-bit big-endian numbers. Append
+  // the one or two bytes with values equal to those one or two numbers to
+  // output, in the same order."
+  if (accumulatedBits === 12) {
+    buffer >>= 4;
+    output += String.fromCharCode(buffer);
+  } else if (accumulatedBits === 18) {
+    buffer >>= 2;
+    output += String.fromCharCode((buffer & 0xff00) >> 8);
+    output += String.fromCharCode(buffer & 0xff);
+  }
+  // "Return output."
+  return output;
+}
+/**
+ * A lookup table for atob(), which converts an ASCII character to the
+ * corresponding six-bit number.
+ */
+
+const keystr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+function atobLookup(chr: string): number | undefined {
+  const index = keystr.indexOf(chr);
+  // Throw exception if character is not in the lookup string; should not be hit in tests
+  return index < 0 ? undefined : index;
+}
+
 /**
  * Lookup table for btoa(), which converts a six-bit number into the
  * corresponding ASCII character.
diff --git a/modules/crypto/src/lib/utils/digest-utils.ts b/modules/crypto/src/lib/utils/digest-utils.ts
index b46ea467cb..418ad7f4e8 100644
--- a/modules/crypto/src/lib/utils/digest-utils.ts
+++ b/modules/crypto/src/lib/utils/digest-utils.ts
@@ -1,17 +1,50 @@
-import {toBase64} from './base64-utils';
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
+import {asciiToBase64, base64ToAscii} from './base64-utils';
 
 /**
- *
+ * Encode a number (usually a digest from a hash function / cipher) as either hex or base64
+ * Suitable for hashes like CRC32 where the number of required bits fit withing a JavaScript number.
  */
-export function toHex(cipher: number): string {
-  const hexString = cipher.toString(16);
-  return hexString === '0' ? `0${hexString}` : hexString;
+export function encodeNumber(number: number, encoding: 'hex' | 'base64'): string {
+  switch (encoding) {
+    case 'hex':
+      return convertNumberToHex(number);
+    case 'base64':
+      return convertHexToBase64(convertNumberToHex(number));
+    default:
+      throw new Error(encoding);
+  }
+}
+
+/** Encode a hex string, aeither return hex or re-encode as base64 */
+export function encodeHex(hex: string, encoding: 'hex' | 'base64'): string {
+  switch (encoding) {
+    case 'hex':
+      return hex;
+    case 'base64':
+      return convertHexToBase64(hex);
+    default:
+      throw new Error(encoding);
+  }
+}
+
+export function encodeBase64(base64: string, encoding: 'hex' | 'base64'): string {
+  switch (encoding) {
+    case 'hex':
+      return convertBase64ToHex(base64);
+    case 'base64':
+      return base64;
+    default:
+      throw new Error(encoding);
+  }
 }
 
 /**
- * @see https://stackoverflow.com/questions/23190056/hex-to-base64-converter-for-javascript
+ * Convert a hexadecimal string to base64 encoded string representation
  */
-export function hexToBase64(hexstring: string): string {
+function convertHexToBase64(hexstring: string): string {
   // Add leading zero to keep even count of digits
   // eg. f85d741 => 0f85d741
   if (hexstring.length % 2 !== 0) {
@@ -20,5 +53,22 @@ export function hexToBase64(hexstring: string): string {
   const matches = hexstring.match(/\w{2}/g) || [];
   const string = matches.map((a) => String.fromCharCode(parseInt(a, 16))).join('');
   // TODO - define how to handle failures
-  return toBase64(string) || '';
+  return asciiToBase64(string) || '';
+}
+
+/**
+ * Convert a base64 encoded string to hexadecimal encoded string representation
+ */
+function convertBase64ToHex(base64String: string): string {
+  return [...base64ToAscii(base64String)]
+    .map((c) => c.charCodeAt(0).toString(16).padStart(2, '0'))
+    .join('');
+}
+
+/**
+ * Converts a number to hex
+ */
+function convertNumberToHex(cipher: number): string {
+  const hexString = cipher.toString(16);
+  return hexString === '0' ? `0${hexString}` : hexString;
 }
diff --git a/modules/crypto/src/types.ts b/modules/crypto/src/types.ts
index f62192433e..e942571d96 100644
--- a/modules/crypto/src/types.ts
+++ b/modules/crypto/src/types.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 export type CryptoHashOptions = {
   modules?: {[moduleName: string]: any};
   crypto?: {
diff --git a/modules/crypto/src/workers/crypto-worker-node.ts b/modules/crypto/src/workers/crypto-worker-node.ts
new file mode 100644
index 0000000000..62c1d028e7
--- /dev/null
+++ b/modules/crypto/src/workers/crypto-worker-node.ts
@@ -0,0 +1 @@
+import './crypto-worker';
diff --git a/modules/crypto/src/workers/worker.ts b/modules/crypto/src/workers/crypto-worker.ts
similarity index 60%
rename from modules/crypto/src/workers/worker.ts
rename to modules/crypto/src/workers/crypto-worker.ts
index 1c01354a43..fef4477188 100644
--- a/modules/crypto/src/workers/worker.ts
+++ b/modules/crypto/src/workers/crypto-worker.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import {createWorker} from '@loaders.gl/worker-utils';
 import {CRC32Hash} from '../lib/crc32-hash';
 import {CRC32CHash} from '../lib/crc32c-hash';
@@ -8,15 +11,15 @@ export {CRC32Hash, CRC32CHash};
 
 createWorker(async (data, options = {}) => {
   // @ts-ignore
-  const {operation} = options;
+  const {operation, encoding = 'base64'} = options;
 
   switch (operation) {
     case 'crc32':
-      return await new CRC32Hash(options).hash(data);
+      return await new CRC32Hash(options).hash(data, encoding);
     case 'crc32c':
-      return await new CRC32CHash(options).hash(data);
+      return await new CRC32CHash(options).hash(data, encoding);
     case 'md5':
-      return await new MD5Hash(options).hash(data);
+      return await new MD5Hash(options).hash(data, encoding);
     default:
       throw new Error(`invalid option: ${operation}`);
   }
diff --git a/modules/crypto/src/workers/cryptojs-worker.ts.disabled b/modules/crypto/src/workers/cryptojs-worker.ts.disabled
index 583ad587d9..53e5d3b331 100644
--- a/modules/crypto/src/workers/cryptojs-worker.ts.disabled
+++ b/modules/crypto/src/workers/cryptojs-worker.ts.disabled
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import type {CryptoHashOptions} from '../lib/crypto-hash';
 import {createWorker} from '@loaders.gl/worker-utils';
 import {CryptoHash} from '../lib/crypto-hash';
diff --git a/modules/crypto/test/crypto-worker.spec.js b/modules/crypto/test/crypto-worker.spec.ts
similarity index 96%
rename from modules/crypto/test/crypto-worker.spec.js
rename to modules/crypto/test/crypto-worker.spec.ts
index 41d11f481c..dd697e5b59 100644
--- a/modules/crypto/test/crypto-worker.spec.js
+++ b/modules/crypto/test/crypto-worker.spec.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {processOnWorker, isBrowser, WorkerFarm} from '@loaders.gl/worker-utils';
 import {CryptoWorker, CryptoJSWorker} from '@loaders.gl/crypto';
diff --git a/modules/crypto/test/crypto.bench.js b/modules/crypto/test/crypto.bench.ts
similarity index 70%
rename from modules/crypto/test/crypto.bench.js
rename to modules/crypto/test/crypto.bench.ts
index 58685f1bf0..715c99ede8 100644
--- a/modules/crypto/test/crypto.bench.js
+++ b/modules/crypto/test/crypto.bench.ts
@@ -1,6 +1,9 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import {CRC32Hash, CRC32CHash, MD5Hash, SHA256Hash, CryptoHash} from '@loaders.gl/crypto';
 import {getBinaryData} from './test-utils/test-utils';
-import * as CryptoJS from 'crypto-js';
+import CryptoJS from 'crypto-js';
 
 export default async function cryptoBench(bench) {
   const {binaryData} = getBinaryData();
@@ -8,27 +11,27 @@ export default async function cryptoBench(bench) {
   bench = bench.group('Cryptographic Hash');
 
   bench = bench.addAsync('CRC32Hash#hash()', {multiplier: 100000, unit: 'bytes'}, () =>
-    new CRC32Hash().hash(binaryData)
+    new CRC32Hash().hash(binaryData, 'base64')
   );
 
   bench = bench.addAsync('CRC32CHash#hash()', {multiplier: 100000, unit: 'bytes'}, () =>
-    new CRC32CHash().hash(binaryData)
+    new CRC32CHash().hash(binaryData, 'base64')
   );
 
   bench = bench.addAsync('MD5Hash#hash(warmup)', {multiplier: 100000, unit: 'bytes'}, () =>
-    new MD5Hash().hash(binaryData)
+    new MD5Hash().hash(binaryData, 'base64')
   );
 
   bench = bench.addAsync('MD5Hash#hash()', {multiplier: 100000, unit: 'bytes'}, () =>
-    new MD5Hash().hash(binaryData)
+    new MD5Hash().hash(binaryData, 'base64')
   );
 
   bench = bench.addAsync('CryptoHash(MD5)#hash()', {multiplier: 100000, unit: 'bytes'}, () =>
-    new CryptoHash({modules: {CryptoJS}, crypto: {algorithm: 'MD5'}}).hash(binaryData)
+    new CryptoHash({modules: {CryptoJS}, crypto: {algorithm: 'MD5'}}).hash(binaryData, 'base64')
   );
 
   bench = bench.addAsync('SHA256Hash#hash()', {multiplier: 100000, unit: 'bytes'}, () =>
-    new SHA256Hash({modules: {CryptoJS}}).hash(binaryData)
+    new SHA256Hash({modules: {CryptoJS}}).hash(binaryData, 'base64')
   );
 
   return bench;
diff --git a/modules/crypto/test/crypto.spec.js b/modules/crypto/test/crypto.spec.ts
similarity index 90%
rename from modules/crypto/test/crypto.spec.js
rename to modules/crypto/test/crypto.spec.ts
index 50d7b344e8..e60d5be2e4 100644
--- a/modules/crypto/test/crypto.spec.js
+++ b/modules/crypto/test/crypto.spec.ts
@@ -1,9 +1,12 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
-import {isBrowser, fetchFile, loadInBatches, NullLoader} from '@loaders.gl/core';
+import {fetchFile, loadInBatches, NullLoader, isBrowser} from '@loaders.gl/core';
 import {CRC32Hash, CRC32CHash, MD5Hash, SHA256Hash, NodeHash} from '@loaders.gl/crypto';
 import {getBinaryData} from './test-utils/test-utils';
 
-import * as CryptoJS from 'crypto-js';
+import CryptoJS from 'crypto-js';
 
 const modules = {CryptoJS};
 
@@ -47,8 +50,7 @@ test('crypto#atomic hashes', async (t) => {
     for (const algorithm in tc.digests) {
       const cryptoHash = getHash(algorithm);
 
-      // @ts-expect-error
-      const hash = await cryptoHash.hash(tc.data);
+      const hash = await cryptoHash.hash(tc.data, 'base64');
       const expectedHash = tc.digests[algorithm];
       t.equal(hash, expectedHash, `${algorithm} hash is correct for ${tc.title}`);
     }
@@ -98,14 +100,14 @@ test('NodeHash#hash', async (t) => {
   if (!isBrowser) {
     const cryptoHash = new NodeHash({crypto: {algorithm: 'SHA256'}});
 
-    let hash = await cryptoHash.hash(binaryData);
+    let hash = await cryptoHash.hash(binaryData, 'base64');
     t.equal(
       hash,
       'gsoMi29gqdIBCEdTdRJW8VPFx5PQyFPTF4Lv7TJ4eQw=',
       'binary data SHA256 hash is correct'
     );
 
-    hash = await cryptoHash.hash(repeatedData);
+    hash = await cryptoHash.hash(repeatedData, 'base64');
     t.equal(
       hash,
       'bSCTuOJei5XsmAnqtmm2Aw/2EvUHldNdAxYb3mjSK9s=',
diff --git a/modules/crypto/test/index.js b/modules/crypto/test/index.ts
similarity index 76%
rename from modules/crypto/test/index.js
rename to modules/crypto/test/index.ts
index f3bdc147d6..b568180c72 100644
--- a/modules/crypto/test/index.js
+++ b/modules/crypto/test/index.ts
@@ -1,9 +1,12 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 // crypto module tests
 import './lib/utils/digest-utils.spec';
 
 import './crypto.spec';
 // Additional test cases
 import './lib/crc32c-hash.spec';
-import './lib/crypto-hash.spec';
 
+import './lib/crypto-hash.spec';
 import './crypto-worker.spec';
diff --git a/modules/crypto/test/lib/crc32c-hash.spec.js b/modules/crypto/test/lib/crc32c-hash.spec.ts
similarity index 76%
rename from modules/crypto/test/lib/crc32c-hash.spec.js
rename to modules/crypto/test/lib/crc32c-hash.spec.ts
index 453bb261f3..3f2acbaf7e 100644
--- a/modules/crypto/test/lib/crc32c-hash.spec.js
+++ b/modules/crypto/test/lib/crc32c-hash.spec.ts
@@ -1,7 +1,10 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import test from 'tape-promise/tape';
 import {fetchFile} from '@loaders.gl/core';
-import {CRC32CHash, _hexToBase64, _toHex} from '@loaders.gl/crypto';
-import TEST_CASES from './crc32c-test-cases.json';
+import {CRC32CHash, encodeNumber} from '@loaders.gl/crypto';
+import TEST_CASES from './crc32c-test-cases.json' assert {type: 'json'};
 
 test('crc32c#additional tests', async (t) => {
   for (const type in TEST_CASES) {
@@ -13,14 +16,14 @@ test('crc32c#additional tests', async (t) => {
       const mediaType = tc.charset ? `charset=${tc.charset}` : 'base64';
       const response = await fetchFile(`data:${mediaType},${tc.input}`);
       tc.arrayBuffer = await response.arrayBuffer();
-      tc.expected = _hexToBase64(_toHex(tc.want));
+      tc.expected = encodeNumber(tc.want, 'base64');
     }
-    set.expected = _hexToBase64(_toHex(set));
+    set.expected = encodeNumber(set, 'base64');
 
     // Run the test cases
     for (const tc of set.cases) {
       if (tc.expected && !tc.charset) {
-        const hash = await new CRC32CHash().hash(tc.arrayBuffer);
+        const hash = await new CRC32CHash().hash(tc.arrayBuffer, 'base64');
         t.equals(
           hash,
           tc.expected,
diff --git a/modules/crypto/test/lib/crypto-hash.spec.js b/modules/crypto/test/lib/crypto-hash.spec.ts
similarity index 87%
rename from modules/crypto/test/lib/crypto-hash.spec.js
rename to modules/crypto/test/lib/crypto-hash.spec.ts
index d5f43552d3..ab2545a2e6 100644
--- a/modules/crypto/test/lib/crypto-hash.spec.js
+++ b/modules/crypto/test/lib/crypto-hash.spec.ts
@@ -1,10 +1,15 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
+/** eslint-disable @typescript-eslint/unbound-method */
+
 import test from 'tape-promise/tape';
 import {compareArrayBuffers, getBinaryData} from '../test-utils/test-utils';
 import {concatenateArrayBuffers, concatenateArrayBuffersAsync} from '@loaders.gl/loader-utils';
 import {fetchFile, loadInBatches} from '@loaders.gl/core';
 import {CSVLoader} from '@loaders.gl/csv';
 import {CryptoHash} from '@loaders.gl/crypto';
-import * as CryptoJS from 'crypto-js';
+import CryptoJS from 'crypto-js';
 
 const CSV_URL = '@loaders.gl/csv/test/data/sample-very-long.csv';
 /** Externally computed hash: `openssl md5 -binary sample-very-long.json | openssl base64` */
@@ -14,7 +19,10 @@ test('CryptoHash#hash(CSV, against external hash)', async (t) => {
   const response = await fetchFile(CSV_URL);
   const data = await response.arrayBuffer();
 
-  const hash = await new CryptoHash({modules: {CryptoJS}, crypto: {algorithm: 'MD5'}}).hash(data);
+  const hash = await new CryptoHash({modules: {CryptoJS}, crypto: {algorithm: 'MD5'}}).hash(
+    data,
+    'base64'
+  );
   t.equal(hash, CSV_MD5, 'repeated data MD5 hash is correct');
 
   t.end();
@@ -34,14 +42,14 @@ test('CryptoHash#iterator(CSV stream, against external hash)', async (t) => {
   });
 
   const csvIterator = await loadInBatches(CSV_URL, CSVLoader, {
-    transforms: [cryptoHash.hashBatches]
+    transforms: [cryptoHash.hashBatches.bind(cryptoHash)]
   });
 
   let csv;
   for await (const batch of csvIterator) {
     csv = batch;
   }
-  t.ok(Array.isArray(csv.data), 'parsing from wrapped iterator works');
+  t.ok(Array.isArray(csv?.data), 'parsing from wrapped iterator works');
 
   t.equal(hash, CSV_MD5, 'streaming MD5 hash is correct');
 
@@ -56,11 +64,11 @@ test('CryptoHash#hash(MD5 = default)', async (t) => {
     crypto: {algorithm: 'MD5'}
   });
 
-  let hash = await cryptoHash.hash(binaryData);
+  let hash = await cryptoHash.hash(binaryData, 'base64');
 
   t.equal(hash, 'YnxTb+lyen1CsNkpmLv+qA==', 'binary data MD5 hash is correct');
 
-  hash = await cryptoHash.hash(repeatedData);
+  hash = await cryptoHash.hash(repeatedData, 'base64');
   t.equal(
     hash,
     // '2d4uZUoLXXO/XWJGnrVl5Q==',
diff --git a/modules/crypto/test/lib/utils/digest-utils.spec.js b/modules/crypto/test/lib/utils/digest-utils.spec.js
deleted file mode 100644
index c149895ae6..0000000000
--- a/modules/crypto/test/lib/utils/digest-utils.spec.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import test from 'tape-promise/tape';
-import {_toHex, _hexToBase64} from '@loaders.gl/crypto';
-import TEST_CASES from '../crc32c-test-cases.json';
-
-test('hexToBase64', (t) => {
-  for (const type in TEST_CASES) {
-    const set = TEST_CASES[type];
-
-    for (const tc of set.cases) {
-      if (!tc.charset) {
-        tc.expected = _hexToBase64(_toHex(tc.want));
-        t.ok(tc.expected, `${tc.want} converted to ${tc.expected}`);
-      }
-    }
-
-    set.expected = _hexToBase64(set.want.toString(16));
-  }
-  t.end();
-});
-
-test('hexToBase64#convert zero leading hex correctly', (t) => {
-  t.equal(_hexToBase64('f85d741'), 'D4XXQQ==');
-  t.end();
-});
diff --git a/modules/crypto/test/lib/utils/digest-utils.spec.ts b/modules/crypto/test/lib/utils/digest-utils.spec.ts
new file mode 100644
index 0000000000..187c8b36a0
--- /dev/null
+++ b/modules/crypto/test/lib/utils/digest-utils.spec.ts
@@ -0,0 +1,32 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
+import test from 'tape-promise/tape';
+import {encodeNumber, encodeHex, encodeBase64} from '@loaders.gl/crypto';
+import TEST_CASES from '../crc32c-test-cases.json' assert {type: 'json'};
+
+test('encodeHexToBase64#crc32 test cases', (t) => {
+  for (const type in TEST_CASES) {
+    const set = TEST_CASES[type];
+
+    for (const tc of set.cases) {
+      if (!tc.charset) {
+        tc.expected = encodeNumber(tc.want, 'base64');
+        t.ok(tc.expected, `${tc.want} encodeed to ${tc.expected}`);
+      }
+    }
+
+    set.expected = encodeHex(set.want.toString(16), 'base64');
+  }
+  t.end();
+});
+
+test('encodeHexToBase64', (t) => {
+  t.equal(encodeHex('f85d741', 'base64'), 'D4XXQQ==', 'encode zero leading hex correctly');
+  t.end();
+});
+
+test('encodeBase64ToHex', (t) => {
+  t.equal(encodeBase64('D4XXQQ==', 'hex'), '0f85d741');
+  t.end();
+});
diff --git a/modules/crypto/test/test-utils/test-utils.js b/modules/crypto/test/test-utils/test-utils.ts
similarity index 90%
rename from modules/crypto/test/test-utils/test-utils.js
rename to modules/crypto/test/test-utils/test-utils.ts
index ea2b9bb1ab..d313222ea4 100644
--- a/modules/crypto/test/test-utils/test-utils.js
+++ b/modules/crypto/test/test-utils/test-utils.ts
@@ -1,3 +1,6 @@
+// loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
+
 import {generateRandomArrayBuffer} from '@loaders.gl/compression/test/utils/test-utils';
 
 export {
diff --git a/modules/csv/package.json b/modules/csv/package.json
index 6eca509973..51e27fb68c 100644
--- a/modules/csv/package.json
+++ b/modules/csv/package.json
@@ -1,8 +1,9 @@
 {
   "name": "@loaders.gl/csv",
-  "version": "4.0.0-alpha.13",
+  "version": "4.0.3",
   "description": "Framework-independent loader for CSV and DSV table formats",
   "license": "MIT",
+  "type": "module",
   "publishConfig": {
     "access": "public",
     "registry": "https://npm.pkg.github.com"
@@ -18,8 +19,15 @@
     "CSV"
   ],
   "types": "dist/index.d.ts",
-  "main": "dist/es5/index.js",
-  "module": "dist/esm/index.js",
+  "main": "dist/index.cjs",
+  "module": "dist/index.js",
+  "exports": {
+    ".": {
+      "import": "./dist/index.js",
+      "require": "./dist/index.cjs",
+      "types": "./dist/index.d.ts"
+    }
+  },
   "sideEffects": false,
   "files": [
     "src",
@@ -27,12 +35,12 @@
     "README.md"
   ],
   "scripts": {
-    "pre-build": "npm run build-bundle",
-    "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js"
+    "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev",
+    "build-bundle": "ocular-bundle ./src/index.ts"
   },
   "dependencies": {
-    "@loaders.gl/loader-utils": "4.0.0-alpha.13",
-    "@loaders.gl/schema": "4.0.0-alpha.13"
+    "@loaders.gl/loader-utils": "4.0.3",
+    "@loaders.gl/schema": "4.0.3"
   },
   "devDependencies": {
     "d3-dsv": "^1.2.0"
diff --git a/modules/csv/src/bundle.ts b/modules/csv/src/bundle.ts
deleted file mode 100644
index 0db0c48b55..0000000000
--- a/modules/csv/src/bundle.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// @ts-nocheck
-const moduleExports = require('./index');
-globalThis.loaders = globalThis.loaders || {};
-module.exports = Object.assign(globalThis.loaders, moduleExports);
diff --git a/modules/csv/src/csv-loader.ts b/modules/csv/src/csv-loader.ts
index df6c1643e2..09de0e214b 100644
--- a/modules/csv/src/csv-loader.ts
+++ b/modules/csv/src/csv-loader.ts
@@ -1,7 +1,8 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
 
 import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils';
-import type {Batch, TableBatch} from '@loaders.gl/schema';
+import type {ArrayRowTable, ObjectRowTable, TableBatch} from '@loaders.gl/schema';
 
 import {
   AsyncQueue,
@@ -20,10 +21,12 @@ type ObjectSchema = {[key: string]: ObjectField} | ObjectField[];
 // @ts-ignore TS2304: Cannot find name '__VERSION__'.
 const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest';
 
+const DEFAULT_CSV_SHAPE = 'object-row-table';
+
 export type CSVLoaderOptions = LoaderOptions & {
   csv?: {
     // loaders.gl options
-    shape?: 'array-row-table' | 'object-row-table' | 'columnar-table';
+    shape?: 'array-row-table' | 'object-row-table';
     /** optimizes memory usage but increases parsing time. */
     optimizeMemoryUsage?: boolean;
     columnPrefix?: string;
@@ -44,26 +47,6 @@ export type CSVLoaderOptions = LoaderOptions & {
   };
 };
 
-const DEFAULT_CSV_LOADER_OPTIONS = {
-  csv: {
-    shape: 'object-row-table',
-    optimizeMemoryUsage: false,
-    // CSV options
-    header: 'auto',
-    columnPrefix: 'column',
-    // delimiter: auto
-    // newline: auto
-    quoteChar: '"',
-    escapeChar: '"',
-    dynamicTyping: true,
-    comments: false,
-    skipEmptyLines: true,
-    // transform: null?
-    delimitersToGuess: [',', '\t', '|', ';']
-    // fastMode: auto
-  }
-};
-
 export const CSVLoader: LoaderWithParser = {
   id: 'csv',
   module: 'csv',
@@ -78,12 +61,33 @@ export const CSVLoader: LoaderWithParser =
   parseInBatches: parseCSVInBatches,
   // @ts-ignore
   // testText: null,
-  options: DEFAULT_CSV_LOADER_OPTIONS as CSVLoaderOptions
+  options: {
+    csv: {
+      shape: DEFAULT_CSV_SHAPE, // 'object-row-table'
+      optimizeMemoryUsage: false,
+      // CSV options
+      header: 'auto',
+      columnPrefix: 'column',
+      // delimiter: auto
+      // newline: auto
+      quoteChar: '"',
+      escapeChar: '"',
+      dynamicTyping: true,
+      comments: false,
+      skipEmptyLines: true,
+      // transform: null?
+      delimitersToGuess: [',', '\t', '|', ';']
+      // fastMode: auto
+    }
+  }
 };
 
-async function parseCSV(csvText: string, options?: CSVLoaderOptions) {
+async function parseCSV(
+  csvText: string,
+  options?: CSVLoaderOptions
+): Promise {
   // Apps can call the parse method directly, we so apply default options here
-  const csvOptions = {...DEFAULT_CSV_LOADER_OPTIONS.csv, ...options?.csv};
+  const csvOptions = {...CSVLoader.options.csv, ...options?.csv};
 
   const firstRow = readFirstRow(csvText);
   const header: boolean =
@@ -103,40 +107,32 @@ async function parseCSV(csvText: string, options?: CSVLoaderOptions) {
   };
 
   const result = Papa.parse(csvText, papaparseConfig);
-  let {data: rows} = result;
+  const rows = result.data as any[];
 
-  const headerRow = result.meta.fields || generateHeader(csvOptions.columnPrefix, firstRow.length);
+  const headerRow = result.meta.fields || generateHeader(csvOptions.columnPrefix!, firstRow.length);
 
-  switch (csvOptions.shape) {
+  const shape = csvOptions.shape || DEFAULT_CSV_SHAPE;
+  switch (shape) {
     case 'object-row-table':
-      rows = rows.map((row) => (Array.isArray(row) ? convertToObjectRow(row, headerRow) : row));
-      break;
+      return {
+        shape: 'object-row-table',
+        data: rows.map((row) => (Array.isArray(row) ? convertToObjectRow(row, headerRow) : row))
+      };
     case 'array-row-table':
-      rows = rows.map((row) => (Array.isArray(row) ? row : convertToArrayRow(row, headerRow)));
-      break;
+      return {
+        shape: 'array-row-table',
+        data: rows.map((row) => (Array.isArray(row) ? row : convertToArrayRow(row, headerRow)))
+      };
     default:
+      throw new Error(shape);
   }
-
-  /*
-  if (!header && shape === 'object-row-table') {
-    // If the dataset has no header, transform the array result into an object shape with an
-    // autogenerated header
-    return result.data.map((row) =>
-      row.reduce((acc, value, i) => {
-        acc[headerRow[i]] = value;
-        return acc;
-      }, {})
-    );
-  }
-  */
-  return rows;
 }
 
 // TODO - support batch size 0 = no batching/single batch?
 function parseCSVInBatches(
   asyncIterator: AsyncIterable | Iterable,
   options?: CSVLoaderOptions
-): AsyncIterable {
+): AsyncIterable {
   // Papaparse does not support standard batch size handling
   // TODO - investigate papaparse chunks mode
   options = {...options};
@@ -145,9 +141,9 @@ function parseCSVInBatches(
   }
 
   // Apps can call the parse method directly, we so apply default options here
-  const csvOptions = {...DEFAULT_CSV_LOADER_OPTIONS.csv, ...options?.csv};
+  const csvOptions = {...CSVLoader.options.csv, ...options?.csv};
 
-  const asyncQueue = new AsyncQueue();
+  const asyncQueue = new AsyncQueue();
 
   let isFirstRow: boolean = true;
   let headerRow: string[] | null = null;
@@ -171,7 +167,7 @@ function parseCSVInBatches(
     skipEmptyLines: false,
 
     // step is called on every row
-    // eslint-disable-next-line complexity
+    // eslint-disable-next-line complexity, max-statements
     step(results) {
       let row = results.data;
 
@@ -198,7 +194,7 @@ function parseCSVInBatches(
       if (isFirstRow) {
         isFirstRow = false;
         if (!headerRow) {
-          headerRow = generateHeader(csvOptions.columnPrefix, row.length);
+          headerRow = generateHeader(csvOptions.columnPrefix!, row.length);
         }
         schema = deduceSchema(row, headerRow);
       }
@@ -209,6 +205,8 @@ function parseCSVInBatches(
         row = JSON.parse(JSON.stringify(row));
       }
 
+      const shape = csvOptions.shape || DEFAULT_CSV_SHAPE;
+
       // Add the row
       tableBatchBuilder =
         tableBatchBuilder ||
@@ -216,7 +214,7 @@ function parseCSVInBatches(
           // @ts-expect-error TODO this is not a proper schema
           schema,
           {
-            shape: csvOptions.shape || 'array-row-table',
+            shape,
             ...options
           }
         );
@@ -286,8 +284,8 @@ function readFirstRow(csvText: string): any[] {
  * See the header option in https://www.papaparse.com/docs#config
  * @returns a transform function that returns sanitized names for duplicate fields
  */
-function duplicateColumnTransformer() {
-  const observedColumns = new Set();
+function duplicateColumnTransformer(): (column: string) => string {
+  const observedColumns = new Set();
   return (col) => {
     let colName = col;
     let counter = 1;
diff --git a/modules/csv/src/csv-writer.ts b/modules/csv/src/csv-writer.ts
index b96b496b50..35f450dfdb 100644
--- a/modules/csv/src/csv-writer.ts
+++ b/modules/csv/src/csv-writer.ts
@@ -1,30 +1,33 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
 
 /* global TextEncoder */
-import type {Writer} from '@loaders.gl/loader-utils';
+import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils';
 import type {Table, TableBatch} from '@loaders.gl/schema';
-import type {CSVWriterOptions} from './lib/encoders/encode-csv';
 import {encodeTableAsCSV} from './lib/encoders/encode-csv';
 
-export type {CSVWriterOptions};
-
-const DEFAULT_WRITER_OPTIONS: Required = {
-  csv: {
-    useDisplayNames: false
-  },
-  useDisplayNames: false
+export type CSVWriterOptions = WriterOptions & {
+  csv?: {
+    useDisplayNames?: boolean;
+  };
+  /** @deprecated */
+  useDisplayNames?: never;
 };
 
-export const CSVWriter: Writer = {
+export const CSVWriter: WriterWithEncoder = {
   id: 'csv',
   version: 'latest',
   module: 'csv',
   name: 'CSV',
   extensions: ['csv'],
   mimeTypes: ['text/csv'],
-  options: DEFAULT_WRITER_OPTIONS,
+  options: {
+    csv: {
+      useDisplayNames: false
+    }
+  },
   text: true,
   encode: async (table, options) =>
     new TextEncoder().encode(encodeTableAsCSV(table, options)).buffer,
-  encodeText: (table, options) => encodeTableAsCSV(table, options)
+  encodeTextSync: (table, options) => encodeTableAsCSV(table, options)
 };
diff --git a/modules/csv/src/index.ts b/modules/csv/src/index.ts
index 78b4c21ba2..c5df5d5dea 100644
--- a/modules/csv/src/index.ts
+++ b/modules/csv/src/index.ts
@@ -1,4 +1,5 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
 
 export type {CSVLoaderOptions} from './csv-loader';
 export {CSVLoader} from './csv-loader';
diff --git a/modules/csv/src/lib/encoders/encode-csv.ts b/modules/csv/src/lib/encoders/encode-csv.ts
index fb92db7a74..30189d7b02 100644
--- a/modules/csv/src/lib/encoders/encode-csv.ts
+++ b/modules/csv/src/lib/encoders/encode-csv.ts
@@ -1,19 +1,13 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
 // Copyright 2022 Foursquare Labs, Inc.
 
 import {Table, makeArrayRowIterator, getTableNumCols} from '@loaders.gl/schema';
 import {csvFormatRows} from 'd3-dsv';
+import type {CSVWriterOptions} from '../../csv-writer';
 
 type EncodableData = string | null;
 
-export type CSVWriterOptions = {
-  csv?: {
-    useDisplayNames?: boolean;
-  };
-  /** @deprecated */
-  useDisplayNames?: boolean;
-};
-
 /**
  * Encode a Table object as CSV
  */
diff --git a/modules/csv/test/csv-loader-arrow.spec.ts b/modules/csv/test/csv-loader-arrow.spec.ts
index 17000f30f5..23885c63dd 100644
--- a/modules/csv/test/csv-loader-arrow.spec.ts
+++ b/modules/csv/test/csv-loader-arrow.spec.ts
@@ -1,8 +1,7 @@
 import test from 'tape-promise/tape';
 import {loadInBatches, isIterator, isAsyncIterable} from '@loaders.gl/core';
-import {CSVLoader} from '@loaders.gl/csv';
-import {RecordBatch} from 'apache-arrow';
-// import {Schema, Field, RecordBatch, Float32Vector} from 'apache-arrow';
+import {CSVLoader} from '../src/csv-loader'; // from '@loaders.gl/csv';
+import * as arrow from 'apache-arrow';
 
 // Small CSV Sample Files
 const CSV_NUMBERS_100_URL = '@loaders.gl/csv/test/data/numbers-100.csv';
@@ -20,8 +19,8 @@ test('CSVLoader#loadInBatches(numbers-100.csv, arrow)', async (t) => {
 
   let batchCount = 0;
   for await (const batch of iterator) {
-    t.ok(batch.data instanceof RecordBatch, 'returns arrow RecordBatch');
-    t.comment(`BATCH: ${batch.length}`);
+    t.ok(batch.data instanceof arrow.Table, 'returns arrow RecordBatch');
+    // t.comment(`BATCH: ${batch.length}`);
     batchCount++;
   }
   t.equal(batchCount, 3, 'Correct number of batches received');
@@ -40,8 +39,8 @@ test('CSVLoader#loadInBatches(numbers-10000.csv, arrow)', async (t) => {
 
   let batchCount = 0;
   for await (const batch of iterator) {
-    t.ok(batch.data instanceof RecordBatch, 'returns arrow RecordBatch');
-    t.comment(`BATCH: ${batch.length}`);
+    t.ok(batch.data instanceof arrow.Table, 'returns arrow RecordBatch');
+    // t.comment(`BATCH: ${batch.length}`);
     batchCount++;
   }
   t.equal(batchCount, 5, 'Correct number of batches received');
diff --git a/modules/csv/test/csv-loader.spec.ts b/modules/csv/test/csv-loader.spec.ts
index fe843ab93d..3a2af96070 100644
--- a/modules/csv/test/csv-loader.spec.ts
+++ b/modules/csv/test/csv-loader.spec.ts
@@ -2,7 +2,8 @@ import test from 'tape-promise/tape';
 import {validateLoader} from 'test/common/conformance';
 
 import {load, loadInBatches, isAsyncIterable} from '@loaders.gl/core';
-import {CSVLoader} from '@loaders.gl/csv';
+import {CSVLoader} from '../src/csv-loader';
+import {getTableLength} from '@loaders.gl/schema';
 
 // Small CSV Sample Files
 const CSV_SAMPLE_URL = '@loaders.gl/csv/test/data/sample.csv';
@@ -21,113 +22,136 @@ test('CSVLoader#loader conformance', (t) => {
 });
 
 test('CSVLoader#load(states.csv)', async (t) => {
-  const rows = await load(CSV_STATES_URL, [CSVLoader]);
-  t.equal(rows.length, 110);
+  const table = await load(CSV_STATES_URL, CSVLoader);
+  t.equal(getTableLength(table), 110);
   t.end();
 });
 
+// eslint-disable-next-line max-statements
 test('CSVLoader#load', async (t) => {
-  const rows = await load(CSV_SAMPLE_URL, [CSVLoader], {csv: {shape: 'object-row-table'}});
-  t.is(rows.length, 2, 'Got correct table size, correctly inferred no header');
-  t.deepEqual(rows[0], {column1: 'A', column2: 'B', column3: 1}, 'Got correct first row');
+  const table = await load(CSV_SAMPLE_URL, CSVLoader, {csv: {shape: 'object-row-table'}});
+  t.assert(table.shape === 'object-row-table', 'Got correct table shape');
+  if (table.shape === 'object-row-table') {
+    t.is(getTableLength(table), 2, 'Got correct table size, correctly inferred no header');
+    t.deepEqual(table.data[0], {column1: 'A', column2: 'B', column3: 1}, 'Got correct first row');
+  }
 
-  const rows1 = await load(CSV_SAMPLE_URL, [CSVLoader], {
+  const table1 = await load(CSV_SAMPLE_URL, CSVLoader, {
     csv: {shape: 'object-row-table', header: true}
   });
-  t.is(rows1.length, 1, 'Got correct table size, forced first row as header');
-  t.deepEqual(rows1[0], {A: 'X', B: 'Y', 1: 2}, 'Got correct first row');
 
-  const rows2 = await load(CSV_SAMPLE_URL, [CSVLoader], {csv: {shape: 'array-row-table'}});
-  t.is(rows2.length, 2, 'Got correct table size');
-  t.deepEqual(
-    rows2,
-    [
-      ['A', 'B', 1],
-      ['X', 'Y', 2]
-    ],
-    'Got correct array content'
-  );
+  t.assert(table1.shape === 'object-row-table', 'Got correct table shape');
+  if (table1.shape === 'object-row-table') {
+    t.is(getTableLength(table1), 1, 'Got correct table size, forced first row as header');
+    t.deepEqual(table1.data[0], {A: 'X', B: 'Y', 1: 2}, 'Got correct first row');
+  }
+
+  const table2 = await load(CSV_SAMPLE_URL, CSVLoader, {csv: {shape: 'array-row-table'}});
+  t.assert(table2.shape === 'array-row-table', 'Got correct table shape');
+  if (table2.shape === 'array-row-table') {
+    t.is(getTableLength(table2), 2, 'Got correct table size');
+    t.deepEqual(
+      table2.data,
+      [
+        ['A', 'B', 1],
+        ['X', 'Y', 2]
+      ],
+      'Got correct array content'
+    );
+  }
 
-  const rows3 = await load(CSV_SAMPLE_VERY_LONG_URL, [CSVLoader], {
+  const table3 = await load(CSV_SAMPLE_VERY_LONG_URL, CSVLoader, {
     csv: {shape: 'object-row-table'}
   });
-  t.is(rows3.length, 2000, 'Got correct table size');
-  t.deepEqual(
-    rows3[0],
-    {
-      TLD: 'ABC',
-      'meaning of life': 42,
-      placeholder: 'Lorem ipsum dolor sit'
-    },
-    'Got correct first row'
-  );
+  t.assert(table3.shape === 'object-row-table', 'Got correct table shape');
+  if (table3.shape === 'object-row-table') {
+    t.is(getTableLength(table3), 2000, 'Got correct table size');
+    t.deepEqual(
+      table3.data[0],
+      {
+        TLD: 'ABC',
+        'meaning of life': 42,
+        placeholder: 'Lorem ipsum dolor sit'
+      },
+      'Got correct first row'
+    );
+  }
 
-  const rows4 = await load(CSV_INCIDENTS_URL_QUOTES, [CSVLoader], {
+  const table4 = await load(CSV_INCIDENTS_URL_QUOTES, CSVLoader, {
     csv: {shape: 'object-row-table'}
   });
-  t.is(rows4.length, 499, 'Got correct table size (csv with quotes)');
-  t.deepEqual(
-    rows4[0],
-    {
-      IncidntNum: 160919032,
-      Category: 'VANDALISM',
-      Descript: 'MALICIOUS MISCHIEF, VANDALISM OF VEHICLES',
-      DayOfWeek: 'Friday',
-      DateTime: '11/11/16 7:00',
-      PdDistrict: 'MISSION',
-      Address: '1400 Block of UTAH ST',
-      Resolution: 'NONE',
-      Longitude: -122.4052518,
-      Latitude: 37.75152496
-    },
-    'Got correct first row (csv with quotes)'
-  );
+  t.assert(table4.shape === 'object-row-table', 'Got correct table shape');
+  if (table4.shape === 'object-row-table') {
+    t.is(getTableLength(table4), 499, 'Got correct table size (csv with quotes)');
+    t.deepEqual(
+      table4.data[0],
+      {
+        IncidntNum: 160919032,
+        Category: 'VANDALISM',
+        Descript: 'MALICIOUS MISCHIEF, VANDALISM OF VEHICLES',
+        DayOfWeek: 'Friday',
+        DateTime: '11/11/16 7:00',
+        PdDistrict: 'MISSION',
+        Address: '1400 Block of UTAH ST',
+        Resolution: 'NONE',
+        Longitude: -122.4052518,
+        Latitude: 37.75152496
+      },
+      'Got correct first row (csv with quotes)'
+    );
+  }
   t.end();
 });
 
 test('CSVLoader#load(sample.csv, duplicate column names)', async (t) => {
-  const rows = await load(CSV_SAMPLE_URL_DUPLICATE_COLS, [CSVLoader], {
+  const table = await load(CSV_SAMPLE_URL_DUPLICATE_COLS, CSVLoader, {
     csv: {shape: 'object-row-table'}
   });
-  t.is(rows.length, 3, 'Got correct table size');
-  t.deepEqual(
-    rows,
-    [
-      {A: 'x', B: 1, 'A.1': 'y', 'A.1.1': 'z', 'A.2': 'w', 'B.1': 2},
-      {A: 'y', B: 29, 'A.1': 'z', 'A.1.1': 'y', 'A.2': 'w', 'B.1': 19},
-      {A: 'x', B: 1, 'A.1': 'y', 'A.1.1': 'z', 'A.2': 'w', 'B.1': 2}
-    ],
-    'dataset should be parsed with the corrected duplicate headers'
-  );
+  t.assert(table.shape === 'object-row-table', 'Got correct table shape');
+  if (table.shape === 'object-row-table') {
+    t.is(getTableLength(table), 3, 'Got correct table size');
+    t.deepEqual(
+      table.data,
+      [
+        {A: 'x', B: 1, 'A.1': 'y', 'A.1.1': 'z', 'A.2': 'w', 'B.1': 2},
+        {A: 'y', B: 29, 'A.1': 'z', 'A.1.1': 'y', 'A.2': 'w', 'B.1': 19},
+        {A: 'x', B: 1, 'A.1': 'y', 'A.1.1': 'z', 'A.2': 'w', 'B.1': 2}
+      ],
+      'dataset should be parsed with the corrected duplicate headers'
+    );
+  }
 
-  const rows2 = await load(CSV_SAMPLE_URL_DUPLICATE_COLS, [CSVLoader], {
+  const table2 = await load(CSV_SAMPLE_URL_DUPLICATE_COLS, CSVLoader, {
     csv: {shape: 'array-row-table', header: false}
   });
-  t.is(rows2.length, 4, 'Got correct table size');
-  t.deepEqual(
-    rows2,
-    [
-      ['A', 'B', 'A', 'A.1', 'A', 'B'],
-      ['x', 1, 'y', 'z', 'w', 2],
-      ['y', 29, 'z', 'y', 'w', 19],
-      ['x', 1, 'y', 'z', 'w', 2]
-    ],
-    'dataset should be parsed correctly as the array rows'
-  );
+  t.assert(table2.shape === 'array-row-table', 'Got correct table shape');
+  if (table2.shape === 'array-row-table') {
+    t.is(getTableLength(table2), 4, 'Got correct table size');
+    t.deepEqual(
+      table2.data,
+      [
+        ['A', 'B', 'A', 'A.1', 'A', 'B'],
+        ['x', 1, 'y', 'z', 'w', 2],
+        ['y', 29, 'z', 'y', 'w', 19],
+        ['x', 1, 'y', 'z', 'w', 2]
+      ],
+      'dataset should be parsed correctly as the array rows'
+    );
+  }
 });
 
 // TSV
 
 test('CSVLoader#load(brazil.tsv)', async (t) => {
-  const rows = await load(TSV_BRAZIL, [CSVLoader]);
-  t.equal(rows.length, 10);
+  const table = await load(TSV_BRAZIL, CSVLoader);
+  t.equal(getTableLength(table), 10);
   t.end();
 });
 
 // loadInBatches
 
 test('CSVLoader#loadInBatches(sample.csv, columns)', async (t) => {
-  const iterator = await loadInBatches(CSV_SAMPLE_URL, [CSVLoader], {
+  const iterator = await loadInBatches(CSV_SAMPLE_URL, CSVLoader, {
     csv: {
       shape: 'columnar-table'
     }
@@ -136,12 +160,15 @@ test('CSVLoader#loadInBatches(sample.csv, columns)', async (t) => {
 
   let batchCount = 0;
   for await (const batch of iterator) {
-    t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
+    // t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
     t.equal(batch.length, 2, 'Got correct batch size');
 
-    t.ok(validateColumn(batch.data.column1, batch.length, 'string'), 'column 0 valid');
-    t.ok(validateColumn(batch.data.column2, batch.length, 'string'), 'column 1 valid');
-    t.ok(validateColumn(batch.data.column3, batch.length, 'float'), 'column 2 valid');
+    t.equal(batch.shape, 'columnar-table', 'Got correct batch shape');
+    if (batch.shape === 'columnar-table') {
+      t.ok(validateColumn(batch.data.column1, batch.length, 'string'), 'column 0 valid');
+      t.ok(validateColumn(batch.data.column2, batch.length, 'string'), 'column 1 valid');
+      t.ok(validateColumn(batch.data.column3, batch.length, 'float'), 'column 2 valid');
+    }
 
     batchCount++;
   }
@@ -151,7 +178,7 @@ test('CSVLoader#loadInBatches(sample.csv, columns)', async (t) => {
 
 test('CSVLoader#loadInBatches(sample-very-long.csv, columns)', async (t) => {
   const batchSize = 25;
-  const iterator = await loadInBatches(CSV_SAMPLE_VERY_LONG_URL, [CSVLoader], {
+  const iterator = await loadInBatches(CSV_SAMPLE_VERY_LONG_URL, CSVLoader, {
     csv: {
       shape: 'columnar-table'
     },
@@ -160,19 +187,21 @@ test('CSVLoader#loadInBatches(sample-very-long.csv, columns)', async (t) => {
 
   let batchCount = 0;
   for await (const batch of iterator) {
-    t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
+    // t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
     t.equal(batch.length, batchSize, 'Got correct batch size');
 
-    t.ok(validateColumn(batch.data.TLD, batch.length, 'string'), 'column TLD valid');
-    t.ok(
-      validateColumn(batch.data['meaning of life'], batch.length, 'float'),
-      'column meaning of life valid'
-    );
-    t.ok(
-      validateColumn(batch.data.placeholder, batch.length, 'string'),
-      'column placeholder valid'
-    );
-
+    t.equal(batch.shape, 'columnar-table', 'Got correct batch shape');
+    if (batch.shape === 'columnar-table') {
+      t.ok(validateColumn(batch.data.TLD, batch.length, 'string'), 'column TLD valid');
+      t.ok(
+        validateColumn(batch.data['meaning of life'], batch.length, 'float'),
+        'column meaning of life valid'
+      );
+      t.ok(
+        validateColumn(batch.data.placeholder, batch.length, 'string'),
+        'column placeholder valid'
+      );
+    }
     batchCount++;
     if (batchCount === 5) {
       break;
@@ -184,13 +213,16 @@ test('CSVLoader#loadInBatches(sample-very-long.csv, columns)', async (t) => {
 });
 
 test('CSVLoader#loadInBatches(sample.csv, array-rows)', async (t) => {
-  const iterator = await loadInBatches(CSV_SAMPLE_URL, [CSVLoader], {shape: 'array-row-table'});
+  const iterator = await loadInBatches(CSV_SAMPLE_URL, CSVLoader, {shape: 'array-row-table'});
 
   let batchCount = 0;
   for await (const batch of iterator) {
-    t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
-    t.equal(batch.length, 2, 'Got correct batch size');
-    t.deepEqual(batch.data[0], ['A', 'B', 1], 'Got correct first row');
+    // t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
+    t.equal(batch.shape, 'array-row-table', 'Got correct batch shape');
+    if (batch.shape === 'array-row-table') {
+      t.equal(batch.length, 2, 'Got correct batch size');
+      t.deepEqual(batch.data[0], ['A', 'B', 1], 'Got correct first row');
+    }
     batchCount++;
   }
   t.equal(batchCount, 1, 'Correct number of batches received');
@@ -199,15 +231,20 @@ test('CSVLoader#loadInBatches(sample.csv, array-rows)', async (t) => {
 });
 
 test('CSVLoader#loadInBatches(sample.csv, object-rows)', async (t) => {
-  const iterator = await loadInBatches(CSV_SAMPLE_URL, [CSVLoader], {
+  const iterator = await loadInBatches(CSV_SAMPLE_URL, CSVLoader, {
     csv: {shape: 'object-row-table'}
   });
 
   let batchCount = 0;
   for await (const batch of iterator) {
-    t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
-    t.equal(batch.length, 2, 'Got correct batch size');
-    t.deepEqual(batch.data[0], {column1: 'A', column2: 'B', column3: 1}, 'Got correct first row');
+    t.equal(batch.shape, 'object-row-table', 'Got correct batch shape');
+    if (batch.shape === 'object-row-table') {
+      t.comment(
+        `BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`
+      );
+      t.equal(batch.length, 2, 'Got correct batch size');
+      t.deepEqual(batch.data[0], {column1: 'A', column2: 'B', column3: 1}, 'Got correct first row');
+    }
     batchCount++;
   }
   t.equal(batchCount, 1, 'Correct number of batches received');
@@ -216,7 +253,7 @@ test('CSVLoader#loadInBatches(sample.csv, object-rows)', async (t) => {
 });
 
 test('CSVLoader#loadInBatches(sample.csv, arrays, header)', async (t) => {
-  let iterator = await loadInBatches(CSV_SAMPLE_URL, [CSVLoader], {
+  let iterator = await loadInBatches(CSV_SAMPLE_URL, CSVLoader, {
     csv: {
       shape: 'array-row-table',
       header: false
@@ -225,22 +262,28 @@ test('CSVLoader#loadInBatches(sample.csv, arrays, header)', async (t) => {
 
   let batchCount = 0;
   for await (const batch of iterator) {
-    t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
-    t.equal(batch.length, 2, 'Got correct batch size');
-    t.deepEqual(batch.data[0], ['A', 'B', 1], 'Got correct first row');
+    // t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
+    t.equal(batch.shape, 'array-row-table', 'Got correct batch shape');
+    if (batch.shape === 'array-row-table') {
+      t.equal(batch.length, 2, 'Got correct batch size');
+      t.deepEqual(batch.data[0], ['A', 'B', 1], 'Got correct first row');
+    }
     batchCount++;
   }
   t.equal(batchCount, 1, 'Correct number of batches received');
 
-  iterator = await loadInBatches(CSV_SAMPLE_URL, [CSVLoader], {
+  iterator = await loadInBatches(CSV_SAMPLE_URL, CSVLoader, {
     csv: {header: false, shape: 'object-row-table'}
   });
 
   batchCount = 0;
   for await (const batch of iterator) {
-    t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
-    t.equal(batch.length, 2, 'Got correct batch size');
-    t.deepEqual(batch.data[0], {column1: 'A', column2: 'B', column3: 1}, 'Got correct first row');
+    // t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
+    t.equal(batch.shape, 'object-row-table', 'Got correct batch shape');
+    if (batch.shape === 'object-row-table') {
+      t.equal(batch.length, 2, 'Got correct batch size');
+      t.deepEqual(batch.data[0], {column1: 'A', column2: 'B', column3: 1}, 'Got correct first row');
+    }
     batchCount++;
   }
   t.equal(batchCount, 1, 'Correct number of batches received');
@@ -250,7 +293,7 @@ test('CSVLoader#loadInBatches(sample.csv, arrays, header)', async (t) => {
 
 test('CSVLoader#loadInBatches(no header, row format, prefix)', async (t) => {
   const batchSize = 25;
-  const iterator = await loadInBatches(CSV_NO_HEADER_URL, [CSVLoader], {
+  const iterator = await loadInBatches(CSV_NO_HEADER_URL, CSVLoader, {
     csv: {
       shape: 'object-row-table',
       columnPrefix: 'column_'
@@ -259,17 +302,20 @@ test('CSVLoader#loadInBatches(no header, row format, prefix)', async (t) => {
   });
 
   for await (const batch of iterator) {
-    t.comment(JSON.stringify(batch.data[0]));
-    t.ok(batch.data[0].column_1, 'first column has a value');
-    t.ok(batch.data[0].column_2, 'second column has a value value');
-    t.ok(batch.data[0].column_3, 'third column has a value');
+    t.equal(batch.shape, 'object-row-table', 'Got correct batch shape');
+    if (batch.shape === 'object-row-table') {
+      // t.comment(JSON.stringify(batch.data[0]));
+      t.ok(batch.data[0].column_1, 'first column has a value');
+      t.ok(batch.data[0].column_2, 'second column has a value value');
+      t.ok(batch.data[0].column_3, 'third column has a value');
+    }
   }
 
   t.end();
 });
 
 test('CSVLoader#loadInBatches(sample.csv, no dynamicTyping)', async (t) => {
-  const iterator = await loadInBatches(CSV_SAMPLE_URL, [CSVLoader], {
+  const iterator = await loadInBatches(CSV_SAMPLE_URL, CSVLoader, {
     csv: {
       shape: 'columnar-table',
       dynamicTyping: false,
@@ -281,15 +327,18 @@ test('CSVLoader#loadInBatches(sample.csv, no dynamicTyping)', async (t) => {
 
   let rowCount = 0;
   for await (const batch of iterator) {
-    t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
-    t.equal(batch.length, 2, 'Got correct batch size');
-
-    t.ok(validateColumn(batch.data.column1, batch.length, 'string'), 'column 0 valid');
-    t.ok(validateColumn(batch.data.column2, batch.length, 'string'), 'column 1 valid');
-    t.ok(
-      validateColumn(batch.data.column3, batch.length, 'string'),
-      'column 2 is a string and is valid'
-    );
+    // t.comment(`BATCH ${batch.count}: ${batch.length} ${JSON.stringify(batch.data).slice(0, 200)}`);
+    t.equal(batch.shape, 'columnar-table', 'Got correct batch shape');
+    if (batch.shape === 'columnar-table') {
+      t.equal(getTableLength(batch), 2, 'Got correct batch size');
+
+      t.ok(validateColumn(batch.data.column1, batch.length, 'string'), 'column 0 valid');
+      t.ok(validateColumn(batch.data.column2, batch.length, 'string'), 'column 1 valid');
+      t.ok(
+        validateColumn(batch.data.column3, batch.length, 'string'),
+        'column 2 is a string and is valid'
+      );
+    }
 
     rowCount = rowCount + batch.length;
   }
@@ -298,14 +347,16 @@ test('CSVLoader#loadInBatches(sample.csv, no dynamicTyping)', async (t) => {
 });
 
 test('CSVLoader#loadInBatches(sample.csv, duplicate columns)', async (t) => {
-  const iterator = await loadInBatches(CSV_SAMPLE_URL_DUPLICATE_COLS, [CSVLoader], {
+  const iterator = await loadInBatches(CSV_SAMPLE_URL_DUPLICATE_COLS, CSVLoader, {
     csv: {shape: 'object-row-table'}
   });
 
   const rows: any[] = [];
 
   for await (const batch of iterator) {
-    rows.push(...batch.data);
+    if (batch.shape === 'object-row-table') {
+      rows.push(...batch.data);
+    }
   }
 
   t.is(rows.length, 3, 'Got correct table size');
@@ -319,14 +370,16 @@ test('CSVLoader#loadInBatches(sample.csv, duplicate columns)', async (t) => {
     'dataset should be parsed with the corrected duplicate headers'
   );
 
-  const iterator2 = await loadInBatches(CSV_SAMPLE_URL_DUPLICATE_COLS, [CSVLoader], {
+  const iterator2 = await loadInBatches(CSV_SAMPLE_URL_DUPLICATE_COLS, CSVLoader, {
     csv: {shape: 'array-row-table'}
   });
 
   const rows2: any[] = [];
 
   for await (const batch of iterator2) {
-    rows2.push(...batch.data);
+    if (batch.shape === 'array-row-table') {
+      rows2.push(...batch.data);
+    }
   }
 
   t.is(rows2.length, 3, 'Got correct table size');
@@ -342,14 +395,17 @@ test('CSVLoader#loadInBatches(sample.csv, duplicate columns)', async (t) => {
 });
 
 test('CSVLoader#loadInBatches(skipEmptyLines)', async (t) => {
-  const iterator = await loadInBatches(CSV_SAMPLE_URL_EMPTY_LINES, [CSVLoader], {
+  const iterator = await loadInBatches(CSV_SAMPLE_URL_EMPTY_LINES, CSVLoader, {
     csv: {shape: 'object-row-table', skipEmptyLines: true}
   });
 
-  const rows: any[] = [];
+  const rows: unknown[] = [];
 
   for await (const batch of iterator) {
-    rows.push(...batch.data);
+    t.equal(batch.shape, 'object-row-table', 'Got correct batch shape');
+    if (batch.shape === 'object-row-table') {
+      rows.push(...batch.data);
+    }
   }
 
   t.is(rows.length, 2, 'Got correct table size');
@@ -365,13 +421,16 @@ test('CSVLoader#loadInBatches(skipEmptyLines)', async (t) => {
 });
 
 test('CSVLoader#loadInBatches(csv with quotes)', async (t) => {
-  const iterator = await loadInBatches(CSV_INCIDENTS_URL_QUOTES, [CSVLoader], {
+  const iterator = await loadInBatches(CSV_INCIDENTS_URL_QUOTES, CSVLoader, {
     csv: {shape: 'object-row-table'}
   });
 
-  const rows: any[] = [];
+  const rows: unknown[] = [];
   for await (const batch of iterator) {
-    rows.push(...batch.data);
+    t.equal(batch.shape, 'object-row-table', 'Got correct batch shape');
+    if (batch.shape === 'object-row-table') {
+      rows.push(...batch.data);
+    }
   }
   t.is(rows.length, 499, 'Got the correct table size');
   t.deepEqual(
diff --git a/modules/csv/test/csv-writer.spec.ts b/modules/csv/test/csv-writer.spec.ts
index 8b8b1196dc..ab7a04e8e3 100644
--- a/modules/csv/test/csv-writer.spec.ts
+++ b/modules/csv/test/csv-writer.spec.ts
@@ -1,4 +1,5 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
 // Copyright 2022 Foursquare Labs, Inc.
 
 import test from 'tape-promise/tape';
@@ -113,7 +114,7 @@ c,3`
   },
   {
     name: 'table with display names (explicit options)',
-    options: {useDisplayNames: true},
+    options: {csv: {useDisplayNames: true}},
     input: makeTestTable([
       [{name: 'id', type: 'utf8', metadata: {displayName: 'foo'}}, ['a', 'b', 'c']],
       [{name: 'val_uint', type: 'uint32', metadata: {displayName: 'bar'}}, [1, 2, 3]]
@@ -125,7 +126,7 @@ c,3`
   },
   {
     name: 'table with display names (explicit suppression)',
-    options: {useDisplayNames: false},
+    options: {csv: {useDisplayNames: false}},
     input: makeTestTable([
       [{name: 'id', type: 'utf8', metadata: {displayName: 'foo'}}, ['a', 'b', 'c']],
       [{name: 'val_uint', type: 'uint32', metadata: {displayName: 'bar'}}, [1, 2, 3]]
diff --git a/modules/csv/test/papaparse/papaparse.spec.ts b/modules/csv/test/papaparse/papaparse.spec.ts
index 6b2d4e89c2..8e67114599 100644
--- a/modules/csv/test/papaparse/papaparse.spec.ts
+++ b/modules/csv/test/papaparse/papaparse.spec.ts
@@ -488,7 +488,6 @@ const CUSTOM_TESTS = [
       Papa.parse(BASE_PATH + 'long-sample.csv', {
         download: true,
         chunkSize: 500,
-        // eslint-disable-next-line @typescript-eslint/no-empty-function
         beforeFirstChunk(chunk) {},
         step(response) {
           updates++;
diff --git a/modules/draco/package.json b/modules/draco/package.json
index 626c064984..21c59c5f88 100644
--- a/modules/draco/package.json
+++ b/modules/draco/package.json
@@ -1,8 +1,9 @@
 {
   "name": "@loaders.gl/draco",
-  "version": "4.0.0-alpha.13",
+  "version": "4.0.3",
   "description": "Framework-independent loader and writer for Draco compressed meshes and point clouds",
   "license": "MIT",
+  "type": "module",
   "publishConfig": {
     "access": "public",
     "registry": "https://npm.pkg.github.com"
@@ -21,8 +22,15 @@
     "draco"
   ],
   "types": "dist/index.d.ts",
-  "main": "dist/es5/index.js",
-  "module": "dist/esm/index.js",
+  "main": "dist/index.cjs",
+  "module": "dist/index.js",
+  "exports": {
+    ".": {
+      "import": "./dist/index.js",
+      "require": "./dist/index.cjs",
+      "types": "./dist/index.d.ts"
+    }
+  },
   "sideEffects": false,
   "browser": {
     "fs": false
@@ -33,23 +41,24 @@
     "README.md"
   ],
   "scripts": {
-    "pre-build": "npm run build-bundle && npm run build-workers",
-    "build-bundle": "esbuild src/bundle.ts --outfile=dist/dist.min.js --bundle --minify --sourcemap",
+    "pre-build": "npm run copy-libs && npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-workers",
+    "copy-libs": "cp -rf ./src/libs ./dist/libs",
+    "build-bundle": "ocular-bundle ./src/index.ts",
     "build-workers": "yarn build-loader-worker && yarn build-loader-worker-node && yarn build-writer-worker && yarn build-writer-worker-node",
-    "build-loader-worker": "esbuild src/workers/draco-worker.ts --outfile=dist/draco-worker.js --target=esnext --bundle --minify --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"",
-    "build-loader-worker-node": "esbuild src/workers/draco-worker-node.ts --outfile=dist/draco-worker-node.js --target=node16 --platform=node --minify --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"",
-    "build-writer-worker": "esbuild src/workers/draco-writer-worker.ts --outfile=dist/draco-writer-worker.js --target=esnext --minify --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"",
-    "build-writer-worker-node": "esbuild src/workers/draco-writer-worker-node.ts --outfile=dist/draco-writer-worker-node.js --target=node16 --platform=node --minify --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\""
+    "build-loader-worker": "esbuild src/workers/draco-worker.ts --outfile=dist/draco-worker.js --target=esnext --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"",
+    "build-loader-worker-node": "esbuild src/workers/draco-worker-node.ts --outfile=dist/draco-worker-node.js --target=node16 --format=esm --platform=node --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"",
+    "build-writer-worker": "esbuild src/workers/draco-writer-worker.ts --outfile=dist/draco-writer-worker.js --target=esnext --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"",
+    "build-writer-worker-node": "esbuild src/workers/draco-writer-worker-node.ts --outfile=dist/draco-writer-worker-node.js --target=node16 --platform=node --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\""
   },
   "dependencies": {
     "@babel/runtime": "^7.3.1",
-    "@loaders.gl/loader-utils": "4.0.0-alpha.13",
-    "@loaders.gl/schema": "4.0.0-alpha.13",
-    "@loaders.gl/worker-utils": "4.0.0-alpha.13",
+    "@loaders.gl/loader-utils": "4.0.3",
+    "@loaders.gl/schema": "4.0.3",
+    "@loaders.gl/worker-utils": "4.0.3",
     "draco3d": "1.5.5"
   },
   "devDependencies": {
-    "@loaders.gl/polyfills": "4.0.0-alpha.13"
+    "@loaders.gl/polyfills": "4.0.3"
   },
   "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5"
 }
diff --git a/modules/draco/src/bundle.ts b/modules/draco/src/bundle.ts
deleted file mode 100644
index 0db0c48b55..0000000000
--- a/modules/draco/src/bundle.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// @ts-nocheck
-const moduleExports = require('./index');
-globalThis.loaders = globalThis.loaders || {};
-module.exports = Object.assign(globalThis.loaders, moduleExports);
diff --git a/modules/draco/src/draco-loader.ts b/modules/draco/src/draco-loader.ts
index d24106d031..d4c514f701 100644
--- a/modules/draco/src/draco-loader.ts
+++ b/modules/draco/src/draco-loader.ts
@@ -1,4 +1,5 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
 import type {Loader, LoaderOptions} from '@loaders.gl/loader-utils';
 import type {DracoMesh} from './lib/draco-types';
 import type {DracoParseOptions} from './lib/draco-parser';
@@ -39,5 +40,3 @@ export const DracoLoader: Loader = {
   tests: ['DRACO'],
   options: DEFAULT_DRACO_OPTIONS
 };
-
-export const _TypecheckDracoLoader: Loader = DracoLoader;
diff --git a/modules/draco/src/draco-writer.ts b/modules/draco/src/draco-writer.ts
index edda70fb37..889ba470a9 100644
--- a/modules/draco/src/draco-writer.ts
+++ b/modules/draco/src/draco-writer.ts
@@ -1,4 +1,4 @@
-import type {Writer, WriterOptions} from '@loaders.gl/loader-utils';
+import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils';
 import type {DracoMesh} from './lib/draco-types';
 import type {DracoBuildOptions} from './lib/draco-builder';
 import DRACOBuilder from './lib/draco-builder';
@@ -29,16 +29,16 @@ const DEFAULT_DRACO_WRITER_OPTIONS = {
 /**
  * Exporter for Draco3D compressed geometries
  */
-export const DracoWriter: Writer = {
+export const DracoWriter: WriterWithEncoder = {
   name: 'DRACO',
   id: 'draco',
   module: 'draco',
   version: VERSION,
   extensions: ['drc'],
-  encode,
   options: {
     draco: DEFAULT_DRACO_WRITER_OPTIONS
-  }
+  },
+  encode
 };
 
 async function encode(data: DracoMesh, options: DracoWriterOptions = {}): Promise {
diff --git a/modules/draco/src/index.ts b/modules/draco/src/index.ts
index 7f6bd4f716..b86208be27 100644
--- a/modules/draco/src/index.ts
+++ b/modules/draco/src/index.ts
@@ -1,4 +1,5 @@
 // loaders.gl, MIT license
+// Copyright (c) vis.gl contributors
 import type {LoaderWithParser} from '@loaders.gl/loader-utils';
 import type {DracoMesh, DracoLoaderData} from './lib/draco-types';
 import type {DracoLoaderOptions} from './draco-loader';
@@ -7,6 +8,9 @@ import DracoParser from './lib/draco-parser';
 import {loadDracoDecoderModule} from './lib/draco-module-loader';
 import {VERSION} from './lib/utils/version';
 
+// Module constants
+export {DRACO_EXTERNAL_LIBRARIES, DRACO_EXTERNAL_LIBRARY_URLS} from './lib/draco-module-loader';
+
 // Draco data types
 
 export type {DracoMesh, DracoLoaderData};
@@ -54,6 +58,3 @@ async function parse(arrayBuffer: ArrayBuffer, options?: DracoLoaderOptions): Pr
     dracoParser.destroy();
   }
 }
-
-// TYPE TESTS - TODO find a better way than exporting junk
-export const _TypecheckDracoLoader: LoaderWithParser = DracoLoader;
diff --git a/modules/draco/src/lib/draco-module-loader.ts b/modules/draco/src/lib/draco-module-loader.ts
index f0eb315435..c1025c2b84 100644
--- a/modules/draco/src/lib/draco-module-loader.ts
+++ b/modules/draco/src/lib/draco-module-loader.ts
@@ -4,16 +4,28 @@
 
 import {loadLibrary} from '@loaders.gl/worker-utils';
 
-const DRACO_DECODER_VERSION = '1.5.5';
+const DRACO_DECODER_VERSION = '1.5.6';
 const DRACO_ENCODER_VERSION = '1.4.1';
 
 const STATIC_DECODER_URL = `https://www.gstatic.com/draco/versioned/decoders/${DRACO_DECODER_VERSION}`;
 
-const DRACO_JS_DECODER_URL = `${STATIC_DECODER_URL}/draco_decoder.js`;
-const DRACO_WASM_WRAPPER_URL = `${STATIC_DECODER_URL}/draco_wasm_wrapper.js`;
-const DRACO_WASM_DECODER_URL = `${STATIC_DECODER_URL}/draco_decoder.wasm`;
-
-const DRACO_ENCODER_URL = `https://raw.githubusercontent.com/google/draco/${DRACO_ENCODER_VERSION}/javascript/draco_encoder.js`;
+export const DRACO_EXTERNAL_LIBRARIES = {
+  /** The primary Draco3D encoder, javascript wrapper part */
+  DECODER: 'draco_wasm_wrapper.js',
+  /** The primary draco decoder, compiled web assembly part */
+  DECODER_WASM: 'draco_decoder.wasm',
+  /** Fallback decoder for non-webassebly environments. Very big bundle, lower performance */
+  FALLBACK_DECODER: 'draco_decoder.js',
+  /** Draco encoder */
+  ENCODER: 'draco_encoder.js'
+};
+
+export const DRACO_EXTERNAL_LIBRARY_URLS = {
+  [DRACO_EXTERNAL_LIBRARIES.DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER}`,
+  [DRACO_EXTERNAL_LIBRARIES.DECODER_WASM]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.DECODER_WASM}`,
+  [DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER]: `${STATIC_DECODER_URL}/${DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER}`,
+  [DRACO_EXTERNAL_LIBRARIES.ENCODER]: `https://raw.githubusercontent.com/google/draco/${DRACO_ENCODER_VERSION}/javascript/${DRACO_EXTERNAL_LIBRARIES.ENCODER}`
+};
 
 let loadDecoderPromise;
 let loadEncoderPromise;
@@ -59,14 +71,29 @@ async function loadDracoDecoder(options) {
   let wasmBinary;
   switch (options.draco && options.draco.decoderType) {
     case 'js':
-      DracoDecoderModule = await loadLibrary(DRACO_JS_DECODER_URL, 'draco', options);
+      DracoDecoderModule = await loadLibrary(
+        DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER],
+        'draco',
+        options,
+        DRACO_EXTERNAL_LIBRARIES.FALLBACK_DECODER
+      );
       break;
 
     case 'wasm':
     default:
       [DracoDecoderModule, wasmBinary] = await Promise.all([
-        await loadLibrary(DRACO_WASM_WRAPPER_URL, 'draco', options),
-        await loadLibrary(DRACO_WASM_DECODER_URL, 'draco', options)
+        await loadLibrary(
+          DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER],
+          'draco',
+          options,
+          DRACO_EXTERNAL_LIBRARIES.DECODER
+        ),
+        await loadLibrary(
+          DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM],
+          'draco',
+          options,
+          DRACO_EXTERNAL_LIBRARIES.DECODER_WASM
+        )
       ]);
   }
   // Depends on how import happened...
@@ -92,7 +119,12 @@ function initializeDracoDecoder(DracoDecoderModule, wasmBinary) {
 // ENCODER
 
 async function loadDracoEncoder(options) {
-  let DracoEncoderModule = await loadLibrary(DRACO_ENCODER_URL, 'draco', options);
+  let DracoEncoderModule = await loadLibrary(
+    DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.ENCODER],
+    'draco',
+    options,
+    DRACO_EXTERNAL_LIBRARIES.ENCODER
+  );
   // @ts-ignore
   DracoEncoderModule = DracoEncoderModule || globalThis.DracoEncoderModule;
 
diff --git a/modules/draco/src/libs/draco_decoder.wasm b/modules/draco/src/libs/draco_decoder.wasm
new file mode 100644
index 0000000000..ef332a6a7a
Binary files /dev/null and b/modules/draco/src/libs/draco_decoder.wasm differ
diff --git a/modules/draco/src/libs/draco_encoder.js b/modules/draco/src/libs/draco_encoder.js
new file mode 100644
index 0000000000..1ae8af865c
--- /dev/null
+++ b/modules/draco/src/libs/draco_encoder.js
@@ -0,0 +1,52 @@
+
+var DracoEncoderModule = (function() {
+  var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
+  if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
+  return (
+function(DracoEncoderModule) {
+  DracoEncoderModule = DracoEncoderModule || {};
+
+var Module=typeof DracoEncoderModule!=="undefined"?DracoEncoderModule:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var isRuntimeInitialized=false;var isModuleParsed=false;Module["onRuntimeInitialized"]=function(){isRuntimeInitialized=true;if(isModuleParsed){if(typeof Module["onModuleLoaded"]==="function"){Module["onModuleLoaded"](Module)}}};Module["onModuleParsed"]=function(){isModuleParsed=true;if(isRuntimeInitialized){if(typeof Module["onModuleLoaded"]==="function"){Module["onModuleLoaded"](Module)}}};function isVersionSupported(versionString){if(typeof versionString!=="string")return false;const version=versionString.split(".");if(version.length<2||version.length>3)return false;if(version[0]==1&&version[1]>=0&&version[1]<=4)return true;if(version[0]!=0||version[1]>10)return false;return true}Module["isVersionSupported"]=isVersionSupported;var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){var ret=tryParseAsDataURI(filename);if(ret){return binary?ret:ret.toString()}if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){var data=tryParseAsDataURI(f);if(data){return intArrayToString(data)}return read(f)}}readBinary=function readBinary(f){var data;data=tryParseAsDataURI(f);if(data){return data}if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText}catch(err){var data=tryParseAsDataURI(url);if(data){return intArrayToString(data)}throw err}};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){try{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}catch(err){var data=tryParseAsDataURI(url);if(data){return data}throw err}}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}var data=tryParseAsDataURI(url);if(data){onload(data.buffer);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function warnOnce(text){if(!warnOnce.shown)warnOnce.shown={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;err(text)}}function convertJsFunctionToWasm(func,sig){return func}var freeTableIndexes=[];var functionsInTableMap;function getEmptyTableSlot(){if(freeTableIndexes.length){return freeTableIndexes.pop()}try{wasmTable.grow(1)}catch(err){if(!(err instanceof RangeError)){throw err}throw"Unable to grow wasm table. Set ALLOW_TABLE_GROWTH."}return wasmTable.length-1}function addFunctionWasm(func,sig){if(!functionsInTableMap){functionsInTableMap=new WeakMap;for(var i=0;i>2]);c=i[e+32>>2];i[e+32>>2]=0;d=i[b+12>>2];i[b+12>>2]=c;if(!d){break a}bi(d);d=i[e+32>>2];i[e+32>>2]=0;if(!d){break a}bi(d);break a}dm(e+32|0,i[b+8>>2]);c=i[e+32>>2];i[e+32>>2]=0;d=i[b+12>>2];i[b+12>>2]=c;if(!d){break a}bi(d);d=i[e+32>>2];i[e+32>>2]=0;if(!d){break a}bi(d)}b:{c:{d:{d=i[b+12>>2];e:{if(!(i[d+40>>2]!=((i[d+4>>2]-i[d>>2]>>2>>>0)/3|0)?d:0)){b=ho(32);i[e+32>>2]=b;i[e+36>>2]=29;i[e+40>>2]=-2147483616;g[b+29|0]=0;d=j[11281]|j[11282]<<8|(j[11283]<<16|j[11284]<<24);c=j[11277]|j[11278]<<8|(j[11279]<<16|j[11280]<<24);g[b+21|0]=c;g[b+22|0]=c>>>8;g[b+23|0]=c>>>16;g[b+24|0]=c>>>24;g[b+25|0]=d;g[b+26|0]=d>>>8;g[b+27|0]=d>>>16;g[b+28|0]=d>>>24;d=j[11276]|j[11277]<<8|(j[11278]<<16|j[11279]<<24);c=j[11272]|j[11273]<<8|(j[11274]<<16|j[11275]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11268]|j[11269]<<8|(j[11270]<<16|j[11271]<<24);c=j[11264]|j[11265]<<8|(j[11266]<<16|j[11267]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11260]|j[11261]<<8|(j[11262]<<16|j[11263]<<24);c=j[11256]|j[11257]<<8|(j[11258]<<16|j[11259]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+32|0);if(g[e+43|0]>-1){break e}bp(i[e+32>>2]);break e}i[b+264>>2]=b;Jb((i[d+28>>2]-i[d+24>>2]>>2)-i[d+44>>2]|0,i[i[b+4>>2]+44>>2]);d=i[b+12>>2];Jb(((i[d+4>>2]-i[d>>2]>>2>>>0)/3|0)-i[d+40>>2]|0,i[i[b+4>>2]+44>>2]);d=i[b+8>>2];c=i[d+100>>2];d=i[d+96>>2];g[e+32|0]=0;q=b+28|0;Uh(q,(c-d|0)/12|0,e+32|0);d=i[b+12>>2];c=i[d+28>>2];d=i[d+24>>2];i[e+32>>2]=-1;Sh(b+52|0,c-d>>2,e+32|0);c=i[b+40>>2];i[b+44>>2]=c;d=i[b+12>>2];f=i[d+4>>2]-i[d>>2]>>2;f=f-((f>>>0)%3|0)|0;f:{if(i[b+48>>2]-c>>2>>>0>=f>>>0){break f}if(f>>>0>=1073741824){break b}h=f<<2;f=ho(h);i[b+44>>2]=f;i[b+40>>2]=f;i[b+48>>2]=f+h;if(!c){break f}bp(c);d=i[b+12>>2]}c=i[d+28>>2];d=i[d+24>>2];g[e+32|0]=0;Uh(b+84|0,c-d>>2,e+32|0);i[b+164>>2]=-1;i[b+168>>2]=0;i[b+100>>2]=i[b+96>>2];i[b+112>>2]=i[b+108>>2];if(i[b+132>>2]){d=i[b+128>>2];if(d){while(1){c=i[d>>2];bp(d);d=c;if(c){continue}break}}i[b+128>>2]=0;c=i[b+124>>2];if(c){d=0;while(1){i[i[b+120>>2]+(d<<2)>>2]=0;d=d+1|0;if((c|0)!=(d|0)){continue}break}}i[b+132>>2]=0}i[b+144>>2]=0;d=i[b+12>>2];c=i[d+28>>2];d=i[d+24>>2];i[e+32>>2]=-1;Sh(b+152|0,c-d>>2,e+32|0);d=i[b+72>>2];i[b+76>>2]=d;c=i[b+12>>2];c=i[c+4>>2]-i[c>>2]>>2;f=(c>>>0)/3|0;g:{if(i[b+80>>2]-d>>2>>>0>=f>>>0){break g}if(c>>>0>=3221225472){break b}f=f<<2;c=ho(f);i[b+76>>2]=c;i[b+72>>2]=c;i[b+80>>2]=c+f;if(!d){break g}bp(d)}i[b- -64>>2]=0;if(!ci(b)){b=ho(32);i[e+32>>2]=b;i[e+36>>2]=29;i[e+40>>2]=-2147483616;g[b+29|0]=0;d=j[11311]|j[11312]<<8|(j[11313]<<16|j[11314]<<24);c=j[11307]|j[11308]<<8|(j[11309]<<16|j[11310]<<24);g[b+21|0]=c;g[b+22|0]=c>>>8;g[b+23|0]=c>>>16;g[b+24|0]=c>>>24;g[b+25|0]=d;g[b+26|0]=d>>>8;g[b+27|0]=d>>>16;g[b+28|0]=d>>>24;d=j[11306]|j[11307]<<8|(j[11308]<<16|j[11309]<<24);c=j[11302]|j[11303]<<8|(j[11304]<<16|j[11305]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11298]|j[11299]<<8|(j[11300]<<16|j[11301]<<24);c=j[11294]|j[11295]<<8|(j[11296]<<16|j[11297]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11290]|j[11291]<<8|(j[11292]<<16|j[11293]<<24);c=j[11286]|j[11287]<<8|(j[11288]<<16|j[11289]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+32|0);if(g[e+43|0]>-1){break e}bp(i[e+32>>2]);break e}if(!di(b)){b=ho(48);i[e+32>>2]=b;i[e+36>>2]=36;i[e+40>>2]=-2147483600;g[b+36|0]=0;d=j[11348]|j[11349]<<8|(j[11350]<<16|j[11351]<<24);g[b+32|0]=d;g[b+33|0]=d>>>8;g[b+34|0]=d>>>16;g[b+35|0]=d>>>24;d=j[11344]|j[11345]<<8|(j[11346]<<16|j[11347]<<24);c=j[11340]|j[11341]<<8|(j[11342]<<16|j[11343]<<24);g[b+24|0]=c;g[b+25|0]=c>>>8;g[b+26|0]=c>>>16;g[b+27|0]=c>>>24;g[b+28|0]=d;g[b+29|0]=d>>>8;g[b+30|0]=d>>>16;g[b+31|0]=d>>>24;d=j[11336]|j[11337]<<8|(j[11338]<<16|j[11339]<<24);c=j[11332]|j[11333]<<8|(j[11334]<<16|j[11335]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11328]|j[11329]<<8|(j[11330]<<16|j[11331]<<24);c=j[11324]|j[11325]<<8|(j[11326]<<16|j[11327]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11320]|j[11321]<<8|(j[11322]<<16|j[11323]<<24);c=j[11316]|j[11317]<<8|(j[11318]<<16|j[11319]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+32|0);if(g[e+43|0]>-1){break e}bp(i[e+32>>2]);break e}d=(i[b+176>>2]-i[b+172>>2]|0)/136|0;g[e+31|0]=d;c=b;f=i[i[b+4>>2]+44>>2];h=i[f+20>>2];if((h|0)<0?1:(h|0)<=0?l[f+16>>2]<=0:0){ca(f,i[f+4>>2],e+31|0,e+32|0);d=j[e+31|0]}i[c+284>>2]=d&255;c=i[b+12>>2];d=i[c>>2];c=i[c+4>>2];r=b+200|0;ei(r);i[e+40>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;f=c-d|0;h:{if((f|0)>=1){s=b+96|0;d=d-c|0;d=((d|0)>(f|0)?d:f)>>>2|0;t=d>>>0>1?d:1;d=0;while(1){i:{c=(d>>>0)/3|0;p=c>>>5<<2;o=1<>2]>>2]&o){break i}if(Ql(i[b+12>>2],c)){break i}i[e+24>>2]=0;c=fi(b,c,e+24|0);qf(r,c);f=i[e+24>>2];if(c){c=-1;h=-1;m=-1;j:{if((f|0)==-1){break j}n=i[i[b+12>>2]>>2];c=i[n+(f<<2)>>2];k=f+1|0;k=(k>>>0)%3|0?k:f+ -2|0;if((k|0)!=-1){h=i[n+(k<<2)>>2]}k=f+((f>>>0)%3|0?-1:2)|0;if((k|0)==-1){break j}m=i[n+(k<<2)>>2]}n=i[b+84>>2];k=n+(c>>>3&536870908)|0;i[k>>2]=i[k>>2]|1<>>3&536870908)|0;i[c>>2]=i[c>>2]|1<>>3&536870908)|0;i[c>>2]=i[c>>2]|1<>2];k=i[b+104>>2];k:{if(c>>>0>>0){i[c>>2]=1;i[b+100>>2]=c+4;break k}h=i[s>>2];n=c-h|0;c=n>>2;m=c+1|0;if(m>>>0>=1073741824){break d}u=c<<2;k=k-h|0;c=k>>1;m=k>>2>>>0<536870911?c>>>0>>0?m:c:1073741823;c=0;l:{if(!m){break l}if(m>>>0>=1073741824){break b}c=ho(m<<2)}k=u+c|0;i[k>>2]=1;m=c+(m<<2)|0;k=k+4|0;if((n|0)>=1){hp(c,h,n)}i[b+104>>2]=m;i[b+100>>2]=k;i[b+96>>2]=c;if(!h){break k}bp(h)}c=p+i[q>>2]|0;i[c>>2]=o|i[c>>2];m=-1;if((f|0)!=-1){c=f+1|0;m=(c>>>0)%3|0?c:f+ -2|0}c=i[e+36>>2];o=i[e+40>>2];m:{if(c>>>0>>0){i[c>>2]=m;i[e+36>>2]=c+4;break m}h=i[e+32>>2];p=c-h|0;k=p>>2;n=k+1|0;if(n>>>0>=1073741824){break c}o=o-h|0;c=o>>1;n=o>>2>>>0<536870911?c>>>0>>0?n:c:1073741823;c=0;n:{if(!n){break n}if(n>>>0>=1073741824){break b}c=ho(n<<2)}o=c+(k<<2)|0;i[o>>2]=m;m=c+(n<<2)|0;n=o+4|0;if((p|0)>=1){hp(c,h,p)}i[e+40>>2]=m;i[e+36>>2]=n;i[e+32>>2]=c;if(!h){break m}bp(h)}if((f|0)==-1){break i}c=f+1|0;c=(c>>>0)%3|0?c:f+ -2|0;if((c|0)==-1){break i}c=i[i[i[b+12>>2]+12>>2]+(c<<2)>>2];if((c|0)==-1){break i}f=(c>>>0)/3|0;if(i[i[q>>2]+(f>>>3&268435452)>>2]>>>f&1){break i}if(gi(b,c)){break i}b=ho(48);i[e+8>>2]=b;i[e+12>>2]=32;i[e+16>>2]=-2147483600;g[b+32|0]=0;d=j[11381]|j[11382]<<8|(j[11383]<<16|j[11384]<<24);c=j[11377]|j[11378]<<8|(j[11379]<<16|j[11380]<<24);g[b+24|0]=c;g[b+25|0]=c>>>8;g[b+26|0]=c>>>16;g[b+27|0]=c>>>24;g[b+28|0]=d;g[b+29|0]=d>>>8;g[b+30|0]=d>>>16;g[b+31|0]=d>>>24;d=j[11373]|j[11374]<<8|(j[11375]<<16|j[11376]<<24);c=j[11369]|j[11370]<<8|(j[11371]<<16|j[11372]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11365]|j[11366]<<8|(j[11367]<<16|j[11368]<<24);c=j[11361]|j[11362]<<8|(j[11363]<<16|j[11364]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11357]|j[11358]<<8|(j[11359]<<16|j[11360]<<24);c=j[11353]|j[11354]<<8|(j[11355]<<16|j[11356]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+8|0);if(g[e+19|0]>-1){break h}bp(i[e+8>>2]);break h}c=b;if((f|0)!=-1){h=f+1|0;f=(h>>>0)%3|0?h:f+ -2|0}else{f=-1}hi(c,f,1);if(gi(b,i[e+24>>2])){break i}b=ho(48);i[e+8>>2]=b;i[e+12>>2]=32;i[e+16>>2]=-2147483600;g[b+32|0]=0;d=j[11381]|j[11382]<<8|(j[11383]<<16|j[11384]<<24);c=j[11377]|j[11378]<<8|(j[11379]<<16|j[11380]<<24);g[b+24|0]=c;g[b+25|0]=c>>>8;g[b+26|0]=c>>>16;g[b+27|0]=c>>>24;g[b+28|0]=d;g[b+29|0]=d>>>8;g[b+30|0]=d>>>16;g[b+31|0]=d>>>24;d=j[11373]|j[11374]<<8|(j[11375]<<16|j[11376]<<24);c=j[11369]|j[11370]<<8|(j[11371]<<16|j[11372]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11365]|j[11366]<<8|(j[11367]<<16|j[11368]<<24);c=j[11361]|j[11362]<<8|(j[11363]<<16|j[11364]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11357]|j[11358]<<8|(j[11359]<<16|j[11360]<<24);c=j[11353]|j[11354]<<8|(j[11355]<<16|j[11356]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+8|0);if(g[e+19|0]>-1){break h}bp(i[e+8>>2]);break h}d=d+1|0;if((t|0)!=(d|0)){continue}break}}h=b+72|0;d=i[b+72>>2];f=i[b+76>>2];o:{if((d|0)==(f|0)){break o}c=f+ -4|0;if(c>>>0<=d>>>0){break o}while(1){m=i[d>>2];i[d>>2]=i[c>>2];i[c>>2]=m;d=d+4|0;c=c+ -4|0;if(d>>>0>>0){continue}break}}ii(h,f,i[e+32>>2],i[e+36>>2]);p:{if(i[b+176>>2]==i[b+172>>2]){break p}d=i[b+8>>2];c=i[d+100>>2];d=i[d+96>>2];g[e+8|0]=0;Uh(q,(c-d|0)/12|0,e+8|0);d=i[b+72>>2];c=i[b+76>>2];if((d|0)==(c|0)){break p}while(1){ji(b,i[d>>2]);d=d+4|0;if((c|0)!=(d|0)){continue}break}}ki(r);c=b+232|0;sf(r,c);f=i[b+280>>2];q:{if(!f){break q}d=1;if(i[b+284>>2]<1){break q}sf(f,c);if(i[b+284>>2]<2){break q}while(1){sf(i[b+280>>2]+(d<<5)|0,c);d=d+1|0;if((d|0)>2]){continue}break}}Jb(i[b+272>>2]-i[b+268>>2]>>2,i[i[b+4>>2]+44>>2]);Jb(i[b+168>>2],i[i[b+4>>2]+44>>2]);if(!li(b)){b=ho(32);i[e+8>>2]=b;i[e+12>>2]=28;i[e+16>>2]=-2147483616;g[b+28|0]=0;d=j[11410]|j[11411]<<8|(j[11412]<<16|j[11413]<<24);g[b+24|0]=d;g[b+25|0]=d>>>8;g[b+26|0]=d>>>16;g[b+27|0]=d>>>24;d=j[11406]|j[11407]<<8|(j[11408]<<16|j[11409]<<24);c=j[11402]|j[11403]<<8|(j[11404]<<16|j[11405]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11398]|j[11399]<<8|(j[11400]<<16|j[11401]<<24);c=j[11394]|j[11395]<<8|(j[11396]<<16|j[11397]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11390]|j[11391]<<8|(j[11392]<<16|j[11393]<<24);c=j[11386]|j[11387]<<8|(j[11388]<<16|j[11389]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+8|0);if(g[e+19|0]>-1){break h}bp(i[e+8>>2]);break h}d=i[i[b+4>>2]+44>>2];c=i[d+20>>2];if((c|0)<0?1:(c|0)<=0?l[d+16>>2]<=0:0){ca(d,i[d+4>>2],i[b+232>>2],i[b+236>>2])}i[a>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0}a=i[e+32>>2];if(!a){break e}i[e+36>>2]=a;bp(a)}F=e+48|0;return}Ho();x()}Ho();x()}za(11708);x()}function vi(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,k=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;e=F-48|0;F=e;a:{if(j[b+352|0]){fm(e+32|0,i[b+8>>2]);c=i[e+32>>2];i[e+32>>2]=0;d=i[b+12>>2];i[b+12>>2]=c;if(!d){break a}bi(d);d=i[e+32>>2];i[e+32>>2]=0;if(!d){break a}bi(d);break a}dm(e+32|0,i[b+8>>2]);c=i[e+32>>2];i[e+32>>2]=0;d=i[b+12>>2];i[b+12>>2]=c;if(!d){break a}bi(d);d=i[e+32>>2];i[e+32>>2]=0;if(!d){break a}bi(d)}b:{c:{d:{d=i[b+12>>2];e:{if(!(i[d+40>>2]!=((i[d+4>>2]-i[d>>2]>>2>>>0)/3|0)?d:0)){b=ho(32);i[e+32>>2]=b;i[e+36>>2]=29;i[e+40>>2]=-2147483616;g[b+29|0]=0;d=j[11281]|j[11282]<<8|(j[11283]<<16|j[11284]<<24);c=j[11277]|j[11278]<<8|(j[11279]<<16|j[11280]<<24);g[b+21|0]=c;g[b+22|0]=c>>>8;g[b+23|0]=c>>>16;g[b+24|0]=c>>>24;g[b+25|0]=d;g[b+26|0]=d>>>8;g[b+27|0]=d>>>16;g[b+28|0]=d>>>24;d=j[11276]|j[11277]<<8|(j[11278]<<16|j[11279]<<24);c=j[11272]|j[11273]<<8|(j[11274]<<16|j[11275]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11268]|j[11269]<<8|(j[11270]<<16|j[11271]<<24);c=j[11264]|j[11265]<<8|(j[11266]<<16|j[11267]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11260]|j[11261]<<8|(j[11262]<<16|j[11263]<<24);c=j[11256]|j[11257]<<8|(j[11258]<<16|j[11259]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+32|0);if(g[e+43|0]>-1){break e}bp(i[e+32>>2]);break e}r=b+200|0;wi(r,b);d=i[b+12>>2];Jb((i[d+28>>2]-i[d+24>>2]>>2)-i[d+44>>2]|0,i[i[b+4>>2]+44>>2]);d=i[b+12>>2];Jb(((i[d+4>>2]-i[d>>2]>>2>>>0)/3|0)-i[d+40>>2]|0,i[i[b+4>>2]+44>>2]);d=i[b+8>>2];c=i[d+100>>2];d=i[d+96>>2];g[e+32|0]=0;q=b+28|0;Uh(q,(c-d|0)/12|0,e+32|0);d=i[b+12>>2];c=i[d+28>>2];d=i[d+24>>2];i[e+32>>2]=-1;Sh(b+52|0,c-d>>2,e+32|0);c=i[b+40>>2];i[b+44>>2]=c;d=i[b+12>>2];f=i[d+4>>2]-i[d>>2]>>2;f=f-((f>>>0)%3|0)|0;f:{if(i[b+48>>2]-c>>2>>>0>=f>>>0){break f}if(f>>>0>=1073741824){break b}h=f<<2;f=ho(h);i[b+44>>2]=f;i[b+40>>2]=f;i[b+48>>2]=f+h;if(!c){break f}bp(c);d=i[b+12>>2]}c=i[d+28>>2];d=i[d+24>>2];g[e+32|0]=0;Uh(b+84|0,c-d>>2,e+32|0);i[b+164>>2]=-1;i[b+168>>2]=0;i[b+100>>2]=i[b+96>>2];i[b+112>>2]=i[b+108>>2];if(i[b+132>>2]){d=i[b+128>>2];if(d){while(1){c=i[d>>2];bp(d);d=c;if(c){continue}break}}i[b+128>>2]=0;c=i[b+124>>2];if(c){d=0;while(1){i[i[b+120>>2]+(d<<2)>>2]=0;d=d+1|0;if((c|0)!=(d|0)){continue}break}}i[b+132>>2]=0}i[b+144>>2]=0;d=i[b+12>>2];c=i[d+28>>2];d=i[d+24>>2];i[e+32>>2]=-1;Sh(b+152|0,c-d>>2,e+32|0);d=i[b+72>>2];i[b+76>>2]=d;c=i[b+12>>2];c=i[c+4>>2]-i[c>>2]>>2;f=(c>>>0)/3|0;g:{if(i[b+80>>2]-d>>2>>>0>=f>>>0){break g}if(c>>>0>=3221225472){break b}f=f<<2;c=ho(f);i[b+76>>2]=c;i[b+72>>2]=c;i[b+80>>2]=c+f;if(!d){break g}bp(d)}i[b- -64>>2]=0;if(!ci(b)){b=ho(32);i[e+32>>2]=b;i[e+36>>2]=29;i[e+40>>2]=-2147483616;g[b+29|0]=0;d=j[11311]|j[11312]<<8|(j[11313]<<16|j[11314]<<24);c=j[11307]|j[11308]<<8|(j[11309]<<16|j[11310]<<24);g[b+21|0]=c;g[b+22|0]=c>>>8;g[b+23|0]=c>>>16;g[b+24|0]=c>>>24;g[b+25|0]=d;g[b+26|0]=d>>>8;g[b+27|0]=d>>>16;g[b+28|0]=d>>>24;d=j[11306]|j[11307]<<8|(j[11308]<<16|j[11309]<<24);c=j[11302]|j[11303]<<8|(j[11304]<<16|j[11305]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11298]|j[11299]<<8|(j[11300]<<16|j[11301]<<24);c=j[11294]|j[11295]<<8|(j[11296]<<16|j[11297]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11290]|j[11291]<<8|(j[11292]<<16|j[11293]<<24);c=j[11286]|j[11287]<<8|(j[11288]<<16|j[11289]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+32|0);if(g[e+43|0]>-1){break e}bp(i[e+32>>2]);break e}if(!xi(b)){b=ho(48);i[e+32>>2]=b;i[e+36>>2]=36;i[e+40>>2]=-2147483600;g[b+36|0]=0;d=j[11348]|j[11349]<<8|(j[11350]<<16|j[11351]<<24);g[b+32|0]=d;g[b+33|0]=d>>>8;g[b+34|0]=d>>>16;g[b+35|0]=d>>>24;d=j[11344]|j[11345]<<8|(j[11346]<<16|j[11347]<<24);c=j[11340]|j[11341]<<8|(j[11342]<<16|j[11343]<<24);g[b+24|0]=c;g[b+25|0]=c>>>8;g[b+26|0]=c>>>16;g[b+27|0]=c>>>24;g[b+28|0]=d;g[b+29|0]=d>>>8;g[b+30|0]=d>>>16;g[b+31|0]=d>>>24;d=j[11336]|j[11337]<<8|(j[11338]<<16|j[11339]<<24);c=j[11332]|j[11333]<<8|(j[11334]<<16|j[11335]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11328]|j[11329]<<8|(j[11330]<<16|j[11331]<<24);c=j[11324]|j[11325]<<8|(j[11326]<<16|j[11327]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11320]|j[11321]<<8|(j[11322]<<16|j[11323]<<24);c=j[11316]|j[11317]<<8|(j[11318]<<16|j[11319]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+32|0);if(g[e+43|0]>-1){break e}bp(i[e+32>>2]);break e}d=(i[b+176>>2]-i[b+172>>2]|0)/136|0;g[e+31|0]=d;c=b;f=i[i[b+4>>2]+44>>2];h=i[f+20>>2];if((h|0)<0?1:(h|0)<=0?l[f+16>>2]<=0:0){ca(f,i[f+4>>2],e+31|0,e+32|0);d=j[e+31|0]}i[c+284>>2]=d&255;c=i[b+12>>2];d=i[c>>2];c=i[c+4>>2];ei(r);i[e+40>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;f=c-d|0;h:{if((f|0)>=1){s=b+96|0;d=d-c|0;d=((d|0)>(f|0)?d:f)>>>2|0;t=d>>>0>1?d:1;d=0;while(1){i:{c=(d>>>0)/3|0;p=c>>>5<<2;o=1<>2]>>2]&o){break i}if(Ql(i[b+12>>2],c)){break i}i[e+24>>2]=0;c=fi(b,c,e+24|0);qf(r,c);f=i[e+24>>2];if(c){c=-1;h=-1;m=-1;j:{if((f|0)==-1){break j}n=i[i[b+12>>2]>>2];c=i[n+(f<<2)>>2];k=f+1|0;k=(k>>>0)%3|0?k:f+ -2|0;if((k|0)!=-1){h=i[n+(k<<2)>>2]}k=f+((f>>>0)%3|0?-1:2)|0;if((k|0)==-1){break j}m=i[n+(k<<2)>>2]}n=i[b+84>>2];k=n+(c>>>3&536870908)|0;i[k>>2]=i[k>>2]|1<>>3&536870908)|0;i[c>>2]=i[c>>2]|1<>>3&536870908)|0;i[c>>2]=i[c>>2]|1<>2];k=i[b+104>>2];k:{if(c>>>0>>0){i[c>>2]=1;i[b+100>>2]=c+4;break k}h=i[s>>2];n=c-h|0;c=n>>2;m=c+1|0;if(m>>>0>=1073741824){break d}u=c<<2;k=k-h|0;c=k>>1;m=k>>2>>>0<536870911?c>>>0>>0?m:c:1073741823;c=0;l:{if(!m){break l}if(m>>>0>=1073741824){break b}c=ho(m<<2)}k=u+c|0;i[k>>2]=1;m=c+(m<<2)|0;k=k+4|0;if((n|0)>=1){hp(c,h,n)}i[b+104>>2]=m;i[b+100>>2]=k;i[b+96>>2]=c;if(!h){break k}bp(h)}c=p+i[q>>2]|0;i[c>>2]=o|i[c>>2];m=-1;if((f|0)!=-1){c=f+1|0;m=(c>>>0)%3|0?c:f+ -2|0}c=i[e+36>>2];o=i[e+40>>2];m:{if(c>>>0>>0){i[c>>2]=m;i[e+36>>2]=c+4;break m}h=i[e+32>>2];p=c-h|0;k=p>>2;n=k+1|0;if(n>>>0>=1073741824){break c}o=o-h|0;c=o>>1;n=o>>2>>>0<536870911?c>>>0>>0?n:c:1073741823;c=0;n:{if(!n){break n}if(n>>>0>=1073741824){break b}c=ho(n<<2)}o=c+(k<<2)|0;i[o>>2]=m;m=c+(n<<2)|0;n=o+4|0;if((p|0)>=1){hp(c,h,p)}i[e+40>>2]=m;i[e+36>>2]=n;i[e+32>>2]=c;if(!h){break m}bp(h)}if((f|0)==-1){break i}c=f+1|0;c=(c>>>0)%3|0?c:f+ -2|0;if((c|0)==-1){break i}c=i[i[i[b+12>>2]+12>>2]+(c<<2)>>2];if((c|0)==-1){break i}f=(c>>>0)/3|0;if(i[i[q>>2]+(f>>>3&268435452)>>2]>>>f&1){break i}if(yi(b,c)){break i}b=ho(48);i[e+8>>2]=b;i[e+12>>2]=32;i[e+16>>2]=-2147483600;g[b+32|0]=0;d=j[11381]|j[11382]<<8|(j[11383]<<16|j[11384]<<24);c=j[11377]|j[11378]<<8|(j[11379]<<16|j[11380]<<24);g[b+24|0]=c;g[b+25|0]=c>>>8;g[b+26|0]=c>>>16;g[b+27|0]=c>>>24;g[b+28|0]=d;g[b+29|0]=d>>>8;g[b+30|0]=d>>>16;g[b+31|0]=d>>>24;d=j[11373]|j[11374]<<8|(j[11375]<<16|j[11376]<<24);c=j[11369]|j[11370]<<8|(j[11371]<<16|j[11372]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11365]|j[11366]<<8|(j[11367]<<16|j[11368]<<24);c=j[11361]|j[11362]<<8|(j[11363]<<16|j[11364]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11357]|j[11358]<<8|(j[11359]<<16|j[11360]<<24);c=j[11353]|j[11354]<<8|(j[11355]<<16|j[11356]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+8|0);if(g[e+19|0]>-1){break h}bp(i[e+8>>2]);break h}c=b;if((f|0)!=-1){h=f+1|0;f=(h>>>0)%3|0?h:f+ -2|0}else{f=-1}hi(c,f,1);if(yi(b,i[e+24>>2])){break i}b=ho(48);i[e+8>>2]=b;i[e+12>>2]=32;i[e+16>>2]=-2147483600;g[b+32|0]=0;d=j[11381]|j[11382]<<8|(j[11383]<<16|j[11384]<<24);c=j[11377]|j[11378]<<8|(j[11379]<<16|j[11380]<<24);g[b+24|0]=c;g[b+25|0]=c>>>8;g[b+26|0]=c>>>16;g[b+27|0]=c>>>24;g[b+28|0]=d;g[b+29|0]=d>>>8;g[b+30|0]=d>>>16;g[b+31|0]=d>>>24;d=j[11373]|j[11374]<<8|(j[11375]<<16|j[11376]<<24);c=j[11369]|j[11370]<<8|(j[11371]<<16|j[11372]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11365]|j[11366]<<8|(j[11367]<<16|j[11368]<<24);c=j[11361]|j[11362]<<8|(j[11363]<<16|j[11364]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11357]|j[11358]<<8|(j[11359]<<16|j[11360]<<24);c=j[11353]|j[11354]<<8|(j[11355]<<16|j[11356]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+8|0);if(g[e+19|0]>-1){break h}bp(i[e+8>>2]);break h}d=d+1|0;if((t|0)!=(d|0)){continue}break}}h=b+72|0;d=i[b+72>>2];f=i[b+76>>2];o:{if((d|0)==(f|0)){break o}c=f+ -4|0;if(c>>>0<=d>>>0){break o}while(1){m=i[d>>2];i[d>>2]=i[c>>2];i[c>>2]=m;d=d+4|0;c=c+ -4|0;if(d>>>0>>0){continue}break}}ii(h,f,i[e+32>>2],i[e+36>>2]);p:{if(i[b+176>>2]==i[b+172>>2]){break p}d=i[b+8>>2];c=i[d+100>>2];d=i[d+96>>2];g[e+8|0]=0;Uh(q,(c-d|0)/12|0,e+8|0);d=i[b+72>>2];c=i[b+76>>2];if((d|0)==(c|0)){break p}while(1){ji(b,i[d>>2]);d=d+4|0;if((c|0)!=(d|0)){continue}break}}zi(r);Jb(i[b+324>>2],i[i[b+4>>2]+44>>2]);Jb(i[b+168>>2],i[i[b+4>>2]+44>>2]);if(!li(b)){b=ho(32);i[e+8>>2]=b;i[e+12>>2]=28;i[e+16>>2]=-2147483616;g[b+28|0]=0;d=j[11410]|j[11411]<<8|(j[11412]<<16|j[11413]<<24);g[b+24|0]=d;g[b+25|0]=d>>>8;g[b+26|0]=d>>>16;g[b+27|0]=d>>>24;d=j[11406]|j[11407]<<8|(j[11408]<<16|j[11409]<<24);c=j[11402]|j[11403]<<8|(j[11404]<<16|j[11405]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;g[b+20|0]=d;g[b+21|0]=d>>>8;g[b+22|0]=d>>>16;g[b+23|0]=d>>>24;d=j[11398]|j[11399]<<8|(j[11400]<<16|j[11401]<<24);c=j[11394]|j[11395]<<8|(j[11396]<<16|j[11397]<<24);g[b+8|0]=c;g[b+9|0]=c>>>8;g[b+10|0]=c>>>16;g[b+11|0]=c>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[11390]|j[11391]<<8|(j[11392]<<16|j[11393]<<24);c=j[11386]|j[11387]<<8|(j[11388]<<16|j[11389]<<24);g[b|0]=c;g[b+1|0]=c>>>8;g[b+2|0]=c>>>16;g[b+3|0]=c>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;i[a>>2]=-1;ro(a+4|0,e+8|0);if(g[e+19|0]>-1){break h}bp(i[e+8>>2]);break h}d=i[i[b+4>>2]+44>>2];c=i[d+20>>2];if((c|0)<0?1:(c|0)<=0?l[d+16>>2]<=0:0){ca(d,i[d+4>>2],i[b+232>>2],i[b+236>>2])}i[a>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0}a=i[e+32>>2];if(!a){break e}i[e+36>>2]=a;bp(a)}F=e+48|0;return}Ho();x()}Ho();x()}za(11708);x()}function ap(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0,k=0,m=0,n=0,o=0,p=0,q=0,r=0;p=F-16|0;F=p;a:{b:{c:{d:{e:{f:{g:{h:{i:{j:{k:{l:{if(a>>>0<=244){g=i[4823];f=a>>>0<11?16:a+11&-8;a=f>>>3|0;b=g>>>a|0;if(b&3){c=a+((b^-1)&1)|0;f=c<<3;b=i[f+19340>>2];a=b+8|0;d=i[b+8>>2];f=f+19332|0;m:{if((d|0)==(f|0)){q=19292,r=yp(-2,c)&g,i[q>>2]=r;break m}i[d+12>>2]=f;i[f+8>>2]=d}c=c<<3;i[b+4>>2]=c|3;b=b+c|0;i[b+4>>2]=i[b+4>>2]|1;break a}k=i[4825];if(f>>>0<=k>>>0){break l}if(b){b=b<>>12&16;c=b;a=a>>>b|0;b=a>>>5&8;c=c|b;a=a>>>b|0;b=a>>>2&4;c=c|b;a=a>>>b|0;b=a>>>1&2;c=c|b;a=a>>>b|0;b=a>>>1&1;c=(c|b)+(a>>>b|0)|0;d=c<<3;b=i[d+19340>>2];a=i[b+8>>2];d=d+19332|0;n:{if((a|0)==(d|0)){g=yp(-2,c)&g;i[4823]=g;break n}i[a+12>>2]=d;i[d+8>>2]=a}a=b+8|0;i[b+4>>2]=f|3;e=b+f|0;c=c<<3;f=c-f|0;i[e+4>>2]=f|1;i[b+c>>2]=f;if(k){c=k>>>3|0;b=(c<<3)+19332|0;d=i[4828];c=1<>2]}i[b+8>>2]=d;i[c+12>>2]=d;i[d+12>>2]=b;i[d+8>>2]=c}i[4828]=e;i[4825]=f;break a}n=i[4824];if(!n){break l}a=(n&0-n)+ -1|0;b=a>>>12&16;c=b;a=a>>>b|0;b=a>>>5&8;c=c|b;a=a>>>b|0;b=a>>>2&4;c=c|b;a=a>>>b|0;b=a>>>1&2;c=c|b;a=a>>>b|0;b=a>>>1&1;b=i[((c|b)+(a>>>b|0)<<2)+19596>>2];e=(i[b+4>>2]&-8)-f|0;c=b;while(1){p:{a=i[c+16>>2];if(!a){a=i[c+20>>2];if(!a){break p}}d=(i[a+4>>2]&-8)-f|0;c=d>>>0>>0;e=c?d:e;b=c?a:b;c=a;continue}break}o=b+f|0;if(o>>>0<=b>>>0){break k}m=i[b+24>>2];d=i[b+12>>2];if((d|0)!=(b|0)){a=i[b+8>>2];i[a+12>>2]=d;i[d+8>>2]=a;break b}c=b+20|0;a=i[c>>2];if(!a){a=i[b+16>>2];if(!a){break j}c=b+16|0}while(1){h=c;d=a;c=a+20|0;a=i[c>>2];if(a){continue}c=d+16|0;a=i[d+16>>2];if(a){continue}break}i[h>>2]=0;break b}f=-1;if(a>>>0>4294967231){break l}a=a+11|0;f=a&-8;k=i[4824];if(!k){break l}h=31;e=0-f|0;if(f>>>0<=16777215){b=a>>>8|0;a=b+1048320>>>16&8;c=b<>>16&4;g=c<>>16&2;a=(g<>>15|0)-(c|(a|b))|0;h=(a<<1|f>>>a+21&1)+28|0}c=i[(h<<2)+19596>>2];q:{r:{s:{if(!c){a=0;break s}a=0;b=f<<((h|0)==31?0:25-(h>>>1|0)|0);while(1){t:{g=(i[c+4>>2]&-8)-f|0;if(g>>>0>=e>>>0){break t}d=c;e=g;if(e){break t}e=0;a=c;break r}g=i[c+20>>2];c=i[((b>>>29&4)+c|0)+16>>2];a=g?(g|0)==(c|0)?a:g:a;b=b<<1;if(c){continue}break}}if(!(a|d)){a=2<>>12&16;c=b;a=a>>>b|0;b=a>>>5&8;c=c|b;a=a>>>b|0;b=a>>>2&4;c=c|b;a=a>>>b|0;b=a>>>1&2;c=c|b;a=a>>>b|0;b=a>>>1&1;a=i[((c|b)+(a>>>b|0)<<2)+19596>>2]}if(!a){break q}}while(1){c=(i[a+4>>2]&-8)-f|0;b=c>>>0>>0;e=b?c:e;d=b?a:d;b=i[a+16>>2];if(b){a=b}else{a=i[a+20>>2]}if(a){continue}break}}if(!d|e>>>0>=i[4825]-f>>>0){break l}h=d+f|0;if(h>>>0<=d>>>0){break k}m=i[d+24>>2];b=i[d+12>>2];if((d|0)!=(b|0)){a=i[d+8>>2];i[a+12>>2]=b;i[b+8>>2]=a;break c}c=d+20|0;a=i[c>>2];if(!a){a=i[d+16>>2];if(!a){break i}c=d+16|0}while(1){g=c;b=a;c=a+20|0;a=i[c>>2];if(a){continue}c=b+16|0;a=i[b+16>>2];if(a){continue}break}i[g>>2]=0;break c}b=i[4825];if(b>>>0>=f>>>0){a=i[4828];c=b-f|0;u:{if(c>>>0>=16){i[4825]=c;d=a+f|0;i[4828]=d;i[d+4>>2]=c|1;i[a+b>>2]=c;i[a+4>>2]=f|3;break u}i[4828]=0;i[4825]=0;i[a+4>>2]=b|3;b=a+b|0;i[b+4>>2]=i[b+4>>2]|1}a=a+8|0;break a}d=i[4826];if(d>>>0>f>>>0){b=d-f|0;i[4826]=b;a=i[4829];c=a+f|0;i[4829]=c;i[c+4>>2]=b|1;i[a+4>>2]=f|3;a=a+8|0;break a}a=0;e=f+47|0;c=e;if(i[4941]){b=i[4943]}else{i[4944]=-1;i[4945]=-1;i[4942]=4096;i[4943]=4096;i[4941]=p+12&-16^1431655768;i[4946]=0;i[4934]=0;b=4096}g=c+b|0;h=0-b|0;c=g&h;if(c>>>0<=f>>>0){break a}b=i[4933];if(b){k=i[4931];m=k+c|0;if(m>>>0<=k>>>0|m>>>0>b>>>0){break a}}if(j[19736]&4){break f}v:{w:{b=i[4829];if(b){a=19740;while(1){k=i[a>>2];if(k+i[a+4>>2]>>>0>b>>>0?k>>>0<=b>>>0:0){break w}a=i[a+8>>2];if(a){continue}break}}b=cp(0);if((b|0)==-1){break g}g=c;a=i[4942];d=a+ -1|0;if(d&b){g=(c-b|0)+(b+d&0-a)|0}if(g>>>0<=f>>>0|g>>>0>2147483646){break g}a=i[4933];if(a){d=i[4931];h=d+g|0;if(h>>>0<=d>>>0|h>>>0>a>>>0){break g}}a=cp(g);if((b|0)!=(a|0)){break v}break e}g=h&g-d;if(g>>>0>2147483646){break g}b=cp(g);if((b|0)==(i[a>>2]+i[a+4>>2]|0)){break h}a=b}if(!((a|0)==-1|f+48>>>0<=g>>>0)){b=i[4943];b=b+(e-g|0)&0-b;if(b>>>0>2147483646){b=a;break e}if((cp(b)|0)!=-1){g=b+g|0;b=a;break e}cp(0-g|0);break g}b=a;if((a|0)!=-1){break e}break g}x()}d=0;break b}b=0;break c}if((b|0)!=-1){break e}}i[4934]=i[4934]|4}if(c>>>0>2147483646){break d}b=cp(c);a=cp(0);if(b>>>0>=a>>>0|(b|0)==-1|(a|0)==-1){break d}g=a-b|0;if(g>>>0<=f+40>>>0){break d}}a=i[4931]+g|0;i[4931]=a;if(a>>>0>l[4932]){i[4932]=a}x:{y:{z:{e=i[4829];if(e){a=19740;while(1){c=i[a>>2];d=i[a+4>>2];if((c+d|0)==(b|0)){break z}a=i[a+8>>2];if(a){continue}break}break y}a=i[4827];if(!(b>>>0>=a>>>0?a:0)){i[4827]=b}a=0;i[4936]=g;i[4935]=b;i[4831]=-1;i[4832]=i[4941];i[4938]=0;while(1){c=a<<3;d=c+19332|0;i[c+19340>>2]=d;i[c+19344>>2]=d;a=a+1|0;if((a|0)!=32){continue}break}a=g+ -40|0;c=b+8&7?-8-b&7:0;d=a-c|0;i[4826]=d;c=b+c|0;i[4829]=c;i[c+4>>2]=d|1;i[(a+b|0)+4>>2]=40;i[4830]=i[4945];break x}if(j[a+12|0]&8|b>>>0<=e>>>0|c>>>0>e>>>0){break y}i[a+4>>2]=d+g;a=e+8&7?-8-e&7:0;b=a+e|0;i[4829]=b;c=i[4826]+g|0;a=c-a|0;i[4826]=a;i[b+4>>2]=a|1;i[(c+e|0)+4>>2]=40;i[4830]=i[4945];break x}d=i[4827];if(b>>>0>>0){i[4827]=b;d=0}c=b+g|0;a=19740;A:{B:{C:{D:{E:{F:{while(1){if((c|0)!=i[a>>2]){a=i[a+8>>2];if(a){continue}break F}break}if(!(j[a+12|0]&8)){break E}}a=19740;while(1){c=i[a>>2];if(c>>>0<=e>>>0){d=c+i[a+4>>2]|0;if(d>>>0>e>>>0){break D}}a=i[a+8>>2];continue}}i[a>>2]=b;i[a+4>>2]=i[a+4>>2]+g;m=(b+8&7?-8-b&7:0)+b|0;i[m+4>>2]=f|3;b=c+(c+8&7?-8-c&7:0)|0;a=(b-m|0)-f|0;h=f+m|0;if((b|0)==(e|0)){i[4829]=h;a=i[4826]+a|0;i[4826]=a;i[h+4>>2]=a|1;break B}if(i[4828]==(b|0)){i[4828]=h;a=i[4825]+a|0;i[4825]=a;i[h+4>>2]=a|1;i[a+h>>2]=a;break B}c=i[b+4>>2];if((c&3)==1){n=c&-8;G:{if(c>>>0<=255){f=c>>>3|0;c=i[b+8>>2];d=i[b+12>>2];if((d|0)==(c|0)){q=19292,r=i[4823]&yp(-2,f),i[q>>2]=r;break G}i[c+12>>2]=d;i[d+8>>2]=c;break G}k=i[b+24>>2];g=i[b+12>>2];H:{if((g|0)!=(b|0)){c=i[b+8>>2];i[c+12>>2]=g;i[g+8>>2]=c;break H}I:{e=b+20|0;f=i[e>>2];if(f){break I}e=b+16|0;f=i[e>>2];if(f){break I}g=0;break H}while(1){c=e;g=f;e=f+20|0;f=i[e>>2];if(f){continue}e=g+16|0;f=i[g+16>>2];if(f){continue}break}i[c>>2]=0}if(!k){break G}c=i[b+28>>2];d=(c<<2)+19596|0;J:{if(i[d>>2]==(b|0)){i[d>>2]=g;if(g){break J}q=19296,r=i[4824]&yp(-2,c),i[q>>2]=r;break G}i[k+(i[k+16>>2]==(b|0)?16:20)>>2]=g;if(!g){break G}}i[g+24>>2]=k;c=i[b+16>>2];if(c){i[g+16>>2]=c;i[c+24>>2]=g}c=i[b+20>>2];if(!c){break G}i[g+20>>2]=c;i[c+24>>2]=g}b=b+n|0;a=a+n|0}i[b+4>>2]=i[b+4>>2]&-2;i[h+4>>2]=a|1;i[a+h>>2]=a;if(a>>>0<=255){b=a>>>3|0;a=(b<<3)+19332|0;c=i[4823];b=1<>2]}i[a+8>>2]=h;i[b+12>>2]=h;i[h+12>>2]=a;i[h+8>>2]=b;break B}e=31;if(a>>>0<=16777215){c=a>>>8|0;b=c+1048320>>>16&8;d=c<>>16&4;f=d<>>16&2;b=(f<>>15|0)-(d|(b|c))|0;e=(b<<1|a>>>b+21&1)+28|0}i[h+28>>2]=e;i[h+16>>2]=0;i[h+20>>2]=0;b=(e<<2)+19596|0;c=i[4824];d=1<>2]=h;i[h+24>>2]=b;break L}e=a<<((e|0)==31?0:25-(e>>>1|0)|0);b=i[b>>2];while(1){c=b;if((i[b+4>>2]&-8)==(a|0)){break C}b=e>>>29|0;e=e<<1;d=(c+(b&4)|0)+16|0;b=i[d>>2];if(b){continue}break}i[d>>2]=h;i[h+24>>2]=c}i[h+12>>2]=h;i[h+8>>2]=h;break B}a=g+ -40|0;c=b+8&7?-8-b&7:0;h=a-c|0;i[4826]=h;c=b+c|0;i[4829]=c;i[c+4>>2]=h|1;i[(a+b|0)+4>>2]=40;i[4830]=i[4945];a=(d+(d+ -39&7?39-d&7:0)|0)+ -47|0;c=a>>>0>>0?e:a;i[c+4>>2]=27;a=i[4938];i[c+16>>2]=i[4937];i[c+20>>2]=a;a=i[4936];i[c+8>>2]=i[4935];i[c+12>>2]=a;i[4937]=c+8;i[4936]=g;i[4935]=b;i[4938]=0;a=c+24|0;while(1){i[a+4>>2]=7;b=a+8|0;a=a+4|0;if(d>>>0>b>>>0){continue}break}if((c|0)==(e|0)){break x}i[c+4>>2]=i[c+4>>2]&-2;d=c-e|0;i[e+4>>2]=d|1;i[c>>2]=d;if(d>>>0<=255){b=d>>>3|0;a=(b<<3)+19332|0;c=i[4823];b=1<>2]}i[a+8>>2]=e;i[b+12>>2]=e;i[e+12>>2]=a;i[e+8>>2]=b;break x}a=31;i[e+16>>2]=0;i[e+20>>2]=0;if(d>>>0<=16777215){b=d>>>8|0;a=b+1048320>>>16&8;c=b<>>16&4;g=c<>>16&2;a=(g<>>15|0)-(c|(a|b))|0;a=(a<<1|d>>>a+21&1)+28|0}i[e+28>>2]=a;b=(a<<2)+19596|0;c=i[4824];g=1<>2]=e;i[e+24>>2]=b;break N}a=d<<((a|0)==31?0:25-(a>>>1|0)|0);b=i[b>>2];while(1){c=b;if((d|0)==(i[b+4>>2]&-8)){break A}b=a>>>29|0;a=a<<1;g=(c+(b&4)|0)+16|0;b=i[g>>2];if(b){continue}break}i[g>>2]=e;i[e+24>>2]=c}i[e+12>>2]=e;i[e+8>>2]=e;break x}a=i[c+8>>2];i[a+12>>2]=h;i[c+8>>2]=h;i[h+24>>2]=0;i[h+12>>2]=c;i[h+8>>2]=a}a=m+8|0;break a}a=i[c+8>>2];i[a+12>>2]=e;i[c+8>>2]=e;i[e+24>>2]=0;i[e+12>>2]=c;i[e+8>>2]=a}a=i[4826];if(a>>>0<=f>>>0){break d}b=a-f|0;i[4826]=b;a=i[4829];c=a+f|0;i[4829]=c;i[c+4>>2]=b|1;i[a+4>>2]=f|3;a=a+8|0;break a}i[4805]=48;a=0;break a}O:{if(!m){break O}a=i[d+28>>2];c=(a<<2)+19596|0;P:{if(i[c>>2]==(d|0)){i[c>>2]=b;if(b){break P}k=yp(-2,a)&k;i[4824]=k;break O}i[m+(i[m+16>>2]==(d|0)?16:20)>>2]=b;if(!b){break O}}i[b+24>>2]=m;a=i[d+16>>2];if(a){i[b+16>>2]=a;i[a+24>>2]=b}a=i[d+20>>2];if(!a){break O}i[b+20>>2]=a;i[a+24>>2]=b}Q:{if(e>>>0<=15){a=e+f|0;i[d+4>>2]=a|3;a=a+d|0;i[a+4>>2]=i[a+4>>2]|1;break Q}i[d+4>>2]=f|3;i[h+4>>2]=e|1;i[e+h>>2]=e;if(e>>>0<=255){b=e>>>3|0;a=(b<<3)+19332|0;c=i[4823];b=1<>2]}i[a+8>>2]=h;i[b+12>>2]=h;i[h+12>>2]=a;i[h+8>>2]=b;break Q}a=31;if(e>>>0<=16777215){b=e>>>8|0;a=b+1048320>>>16&8;c=b<>>16&4;f=c<>>16&2;a=(f<>>15|0)-(c|(a|b))|0;a=(a<<1|e>>>a+21&1)+28|0}i[h+28>>2]=a;i[h+16>>2]=0;i[h+20>>2]=0;b=(a<<2)+19596|0;S:{c=1<>2]=h;break T}a=e<<((a|0)==31?0:25-(a>>>1|0)|0);f=i[b>>2];while(1){b=f;if((i[b+4>>2]&-8)==(e|0)){break S}c=a>>>29|0;a=a<<1;c=(b+(c&4)|0)+16|0;f=i[c>>2];if(f){continue}break}i[c>>2]=h}i[h+24>>2]=b;i[h+12>>2]=h;i[h+8>>2]=h;break Q}a=i[b+8>>2];i[a+12>>2]=h;i[b+8>>2]=h;i[h+24>>2]=0;i[h+12>>2]=b;i[h+8>>2]=a}a=d+8|0;break a}U:{if(!m){break U}a=i[b+28>>2];c=(a<<2)+19596|0;V:{if(i[c>>2]==(b|0)){i[c>>2]=d;if(d){break V}q=19296,r=yp(-2,a)&n,i[q>>2]=r;break U}i[(i[m+16>>2]==(b|0)?16:20)+m>>2]=d;if(!d){break U}}i[d+24>>2]=m;a=i[b+16>>2];if(a){i[d+16>>2]=a;i[a+24>>2]=d}a=i[b+20>>2];if(!a){break U}i[d+20>>2]=a;i[a+24>>2]=d}W:{if(e>>>0<=15){a=e+f|0;i[b+4>>2]=a|3;a=a+b|0;i[a+4>>2]=i[a+4>>2]|1;break W}i[b+4>>2]=f|3;i[o+4>>2]=e|1;i[e+o>>2]=e;if(k){c=k>>>3|0;a=(c<<3)+19332|0;d=i[4828];c=1<>2]}i[a+8>>2]=d;i[c+12>>2]=d;i[d+12>>2]=a;i[d+8>>2]=c}i[4828]=o;i[4825]=e}a=b+8|0}F=p+16|0;return a|0}function Cn(a,b,c,d,e,f,g,h,j){var k=0,l=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0;o=F-192|0;F=o;A=h;J=j&65535;q=d;t=e&65535;D=(e^j)&-2147483648;p=j>>>16&32767;a:{w=e>>>16&32767;b:{c:{if(p+ -1>>>0<32766?w+ -1>>>0<=32765:0){break c}n=e&2147483647;k=n;m=d;if(!(!d&(k|0)==2147418112?!(b|c):(k|0)==2147418112&d>>>0<0|k>>>0<2147418112)){L=d;D=e|32768;break b}n=j&2147483647;e=n;d=h;if(!(!d&(e|0)==2147418112?!(f|g):(e|0)==2147418112&d>>>0<0|e>>>0<2147418112)){L=h;D=j|32768;b=f;c=g;break b}if(!(b|m|(k^2147418112|c))){if(!(d|f|(e^2147418112|g))){b=0;c=0;D=2147450880;break b}D=D|2147418112;b=0;c=0;break b}if(!(d|f|(e^2147418112|g))){b=0;c=0;break b}if(!(b|m|(c|k))){break a}if(!(d|f|(e|g))){D=D|2147418112;b=0;c=0;break b}if((k|0)==65535|k>>>0<65535){j=b;d=!(q|t);h=d<<6;k=r(d?b:q)+32|0;b=r(d?c:t);b=h+((b|0)==32?k:b)|0;sn(o+176|0,j,c,q,t,b+ -15|0);l=16-b|0;q=i[o+184>>2];t=i[o+188>>2];c=i[o+180>>2];b=i[o+176>>2]}if(e>>>0>65535){break c}d=!(A|J);e=d<<6;h=r(d?f:A)+32|0;d=r(d?g:J);d=e+((d|0)==32?h:d)|0;sn(o+160|0,f,g,A,J,d+ -15|0);l=(d+l|0)+ -16|0;A=i[o+168>>2];J=i[o+172>>2];f=i[o+160>>2];g=i[o+164>>2]}e=J|65536;K=e;P=A;d=A;n=e<<15|d>>>17;d=d<<15|g>>>17;h=-102865788-d|0;e=n;k=e;j=1963258675-(k+(4192101508>>0)|0)|0;Bn(o+144|0,d,k,h,j);m=i[o+152>>2];Bn(o+128|0,0-m|0,0-(i[o+156>>2]+(0>>0)|0)|0,h,j);h=i[o+136>>2];j=h<<1|i[o+132>>2]>>>31;h=i[o+140>>2]<<1|h>>>31;Bn(o+112|0,j,h,d,k);m=h;h=i[o+120>>2];Bn(o+96|0,j,m,0-h|0,0-(i[o+124>>2]+(0>>0)|0)|0);h=i[o+104>>2];j=h<<1|i[o+100>>2]>>>31;h=i[o+108>>2]<<1|h>>>31;Bn(o+80|0,j,h,d,k);m=h;h=i[o+88>>2];Bn(o- -64|0,j,m,0-h|0,0-(i[o+92>>2]+(0>>0)|0)|0);h=i[o+72>>2];j=h<<1|i[o+68>>2]>>>31;h=i[o+76>>2]<<1|h>>>31;Bn(o+48|0,j,h,d,k);m=h;h=i[o+56>>2];Bn(o+32|0,j,m,0-h|0,0-(i[o+60>>2]+(0>>0)|0)|0);h=i[o+40>>2];j=h<<1|i[o+36>>2]>>>31;h=i[o+44>>2]<<1|h>>>31;Bn(o+16|0,j,h,d,k);k=h;h=i[o+24>>2];Bn(o,j,k,0-h|0,0-(i[o+28>>2]+(0>>0)|0)|0);N=(w-p|0)+l|0;h=i[o+8>>2];m=i[o+12>>2]<<1|h>>>31;j=h<<1;n=m+ -1|0;j=(i[o+4>>2]>>>31|j)+ -1|0;if((j|0)!=-1){n=n+1|0}h=j;m=0;B=m;s=e;l=0;w=up(h,m,e,l);e=H;u=e;y=n;p=0;h=d;m=up(n,p,d,0);d=m+w|0;k=H+e|0;e=d;k=d>>>0>>0?k+1|0:k;m=0;n=(k|0)==(u|0)&e>>>0>>0|k>>>0>>0;w=up(s,l,y,p);l=w+k|0;k=H+(m|n)|0;k=l>>>0>>0?k+1|0:k;m=l;n=e;e=0;h=up(j,B,h,v);d=e+h|0;l=H+n|0;l=d>>>0>>0?l+1|0:l;w=d;h=d;d=l;e=(n|0)==(d|0)&h>>>0>>0|d>>>0>>0;h=m+e|0;if(h>>>0>>0){k=k+1|0}C=h;h=k;e=g;z=(e&131071)<<15|f>>>17;s=up(j,B,z,0);e=H;v=e;k=f;x=k<<15&-32768;k=up(y,p,x,0);m=k+s|0;l=H+e|0;l=m>>>0>>0?l+1|0:l;e=l;G=up(j,B,x,G);x=0+G|0;k=m;l=k+H|0;l=x>>>0>>0?l+1|0:l;l=(k|0)==(l|0)&x>>>0>>0|l>>>0>>0;k=(e|0)==(v|0)&k>>>0>>0|e>>>0>>0;m=e;e=up(y,p,z,E)+e|0;n=k+H|0;n=e>>>0>>0?n+1|0:n;k=e;e=l+k|0;m=n;m=e>>>0>>0?m+1|0:m;n=e;e=w+e|0;k=m+d|0;k=e>>>0>>0?k+1|0:k;l=h;u=k;d=(d|0)==(k|0)&e>>>0>>0|k>>>0>>0;h=d+C|0;if(h>>>0>>0){l=l+1|0}k=l;d=(e|0)!=0|(u|0)!=0;h=h+d|0;if(h>>>0>>0){k=k+1|0}n=h;h=0-h|0;s=0;w=up(h,s,j,B);d=H;v=d;x=up(y,p,h,s);h=H;C=h;z=0-((0>>0)+k|0)|0;k=0;s=up(j,B,z,k);n=s+x|0;l=H+h|0;l=n>>>0>>0?l+1|0:l;h=n;m=h;s=0+w|0;n=d+m|0;n=s>>>0>>0?n+1|0:n;m=s;d=n;n=(v|0)==(d|0)&m>>>0>>0|d>>>0>>0;m=(l|0)==(C|0)&h>>>0>>0|l>>>0>>0;h=up(y,p,z,k)+l|0;k=m+H|0;k=h>>>0>>0?k+1|0:k;m=h;h=n+m|0;if(h>>>0>>0){k=k+1|0}z=h;h=k;n=s;m=0-((0>>0)+u|0)|0;e=0-e|0;E=m;u=0;C=up(m,u,j,B);x=H;v=e;G=0;m=up(e,G,y,p);e=m+C|0;l=H+x|0;l=e>>>0>>0?l+1|0:l;m=e;e=l;v=up(j,B,v,G);j=0+v|0;k=m;l=k+H|0;l=j>>>0>>0?l+1|0:l;l=(k|0)==(l|0)&j>>>0>>0|l>>>0>>0;k=(e|0)==(x|0)&k>>>0>>0|e>>>0>>0;j=e;e=up(E,u,y,p)+e|0;m=k+H|0;m=e>>>0>>0?m+1|0:m;j=e;e=l+e|0;k=m;k=e>>>0>>0?k+1|0:k;j=e;e=e+n|0;k=k+d|0;k=e>>>0>>0?k+1|0:k;j=e;l=h;e=k;d=(d|0)==(k|0)&j>>>0>>0|k>>>0>>0;h=d+z|0;if(h>>>0>>0){l=l+1|0}d=h;k=l;l=d;n=e+ -1|0;d=j+ -2|0;if(d>>>0<4294967294){n=n+1|0}h=d;m=d;d=n;e=(e|0)==(d|0)&m>>>0>>0|d>>>0>>0;j=l+e|0;if(j>>>0>>0){k=k+1|0}e=j+ -1|0;l=k+ -1|0;l=(e|0)!=-1?l+1|0:l;j=0;y=j;p=e;m=q;x=m<<2|c>>>30;z=0;u=up(e,j,x,z);j=H;m=j;j=c;E=(j&1073741823)<<2|b>>>30;w=0;G=l;j=0;k=up(E,w,l,j);e=k+u|0;n=H+m|0;n=e>>>0>>0?n+1|0:n;k=e;s=n;v=(m|0)==(n|0)&k>>>0>>0|n>>>0>>0;m=n;n=0;u=n;l=0;C=d;I=((t&1073741823)<<2|q>>>30)&-262145|262144;e=up(d,n,I,0);d=e+k|0;m=H+m|0;m=d>>>0>>0?m+1|0:m;q=d;e=m;d=(s|0)==(e|0)&d>>>0>>0|e>>>0>>0;m=d+v|0;if(m>>>0>>0){l=1}c=m;m=up(G,j,I,M);d=c+m|0;k=H+l|0;s=d;n=d>>>0>>0?k+1|0:k;l=up(p,y,I,M);k=H;t=up(x,z,G,j);d=t+l|0;m=H+k|0;m=d>>>0>>0?m+1|0:m;t=d;d=m;m=(k|0)==(d|0)&t>>>0>>0|d>>>0>>0;s=d+s|0;l=m+n|0;k=s;m=k>>>0>>0?l+1|0:l;c=k;n=e+t|0;l=0;d=l+q|0;if(d>>>0>>0){n=n+1|0}t=d;k=d;d=n;e=(e|0)==(d|0)&k>>>0>>0|d>>>0>>0;k=c+e|0;if(k>>>0>>0){m=m+1|0}Q=k;e=t;l=d;q=up(E,w,C,u);n=H;s=h;v=up(h,0,x,z);h=v+q|0;k=H+n|0;k=h>>>0>>0?k+1|0:k;B=h;v=h;h=k;q=(n|0)==(k|0)&v>>>0>>0|k>>>0>>0;c=e;k=0;S=q;O=b<<2&-4;q=up(p,y,O,0);e=q+v|0;n=H+h|0;n=e>>>0>>0?n+1|0:n;v=e;q=e;e=n;h=(h|0)==(e|0)&q>>>0>>0|e>>>0>>0;n=S+h|0;if(n>>>0>>0){k=1}h=c+n|0;l=k+l|0;l=h>>>0>>0?l+1|0:l;q=h;n=m;h=l;d=(d|0)==(l|0)&q>>>0>>0|l>>>0>>0;m=d+Q|0;if(m>>>0>>0){n=n+1|0}c=m;t=q;B=h;Q=up(G,j,O,R);G=H;j=up(s,T,I,M);d=j+Q|0;m=H+G|0;m=d>>>0>>0?m+1|0:m;I=d;k=up(x,z,C,u);d=d+k|0;j=m;l=m+H|0;l=d>>>0>>0?l+1|0:l;x=d;m=up(p,y,E,w);d=d+m|0;k=H+l|0;p=d;k=d>>>0>>0?k+1|0:k;y=0;m=n;d=k;k=(k|0)==(l|0)&p>>>0>>0|k>>>0>>0;n=(j|0)==(G|0)&I>>>0>>0|j>>>0>>0;j=(j|0)==(l|0)&x>>>0>>0|l>>>0>>0;n=n+j|0;n>>>0>>0;l=n;j=k+l|0;l=j;k=d|0;j=k+t|0;l=(l|y)+B|0;l=j>>>0>>0?l+1|0:l;B=j;t=l;h=(h|0)==(l|0)&j>>>0>>0|l>>>0>>0;j=h+c|0;if(j>>>0>>0){m=m+1|0}z=j;j=m;q=B;y=t;l=v;C=up(C,u,O,R);u=H;m=up(E,w,s,T);h=m+C|0;n=H+u|0;n=h>>>0>>0?n+1|0:n;m=n;n=0;k=(m|0)==(u|0)&h>>>0>>0|m>>>0>>0;h=m+l|0;l=(k|n)+e|0;l=h>>>0>>0?l+1|0:l;m=h;k=m;k=(e|0)==(l|0)&k>>>0>>0|l>>>0>>0;e=l;c=k;l=p;p=0;d=p+m|0;k=e+l|0;k=d>>>0

>>0?k+1|0:k;d=(e|0)==(k|0)&d>>>0>>0|k>>>0>>0;e=c+d|0;if(e>>>0>>0){n=1}d=e+q|0;m=n+y|0;h=d;k=j;m=d>>>0>>0?m+1|0:m;j=m;d=(t|0)==(m|0)&d>>>0>>0|m>>>0>>0;e=d+z|0;if(e>>>0>>0){k=k+1|0}d=e;e=k;d:{if((k|0)==131071|k>>>0<131071){m=h;y=0;x=0;n=up(m,y,f,x);l=H;k=b<<17;b=0;c=(n|0)!=0|(l|0)!=0;q=b-c|0;I=k-(b>>>0>>0)|0;w=0-n|0;u=0-((0>>0)+l|0)|0;c=0;z=up(j,c,f,x);b=H;E=b;p=0;l=up(m,y,g,p);k=l+z|0;n=H+b|0;n=k>>>0>>0?n+1|0:n;b=k;l=k;s=0;k=s;v=l;k=(l|0)==(u|0)&w>>>0>>0|u>>>0>>0;B=q-k|0;q=I-(q>>>0>>0)|0;k=up(d,0,f,x);l=H;t=up(m,y,A,0);k=t+k|0;m=H+l|0;m=k>>>0>>0?m+1|0:m;t=up(j,c,g,p);k=t+k|0;l=H+m|0;l=k>>>0>>0?l+1|0:l;m=l;l=(n|0)==(E|0)&b>>>0>>0|n>>>0>>0;b=n+k|0;l=l+m|0;l=b>>>0>>0?l+1|0:l;n=b;b=l;m=up(h,j,K,0);l=H;t=n;n=up(f,g,e,0);m=n+m|0;k=H+l|0;k=m>>>0>>0?k+1|0:k;n=up(d,e,g,p);m=n+m|0;l=H+k|0;k=m;m=up(j,c,A,J);c=k+m|0;k=c;m=0;c=t+m|0;k=b+k|0;b=c;t=B-b|0;c=q-((B>>>0>>0)+(b>>>0>>0?k+1|0:k)|0)|0;N=N+ -1|0;A=w-s|0;b=u-((w>>>0>>0)+v|0)|0;break d}B=j>>>1|0;n=0;m=b<<16;c=0;l=d<<31;h=(j&1)<<31|h>>>1;j=j>>>1|l;z=0;p=f;E=0;b=up(h,z,p,E);k=H;l=k;k=(b|0)!=0|(k|0)!=0;u=c-k|0;M=m-(c>>>0>>0)|0;s=0-b|0;v=0-((0>>0)+l|0)|0;q=0;w=up(h,z,g,q);b=H;I=b;m=n;l=e<<31|d>>>1;n=d<<31|B;O=l|m;m=up(n,0,p,E);c=m+w|0;k=H+b|0;k=c>>>0>>0?k+1|0:k;b=k;k=c;x=k;y=0;m=(k|0)==(v|0)&s>>>0>>0|v>>>0>>0;C=u-m|0;u=M-(u>>>0>>0)|0;m=up(h,j,K,0);l=H;k=up(p,g,e>>>1|0,0);m=k+m|0;l=H+l|0;l=m>>>0>>0?l+1|0:l;d=(e&1)<<31|d>>>1;e=e>>>1|0;M=up(d,e,g,q);m=M+m|0;k=H+l|0;l=up(n,O,A,J);m=l+m|0;J=0;k=up(g,q,n,R);l=H;p=up(d,0,p,E);n=p+k|0;k=H+l|0;k=n>>>0

>>0?k+1|0:k;A=up(h,z,A,0);n=A+n|0;l=H+k|0;l=n>>>0>>0?l+1|0:l;A=n;n=((b|0)==(I|0)&c>>>0>>0|b>>>0>>0)+l|0;c=b;b=b+A|0;if(b>>>0>>0){n=n+1|0}c=b;b=b+J|0;k=m+n|0;k=b>>>0>>0?k+1|0:k;t=C-b|0;c=u-((C>>>0>>0)+k|0)|0;A=s-y|0;b=v-((s>>>0>>0)+x|0)|0}if((N|0)>=16384){D=D|2147418112;b=0;c=0;break b}m=N+16383|0;if((N|0)<=-16383){e:{if(m){break e}m=j;p=A;l=b<<1|p>>>31;n=p<<1;g=(g|0)==(l|0)&n>>>0>f>>>0|l>>>0>g>>>0;n=e&65535;f=t;l=c<<1|f>>>31;c=f<<1|b>>>31;e=c;b=(e|0)==(P|0)&(l|0)==(K|0)?g:(K|0)==(l|0)&e>>>0>P>>>0|l>>>0>K>>>0;c=b+h|0;if(c>>>0>>0){m=m+1|0}b=c;e=b;c=m;e=d+((j|0)==(m|0)&e>>>0>>0|m>>>0>>0)|0;if(e>>>0>>0){n=n+1|0}d=n;if(!(d&65536)){break e}L=e|L;D=d|D;break b}b=0;c=0;break b}k=j;e=e&65535;p=A;n=b<<1|p>>>31;p=p<<1;g=(g|0)==(n|0)&p>>>0>=f>>>0|n>>>0>g>>>0;f=t;n=c<<1|f>>>31;c=f<<1|b>>>31;b=(c|0)==(P|0)&(n|0)==(K|0)?g:(K|0)==(n|0)&c>>>0>=P>>>0|n>>>0>K>>>0;c=b+h|0;if(c>>>0>>0){k=k+1|0}b=c;c=k;f=d;d=((j|0)==(k|0)&b>>>0>>0|k>>>0>>0)+d|0;k=m<<16|e;L=d|L;D=D|(d>>>0>>0?k+1|0:k)}i[a>>2]=b;i[a+4>>2]=c;i[a+8>>2]=L;i[a+12>>2]=D;F=o+192|0;return}i[a>>2]=0;i[a+4>>2]=0;b=!(d|f|(e|g));i[a+8>>2]=b?0:L;i[a+12>>2]=b?2147450880:D;F=o+192|0}function Gd(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var h=0,k=0,l=0,m=0,n=0,p=0,r=0,t=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0;k=F-224|0;F=k;i[a+8>>2]=e;l=a+32|0;h=i[l>>2];f=i[a+36>>2]-h>>2;a:{if(f>>>0>>0){Bd(l,e-f|0);break a}if(f>>>0<=e>>>0){break a}i[a+36>>2]=h+(e<<2)}b:{if(!d){break b}m=1;h=i[b>>2];c:{if((d|0)<=1){l=h;break c}l=h;while(1){f=i[(m<<2)+b>>2];n=(f|0)<(l|0);l=n?f:l;h=n?h:(f|0)>(h|0)?f:h;m=m+1|0;if((m|0)!=(d|0)){continue}break}}i[a+16>>2]=h;i[a+12>>2]=l;d=l;f=(h>>31)-((h>>>0>>0)+(d>>31)|0)|0;d=h-d|0;if(!f&d>>>0>2147483646|f>>>0>0){break b}d=d+1|0;i[a+20>>2]=d;f=(d|0)/2|0;i[a+24>>2]=f;i[a+28>>2]=0-f;if(d&1){break b}i[a+24>>2]=f+ -1}W=i[a+52>>2];y=i[a+48>>2];i[k+184>>2]=0;i[k+188>>2]=0;i[k+176>>2]=0;i[k+180>>2]=0;i[k+168>>2]=0;i[k+172>>2]=0;d=k+160|0;i[d>>2]=0;i[d+4>>2]=0;i[k+152>>2]=0;i[k+156>>2]=0;i[k+144>>2]=0;i[k+148>>2]=0;d:{if(!e){m=0;d=0;break d}Bd(k+144|0,e);m=i[d>>2];d=i[k+156>>2]}f=m-d>>2;e:{if(f>>>0>=e>>>0){if(f>>>0<=e>>>0){break e}i[k+160>>2]=d+(e<<2);break e}Bd(k+144|12,e-f|0)}f=i[k+168>>2];d=i[k+172>>2]-f>>2;f:{if(d>>>0>=e>>>0){if(d>>>0<=e>>>0){break f}i[k+172>>2]=f+(e<<2);break f}Bd(k+168|0,e-d|0)}f=i[k+180>>2];d=i[k+184>>2]-f>>2;g:{if(d>>>0>=e>>>0){if(d>>>0<=e>>>0){break g}i[k+184>>2]=f+(e<<2);break g}Bd(k+180|0,e-d|0)}i[k+136>>2]=0;i[k+128>>2]=0;i[k+132>>2]=0;h:{i:{if(e){if(e>>>0>=1073741824){break i}d=e<<2;f=ho(d);i[k+128>>2]=f;l=d+f|0;i[k+136>>2]=l;ip(f,0,d);i[k+132>>2]=l}K=a+152|0;f=i[a+152>>2];d=i[a+156>>2]-f>>2;j:{if(d>>>0>>0){Bd(K,e-d|0);break j}if(d>>>0<=e>>>0){break j}i[a+156>>2]=f+(e<<2)}i[k+104>>2]=0;i[k+108>>2]=0;i[k+96>>2]=0;i[k+100>>2]=0;i[k+88>>2]=0;i[k+92>>2]=0;i[k+80>>2]=0;i[k+84>>2]=0;i[k+72>>2]=0;i[k+76>>2]=0;d=k- -64|0;i[d>>2]=0;i[d+4>>2]=0;i[k+56>>2]=0;i[k+60>>2]=0;i[k+48>>2]=0;i[k+52>>2]=0;if(e){d=e<<2;C=ho(d);d=ip(C,0,d)+d|0}else{d=0}f=i[a+56>>2];m=i[f>>2];f=i[f+4>>2]-m|0;if((f|0)<5){break h}P=d;J=f>>>2|0;l=J+ -1|0;if(f>>2>>>0>l>>>0){Q=e<<2;L=a+112|0;X=k+125|0;R=k+36|0;S=k+24|0;M=k+20|0;Y=M+24|0;T=M+16|0;G=(e|0)<1;while(1){n=0;D=l;d=i[(l<<2)+m>>2];k:{if((d|0)==-1){break k}r=d+((d>>>0)%3|0?-1:2)|0;z=r>>>5|0;l=1;B=1<>2];p=0;m=d;while(1){l:{if(i[I+(m>>>3&536870908)>>2]>>>m&1){break l}f=i[i[i[y+64>>2]+12>>2]+(m<<2)>>2];if((f|0)==-1){break l}n=i[W>>2];h=i[y+28>>2];A=i[n+(i[h+(f<<2)>>2]<<2)>>2];if((A|0)>=(D|0)){break l}t=f+1|0;t=i[n+(i[h+(((t>>>0)%3|0?t:f+ -2|0)<<2)>>2]<<2)>>2];if((t|0)>=(D|0)){break l}f=i[n+(i[h+(f+((f>>>0)%3|0?-1:2)<<2)>>2]<<2)>>2];if((f|0)>=(D|0)){break l}if(!G){n=i[(k+144|0)+o(p,12)>>2];f=o(e,f);t=o(e,t);A=o(e,A);h=0;while(1){i[n+(h<<2)>>2]=(i[(f+h<<2)+b>>2]+i[(h+t<<2)+b>>2]|0)-i[(h+A<<2)+b>>2];h=h+1|0;if((h|0)!=(e|0)){continue}break}}n=4;p=p+1|0;if((p|0)==4){break k}}m:{if(l&1){h=-1;f=m+1|0;f=(f>>>0)%3|0?f:m+ -2|0;if((f|0)==-1|i[i[y>>2]+(f>>>3&536870908)>>2]>>>f&1){break m}f=i[i[i[y+64>>2]+12>>2]+(f<<2)>>2];if((f|0)==-1){break m}h=f+1|0;h=(h>>>0)%3|0?h:f+ -2|0;break m}h=-1;f=((m>>>0)%3|0?-1:2)+m|0;if((f|0)==-1|i[i[y>>2]+(f>>>3&536870908)>>2]>>>f&1){break m}f=i[i[i[y+64>>2]+12>>2]+(f<<2)>>2];if((f|0)==-1){break m}if((f>>>0)%3|0){h=f+ -1|0;break m}h=f+2|0}if((d|0)==(h|0)){n=p;break k}m=h;f=(h|0)!=-1;h=(f|l^-1)&1;m=h?m:-1;l=f&l;if(!((r|0)==-1|h)){if(B&i[i[y>>2]+(z<<2)>>2]){n=p;break k}f=i[i[i[y+64>>2]+12>>2]+(r<<2)>>2];if((f|0)==-1){n=p;break k}if((f>>>0)%3|0){m=f+ -1|0}else{m=f+2|0}l=0}n=p;if((m|0)!=-1){continue}break}}m=0;g[k+16|0]=0;i[k+8>>2]=0;i[k+12>>2]=0;i[Y>>2]=0;i[T>>2]=0;i[T+4>>2]=0;d=M;i[d+8>>2]=0;i[d+12>>2]=0;i[d>>2]=0;i[d+4>>2]=0;U=o(e,D)<<2;I=U+b|0;p=(o(J+ -2|0,e)<<2)+b|0;h=i[a+152>>2];l=0;if(!G){while(1){f=l<<2;d=i[f+p>>2]-i[f+I>>2]|0;i[f+C>>2]=d;i[f+h>>2]=(d|0)>-1?d<<1:(d^-1)<<1|1;f=d;d=d>>31;m=(f+d^d)+m|0;l=l+1|0;if((l|0)!=(e|0)){continue}break}}dg(k+200|0,L,h,e);d=gg(k+200|0);l=H;h=hg(k+200|0);f=h+d|0;d=H+l|0;d=f>>>0>>0?d+1|0:d;l=f;N=(n|0)<1;if(!N){r=(n<<3)+ -8|0;h=r+(k+48|0)|0;z=h;O=h;f=i[h+4>>2];B=i[h>>2];h=n;B=B+h|0;if(B>>>0>>0){f=f+1|0}h=B;i[O>>2]=h;i[z+4>>2]=f;w=v(bg(h,i[r+(k+80|0)>>2])*(+(h>>>0)+ +(f|0)*4294967296));n:{if(q(w)<0x8000000000000000){h=q(w)>=1?w>0?~~s(u(w*2.3283064365386963e-10),4294967295)>>>0:~~v((w- +(~~w>>>0>>>0))*2.3283064365386963e-10)>>>0:0;f=~~w>>>0;break n}h=-2147483648;f=0}d=d+h|0;f=f+l|0;if(f>>>0>>0){d=d+1|0}l=f}i[k+20>>2]=0;g[k+16|0]=0;i[k+8>>2]=l;i[k+12>>2]=m;ra(S,p,p+Q|0);Hd(R,C,P);if(!N){z=(k+124|0)+n|0;B=z+ -2|0;r=z+ -1|0;d=(n<<3)+ -8|0;V=d+(k+48|0)|0;O=d+(k+80|0)|0;p=1;A=0;while(1){ip(k+124|0,1,n);A=A+1|0;ip(k+124|0,0,A);o:{p:while(1){if(!G){ip(i[k+128>>2],0,Q)}d=0;f=i[k+128>>2];t=0;while(1){if(!j[(k+124|0)+d|0]){if(!G){l=i[(k+144|0)+o(d,12)>>2];h=0;while(1){m=h<<2;E=m+f|0;i[E>>2]=i[E>>2]+i[l+m>>2];h=h+1|0;if((h|0)!=(e|0)){continue}break}}t=1<>2];while(1){f=d+(h<<2)|0;i[f>>2]=i[f>>2]/(p|0);h=h+1|0;if((h|0)!=(e|0)){continue}break}if((e|0)>0){break r}}f=i[a+152>>2];m=0;break q}E=i[k+128>>2];f=i[a+152>>2];m=0;l=0;while(1){h=l<<2;d=i[h+E>>2]-i[h+I>>2]|0;i[h+C>>2]=d;i[f+h>>2]=(d|0)>-1?d<<1:(d^-1)<<1|1;h=d;d=d>>31;m=(h+d^d)+m|0;l=l+1|0;if((l|0)!=(e|0)){continue}break}}dg(k+200|0,L,f,e);f=gg(k+200|0)+hg(k+200|0)|0;h=f;if((n|0)>0){d=i[V+4>>2];f=i[V>>2];w=v(bg(f,i[O>>2]+p|0)*(+(f>>>0)+ +(d|0)*4294967296));s:{if(q(w)<0x8000000000000000){d=~~w>>>0;break s}d=0}h=d+h|0}d=i[k+8>>2];if(!((h|0)>(d|0)|(m|0)>=i[k+12>>2]?(h|0)>=(d|0):0)){i[k+20>>2]=p;g[k+16|0]=t;i[k+8>>2]=h;i[k+12>>2]=m;Hd(S,i[k+128>>2],i[k+132>>2]);Hd(R,C,P)}if((r|0)==(k+124|0)){break o}m=-1;d=j[r|0];h=d;while(1){l=h&255;f=m+ -1|0;t=f+z|0;h=j[t|0];if(h>>>0>>0){f=r;l=z;if(h>>>0>=(d&255)>>>0){while(1){d=f;f=d+ -1|0;E=l+ -2|0;l=d;if(h>>>0>=j[E|0]){continue}break}d=1}l=m+z|0;g[t|0]=d;g[f|0]=h;h=r;if((m|0)==-1){continue p}while(1){d=j[l|0];g[l|0]=j[h|0];g[h|0]=d;l=l+1|0;h=h+ -1|0;if(l>>>0>>0){continue}break}continue p}m=f;if((t|0)!=(k+124|0)){continue}break}break}if(r>>>0<=k+124>>>0){break o}f=j[k+124|0];g[k+124|0]=d;g[r|0]=f;m=X;h=B;if(m>>>0>=h>>>0){break o}while(1){d=j[m|0];g[m|0]=j[h|0];g[h|0]=d;m=m+1|0;h=h+ -1|0;if(m>>>0>>0){continue}break}}p=p+1|0;if((n|0)!=(A|0)){continue}break}}if((n|0)>0){d=((n<<3)+k|0)+72|0;l=d;h=i[k+20>>2];f=h+i[d>>2]|0;d=i[d+4>>2]+(h>>31)|0;i[l>>2]=f;i[l+4>>2]=f>>>0>>0?d+1|0:d}t:{if((e|0)<=0){l=i[K>>2];break t}l=i[K>>2];h=0;f=i[k+36>>2];while(1){m=h<<2;d=i[m+f>>2];i[l+m>>2]=(d|0)>-1?d<<1:(d^-1)<<1|1;h=h+1|0;if((h|0)!=(e|0)){continue}break}}fg(k+200|0,L,l,e);u:{if(N){break u}d=o(n,12)+a|0;r=d+56|0;l=d+52|0;d=d+48|0;m=0;while(1){v:{p=i[r>>2];f=p<<5;h=i[l>>2];w:{if(!(j[k+16|0]>>>m&1)){if((f|0)==(h|0)){if((h+1|0)<=-1){break v}f=d;if(h>>>0<=1073741822){h=h+32&-32;p=p<<6;h=p>>>0>>0?h:p}else{h=2147483647}bd(f,h);h=i[l>>2]}i[l>>2]=h+1;f=i[d>>2]+(h>>>3&536870908)|0;i[f>>2]=i[f>>2]|1<>>0<=1073741822){h=h+32&-32;p=p<<6;h=p>>>0>>0?h:p}else{h=2147483647}bd(f,h);h=i[l>>2]}i[l>>2]=h+1;f=i[d>>2]+(h>>>3&536870908)|0;p=i[f>>2];Z=f,_=yp(-2,h)&p,i[Z>>2]=_}m=m+1|0;if((n|0)!=(m|0)){continue}break u}break}Ho();x()}m=i[a+8>>2];if((m|0)>=1){p=c+U|0;d=i[k+24>>2];n=0;while(1){h=0;x:{if((m|0)<=0){f=i[a+32>>2];break x}while(1){l=h<<2;m=i[l+d>>2];r=i[a+16>>2];y:{if((m|0)>(r|0)){f=i[a+32>>2];i[l+f>>2]=r;break y}f=i[a+32>>2];l=l+f|0;r=i[a+12>>2];if((m|0)<(r|0)){i[l>>2]=r;break y}i[l>>2]=m}h=h+1|0;if((h|0)>2]){continue}break}}l=n<<2;d=l+p|0;l=i[l+I>>2]-i[f+l>>2]|0;i[d>>2]=l;z:{if((l|0)>2]){h=l+i[a+20>>2]|0}else{if((l|0)<=i[a+24>>2]){break z}h=l-i[a+20>>2]|0}i[d>>2]=h}d=f;n=n+1|0;m=i[a+8>>2];if((n|0)<(m|0)){continue}break}}d=i[k+36>>2];if(d){i[k+40>>2]=d;bp(d)}d=i[k+24>>2];if(d){i[k+28>>2]=d;bp(d)}if((J|0)<3){break h}J=D;d=i[a+56>>2];m=i[d>>2];l=D+ -1|0;if(i[d+4>>2]-m>>2>>>0>l>>>0){continue}break}}Io();x()}Ho();x()}if((e|0)>=1){ip(i[k+144>>2],0,e<<2)}m=i[a+8>>2];if((m|0)>=1){f=i[k+144>>2];d=0;while(1){h=0;A:{if((m|0)<=0){m=i[a+32>>2];break A}while(1){e=h<<2;l=i[e+f>>2];n=i[a+16>>2];B:{if((l|0)>(n|0)){m=i[a+32>>2];i[e+m>>2]=n;break B}m=i[a+32>>2];e=e+m|0;n=i[a+12>>2];if((l|0)<(n|0)){i[e>>2]=n;break B}i[e>>2]=l}h=h+1|0;if((h|0)>2]){continue}break}}f=d<<2;e=f+c|0;f=i[b+f>>2]-i[f+m>>2]|0;i[e>>2]=f;C:{if((f|0)>2]){f=f+i[a+20>>2]|0}else{if((f|0)<=i[a+24>>2]){break C}f=f-i[a+20>>2]|0}i[e>>2]=f}f=m;d=d+1|0;m=i[a+8>>2];if((d|0)<(m|0)){continue}break}}if(C){bp(C)}a=i[k+128>>2];if(a){i[k+132>>2]=a;bp(a)}a=i[k+180>>2];if(a){i[k+184>>2]=a;bp(a)}a=i[k+168>>2];if(a){i[k+172>>2]=a;bp(a)}a=i[k+156>>2];if(a){i[k+160>>2]=a;bp(a)}a=i[k+144>>2];if(a){i[k+148>>2]=a;bp(a)}F=k+224|0;return 1}function de(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var h=0,k=0,l=0,m=0,n=0,p=0,r=0,t=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0;k=F-224|0;F=k;i[a+8>>2]=e;l=a+32|0;h=i[l>>2];f=i[a+36>>2]-h>>2;a:{if(f>>>0>>0){Bd(l,e-f|0);break a}if(f>>>0<=e>>>0){break a}i[a+36>>2]=h+(e<<2)}b:{if(!d){break b}m=1;h=i[b>>2];c:{if((d|0)<=1){l=h;break c}l=h;while(1){f=i[(m<<2)+b>>2];n=(f|0)<(l|0);l=n?f:l;h=n?h:(f|0)>(h|0)?f:h;m=m+1|0;if((m|0)!=(d|0)){continue}break}}i[a+16>>2]=h;i[a+12>>2]=l;d=l;f=(h>>31)-((h>>>0>>0)+(d>>31)|0)|0;d=h-d|0;if(!f&d>>>0>2147483646|f>>>0>0){break b}d=d+1|0;i[a+20>>2]=d;f=(d|0)/2|0;i[a+24>>2]=f;i[a+28>>2]=0-f;if(d&1){break b}i[a+24>>2]=f+ -1}V=i[a+52>>2];N=i[a+48>>2];i[k+184>>2]=0;i[k+188>>2]=0;i[k+176>>2]=0;i[k+180>>2]=0;i[k+168>>2]=0;i[k+172>>2]=0;d=k+160|0;i[d>>2]=0;i[d+4>>2]=0;i[k+152>>2]=0;i[k+156>>2]=0;i[k+144>>2]=0;i[k+148>>2]=0;d:{if(!e){m=0;d=0;break d}Bd(k+144|0,e);m=i[d>>2];d=i[k+156>>2]}f=m-d>>2;e:{if(f>>>0>=e>>>0){if(f>>>0<=e>>>0){break e}i[k+160>>2]=d+(e<<2);break e}Bd(k+144|12,e-f|0)}f=i[k+168>>2];d=i[k+172>>2]-f>>2;f:{if(d>>>0>=e>>>0){if(d>>>0<=e>>>0){break f}i[k+172>>2]=f+(e<<2);break f}Bd(k+168|0,e-d|0)}f=i[k+180>>2];d=i[k+184>>2]-f>>2;g:{if(d>>>0>=e>>>0){if(d>>>0<=e>>>0){break g}i[k+184>>2]=f+(e<<2);break g}Bd(k+180|0,e-d|0)}i[k+136>>2]=0;i[k+128>>2]=0;i[k+132>>2]=0;h:{i:{if(e){if(e>>>0>=1073741824){break i}d=e<<2;f=ho(d);i[k+128>>2]=f;l=d+f|0;i[k+136>>2]=l;ip(f,0,d);i[k+132>>2]=l}J=a+152|0;f=i[a+152>>2];d=i[a+156>>2]-f>>2;j:{if(d>>>0>>0){Bd(J,e-d|0);break j}if(d>>>0<=e>>>0){break j}i[a+156>>2]=f+(e<<2)}i[k+104>>2]=0;i[k+108>>2]=0;i[k+96>>2]=0;i[k+100>>2]=0;i[k+88>>2]=0;i[k+92>>2]=0;i[k+80>>2]=0;i[k+84>>2]=0;i[k+72>>2]=0;i[k+76>>2]=0;d=k- -64|0;i[d>>2]=0;i[d+4>>2]=0;i[k+56>>2]=0;i[k+60>>2]=0;i[k+48>>2]=0;i[k+52>>2]=0;if(e){d=e<<2;A=ho(d);d=ip(A,0,d)+d|0}else{d=0}f=i[a+56>>2];m=i[f>>2];f=i[f+4>>2]-m|0;if((f|0)<5){break h}O=d;I=f>>>2|0;l=I+ -1|0;if(f>>2>>>0>l>>>0){P=e<<2;K=a+112|0;W=k+125|0;Q=k+36|0;R=k+24|0;L=k+20|0;X=L+24|0;S=L+16|0;G=(e|0)<1;while(1){n=0;B=l;l=i[(l<<2)+m>>2];k:{if((l|0)==-1){break k}m=i[N+12>>2];C=l+((l>>>0)%3|0?-1:2)|0;D=m+(C<<2)|0;t=1;p=0;d=l;while(1){h=i[m+(d<<2)>>2];l:{if((h|0)==-1){break l}f=-1;r=i[V>>2];n=i[N>>2];y=i[r+(i[n+(h<<2)>>2]<<2)>>2];w=h+1|0;w=(w>>>0)%3|0?w:h+ -2|0;if((w|0)!=-1){w=i[n+(w<<2)>>2]}else{w=-1}h=h+((h>>>0)%3|0?-1:2)|0;if((h|0)!=-1){f=i[n+(h<<2)>>2]}if((y|0)>=(B|0)){break l}h=i[(w<<2)+r>>2];if((h|0)>=(B|0)){break l}f=i[r+(f<<2)>>2];if((f|0)>=(B|0)){break l}n=i[(k+144|0)+o(p,12)>>2];if(!G){f=o(e,f);r=o(e,h);y=o(e,y);h=0;while(1){i[n+(h<<2)>>2]=(i[(f+h<<2)+b>>2]+i[(h+r<<2)+b>>2]|0)-i[(h+y<<2)+b>>2];h=h+1|0;if((h|0)!=(e|0)){continue}break}}n=4;p=p+1|0;if((p|0)==4){break k}}m:{if(t&1){n=d+1|0;d=(n>>>0)%3|0?n:d+ -2|0;f=-1;if((d|0)==-1){break m}d=i[m+(d<<2)>>2];f=-1;if((d|0)==-1){break m}f=d+1|0;f=(f>>>0)%3|0?f:d+ -2|0;break m}d=((d>>>0)%3|0?-1:2)+d|0;f=-1;if((d|0)==-1){break m}d=i[m+(d<<2)>>2];f=-1;if((d|0)==-1){break m}f=d+ -1|0;if((d>>>0)%3|0){break m}f=d+2|0}if((f|0)==(l|0)){n=p;break k}d=f;f=(f|0)!=-1;h=(f|t^-1)&1;d=h?d:-1;t=f&t;if(!((C|0)==-1|h)){f=i[D>>2];if((f|0)==-1){n=p;break k}t=0;if((f>>>0)%3|0){d=f+ -1|0}else{d=f+2|0}}n=p;if((d|0)!=-1){continue}break}}m=0;g[k+16|0]=0;i[k+8>>2]=0;i[k+12>>2]=0;i[X>>2]=0;i[S>>2]=0;i[S+4>>2]=0;d=L;i[d+8>>2]=0;i[d+12>>2]=0;i[d>>2]=0;i[d+4>>2]=0;T=o(e,B)<<2;C=T+b|0;p=(o(I+ -2|0,e)<<2)+b|0;h=i[a+152>>2];l=0;if(!G){while(1){f=l<<2;d=i[f+p>>2]-i[f+C>>2]|0;i[f+A>>2]=d;i[f+h>>2]=(d|0)>-1?d<<1:(d^-1)<<1|1;f=d;d=d>>31;m=(f+d^d)+m|0;l=l+1|0;if((l|0)!=(e|0)){continue}break}}dg(k+200|0,K,h,e);d=gg(k+200|0);l=H;h=hg(k+200|0);f=h+d|0;d=H+l|0;d=f>>>0>>0?d+1|0:d;l=f;M=(n|0)<1;if(!M){r=(n<<3)+ -8|0;h=r+(k+48|0)|0;y=h;t=h;f=i[h+4>>2];w=i[h>>2];h=n;w=w+h|0;if(w>>>0>>0){f=f+1|0}h=w;i[t>>2]=h;i[y+4>>2]=f;z=v(bg(h,i[r+(k+80|0)>>2])*(+(h>>>0)+ +(f|0)*4294967296));n:{if(q(z)<0x8000000000000000){h=q(z)>=1?z>0?~~s(u(z*2.3283064365386963e-10),4294967295)>>>0:~~v((z- +(~~z>>>0>>>0))*2.3283064365386963e-10)>>>0:0;f=~~z>>>0;break n}h=-2147483648;f=0}d=d+h|0;f=f+l|0;if(f>>>0>>0){d=d+1|0}l=f}i[k+20>>2]=0;g[k+16|0]=0;i[k+8>>2]=l;i[k+12>>2]=m;ra(R,p,p+P|0);Hd(Q,A,O);if(!M){y=(k+124|0)+n|0;w=y+ -2|0;r=y+ -1|0;d=(n<<3)+ -8|0;U=d+(k+48|0)|0;Y=d+(k+80|0)|0;p=1;D=0;while(1){ip(k+124|0,1,n);D=D+1|0;ip(k+124|0,0,D);o:{p:while(1){if(!G){ip(i[k+128>>2],0,P)}d=0;f=i[k+128>>2];t=0;while(1){if(!j[(k+124|0)+d|0]){if(!G){l=i[(k+144|0)+o(d,12)>>2];h=0;while(1){m=h<<2;E=m+f|0;i[E>>2]=i[E>>2]+i[l+m>>2];h=h+1|0;if((h|0)!=(e|0)){continue}break}}t=1<>2];while(1){f=d+(h<<2)|0;i[f>>2]=i[f>>2]/(p|0);h=h+1|0;if((h|0)!=(e|0)){continue}break}if((e|0)>0){break r}}f=i[a+152>>2];m=0;break q}E=i[k+128>>2];f=i[a+152>>2];m=0;l=0;while(1){h=l<<2;d=i[h+E>>2]-i[h+C>>2]|0;i[h+A>>2]=d;i[f+h>>2]=(d|0)>-1?d<<1:(d^-1)<<1|1;h=d;d=d>>31;m=(h+d^d)+m|0;l=l+1|0;if((l|0)!=(e|0)){continue}break}}dg(k+200|0,K,f,e);f=gg(k+200|0)+hg(k+200|0)|0;h=f;if((n|0)>0){d=i[U+4>>2];f=i[U>>2];z=v(bg(f,i[Y>>2]+p|0)*(+(f>>>0)+ +(d|0)*4294967296));s:{if(q(z)<0x8000000000000000){d=~~z>>>0;break s}d=0}h=d+h|0}d=i[k+8>>2];if(!((h|0)>(d|0)|(m|0)>=i[k+12>>2]?(h|0)>=(d|0):0)){i[k+20>>2]=p;g[k+16|0]=t;i[k+8>>2]=h;i[k+12>>2]=m;Hd(R,i[k+128>>2],i[k+132>>2]);Hd(Q,A,O)}if((r|0)==(k+124|0)){break o}m=-1;d=j[r|0];h=d;while(1){l=h&255;f=m+ -1|0;t=f+y|0;h=j[t|0];if(h>>>0>>0){f=r;l=y;if(h>>>0>=(d&255)>>>0){while(1){d=f;f=d+ -1|0;E=l+ -2|0;l=d;if(h>>>0>=j[E|0]){continue}break}d=1}l=m+y|0;g[t|0]=d;g[f|0]=h;h=r;if((m|0)==-1){continue p}while(1){d=j[l|0];g[l|0]=j[h|0];g[h|0]=d;l=l+1|0;h=h+ -1|0;if(l>>>0>>0){continue}break}continue p}m=f;if((t|0)!=(k+124|0)){continue}break}break}if(r>>>0<=k+124>>>0){break o}f=j[k+124|0];g[k+124|0]=d;g[r|0]=f;m=W;h=w;if(m>>>0>=h>>>0){break o}while(1){d=j[m|0];g[m|0]=j[h|0];g[h|0]=d;m=m+1|0;h=h+ -1|0;if(m>>>0>>0){continue}break}}p=p+1|0;if((n|0)!=(D|0)){continue}break}}if((n|0)>0){d=((n<<3)+k|0)+72|0;l=d;h=i[k+20>>2];f=h+i[d>>2]|0;d=i[d+4>>2]+(h>>31)|0;i[l>>2]=f;i[l+4>>2]=f>>>0>>0?d+1|0:d}t:{if((e|0)<=0){l=i[J>>2];break t}l=i[J>>2];h=0;f=i[k+36>>2];while(1){m=h<<2;d=i[m+f>>2];i[l+m>>2]=(d|0)>-1?d<<1:(d^-1)<<1|1;h=h+1|0;if((h|0)!=(e|0)){continue}break}}fg(k+200|0,K,l,e);u:{if(M){break u}d=o(n,12)+a|0;r=d+56|0;l=d+52|0;d=d+48|0;m=0;while(1){v:{p=i[r>>2];f=p<<5;h=i[l>>2];w:{if(!(j[k+16|0]>>>m&1)){if((f|0)==(h|0)){if((h+1|0)<=-1){break v}f=d;if(h>>>0<=1073741822){h=h+32&-32;p=p<<6;h=p>>>0>>0?h:p}else{h=2147483647}bd(f,h);h=i[l>>2]}i[l>>2]=h+1;f=i[d>>2]+(h>>>3&536870908)|0;i[f>>2]=i[f>>2]|1<>>0<=1073741822){h=h+32&-32;p=p<<6;h=p>>>0>>0?h:p}else{h=2147483647}bd(f,h);h=i[l>>2]}i[l>>2]=h+1;f=i[d>>2]+(h>>>3&536870908)|0;p=i[f>>2];Z=f,_=yp(-2,h)&p,i[Z>>2]=_}m=m+1|0;if((n|0)!=(m|0)){continue}break u}break}Ho();x()}m=i[a+8>>2];if((m|0)>=1){p=c+T|0;d=i[k+24>>2];n=0;while(1){h=0;x:{if((m|0)<=0){f=i[a+32>>2];break x}while(1){l=h<<2;m=i[l+d>>2];r=i[a+16>>2];y:{if((m|0)>(r|0)){f=i[a+32>>2];i[l+f>>2]=r;break y}f=i[a+32>>2];l=l+f|0;r=i[a+12>>2];if((m|0)<(r|0)){i[l>>2]=r;break y}i[l>>2]=m}h=h+1|0;if((h|0)>2]){continue}break}}l=n<<2;d=l+p|0;l=i[l+C>>2]-i[f+l>>2]|0;i[d>>2]=l;z:{if((l|0)>2]){h=l+i[a+20>>2]|0}else{if((l|0)<=i[a+24>>2]){break z}h=l-i[a+20>>2]|0}i[d>>2]=h}d=f;n=n+1|0;m=i[a+8>>2];if((n|0)<(m|0)){continue}break}}d=i[k+36>>2];if(d){i[k+40>>2]=d;bp(d)}d=i[k+24>>2];if(d){i[k+28>>2]=d;bp(d)}if((I|0)<3){break h}I=B;d=i[a+56>>2];m=i[d>>2];l=B+ -1|0;if(i[d+4>>2]-m>>2>>>0>l>>>0){continue}break}}Io();x()}Ho();x()}if((e|0)>=1){ip(i[k+144>>2],0,e<<2)}m=i[a+8>>2];if((m|0)>=1){f=i[k+144>>2];d=0;while(1){h=0;A:{if((m|0)<=0){m=i[a+32>>2];break A}while(1){e=h<<2;l=i[e+f>>2];n=i[a+16>>2];B:{if((l|0)>(n|0)){m=i[a+32>>2];i[e+m>>2]=n;break B}m=i[a+32>>2];e=e+m|0;n=i[a+12>>2];if((l|0)<(n|0)){i[e>>2]=n;break B}i[e>>2]=l}h=h+1|0;if((h|0)>2]){continue}break}}f=d<<2;e=f+c|0;f=i[b+f>>2]-i[f+m>>2]|0;i[e>>2]=f;C:{if((f|0)>2]){f=f+i[a+20>>2]|0}else{if((f|0)<=i[a+24>>2]){break C}f=f-i[a+20>>2]|0}i[e>>2]=f}f=m;d=d+1|0;m=i[a+8>>2];if((d|0)<(m|0)){continue}break}}if(A){bp(A)}a=i[k+128>>2];if(a){i[k+132>>2]=a;bp(a)}a=i[k+180>>2];if(a){i[k+184>>2]=a;bp(a)}a=i[k+168>>2];if(a){i[k+172>>2]=a;bp(a)}a=i[k+156>>2];if(a){i[k+160>>2]=a;bp(a)}a=i[k+144>>2];if(a){i[k+148>>2]=a;bp(a)}F=k+224|0;return 1}function ln(a,b,c,d,e,f){var g=0,h=0,k=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,I=0,J=0;g=F-8976|0;F=g;y=d+e|0;E=0-y|0;a:{b:{while(1){c:{if((c|0)!=48){if((c|0)!=46){break a}c=i[b+4>>2];if(c>>>0>=l[b+104>>2]){break c}i[b+4>>2]=c+1;c=j[c|0];break b}c=i[b+4>>2];if(c>>>0>2]){k=1;i[b+4>>2]=c+1;c=j[c|0]}else{k=1;c=fn(b)}continue}break}c=fn(b)}n=1;if((c|0)!=48){break a}while(1){h=h+ -1|0;c=m+ -1|0;if((c|0)!=-1){h=h+1|0}m=c;c=i[b+4>>2];d:{if(c>>>0>2]){i[b+4>>2]=c+1;c=j[c|0];break d}c=fn(b)}if((c|0)==48){continue}break}k=1}i[g+784>>2]=0;e:{f:{s=(c|0)==46;r=c+ -48|0;g:{h:{i:{if(s|r>>>0<=9){while(1){j:{if(s&1){if(!n){m=q;h=p;n=1;break j}k=!k;break i}q=q+1|0;if(q>>>0<1){p=p+1|0}if((t|0)<=2044){x=(c|0)==48?x:q;k=(g+784|0)+(t<<2)|0;s=k;if(u){r=(o(i[k>>2],10)+c|0)+ -48|0}i[s>>2]=r;k=1;r=u+1|0;c=(r|0)==9;u=c?0:r;t=c+t|0;break j}if((c|0)==48){break j}i[g+8960>>2]=i[g+8960>>2]|1;x=18396}c=i[b+4>>2];k:{if(c>>>0>2]){i[b+4>>2]=c+1;c=j[c|0];break k}c=fn(b)}s=(c|0)==46;r=c+ -48|0;if(s|r>>>0<10){continue}break}}m=n?m:q;h=n?h:p;if(!(!k|(c&-33)!=69)){n=mn(b);c=H;s=c;l:{if(n|(c|0)!=-2147483648){break l}n=0;s=0;if(!i[b+104>>2]){break l}i[b+4>>2]=i[b+4>>2]+ -1}if(!k){break g}h=h+s|0;b=m+n|0;if(b>>>0>>0){h=h+1|0}m=b;break f}k=!k;if((c|0)<0){break h}}if(!i[b+104>>2]){break h}i[b+4>>2]=i[b+4>>2]+ -1}if(!k){break f}}i[4805]=28;q=0;p=0;en(b);c=0;b=0;break e}b=i[g+784>>2];if(!b){xn(g,+(f|0)*0);q=i[g>>2];p=i[g+4>>2];c=i[g+12>>2];b=i[g+8>>2];break e}if(!((m|0)!=(q|0)|(h|0)!=(p|0)|((p|0)>0?1:(p|0)>=0?q>>>0>9:0)|(b>>>d|0?(d|0)<=30:0))){An(g+48|0,f);En(g+32|0,b);yn(g+16|0,i[g+48>>2],i[g+52>>2],i[g+56>>2],i[g+60>>2],i[g+32>>2],i[g+36>>2],i[g+40>>2],i[g+44>>2]);q=i[g+16>>2];p=i[g+20>>2];c=i[g+28>>2];b=i[g+24>>2];break e}if((h|0)>0?1:(h|0)>=0?m>>>0>(e|0)/-2>>>0:0){i[4805]=68;An(g+96|0,f);yn(g+80|0,i[g+96>>2],i[g+100>>2],i[g+104>>2],i[g+108>>2],-1,-1,-1,2147418111);yn(g- -64|0,i[g+80>>2],i[g+84>>2],i[g+88>>2],i[g+92>>2],-1,-1,-1,2147418111);q=i[g+64>>2];p=i[g+68>>2];c=i[g+76>>2];b=i[g+72>>2];break e}b=e+ -226|0;c=m>>>0>>0;b=b>>31;if((h|0)<(b|0)?1:(h|0)<=(b|0)?c:0){i[4805]=68;An(g+144|0,f);yn(g+128|0,i[g+144>>2],i[g+148>>2],i[g+152>>2],i[g+156>>2],0,0,0,65536);yn(g+112|0,i[g+128>>2],i[g+132>>2],i[g+136>>2],i[g+140>>2],0,0,0,65536);q=i[g+112>>2];p=i[g+116>>2];c=i[g+124>>2];b=i[g+120>>2];break e}if(u){if((u|0)<=8){c=(g+784|0)+(t<<2)|0;b=i[c>>2];while(1){b=o(b,10);u=u+1|0;if((u|0)!=9){continue}break}i[c>>2]=b}t=t+1|0}m:{n=m;if((x|0)>(n|0)|(x|0)>=9|(n|0)>17){break m}if((n|0)==9){An(g+192|0,f);En(g+176|0,i[g+784>>2]);yn(g+160|0,i[g+192>>2],i[g+196>>2],i[g+200>>2],i[g+204>>2],i[g+176>>2],i[g+180>>2],i[g+184>>2],i[g+188>>2]);q=i[g+160>>2];p=i[g+164>>2];c=i[g+172>>2];b=i[g+168>>2];break e}if((n|0)<=8){An(g+272|0,f);En(g+256|0,i[g+784>>2]);yn(g+240|0,i[g+272>>2],i[g+276>>2],i[g+280>>2],i[g+284>>2],i[g+256>>2],i[g+260>>2],i[g+264>>2],i[g+268>>2]);An(g+224|0,i[(0-n<<2)+17616>>2]);Cn(g+208|0,i[g+240>>2],i[g+244>>2],i[g+248>>2],i[g+252>>2],i[g+224>>2],i[g+228>>2],i[g+232>>2],i[g+236>>2]);q=i[g+208>>2];p=i[g+212>>2];c=i[g+220>>2];b=i[g+216>>2];break e}b=(o(n,-3)+d|0)+27|0;c=i[g+784>>2];if(c>>>b|0?(b|0)<=30:0){break m}An(g+352|0,f);En(g+336|0,c);yn(g+320|0,i[g+352>>2],i[g+356>>2],i[g+360>>2],i[g+364>>2],i[g+336>>2],i[g+340>>2],i[g+344>>2],i[g+348>>2]);An(g+304|0,i[(n<<2)+17544>>2]);yn(g+288|0,i[g+320>>2],i[g+324>>2],i[g+328>>2],i[g+332>>2],i[g+304>>2],i[g+308>>2],i[g+312>>2],i[g+316>>2]);q=i[g+288>>2];p=i[g+292>>2];c=i[g+300>>2];b=i[g+296>>2];break e}while(1){c=t;t=c+ -1|0;if(!i[(g+784|0)+(t<<2)>>2]){continue}break}u=0;b=(n|0)%9|0;n:{if(!b){k=0;break n}r=(n|0)>-1?b:b+9|0;o:{if(!c){k=0;c=0;break o}h=i[(0-r<<2)+17616>>2];p=1e9/(h|0)|0;s=0;b=0;k=0;while(1){m=s;q=(g+784|0)+(b<<2)|0;s=i[q>>2];t=(s>>>0)/(h>>>0)|0;m=m+t|0;i[q>>2]=m;m=!m&(b|0)==(k|0);k=m?k+1&2047:k;n=m?n+ -9|0:n;s=o(p,s-o(h,t)|0);b=b+1|0;if((c|0)!=(b|0)){continue}break}if(!s){break o}i[(g+784|0)+(c<<2)>>2]=s;c=c+1|0}n=(n-r|0)+9|0}while(1){q=(g+784|0)+(k<<2)|0;p:{while(1){if((n|0)!=36|l[q>>2]>=10384593?(n|0)>=36:0){break p}t=c+2047|0;s=0;r=c;while(1){c=r;p=t&2047;t=(g+784|0)+(p<<2)|0;b=i[t>>2];h=b>>>3|0;r=b<<29;b=r+s|0;if(b>>>0>>0){h=h+1|0}m=b;r=0;q:{if(!h&b>>>0<1000000001|h>>>0<0){break q}r=wp(b,h,1e9,0);m=m-up(r,H,1e9,0)|0}s=r;i[t>>2]=m;r=(p|0)!=(c+ -1&2047)?c:(p|0)==(k|0)?c:m?c:p;t=p+ -1|0;if((p|0)!=(k|0)){continue}break}u=u+ -29|0;if(!s){continue}break}k=k+ -1&2047;if((r|0)==(k|0)){b=(g+784|0)+((r+2046&2047)<<2)|0;c=r+ -1&2047;i[b>>2]=i[b>>2]|i[(g+784|0)+(c<<2)>>2]}n=n+9|0;i[(g+784|0)+(k<<2)>>2]=s;continue}break}r:{s:while(1){h=c+1&2047;p=(g+784|0)+((c+ -1&2047)<<2)|0;while(1){m=(n|0)>45?9:1;t:{while(1){r=k;b=0;u:{while(1){v:{k=b+r&2047;if((k|0)==(c|0)){break v}k=i[(g+784|0)+(k<<2)>>2];q=i[(b<<2)+17568>>2];if(k>>>0>>0){break v}if(k>>>0>q>>>0){break u}b=b+1|0;if((b|0)!=4){continue}}break}if((n|0)!=36){break u}m=0;h=0;b=0;q=0;p=0;while(1){k=b+r&2047;if((k|0)==(c|0)){c=c+1&2047;i[((c<<2)+g|0)+780>>2]=0}yn(g+768|0,m,h,q,p,0,0,1342177280,1075633366);En(g+752|0,i[(g+784|0)+(k<<2)>>2]);tn(g+736|0,i[g+768>>2],i[g+772>>2],i[g+776>>2],i[g+780>>2],i[g+752>>2],i[g+756>>2],i[g+760>>2],i[g+764>>2]);q=i[g+744>>2];p=i[g+748>>2];m=i[g+736>>2];h=i[g+740>>2];b=b+1|0;if((b|0)!=4){continue}break}An(g+720|0,f);yn(g+704|0,m,h,q,p,i[g+720>>2],i[g+724>>2],i[g+728>>2],i[g+732>>2]);q=i[g+712>>2];p=i[g+716>>2];m=0;h=0;n=i[g+704>>2];s=i[g+708>>2];t=u+113|0;e=t-e|0;k=(e|0)<(d|0);d=k?(e|0)>0?e:0:d;if((d|0)<=112){break t}break r}u=m+u|0;k=c;if((c|0)==(r|0)){continue}break}q=1e9>>>m|0;s=-1<>2];b=(x>>>m|0)+b|0;i[t>>2]=b;b=!b&(k|0)==(r|0);k=b?k+1&2047:k;n=b?n+ -9|0:n;b=o(q,s&x);r=r+1&2047;if((r|0)!=(c|0)){continue}break}if(!b){continue}if((h|0)!=(k|0)){i[(g+784|0)+(c<<2)>>2]=b;c=h;continue s}i[p>>2]=i[p>>2]|1;k=h;continue}break}break}xn(g+656|0,fp(225-d|0));gn(g+688|0,i[g+656>>2],i[g+660>>2],i[g+664>>2],i[g+668>>2],n,s,q,p);z=i[g+696>>2];A=i[g+700>>2];B=i[g+688>>2];C=i[g+692>>2];xn(g+640|0,fp(113-d|0));dp(g+672|0,n,s,q,p,i[g+640>>2],i[g+644>>2],i[g+648>>2],i[g+652>>2]);m=i[g+672>>2];h=i[g+676>>2];v=i[g+680>>2];w=i[g+684>>2];zn(g+624|0,n,s,q,p,m,h,v,w);tn(g+608|0,B,C,z,A,i[g+624>>2],i[g+628>>2],i[g+632>>2],i[g+636>>2]);q=i[g+616>>2];p=i[g+620>>2];n=i[g+608>>2];s=i[g+612>>2]}b=r+4&2047;w:{if((b|0)==(c|0)){break w}b=i[(g+784|0)+(b<<2)>>2];x:{if(b>>>0<=499999999){if((r+5&2047)==(c|0)?!b:0){break x}xn(g+496|0,+(f|0)*.25);tn(g+480|0,m,h,v,w,i[g+496>>2],i[g+500>>2],i[g+504>>2],i[g+508>>2]);v=i[g+488>>2];w=i[g+492>>2];m=i[g+480>>2];h=i[g+484>>2];break x}if((b|0)!=5e8){xn(g+592|0,+(f|0)*.75);tn(g+576|0,m,h,v,w,i[g+592>>2],i[g+596>>2],i[g+600>>2],i[g+604>>2]);v=i[g+584>>2];w=i[g+588>>2];m=i[g+576>>2];h=i[g+580>>2];break x}D=+(f|0);if((r+5&2047)==(c|0)){xn(g+528|0,D*.5);tn(g+512|0,m,h,v,w,i[g+528>>2],i[g+532>>2],i[g+536>>2],i[g+540>>2]);v=i[g+520>>2];w=i[g+524>>2];m=i[g+512>>2];h=i[g+516>>2];break x}xn(g+560|0,D*.75);tn(g+544|0,m,h,v,w,i[g+560>>2],i[g+564>>2],i[g+568>>2],i[g+572>>2]);v=i[g+552>>2];w=i[g+556>>2];m=i[g+544>>2];h=i[g+548>>2]}if((d|0)>111){break w}dp(g+464|0,m,h,v,w,0,0,0,1073676288);if(qn(i[g+464>>2],i[g+468>>2],i[g+472>>2],i[g+476>>2],0,0,0,0)){break w}tn(g+448|0,m,h,v,w,0,0,0,1073676288);v=i[g+456>>2];w=i[g+460>>2];m=i[g+448>>2];h=i[g+452>>2]}tn(g+432|0,n,s,q,p,m,h,v,w);zn(g+416|0,i[g+432>>2],i[g+436>>2],i[g+440>>2],i[g+444>>2],B,C,z,A);q=i[g+424>>2];p=i[g+428>>2];n=i[g+416>>2];s=i[g+420>>2];y:{if((t&2147483647)<=(-2-y|0)){break y}b=g+400|0;i[b+8>>2]=q;i[b+12>>2]=p&2147483647;i[b>>2]=n;i[b+4>>2]=s;yn(g+384|0,n,s,q,p,0,0,0,1073610752);c=rn(i[g+400>>2],i[g+404>>2],i[g+408>>2],i[g+412>>2],1081081856);b=(c|0)<0;q=b?q:i[g+392>>2];p=b?p:i[g+396>>2];n=b?n:i[g+384>>2];s=b?s:i[g+388>>2];u=((c|0)>-1)+u|0;if(G=!(k&(b|(d|0)!=(e|0))&(qn(m,h,v,w,0,0,0,0)|0)!=0),I=0,J=(u+110|0)<=(E|0),J?G:I){break y}i[4805]=68}hn(g+368|0,n,s,q,p,u);q=i[g+368>>2];p=i[g+372>>2];c=i[g+380>>2];b=i[g+376>>2]}i[a>>2]=q;i[a+4>>2]=p;i[a+8>>2]=b;i[a+12>>2]=c;F=g+8976|0}function De(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var h=0,k=0,l=0,m=0,n=0,p=0,r=0,t=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0;h=F-240|0;F=h;V=i[a+36>>2];y=i[a+32>>2];i[h+184>>2]=0;i[h+188>>2]=0;i[h+176>>2]=0;i[h+180>>2]=0;i[h+168>>2]=0;i[h+172>>2]=0;d=h+160|0;i[d>>2]=0;i[d+4>>2]=0;i[h+152>>2]=0;i[h+156>>2]=0;i[h+144>>2]=0;i[h+148>>2]=0;if(e){Bd(h+144|0,e);m=i[d>>2];d=i[h+156>>2]}else{d=0}f=m-d>>2;a:{if(f>>>0>=e>>>0){if(f>>>0<=e>>>0){break a}i[h+160>>2]=d+(e<<2);break a}Bd(h+144|12,e-f|0)}f=i[h+168>>2];d=i[h+172>>2]-f>>2;b:{if(d>>>0>=e>>>0){if(d>>>0<=e>>>0){break b}i[h+172>>2]=f+(e<<2);break b}Bd(h+168|0,e-d|0)}f=i[h+180>>2];d=i[h+184>>2]-f>>2;c:{if(d>>>0>=e>>>0){if(d>>>0<=e>>>0){break c}i[h+184>>2]=f+(e<<2);break c}Bd(h+180|0,e-d|0)}i[h+136>>2]=0;i[h+128>>2]=0;i[h+132>>2]=0;d:{e:{if(e){if(e>>>0>=1073741824){break e}d=e<<2;f=ho(d);i[h+128>>2]=f;m=d+f|0;i[h+136>>2]=m;ip(f,0,d);i[h+132>>2]=m}K=a+136|0;f=i[a+136>>2];d=i[a+140>>2]-f>>2;f:{if(d>>>0>>0){Bd(K,e-d|0);break f}if(d>>>0<=e>>>0){break f}i[a+140>>2]=f+(e<<2)}i[h+104>>2]=0;i[h+108>>2]=0;i[h+96>>2]=0;i[h+100>>2]=0;i[h+88>>2]=0;i[h+92>>2]=0;i[h+80>>2]=0;i[h+84>>2]=0;i[h+72>>2]=0;i[h+76>>2]=0;d=h- -64|0;i[d>>2]=0;i[d+4>>2]=0;i[h+56>>2]=0;i[h+60>>2]=0;i[h+48>>2]=0;i[h+52>>2]=0;if(e){d=e<<2;B=ho(d);d=ip(B,0,d)+d|0}else{d=0}f=i[a+40>>2];m=i[f>>2];f=i[f+4>>2]-m|0;if((f|0)<5){break d}O=d;J=f>>>2|0;l=J+ -1|0;if(f>>2>>>0>l>>>0){P=e<<2;W=a+8|0;L=a+96|0;X=h+125|0;Q=h+36|0;R=h+24|0;M=h+20|0;Y=M+24|0;S=M+16|0;I=(e|0)<1;while(1){d=0;C=l;n=i[(l<<2)+m>>2];g:{if((n|0)==-1){break g}r=n+((n>>>0)%3|0?-1:2)|0;z=r>>>5|0;l=1;D=1<>2];f=0;m=n;while(1){h:{if(i[E+(m>>>3&536870908)>>2]>>>m&1){break h}d=i[i[i[y+64>>2]+12>>2]+(m<<2)>>2];if((d|0)==-1){break h}p=i[V>>2];k=i[y+28>>2];A=i[p+(i[k+(d<<2)>>2]<<2)>>2];if((A|0)>=(C|0)){break h}t=d+1|0;t=i[p+(i[k+(((t>>>0)%3|0?t:d+ -2|0)<<2)>>2]<<2)>>2];if((t|0)>=(C|0)){break h}d=i[p+(i[k+(d+((d>>>0)%3|0?-1:2)<<2)>>2]<<2)>>2];if((d|0)>=(C|0)){break h}if(!I){p=i[(h+144|0)+o(f,12)>>2];d=o(d,e);t=o(e,t);A=o(e,A);k=0;while(1){i[p+(k<<2)>>2]=(i[(d+k<<2)+b>>2]+i[(k+t<<2)+b>>2]|0)-i[(k+A<<2)+b>>2];k=k+1|0;if((k|0)!=(e|0)){continue}break}}d=4;f=f+1|0;if((f|0)==4){break g}}i:{if(l&1){k=-1;d=m+1|0;d=(d>>>0)%3|0?d:m+ -2|0;if((d|0)==-1|i[i[y>>2]+(d>>>3&536870908)>>2]>>>d&1){break i}d=i[i[i[y+64>>2]+12>>2]+(d<<2)>>2];if((d|0)==-1){break i}m=d+1|0;k=(m>>>0)%3|0?m:d+ -2|0;break i}k=-1;d=((m>>>0)%3|0?-1:2)+m|0;if((d|0)==-1|i[i[y>>2]+(d>>>3&536870908)>>2]>>>d&1){break i}d=i[i[i[y+64>>2]+12>>2]+(d<<2)>>2];if((d|0)==-1){break i}if((d>>>0)%3|0){k=d+ -1|0;break i}k=d+2|0}if((k|0)==(n|0)){d=f;break g}m=k;d=(k|0)!=-1;k=(d|l^-1)&1;m=k?m:-1;l=d&l;if(!((r|0)==-1|k)){if(D&i[i[y>>2]+(z<<2)>>2]){d=f;break g}d=i[i[i[y+64>>2]+12>>2]+(r<<2)>>2];if((d|0)==-1){d=f;break g}l=0;if((d>>>0)%3|0){m=d+ -1|0}else{m=d+2|0}}d=f;if((m|0)!=-1){continue}break}}m=0;g[h+16|0]=0;i[h+8>>2]=0;i[h+12>>2]=0;i[Y>>2]=0;i[S>>2]=0;i[S+4>>2]=0;f=M;i[f+8>>2]=0;i[f+12>>2]=0;i[f>>2]=0;i[f+4>>2]=0;T=o(e,C)<<2;E=T+b|0;r=(o(J+ -2|0,e)<<2)+b|0;n=i[a+136>>2];l=0;if(!I){while(1){k=l<<2;f=i[k+r>>2]-i[k+E>>2]|0;i[k+B>>2]=f;i[k+n>>2]=(f|0)>-1?f<<1:(f^-1)<<1|1;k=f;f=f>>31;m=(k+f^f)+m|0;l=l+1|0;if((l|0)!=(e|0)){continue}break}}dg(h+216|0,L,n,e);f=gg(h+216|0);l=H;k=hg(h+216|0);f=k+f|0;l=H+l|0;l=f>>>0>>0?l+1|0:l;k=f;N=(d|0)<1;if(!N){p=(d<<3)+ -8|0;n=p+(h+48|0)|0;z=n;t=n;f=i[n+4>>2];D=d+i[n>>2]|0;if(D>>>0>>0){f=f+1|0}n=D;i[t>>2]=n;i[z+4>>2]=f;w=v(bg(n,i[p+(h+80|0)>>2])*(+(n>>>0)+ +(f|0)*4294967296));j:{if(q(w)<0x8000000000000000){n=q(w)>=1?w>0?~~s(u(w*2.3283064365386963e-10),4294967295)>>>0:~~v((w- +(~~w>>>0>>>0))*2.3283064365386963e-10)>>>0:0;f=~~w>>>0;break j}n=-2147483648;f=0}l=l+n|0;f=f+k|0;if(f>>>0>>0){l=l+1|0}k=f}i[h+20>>2]=0;g[h+16|0]=0;i[h+8>>2]=k;i[h+12>>2]=m;ra(R,r,r+P|0);Hd(Q,B,O);if(!N){z=(h+124|0)+d|0;D=z+ -2|0;r=z+ -1|0;f=(d<<3)+ -8|0;U=f+(h+48|0)|0;Z=f+(h+80|0)|0;f=1;A=0;while(1){ip(h+124|0,1,d);A=A+1|0;ip(h+124|0,0,A);k:{l:while(1){if(!I){ip(i[h+128>>2],0,P)}p=0;m=i[h+128>>2];t=0;while(1){if(!j[(h+124|0)+p|0]){if(!I){l=i[(h+144|0)+o(p,12)>>2];k=0;while(1){n=k<<2;G=n+m|0;i[G>>2]=i[G>>2]+i[l+n>>2];k=k+1|0;if((k|0)!=(e|0)){continue}break}}t=1<>2];while(1){l=m+(k<<2)|0;i[l>>2]=i[l>>2]/(f|0);k=k+1|0;if((k|0)!=(e|0)){continue}break}if((e|0)>0){break n}}n=i[a+136>>2];m=0;break m}G=i[h+128>>2];n=i[a+136>>2];m=0;l=0;while(1){p=l<<2;k=i[p+G>>2]-i[p+E>>2]|0;i[p+B>>2]=k;i[n+p>>2]=(k|0)>-1?k<<1:(k^-1)<<1|1;p=m;m=k>>31;m=p+(m+k^m)|0;l=l+1|0;if((l|0)!=(e|0)){continue}break}}dg(h+216|0,L,n,e);k=gg(h+216|0)+hg(h+216|0)|0;if((d|0)>0){l=i[U+4>>2];n=i[U>>2];w=v(bg(n,i[Z>>2]+f|0)*(+(n>>>0)+ +(l|0)*4294967296));o:{if(q(w)<0x8000000000000000){n=~~w>>>0;break o}n=0}k=n+k|0}l=i[h+8>>2];if(!((k|0)>(l|0)|(m|0)>=i[h+12>>2]?(k|0)>=(l|0):0)){i[h+20>>2]=f;g[h+16|0]=t;i[h+8>>2]=k;i[h+12>>2]=m;Hd(R,i[h+128>>2],i[h+132>>2]);Hd(Q,B,O)}if((r|0)==(h+124|0)){break k}m=-1;p=j[r|0];k=p;while(1){n=k&255;l=m+ -1|0;t=l+z|0;k=j[t|0];if(k>>>0>>0){n=r;l=z;if(k>>>0>=(p&255)>>>0){while(1){p=n;n=n+ -1|0;G=l+ -2|0;l=p;if(k>>>0>=j[G|0]){continue}break}p=1}l=m+z|0;g[t|0]=p;g[n|0]=k;k=r;if((m|0)==-1){continue l}while(1){m=j[l|0];g[l|0]=j[k|0];g[k|0]=m;l=l+1|0;k=k+ -1|0;if(l>>>0>>0){continue}break}continue l}m=l;if((t|0)!=(h+124|0)){continue}break}break}if(r>>>0<=h+124>>>0){break k}m=j[h+124|0];g[h+124|0]=p;g[r|0]=m;m=X;k=D;if(m>>>0>=k>>>0){break k}while(1){l=j[m|0];g[m|0]=j[k|0];g[k|0]=l;m=m+1|0;k=k+ -1|0;if(m>>>0>>0){continue}break}}f=f+1|0;if((d|0)!=(A|0)){continue}break}}if((d|0)>0){f=((d<<3)+h|0)+72|0;l=f;k=i[h+20>>2];m=k+i[f>>2]|0;f=i[f+4>>2]+(k>>31)|0;i[l>>2]=m;i[l+4>>2]=m>>>0>>0?f+1|0:f}p:{if((e|0)<=0){l=i[K>>2];break p}l=i[K>>2];k=0;m=i[h+36>>2];while(1){n=k<<2;f=i[n+m>>2];i[l+n>>2]=(f|0)>-1?f<<1:(f^-1)<<1|1;k=k+1|0;if((k|0)!=(e|0)){continue}break}}fg(h+216|0,L,l,e);q:{if(N){break q}f=o(d,12)+a|0;p=f+40|0;n=f+36|0;f=f+32|0;m=0;while(1){r:{r=i[p>>2];l=r<<5;k=i[n>>2];s:{if(!(j[h+16|0]>>>m&1)){if((k|0)==(l|0)){if((k+1|0)<=-1){break r}l=f;if(k>>>0<=1073741822){k=k+32&-32;r=r<<6;k=r>>>0>>0?k:r}else{k=2147483647}bd(l,k);k=i[n>>2]}i[n>>2]=k+1;l=i[f>>2]+(k>>>3&536870908)|0;i[l>>2]=i[l>>2]|1<>>0<=1073741822){k=k+32&-32;r=r<<6;k=r>>>0>>0?k:r}else{k=2147483647}bd(l,k);k=i[n>>2]}i[n>>2]=k+1;l=i[f>>2]+(k>>>3&536870908)|0;r=i[l>>2];_=l,$=yp(-2,k)&r,i[_>>2]=$}m=m+1|0;if((m|0)!=(d|0)){continue}break q}break}Ho();x()}d=i[h+24>>2];f=i[d>>2];d=i[d+4>>2];m=i[E+4>>2];i[h+208>>2]=i[E>>2];i[h+212>>2]=m;i[h+200>>2]=f;i[h+204>>2]=d;ze(h+216|0,W,h+208|0,h+200|0);d=c+T|0;i[d>>2]=i[h+216>>2];i[d+4>>2]=i[h+220>>2];d=i[h+36>>2];if(d){i[h+40>>2]=d;bp(d)}d=i[h+24>>2];if(d){i[h+28>>2]=d;bp(d)}if((J|0)<3){break d}J=C;d=i[a+40>>2];m=i[d>>2];l=C+ -1|0;if(i[d+4>>2]-m>>2>>>0>l>>>0){continue}break}}Io();x()}Ho();x()}if((e|0)>=1){ip(i[h+144>>2],0,e<<2)}d=i[h+144>>2];e=i[d>>2];d=i[d+4>>2];f=i[b+4>>2];i[h+216>>2]=i[b>>2];i[h+220>>2]=f;i[h+208>>2]=e;i[h+212>>2]=d;ze(h+8|0,a+8|0,h+216|0,h+208|0);i[c>>2]=i[h+8>>2];i[c+4>>2]=i[h+12>>2];if(B){bp(B)}a=i[h+128>>2];if(a){i[h+132>>2]=a;bp(a)}a=i[h+180>>2];if(a){i[h+184>>2]=a;bp(a)}a=i[h+168>>2];if(a){i[h+172>>2]=a;bp(a)}a=i[h+156>>2];if(a){i[h+160>>2]=a;bp(a)}a=i[h+144>>2];if(a){i[h+148>>2]=a;bp(a)}F=h+240|0;return 1}function Rb(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,h=0,k=0,n=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=p(0),z=0,A=0,B=0,C=0,D=0;c=F+ -64|0;F=c;n=i[a+28>>2];r=i[n+4>>2];z=i[r+80>>2];q=i[a+4>>2];e=i[a+8>>2];k=(q|0)==(e|0);a:{if(k){break a}f=e-q|0;b=(f|0)>-1?f:-1;s=(b|0)<1?b:1;b=q-e|0;b=o(s,((b|0)>(f|0)?b:f)>>>2|0);e=b>>>0>1?b:1;b=i[r+8>>2];while(1){d=g[i[b+(i[(h<<2)+q>>2]<<2)>>2]+24|0]+d|0;h=h+1|0;if((e|0)!=(h|0)){continue}break}}i[a+72>>2]=d;b:{if(k){h=1;break b}A=a+60|0;B=a+36|0;C=a+48|0;D=c+48|0;while(1){c:{d:{e:{f:{g:{h:{i:{j:{u=i[(w<<2)+q>>2];t=i[i[r+8>>2]+(u<<2)>>2];switch(i[t+28>>2]+ -1|0){case 0:case 2:case 4:break i;case 8:break j;default:break c}}i[c+44>>2]=-1;i[c+40>>2]=1140;b=D;i[b+8>>2]=0;i[b+12>>2]=0;i[b>>2]=0;i[b+4>>2]=0;k=i[n+48>>2];f=ho(32);i[c+24>>2]=f;i[c+28>>2]=17;i[c+32>>2]=-2147483616;g[f+17|0]=0;g[f+16|0]=j[1664];e=j[1660]|j[1661]<<8|(j[1662]<<16|j[1663]<<24);b=j[1656]|j[1657]<<8|(j[1658]<<16|j[1659]<<24);g[f+8|0]=b;g[f+9|0]=b>>>8;g[f+10|0]=b>>>16;g[f+11|0]=b>>>24;g[f+12|0]=e;g[f+13|0]=e>>>8;g[f+14|0]=e>>>16;g[f+15|0]=e>>>24;e=j[1652]|j[1653]<<8|(j[1654]<<16|j[1655]<<24);b=j[1648]|j[1649]<<8|(j[1650]<<16|j[1651]<<24);g[f|0]=b;g[f+1|0]=b>>>8;g[f+2|0]=b>>>16;g[f+3|0]=b>>>24;g[f+4|0]=e;g[f+5|0]=e>>>8;g[f+6|0]=e>>>16;g[f+7|0]=e>>>24;k:{l:{e=k+16|0;d=e;h=i[d>>2];if(!h){break l}while(1){b=i[h+16>>2]<(u|0);d=b?d:h;h=i[(b<<2)+h>>2];if(h){continue}break}if((d|0)==(e|0)|(u|0)>2]){break l}b=d+20|0;if(!Sb(b,c+24|0)){break l}s=$j(b,c+24|0,-1);break k}s=$j(k,c+24|0,-1)}if(g[c+35|0]<=-1){bp(i[c+24>>2])}m:{if((s|0)<1){break m}q=i[i[a+28>>2]+48>>2];d=ho(32);i[c+24>>2]=d;i[c+28>>2]=19;i[c+32>>2]=-2147483616;g[d+19|0]=0;v=j[1681]|j[1682]<<8|(j[1683]<<16|j[1684]<<24);g[d+15|0]=v;g[d+16|0]=v>>>8;g[d+17|0]=v>>>16;g[d+18|0]=v>>>24;e=j[1678]|j[1679]<<8|(j[1680]<<16|j[1681]<<24);r=e;f=j[1674]|j[1675]<<8|(j[1676]<<16|j[1677]<<24);b=f;g[d+8|0]=b;g[d+9|0]=b>>>8;g[d+10|0]=b>>>16;g[d+11|0]=b>>>24;g[d+12|0]=e;g[d+13|0]=e>>>8;g[d+14|0]=e>>>16;g[d+15|0]=e>>>24;n=j[1670]|j[1671]<<8|(j[1672]<<16|j[1673]<<24);k=n;e=j[1666]|j[1667]<<8|(j[1668]<<16|j[1669]<<24);b=e;g[d|0]=b;g[d+1|0]=b>>>8;g[d+2|0]=b>>>16;g[d+3|0]=b>>>24;g[d+4|0]=k;g[d+5|0]=k>>>8;g[d+6|0]=k>>>16;g[d+7|0]=k>>>24;n:{o:{n=q+16|0;d=n;h=i[d>>2];if(!h){break o}while(1){b=i[h+16>>2]<(u|0);d=b?d:h;h=i[(b<<2)+h>>2];if(h){continue}break}if((d|0)==(n|0)|(u|0)>2]){break o}n=Sb(d+20|0,c+24|0);break n}n=Sb(q,c+24|0)}b=0;p:{if(!n){break p}q=i[i[a+28>>2]+48>>2];d=ho(32);i[c+8>>2]=d;i[c+12>>2]=18;i[c+16>>2]=-2147483616;g[d+18|0]=0;b=j[1702]|j[1703]<<8;g[d+16|0]=b;g[d+17|0]=b>>>8;n=j[1698]|j[1699]<<8|(j[1700]<<16|j[1701]<<24);b=j[1694]|j[1695]<<8|(j[1696]<<16|j[1697]<<24);g[d+8|0]=b;g[d+9|0]=b>>>8;g[d+10|0]=b>>>16;g[d+11|0]=b>>>24;g[d+12|0]=n;g[d+13|0]=n>>>8;g[d+14|0]=n>>>16;g[d+15|0]=n>>>24;n=j[1690]|j[1691]<<8|(j[1692]<<16|j[1693]<<24);b=j[1686]|j[1687]<<8|(j[1688]<<16|j[1689]<<24);g[d|0]=b;g[d+1|0]=b>>>8;g[d+2|0]=b>>>16;g[d+3|0]=b>>>24;g[d+4|0]=n;g[d+5|0]=n>>>8;g[d+6|0]=n>>>16;g[d+7|0]=n>>>24;q:{r:{n=q+16|0;d=n;h=i[d>>2];if(!h){break r}while(1){b=i[h+16>>2]<(u|0);d=b?d:h;h=i[(b<<2)+h>>2];if(h){continue}break}if((d|0)==(n|0)|(u|0)>2]){break r}b=Sb(d+20|0,c+8|0);break q}b=Sb(q,c+8|0)}if(g[c+19|0]<=-1){bp(i[c+8>>2])}b=(b|0)!=0}if(g[c+35|0]<=-1){bp(i[c+24>>2])}s:{if(b){b=g[t+24|0];q=0;i[c+32>>2]=0;i[c+24>>2]=0;i[c+28>>2]=0;if(b){if((b|0)<=-1){break h}n=b<<2;q=ho(n);i[c+24>>2]=q;b=n+q|0;i[c+32>>2]=b;ip(q,0,n);i[c+28>>2]=b}n=i[i[a+28>>2]+48>>2];b=ho(32);i[c+8>>2]=b;i[c+12>>2]=19;i[c+16>>2]=-2147483616;g[b+19|0]=0;g[b+15|0]=v;g[b+16|0]=v>>>8;g[b+17|0]=v>>>16;g[b+18|0]=v>>>24;g[b+8|0]=f;g[b+9|0]=f>>>8;g[b+10|0]=f>>>16;g[b+11|0]=f>>>24;g[b+12|0]=r;g[b+13|0]=r>>>8;g[b+14|0]=r>>>16;g[b+15|0]=r>>>24;g[b|0]=e;g[b+1|0]=e>>>8;g[b+2|0]=e>>>16;g[b+3|0]=e>>>24;g[b+4|0]=k;g[b+5|0]=k>>>8;g[b+6|0]=k>>>16;g[b+7|0]=k>>>24;k=g[t+24|0];t:{u:{e=n+16|0;d=e;h=i[d>>2];if(!h){break u}while(1){b=i[h+16>>2]<(u|0);d=b?d:h;h=i[(b<<2)+h>>2];if(h){continue}break}if((d|0)==(e|0)|(u|0)>2]){break u}b=d+20|0;if(!Sb(b,c+8|0)){break u}Tb(b,c+8|0,k,q);break t}Tb(n,c+8|0,k,q)}if(g[c+19|0]<=-1){bp(i[c+8>>2])}k=i[i[a+28>>2]+48>>2];f=ho(32);i[c+8>>2]=f;i[c+12>>2]=18;i[c+16>>2]=-2147483616;g[f+18|0]=0;b=j[1702]|j[1703]<<8;g[f+16|0]=b;g[f+17|0]=b>>>8;e=j[1698]|j[1699]<<8|(j[1700]<<16|j[1701]<<24);b=j[1694]|j[1695]<<8|(j[1696]<<16|j[1697]<<24);g[f+8|0]=b;g[f+9|0]=b>>>8;g[f+10|0]=b>>>16;g[f+11|0]=b>>>24;g[f+12|0]=e;g[f+13|0]=e>>>8;g[f+14|0]=e>>>16;g[f+15|0]=e>>>24;e=j[1690]|j[1691]<<8|(j[1692]<<16|j[1693]<<24);b=j[1686]|j[1687]<<8|(j[1688]<<16|j[1689]<<24);g[f|0]=b;g[f+1|0]=b>>>8;g[f+2|0]=b>>>16;g[f+3|0]=b>>>24;g[f+4|0]=e;g[f+5|0]=e>>>8;g[f+6|0]=e>>>16;g[f+7|0]=e>>>24;v:{w:{e=k+16|0;d=e;h=i[d>>2];if(!h){break w}while(1){b=i[h+16>>2]<(u|0);d=b?d:h;h=i[(b<<2)+h>>2];if(h){continue}break}if((d|0)==(e|0)|(u|0)>2]){break w}b=d+20|0;if(!Sb(b,c+8|0)){break w}y=ak(b,c+8|0);break v}y=ak(k,c+8|0)}if(g[c+19|0]<=-1){bp(i[c+8>>2])}qa(c+40|0,s,i[c+24>>2],g[t+24|0],y);b=i[c+24>>2];if(!b){break s}i[c+28>>2]=b;bp(b);break s}if(!sa(c+40|0,t,s)){break m}}k=i[a+40>>2];x:{if((k|0)!=i[a+44>>2]){i[k>>2]=1140;b=i[c+44>>2];i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;i[k+4>>2]=b;e=i[c+52>>2]-i[c+48>>2]|0;if(e){b=e>>2;if(b>>>0>=1073741824){break g}e=ho(e);i[k+8>>2]=e;i[k+12>>2]=e;i[k+16>>2]=e+(b<<2);b=k;f=i[c+48>>2];r=i[c+52>>2]-f|0;if((r|0)>=1){e=hp(e,f,r)+r|0}i[b+12>>2]=e}m[k+20>>2]=m[c+60>>2];i[a+40>>2]=k+24;break x}Ub(B,c+40|0)}Aa(c+8|0,c+40|0,t,z);i[c+32>>2]=0;i[c+24>>2]=0;i[c+28>>2]=0;ma(c+40|0,t,c+24|0,i[c+8>>2]);b=i[c+24>>2];if(b){i[c+28>>2]=b;bp(b)}e=i[a+64>>2];y:{if(e>>>0>2]){b=i[c+8>>2];i[c+8>>2]=0;i[e>>2]=b;i[a+64>>2]=e+4;break y}Vb(A,c+8|0)}b=i[c+8>>2];i[c+8>>2]=0;if(b){Wb(b)}i[c+40>>2]=1140;b=i[c+48>>2];if(!b){break c}i[c+52>>2]=b;bp(b);break c}i[c+40>>2]=1140;a=i[c+48>>2];if(a){i[c+52>>2]=a;bp(a)}h=0;break b}b=g[t+24|0];q=0;i[c+48>>2]=0;i[c+40>>2]=0;i[c+44>>2]=0;d=0;if(b){if((b|0)<=-1){break f}b=b<<2;h=ho(b);i[c+40>>2]=h;b=b+h|0;i[c+48>>2]=b;while(1){i[h>>2]=2147483647;h=h+4|0;if((b|0)!=(h|0)){continue}break}i[c+44>>2]=b;d=j[t+24|0]}i[c+32>>2]=0;i[c+24>>2]=0;i[c+28>>2]=0;if(d&255){b=d<<24>>24;if((b|0)<=-1){break e}e=b<<2;q=ho(e);i[c+24>>2]=q;b=e+q|0;i[c+32>>2]=b;ip(q,0,e);i[c+28>>2]=b}s=j[t+24|0];n=0;if(!i[t+80>>2]){break d}while(1){Xb(t,n,s<<24>>24,q);b=j[t+24|0];s=b<<24>>24;if((s|0)>=1){r=b>>>0>1?b:1;h=0;f=i[c+24>>2];k=i[c+40>>2];while(1){b=h<<2;e=b+k|0;b=i[b+f>>2];if(i[e>>2]>(b|0)){i[e>>2]=b}h=h+1|0;if((r|0)!=(h|0)){continue}break}}n=n+1|0;if(n>>>0>=l[t+80>>2]){break d}q=i[c+24>>2];continue}}Ho();x()}Ho();x()}Ho();x()}Ho();x()}h=0;z:{if(s<<24>>24<=0){break z}A:{while(1){B:{r=i[c+40>>2]+(h<<2)|0;b=i[a+52>>2];C:{if((b|0)!=i[a+56>>2]){i[b>>2]=i[r>>2];i[a+52>>2]=b+4;break C}n=i[C>>2];s=b-n|0;f=s>>2;k=f+1|0;if(k>>>0>=1073741824){break B}e=s>>1;k=f>>>0<536870911?e>>>0>>0?k:e:1073741823;b=0;D:{if(!k){break D}if(k>>>0>=1073741824){break A}b=ho(k<<2)}e=b+(f<<2)|0;i[e>>2]=i[r>>2];k=b+(k<<2)|0;e=e+4|0;if((s|0)>=1){hp(b,n,s)}i[a+56>>2]=k;i[a+52>>2]=e;i[a+48>>2]=b;if(!n){break C}bp(n)}h=h+1|0;if((h|0)>2];if(b){i[c+28>>2]=b;bp(b)}b=i[c+40>>2];if(!b){break c}i[c+44>>2]=b;bp(b)}h=1;w=w+1|0;q=i[a+4>>2];if(w>>>0>=i[a+8>>2]-q>>2>>>0){break b}n=i[a+28>>2];r=i[n+4>>2];continue}}F=c- -64|0;return h|0}function Te(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var h=0,k=0,l=0,m=0,n=0,p=0,r=0,t=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0;h=F-240|0;F=h;V=i[a+36>>2];N=i[a+32>>2];i[h+184>>2]=0;i[h+188>>2]=0;i[h+176>>2]=0;i[h+180>>2]=0;i[h+168>>2]=0;i[h+172>>2]=0;d=h+160|0;i[d>>2]=0;i[d+4>>2]=0;i[h+152>>2]=0;i[h+156>>2]=0;i[h+144>>2]=0;i[h+148>>2]=0;if(e){Bd(h+144|0,e);m=i[d>>2];d=i[h+156>>2]}else{d=0}f=m-d>>2;a:{if(f>>>0>=e>>>0){if(f>>>0<=e>>>0){break a}i[h+160>>2]=d+(e<<2);break a}Bd(h+144|12,e-f|0)}f=i[h+168>>2];d=i[h+172>>2]-f>>2;b:{if(d>>>0>=e>>>0){if(d>>>0<=e>>>0){break b}i[h+172>>2]=f+(e<<2);break b}Bd(h+168|0,e-d|0)}f=i[h+180>>2];d=i[h+184>>2]-f>>2;c:{if(d>>>0>=e>>>0){if(d>>>0<=e>>>0){break c}i[h+184>>2]=f+(e<<2);break c}Bd(h+180|0,e-d|0)}i[h+136>>2]=0;i[h+128>>2]=0;i[h+132>>2]=0;d:{e:{if(e){if(e>>>0>=1073741824){break e}d=e<<2;f=ho(d);i[h+128>>2]=f;l=d+f|0;i[h+136>>2]=l;ip(f,0,d);i[h+132>>2]=l}J=a+136|0;f=i[a+136>>2];d=i[a+140>>2]-f>>2;f:{if(d>>>0>>0){Bd(J,e-d|0);break f}if(d>>>0<=e>>>0){break f}i[a+140>>2]=f+(e<<2)}i[h+104>>2]=0;i[h+108>>2]=0;i[h+96>>2]=0;i[h+100>>2]=0;i[h+88>>2]=0;i[h+92>>2]=0;i[h+80>>2]=0;i[h+84>>2]=0;i[h+72>>2]=0;i[h+76>>2]=0;d=h- -64|0;i[d>>2]=0;i[d+4>>2]=0;i[h+56>>2]=0;i[h+60>>2]=0;i[h+48>>2]=0;i[h+52>>2]=0;if(e){d=e<<2;B=ho(d);d=ip(B,0,d)+d|0}else{d=0}f=i[a+40>>2];m=i[f>>2];f=i[f+4>>2]-m|0;if((f|0)<5){break d}O=d;I=f>>>2|0;d=I+ -1|0;if(f>>2>>>0>d>>>0){P=e<<2;W=a+8|0;K=a+96|0;X=h+125|0;Q=h+36|0;R=h+24|0;L=h+20|0;Y=L+24|0;S=L+16|0;G=(e|0)<1;while(1){l=0;D=d;d=i[(d<<2)+m>>2];g:{if((d|0)==-1){break g}t=i[N+12>>2];C=d+((d>>>0)%3|0?-1:2)|0;r=t+(C<<2)|0;m=1;n=0;f=d;while(1){l=i[t+(f<<2)>>2];h:{if((l|0)==-1){break h}p=-1;w=i[V>>2];k=i[N>>2];A=i[w+(i[k+(l<<2)>>2]<<2)>>2];y=l+1|0;y=(y>>>0)%3|0?y:l+ -2|0;if((y|0)!=-1){y=i[k+(y<<2)>>2]}else{y=-1}l=l+((l>>>0)%3|0?-1:2)|0;if((l|0)!=-1){p=i[k+(l<<2)>>2]}if((A|0)>=(D|0)){break h}l=i[(y<<2)+w>>2];if((l|0)>=(D|0)){break h}k=i[w+(p<<2)>>2];if((k|0)>=(D|0)){break h}p=i[(h+144|0)+o(n,12)>>2];if(!G){w=o(e,k);l=o(e,l);A=o(e,A);k=0;while(1){i[p+(k<<2)>>2]=(i[(k+w<<2)+b>>2]+i[(k+l<<2)+b>>2]|0)-i[(k+A<<2)+b>>2];k=k+1|0;if((k|0)!=(e|0)){continue}break}}l=4;n=n+1|0;if((n|0)==4){break g}}i:{if(m&1){p=f+1|0;f=(p>>>0)%3|0?p:f+ -2|0;l=-1;if((f|0)==-1){break i}f=i[t+(f<<2)>>2];l=-1;if((f|0)==-1){break i}l=f+1|0;l=(l>>>0)%3|0?l:f+ -2|0;break i}f=((f>>>0)%3|0?-1:2)+f|0;l=-1;if((f|0)==-1){break i}f=i[t+(f<<2)>>2];l=-1;if((f|0)==-1){break i}l=f+ -1|0;if((f>>>0)%3|0){break i}l=f+2|0}if((l|0)==(d|0)){l=n;break g}f=l;l=(l|0)!=-1;k=(l|m^-1)&1;f=k?f:-1;m=m&l;if(!((C|0)==-1|k)){l=i[r>>2];if((l|0)==-1){l=n;break g}m=0;if((l>>>0)%3|0){f=l+ -1|0}else{f=l+2|0}}l=n;if((f|0)!=-1){continue}break}}f=0;g[h+16|0]=0;i[h+8>>2]=0;i[h+12>>2]=0;i[Y>>2]=0;i[S>>2]=0;i[S+4>>2]=0;d=L;i[d+8>>2]=0;i[d+12>>2]=0;i[d>>2]=0;i[d+4>>2]=0;T=o(e,D)<<2;y=T+b|0;p=(o(I+ -2|0,e)<<2)+b|0;m=i[a+136>>2];d=0;if(!G){while(1){k=d<<2;n=i[k+p>>2]-i[k+y>>2]|0;i[k+B>>2]=n;i[k+m>>2]=(n|0)>-1?n<<1:(n^-1)<<1|1;k=f;f=n>>31;f=k+(f+n^f)|0;d=d+1|0;if((e|0)!=(d|0)){continue}break}}dg(h+216|0,K,m,e);d=gg(h+216|0);k=H;m=hg(h+216|0);n=m+d|0;d=H+k|0;d=n>>>0>>0?d+1|0:d;k=n;M=(l|0)<1;if(!M){t=(l<<3)+ -8|0;m=t+(h+48|0)|0;w=m;r=m;n=i[m+4>>2];A=l+i[m>>2]|0;if(A>>>0>>0){n=n+1|0}m=A;i[r>>2]=m;i[w+4>>2]=n;z=v(bg(m,i[t+(h+80|0)>>2])*(+(m>>>0)+ +(n|0)*4294967296));j:{if(q(z)<0x8000000000000000){m=q(z)>=1?z>0?~~s(u(z*2.3283064365386963e-10),4294967295)>>>0:~~v((z- +(~~z>>>0>>>0))*2.3283064365386963e-10)>>>0:0;n=~~z>>>0;break j}m=-2147483648;n=0}d=d+m|0;n=k+n|0;if(n>>>0>>0){d=d+1|0}k=n}i[h+20>>2]=0;g[h+16|0]=0;i[h+8>>2]=k;i[h+12>>2]=f;ra(R,p,p+P|0);Hd(Q,B,O);if(!M){w=(h+124|0)+l|0;A=w+ -2|0;t=w+ -1|0;d=(l<<3)+ -8|0;U=d+(h+48|0)|0;Z=d+(h+80|0)|0;n=1;C=0;while(1){ip(h+124|0,1,l);C=C+1|0;ip(h+124|0,0,C);k:{l:while(1){if(!G){ip(i[h+128>>2],0,P)}f=0;d=i[h+128>>2];m=0;while(1){if(!j[(h+124|0)+f|0]){if(!G){p=i[(h+144|0)+o(f,12)>>2];k=0;while(1){r=k<<2;E=r+d|0;i[E>>2]=i[E>>2]+i[p+r>>2];k=k+1|0;if((k|0)!=(e|0)){continue}break}}m=1<>2];while(1){f=d+(k<<2)|0;i[f>>2]=i[f>>2]/(n|0);k=k+1|0;if((k|0)!=(e|0)){continue}break}if((e|0)>0){break n}}p=i[a+136>>2];f=0;break m}E=i[h+128>>2];p=i[a+136>>2];f=0;d=0;while(1){r=d<<2;k=i[r+E>>2]-i[r+y>>2]|0;i[r+B>>2]=k;i[p+r>>2]=(k|0)>-1?k<<1:(k^-1)<<1|1;r=f;f=k>>31;f=r+(f+k^f)|0;d=d+1|0;if((e|0)!=(d|0)){continue}break}}dg(h+216|0,K,p,e);k=gg(h+216|0)+hg(h+216|0)|0;if((l|0)>0){d=i[U+4>>2];p=i[U>>2];z=v(bg(p,i[Z>>2]+n|0)*(+(p>>>0)+ +(d|0)*4294967296));o:{if(q(z)<0x8000000000000000){d=~~z>>>0;break o}d=0}k=d+k|0}d=i[h+8>>2];if(!((k|0)>(d|0)|(f|0)>=i[h+12>>2]?(k|0)>=(d|0):0)){i[h+20>>2]=n;g[h+16|0]=m;i[h+8>>2]=k;i[h+12>>2]=f;Hd(R,i[h+128>>2],i[h+132>>2]);Hd(Q,B,O)}if((t|0)==(h+124|0)){break k}m=-1;f=j[t|0];k=f;while(1){p=k&255;d=m+ -1|0;r=d+w|0;k=j[r|0];if(k>>>0

>>0){p=t;d=w;if(k>>>0>=(f&255)>>>0){while(1){f=p;p=f+ -1|0;E=d+ -2|0;d=f;if(k>>>0>=j[E|0]){continue}break}f=1}d=m+w|0;g[r|0]=f;g[p|0]=k;k=t;if((m|0)==-1){continue l}while(1){f=j[d|0];g[d|0]=j[k|0];g[k|0]=f;d=d+1|0;k=k+ -1|0;if(d>>>0>>0){continue}break}continue l}m=d;if((r|0)!=(h+124|0)){continue}break}break}if(t>>>0<=h+124>>>0){break k}d=j[h+124|0];g[h+124|0]=f;g[t|0]=d;m=X;k=A;if(m>>>0>=k>>>0){break k}while(1){d=j[m|0];g[m|0]=j[k|0];g[k|0]=d;m=m+1|0;k=k+ -1|0;if(m>>>0>>0){continue}break}}n=n+1|0;if((l|0)!=(C|0)){continue}break}}if((l|0)>0){d=((l<<3)+h|0)+72|0;n=d;k=i[h+20>>2];f=k+i[d>>2]|0;d=i[d+4>>2]+(k>>31)|0;i[n>>2]=f;i[n+4>>2]=f>>>0>>0?d+1|0:d}p:{if((e|0)<=0){d=i[J>>2];break p}d=i[J>>2];k=0;n=i[h+36>>2];while(1){m=k<<2;f=i[m+n>>2];i[d+m>>2]=(f|0)>-1?f<<1:(f^-1)<<1|1;k=k+1|0;if((k|0)!=(e|0)){continue}break}}fg(h+216|0,K,d,e);q:{if(M){break q}d=o(l,12)+a|0;t=d+40|0;n=d+36|0;d=d+32|0;m=0;while(1){r:{p=i[t>>2];f=p<<5;k=i[n>>2];s:{if(!(j[h+16|0]>>>m&1)){if((f|0)==(k|0)){if((k+1|0)<=-1){break r}f=d;if(k>>>0<=1073741822){k=k+32&-32;p=p<<6;k=p>>>0>>0?k:p}else{k=2147483647}bd(f,k);k=i[n>>2]}i[n>>2]=k+1;f=i[d>>2]+(k>>>3&536870908)|0;i[f>>2]=i[f>>2]|1<>>0<=1073741822){k=k+32&-32;p=p<<6;k=p>>>0>>0?k:p}else{k=2147483647}bd(f,k);k=i[n>>2]}i[n>>2]=k+1;f=i[d>>2]+(k>>>3&536870908)|0;p=i[f>>2];_=f,$=yp(-2,k)&p,i[_>>2]=$}m=m+1|0;if((l|0)!=(m|0)){continue}break q}break}Ho();x()}d=i[h+24>>2];f=i[d>>2];d=i[d+4>>2];l=i[y+4>>2];i[h+208>>2]=i[y>>2];i[h+212>>2]=l;i[h+200>>2]=f;i[h+204>>2]=d;ze(h+216|0,W,h+208|0,h+200|0);d=c+T|0;i[d>>2]=i[h+216>>2];i[d+4>>2]=i[h+220>>2];d=i[h+36>>2];if(d){i[h+40>>2]=d;bp(d)}d=i[h+24>>2];if(d){i[h+28>>2]=d;bp(d)}if((I|0)<3){break d}I=D;d=i[a+40>>2];m=i[d>>2];f=i[d+4>>2]-m>>2;d=D+ -1|0;if(f>>>0>d>>>0){continue}break}}Io();x()}Ho();x()}if((e|0)>=1){ip(i[h+144>>2],0,e<<2)}d=i[h+144>>2];e=i[d>>2];d=i[d+4>>2];f=i[b+4>>2];i[h+216>>2]=i[b>>2];i[h+220>>2]=f;i[h+208>>2]=e;i[h+212>>2]=d;ze(h+8|0,a+8|0,h+216|0,h+208|0);i[c>>2]=i[h+8>>2];i[c+4>>2]=i[h+12>>2];if(B){bp(B)}a=i[h+128>>2];if(a){i[h+132>>2]=a;bp(a)}a=i[h+180>>2];if(a){i[h+184>>2]=a;bp(a)}a=i[h+168>>2];if(a){i[h+172>>2]=a;bp(a)}a=i[h+156>>2];if(a){i[h+160>>2]=a;bp(a)}a=i[h+144>>2];if(a){i[h+148>>2]=a;bp(a)}F=h+240|0;return 1}function yj(a,b,c,d){var e=0,f=0,h=0,k=0;f=F-32|0;F=f;i[b+44>>2]=d;i[b+48>>2]=c;d=i[b+12>>2];e=i[b+8>>2];if((d|0)!=(e|0)){while(1){d=d+ -4|0;h=i[d>>2];i[d>>2]=0;if(h){I[i[i[h>>2]+4>>2]](h)}if((d|0)!=(e|0)){continue}break}}i[b+12>>2]=e;i[b+24>>2]=i[b+20>>2];i[b+36>>2]=i[b+32>>2];a:{if(!i[b+4>>2]){b=ho(32);i[f+8>>2]=b;i[f+12>>2]=23;i[f+16>>2]=-2147483616;g[b+23|0]=0;c=j[13555]|j[13556]<<8|(j[13557]<<16|j[13558]<<24);d=j[13551]|j[13552]<<8|(j[13553]<<16|j[13554]<<24);g[b+15|0]=d;g[b+16|0]=d>>>8;g[b+17|0]=d>>>16;g[b+18|0]=d>>>24;g[b+19|0]=c;g[b+20|0]=c>>>8;g[b+21|0]=c>>>16;g[b+22|0]=c>>>24;c=j[13548]|j[13549]<<8|(j[13550]<<16|j[13551]<<24);d=j[13544]|j[13545]<<8|(j[13546]<<16|j[13547]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[13540]|j[13541]<<8|(j[13542]<<16|j[13543]<<24);d=j[13536]|j[13537]<<8|(j[13538]<<16|j[13539]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;i[a>>2]=-1;ro(a+4|0,f+8|0);if(g[f+19|0]>-1){break a}bp(i[f+8>>2]);break a}zj(a,b);if(i[a>>2]){break a}h=a+4|0;if(g[a+15|0]<=-1){bp(i[h>>2])}d=i[i[b+4>>2]+4>>2];b:{if(!d){i[a>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0;break b}if(!im(f+24|0,i[b+44>>2],d)){d=ho(32);i[f+8>>2]=d;i[f+12>>2]=26;i[f+16>>2]=-2147483616;g[d+26|0]=0;e=j[13718]|j[13719]<<8;g[d+24|0]=e;g[d+25|0]=e>>>8;e=j[13714]|j[13715]<<8|(j[13716]<<16|j[13717]<<24);k=j[13710]|j[13711]<<8|(j[13712]<<16|j[13713]<<24);g[d+16|0]=k;g[d+17|0]=k>>>8;g[d+18|0]=k>>>16;g[d+19|0]=k>>>24;g[d+20|0]=e;g[d+21|0]=e>>>8;g[d+22|0]=e>>>16;g[d+23|0]=e>>>24;e=j[13706]|j[13707]<<8|(j[13708]<<16|j[13709]<<24);k=j[13702]|j[13703]<<8|(j[13704]<<16|j[13705]<<24);g[d+8|0]=k;g[d+9|0]=k>>>8;g[d+10|0]=k>>>16;g[d+11|0]=k>>>24;g[d+12|0]=e;g[d+13|0]=e>>>8;g[d+14|0]=e>>>16;g[d+15|0]=e>>>24;e=j[13698]|j[13699]<<8|(j[13700]<<16|j[13701]<<24);k=j[13694]|j[13695]<<8|(j[13696]<<16|j[13697]<<24);g[d|0]=k;g[d+1|0]=k>>>8;g[d+2|0]=k>>>16;g[d+3|0]=k>>>24;g[d+4|0]=e;g[d+5|0]=e>>>8;g[d+6|0]=e>>>16;g[d+7|0]=e>>>24;i[a>>2]=-1;ro(h,f+8|0);if(g[f+19|0]<=-1){bp(i[f+8>>2])}if(i[a>>2]){break a}break b}i[a>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0}if(g[h+11|0]<=-1){bp(i[h>>2])}if(!(I[i[i[b>>2]+16>>2]](b)|0)){b=ho(32);i[f+8>>2]=b;i[f+12>>2]=29;i[f+16>>2]=-2147483616;g[b+29|0]=0;c=j[13585]|j[13586]<<8|(j[13587]<<16|j[13588]<<24);d=j[13581]|j[13582]<<8|(j[13583]<<16|j[13584]<<24);g[b+21|0]=d;g[b+22|0]=d>>>8;g[b+23|0]=d>>>16;g[b+24|0]=d>>>24;g[b+25|0]=c;g[b+26|0]=c>>>8;g[b+27|0]=c>>>16;g[b+28|0]=c>>>24;c=j[13580]|j[13581]<<8|(j[13582]<<16|j[13583]<<24);d=j[13576]|j[13577]<<8|(j[13578]<<16|j[13579]<<24);g[b+16|0]=d;g[b+17|0]=d>>>8;g[b+18|0]=d>>>16;g[b+19|0]=d>>>24;g[b+20|0]=c;g[b+21|0]=c>>>8;g[b+22|0]=c>>>16;g[b+23|0]=c>>>24;c=j[13572]|j[13573]<<8|(j[13574]<<16|j[13575]<<24);d=j[13568]|j[13569]<<8|(j[13570]<<16|j[13571]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[13564]|j[13565]<<8|(j[13566]<<16|j[13567]<<24);d=j[13560]|j[13561]<<8|(j[13562]<<16|j[13563]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;i[a>>2]=-1;ro(h,f+8|0);if(g[f+19|0]>-1){break a}bp(i[f+8>>2]);break a}if(!(I[i[i[b>>2]+20>>2]](b)|0)){b=ho(32);i[f+8>>2]=b;i[f+12>>2]=31;i[f+16>>2]=-2147483616;g[b+31|0]=0;c=j[13617]|j[13618]<<8|(j[13619]<<16|j[13620]<<24);d=j[13613]|j[13614]<<8|(j[13615]<<16|j[13616]<<24);g[b+23|0]=d;g[b+24|0]=d>>>8;g[b+25|0]=d>>>16;g[b+26|0]=d>>>24;g[b+27|0]=c;g[b+28|0]=c>>>8;g[b+29|0]=c>>>16;g[b+30|0]=c>>>24;c=j[13610]|j[13611]<<8|(j[13612]<<16|j[13613]<<24);d=j[13606]|j[13607]<<8|(j[13608]<<16|j[13609]<<24);g[b+16|0]=d;g[b+17|0]=d>>>8;g[b+18|0]=d>>>16;g[b+19|0]=d>>>24;g[b+20|0]=c;g[b+21|0]=c>>>8;g[b+22|0]=c>>>16;g[b+23|0]=c>>>24;c=j[13602]|j[13603]<<8|(j[13604]<<16|j[13605]<<24);d=j[13598]|j[13599]<<8|(j[13600]<<16|j[13601]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[13594]|j[13595]<<8|(j[13596]<<16|j[13597]<<24);d=j[13590]|j[13591]<<8|(j[13592]<<16|j[13593]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;i[a>>2]=-1;ro(h,f+8|0);if(g[f+19|0]>-1){break a}bp(i[f+8>>2]);break a}I[i[i[b>>2]+24>>2]](a,b);if(i[a>>2]){break a}if(g[h+11|0]<=-1){bp(i[h>>2])}if(!(I[i[i[b>>2]+28>>2]](b)|0)){b=ho(48);i[f+8>>2]=b;i[f+12>>2]=34;i[f+16>>2]=-2147483600;g[b+34|0]=0;c=j[13654]|j[13655]<<8;g[b+32|0]=c;g[b+33|0]=c>>>8;c=j[13650]|j[13651]<<8|(j[13652]<<16|j[13653]<<24);d=j[13646]|j[13647]<<8|(j[13648]<<16|j[13649]<<24);g[b+24|0]=d;g[b+25|0]=d>>>8;g[b+26|0]=d>>>16;g[b+27|0]=d>>>24;g[b+28|0]=c;g[b+29|0]=c>>>8;g[b+30|0]=c>>>16;g[b+31|0]=c>>>24;c=j[13642]|j[13643]<<8|(j[13644]<<16|j[13645]<<24);d=j[13638]|j[13639]<<8|(j[13640]<<16|j[13641]<<24);g[b+16|0]=d;g[b+17|0]=d>>>8;g[b+18|0]=d>>>16;g[b+19|0]=d>>>24;g[b+20|0]=c;g[b+21|0]=c>>>8;g[b+22|0]=c>>>16;g[b+23|0]=c>>>24;c=j[13634]|j[13635]<<8|(j[13636]<<16|j[13637]<<24);d=j[13630]|j[13631]<<8|(j[13632]<<16|j[13633]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[13626]|j[13627]<<8|(j[13628]<<16|j[13629]<<24);d=j[13622]|j[13623]<<8|(j[13624]<<16|j[13625]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;i[a>>2]=-1;ro(h,f+8|0);if(g[f+19|0]>-1){break a}bp(i[f+8>>2]);break a}d=ho(32);i[f+8>>2]=d;i[f+12>>2]=30;i[f+16>>2]=-2147483616;g[d+30|0]=0;h=j[13683]|j[13684]<<8|(j[13685]<<16|j[13686]<<24);e=j[13679]|j[13680]<<8|(j[13681]<<16|j[13682]<<24);g[d+22|0]=e;g[d+23|0]=e>>>8;g[d+24|0]=e>>>16;g[d+25|0]=e>>>24;g[d+26|0]=h;g[d+27|0]=h>>>8;g[d+28|0]=h>>>16;g[d+29|0]=h>>>24;h=j[13677]|j[13678]<<8|(j[13679]<<16|j[13680]<<24);e=j[13673]|j[13674]<<8|(j[13675]<<16|j[13676]<<24);g[d+16|0]=e;g[d+17|0]=e>>>8;g[d+18|0]=e>>>16;g[d+19|0]=e>>>24;g[d+20|0]=h;g[d+21|0]=h>>>8;g[d+22|0]=h>>>16;g[d+23|0]=h>>>24;h=j[13669]|j[13670]<<8|(j[13671]<<16|j[13672]<<24);e=j[13665]|j[13666]<<8|(j[13667]<<16|j[13668]<<24);g[d+8|0]=e;g[d+9|0]=e>>>8;g[d+10|0]=e>>>16;g[d+11|0]=e>>>24;g[d+12|0]=h;g[d+13|0]=h>>>8;g[d+14|0]=h>>>16;g[d+15|0]=h>>>24;h=j[13661]|j[13662]<<8|(j[13663]<<16|j[13664]<<24);e=j[13657]|j[13658]<<8|(j[13659]<<16|j[13660]<<24);g[d|0]=e;g[d+1|0]=e>>>8;g[d+2|0]=e>>>16;g[d+3|0]=e>>>24;g[d+4|0]=h;g[d+5|0]=h>>>8;g[d+6|0]=h>>>16;g[d+7|0]=h>>>24;c=ck(c,f+8|0,0);if(g[f+19|0]<=-1){bp(i[f+8>>2])}if(c){I[i[i[b>>2]+48>>2]](b)}i[a>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0}F=f+32|0}function _m(a,b,c,d,e,f){a=a|0;b=+b;c=c|0;d=d|0;e=e|0;f=f|0;var h=0,k=0,l=0,m=0,n=0,p=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0;p=F-560|0;F=p;i[p+44>>2]=0;Ep(+b);h=Bp(1)|0;Bp(0)|0;a:{if((h|0)<-1?1:(h|0)<=-1){B=1;b=-b;Ep(+b);h=Bp(1)|0;Bp(0)|0;w=17504;break a}B=1;w=17507;if(e&2048){break a}w=17510;if(e&1){break a}B=0;E=1;w=17505}b:{if((h&2146435072)==2146435072){n=B+3|0;Zm(a,32,c,n,e&-65537);Tm(a,w,B);d=f&32;Tm(a,b!=b?d?17531:17535:d?17523:17527,3);break b}x=p+16|0;c:{d:{e:{b=Qm(b,p+44|0);b=b+b;if(b!=0){h=i[p+44>>2];i[p+44>>2]=h+ -1;y=f|32;if((y|0)!=97){break e}break c}y=f|32;if((y|0)==97){break c}n=i[p+44>>2];r=(d|0)<0?6:d;break d}n=h+ -29|0;i[p+44>>2]=n;b=b*268435456;r=(d|0)<0?6:d}t=(n|0)<0?p+48|0:p+336|0;m=t;while(1){d=m;if(b<4294967296&b>=0){h=~~b>>>0}else{h=0}i[d>>2]=h;m=m+4|0;b=(b- +(h>>>0))*1e9;if(b!=0){continue}break}f:{if((n|0)<1){d=n;h=m;k=t;break f}k=t;d=n;while(1){s=(d|0)<29?d:29;h=m+ -4|0;g:{if(h>>>0>>0){break g}d=s;l=0;while(1){u=h;C=l;D=i[h>>2];v=d&31;if(32<=(d&63)>>>0){l=D<>>32-v;D=D<>>0>>0?l+1|0:l;l=wp(v,l,1e9,0);C=u;u=up(l,H,1e9,0);i[C>>2]=v-u;h=h+ -4|0;if(h>>>0>=k>>>0){continue}break}if(!l){break g}k=k+ -4|0;i[k>>2]=l}while(1){h=m;if(h>>>0>k>>>0){m=h+ -4|0;if(!i[m>>2]){continue}}break}d=i[p+44>>2]-s|0;i[p+44>>2]=d;m=h;if((d|0)>0){continue}break}}if((d|0)<=-1){z=((r+25|0)/9|0)+1|0;s=(y|0)==102;while(1){l=(d|0)<-9?9:0-d|0;h:{if(k>>>0>=h>>>0){k=i[k>>2]?k:k+4|0;break h}u=1e9>>>l|0;v=-1<>2];i[m>>2]=C+(d>>>l|0);d=o(u,d&v);m=m+4|0;if(m>>>0>>0){continue}break}k=i[k>>2]?k:k+4|0;if(!d){break h}i[h>>2]=d;h=h+4|0}d=l+i[p+44>>2]|0;i[p+44>>2]=d;m=s?t:k;h=h-m>>2>(z|0)?m+(z<<2)|0:h;if((d|0)<0){continue}break}}m=0;i:{if(k>>>0>=h>>>0){break i}m=o(t-k>>2,9);d=10;l=i[k>>2];if(l>>>0<10){break i}while(1){m=m+1|0;d=o(d,10);if(l>>>0>=d>>>0){continue}break}}d=(r-((y|0)==102?0:m)|0)-((y|0)==103&(r|0)!=0)|0;if((d|0)<(o(h-t>>2,9)+ -9|0)){l=d+9216|0;s=(l|0)/9|0;n=((s<<2)+((n|0)<0?p+48|4:p+340|0)|0)+ -4096|0;d=10;l=l-o(s,9)|0;if((l|0)<=7){while(1){d=o(d,10);l=l+1|0;if((l|0)!=8){continue}break}}s=i[n>>2];u=(s>>>0)/(d>>>0)|0;z=n+4|0;l=s-o(d,u)|0;j:{if(l?0:(z|0)==(h|0)){break j}v=d>>>1|0;A=l>>>0>>0?.5:(h|0)==(z|0)?(v|0)==(l|0)?1:1.5:1.5;b=u&1?9007199254740994:9007199254740992;if(!(j[w|0]!=45|E)){A=-A;b=-b}l=s-l|0;i[n>>2]=l;if(b+A==b){break j}d=d+l|0;i[n>>2]=d;if(d>>>0>=1e9){while(1){i[n>>2]=0;n=n+ -4|0;if(n>>>0>>0){k=k+ -4|0;i[k>>2]=0}d=i[n>>2]+1|0;i[n>>2]=d;if(d>>>0>999999999){continue}break}}m=o(t-k>>2,9);d=10;l=i[k>>2];if(l>>>0<10){break j}while(1){m=m+1|0;d=o(d,10);if(l>>>0>=d>>>0){continue}break}}d=n+4|0;h=h>>>0>d>>>0?d:h}while(1){d=h;n=h>>>0<=k>>>0;if(!n){h=d+ -4|0;if(!i[h>>2]){continue}}break}k:{if((y|0)!=103){s=e&8;break k}h=r?r:1;l=(h|0)>(m|0)&(m|0)>-5;r=(l?m^-1:-1)+h|0;f=(l?-1:-2)+f|0;s=e&8;if(s){break k}h=-9;l:{if(n){break l}s=i[d+ -4>>2];if(!s){break l}l=10;h=0;if((s>>>0)%10|0){break l}while(1){n=h;h=h+1|0;l=o(l,10);if(!((s>>>0)%(l>>>0)|0)){continue}break}h=n^-1}n=o(d-t>>2,9);if((f&-33)==70){s=0;h=(h+n|0)+ -9|0;h=(h|0)>0?h:0;r=(r|0)<(h|0)?r:h;break k}s=0;h=((m+n|0)+h|0)+ -9|0;h=(h|0)>0?h:0;r=(r|0)<(h|0)?r:h}u=r|s;v=(u|0)!=0;C=a;l=c;y=f&-33;h=(m|0)>0?m:0;m:{if((y|0)==70){break m}h=m>>31;h=Ym(h+m^h,0,x);if((x-h|0)<=1){while(1){h=h+ -1|0;g[h|0]=48;if((x-h|0)<2){continue}break}}z=h+ -2|0;g[z|0]=f;g[h+ -1|0]=(m|0)<0?45:43;h=x-z|0}n=(h+(v+(r+B|0)|0)|0)+1|0;Zm(C,32,l,n,e);Tm(a,w,B);Zm(a,48,c,n,e^65536);n:{o:{p:{if((y|0)==70){f=p+16|8;m=p+16|9;l=k>>>0>t>>>0?t:k;k=l;while(1){h=Ym(i[k>>2],0,m);q:{if((k|0)!=(l|0)){if(h>>>0<=p+16>>>0){break q}while(1){h=h+ -1|0;g[h|0]=48;if(h>>>0>p+16>>>0){continue}break}break q}if((h|0)!=(m|0)){break q}g[p+24|0]=48;h=f}Tm(a,h,m-h|0);k=k+4|0;if(k>>>0<=t>>>0){continue}break}if(u){Tm(a,17539,1)}if((r|0)<1|k>>>0>=d>>>0){break p}while(1){h=Ym(i[k>>2],0,m);if(h>>>0>p+16>>>0){while(1){h=h+ -1|0;g[h|0]=48;if(h>>>0>p+16>>>0){continue}break}}Tm(a,h,(r|0)<9?r:9);h=r+ -9|0;k=k+4|0;if(k>>>0>=d>>>0){break o}f=(r|0)>9;r=h;if(f){continue}break}break o}r:{if((r|0)<0){break r}t=d>>>0>k>>>0?d:k+4|0;d=p+16|8;f=p+16|9;m=k;while(1){h=Ym(i[m>>2],0,f);if((f|0)==(h|0)){g[p+24|0]=48;h=d}s:{if((k|0)!=(m|0)){if(h>>>0<=p+16>>>0){break s}while(1){h=h+ -1|0;g[h|0]=48;if(h>>>0>p+16>>>0){continue}break}break s}Tm(a,h,1);h=h+1|0;if((r|0)<1?!s:0){break s}Tm(a,17539,1)}u=h;h=f-h|0;Tm(a,u,(r|0)>(h|0)?h:r);r=r-h|0;m=m+4|0;if(m>>>0>=t>>>0){break r}if((r|0)>-1){continue}break}}Zm(a,48,r+18|0,18,0);Tm(a,z,x-z|0);break n}h=r}Zm(a,48,h+9|0,9,0)}break b}r=f&32;l=r?w+9|0:w;t:{if(d>>>0>11){break t}h=12-d|0;if(!h){break t}A=8;while(1){A=A*16;h=h+ -1|0;if(h){continue}break}if(j[l|0]==45){b=-(A+(-b-A));break t}b=b+A-A}m=i[p+44>>2];h=m>>31;h=Ym(h^h+m,0,x);if((x|0)==(h|0)){g[p+15|0]=48;h=p+15|0}m=B|2;k=i[p+44>>2];t=h+ -2|0;g[t|0]=f+15;g[h+ -1|0]=(k|0)<0?45:43;n=e&8;k=p+16|0;while(1){f=k;u=k;k=r;if(q(b)<2147483648){h=~~b}else{h=-2147483648}g[u|0]=k|j[h+17488|0];b=(b- +(h|0))*16;k=f+1|0;if(!((k-(p+16|0)|0)!=1|(b==0?!((d|0)>0|n):0))){g[f+1|0]=46;k=f+2|0}if(b!=0){continue}break}h=a;f=c;if(!d|((k-p|0)+ -18|0)>=(d|0)){r=((x-(p+16|0)|0)-t|0)+k|0}else{r=((d+x|0)-t|0)+2|0}d=r;n=d+m|0;Zm(h,32,f,n,e);Tm(a,l,m);Zm(a,48,c,n,e^65536);f=k-(p+16|0)|0;Tm(a,p+16|0,f);d=x-t|0;Zm(a,48,r-(d+f|0)|0,0,0);Tm(a,t,d)}Zm(a,32,c,n,e^8192);F=p+560|0;return((n|0)<(c|0)?c:n)|0}function Zb(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,k=0,m=0,n=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,y=0;c=F-1696|0;F=c;d=10-_b(i[i[a+28>>2]+48>>2])|0;d=(d|0)<6?d:6;g[c+1695|0]=d;if(!((d&255)!=6|i[a+72>>2]<16)){g[c+1695|0]=5}d=i[b+20>>2];if((d|0)<0?1:(d|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],c+1695|0,c+1696|0)}f=i[a+28>>2];m=i[i[f+4>>2]+80>>2];e=i[a+72>>2];i[c+1684>>2]=0;i[c+1676>>2]=0;i[c+1680>>2]=0;i[c+1668>>2]=e;i[c+1664>>2]=m;i[c+1672>>2]=e<<2;a:{h=o(e,m);if(h){if(h>>>0>=1073741824){break a}d=h<<2;k=ho(d);i[c+1676>>2]=k;n=d+k|0;i[c+1684>>2]=n;ip(k,0,d);i[c+1680>>2]=n}i[c+1688>>2]=k;b:{d=i[a+4>>2];c:{if((d|0)!=i[a+8>>2]){while(1){f=i[i[i[f+4>>2]+8>>2]+(i[(s<<2)+d>>2]<<2)>>2];d=i[f+28>>2];if(d+ -1>>>0>=6){if((d|0)!=9){break c}f=i[i[a+60>>2]+(v<<2)>>2];if(!f){break c}v=v+1|0;d=i[f+28>>2]}d:{e:{switch(d+ -1|0){case 5:if(!m){break d}d=0;while(1){e=d;h=(i[c+1688>>2]+(p<<2)|0)+(o(i[c+1668>>2],d)<<2)|0;k=i[i[f>>2]>>2];n=i[f+48>>2];t=i[f+40>>2];u=i[f+44>>2];if(!j[f+84|0]){e=i[i[f+68>>2]+(d<<2)>>2]}q=h;h=up(t,u,e,0)+n|0;hp(q,h+k|0,g[f+24|0]<<2);d=d+1|0;if((m|0)!=(d|0)){continue}break}break d;case 0:case 2:case 4:e=g[f+24|0];d=0;i[c+464>>2]=0;i[c+456>>2]=0;i[c+460>>2]=0;h=0;f:{if(e){if((e|0)<=-1){break f}e=e<<2;h=ho(e);i[c+456>>2]=h;k=e+h|0;i[c+464>>2]=k;ip(h,0,e);i[c+460>>2]=k;h=j[f+24|0]}i[c+1656>>2]=0;i[c+1648>>2]=0;i[c+1652>>2]=0;g:{if(h&255){d=h<<24>>24;if((d|0)<=-1){break g}e=d<<2;d=ho(e);i[c+1648>>2]=d;h=d+e|0;i[c+1656>>2]=h;ip(d,0,e);i[c+1652>>2]=h}if(m){e=0;while(1){d=e;h=f;if(!j[f+84|0]){d=i[i[f+68>>2]+(e<<2)>>2]}Xb(h,d,g[f+24|0],i[c+456>>2]);k=g[f+24|0];h:{if((k|0)<=0){h=i[c+1648>>2];break h}k=k&255;n=k>>>0>1?k:1;t=i[a+48>>2];d=0;h=i[c+1648>>2];u=i[c+456>>2];while(1){q=d<<2;i[q+h>>2]=i[u+q>>2]-i[t+(d+w<<2)>>2];d=d+1|0;if((n|0)!=(d|0)){continue}break}}hp((i[c+1688>>2]+(p<<2)|0)+(o(i[c+1668>>2],e)<<2)|0,h,k<<2);e=e+1|0;if((e|0)!=(m|0)){continue}break}d=i[c+1648>>2]}e=g[f+24|0];if(d){i[c+1652>>2]=d;bp(d)}d=i[c+456>>2];if(d){i[c+460>>2]=d;bp(d)}w=e+w|0;break d}Ho();x()}Ho();x();default:break e}}e=g[f+24|0];d=0;i[c+464>>2]=0;i[c+456>>2]=0;i[c+460>>2]=0;if(e){if((e|0)<=-1){break b}e=e<<2;d=ho(e);i[c+456>>2]=d;h=d+e|0;i[c+464>>2]=h;ip(d,0,e);i[c+460>>2]=h}if(m){d=0;while(1){e=d;h=f;if(!j[f+84|0]){e=i[i[f+68>>2]+(d<<2)>>2]}$b(h,e,g[f+24|0],i[c+456>>2]);hp((i[c+1688>>2]+(p<<2)|0)+(o(i[c+1668>>2],d)<<2)|0,i[c+456>>2],g[f+24|0]<<2);d=d+1|0;if((m|0)!=(d|0)){continue}break}d=i[c+456>>2]}if(!d){break d}i[c+460>>2]=d;bp(d)}s=s+1|0;d=i[a+4>>2];if(s>>>0>2]-d>>2>>>0){p=g[f+24|0]+p|0;f=i[a+28>>2];continue}break}k=i[c+1688>>2];e=i[a+72>>2];h=o(m,e)}f=0;if((h|0)>0){d=0;while(1){a=i[(d<<2)+k>>2];if(a){a=r(a);f=(a^31)<(f|0)?f:32-a|0}d=d+1|0;if((h|0)!=(d|0)){continue}break}}i:{j:{switch(j[c+1695|0]){case 6:d=cc(c+456|0,e);a=i[c+1668>>2];i[c+448>>2]=a;i[c+432>>2]=a;i[c+32>>2]=a;i[c+16>>2]=a;i[c+440>>2]=0;i[c+424>>2]=i[c+1664>>2];i[c+444>>2]=c+1664;a=i[c+444>>2];i[c+24>>2]=i[c+440>>2];i[c+28>>2]=a;i[c+428>>2]=c+1664;i[c+1648>>2]=f;a=i[c+428>>2];i[c+8>>2]=i[c+424>>2];i[c+12>>2]=a;a=ac(d,c+24|0,c+8|0,c+1648|0,b);bc(d);if(a){break i}break c;case 5:d=cc(c+456|0,e);a=i[c+1668>>2];i[c+416>>2]=a;i[c+400>>2]=a;i[c- -64>>2]=a;i[c+48>>2]=a;i[c+408>>2]=0;i[c+392>>2]=i[c+1664>>2];i[c+412>>2]=c+1664;a=i[c+412>>2];i[c+56>>2]=i[c+408>>2];i[c+60>>2]=a;i[c+396>>2]=c+1664;i[c+1648>>2]=f;a=i[c+396>>2];i[c+40>>2]=i[c+392>>2];i[c+44>>2]=a;a=dc(d,c+56|0,c+40|0,c+1648|0,b);bc(d);if(a){break i}break c;case 4:d=cc(c+456|0,e);a=i[c+1668>>2];i[c+384>>2]=a;i[c+368>>2]=a;i[c+96>>2]=a;i[c+80>>2]=a;i[c+376>>2]=0;i[c+360>>2]=i[c+1664>>2];i[c+380>>2]=c+1664;a=i[c+380>>2];i[c+88>>2]=i[c+376>>2];i[c+92>>2]=a;i[c+364>>2]=c+1664;i[c+1648>>2]=f;a=i[c+364>>2];i[c+72>>2]=i[c+360>>2];i[c+76>>2]=a;a=dc(d,c+88|0,c+72|0,c+1648|0,b);bc(d);if(a){break i}break c;case 3:d=ec(c+456|0,e);a=i[c+1668>>2];i[c+352>>2]=a;i[c+336>>2]=a;i[c+128>>2]=a;i[c+112>>2]=a;i[c+344>>2]=0;i[c+328>>2]=i[c+1664>>2];i[c+348>>2]=c+1664;a=i[c+348>>2];i[c+120>>2]=i[c+344>>2];i[c+124>>2]=a;i[c+332>>2]=c+1664;i[c+1648>>2]=f;a=i[c+332>>2];i[c+104>>2]=i[c+328>>2];i[c+108>>2]=a;a=fc(d,c+120|0,c+104|0,c+1648|0,b);gc(d);if(a){break i}break c;case 2:d=ec(c+456|0,e);a=i[c+1668>>2];i[c+320>>2]=a;i[c+304>>2]=a;i[c+160>>2]=a;i[c+144>>2]=a;i[c+312>>2]=0;i[c+296>>2]=i[c+1664>>2];i[c+316>>2]=c+1664;a=i[c+316>>2];i[c+152>>2]=i[c+312>>2];i[c+156>>2]=a;i[c+300>>2]=c+1664;i[c+1648>>2]=f;a=i[c+300>>2];i[c+136>>2]=i[c+296>>2];i[c+140>>2]=a;a=fc(d,c+152|0,c+136|0,c+1648|0,b);gc(d);if(a){break i}break c;case 1:d=hc(c+456|0,e);a=i[c+1668>>2];i[c+288>>2]=a;i[c+272>>2]=a;i[c+192>>2]=a;i[c+176>>2]=a;i[c+280>>2]=0;i[c+264>>2]=i[c+1664>>2];i[c+284>>2]=c+1664;a=i[c+284>>2];i[c+184>>2]=i[c+280>>2];i[c+188>>2]=a;i[c+268>>2]=c+1664;i[c+1648>>2]=f;a=i[c+268>>2];i[c+168>>2]=i[c+264>>2];i[c+172>>2]=a;a=ic(d,c+184|0,c+168|0,c+1648|0,b);jc(d);if(a){break i}break c;case 0:break j;default:break c}}d=hc(c+456|0,e);a=i[c+1668>>2];i[c+256>>2]=a;i[c+240>>2]=a;i[c+224>>2]=a;i[c+208>>2]=a;i[c+248>>2]=0;i[c+232>>2]=i[c+1664>>2];i[c+252>>2]=c+1664;a=i[c+252>>2];i[c+216>>2]=i[c+248>>2];i[c+220>>2]=a;i[c+236>>2]=c+1664;i[c+1648>>2]=f;a=i[c+236>>2];i[c+200>>2]=i[c+232>>2];i[c+204>>2]=a;a=ic(d,c+216|0,c+200|0,c+1648|0,b);jc(d);if(!a){break c}}y=1}a=i[c+1676>>2];if(a){i[c+1680>>2]=a;bp(a)}F=c+1696|0;return y|0}Ho();x()}Ho();x()}function gi(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0;l=F-32|0;F=l;d=i[a+16>>2];i[a+20>>2]=d;a:{if((d|0)!=i[a+24>>2]){i[d>>2]=b;b=d+4|0;i[a+20>>2]=b;g=d;break a}g=ho(4);i[g>>2]=b;b=g+4|0;i[a+24>>2]=b;i[a+20>>2]=b;i[a+16>>2]=g;if(!d){break a}bp(d);b=i[a+20>>2];g=i[a+16>>2]}d=i[a+8>>2];q=i[d+100>>2]-i[d+96>>2]|0;d=(q|0)/12|0;b:{if((b|0)!=(g|0)){u=a+16|0;v=(d|0)>1?d:1;w=a+120|0;n=a+268|0;y=a+72|0;while(1){c:{d:{d=b+ -4|0;b=i[d>>2];if((b|0)==-1){break d}g=i[a+28>>2];e=(b>>>0)/3|0;if(i[g+(e>>>3&268435452)>>2]>>>e&1){break d}h=0;if((q|0)<=0){break c}while(1){i[a+164>>2]=i[a+164>>2]+1;j=(b>>>0)/3|0;m=(b|0)==-1;k=m?-1:j;d=(k>>>3&536870908)+g|0;i[d>>2]=i[d>>2]|1<>2];i:{if((d|0)!=i[a+80>>2]){i[d>>2]=b;i[a+76>>2]=d+4;break i}g=i[y>>2];e=d-g|0;f=e>>2;c=f+1|0;if(c>>>0>=1073741824){break h}p=e>>1;c=f>>>0<536870911?p>>>0>>0?c:p:1073741823;d=0;j:{if(!c){break j}if(c>>>0>=1073741824){break b}d=ho(c<<2)}f=d+(f<<2)|0;i[f>>2]=b;c=d+(c<<2)|0;f=f+4|0;if((e|0)>=1){hp(d,g,e)}i[a+80>>2]=c;i[a+76>>2]=f;i[a+72>>2]=d;if(!g){break i}bp(g)}d=-1;d=(b|0)!=-1?i[i[i[a+12>>2]>>2]+(b<<2)>>2]:d;r=d<<2;p=i[r+i[a+152>>2]>>2];k:{g=i[a+84>>2]+(d>>>3&536870908)|0;e=i[g>>2];d=1<>2]=d|e;if((p|0)!=-1){break k}d=i[a+272>>2];l:{if((d|0)!=i[a+276>>2]){i[d>>2]=0;i[a+272>>2]=d+4;break l}g=i[n>>2];e=d-g|0;f=e>>2;c=f+1|0;if(c>>>0>=1073741824){break g}k=e>>1;c=f>>>0<536870911?k>>>0>>0?c:k:1073741823;d=0;m:{if(!c){break m}if(c>>>0>=1073741824){break b}d=ho(c<<2)}f=d+(f<<2)|0;i[f>>2]=0;c=d+(c<<2)|0;f=f+4|0;if((e|0)>=1){hp(d,g,e)}i[a+276>>2]=c;i[a+272>>2]=f;i[a+268>>2]=d;if(!g){break l}bp(g)}d=-1;if((b|0)==-1){break e}g=b+1|0;b=(g>>>0)%3|0?g:b+ -2|0;if((b|0)==-1){break e}d=i[i[i[a+12>>2]+12>>2]+(b<<2)>>2];break e}n:{o:{p:{if(m){break p}d=-1;g=-1;e=b+1|0;c=(e>>>0)%3|0?e:b+ -2|0;if((c|0)!=-1){g=i[i[i[a+12>>2]+12>>2]+(c<<2)>>2]}e=(b-o(j,3)|0?-1:2)+b|0;if((e|0)!=-1){d=i[i[i[a+12>>2]+12>>2]+(e<<2)>>2]}f=(d|0)==-1;j=f?-1:(d>>>0)/3|0;m=(g>>>0)/3|0;q:{if((c|0)==-1){break q}s=i[i[a+12>>2]+12>>2];c=i[s+(c<<2)>>2];if((c|0)==-1){break q}t=i[a+28>>2];c=(c>>>0)/3|0;if(!(i[t+(c>>>3&268435452)>>2]>>>c&1)){break n}}b=(g|0)==-1;if(!b){oi(a,i[a+164>>2],1,b?-1:m)}r:{if((e|0)==-1){break r}b=i[i[i[a+12>>2]+12>>2]+(e<<2)>>2];if((b|0)==-1){break r}b=(b>>>0)/3|0;if(!(i[i[a+28>>2]+(b>>>3&268435452)>>2]>>>b&1)){break o}}if(f){break p}oi(a,i[a+164>>2],0,j)}b=i[a+272>>2];s:{if((b|0)!=i[a+276>>2]){i[b>>2]=7;i[a+272>>2]=b+4;break s}d=i[n>>2];g=b-d|0;c=g>>2;e=c+1|0;if(e>>>0>=1073741824){break g}h=g>>1;e=c>>>0<536870911?h>>>0>>0?e:h:1073741823;b=0;t:{if(!e){break t}if(e>>>0>=1073741824){break b}b=ho(e<<2)}c=b+(c<<2)|0;i[c>>2]=7;e=b+(e<<2)|0;c=c+4|0;if((g|0)>=1){hp(b,d,g)}i[a+276>>2]=e;i[a+272>>2]=c;i[a+268>>2]=b;if(!d){break s}bp(d)}i[a+20>>2]=i[a+20>>2]+ -4;break c}b=i[a+272>>2];if((b|0)!=i[a+276>>2]){i[b>>2]=5;i[a+272>>2]=b+4;break e}g=i[n>>2];e=b-g|0;f=e>>2;c=f+1|0;if(c>>>0>=1073741824){break g}k=e>>1;c=f>>>0<536870911?k>>>0>>0?c:k:1073741823;b=0;u:{if(!c){break u}if(c>>>0>=1073741824){break b}b=ho(c<<2)}f=b+(f<<2)|0;i[f>>2]=5;c=b+(c<<2)|0;f=f+4|0;if((e|0)>=1){hp(b,g,e)}i[a+276>>2]=c;i[a+272>>2]=f;i[a+268>>2]=b;if(!g){break e}bp(g);break e}v:{w:{if((e|0)==-1){break w}e=i[(e<<2)+s>>2];if((e|0)==-1){break w}e=(e>>>0)/3|0;if(!(i[(e>>>3&268435452)+t>>2]>>>e&1)){break v}}if(!f){oi(a,i[a+164>>2],0,j)}b=i[a+272>>2];if((b|0)!=i[a+276>>2]){i[b>>2]=3;i[a+272>>2]=b+4;d=g;break e}d=i[n>>2];e=b-d|0;f=e>>2;c=f+1|0;if(c>>>0>=1073741824){break g}k=e>>1;c=f>>>0<536870911?k>>>0>>0?c:k:1073741823;b=0;x:{if(!c){break x}if(c>>>0>=1073741824){break b}b=ho(c<<2)}f=b+(f<<2)|0;i[f>>2]=3;c=b+(c<<2)|0;f=f+4|0;if((e|0)>=1){hp(b,d,e)}i[a+276>>2]=c;i[a+272>>2]=f;i[a+268>>2]=b;if(d){bp(d)}d=g;break e}e=i[a+272>>2];y:{if((e|0)!=i[a+276>>2]){i[e>>2]=1;i[a+272>>2]=e+4;break y}c=i[n>>2];h=e-c|0;j=h>>2;f=j+1|0;if(f>>>0>=1073741824){break g}m=h>>1;f=j>>>0<536870911?m>>>0>>0?f:m:1073741823;e=0;z:{if(!f){break z}if(f>>>0>=1073741824){break b}e=ho(f<<2)}j=e+(j<<2)|0;i[j>>2]=1;f=e+(f<<2)|0;j=j+4|0;if((h|0)>=1){hp(e,c,h)}i[a+276>>2]=f;i[a+272>>2]=j;i[a+268>>2]=e;if(!c){break y}bp(c)}i[a+168>>2]=i[a+168>>2]+1;A:{if((p|0)==-1){break A}e=i[r+i[a+152>>2]>>2];if(i[i[a+140>>2]+(e>>>3&536870908)>>2]>>>e&1){break A}hi(a,b,0)}b=i[a+164>>2];i[l+4>>2]=k;i[l+16>>2]=l+4;pi(l+24|0,w,l+4|0,l+16|0);i[i[l+24>>2]+12>>2]=b;b=i[a+20>>2];i[b+ -4>>2]=d;if((b|0)!=i[a+24>>2]){i[b>>2]=g;i[a+20>>2]=b+4;break c}d=i[u>>2];e=b-d|0;h=e>>2;c=h+1|0;if(c>>>0>=1073741824){break f}f=e>>1;c=h>>>0<536870911?f>>>0>>0?c:f:1073741823;b=0;B:{if(!c){break B}if(c>>>0>=1073741824){break b}b=ho(c<<2)}h=b+(h<<2)|0;i[h>>2]=g;g=b+(c<<2)|0;c=h+4|0;if((e|0)>=1){hp(b,d,e)}i[a+24>>2]=g;i[a+20>>2]=c;i[a+16>>2]=b;if(!d){break c}bp(d);break c}Ho();x()}Ho();x()}Ho();x()}h=h+1|0;if((v|0)==(h|0)){break c}g=i[a+28>>2];b=d;continue}}i[a+20>>2]=d}b=i[a+20>>2];if((b|0)!=i[a+16>>2]){continue}break}}F=l+32|0;return 1}za(11708);x()}function yn(a,b,c,d,e,f,g,h,j){var k=0,l=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,I=0,J=0,K=0,L=0,M=0;m=F-96|0;F=m;u=c;w=(g&131071)<<15|f>>>17;k=j&65535;o=k;n=h;l=h;z=l<<15|g>>>17;p=(e^j)&-2147483648;v=e&65535;l=v;q=d;x=l;v=0;B=(k&131071)<<15|h>>>17;C=j>>>16&32767;D=e>>>16&32767;a:{b:{if(C+ -1>>>0<32766?D+ -1>>>0<=32765:0){break b}s=e&2147483647;y=s;k=d;if(!(!d&(s|0)==2147418112?!(b|c):(s|0)==2147418112&d>>>0<0|s>>>0<2147418112)){A=d;p=e|32768;break a}s=j&2147483647;e=s;d=h;if(!(!d&(e|0)==2147418112?!(f|g):(e|0)==2147418112&d>>>0<0|e>>>0<2147418112)){A=h;p=j|32768;b=f;c=g;break a}if(!(b|k|(y^2147418112|c))){if(!(d|f|(e|g))){p=2147450880;b=0;c=0;break a}p=p|2147418112;b=0;c=0;break a}if(!(d|f|(e^2147418112|g))){d=b|k;e=c|y;b=0;c=0;if(!(d|e)){p=2147450880;break a}p=p|2147418112;break a}if(!(b|k|(c|y))){b=0;c=0;break a}if(!(d|f|(e|g))){b=0;c=0;break a}if((y|0)==65535|y>>>0<65535){j=b;d=!(l|q);h=d<<6;k=r(d?b:q)+32|0;b=r(d?c:l);b=h+((b|0)==32?k:b)|0;sn(m+80|0,j,c,q,l,b+ -15|0);t=16-b|0;q=i[m+88>>2];u=i[m+84>>2];x=i[m+92>>2];b=i[m+80>>2]}if(e>>>0>65535){break b}c=!(n|o);d=c<<6;e=r(c?f:n)+32|0;c=r(c?g:o);c=d+((c|0)==32?e:c)|0;sn(m- -64|0,f,g,n,o,c+ -15|0);t=(t-c|0)+16|0;f=i[m+76>>2];c=f;h=i[m+68>>2];g=h;e=i[m+72>>2];d=e;z=d<<15|g>>>17;d=g;f=i[m+64>>2];w=(d&131071)<<15|f>>>17;B=(c&131071)<<15|e>>>17}L=(C+D|0)+t|0;o=z;s=0;t=0;h=up(o,s,q,t);d=H;j=0;n=w;C=0;x=x|65536;l=up(n,C,x,v);e=l+h|0;c=H+d|0;c=e>>>0>>0?c+1|0:c;l=e;k=e;e=c;g=(d|0)==(c|0)&k>>>0>>0|c>>>0>>0;B=B&2147483647|-2147483648;D=0;h=up(B,D,u,0);d=h+k|0;k=H+c|0;E=d;c=d;k=c>>>0>>0?k+1|0:k;d=k;c=(e|0)==(d|0)&c>>>0>>0|d>>>0>>0;e=g+c|0;if(e>>>0>>0){j=1}M=e;h=j;l=d;e=up(n,C,u,I);c=H;k=c;j=0;G=f<<15&-32768;g=up(G,0,q,t);f=g+e|0;c=H+c|0;c=f>>>0>>0?c+1|0:c;g=f;f=(k|0)==(c|0)&g>>>0>>0|c>>>0>>0;w=b;e=up(o,s,b,0);b=e+g|0;k=H+c|0;k=b>>>0>>0?k+1|0:k;z=b;e=k;b=(c|0)==(k|0)&b>>>0>>0|k>>>0>>0;c=f+b|0;if(c>>>0>>0){j=1}b=c+E|0;f=j+l|0;y=b;k=h;f=b>>>0>>0?f+1|0:f;g=f;b=(d|0)==(f|0)&b>>>0>>0|f>>>0>>0;c=b+M|0;if(c>>>0>>0){k=k+1|0}b=c;c=up(x,v,B,D);b=b+c|0;l=H+k|0;l=b>>>0>>0?l+1|0:l;d=up(q,t,B,D);c=H;j=b;h=up(o,s,x,v);b=h+d|0;f=H+c|0;f=b>>>0>>0?f+1|0:f;h=b;k=b;b=f;f=(c|0)==(b|0)&k>>>0>>0|b>>>0>>0;d=j+b|0;k=f+l|0;c=d;f=c>>>0>>0?k+1|0:k;c=h;h=0;b=h+y|0;j=c+g|0;j=b>>>0>>0?j+1|0:j;h=b;c=b;b=j;c=(g|0)==(b|0)&c>>>0>>0|b>>>0>>0;d=d+c|0;if(d>>>0>>0){f=f+1|0}E=d;c=h;y=b;t=up(q,t,n,C);q=H;g=up(G,J,x,v);d=g+t|0;l=H+q|0;l=d>>>0>>0?l+1|0:l;x=d;k=up(o,s,u,I);d=d+k|0;g=l;j=g+H|0;j=d>>>0>>0?j+1|0:j;o=d;l=up(w,K,B,D);d=d+l|0;k=H+j|0;v=d;k=d>>>0>>0?k+1|0:k;d=k;s=d;k=0;l=f;d=(d|0)==(j|0)&v>>>0>>0|d>>>0>>0;f=(g|0)==(j|0)&o>>>0>>0|j>>>0>>0;g=f+((g|0)==(q|0)&x>>>0>>0|g>>>0>>0)|0;g>>>0>>0;g=d+g|0;f=s;c=c+f|0;k=(g|k)+y|0;k=c>>>0>>0?k+1|0:k;j=c;g=k;b=(b|0)==(k|0)&c>>>0>>0|k>>>0>>0;c=b+E|0;if(c>>>0>>0){l=l+1|0}t=c;b=l;l=j;o=g;k=z;s=up(w,K,n,C);n=H;h=up(G,J,u,I);c=h+s|0;f=H+n|0;q=c;f=c>>>0>>0?f+1|0:f;h=f;f=0;c=(h|0)==(n|0)&c>>>0>>0|h>>>0>>0;n=h+k|0;k=(c|f)+e|0;k=n>>>0>>0?k+1|0:k;f=n;c=k;u=(e|0)==(c|0)&f>>>0>>0|c>>>0>>0;h=l;f=0;k=c+v|0;l=0;d=l+n|0;if(d>>>0>>0){k=k+1|0}e=d;d=k;c=(c|0)==(d|0)&e>>>0>>0|d>>>0>>0;l=c+u|0;if(l>>>0>>0){f=1}k=l;c=h+k|0;l=f+o|0;l=c>>>0>>0?l+1|0:l;v=c;k=b;b=l;c=(g|0)==(b|0)&c>>>0>>0|b>>>0>>0;f=c+t|0;if(f>>>0>>0){k=k+1|0}o=f;c=k;f=c&65536;k=f;n=0;u=k>>>16|0;g=(u+L|0)+ -16383|0;if((g|0)>=32767){p=p|2147418112;b=0;c=0;break a}l=o;j=c<<1|l>>>31;f=l<<1|b>>>31;z=!(k|n);l=z;o=l?f:o;n=l?j:c;j=d;h=0;f=up(w,K,G,J);d=h+f|0;k=q;c=k+H|0;c=d>>>0>>0?c+1|0:c;q=d;f=c;c=e+((k|0)==(c|0)&d>>>0>>0|c>>>0>>0)|0;if(c>>>0>>0){j=j+1|0}w=c;c=j;h=c;e=w;l=u^1;d=l&31;if(32<=(l&63)>>>0){k=e<>>32-d|h<>>1|0;h=(d&1)<<31|q>>>1;j=u|62;d=j&31;if(32<=(j&63)>>>0){j=0;d=k>>>d|0}else{j=k>>>d|0;d=((1<>>d}d=w|d;e=e|j;j=v;k=b<<1|j>>>31;h=z?j<<1|c>>>31:j;j=z?k:b;c=q;b=l&31;if(32<=(l&63)>>>0){f=c<>>32-b|f<>>0>=128){b=0;c=0;break a}g=g+127|0;sn(m+48|0,b,c,d,e,g);sn(m+32|0,h,j,o,n,g);vn(m+16|0,b,c,d,e,f);vn(m,h,j,o,n,f);b=(i[m+48>>2]|i[m+56>>2])!=0|(i[m+52>>2]|i[m+60>>2])!=0|(i[m+32>>2]|i[m+16>>2]);c=i[m+36>>2]|i[m+20>>2];d=i[m+40>>2]|i[m+24>>2];e=i[m+44>>2]|i[m+28>>2];h=i[m>>2];j=i[m+4>>2];g=i[m+12>>2];o=i[m+8>>2];break c}g=n&65535|g<<16}A=o|A;p=g|p;if(!(!d&(e|0)==-2147483648?!(b|c):(e|0)>-1)){k=p;l=j;b=h+1|0;if(b>>>0<1){l=l+1|0}d=b;c=l;d=(j|0)==(c|0)&d>>>0>>0|c>>>0>>0;e=d+A|0;if(e>>>0>>0){k=k+1|0}A=e;p=k;break a}if(b|d|(e^-2147483648|c)){b=h;c=j;break a}l=p;f=j;b=h&1;c=b+h|0;if(c>>>0>>0){f=f+1|0}b=c;d=c;c=f;d=(j|0)==(c|0)&d>>>0>>0|c>>>0>>0;e=d+A|0;if(e>>>0>>0){l=l+1|0}A=e;p=l}i[a>>2]=b;i[a+4>>2]=c;i[a+8>>2]=A;i[a+12>>2]=p;F=m+96|0}function Pd(a,b,c,d){var e=0,f=0,h=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0;k=F-80|0;F=k;h=-1;e=-1;a:{if((b|0)==-1){break a}e=b+1|0;h=(e>>>0)%3|0?e:b+ -2|0;e=b+ -1|0;if((b>>>0)%3|0){break a}e=b+2|0}f=i[a+36>>2];b=i[f>>2];b:{c:{d:{e:{f:{g:{h:{i:{f=i[f+4>>2]-b>>2;m=h<<2;h=i[i[a+32>>2]+28>>2];l=i[m+h>>2];if(f>>>0<=l>>>0){break i}e=i[h+(e<<2)>>2];if(f>>>0<=e>>>0){break i}K=i[b+(l<<2)>>2];M=(K|0)>=(d|0);if(M){break d}l=i[b+(e<<2)>>2];if((l|0)>=(d|0)){break d}b=l<<3;f=i[(b|4)+c>>2];e=K<<3;h=i[(e|4)+c>>2];J=i[b+c>>2];z=i[c+e>>2];if(!((J|0)!=(z|0)|(f|0)!=(h|0))){i[a+8>>2]=z;i[a+12>>2]=h;break c}b=i[i[a+4>>2]+(d<<2)>>2];i[k+72>>2]=0;i[k+76>>2]=0;e=k- -64|0;i[e>>2]=0;i[e+4>>2]=0;i[k+56>>2]=0;i[k+60>>2]=0;e=i[a>>2];if(!j[e+84|0]){b=i[i[e+68>>2]+(b<<2)>>2]}Qd(e,b,g[e+24|0],k+56|0);b=i[i[a+4>>2]+(K<<2)>>2];i[k+48>>2]=0;i[k+52>>2]=0;i[k+40>>2]=0;i[k+44>>2]=0;i[k+32>>2]=0;i[k+36>>2]=0;e=i[a>>2];if(!j[e+84|0]){b=i[i[e+68>>2]+(b<<2)>>2]}Qd(e,b,g[e+24|0],k+32|0);b=i[i[a+4>>2]+(l<<2)>>2];i[k+24>>2]=0;i[k+28>>2]=0;i[k+16>>2]=0;i[k+20>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;e=i[a>>2];if(!j[e+84|0]){b=i[i[e+68>>2]+(b<<2)>>2]}Qd(e,b,g[e+24|0],k+8|0);A=i[k+44>>2];e=i[k+16>>2];b=i[k+40>>2];n=i[k+20>>2]-(A+(e>>>0>>0)|0)|0;l=e-b|0;e=up(l,n,l,n);r=H;m=e;y=i[k+36>>2];e=i[k+8>>2];u=i[k+32>>2];o=u;s=i[k+12>>2]-(y+(e>>>0>>0)|0)|0;o=e-o|0;p=up(o,s,o,s);m=m+p|0;e=H+r|0;e=m>>>0

>>0?e+1|0:e;q=m;t=i[k+52>>2];m=i[k+24>>2];v=i[k+48>>2];p=v;r=i[k+28>>2]-(t+(m>>>0

>>0)|0)|0;m=m-p|0;C=up(m,r,m,r);p=q+C|0;e=H+e|0;w=p;p=p>>>0>>0?e+1|0:e;if(!(w|p)){break d}e=i[k+64>>2];C=e-b|0;N=i[k+68>>2]-((e>>>0>>0)+A|0)|0;b=up(C,N,l,n);e=H;q=b;b=i[k+56>>2];E=b-u|0;O=i[k+60>>2]-((b>>>0>>0)+y|0)|0;u=up(E,O,o,s);b=q+u|0;e=H+e|0;e=b>>>0>>0?e+1|0:e;q=b;b=i[k+72>>2];G=b-v|0;P=i[k+76>>2]-((b>>>0>>0)+t|0)|0;u=up(G,P,m,r);b=q+u|0;e=H+e|0;v=b;e=b>>>0>>0?e+1|0:e;A=e;B=b;u=e;e=r;y=e>>31;b=e>>31;e=b+e|0;t=y;q=t+m|0;if(q>>>0>>0){e=e+1|0}y=y^q;b=b^e;Q=b;q=y;I=b;e=n;b=e>>31;e=e>>31;D=b;b=b+n|0;L=e+l|0;if(L>>>0>>0){b=b+1|0}e=e^L;b=b^D;R=b;L=b;S=B;T=q;q=e;B=s>>31;b=B+s|0;t=s>>31;D=t+o|0;if(D>>>0>>0){b=b+1|0}t=t^D;e=t>>>0>>0;b=b^B;e=(b|0)<(R|0)?1:(b|0)<=(R|0)?e:0;t=e?q:t;b=e?L:b;e=(b|0)<(Q|0)?1:(b|0)<=(Q|0)?t>>>0>>0:0;e=S>>>0>wp(-1,2147483647,e?T:t,e?I:b)>>>0;b=H;if((u|0)>(b|0)?1:(u|0)>=(b|0)?e:0){break e}b=f;e=h;B=b-e|0;h=e>>31;q=(b>>31)-(h+(b>>>0>>0)|0)|0;b=up(v,A,B,q);f=H;e=up(e,h,w,p);b=e+b|0;f=H+f|0;u=b;y=b>>>0>>0?f+1|0:f;b=J;e=z;I=b-e|0;f=e>>31;D=(b>>31)-(f+(b>>>0>>0)|0)|0;b=up(v,A,I,D);h=H;f=up(e,f,w,p);e=f+b|0;b=H+h|0;z=e;J=e>>>0>>0?b+1|0:b;h=1;e=0;b=vp(up(l,n,v,A),H,w,p);f=N-(H+(C>>>0>>0)|0)|0;b=C-b|0;b=up(b,f,b,f);l=H;n=b;b=vp(up(o,s,v,A),H,w,p);f=O-(H+(E>>>0>>0)|0)|0;b=E-b|0;o=up(b,f,b,f);b=n+o|0;f=H+l|0;f=b>>>0>>0?f+1|0:f;n=b;b=vp(up(m,r,v,A),H,w,p);l=P-(H+(G>>>0>>0)|0)|0;b=G-b|0;o=up(b,l,b,l);l=n+o|0;b=H+f|0;l=up(l,l>>>0>>0?b+1|0:b,w,p);b=H;o=b;if(!b&l>>>0<=1|b>>>0<0){break h}m=l;f=o;while(1){b=e<<1|h>>>31;h=h<<1;e=b;n=!f&m>>>0>7|f>>>0>0;m=(f&3)<<30|m>>>2;f=f>>>2|0;if(n){continue}break}break g}Io();x()}h=l;e=o;if(h-1|0){break f}}while(1){b=wp(l,o,h,e)+h|0;e=e+H|0;e=b>>>0>>0?e+1|0:e;h=(e&1)<<31|b>>>1;e=e>>>1|0;b=up(h,e,h,e);f=H;if((o|0)==(f|0)&b>>>0>l>>>0|f>>>0>o>>>0){continue}break}}o=a+16|0;t=i[a+24>>2];C=t<<5;b=i[a+20>>2];v=d<<3;n=i[(v|4)+c>>2];m=n;f=u;E=up(h,e,I,D);l=E;G=H;l=vp(f-l|0,y-(G+(f>>>0>>0)|0)|0,w,p);r=m>>31;f=m;s=r-(H+(f>>>0>>0)|0)|0;f=f-l|0;I=up(f,s,f,s);D=H;n=i[c+v>>2];f=n>>31;v=n;s=n;A=f;n=up(h,e,B,q);B=H;e=J+B|0;h=z;q=h+n|0;if(q>>>0>>0){e=e+1|0}h=vp(q,e,w,p);e=h;f=f-(H+(s>>>0>>0)|0)|0;e=s-e|0;s=up(e,f,e,f);e=s+I|0;f=H+D|0;q=e;s=e>>>0>>0?f+1|0:f;e=y+G|0;f=u+E|0;if(f>>>0>>0){e=e+1|0}e=vp(f,e,w,p);r=r-(H+(m>>>0>>0)|0)|0;f=m-e|0;f=up(f,r,f,r);u=H;m=vp(z-n|0,J-((z>>>0>>0)+B|0)|0,w,p);n=f;f=m;r=A-(H+(v>>>0>>0)|0)|0;f=v-f|0;z=up(f,r,f,r);r=n+z|0;f=H+u|0;f=r>>>0>>0?f+1|0:f;j:{if((f|0)==(s|0)&q>>>0>>0|s>>>0>>0){if((b|0)==(C|0)){if((b+1|0)<=-1){break b}if(b>>>0<=1073741822){b=b+32&-32;e=t<<6;b=e>>>0>>0?b:e}else{b=2147483647}bd(o,b);b=i[a+20>>2]}i[a+20>>2]=b+1;e=i[a+16>>2]+(b>>>3&536870908)|0;i[e>>2]=i[e>>2]|1<>>0<=1073741822){b=b+32&-32;h=t<<6;b=h>>>0>>0?b:h}else{b=2147483647}bd(o,b);b=i[a+20>>2]}i[a+20>>2]=b+1;h=i[a+16>>2]+(b>>>3&536870908)|0;f=i[h>>2];U=h,V=yp(-2,b)&f,i[U>>2]=V;l=e;h=m}i[a+8>>2]=h;i[a+12>>2]=l}if(!(p|w)){break d}break c}e=a;if(M){if((d|0)<=0){i[a+8>>2]=0;i[a+12>>2]=0;break c}b=(d<<1)+ -2|0}else{b=K<<1}b=(b<<2)+c|0;i[e+8>>2]=i[b>>2];i[a+12>>2]=i[b+4>>2]}F=k+80|0;return}Ho();x()} + + + +function lc(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0;e=F+ -64|0;F=e;g=i[a+8>>2];i[e+48>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;a:{if(g){if(g>>>0>=1073741824){break a}d=g<<2;f=ho(d);i[e+40>>2]=f;k=d+f|0;i[e+48>>2]=k;ip(f,0,d);i[e+44>>2]=k}h=i[a+1164>>2];d=i[h>>2];if(d){i[h+4>>2]=d;bp(d);i[h+8>>2]=0;i[h>>2]=0;i[h+4>>2]=0;g=i[a+8>>2];k=i[e+44>>2];f=i[e+40>>2]}i[h+4>>2]=k;i[h>>2]=f;i[h+8>>2]=i[e+48>>2];f=0;i[e+48>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;d=0;b:{if(g){if(g>>>0>=1073741824){break b}h=g<<2;d=ho(h);i[e+40>>2]=d;f=d+h|0;i[e+48>>2]=f;ip(d,0,h);i[e+44>>2]=f}n=i[a+1176>>2];h=i[n>>2];if(h){i[n+4>>2]=h;bp(h);i[n+8>>2]=0;i[n>>2]=0;i[n+4>>2]=0;d=i[e+40>>2];f=i[e+44>>2]}i[n+4>>2]=f;i[n>>2]=d;i[n+8>>2]=i[e+48>>2];z=i[b+4>>2];G=i[b+8>>2];A=i[c+4>>2];H=i[c+8>>2];q=i[c>>2];j=i[b>>2];d=e+56|0;i[d>>2]=0;i[d+4>>2]=0;f=e+48|0;i[f>>2]=0;i[f+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;n=q-j|0;vc(e+40|0);g=0;h=i[e+44>>2];if((h|0)!=i[f>>2]){d=i[d>>2]+i[e+60>>2]|0;f=(d>>>0)/113|0;g=i[h+(f<<2)>>2]+o(d-o(f,113)|0,36)|0}i[g+32>>2]=0;i[g+28>>2]=n;i[g+24>>2]=0;i[g+16>>2]=A;i[g+20>>2]=H;i[g+12>>2]=q;i[g+4>>2]=z;i[g+8>>2]=G;i[g>>2]=j;f=i[e+60>>2];g=f+1|0;i[e+60>>2]=g;if(g>>>0>=f>>>0){n=A;q=z;J=a+1068|0;K=a+1108|0;L=i[c+8>>2];u=i[b+8>>2];while(1){f=i[e+44>>2];j=g+ -1|0;m=j+i[e+56>>2]|0;d=(m>>>0)/113|0;d=i[f+(d<<2)>>2]+o(m-o(d,113)|0,36)|0;B=i[d+32>>2];p=i[d+12>>2];h=i[d>>2];i[e+60>>2]=j;d=i[e+48>>2];f=d-f|0;if((f?o(f>>2,113)+ -1|0:0)-m>>>0>=226){bp(i[d+ -4>>2]);i[e+48>>2]=i[e+48>>2]+ -4}i[b>>2]=h;i[c>>2]=p;j=i[a+1164>>2];d=i[a+1176>>2];i[e+32>>2]=i[b+8>>2];f=i[b+4>>2];i[e+24>>2]=i[b>>2];i[e+28>>2]=f;i[e+16>>2]=i[c+8>>2];f=i[c+4>>2];i[e+8>>2]=i[c>>2];i[e+12>>2]=f;C=o(B,12);k=j+C|0;m=d+C|0;g=wc(a,e+24|0,e+8|0,k,m);v=g<<2;f=i[a>>2]-i[v+i[m>>2]>>2]|0;c:{d:{if(!f){break d}e:{f:{w=p-h|0;if(w>>>0<=2){j=i[a+1152>>2];i[j>>2]=g;f=1;d=i[a+8>>2];if(d>>>0>1){break f}break e}j=1<>2];d=D+f|0;if((d|0)!=(k|0)){ra(d,i[k>>2],i[k+4>>2]);f=i[a+1164>>2]}f=v+i[f+D>>2]|0;E=j+i[f>>2]|0;i[f>>2]=E;f=h;g:{if((p|0)==(f|0)){break g}M=i[q+24>>2];d=h;f=p;while(1){s=M+(o(d,u)<<2)|0;if(l[s+v>>2]>>0){d=d+1|0;if((f|0)!=(d|0)){continue}break g}while(1){f=f+ -1|0;if((f|0)==(d|0)){f=d;break g}t=i[n+24>>2]+(o(f,L)<<2)|0;if(l[v+t>>2]>=E>>>0){continue}break}y=0;if(u){while(1){k=y<<2;j=k+s|0;m=i[j>>2];N=j;j=k+t|0;i[N>>2]=i[j>>2];i[j>>2]=m;y=y+1|0;if((u|0)!=(y|0)){continue}break}}d=d+1|0;if((f|0)!=(d|0)){continue}break}}d=r(w);s=f-h|0;t=p-f|0;if((s|0)!=(t|0)){xc(K,s>>>0>>0)}m=d^31;d=w>>>1|0;h:{if(s>>>0>>0){if(!m){break h}j=d-s|0;d=1<>>1|0;k=k+1|0;if((m|0)!=(k|0)){continue}break}break h}if(!m){break h}j=d-t|0;d=1<>>1|0;k=k+1|0;if((m|0)!=(k|0)){continue}break}}k=i[a+1176>>2];m=k+C|0;j=i[m>>2];d=j+v|0;i[d>>2]=i[d>>2]+1;ra(k+D|0,j,i[m+4>>2]);if((f|0)!=(h|0)){k=i[e+60>>2]+i[e+56>>2]|0;m=i[e+48>>2];d=i[e+44>>2];j=m-d|0;if((k|0)==((j?o(j>>2,113)+ -1|0:0)|0)){vc(e+40|0);k=i[e+56>>2]+i[e+60>>2]|0;m=i[e+48>>2];d=i[e+44>>2]}j=0;i:{if((d|0)==(m|0)){break i}j=d;d=(k>>>0)/113|0;j=i[j+(d<<2)>>2]+o(k-o(d,113)|0,36)|0}i[j+32>>2]=B;i[j+28>>2]=s;i[j+24>>2]=g;i[j+20>>2]=u;i[j+16>>2]=q;i[j+12>>2]=f;i[j+4>>2]=z;i[j+8>>2]=G;i[j>>2]=h;i[e+60>>2]=i[e+60>>2]+1}if((f|0)==(p|0)){break d}k=i[e+60>>2]+i[e+56>>2]|0;m=i[e+48>>2];d=i[e+44>>2];h=m-d|0;if((k|0)==((h?o(h>>2,113)+ -1|0:0)|0)){vc(e+40|0);k=i[e+56>>2]+i[e+60>>2]|0;m=i[e+48>>2];d=i[e+44>>2]}h=0;j:{if((d|0)==(m|0)){break j}h=d;d=(k>>>0)/113|0;h=i[h+(d<<2)>>2]+o(k-o(d,113)|0,36)|0}i[h+32>>2]=I;i[h+28>>2]=t;i[h+24>>2]=g;i[h+16>>2]=A;i[h+20>>2]=H;i[h+12>>2]=p;i[h+8>>2]=u;i[h+4>>2]=q;i[h>>2]=f;g=i[e+60>>2]+1|0;i[e+60>>2]=g;break c}while(1){g=(d+ -1|0)==(g|0)?0:g+1|0;i[j+(f<<2)>>2]=g;f=f+1|0;d=i[a+8>>2];if(f>>>0>>0){continue}break}}k=0;f=d;if(!w){break d}while(1){k:{if(!f){f=0;break k}p=i[q+24>>2]+(o(i[q+4>>2],h+k|0)<<2)|0;g=0;while(1){j=i[i[a+1152>>2]+(g<<2)>>2]<<2;f=i[a>>2]-i[j+i[m>>2]>>2]|0;if(f){yc(J,f,i[j+p>>2]);d=i[a+8>>2]}f=d;g=g+1|0;if(g>>>0>>0){continue}break}}k=k+1|0;if((w|0)!=(k|0)){continue}break}}g=i[e+60>>2]}if(g){continue}break}}i[e+60>>2]=0;a=i[e+48>>2];g=i[e+44>>2];d=a-g>>2;if(d>>>0>=3){while(1){bp(i[g>>2]);g=i[e+44>>2]+4|0;i[e+44>>2]=g;a=i[e+48>>2];d=a-g>>2;if(d>>>0>2){continue}break}}f=56;l:{switch(d+ -1|0){case 1:f=113;case 0:i[e+56>>2]=f;break;default:break l}}m:{if((a|0)==(g|0)){break m}while(1){bp(i[g>>2]);g=g+4|0;if((g|0)!=(a|0)){continue}break}b=i[e+48>>2];a=i[e+44>>2];if((b|0)==(a|0)){break m}i[e+48>>2]=b+(((b-a|0)+ -4>>>2^-1)<<2)}a=i[e+40>>2];if(a){bp(a)}F=e- -64|0;return}Ho();x()}Ho();x()}function he(a,b,c,d){var e=0,f=0,h=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0;h=F-80|0;F=h;e=-1;k=-1;a:{if((b|0)==-1){break a}f=((b>>>0)%3|0?-1:2)+b|0;l=i[a+32>>2];m=b+1|0;b=(m>>>0)%3|0?m:b+ -2|0;if((b|0)!=-1){e=i[i[l>>2]+(b<<2)>>2]}if((f|0)==-1){break a}k=i[i[l>>2]+(f<<2)>>2]}f=i[a+36>>2];b=i[f>>2];b:{c:{d:{e:{f:{g:{h:{f=i[f+4>>2]-b>>2;if(!(f>>>0<=e>>>0|f>>>0<=k>>>0)){K=i[b+(e<<2)>>2];M=(K|0)>=(d|0);if(M){break d}l=i[b+(k<<2)>>2];if((l|0)>=(d|0)){break d}b=l<<3;f=i[(b|4)+c>>2];e=K<<3;k=i[(e|4)+c>>2];J=i[b+c>>2];z=i[c+e>>2];if(!((J|0)!=(z|0)|(f|0)!=(k|0))){i[a+8>>2]=z;i[a+12>>2]=k;break c}b=i[i[a+4>>2]+(d<<2)>>2];i[h+72>>2]=0;i[h+76>>2]=0;e=h- -64|0;i[e>>2]=0;i[e+4>>2]=0;i[h+56>>2]=0;i[h+60>>2]=0;e=i[a>>2];if(!j[e+84|0]){b=i[i[e+68>>2]+(b<<2)>>2]}Qd(e,b,g[e+24|0],h+56|0);b=i[i[a+4>>2]+(K<<2)>>2];i[h+48>>2]=0;i[h+52>>2]=0;i[h+40>>2]=0;i[h+44>>2]=0;i[h+32>>2]=0;i[h+36>>2]=0;e=i[a>>2];if(!j[e+84|0]){b=i[i[e+68>>2]+(b<<2)>>2]}Qd(e,b,g[e+24|0],h+32|0);b=i[i[a+4>>2]+(l<<2)>>2];i[h+24>>2]=0;i[h+28>>2]=0;i[h+16>>2]=0;i[h+20>>2]=0;i[h+8>>2]=0;i[h+12>>2]=0;e=i[a>>2];if(!j[e+84|0]){b=i[i[e+68>>2]+(b<<2)>>2]}Qd(e,b,g[e+24|0],h+8|0);A=i[h+44>>2];e=i[h+16>>2];b=i[h+40>>2];o=i[h+20>>2]-(A+(e>>>0>>0)|0)|0;l=e-b|0;e=up(l,o,l,o);q=H;n=e;y=i[h+36>>2];e=i[h+8>>2];t=i[h+32>>2];m=t;r=i[h+12>>2]-(y+(e>>>0>>0)|0)|0;m=e-m|0;p=up(m,r,m,r);n=n+p|0;e=H+q|0;e=n>>>0

>>0?e+1|0:e;u=n;s=i[h+52>>2];n=i[h+24>>2];v=i[h+48>>2];p=v;q=i[h+28>>2]-(s+(n>>>0

>>0)|0)|0;n=n-p|0;C=up(n,q,n,q);p=u+C|0;e=H+e|0;w=p;p=p>>>0>>0?e+1|0:e;if(!(w|p)){break d}e=i[h+64>>2];C=e-b|0;N=i[h+68>>2]-((e>>>0>>0)+A|0)|0;b=up(C,N,l,o);e=H;u=b;b=i[h+56>>2];E=b-t|0;O=i[h+60>>2]-((b>>>0>>0)+y|0)|0;t=up(E,O,m,r);b=u+t|0;e=H+e|0;e=b>>>0>>0?e+1|0:e;u=b;b=i[h+72>>2];G=b-v|0;P=i[h+76>>2]-((b>>>0>>0)+s|0)|0;t=up(G,P,n,q);b=u+t|0;e=H+e|0;v=b;e=b>>>0>>0?e+1|0:e;A=e;B=b;t=e;e=q;y=e>>31;b=e>>31;e=b+e|0;s=y;u=s+n|0;if(u>>>0>>0){e=e+1|0}y=y^u;b=b^e;Q=b;u=y;I=b;e=o;b=e>>31;e=e>>31;D=b;b=b+o|0;L=e+l|0;if(L>>>0>>0){b=b+1|0}e=e^L;b=b^D;R=b;L=b;S=B;T=e;B=r>>31;b=B+r|0;s=r>>31;D=s+m|0;if(D>>>0>>0){b=b+1|0}s=s^D;e=s>>>0>>0;b=b^B;e=(b|0)<(R|0)?1:(b|0)<=(R|0)?e:0;s=e?T:s;b=e?L:b;e=(b|0)<(Q|0)?1:(b|0)<=(Q|0)?s>>>0>>0:0;e=S>>>0>wp(-1,2147483647,e?u:s,e?I:b)>>>0;b=H;if((t|0)>(b|0)?1:(t|0)>=(b|0)?e:0){break e}b=f;e=k;B=b-e|0;k=e>>31;u=(b>>31)-(k+(b>>>0>>0)|0)|0;b=up(v,A,B,u);f=H;e=up(e,k,w,p);b=e+b|0;f=H+f|0;t=b;y=b>>>0>>0?f+1|0:f;b=J;e=z;I=b-e|0;f=e>>31;D=(b>>31)-(f+(b>>>0>>0)|0)|0;b=up(v,A,I,D);k=H;f=up(e,f,w,p);e=f+b|0;b=H+k|0;z=e;J=e>>>0>>0?b+1|0:b;k=1;e=0;b=vp(up(l,o,v,A),H,w,p);f=N-(H+(C>>>0>>0)|0)|0;b=C-b|0;b=up(b,f,b,f);l=H;o=b;b=vp(up(m,r,v,A),H,w,p);f=O-(H+(E>>>0>>0)|0)|0;b=E-b|0;m=up(b,f,b,f);b=o+m|0;f=H+l|0;f=b>>>0>>0?f+1|0:f;o=b;b=vp(up(n,q,v,A),H,w,p);l=P-(H+(G>>>0>>0)|0)|0;b=G-b|0;m=up(b,l,b,l);l=o+m|0;b=H+f|0;l=up(l,l>>>0>>0?b+1|0:b,w,p);b=H;m=b;if(!b&l>>>0<=1|b>>>0<0){break h}n=l;f=m;while(1){b=e<<1|k>>>31;k=k<<1;e=b;o=!f&n>>>0>7|f>>>0>0;n=(f&3)<<30|n>>>2;f=f>>>2|0;if(o){continue}break}break g}Io();x()}k=l;e=m;if(k-1|0){break f}}while(1){b=wp(l,m,k,e)+k|0;e=e+H|0;e=b>>>0>>0?e+1|0:e;k=(e&1)<<31|b>>>1;e=e>>>1|0;b=up(k,e,k,e);f=H;if((m|0)==(f|0)&b>>>0>l>>>0|f>>>0>m>>>0){continue}break}}m=a+16|0;s=i[a+24>>2];C=s<<5;b=i[a+20>>2];v=d<<3;o=i[(v|4)+c>>2];n=o;f=t;E=up(k,e,I,D);l=E;G=H;l=vp(f-l|0,y-(G+(f>>>0>>0)|0)|0,w,p);q=n>>31;f=n;r=q-(H+(f>>>0>>0)|0)|0;f=f-l|0;I=up(f,r,f,r);D=H;o=i[c+v>>2];f=o>>31;v=o;r=o;A=f;o=up(k,e,B,u);B=H;e=J+B|0;k=z;u=k+o|0;if(u>>>0>>0){e=e+1|0}k=vp(u,e,w,p);e=k;f=f-(H+(r>>>0>>0)|0)|0;e=r-e|0;r=up(e,f,e,f);e=r+I|0;f=H+D|0;u=e;r=e>>>0>>0?f+1|0:f;e=y+G|0;f=t+E|0;if(f>>>0>>0){e=e+1|0}e=vp(f,e,w,p);q=q-(H+(n>>>0>>0)|0)|0;f=n-e|0;f=up(f,q,f,q);t=H;n=vp(z-o|0,J-((z>>>0>>0)+B|0)|0,w,p);o=f;f=n;q=A-(H+(v>>>0>>0)|0)|0;f=v-f|0;z=up(f,q,f,q);q=o+z|0;f=H+t|0;f=q>>>0>>0?f+1|0:f;i:{if((f|0)==(r|0)&u>>>0>>0|r>>>0>>0){if((b|0)==(C|0)){if((b+1|0)<=-1){break b}if(b>>>0<=1073741822){b=b+32&-32;e=s<<6;b=e>>>0>>0?b:e}else{b=2147483647}bd(m,b);b=i[a+20>>2]}i[a+20>>2]=b+1;e=i[a+16>>2]+(b>>>3&536870908)|0;i[e>>2]=i[e>>2]|1<>>0<=1073741822){b=b+32&-32;k=s<<6;b=k>>>0>>0?b:k}else{b=2147483647}bd(m,b);b=i[a+20>>2]}i[a+20>>2]=b+1;k=i[a+16>>2]+(b>>>3&536870908)|0;f=i[k>>2];U=k,V=yp(-2,b)&f,i[U>>2]=V;l=e;k=n}i[a+8>>2]=k;i[a+12>>2]=l}if(!(p|w)){break d}break c}e=a;if(M){if((d|0)<=0){i[a+8>>2]=0;i[a+12>>2]=0;break c}b=(d<<1)+ -2|0}else{b=K<<1}b=(b<<2)+c|0;i[e+8>>2]=i[b>>2];i[a+12>>2]=i[b+4>>2]}F=h+80|0;return}Ho();x()}function af(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0,o=0,q=0,r=p(0);h=F-32|0;F=h;a:{b:{if(!fd(a,b,c)){break b}n=i[i[i[b+4>>2]+8>>2]+(c<<2)>>2];if(i[n+28>>2]!=9){break b}k=i[b+48>>2];d=ho(32);i[h+16>>2]=d;i[h+20>>2]=17;i[h+24>>2]=-2147483616;g[d+17|0]=0;g[d+16|0]=j[9828];l=j[9824]|j[9825]<<8|(j[9826]<<16|j[9827]<<24);e=j[9820]|j[9821]<<8|(j[9822]<<16|j[9823]<<24);g[d+8|0]=e;g[d+9|0]=e>>>8;g[d+10|0]=e>>>16;g[d+11|0]=e>>>24;g[d+12|0]=l;g[d+13|0]=l>>>8;g[d+14|0]=l>>>16;g[d+15|0]=l>>>24;l=j[9816]|j[9817]<<8|(j[9818]<<16|j[9819]<<24);e=j[9812]|j[9813]<<8|(j[9814]<<16|j[9815]<<24);g[d|0]=e;g[d+1|0]=e>>>8;g[d+2|0]=e>>>16;g[d+3|0]=e>>>24;g[d+4|0]=l;g[d+5|0]=l>>>8;g[d+6|0]=l>>>16;g[d+7|0]=l>>>24;c:{d:{l=k+16|0;f=i[l>>2];if(!f){break d}d=l;while(1){e=i[f+16>>2]<(c|0);d=e?d:f;f=i[(e<<2)+f>>2];if(f){continue}break}if((d|0)==(l|0)|i[d+16>>2]>(c|0)){break d}d=d+20|0;if(!Sb(d,h+16|0)){break d}l=$j(d,h+16|0,-1);break c}l=$j(k,h+16|0,-1)}if(g[h+27|0]<=-1){bp(i[h+16>>2])}if((l|0)<1){f=0;break b}k=i[b+48>>2];d=ho(32);i[h+16>>2]=d;i[h+20>>2]=19;i[h+24>>2]=-2147483616;g[d+19|0]=0;e=j[9845]|j[9846]<<8|(j[9847]<<16|j[9848]<<24);g[d+15|0]=e;g[d+16|0]=e>>>8;g[d+17|0]=e>>>16;g[d+18|0]=e>>>24;e=j[9842]|j[9843]<<8|(j[9844]<<16|j[9845]<<24);f=j[9838]|j[9839]<<8|(j[9840]<<16|j[9841]<<24);g[d+8|0]=f;g[d+9|0]=f>>>8;g[d+10|0]=f>>>16;g[d+11|0]=f>>>24;g[d+12|0]=e;g[d+13|0]=e>>>8;g[d+14|0]=e>>>16;g[d+15|0]=e>>>24;e=j[9834]|j[9835]<<8|(j[9836]<<16|j[9837]<<24);f=j[9830]|j[9831]<<8|(j[9832]<<16|j[9833]<<24);g[d|0]=f;g[d+1|0]=f>>>8;g[d+2|0]=f>>>16;g[d+3|0]=f>>>24;g[d+4|0]=e;g[d+5|0]=e>>>8;g[d+6|0]=e>>>16;g[d+7|0]=e>>>24;e:{f:{e=k+16|0;f=i[e>>2];if(!f){break f}d=e;while(1){m=i[f+16>>2]<(c|0);d=m?d:f;f=i[(m<<2)+f>>2];if(f){continue}break}if((d|0)==(e|0)|i[d+16>>2]>(c|0)){break f}e=Sb(d+20|0,h+16|0);break e}e=Sb(k,h+16|0)}d=0;g:{if(!e){break g}k=i[b+48>>2];d=ho(32);i[h>>2]=d;i[h+4>>2]=18;i[h+8>>2]=-2147483616;g[d+18|0]=0;e=j[9866]|j[9867]<<8;g[d+16|0]=e;g[d+17|0]=e>>>8;e=j[9862]|j[9863]<<8|(j[9864]<<16|j[9865]<<24);f=j[9858]|j[9859]<<8|(j[9860]<<16|j[9861]<<24);g[d+8|0]=f;g[d+9|0]=f>>>8;g[d+10|0]=f>>>16;g[d+11|0]=f>>>24;g[d+12|0]=e;g[d+13|0]=e>>>8;g[d+14|0]=e>>>16;g[d+15|0]=e>>>24;e=j[9854]|j[9855]<<8|(j[9856]<<16|j[9857]<<24);f=j[9850]|j[9851]<<8|(j[9852]<<16|j[9853]<<24);g[d|0]=f;g[d+1|0]=f>>>8;g[d+2|0]=f>>>16;g[d+3|0]=f>>>24;g[d+4|0]=e;g[d+5|0]=e>>>8;g[d+6|0]=e>>>16;g[d+7|0]=e>>>24;h:{i:{e=k+16|0;f=i[e>>2];if(!f){break i}d=e;while(1){m=i[f+16>>2]<(c|0);d=m?d:f;f=i[(m<<2)+f>>2];if(f){continue}break}if((d|0)==(e|0)|i[d+16>>2]>(c|0)){break i}d=Sb(d+20|0,h);break h}d=Sb(k,h)}if(g[h+11|0]<=-1){bp(i[h>>2])}d=(d|0)!=0}if(g[h+27|0]<=-1){bp(i[h+16>>2])}j:{if(d){d=g[n+24|0];i[h+24>>2]=0;i[h+16>>2]=0;i[h+20>>2]=0;k=0;if(d){if((d|0)<=-1){break a}d=d<<2;k=ho(d);i[h+16>>2]=k;e=d+k|0;i[h+24>>2]=e;ip(k,0,d);i[h+20>>2]=e}m=i[b+48>>2];d=ho(32);i[h>>2]=d;i[h+4>>2]=19;i[h+8>>2]=-2147483616;g[d+19|0]=0;e=j[9845]|j[9846]<<8|(j[9847]<<16|j[9848]<<24);g[d+15|0]=e;g[d+16|0]=e>>>8;g[d+17|0]=e>>>16;g[d+18|0]=e>>>24;e=j[9842]|j[9843]<<8|(j[9844]<<16|j[9845]<<24);f=j[9838]|j[9839]<<8|(j[9840]<<16|j[9841]<<24);g[d+8|0]=f;g[d+9|0]=f>>>8;g[d+10|0]=f>>>16;g[d+11|0]=f>>>24;g[d+12|0]=e;g[d+13|0]=e>>>8;g[d+14|0]=e>>>16;g[d+15|0]=e>>>24;e=j[9834]|j[9835]<<8|(j[9836]<<16|j[9837]<<24);f=j[9830]|j[9831]<<8|(j[9832]<<16|j[9833]<<24);g[d|0]=f;g[d+1|0]=f>>>8;g[d+2|0]=f>>>16;g[d+3|0]=f>>>24;g[d+4|0]=e;g[d+5|0]=e>>>8;g[d+6|0]=e>>>16;g[d+7|0]=e>>>24;o=g[n+24|0];k:{l:{e=m+16|0;f=i[e>>2];if(!f){break l}d=e;while(1){q=i[f+16>>2]<(c|0);d=q?d:f;f=i[(q<<2)+f>>2];if(f){continue}break}if((d|0)==(e|0)|i[d+16>>2]>(c|0)){break l}d=d+20|0;if(!Sb(d,h)){break l}Tb(d,h,o,k);break k}Tb(m,h,o,k)}if(g[h+11|0]<=-1){bp(i[h>>2])}k=i[b+48>>2];b=ho(32);i[h>>2]=b;i[h+4>>2]=18;i[h+8>>2]=-2147483616;g[b+18|0]=0;d=j[9866]|j[9867]<<8;g[b+16|0]=d;g[b+17|0]=d>>>8;d=j[9862]|j[9863]<<8|(j[9864]<<16|j[9865]<<24);e=j[9858]|j[9859]<<8|(j[9860]<<16|j[9861]<<24);g[b+8|0]=e;g[b+9|0]=e>>>8;g[b+10|0]=e>>>16;g[b+11|0]=e>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[9854]|j[9855]<<8|(j[9856]<<16|j[9857]<<24);e=j[9850]|j[9851]<<8|(j[9852]<<16|j[9853]<<24);g[b|0]=e;g[b+1|0]=e>>>8;g[b+2|0]=e>>>16;g[b+3|0]=e>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;m:{n:{b=k+16|0;f=i[b>>2];if(!f){break n}d=b;while(1){e=i[f+16>>2]<(c|0);d=e?d:f;f=i[(e<<2)+f>>2];if(f){continue}break}if((b|0)==(d|0)|i[d+16>>2]>(c|0)){break n}b=d+20|0;if(!Sb(b,h)){break n}r=ak(b,h);break m}r=ak(k,h)}if(g[h+11|0]<=-1){bp(i[h>>2])}b=qa(a+40|0,l,i[h+16>>2],g[n+24|0],r);a=i[h+16>>2];if(a){i[h+20>>2]=a;bp(a)}f=0;if(b){break j}break b}f=0;if(!sa(a+40|0,n,l)){break b}}f=1}F=h+32|0;return f|0}Ho();x()}function Sm(a,b,c,d){var e=0,f=0,k=0,l=0,m=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;f=F-80|0;F=f;i[f+76>>2]=18485;z=f+55|0;w=f+56|0;a:{b:while(1){c:{if((t|0)<0){break c}if((e|0)>(2147483647-t|0)){i[4805]=61;t=-1;break c}t=e+t|0}d:{e:{f:{m=i[f+76>>2];e=m;k=j[e|0];if(k){while(1){g:{k=k&255;h:{if(!k){k=e;break h}if((k|0)!=37){break g}k=e;while(1){if(j[e+1|0]!=37){break h}l=e+2|0;i[f+76>>2]=l;k=k+1|0;p=j[e+2|0];e=l;if((p|0)==37){continue}break}}e=k-m|0;if(a){Tm(a,m,e)}if(e){continue b}k=f;l=!Mm(g[i[f+76>>2]+1|0]);e=i[f+76>>2];i:{if(!(l|j[e+2|0]!=36)){v=g[e+1|0]+ -48|0;x=1;e=e+3|0;break i}v=-1;e=e+1|0}i[k+76>>2]=e;q=0;u=g[e|0];l=u+ -32|0;j:{if(l>>>0>31){k=e;break j}k=e;l=1<>2]=k;q=l|q;u=g[e+1|0];l=u+ -32|0;if(l>>>0>=32){break j}e=k;l=1<>2];if(j[e+2|0]!=36){break m}i[((g[e+1|0]<<2)+d|0)+ -192>>2]=10;r=i[((g[e+1|0]<<3)+c|0)+ -384>>2];x=1;e=e+3|0;break l}if(x){break f}x=0;r=0;if(a){e=i[b>>2];i[b>>2]=e+4;r=i[e>>2]}e=i[f+76>>2]+1|0}i[l+76>>2]=e;if((r|0)>-1){break k}r=0-r|0;q=q|8192;break k}r=Um(f+76|0);if((r|0)<0){break f}e=i[f+76>>2]}p=-1;n:{if(j[e|0]!=46){break n}if(j[e+1|0]==42){o:{if(!Mm(g[e+2|0])){break o}e=i[f+76>>2];if(j[e+3|0]!=36){break o}i[((g[e+2|0]<<2)+d|0)+ -192>>2]=10;p=i[((g[e+2|0]<<3)+c|0)+ -384>>2];e=e+4|0;i[f+76>>2]=e;break n}if(x){break f}if(a){e=i[b>>2];i[b>>2]=e+4;p=i[e>>2]}else{p=0}e=i[f+76>>2]+2|0;i[f+76>>2]=e;break n}i[f+76>>2]=e+1;p=Um(f+76|0);e=i[f+76>>2]}k=0;while(1){y=k;s=-1;if(g[e|0]+ -65>>>0>57){break a}u=e+1|0;i[f+76>>2]=u;k=g[e|0];e=u;k=j[(k+o(y,58)|0)+16959|0];if(k+ -1>>>0<8){continue}break}p:{q:{if((k|0)!=19){if(!k){break a}if((v|0)>=0){i[(v<<2)+d>>2]=k;e=(v<<3)+c|0;k=i[e+4>>2];i[f+64>>2]=i[e>>2];i[f+68>>2]=k;break q}if(!a){break d}Vm(f- -64|0,k,b);u=i[f+76>>2];break p}if((v|0)>-1){break a}}e=0;if(!a){continue b}}l=q&-65537;k=q&8192?l:q;s=0;v=16996;q=w;r:{s:{t:{u:{v:{w:{x:{y:{z:{A:{B:{C:{D:{E:{F:{G:{e=g[u+ -1|0];e=y?(e&15)==3?e&-33:e:e;switch(e+ -88|0){case 11:break r;case 9:case 13:case 14:case 15:break s;case 27:break x;case 12:case 17:break A;case 23:break B;case 0:case 32:break C;case 24:break D;case 22:break E;case 29:break F;case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 10:case 16:case 18:case 19:case 20:case 21:case 25:case 26:case 28:case 30:case 31:break e;default:break G}}H:{switch(e+ -65|0){case 0:case 4:case 5:case 6:break s;case 2:break v;case 1:case 3:break e;default:break H}}if((e|0)==83){break w}break e}e=i[f+64>>2];m=i[f+68>>2];v=16996;break z}e=0;I:{switch(y&255){case 0:i[i[f+64>>2]>>2]=t;continue b;case 1:i[i[f+64>>2]>>2]=t;continue b;case 2:k=i[f+64>>2];i[k>>2]=t;i[k+4>>2]=t>>31;continue b;case 3:h[i[f+64>>2]>>1]=t;continue b;case 4:g[i[f+64>>2]]=t;continue b;case 6:i[i[f+64>>2]>>2]=t;continue b;case 7:break I;default:continue b}}k=i[f+64>>2];i[k>>2]=t;i[k+4>>2]=t>>31;continue b}p=p>>>0>8?p:8;k=k|8;e=120}m=Wm(i[f+64>>2],i[f+68>>2],w,e&32);if(!(k&8)|!(i[f+64>>2]|i[f+68>>2])){break y}v=(e>>>4|0)+16996|0;s=2;break y}m=Xm(i[f+64>>2],i[f+68>>2],w);if(!(k&8)){break y}e=w-m|0;p=(p|0)>(e|0)?p:e+1|0;break y}l=i[f+68>>2];m=l;e=i[f+64>>2];if((l|0)<-1?1:(l|0)<=-1){m=0-(m+(0>>0)|0)|0;e=0-e|0;i[f+64>>2]=e;i[f+68>>2]=m;s=1;v=16996;break z}if(k&2048){s=1;v=16997;break z}s=k&1;v=s?16998:16996}m=Ym(e,m,w)}k=(p|0)>-1?k&-65537:k;e=i[f+64>>2];l=i[f+68>>2];if(!(!!(e|l)|p)){p=0;m=w;break e}e=!(e|l)+(w-m|0)|0;p=(p|0)>(e|0)?p:e;break e}e=i[f+64>>2];m=e?e:17006;e=Nm(m,p);q=e?e:p+m|0;k=l;p=e?e-m|0:p;break e}l=i[f+64>>2];if(p){break u}e=0;Zm(a,32,r,0,k);break t}i[f+12>>2]=0;i[f+8>>2]=i[f+64>>2];i[f+64>>2]=f+8;p=-1;l=f+8|0}e=0;J:{while(1){m=i[l>>2];if(!m){break J}m=Pm(f+4|0,m);q=(m|0)<0;if(!(q|m>>>0>p-e>>>0)){l=l+4|0;e=e+m|0;if(p>>>0>e>>>0){continue}break J}break}s=-1;if(q){break a}}Zm(a,32,r,e,k);if(!e){e=0;break t}u=0;l=i[f+64>>2];while(1){m=i[l>>2];if(!m){break t}m=Pm(f+4|0,m);u=m+u|0;if((u|0)>(e|0)){break t}Tm(a,f+4|0,m);l=l+4|0;if(u>>>0>>0){continue}break}}Zm(a,32,r,e,k^8192);e=(r|0)>(e|0)?r:e;continue b}e=I[368](a,n[f+64>>3],r,p,k,e)|0;continue b}g[f+55|0]=i[f+64>>2];p=1;m=z;k=l;break e}l=e+1|0;i[f+76>>2]=l;k=j[e+1|0];e=l;continue}}s=t;if(a){break a}if(!x){break d}e=1;while(1){a=i[(e<<2)+d>>2];if(a){Vm((e<<3)+c|0,a,b);s=1;e=e+1|0;if((e|0)!=10){continue}break a}break}s=1;if(e>>>0>=10){break a}while(1){if(i[(e<<2)+d>>2]){break f}e=e+1|0;if((e|0)!=10){continue}break}break a}s=-1;break a}q=q-m|0;p=(p|0)<(q|0)?q:p;l=p+s|0;e=(r|0)<(l|0)?l:r;Zm(a,32,e,l,k);Tm(a,v,s);Zm(a,48,e,l,k^65536);Zm(a,48,p,q,0);Tm(a,m,q);Zm(a,32,e,l,k^8192);continue}break}s=0}F=f+80|0;return s}function Fn(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,j=0;e=F-16|0;F=e;i[e+12>>2]=a;a:{if(a>>>0<=211){a=i[Gn(17648,17840,e+12|0)>>2];break a}if(a>>>0>=4294967292){In();x()}f=(a>>>0)/210|0;d=o(f,210);i[e+8>>2]=a-d;g=Gn(17840,18032,e+8|0)-17840>>2;b:{while(1){a=i[(g<<2)+17840>>2]+d|0;d=5;b=h;c:{d:{while(1){h=b;if((d|0)==47){d=211;while(1){b=(a>>>0)/(d>>>0)|0;if(b>>>0>>0){break c}if((o(b,d)|0)==(a|0)){break d}b=d+10|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+12|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+16|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+18|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+22|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+28|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+30|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+36|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+40|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+42|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+46|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+52|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+58|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+60|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+66|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+70|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+72|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+78|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+82|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+88|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+96|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+100|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+102|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+106|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+108|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+112|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+120|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+126|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+130|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+136|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+138|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+142|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+148|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+150|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+156|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+162|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+166|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+168|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+172|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+178|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+180|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+186|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+190|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+192|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+196|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+198|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}if((o(b,c)|0)==(a|0)){break d}b=d+208|0;c=(a>>>0)/(b>>>0)|0;if(c>>>0>>0){break c}d=d+210|0;if((o(b,c)|0)!=(a|0)){continue}break}break d}b=i[(d<<2)+17648>>2];c=(a>>>0)/(b>>>0)|0;j=o(b,c);c=c>>>0>>0;if(!c){b=c?a:h;d=d+1|0;if((a|0)!=(j|0)){continue}}break}if((a|0)!=(j|0)|c){break b}}b=g+1|0;a=(b|0)==48;g=a?0:b;f=a+f|0;d=o(f,210);continue}break}i[e+12>>2]=a;break a}i[e+12>>2]=a;a=c?a:h}F=e+16|0;return a}function pc(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,H=0,I=0,J=0,K=0,L=0;e=F-32|0;F=e;g=i[a+8>>2];i[e+16>>2]=0;i[e+8>>2]=0;i[e+12>>2]=0;a:{if(g){if(g>>>0>=1073741824){break a}d=g<<2;f=ho(d);i[e+8>>2]=f;k=d+f|0;i[e+16>>2]=k;ip(f,0,d);i[e+12>>2]=k}h=i[a+1164>>2];d=i[h>>2];if(d){i[h+4>>2]=d;bp(d);i[h+8>>2]=0;i[h>>2]=0;i[h+4>>2]=0;g=i[a+8>>2];k=i[e+12>>2];f=i[e+8>>2]}i[h+4>>2]=k;i[h>>2]=f;i[h+8>>2]=i[e+16>>2];f=0;i[e+16>>2]=0;i[e+8>>2]=0;i[e+12>>2]=0;d=0;b:{if(g){if(g>>>0>=1073741824){break b}h=g<<2;d=ho(h);i[e+8>>2]=d;f=d+h|0;i[e+16>>2]=f;ip(d,0,h);i[e+12>>2]=f}n=i[a+1176>>2];h=i[n>>2];if(h){i[n+4>>2]=h;bp(h);i[n+8>>2]=0;i[n>>2]=0;i[n+4>>2]=0;d=i[e+8>>2];f=i[e+12>>2]}i[n+4>>2]=f;i[n>>2]=d;i[n+8>>2]=i[e+16>>2];B=i[b+4>>2];H=i[b+8>>2];C=i[c+4>>2];I=i[c+8>>2];q=i[c>>2];j=i[b>>2];d=e+24|0;i[d>>2]=0;i[d+4>>2]=0;f=e+16|0;i[f>>2]=0;i[f+4>>2]=0;i[e+8>>2]=0;i[e+12>>2]=0;n=q-j|0;vc(e+8|0);g=0;h=i[e+12>>2];if((h|0)!=i[f>>2]){d=i[d>>2]+i[e+28>>2]|0;f=(d>>>0)/113|0;g=i[h+(f<<2)>>2]+o(d-o(f,113)|0,36)|0}i[g+32>>2]=0;i[g+28>>2]=n;i[g+24>>2]=0;i[g+16>>2]=C;i[g+20>>2]=I;i[g+12>>2]=q;i[g+4>>2]=B;i[g+8>>2]=H;i[g>>2]=j;f=i[e+28>>2];g=f+1|0;i[e+28>>2]=g;if(g>>>0>=f>>>0){n=C;q=B;J=a+1068|0;K=a+1108|0;L=i[c+8>>2];u=i[b+8>>2];while(1){f=i[e+12>>2];j=g+ -1|0;m=j+i[e+24>>2]|0;d=(m>>>0)/113|0;d=i[f+(d<<2)>>2]+o(m-o(d,113)|0,36)|0;D=i[d+32>>2];k=i[d+24>>2];p=i[d+12>>2];h=i[d>>2];i[e+28>>2]=j;d=i[e+16>>2];f=d-f|0;if((f?o(f>>2,113)+ -1|0:0)-m>>>0>=226){bp(i[d+ -4>>2]);i[e+16>>2]=i[e+16>>2]+ -4}i[b>>2]=h;i[c>>2]=p;g=(k|0)==(i[a+8>>2]+ -1|0)?0:k+1|0;v=g<<2;z=o(D,12);m=z+i[a+1176>>2]|0;d=i[a>>2]-i[v+i[m>>2]>>2]|0;c:{d:{if(!d){break d}e:{f:{w=p-h|0;if(w>>>0<=2){j=i[a+1152>>2];i[j>>2]=g;f=1;d=i[a+8>>2];if(d>>>0>1){break f}break e}f=i[a+1164>>2];j=f+z|0;E=f;f=z+12|0;ra(E+f|0,i[j>>2],i[j+4>>2]);f=v+i[f+i[a+1164>>2]>>2]|0;G=i[f>>2]+(1<>2]=G;f=h;g:{if((p|0)==(f|0)){break g}t=i[q+24>>2];d=h;f=p;while(1){s=t+(o(d,u)<<2)|0;if(l[s+v>>2]>>0){d=d+1|0;if((f|0)!=(d|0)){continue}break g}while(1){f=f+ -1|0;if((f|0)==(d|0)){f=d;break g}y=i[n+24>>2]+(o(f,L)<<2)|0;if(l[y+v>>2]>=G>>>0){continue}break}A=0;if(u){while(1){k=A<<2;j=k+s|0;m=i[j>>2];E=j;j=k+y|0;i[E>>2]=i[j>>2];i[j>>2]=m;A=A+1|0;if((u|0)!=(A|0)){continue}break}}d=d+1|0;if((f|0)!=(d|0)){continue}break}}d=r(w);t=f-h|0;s=p-f|0;if((t|0)!=(s|0)){xc(K,t>>>0>>0)}y=D+1|0;m=d^31;d=w>>>1|0;h:{if(t>>>0>>0){if(!m){break h}j=d-t|0;d=1<>>1|0;k=k+1|0;if((m|0)!=(k|0)){continue}break}break h}if(!m){break h}j=d-s|0;d=1<>>1|0;k=k+1|0;if((m|0)!=(k|0)){continue}break}}k=i[a+1176>>2];m=k+z|0;j=i[m>>2];d=j+v|0;i[d>>2]=i[d>>2]+1;ra(k+o(y,12)|0,j,i[m+4>>2]);if((f|0)!=(h|0)){k=i[e+28>>2]+i[e+24>>2]|0;m=i[e+16>>2];d=i[e+12>>2];j=m-d|0;if((k|0)==((j?o(j>>2,113)+ -1|0:0)|0)){vc(e+8|0);k=i[e+24>>2]+i[e+28>>2]|0;m=i[e+16>>2];d=i[e+12>>2]}j=0;i:{if((d|0)==(m|0)){break i}j=d;d=(k>>>0)/113|0;j=i[j+(d<<2)>>2]+o(k-o(d,113)|0,36)|0}i[j+32>>2]=D;i[j+28>>2]=t;i[j+24>>2]=g;i[j+20>>2]=u;i[j+16>>2]=q;i[j+12>>2]=f;i[j+4>>2]=B;i[j+8>>2]=H;i[j>>2]=h;i[e+28>>2]=i[e+28>>2]+1}if((f|0)==(p|0)){break d}k=i[e+28>>2]+i[e+24>>2]|0;m=i[e+16>>2];d=i[e+12>>2];h=m-d|0;if((k|0)==((h?o(h>>2,113)+ -1|0:0)|0)){vc(e+8|0);k=i[e+24>>2]+i[e+28>>2]|0;m=i[e+16>>2];d=i[e+12>>2]}h=0;j:{if((d|0)==(m|0)){break j}h=d;d=(k>>>0)/113|0;h=i[h+(d<<2)>>2]+o(k-o(d,113)|0,36)|0}i[h+32>>2]=y;i[h+28>>2]=s;i[h+24>>2]=g;i[h+16>>2]=C;i[h+20>>2]=I;i[h+12>>2]=p;i[h+8>>2]=u;i[h+4>>2]=q;i[h>>2]=f;g=i[e+28>>2]+1|0;i[e+28>>2]=g;break c}while(1){g=(d+ -1|0)==(g|0)?0:g+1|0;i[j+(f<<2)>>2]=g;f=f+1|0;d=i[a+8>>2];if(f>>>0>>0){continue}break}}k=0;f=d;if(!w){break d}while(1){k:{if(!f){f=0;break k}p=i[q+24>>2]+(o(i[q+4>>2],h+k|0)<<2)|0;g=0;while(1){j=i[i[a+1152>>2]+(g<<2)>>2]<<2;f=i[a>>2]-i[j+i[m>>2]>>2]|0;if(f){yc(J,f,i[j+p>>2]);d=i[a+8>>2]}f=d;g=g+1|0;if(g>>>0>>0){continue}break}}k=k+1|0;if((w|0)!=(k|0)){continue}break}}g=i[e+28>>2]}if(g){continue}break}}i[e+28>>2]=0;a=i[e+16>>2];g=i[e+12>>2];d=a-g>>2;if(d>>>0>=3){while(1){bp(i[g>>2]);g=i[e+12>>2]+4|0;i[e+12>>2]=g;a=i[e+16>>2];d=a-g>>2;if(d>>>0>2){continue}break}}f=56;l:{switch(d+ -1|0){case 1:f=113;case 0:i[e+24>>2]=f;break;default:break l}}m:{if((a|0)==(g|0)){break m}while(1){bp(i[g>>2]);g=g+4|0;if((g|0)!=(a|0)){continue}break}b=i[e+16>>2];a=i[e+12>>2];if((b|0)==(a|0)){break m}i[e+16>>2]=b+(((b-a|0)+ -4>>>2^-1)<<2)}a=i[e+8>>2];if(a){bp(a)}F=e+32|0;return}Ho();x()}Ho();x()}function Qd(a,b,c,d){var e=0,f=0,l=0,o=0,r=p(0),t=0,w=0,x=0;a:{b:{if(!d){break b}c:{switch(i[a+28>>2]+ -1|0){case 0:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}e=(f<<3)+d|0;l=g[b|0];i[e>>2]=l;i[e+4>>2]=l>>31;b=b+1|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 1:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}e=(f<<3)+d|0;i[e>>2]=j[b|0];i[e+4>>2]=0;b=b+1|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 2:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}e=(f<<3)+d|0;l=h[b>>1];i[e>>2]=l;i[e+4>>2]=l>>31;b=b+2|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 3:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}e=(f<<3)+d|0;i[e>>2]=k[b>>1];i[e+4>>2]=0;b=b+2|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 4:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}e=(f<<3)+d|0;l=i[b>>2];i[e>>2]=l;i[e+4>>2]=l>>31;b=b+4|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 5:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}e=(f<<3)+d|0;i[e>>2]=i[b>>2];i[e+4>>2]=0;b=b+4|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 6:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}l=i[b+4>>2];e=(f<<3)+d|0;i[e>>2]=i[b>>2];i[e+4>>2]=l;b=b+8|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 7:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}e=i[b+4>>2];l=e;w=i[b>>2];if((e|0)<0?1:(e|0)<=0?w>>>0<0:0){return}e=(f<<3)+d|0;i[e>>2]=w;i[e+4>>2]=l;b=b+8|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 8:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;w=i[e+4>>2];f=0;while(1){if(w>>>0<=b>>>0){return}e=(f<<3)+d|0;o=e;r=m[b>>2];d:{if(p(q(r))=p(1)?r>p(0)?~~p(s(p(u(p(r*p(2.3283064365386963e-10)))),p(4294967296)))>>>0:~~p(v(p(p(r-p(~~r>>>0>>>0))*p(2.3283064365386963e-10))))>>>0:0;x=~~r>>>0;break d}l=-2147483648;x=0}i[o>>2]=x;i[e+4>>2]=l;b=b+4|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 9:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;w=i[e+4>>2];f=0;while(1){if(w>>>0<=b>>>0){return}e=(f<<3)+d|0;o=e;t=n[b>>3];e:{if(q(t)<0x8000000000000000){l=q(t)>=1?t>0?~~s(u(t*2.3283064365386963e-10),4294967295)>>>0:~~v((t- +(~~t>>>0>>>0))*2.3283064365386963e-10)>>>0:0;x=~~t>>>0;break e}l=-2147483648;x=0}i[o>>2]=x;i[e+4>>2]=l;b=b+8|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 10:break c;default:break b}}e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}e=(f<<3)+d|0;i[e>>2]=j[b|0];i[e+4>>2]=0;b=b+1|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}ip((e<<3)+d|0,0,c-e<<3)}return}ip((e<<3)+d|0,0,c-e<<3)}function rc(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0;e=F-32|0;F=e;g=i[a+8>>2];i[e+16>>2]=0;i[e+8>>2]=0;i[e+12>>2]=0;a:{if(g){if(g>>>0>=1073741824){break a}d=g<<2;f=ho(d);i[e+8>>2]=f;k=d+f|0;i[e+16>>2]=k;ip(f,0,d);i[e+12>>2]=k}h=i[a+128>>2];d=i[h>>2];if(d){i[h+4>>2]=d;bp(d);i[h+8>>2]=0;i[h>>2]=0;i[h+4>>2]=0;g=i[a+8>>2];k=i[e+12>>2];f=i[e+8>>2]}i[h+4>>2]=k;i[h>>2]=f;i[h+8>>2]=i[e+16>>2];f=0;i[e+16>>2]=0;i[e+8>>2]=0;i[e+12>>2]=0;d=0;b:{if(g){if(g>>>0>=1073741824){break b}h=g<<2;d=ho(h);i[e+8>>2]=d;f=d+h|0;i[e+16>>2]=f;ip(d,0,h);i[e+12>>2]=f}n=i[a+140>>2];h=i[n>>2];if(h){i[n+4>>2]=h;bp(h);i[n+8>>2]=0;i[n>>2]=0;i[n+4>>2]=0;d=i[e+8>>2];f=i[e+12>>2]}i[n+4>>2]=f;i[n>>2]=d;i[n+8>>2]=i[e+16>>2];B=i[b+4>>2];H=i[b+8>>2];C=i[c+4>>2];I=i[c+8>>2];q=i[c>>2];j=i[b>>2];d=e+24|0;i[d>>2]=0;i[d+4>>2]=0;f=e+16|0;i[f>>2]=0;i[f+4>>2]=0;i[e+8>>2]=0;i[e+12>>2]=0;n=q-j|0;vc(e+8|0);g=0;h=i[e+12>>2];if((h|0)!=i[f>>2]){d=i[d>>2]+i[e+28>>2]|0;f=(d>>>0)/113|0;g=i[h+(f<<2)>>2]+o(d-o(f,113)|0,36)|0}i[g+32>>2]=0;i[g+28>>2]=n;i[g+24>>2]=0;i[g+16>>2]=C;i[g+20>>2]=I;i[g+12>>2]=q;i[g+4>>2]=B;i[g+8>>2]=H;i[g>>2]=j;f=i[e+28>>2];g=f+1|0;i[e+28>>2]=g;if(g>>>0>=f>>>0){n=C;q=B;J=a+32|0;K=a+12|0;L=a+72|0;M=i[c+8>>2];u=i[b+8>>2];while(1){f=i[e+12>>2];j=g+ -1|0;m=j+i[e+24>>2]|0;d=(m>>>0)/113|0;d=i[f+(d<<2)>>2]+o(m-o(d,113)|0,36)|0;D=i[d+32>>2];k=i[d+24>>2];p=i[d+12>>2];h=i[d>>2];i[e+28>>2]=j;d=i[e+16>>2];f=d-f|0;if((f?o(f>>2,113)+ -1|0:0)-m>>>0>=226){bp(i[d+ -4>>2]);i[e+16>>2]=i[e+16>>2]+ -4}i[b>>2]=h;i[c>>2]=p;g=(k|0)==(i[a+8>>2]+ -1|0)?0:k+1|0;v=g<<2;z=o(D,12);m=z+i[a+140>>2]|0;d=i[a>>2]-i[v+i[m>>2]>>2]|0;c:{d:{if(!d){break d}e:{f:{w=p-h|0;if(w>>>0<=2){j=i[a+116>>2];i[j>>2]=g;f=1;d=i[a+8>>2];if(d>>>0>1){break f}break e}f=i[a+128>>2];j=f+z|0;E=f;f=z+12|0;ra(E+f|0,i[j>>2],i[j+4>>2]);f=v+i[f+i[a+128>>2]>>2]|0;G=i[f>>2]+(1<>2]=G;f=h;g:{if((p|0)==(f|0)){break g}t=i[q+24>>2];d=h;f=p;while(1){s=t+(o(d,u)<<2)|0;if(l[s+v>>2]>>0){d=d+1|0;if((f|0)!=(d|0)){continue}break g}while(1){f=f+ -1|0;if((f|0)==(d|0)){f=d;break g}y=i[n+24>>2]+(o(f,M)<<2)|0;if(l[v+y>>2]>=G>>>0){continue}break}A=0;if(u){while(1){k=A<<2;j=k+s|0;m=i[j>>2];E=j;j=k+y|0;i[E>>2]=i[j>>2];i[j>>2]=m;A=A+1|0;if((A|0)!=(u|0)){continue}break}}d=d+1|0;if((f|0)!=(d|0)){continue}break}}y=D+1|0;d=r(w)^31;t=f-h|0;s=p-f|0;if((t|0)!=(s|0)){xc(L,t>>>0>>0)}yc(K,d,(w>>>1|0)-(t>>>0>>0?t:s)|0);k=i[a+140>>2];m=k+z|0;j=i[m>>2];d=j+v|0;i[d>>2]=i[d>>2]+1;ra(k+o(y,12)|0,j,i[m+4>>2]);if((f|0)!=(h|0)){k=i[e+28>>2]+i[e+24>>2]|0;m=i[e+16>>2];d=i[e+12>>2];j=m-d|0;if((k|0)==((j?o(j>>2,113)+ -1|0:0)|0)){vc(e+8|0);k=i[e+24>>2]+i[e+28>>2]|0;m=i[e+16>>2];d=i[e+12>>2]}j=0;h:{if((d|0)==(m|0)){break h}j=d;d=(k>>>0)/113|0;j=i[j+(d<<2)>>2]+o(k-o(d,113)|0,36)|0}i[j+32>>2]=D;i[j+28>>2]=t;i[j+24>>2]=g;i[j+20>>2]=u;i[j+16>>2]=q;i[j+12>>2]=f;i[j+4>>2]=B;i[j+8>>2]=H;i[j>>2]=h;i[e+28>>2]=i[e+28>>2]+1}if((f|0)==(p|0)){break d}k=i[e+28>>2]+i[e+24>>2]|0;j=i[e+16>>2];d=i[e+12>>2];h=j-d|0;if((k|0)==((h?o(h>>2,113)+ -1|0:0)|0)){vc(e+8|0);k=i[e+24>>2]+i[e+28>>2]|0;j=i[e+16>>2];d=i[e+12>>2]}h=0;i:{if((d|0)==(j|0)){break i}h=d;d=(k>>>0)/113|0;h=i[h+(d<<2)>>2]+o(k-o(d,113)|0,36)|0}i[h+32>>2]=y;i[h+28>>2]=s;i[h+24>>2]=g;i[h+16>>2]=C;i[h+20>>2]=I;i[h+12>>2]=p;i[h+8>>2]=u;i[h+4>>2]=q;i[h>>2]=f;g=i[e+28>>2]+1|0;i[e+28>>2]=g;break c}while(1){g=(d+ -1|0)==(g|0)?0:g+1|0;i[j+(f<<2)>>2]=g;f=f+1|0;d=i[a+8>>2];if(f>>>0>>0){continue}break}}k=0;f=d;if(!w){break d}while(1){j:{if(!f){f=0;break j}p=i[q+24>>2]+(o(i[q+4>>2],h+k|0)<<2)|0;g=0;while(1){j=i[i[a+116>>2]+(g<<2)>>2]<<2;f=i[a>>2]-i[j+i[m>>2]>>2]|0;if(f){yc(J,f,i[j+p>>2]);d=i[a+8>>2]}g=g+1|0;f=d;if(g>>>0>>0){continue}break}}k=k+1|0;if((w|0)!=(k|0)){continue}break}}g=i[e+28>>2]}if(g){continue}break}}i[e+28>>2]=0;d=i[e+16>>2];g=i[e+12>>2];a=d-g>>2;if(a>>>0>=3){while(1){bp(i[g>>2]);g=i[e+12>>2]+4|0;i[e+12>>2]=g;d=i[e+16>>2];a=d-g>>2;if(a>>>0>2){continue}break}}f=56;k:{switch(a+ -1|0){case 1:f=113;case 0:i[e+24>>2]=f;break;default:break k}}l:{if((d|0)==(g|0)){break l}while(1){bp(i[g>>2]);g=g+4|0;if((g|0)!=(d|0)){continue}break}b=i[e+16>>2];a=i[e+12>>2];if((b|0)==(a|0)){break l}i[e+16>>2]=b+(((b-a|0)+ -4>>>2^-1)<<2)}a=i[e+8>>2];if(a){bp(a)}F=e+32|0;return}Ho();x()}Ho();x()}function qc(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0;e=F-32|0;F=e;g=i[a+8>>2];i[e+16>>2]=0;i[e+8>>2]=0;i[e+12>>2]=0;a:{if(g){if(g>>>0>=1073741824){break a}d=g<<2;f=ho(d);i[e+8>>2]=f;k=d+f|0;i[e+16>>2]=k;ip(f,0,d);i[e+12>>2]=k}h=i[a+140>>2];d=i[h>>2];if(d){i[h+4>>2]=d;bp(d);i[h+8>>2]=0;i[h>>2]=0;i[h+4>>2]=0;g=i[a+8>>2];k=i[e+12>>2];f=i[e+8>>2]}i[h+4>>2]=k;i[h>>2]=f;i[h+8>>2]=i[e+16>>2];f=0;i[e+16>>2]=0;i[e+8>>2]=0;i[e+12>>2]=0;d=0;b:{if(g){if(g>>>0>=1073741824){break b}h=g<<2;d=ho(h);i[e+8>>2]=d;f=d+h|0;i[e+16>>2]=f;ip(d,0,h);i[e+12>>2]=f}n=i[a+152>>2];h=i[n>>2];if(h){i[n+4>>2]=h;bp(h);i[n+8>>2]=0;i[n>>2]=0;i[n+4>>2]=0;d=i[e+8>>2];f=i[e+12>>2]}i[n+4>>2]=f;i[n>>2]=d;i[n+8>>2]=i[e+16>>2];B=i[b+4>>2];H=i[b+8>>2];C=i[c+4>>2];I=i[c+8>>2];q=i[c>>2];j=i[b>>2];d=e+24|0;i[d>>2]=0;i[d+4>>2]=0;f=e+16|0;i[f>>2]=0;i[f+4>>2]=0;i[e+8>>2]=0;i[e+12>>2]=0;n=q-j|0;vc(e+8|0);g=0;h=i[e+12>>2];if((h|0)!=i[f>>2]){d=i[d>>2]+i[e+28>>2]|0;f=(d>>>0)/113|0;g=i[h+(f<<2)>>2]+o(d-o(f,113)|0,36)|0}i[g+32>>2]=0;i[g+28>>2]=n;i[g+24>>2]=0;i[g+16>>2]=C;i[g+20>>2]=I;i[g+12>>2]=q;i[g+4>>2]=B;i[g+8>>2]=H;i[g>>2]=j;f=i[e+28>>2];g=f+1|0;i[e+28>>2]=g;if(g>>>0>=f>>>0){n=C;q=B;J=a+44|0;K=a+12|0;L=a+84|0;M=i[c+8>>2];u=i[b+8>>2];while(1){f=i[e+12>>2];j=g+ -1|0;m=j+i[e+24>>2]|0;d=(m>>>0)/113|0;d=i[f+(d<<2)>>2]+o(m-o(d,113)|0,36)|0;D=i[d+32>>2];k=i[d+24>>2];p=i[d+12>>2];h=i[d>>2];i[e+28>>2]=j;d=i[e+16>>2];f=d-f|0;if((f?o(f>>2,113)+ -1|0:0)-m>>>0>=226){bp(i[d+ -4>>2]);i[e+16>>2]=i[e+16>>2]+ -4}i[b>>2]=h;i[c>>2]=p;g=(k|0)==(i[a+8>>2]+ -1|0)?0:k+1|0;v=g<<2;z=o(D,12);m=z+i[a+152>>2]|0;d=i[a>>2]-i[v+i[m>>2]>>2]|0;c:{d:{if(!d){break d}e:{f:{w=p-h|0;if(w>>>0<=2){j=i[a+128>>2];i[j>>2]=g;f=1;d=i[a+8>>2];if(d>>>0>1){break f}break e}f=i[a+140>>2];j=f+z|0;E=f;f=z+12|0;ra(E+f|0,i[j>>2],i[j+4>>2]);f=v+i[f+i[a+140>>2]>>2]|0;G=i[f>>2]+(1<>2]=G;f=h;g:{if((p|0)==(f|0)){break g}t=i[q+24>>2];d=h;f=p;while(1){s=t+(o(d,u)<<2)|0;if(l[s+v>>2]>>0){d=d+1|0;if((f|0)!=(d|0)){continue}break g}while(1){f=f+ -1|0;if((f|0)==(d|0)){f=d;break g}y=i[n+24>>2]+(o(f,M)<<2)|0;if(l[v+y>>2]>=G>>>0){continue}break}A=0;if(u){while(1){k=A<<2;j=k+s|0;m=i[j>>2];E=j;j=k+y|0;i[E>>2]=i[j>>2];i[j>>2]=m;A=A+1|0;if((A|0)!=(u|0)){continue}break}}d=d+1|0;if((f|0)!=(d|0)){continue}break}}y=D+1|0;d=r(w)^31;t=f-h|0;s=p-f|0;if((t|0)!=(s|0)){xc(L,t>>>0>>0)}rf(K,d,(w>>>1|0)-(t>>>0>>0?t:s)|0);k=i[a+152>>2];m=k+z|0;j=i[m>>2];d=j+v|0;i[d>>2]=i[d>>2]+1;ra(k+o(y,12)|0,j,i[m+4>>2]);if((f|0)!=(h|0)){k=i[e+28>>2]+i[e+24>>2]|0;m=i[e+16>>2];d=i[e+12>>2];j=m-d|0;if((k|0)==((j?o(j>>2,113)+ -1|0:0)|0)){vc(e+8|0);k=i[e+24>>2]+i[e+28>>2]|0;m=i[e+16>>2];d=i[e+12>>2]}j=0;h:{if((d|0)==(m|0)){break h}j=d;d=(k>>>0)/113|0;j=i[j+(d<<2)>>2]+o(k-o(d,113)|0,36)|0}i[j+32>>2]=D;i[j+28>>2]=t;i[j+24>>2]=g;i[j+20>>2]=u;i[j+16>>2]=q;i[j+12>>2]=f;i[j+4>>2]=B;i[j+8>>2]=H;i[j>>2]=h;i[e+28>>2]=i[e+28>>2]+1}if((f|0)==(p|0)){break d}k=i[e+28>>2]+i[e+24>>2]|0;j=i[e+16>>2];d=i[e+12>>2];h=j-d|0;if((k|0)==((h?o(h>>2,113)+ -1|0:0)|0)){vc(e+8|0);k=i[e+24>>2]+i[e+28>>2]|0;j=i[e+16>>2];d=i[e+12>>2]}h=0;i:{if((d|0)==(j|0)){break i}h=d;d=(k>>>0)/113|0;h=i[h+(d<<2)>>2]+o(k-o(d,113)|0,36)|0}i[h+32>>2]=y;i[h+28>>2]=s;i[h+24>>2]=g;i[h+16>>2]=C;i[h+20>>2]=I;i[h+12>>2]=p;i[h+8>>2]=u;i[h+4>>2]=q;i[h>>2]=f;g=i[e+28>>2]+1|0;i[e+28>>2]=g;break c}while(1){g=(d+ -1|0)==(g|0)?0:g+1|0;i[j+(f<<2)>>2]=g;f=f+1|0;d=i[a+8>>2];if(f>>>0>>0){continue}break}}k=0;f=d;if(!w){break d}while(1){j:{if(!f){f=0;break j}p=i[q+24>>2]+(o(i[q+4>>2],h+k|0)<<2)|0;g=0;while(1){j=i[i[a+128>>2]+(g<<2)>>2]<<2;f=i[a>>2]-i[j+i[m>>2]>>2]|0;if(f){yc(J,f,i[j+p>>2]);d=i[a+8>>2]}g=g+1|0;f=d;if(g>>>0>>0){continue}break}}k=k+1|0;if((w|0)!=(k|0)){continue}break}}g=i[e+28>>2]}if(g){continue}break}}i[e+28>>2]=0;d=i[e+16>>2];g=i[e+12>>2];a=d-g>>2;if(a>>>0>=3){while(1){bp(i[g>>2]);g=i[e+12>>2]+4|0;i[e+12>>2]=g;d=i[e+16>>2];a=d-g>>2;if(a>>>0>2){continue}break}}f=56;k:{switch(a+ -1|0){case 1:f=113;case 0:i[e+24>>2]=f;break;default:break k}}l:{if((d|0)==(g|0)){break l}while(1){bp(i[g>>2]);g=g+4|0;if((g|0)!=(d|0)){continue}break}b=i[e+16>>2];a=i[e+12>>2];if((b|0)==(a|0)){break l}i[e+16>>2]=b+(((b-a|0)+ -4>>>2^-1)<<2)}a=i[e+8>>2];if(a){bp(a)}F=e+32|0;return}Ho();x()}Ho();x()}function kn(a,b,c,d,e){var f=0,g=0,h=0,k=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0;f=F-432|0;F=f;g=i[b+4>>2];a:{if(g>>>0>2]){i[b+4>>2]=g+1;h=j[g|0];break a}h=fn(b)}b:{c:{while(1){d:{if((h|0)!=48){if((h|0)!=46){break b}g=i[b+4>>2];if(g>>>0>=l[b+104>>2]){break d}i[b+4>>2]=g+1;h=j[g|0];break c}g=i[b+4>>2];if(g>>>0>2]){y=1;i[b+4>>2]=g+1;h=j[g|0]}else{y=1;h=fn(b)}continue}break}h=fn(b)}x=1;if((h|0)!=48){break b}while(1){q=q+ -1|0;g=u+ -1|0;if((g|0)!=-1){q=q+1|0}u=g;g=i[b+4>>2];e:{if(g>>>0>2]){i[b+4>>2]=g+1;h=j[g|0];break e}h=fn(b)}if((h|0)==48){continue}break}y=1}g=1073676288;while(1){f:{z=h|32;g:{h:{A=h+ -48|0;if(A>>>0<10){break h}if(z+ -97>>>0>5?(h|0)!=46:0){break f}if((h|0)!=46){break h}if(x){break f}x=1;u=o;q=k;break g}h=(h|0)>57?z+ -87|0:A;i:{if((k|0)<0?1:(k|0)<=0?o>>>0<=7:0){r=h+(r<<4)|0;break i}if((k|0)<0?1:(k|0)<=0?o>>>0<=28:0){An(f+48|0,h);yn(f+32|0,v,w,p,g,0,0,0,1073414144);v=i[f+32>>2];w=i[f+36>>2];p=i[f+40>>2];g=i[f+44>>2];yn(f+16|0,v,w,p,g,i[f+48>>2],i[f+52>>2],i[f+56>>2],i[f+60>>2]);tn(f,m,n,s,t,i[f+16>>2],i[f+20>>2],i[f+24>>2],i[f+28>>2]);s=i[f+8>>2];t=i[f+12>>2];m=i[f>>2];n=i[f+4>>2];break i}if(!h|D){break i}yn(f+80|0,v,w,p,g,0,0,0,1073610752);tn(f- -64|0,m,n,s,t,i[f+80>>2],i[f+84>>2],i[f+88>>2],i[f+92>>2]);s=i[f+72>>2];t=i[f+76>>2];D=1;m=i[f+64>>2];n=i[f+68>>2]}o=o+1|0;if(o>>>0<1){k=k+1|0}y=1}h=i[b+4>>2];if(h>>>0>2]){i[b+4>>2]=h+1;h=j[h|0]}else{h=fn(b)}continue}break}j:{k:{if(!y){if(!i[b+104>>2]){break k}c=i[b+4>>2];i[b+4>>2]=c+ -1;i[b+4>>2]=c+ -2;if(!x){break k}i[b+4>>2]=c+ -3;break k}if((k|0)<0?1:(k|0)<=0?o>>>0<=7:0){p=o;g=k;while(1){r=r<<4;p=p+1|0;if(p>>>0<1){g=g+1|0}if((p|0)!=8|g){continue}break}}l:{if((h&-33)==80){p=mn(b);h=H;g=h;if(p|(g|0)!=-2147483648){break l}p=0;g=0;if(!i[b+104>>2]){break l}i[b+4>>2]=i[b+4>>2]+ -1;break l}p=0;g=0;if(!i[b+104>>2]){break l}i[b+4>>2]=i[b+4>>2]+ -1}if(!r){xn(f+112|0,+(e|0)*0);m=i[f+112>>2];n=i[f+116>>2];c=i[f+120>>2];b=i[f+124>>2];break j}b=x?q:k;k=x?u:o;q=b<<2|k>>>30;b=p+(k<<2)|0;k=g+q|0;k=b>>>0

>>0?k+1|0:k;b=b+ -32|0;g=k+ -1|0;o=b;k=b>>>0<4294967264?g+1|0:g;if((k|0)>0?1:(k|0)>=0?b>>>0>0-d>>>0:0){i[4805]=68;An(f+160|0,e);yn(f+144|0,i[f+160>>2],i[f+164>>2],i[f+168>>2],i[f+172>>2],-1,-1,-1,2147418111);yn(f+128|0,i[f+144>>2],i[f+148>>2],i[f+152>>2],i[f+156>>2],-1,-1,-1,2147418111);m=i[f+128>>2];n=i[f+132>>2];c=i[f+136>>2];b=i[f+140>>2];break j}b=d+ -226|0;h=o>>>0>=b>>>0;b=b>>31;if((k|0)>(b|0)?1:(k|0)>=(b|0)?h:0){if((r|0)>-1){while(1){tn(f+416|0,m,n,s,t,0,0,0,-1073807360);h=rn(m,n,s,t,1073610752);g=(h|0)<0;b=g;tn(f+400|0,m,n,s,t,b?m:i[f+416>>2],b?n:i[f+420>>2],b?s:i[f+424>>2],b?t:i[f+428>>2]);k=k+ -1|0;b=o+ -1|0;if((b|0)!=-1){k=k+1|0}o=b;s=i[f+408>>2];t=i[f+412>>2];m=i[f+400>>2];n=i[f+404>>2];r=r<<1|(h|0)>-1;if((r|0)>-1){continue}break}}g=o;b=d;h=(g-b|0)+32|0;k=k-((b>>31)+(g>>>0>>0)|0)|0;b=h;k=b>>>0<32?k+1|0:k;c=((k|0)<0?1:(k|0)<=0?b>>>0>>0:0)?(b|0)>0?b:0:c;m:{if((c|0)>=113){An(f+384|0,e);u=i[f+392>>2];q=i[f+396>>2];v=i[f+384>>2];w=i[f+388>>2];d=0;b=0;break m}xn(f+352|0,fp(144-c|0));An(f+336|0,e);v=i[f+336>>2];w=i[f+340>>2];u=i[f+344>>2];q=i[f+348>>2];gn(f+368|0,i[f+352>>2],i[f+356>>2],i[f+360>>2],i[f+364>>2],v,w,u,q);B=i[f+376>>2];C=i[f+380>>2];d=i[f+372>>2];b=i[f+368>>2]}c=!(r&1)&((qn(m,n,s,t,0,0,0,0)|0)!=0&(c|0)<32);En(f+320|0,c+r|0);yn(f+304|0,v,w,u,q,i[f+320>>2],i[f+324>>2],i[f+328>>2],i[f+332>>2]);tn(f+272|0,i[f+304>>2],i[f+308>>2],i[f+312>>2],i[f+316>>2],b,d,B,C);yn(f+288|0,c?0:m,c?0:n,c?0:s,c?0:t,v,w,u,q);tn(f+256|0,i[f+288>>2],i[f+292>>2],i[f+296>>2],i[f+300>>2],i[f+272>>2],i[f+276>>2],i[f+280>>2],i[f+284>>2]);zn(f+240|0,i[f+256>>2],i[f+260>>2],i[f+264>>2],i[f+268>>2],b,d,B,C);e=i[f+240>>2];d=i[f+244>>2];c=i[f+248>>2];b=i[f+252>>2];if(!qn(e,d,c,b,0,0,0,0)){i[4805]=68}hn(f+224|0,e,d,c,b,o);m=i[f+224>>2];n=i[f+228>>2];c=i[f+232>>2];b=i[f+236>>2];break j}i[4805]=68;An(f+208|0,e);yn(f+192|0,i[f+208>>2],i[f+212>>2],i[f+216>>2],i[f+220>>2],0,0,0,65536);yn(f+176|0,i[f+192>>2],i[f+196>>2],i[f+200>>2],i[f+204>>2],0,0,0,65536);m=i[f+176>>2];n=i[f+180>>2];c=i[f+184>>2];b=i[f+188>>2];break j}xn(f+96|0,+(e|0)*0);m=i[f+96>>2];n=i[f+100>>2];c=i[f+104>>2];b=i[f+108>>2]}i[a>>2]=m;i[a+4>>2]=n;i[a+8>>2]=c;i[a+12>>2]=b;F=f+432|0}function ke(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;l=F-48|0;F=l;i[a+68>>2]=f;q=a+112|0;pf(q);d=i[a+56>>2];f=i[d>>2];j=i[d+4>>2];i[l+40>>2]=0;i[l+32>>2]=0;i[l+36>>2]=0;i[l+16>>2]=0;i[l+20>>2]=0;i[l+8>>2]=0;i[l+12>>2]=0;i[l>>2]=0;i[l+4>>2]=0;a:{g=j-f|0;if((g|0)<1){break a}e=i[d>>2];if((e|0)!=i[d+4>>2]){t=a+60|0;d=f-j|0;d=((d|0)>(g|0)?d:g)>>>2|0;u=d>>>0>1?d:1;v=c+4|0;while(1){le(t,i[(n<<2)+e>>2],l+32|0);f=i[l+36>>2];g=f>>31;j=i[l+32>>2];k=j>>31;e=i[l+40>>2];h=e>>31;d=0;h=h^e+h;g=h+((g^f+g)+(k^j+k)|0)|0;if(g>>>0>>0){d=1}k=g;b:{if(!(d|g)){m=i[a+108>>2];g=m;break b}m=i[a+108>>2];h=m;o=h>>31;g=vp(up(h,o,j,j>>31),H,k,d);f=vp(up(h,o,f,f>>31),H,k,d);if((e|0)>=0){e=(((g|0)<0?g:0-g|0)+m|0)+((f|0)<0?f:0-f|0)|0;break b}d=g>>31;e=(d+g^d)-m|0;d=f>>31;e=e+(d+f^d)|0}c:{if((g|0)>=0){j=f+m|0;h=e+m|0;break c}d:{if((f|0)<=-1){d=e>>31;j=d+e^d;break d}j=i[a+100>>2]+((e|0)<0?e:0-e|0)|0}if((e|0)<=-1){d=f>>31;h=d+f^d;break c}h=i[a+100>>2]+((f|0)<0?f:0-f|0)|0}e:{if(!(h|j)){d=i[a+100>>2];h=d;k=d;break e}d=i[a+100>>2];f:{g:{h:{i:{if(!j){if((d|0)==(h|0)){d=h;k=d;break e}k=0;if(d|h){break i}h=0;break e}if((d|0)!=(j|0)|h){break h}d=j;h=d;k=d;break e}if((m|0)<(h|0)){h=(m<<1)-h|0;break e}if(d){break f}break g}if((d|0)!=(j|0)){break f}}d=j;if((m|0)<=(h|0)){break f}h=(m<<1)-h|0;k=j;break e}if(!((d|0)!=(h|0)|(m|0)<=(j|0))){k=(m<<1)-j|0;break e}if(h){k=j;break e}h=0;if((m|0)>=(j|0)){k=j;break e}k=(m<<1)-j|0}i[l+28>>2]=h;i[l+24>>2]=k;j=0-e|0;i[l+40>>2]=j;k=0-f|0;i[l+36>>2]=k;i[l+32>>2]=0-g;j:{k:{l:{if((g|0)<=0){g=m-f|0;e=m-e|0;break l}g=e>>31;h=((e|0)<0?e:j)+d|0;j=(f|0)>0;g=j?g+e^g:h;if((e|0)>=1){e=f>>31;e=e+f^e;break l}e=(j?k:f)+d|0}if(!(e|g)){break k}m:{n:{o:{if(!g){if((d|0)==(e|0)){break k}f=0;if(d|e){break o}e=0;break j}if((d|0)==(g|0)?!e:0){break k}if((d|0)==(g|0)){break n}break m}if((m|0)<(e|0)){e=(m<<1)-e|0;break j}if(d){break m}}d=g;if((m|0)<=(e|0)){break m}e=(m<<1)-e|0;f=g;break j}if(!((d|0)!=(e|0)|(m|0)<=(g|0))){f=(m<<1)-g|0;break j}if(e){f=g;break j}e=0;if((m|0)>=(g|0)){f=g;break j}f=(m<<1)-g|0;break j}e=d;f=d}i[l+20>>2]=e;i[l+16>>2]=f;g=i[a+8>>2];p:{if((g|0)<1){break p}r=(n<<3)+b|0;m=l+24|0;j=0;while(1){f=0;q:{if((g|0)<=0){e=i[a+32>>2];break q}while(1){d=f<<2;g=i[d+m>>2];k=i[a+16>>2];r:{if((g|0)>(k|0)){e=i[a+32>>2];i[d+e>>2]=k;break r}e=i[a+32>>2];d=d+e|0;k=i[a+12>>2];if((g|0)<(k|0)){i[d>>2]=k;break r}i[d>>2]=g}f=f+1|0;g=i[a+8>>2];if((f|0)<(g|0)){continue}break}}f=j<<2;d=f+(l+8|0)|0;f=i[f+r>>2]-i[e+f>>2]|0;i[d>>2]=f;k=i[a+28>>2];h=f+i[a+20>>2]|0;s:{t:{if((f|0)<(k|0)){break t}if((f|0)<=i[a+24>>2]){break s}h=f-i[a+20>>2]|0}i[d>>2]=h}m=e;j=j+1|0;if((j|0)<(g|0)){continue}break}p=0;o=l+16|0;j=e;f=e;if((g|0)<1){break p}while(1){d=0;if((g|0)>=1){while(1){f=e;h=f;s=d<<2;k=i[s+o>>2];g=i[a+16>>2];if((k|0)<=(g|0)){g=i[a+12>>2];f=(k|0)<(g|0);g=f?g:k;h=m;f=f?h:j}i[f+s>>2]=g;m=h;j=f;d=d+1|0;g=i[a+8>>2];if((d|0)<(g|0)){continue}break}k=i[a+28>>2]}h=p<<2;d=h+l|0;h=i[h+r>>2]-i[f+h>>2]|0;i[d>>2]=h;u:{if((h|0)<(k|0)){h=h+i[a+20>>2]|0}else{if((h|0)<=i[a+24>>2]){break u}h=h-i[a+20>>2]|0}i[d>>2]=h}o=f;p=p+1|0;if((p|0)<(g|0)){continue}break}}j=i[a+108>>2];e=i[l+8>>2];v:{if((j|0)<(e|0)){e=e-i[a+96>>2]|0;break v}if((e|0)>=(0-j|0)){break v}e=i[a+96>>2]+e|0}i[l+8>>2]=e;g=i[l+12>>2];w:{if((j|0)<(g|0)){g=g-i[a+96>>2]|0;break w}if((g|0)>=(0-j|0)){break w}g=i[a+96>>2]+g|0}i[l+12>>2]=g;d=i[l>>2];x:{if((j|0)<(d|0)){d=d-i[a+96>>2]|0;break x}if((d|0)>=(0-j|0)){break x}d=i[a+96>>2]+d|0}i[l>>2]=d;f=i[l+4>>2];y:{if((j|0)<(f|0)){f=f-i[a+96>>2]|0;break y}if((f|0)>=(0-j|0)){break y}f=i[a+96>>2]+f|0}j=n<<1;i[l+4>>2]=f;k=g>>31;k=k+g^k;h=e>>31;h=h+e^h;m=h>>>0>(k^2147483647)>>>0?2147483647:k+h|0;k=f>>31;k=k+f^k;h=d>>31;h=h+d^h;z:{if(m>>>0<(h>>>0>(k^2147483647)>>>0?2147483647:k+h|0)>>>0){qf(q,0);d=(j<<2)+c|0;if((e|0)<=-1){e=i[a+96>>2]+e|0}i[d>>2]=e;if((g|0)>-1){f=g;break z}f=i[a+96>>2]+g|0;break z}qf(q,1);e=(j<<2)+c|0;if((d|0)<=-1){d=i[a+96>>2]+d|0}i[e>>2]=d;if((f|0)>-1){break z}f=i[a+96>>2]+f|0}i[(j<<2)+v>>2]=f;n=n+1|0;if((u|0)==(n|0)){break a}d=i[a+56>>2];e=i[d>>2];if(i[d+4>>2]-e>>2>>>0>n>>>0){continue}break}}Io();x()}F=l+48|0;return 1}function Xd(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;l=F-48|0;F=l;i[a+68>>2]=f;q=a+112|0;pf(q);d=i[a+56>>2];f=i[d>>2];j=i[d+4>>2];i[l+40>>2]=0;i[l+32>>2]=0;i[l+36>>2]=0;i[l+16>>2]=0;i[l+20>>2]=0;i[l+8>>2]=0;i[l+12>>2]=0;i[l>>2]=0;i[l+4>>2]=0;a:{g=j-f|0;if((g|0)<1){break a}e=i[d>>2];if((e|0)!=i[d+4>>2]){t=a+60|0;d=f-j|0;d=((d|0)>(g|0)?d:g)>>>2|0;u=d>>>0>1?d:1;v=c+4|0;while(1){Yd(t,i[(n<<2)+e>>2],l+32|0);f=i[l+36>>2];g=f>>31;j=i[l+32>>2];k=j>>31;e=i[l+40>>2];h=e>>31;d=0;h=h^e+h;g=h+((g^f+g)+(k^j+k)|0)|0;if(g>>>0>>0){d=1}k=g;b:{if(!(d|g)){m=i[a+108>>2];g=m;break b}m=i[a+108>>2];h=m;o=h>>31;g=vp(up(h,o,j,j>>31),H,k,d);f=vp(up(h,o,f,f>>31),H,k,d);if((e|0)>=0){e=(((g|0)<0?g:0-g|0)+m|0)+((f|0)<0?f:0-f|0)|0;break b}d=g>>31;e=(d+g^d)-m|0;d=f>>31;e=e+(d+f^d)|0}c:{if((g|0)>=0){j=f+m|0;h=e+m|0;break c}d:{if((f|0)<=-1){d=e>>31;j=d+e^d;break d}j=i[a+100>>2]+((e|0)<0?e:0-e|0)|0}if((e|0)<=-1){d=f>>31;h=d+f^d;break c}h=i[a+100>>2]+((f|0)<0?f:0-f|0)|0}e:{if(!(h|j)){d=i[a+100>>2];h=d;k=d;break e}d=i[a+100>>2];f:{g:{h:{i:{if(!j){if((d|0)==(h|0)){d=h;k=d;break e}k=0;if(d|h){break i}h=0;break e}if((d|0)!=(j|0)|h){break h}d=j;h=d;k=d;break e}if((m|0)<(h|0)){h=(m<<1)-h|0;break e}if(d){break f}break g}if((d|0)!=(j|0)){break f}}d=j;if((m|0)<=(h|0)){break f}h=(m<<1)-h|0;k=j;break e}if(!((d|0)!=(h|0)|(m|0)<=(j|0))){k=(m<<1)-j|0;break e}if(h){k=j;break e}h=0;if((m|0)>=(j|0)){k=j;break e}k=(m<<1)-j|0}i[l+28>>2]=h;i[l+24>>2]=k;j=0-e|0;i[l+40>>2]=j;k=0-f|0;i[l+36>>2]=k;i[l+32>>2]=0-g;j:{k:{l:{if((g|0)<=0){g=m-f|0;e=m-e|0;break l}g=e>>31;h=((e|0)<0?e:j)+d|0;j=(f|0)>0;g=j?g+e^g:h;if((e|0)>=1){e=f>>31;e=e+f^e;break l}e=(j?k:f)+d|0}if(!(e|g)){break k}m:{n:{o:{if(!g){if((d|0)==(e|0)){break k}f=0;if(d|e){break o}e=0;break j}if((d|0)==(g|0)?!e:0){break k}if((d|0)==(g|0)){break n}break m}if((m|0)<(e|0)){e=(m<<1)-e|0;break j}if(d){break m}}d=g;if((m|0)<=(e|0)){break m}e=(m<<1)-e|0;f=g;break j}if(!((d|0)!=(e|0)|(m|0)<=(g|0))){f=(m<<1)-g|0;break j}if(e){f=g;break j}e=0;if((m|0)>=(g|0)){f=g;break j}f=(m<<1)-g|0;break j}e=d;f=d}i[l+20>>2]=e;i[l+16>>2]=f;g=i[a+8>>2];p:{if((g|0)<1){break p}r=(n<<3)+b|0;m=l+24|0;j=0;while(1){f=0;q:{if((g|0)<=0){e=i[a+32>>2];break q}while(1){d=f<<2;g=i[d+m>>2];k=i[a+16>>2];r:{if((g|0)>(k|0)){e=i[a+32>>2];i[d+e>>2]=k;break r}e=i[a+32>>2];d=d+e|0;k=i[a+12>>2];if((g|0)<(k|0)){i[d>>2]=k;break r}i[d>>2]=g}f=f+1|0;g=i[a+8>>2];if((f|0)<(g|0)){continue}break}}f=j<<2;d=f+(l+8|0)|0;f=i[f+r>>2]-i[e+f>>2]|0;i[d>>2]=f;k=i[a+28>>2];h=f+i[a+20>>2]|0;s:{t:{if((f|0)<(k|0)){break t}if((f|0)<=i[a+24>>2]){break s}h=f-i[a+20>>2]|0}i[d>>2]=h}m=e;j=j+1|0;if((j|0)<(g|0)){continue}break}p=0;o=l+16|0;j=e;f=e;if((g|0)<1){break p}while(1){d=0;if((g|0)>=1){while(1){f=e;h=f;s=d<<2;k=i[s+o>>2];g=i[a+16>>2];if((k|0)<=(g|0)){g=i[a+12>>2];f=(k|0)<(g|0);g=f?g:k;h=m;f=f?h:j}i[f+s>>2]=g;m=h;j=f;d=d+1|0;g=i[a+8>>2];if((d|0)<(g|0)){continue}break}k=i[a+28>>2]}h=p<<2;d=h+l|0;h=i[h+r>>2]-i[f+h>>2]|0;i[d>>2]=h;u:{if((h|0)<(k|0)){h=h+i[a+20>>2]|0}else{if((h|0)<=i[a+24>>2]){break u}h=h-i[a+20>>2]|0}i[d>>2]=h}o=f;p=p+1|0;if((p|0)<(g|0)){continue}break}}j=i[a+108>>2];e=i[l+8>>2];v:{if((j|0)<(e|0)){e=e-i[a+96>>2]|0;break v}if((e|0)>=(0-j|0)){break v}e=i[a+96>>2]+e|0}i[l+8>>2]=e;g=i[l+12>>2];w:{if((j|0)<(g|0)){g=g-i[a+96>>2]|0;break w}if((g|0)>=(0-j|0)){break w}g=i[a+96>>2]+g|0}i[l+12>>2]=g;d=i[l>>2];x:{if((j|0)<(d|0)){d=d-i[a+96>>2]|0;break x}if((d|0)>=(0-j|0)){break x}d=i[a+96>>2]+d|0}i[l>>2]=d;f=i[l+4>>2];y:{if((j|0)<(f|0)){f=f-i[a+96>>2]|0;break y}if((f|0)>=(0-j|0)){break y}f=i[a+96>>2]+f|0}j=n<<1;i[l+4>>2]=f;k=g>>31;k=k+g^k;h=e>>31;h=h+e^h;m=h>>>0>(k^2147483647)>>>0?2147483647:k+h|0;k=f>>31;k=k+f^k;h=d>>31;h=h+d^h;z:{if(m>>>0<(h>>>0>(k^2147483647)>>>0?2147483647:k+h|0)>>>0){qf(q,0);d=(j<<2)+c|0;if((e|0)<=-1){e=i[a+96>>2]+e|0}i[d>>2]=e;if((g|0)>-1){f=g;break z}f=i[a+96>>2]+g|0;break z}qf(q,1);e=(j<<2)+c|0;if((d|0)<=-1){d=i[a+96>>2]+d|0}i[e>>2]=d;if((f|0)>-1){break z}f=i[a+96>>2]+f|0}i[(j<<2)+v>>2]=f;n=n+1|0;if((u|0)==(n|0)){break a}d=i[a+56>>2];e=i[d>>2];if(i[d+4>>2]-e>>2>>>0>n>>>0){continue}break}}Io();x()}F=l+48|0;return 1}function Xb(a,b,c,d){var e=0,f=0,l=0,o=0,r=0,s=p(0),t=0;a:{b:{c:{if(!d){break c}d:{switch(i[a+28>>2]+ -1|0){case 0:o=1;e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;r=i[e+4>>2];f=0;while(1){if(r>>>0<=b>>>0){break b}i[(f<<2)+d>>2]=g[b|0];b=b+1|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break c}break a;case 1:o=1;e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;r=i[e+4>>2];f=0;while(1){if(r>>>0<=b>>>0){break b}i[(f<<2)+d>>2]=j[b|0];b=b+1|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break c}break a;case 2:o=1;e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;r=i[e+4>>2];f=0;while(1){if(r>>>0<=b>>>0){break b}i[(f<<2)+d>>2]=h[b>>1];b=b+2|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break c}break a;case 3:o=1;e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;r=i[e+4>>2];f=0;while(1){if(r>>>0<=b>>>0){break b}i[(f<<2)+d>>2]=k[b>>1];b=b+2|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break c}break a;case 4:o=1;e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;r=i[e+4>>2];f=0;while(1){if(r>>>0<=b>>>0){break b}i[(f<<2)+d>>2]=i[b>>2];b=b+4|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break c}break a;case 5:o=g[a+24|0];if(((o|0)>(c|0)?c:o)<<24>>24>=1){e=i[a>>2];l=i[e>>2];o=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+o|0;e=i[e+4>>2];while(1){if(e>>>0<=b>>>0){break b}o=0;r=i[b>>2];if((r|0)<0){break c}i[(f<<2)+d>>2]=r;b=b+4|0;f=f+1|0;o=g[a+24|0];if((f|0)<((o|0)>(c|0)?c:o)<<24>>24){continue}break}}if((o|0)>=(c|0)){return 1}ip((o<<2)+d|0,0,c-o<<2);return 1;case 6:o=1;e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;r=i[e+4>>2];f=0;while(1){if(r>>>0<=b>>>0){break b}e=i[b+4>>2];l=i[b>>2];if(l+ -2147483648>>>0<2147483648){e=e+1|0}if(e>>>0>0){break b}i[(f<<2)+d>>2]=l;b=b+8|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break c}break a;case 7:o=1;e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;r=i[e+4>>2];f=0;while(1){if(r>>>0<=b>>>0){break b}e=i[b+4>>2];l=i[b>>2];if(!e&l>>>0>2147483647|e>>>0>0){break b}i[(f<<2)+d>>2]=l;b=b+8|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break c}break a;case 8:o=1;e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;r=i[e+4>>2];f=0;while(1){if(r>>>0<=b>>>0){break b}e=(f<<2)+d|0;s=m[b>>2];e:{if(p(q(s))>2]=l;b=b+4|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break c}break a;case 9:o=1;e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;r=i[e+4>>2];f=0;while(1){if(r>>>0<=b>>>0){break b}e=(f<<2)+d|0;t=n[b>>3];f:{if(q(t)<2147483648){l=~~t;break f}l=-2147483648}i[e>>2]=l;b=b+8|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break c}break a;case 10:break d;default:break c}}o=1;e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];l=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=l+f|0;r=i[e+4>>2];f=0;while(1){if(r>>>0<=b>>>0){break b}i[(f<<2)+d>>2]=j[b|0];b=b+1|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break c}ip((e<<2)+d|0,0,c-e<<2)}return o}return 0}ip((e<<2)+d|0,0,c-e<<2);return 1}function $b(a,b,c,d){var e=0,f=0,o=0,q=0,r=p(0),s=0;a:{b:{if(!d){break b}c:{switch(i[a+28>>2]+ -1|0){case 0:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];o=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=o+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}i[(f<<2)+d>>2]=g[b|0];b=b+1|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 1:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];o=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=o+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}i[(f<<2)+d>>2]=j[b|0];b=b+1|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 2:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];o=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=o+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}i[(f<<2)+d>>2]=h[b>>1];b=b+2|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 3:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];o=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=o+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}i[(f<<2)+d>>2]=k[b>>1];b=b+2|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 4:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];o=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=o+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}i[(f<<2)+d>>2]=i[b>>2];b=b+4|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 5:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];o=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=o+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}i[(f<<2)+d>>2]=i[b>>2];b=b+4|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 6:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];o=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=o+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}e=i[b>>2];if(l[b+4>>2]>0){return}i[(f<<2)+d>>2]=e;b=b+8|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 7:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];o=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=o+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}e=i[b>>2];if(l[b+4>>2]>0){return}i[(f<<2)+d>>2]=e;b=b+8|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 8:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];o=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=o+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}q=(f<<2)+d|0;r=m[b>>2];d:{if(r=p(0)){e=~~r>>>0;break d}e=0}i[q>>2]=e;b=b+4|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 9:e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];o=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=o+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}q=(f<<2)+d|0;s=n[b>>3];e:{if(s<4294967296&s>=0){e=~~s>>>0;break e}e=0}i[q>>2]=e;b=b+8|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}break a;case 10:break c;default:break b}}e=g[a+24|0];if(((e|0)>(c|0)?c:e)<<24>>24>=1){e=i[a>>2];o=i[e>>2];f=i[a+48>>2]+up(i[a+40>>2],i[a+44>>2],b,0)|0;b=o+f|0;o=i[e+4>>2];f=0;while(1){if(o>>>0<=b>>>0){return}i[(f<<2)+d>>2]=j[b|0];b=b+1|0;f=f+1|0;e=g[a+24|0];if((f|0)<((e|0)>(c|0)?c:e)<<24>>24){continue}break}}if((e|0)>=(c|0)){break b}ip((e<<2)+d|0,0,c-e<<2)}return}ip((e<<2)+d|0,0,c-e<<2)}function Em(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0,k=0,l=0,n=0,o=0,q=0,r=0,s=0,t=p(0),u=0,w=0,y=0,z=0,A=0,B=0,C=p(0),D=0,E=0;d=F+ -64|0;F=d;h=i[a+80>>2];i[d+40>>2]=0;i[d+44>>2]=0;i[d+56>>2]=a;i[d+48>>2]=a;i[d+32>>2]=0;i[d+36>>2]=0;i[d+52>>2]=1065353216;Fm(d+32|0,h);h=i[a+80>>2];i[d+24>>2]=0;i[d+16>>2]=0;i[d+20>>2]=0;a:{if(h){if(h>>>0>=1073741824){break a}c=h<<2;b=ho(c);i[d+16>>2]=b;f=c+b|0;i[d+24>>2]=f;ip(b,0,c);i[d+20>>2]=f}i[d+8>>2]=0;i[d>>2]=0;i[d+4>>2]=0;b:{c:{d:{if(!h){break d}D=d+40|0;h=0;while(1){c=0;b=i[d+48>>2];s=i[b+12>>2];q=i[b+8>>2];r=s-q|0;u=(r|0)<1;if(!u){b=q-s|0;b=((b|0)>(r|0)?b:r)>>>2|0;f=b>>>0>1?b:1;e=0;while(1){b=h;k=i[q+(e<<2)>>2];if(!j[k+84|0]){b=i[i[k+68>>2]+(h<<2)>>2]}c=b<<2^c<<1;e=e+1|0;if((f|0)!=(e|0)){continue}break}}e:{f:{g=i[d+36>>2];if(!g){break f}f=i[d+32>>2];z=xp(g)>>>0>1;b=g+ -1&c;g:{if(!z){break g}b=c;if(c>>>0>>0){break g}b=(c>>>0)%(g>>>0)|0}f=i[f+(b<<2)>>2];if(!f){break f}o=i[f>>2];if(!o){break f}n=b;E=g+ -1|0;y=i[d+56>>2];h:{while(1){i:{j:{e=i[o+4>>2];if((c|0)!=(e|0)){k:{if(!z){e=e&E;break k}if(e>>>0>>0){break k}e=(e>>>0)%(g>>>0)|0}if((e|0)==(n|0)){break j}break f}A=i[y+8>>2];b=i[y+12>>2]-A|0;if((b|0)<1){break h}f=i[o+8>>2];l=b>>>2|0;l=l>>>0>1?l:1;e=0;while(1){b=f;k=h;B=i[A+(e<<2)>>2];if(!j[B+84|0]){b=i[B+68>>2];k=i[b+(h<<2)>>2];b=i[b+(f<<2)>>2]}if((b|0)!=(k|0)){break j}e=e+1|0;if((l|0)!=(e|0)){continue}break}break i}o=i[o>>2];if(o){continue}break f}break}if(!o){break f}}i[i[d+16>>2]+(h<<2)>>2]=i[o+12>>2];break e}l:{if(u){c=0;break l}c=q-s|0;c=((c|0)>(r|0)?c:r)>>>2|0;f=c>>>0>1?c:1;c=0;e=0;while(1){b=h;k=i[q+(e<<2)>>2];if(!j[k+84|0]){b=i[i[k+68>>2]+(h<<2)>>2]}c=b<<2^c<<1;e=e+1|0;if((f|0)!=(e|0)){continue}break}}m:{n:{if(!g){break n}b=i[d+32>>2];q=xp(g)>>>0>1;l=g+ -1&c;o:{if(!q){break o}l=c;if(c>>>0>>0){break o}l=(c>>>0)%(g>>>0)|0}b=i[b+(l<<2)>>2];if(!b){break n}n=i[b>>2];if(!n){break n}r=g+ -1|0;o=i[d+56>>2];while(1){e=i[n+4>>2];if((c|0)!=(e|0)){p:{if(!q){e=e&r;break p}if(e>>>0>>0){break p}e=(e>>>0)%(g>>>0)|0}if((e|0)!=(l|0)){break n}}s=i[o+8>>2];b=i[o+12>>2]-s|0;if((b|0)<1){break m}f=i[n+8>>2];b=b>>>2|0;y=b>>>0>1?b:1;e=0;while(1){b=f;k=h;u=i[s+(e<<2)>>2];if(!j[u+84|0]){b=i[u+68>>2];k=i[b+(h<<2)>>2];b=i[b+(f<<2)>>2]}if((b|0)==(k|0)){e=e+1|0;if((y|0)==(e|0)){break m}continue}break}n=i[n>>2];if(n){continue}break}}b=ho(16);i[b+12>>2]=w;i[b+8>>2]=h;i[b+4>>2]=c;i[b>>2]=0;t=m[d+52>>2];C=p(i[d+44>>2]+1>>>0);q:{if(p(t*p(g>>>0))>>0<3|g<<1;k=d+32|0;t=p(v(p(C/t)));r:{if(t=p(0)){l=~~t>>>0;break r}l=0}Fm(k,f>>>0>>0?l:f);g=i[d+36>>2];l=g+ -1|0;if(!(g&l)){l=c&l;break q}if(c>>>0>>0){l=c;break q}l=(c>>>0)%(g>>>0)|0}f=i[d+32>>2]+(l<<2)|0;c=i[f>>2];s:{t:{if(!c){i[b>>2]=i[d+40>>2];i[d+40>>2]=b;i[f>>2]=D;c=i[b>>2];if(!c){break s}c=i[c+4>>2];f=g+ -1|0;u:{if(!(f&g)){c=c&f;break u}if(c>>>0>>0){break u}c=(c>>>0)%(g>>>0)|0}c=i[d+32>>2]+(c<<2)|0;break t}i[b>>2]=i[c>>2]}i[c>>2]=b}i[d+44>>2]=i[d+44>>2]+1}i[i[d+16>>2]+(h<<2)>>2]=w;w=w+1|0;c=i[d+4>>2];if((c|0)!=i[d+8>>2]){i[c>>2]=h;i[d+4>>2]=c+4;break e}b=i[d>>2];f=c-b|0;n=f>>2;k=n+1|0;if(k>>>0>=1073741824){break c}e=f>>1;k=n>>>0<536870911?e>>>0>>0?k:e:1073741823;c=0;v:{if(!k){break v}if(k>>>0>=1073741824){break b}c=ho(k<<2)}n=c+(n<<2)|0;i[n>>2]=h;k=c+(k<<2)|0;n=n+4|0;if((f|0)>=1){hp(c,b,f)}i[d+8>>2]=k;i[d+4>>2]=n;i[d>>2]=c;if(!b){break e}bp(b)}h=h+1|0;c=i[a+80>>2];if(h>>>0>>0){continue}break}if((c|0)!=(w|0)){I[i[i[a>>2]+24>>2]](a,d+16|0,d);i[a+80>>2]=w}a=i[d>>2];if(!a){break d}i[d+4>>2]=a;bp(a)}a=i[d+16>>2];if(a){i[d+20>>2]=a;bp(a)}h=i[d+40>>2];if(h){while(1){a=i[h>>2];bp(h);h=a;if(h){continue}break}}a=i[d+32>>2];i[d+32>>2]=0;if(a){bp(a)}F=d- -64|0;return}Ho();x()}za(16928);x()}Ho();x()}function ui(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,k=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;e=F-80|0;F=e;a:{b:{c:{d:{if(!j[a+352|0]){break d}c=1;d=I[i[i[a>>2]+40>>2]](a)|0;if((i[d+12>>2]-i[d+8>>2]|0)<1){break d}Rh(i[i[(I[i[i[a>>2]+40>>2]](a)|0)+8>>2]>>2],b);break c}c=b<<2;h=i[c+i[i[(I[i[i[a>>2]+40>>2]](a)|0)+56>>2]+84>>2]>>2];k=i[c+i[i[(I[i[i[a>>2]+40>>2]](a)|0)+4>>2]+8>>2]>>2];m=-1;d=i[a+176>>2];f=i[a+172>>2];e:{if((d|0)==(f|0)){break e}d=(d-f|0)/136|0;c=0;while(1){if(i[f+o(c,136)>>2]==(b|0)){d=f;m=c;break e}c=c+1|0;if(c>>>0>>0){continue}break}d=f}f:{g:{h:{i:{j:{k:{l:{m:{if(!j[a+352|0]){c=i[k+56>>2];if(!j[(o(m,136)+d|0)+28|0]|(h|0)!=1?!(!h|!c):0){break k}if(c){break m}}c=a+40|0;break l}c=i[a+12>>2];f=i[c+28>>2];c=i[c+24>>2];i[e+16>>2]=-1;n=d;d=o(m,136);h=n+d|0;Sh(h+116|0,f-c>>2,e+16|0);g[(d+i[a+172>>2]|0)+100|0]=0;c=h+104|0}h=0;if(_b(i[(I[i[i[a>>2]+40>>2]](a)|0)+48>>2])|i[k+56>>2]){break j}n:{if(!j[a+352|0]){h=1;break n}d=i[a+8>>2];d=i[d+12>>2]-i[d+8>>2]|0;h=(d|0)<5;if((d|0)>4){break j}}Th(e+16|0,a,c);break i}d=o(m,136)+d|0;c=i[d+56>>2];f=i[d+60>>2];i[e+16>>2]=-1;Sh(d+116|0,f-c>>2,e+16|0);c=ho(80);h=i[a+8>>2];i[c+12>>2]=0;i[c+16>>2]=0;i[c>>2]=12880;i[c+4>>2]=0;k=c+20|0;f=k;i[f>>2]=0;i[f+4>>2]=0;r=c+28|0;f=r;i[f>>2]=0;i[f+4>>2]=0;i[c+36>>2]=0;i[c+40>>2]=0;n=c+44|0;f=n;i[f>>2]=0;i[f+4>>2]=0;i[c+52>>2]=0;i[c+76>>2]=0;s=d+104|0;i[c+72>>2]=s;i[c+68>>2]=h;i[c+64>>2]=0;i[c+56>>2]=0;i[c+60>>2]=0;i[c+8>>2]=11784;t=e+52|0;f=t;i[f>>2]=0;i[f+4>>2]=0;u=e+44|0;f=u;i[f>>2]=0;i[f+4>>2]=0;p=e+36|0;f=p;i[f>>2]=0;i[f+4>>2]=0;f=e+28|0;i[f>>2]=0;i[f+4>>2]=0;i[e+68>>2]=0;i[e+72>>2]=0;i[e+60>>2]=0;i[e+64>>2]=0;i[e+20>>2]=0;i[e+24>>2]=0;i[e+16>>2]=11784;v=d+4|0;i[e+20>>2]=v;d=i[d+68>>2];q=i[d>>2];d=i[d+4>>2];g[e+79|0]=0;Uh(e+40|0,(d-q>>2>>>0)/3|0,e+79|0);d=i[e+20>>2];q=i[d+56>>2];d=i[d+52>>2];g[e+79|0]=0;Uh(t,q-d>>2,e+79|0);i[p>>2]=c;i[e+32>>2]=h;i[f>>2]=s;i[e+24>>2]=v;i[c+76>>2]=a+72;i[r>>2]=i[p>>2];d=i[f+4>>2];i[k>>2]=i[f>>2];i[k+4>>2]=d;d=i[e+24>>2];i[c+12>>2]=i[e+20>>2];i[c+16>>2]=d;d=c;h=i[u>>2];if(h){f=c+32|0;o:{if(h>>>0<=i[c+40>>2]<<5>>>0){k=h+ -1>>>5|0;h=i[f>>2];break o}f=i[f>>2];if(f){bp(f);i[c+40>>2]=0;i[c+32>>2]=0;i[c+36>>2]=0;h=i[e+44>>2]}if((h|0)<=-1){break h}k=h+ -1>>>5|0;f=k+1|0;h=ho(f<<2);i[c+40>>2]=f;i[c+36>>2]=0;i[c+32>>2]=h}jp(h,i[e+40>>2],(k<<2)+4|0);f=i[e+44>>2]}else{f=0}i[d+36>>2]=f;f=c;h=i[e+56>>2];if(h){p:{if(h>>>0<=i[c+52>>2]<<5>>>0){d=h+ -1>>>5|0;h=i[n>>2];break p}d=i[n>>2];if(d){bp(d);i[c+52>>2]=0;i[c+44>>2]=0;i[c+48>>2]=0;h=i[e+56>>2]}if((h|0)<=-1){break g}d=h+ -1>>>5|0;k=d+1|0;h=ho(k<<2);i[c+52>>2]=k;i[c+48>>2]=0;i[c+44>>2]=h}jp(h,i[e+52>>2],(d<<2)+4|0);d=i[e+56>>2]}else{d=0}i[f+48>>2]=d;Vh(c+56|0,i[e+64>>2],i[e+68>>2]);i[e+16>>2]=11784;d=i[e+64>>2];if(d){i[e+68>>2]=d;bp(d)}i[e+16>>2]=12036;d=i[e+52>>2];if(d){bp(d)}d=i[e+40>>2];if(d){bp(d)}h=0;break f}Wh(e+16|0,a,c)}c=i[e+16>>2];if(c){break f}c=0;break c}Ho();x()}Ho();x()}i[((m|0)==-1?a+68|0:(i[a+172>>2]+o(m,136)|0)+132|0)>>2]=h;d=ho(76);i[e+8>>2]=c;Mc(d,e+8|0,b);b=i[e+8>>2];i[e+8>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}b=i[a+188>>2];q:{if((b|0)!=i[a+192>>2]){i[b>>2]=m;i[a+188>>2]=b+4;break q}c=i[a+184>>2];f=b-c|0;k=f>>2;h=k+1|0;if(h>>>0>=1073741824){break b}n=f>>1;h=k>>>0<536870911?n>>>0>>0?h:n:1073741823;b=0;r:{if(!h){break r}if(h>>>0>=1073741824){break a}b=ho(h<<2)}k=b+(k<<2)|0;i[k>>2]=m;m=b+(h<<2)|0;h=k+4|0;if((f|0)>=1){hp(b,c,f)}i[a+192>>2]=m;i[a+188>>2]=h;i[a+184>>2]=b;if(!c){break q}bp(c)}a=I[i[i[a>>2]+40>>2]](a)|0;i[e>>2]=d;b=i[a+12>>2];s:{if(b>>>0>2]){i[e>>2]=0;i[b>>2]=d;i[a+12>>2]=b+4;break s}Xh(a+8|0,e)}a=i[e>>2];i[e>>2]=0;c=1;if(!a){break c}I[i[i[a>>2]+4>>2]](a)}F=e+80|0;return c|0}Ho();x()}za(11708);x()}function Qh(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,k=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;e=F-80|0;F=e;a:{b:{c:{d:{if(!j[a+288|0]){break d}c=1;d=I[i[i[a>>2]+40>>2]](a)|0;if((i[d+12>>2]-i[d+8>>2]|0)<1){break d}Rh(i[i[(I[i[i[a>>2]+40>>2]](a)|0)+8>>2]>>2],b);break c}c=b<<2;h=i[c+i[i[(I[i[i[a>>2]+40>>2]](a)|0)+56>>2]+84>>2]>>2];k=i[c+i[i[(I[i[i[a>>2]+40>>2]](a)|0)+4>>2]+8>>2]>>2];m=-1;d=i[a+176>>2];f=i[a+172>>2];e:{if((d|0)==(f|0)){break e}d=(d-f|0)/136|0;c=0;while(1){if(i[f+o(c,136)>>2]==(b|0)){d=f;m=c;break e}c=c+1|0;if(c>>>0>>0){continue}break}d=f}f:{g:{h:{i:{j:{k:{l:{m:{if(!j[a+288|0]){c=i[k+56>>2];if(!j[(o(m,136)+d|0)+28|0]|(h|0)!=1?!(!h|!c):0){break k}if(c){break m}}c=a+40|0;break l}c=i[a+12>>2];f=i[c+28>>2];c=i[c+24>>2];i[e+16>>2]=-1;n=d;d=o(m,136);h=n+d|0;Sh(h+116|0,f-c>>2,e+16|0);g[(d+i[a+172>>2]|0)+100|0]=0;c=h+104|0}h=0;if(_b(i[(I[i[i[a>>2]+40>>2]](a)|0)+48>>2])|i[k+56>>2]){break j}n:{if(!j[a+288|0]){h=1;break n}d=i[a+8>>2];d=i[d+12>>2]-i[d+8>>2]|0;h=(d|0)<5;if((d|0)>4){break j}}Th(e+16|0,a,c);break i}d=o(m,136)+d|0;c=i[d+56>>2];f=i[d+60>>2];i[e+16>>2]=-1;Sh(d+116|0,f-c>>2,e+16|0);c=ho(80);h=i[a+8>>2];i[c+12>>2]=0;i[c+16>>2]=0;i[c>>2]=12880;i[c+4>>2]=0;k=c+20|0;f=k;i[f>>2]=0;i[f+4>>2]=0;r=c+28|0;f=r;i[f>>2]=0;i[f+4>>2]=0;i[c+36>>2]=0;i[c+40>>2]=0;n=c+44|0;f=n;i[f>>2]=0;i[f+4>>2]=0;i[c+52>>2]=0;i[c+76>>2]=0;s=d+104|0;i[c+72>>2]=s;i[c+68>>2]=h;i[c+64>>2]=0;i[c+56>>2]=0;i[c+60>>2]=0;i[c+8>>2]=11784;t=e+52|0;f=t;i[f>>2]=0;i[f+4>>2]=0;u=e+44|0;f=u;i[f>>2]=0;i[f+4>>2]=0;p=e+36|0;f=p;i[f>>2]=0;i[f+4>>2]=0;f=e+28|0;i[f>>2]=0;i[f+4>>2]=0;i[e+68>>2]=0;i[e+72>>2]=0;i[e+60>>2]=0;i[e+64>>2]=0;i[e+20>>2]=0;i[e+24>>2]=0;i[e+16>>2]=11784;v=d+4|0;i[e+20>>2]=v;d=i[d+68>>2];q=i[d>>2];d=i[d+4>>2];g[e+79|0]=0;Uh(e+40|0,(d-q>>2>>>0)/3|0,e+79|0);d=i[e+20>>2];q=i[d+56>>2];d=i[d+52>>2];g[e+79|0]=0;Uh(t,q-d>>2,e+79|0);i[p>>2]=c;i[e+32>>2]=h;i[f>>2]=s;i[e+24>>2]=v;i[c+76>>2]=a+72;i[r>>2]=i[p>>2];d=i[f+4>>2];i[k>>2]=i[f>>2];i[k+4>>2]=d;d=i[e+24>>2];i[c+12>>2]=i[e+20>>2];i[c+16>>2]=d;d=c;h=i[u>>2];if(h){f=c+32|0;o:{if(h>>>0<=i[c+40>>2]<<5>>>0){k=h+ -1>>>5|0;h=i[f>>2];break o}f=i[f>>2];if(f){bp(f);i[c+40>>2]=0;i[c+32>>2]=0;i[c+36>>2]=0;h=i[e+44>>2]}if((h|0)<=-1){break h}k=h+ -1>>>5|0;f=k+1|0;h=ho(f<<2);i[c+40>>2]=f;i[c+36>>2]=0;i[c+32>>2]=h}jp(h,i[e+40>>2],(k<<2)+4|0);f=i[e+44>>2]}else{f=0}i[d+36>>2]=f;f=c;h=i[e+56>>2];if(h){p:{if(h>>>0<=i[c+52>>2]<<5>>>0){d=h+ -1>>>5|0;h=i[n>>2];break p}d=i[n>>2];if(d){bp(d);i[c+52>>2]=0;i[c+44>>2]=0;i[c+48>>2]=0;h=i[e+56>>2]}if((h|0)<=-1){break g}d=h+ -1>>>5|0;k=d+1|0;h=ho(k<<2);i[c+52>>2]=k;i[c+48>>2]=0;i[c+44>>2]=h}jp(h,i[e+52>>2],(d<<2)+4|0);d=i[e+56>>2]}else{d=0}i[f+48>>2]=d;Vh(c+56|0,i[e+64>>2],i[e+68>>2]);i[e+16>>2]=11784;d=i[e+64>>2];if(d){i[e+68>>2]=d;bp(d)}i[e+16>>2]=12036;d=i[e+52>>2];if(d){bp(d)}d=i[e+40>>2];if(d){bp(d)}h=0;break f}Wh(e+16|0,a,c)}c=i[e+16>>2];if(c){break f}c=0;break c}Ho();x()}Ho();x()}i[((m|0)==-1?a+68|0:(i[a+172>>2]+o(m,136)|0)+132|0)>>2]=h;d=ho(76);i[e+8>>2]=c;Mc(d,e+8|0,b);b=i[e+8>>2];i[e+8>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}b=i[a+188>>2];q:{if((b|0)!=i[a+192>>2]){i[b>>2]=m;i[a+188>>2]=b+4;break q}c=i[a+184>>2];f=b-c|0;k=f>>2;h=k+1|0;if(h>>>0>=1073741824){break b}n=f>>1;h=k>>>0<536870911?n>>>0>>0?h:n:1073741823;b=0;r:{if(!h){break r}if(h>>>0>=1073741824){break a}b=ho(h<<2)}k=b+(k<<2)|0;i[k>>2]=m;m=b+(h<<2)|0;h=k+4|0;if((f|0)>=1){hp(b,c,f)}i[a+192>>2]=m;i[a+188>>2]=h;i[a+184>>2]=b;if(!c){break q}bp(c)}a=I[i[i[a>>2]+40>>2]](a)|0;i[e>>2]=d;b=i[a+12>>2];s:{if(b>>>0>2]){i[e>>2]=0;i[b>>2]=d;i[a+12>>2]=b+4;break s}Xh(a+8|0,e)}a=i[e>>2];i[e>>2]=0;c=1;if(!a){break c}I[i[i[a>>2]+4>>2]](a)}F=e+80|0;return c|0}Ho();x()}za(11708);x()}function kg(a,b,c,d,e){var f=0,h=0,k=0,m=0,n=0,p=0,q=0,s=0,t=0,u=0,v=0,w=0,y=0,z=0,A=0,B=0;k=F-48|0;F=k;a:{b:{c:{d:{e:{if((b|0)<0){break e}if(!b){p=1;break e}if(b>>>0>=1073741824){break a}A=(c|0)>1?c:1;s=b<<2;f=ho(s);i[k+36>>2]=f;i[k+32>>2]=f;z=f+s|0;i[k+40>>2]=z;s=f;n=f;while(1){m=1;p=i[(B<<2)+a>>2];if((c|0)>1){while(1){v=i[(m+B<<2)+a>>2];p=p>>>0>>0?v:p;m=m+1|0;if((A|0)!=(m|0)){continue}break}}t=p?(r(p)^31)+1|0:1;f:{if(n>>>0>>0){i[n>>2]=t;n=n+4|0;i[k+36>>2]=n;break f}u=n-s|0;v=u>>2;q=v+1|0;if(q>>>0>=1073741824){break d}n=z-s|0;m=n>>1;n=n>>2>>>0<536870911?m>>>0>>0?q:m:1073741823;f=0;g:{if(!n){break g}if(n>>>0>=1073741824){break c}f=ho(n<<2)}m=f+(v<<2)|0;i[m>>2]=t;z=(n<<2)+f|0;n=m+4|0;if((u|0)>=1){hp(f,s,u)}i[k+40>>2]=z;i[k+36>>2]=n;i[k+32>>2]=f;if(s){bp(s)}s=f}w=p>>>0>w>>>0?p:w;B=A+B|0;if((B|0)<(b|0)){continue}break}m=0;i[k+24>>2]=0;i[k+16>>2]=0;i[k+20>>2]=0;p=0;n=n-f|0;if(n){c=n>>2;if(c>>>0>=1073741824){break b}m=ho(n);i[k+16>>2]=m;i[k+24>>2]=(c<<2)+m;s=k;if((n|0)<1){c=m}else{c=hp(m,f,n)+n|0}p=c;i[s+20>>2]=p}n=p-m|0;if(n){c=m-p|0;f=((c|0)>(n|0)?c:n)>>>2|0;c=(n|0)>-1?n:-1;c=o(f,(c|0)<1?c:1);s=c>>>0>1?c:1;p=0;while(1){f=i[(p<<2)+m>>2];c=f+y|0;if(c>>>0>>0){h=h+1|0}y=c;p=p+1|0;if((s|0)!=(p|0)){continue}break}}f=ag(m,n>>2,32,k);v=H;m=i[k>>2];c=i[k+16>>2];if(c){i[k+20>>2]=c;bp(c)}p=ag(a,b,w,k);n=H;u=i[k>>2];s=(w-u|0)/64|0;h:{i:{if(!d){break i}c=ho(32);i[k>>2]=c;i[k+4>>2]=22;i[k+8>>2]=-2147483616;g[c+22|0]=0;t=j[10820]|j[10821]<<8|(j[10822]<<16|j[10823]<<24);q=j[10816]|j[10817]<<8|(j[10818]<<16|j[10819]<<24);g[c+14|0]=q;g[c+15|0]=q>>>8;g[c+16|0]=q>>>16;g[c+17|0]=q>>>24;g[c+18|0]=t;g[c+19|0]=t>>>8;g[c+20|0]=t>>>16;g[c+21|0]=t>>>24;t=j[10814]|j[10815]<<8|(j[10816]<<16|j[10817]<<24);q=j[10810]|j[10811]<<8|(j[10812]<<16|j[10813]<<24);g[c+8|0]=q;g[c+9|0]=q>>>8;g[c+10|0]=q>>>16;g[c+11|0]=q>>>24;g[c+12|0]=t;g[c+13|0]=t>>>8;g[c+14|0]=t>>>16;g[c+15|0]=t>>>24;t=j[10806]|j[10807]<<8|(j[10808]<<16|j[10809]<<24);q=j[10802]|j[10803]<<8|(j[10804]<<16|j[10805]<<24);g[c|0]=q;g[c+1|0]=q>>>8;g[c+2|0]=q>>>16;g[c+3|0]=q>>>24;g[c+4|0]=t;g[c+5|0]=t>>>8;g[c+6|0]=t>>>16;g[c+7|0]=t>>>24;c=Sb(d,k);if(g[k+11|0]<=-1){bp(i[k>>2])}if(!c){break i}c=ho(32);i[k>>2]=c;i[k+4>>2]=22;i[k+8>>2]=-2147483616;g[c+22|0]=0;h=j[10820]|j[10821]<<8|(j[10822]<<16|j[10823]<<24);f=j[10816]|j[10817]<<8|(j[10818]<<16|j[10819]<<24);g[c+14|0]=f;g[c+15|0]=f>>>8;g[c+16|0]=f>>>16;g[c+17|0]=f>>>24;g[c+18|0]=h;g[c+19|0]=h>>>8;g[c+20|0]=h>>>16;g[c+21|0]=h>>>24;h=j[10814]|j[10815]<<8|(j[10816]<<16|j[10817]<<24);f=j[10810]|j[10811]<<8|(j[10812]<<16|j[10813]<<24);g[c+8|0]=f;g[c+9|0]=f>>>8;g[c+10|0]=f>>>16;g[c+11|0]=f>>>24;g[c+12|0]=h;g[c+13|0]=h>>>8;g[c+14|0]=h>>>16;g[c+15|0]=h>>>24;h=j[10806]|j[10807]<<8|(j[10808]<<16|j[10809]<<24);f=j[10802]|j[10803]<<8|(j[10804]<<16|j[10805]<<24);g[c|0]=f;g[c+1|0]=f>>>8;g[c+2|0]=f>>>16;g[c+3|0]=f>>>24;g[c+4|0]=h;g[c+5|0]=h>>>8;g[c+6|0]=h>>>16;g[c+7|0]=h>>>24;m=_j(d,k);if(g[k+11|0]>-1){break h}bp(i[k>>2]);break h}if((r(w>>>0>1?w:1)^30)>>>0<18){c=f;f=up(y,h,A,0);h=c+f|0;c=H+v|0;c=h>>>0>>0?c+1|0:c;y=h;h=m<<3;f=h>>31;m=h;h=f<<1|h>>>31;f=m<<1;m=y+f|0;c=c+h|0;c=m>>>0>>0?c+1|0:c;h=s+u<<3;f=h;s=f>>31;y=m;q=f;h=u<<3;f=h;m=f+p|0;h=(f>>31)+n|0;h=m>>>0>>0?h+1|0:h;f=m;m=q+f|0;h=h+s|0;h=m>>>0>>0?h+1|0:h;m=(c|0)>(h|0)?1:(c|0)>=(h|0)?y>>>0>=m>>>0:0}else{m=0}}g[k|0]=m;c=i[e+20>>2];if((c|0)<0?1:(c|0)<=0?l[e+16>>2]<=0:0){ca(e,i[e+4>>2],k,k+1|0)}p=0;j:{k:{switch(m|0){case 0:p=lg(a,b,A,k+32|0,e);break j;case 1:break k;default:break j}}p=mg(a,b,w,u,d,e)}a=i[k+32>>2];if(!a){break e}i[k+36>>2]=a;bp(a)}F=k+48|0;return p}Ho();x()}za(10859);x()}Ho();x()}za(10859);x()}function Si(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0;a:{b:{c:{d:{e:{f:{g:{if(i[a+92>>2]==i[a+88>>2]){break g}d=i[a+52>>2];h:{if((d|0)!=i[a+56>>2]){i[d>>2]=b;i[a+52>>2]=d+4;break h}h=i[a+48>>2];g=d-h|0;f=g>>2;c=f+1|0;if(c>>>0>=1073741824){break a}e=g>>1;c=f>>>0<536870911?e>>>0>>0?c:e:1073741823;d=0;i:{if(!c){break i}if(c>>>0>=1073741824){break b}d=ho(c<<2)}e=d+(f<<2)|0;i[e>>2]=b;c=d+(c<<2)|0;e=e+4|0;if((g|0)>=1){hp(d,h,g)}i[a+56>>2]=c;i[a+52>>2]=e;i[a+48>>2]=d;if(!h){break h}bp(h)}i[a+84>>2]=0;e=-1;d=-1;j:{if((b|0)==-1){break j}f=i[a+4>>2];c=b+1|0;c=(c>>>0)%3|0?c:b+ -2|0;if((c|0)!=-1){e=i[i[f>>2]+(c<<2)>>2]}c=((b>>>0)%3|0?-1:2)+b|0;if((c|0)==-1){break j}d=i[i[f>>2]+(c<<2)>>2]}c=i[a+36>>2];h=c+(e>>>3&536870908)|0;g=i[h>>2];f=1<>2]=f|g;f=a+8|0;if((b|0)!=-1){c=b+1|0;c=(c>>>0)%3|0?c:b+ -2|0}else{c=-1}Ti(f,e,c);c=i[a+36>>2]}f=(d>>>3&536870908)+c|0;c=i[f>>2];e=1<>2]=c|e;c=a+8|0;e=-1;k:{if((b|0)==-1){break k}e=b+ -1|0;if((b>>>0)%3|0){break k}e=b+2|0}Ti(c,d,e)}e=-1;e=(b|0)!=-1?i[i[i[a+4>>2]>>2]+(b<<2)>>2]:e;f=i[a+36>>2]+(e>>>3&536870908)|0;c=i[f>>2];d=1<>2]=c|d;Ti(a+8|0,e,b)}c=i[a+84>>2];if((c|0)>2){break g}q=a+8|0;while(1){b=o(c,12)+a|0;e=b+52|0;d=i[e>>2];if((d|0)==i[b+48>>2]){c=c+1|0;if((c|0)!=3){continue}break g}d=d+ -4|0;b=i[d>>2];i[e>>2]=d;i[a+84>>2]=c;if((b|0)==-1){break g}e=i[a+24>>2];d=(b>>>0)/3|0;l:{if(i[e+(d>>>3&268435452)>>2]>>>d&1){break l}m:{while(1){g=(b>>>0)/3|0;d=(g>>>3&268435452)+e|0;i[d>>2]=i[d>>2]|1<>2]>>2]+(b<<2)>>2]:e;f=i[a+36>>2]+(e>>>3&536870908)|0;c=i[f>>2];d=1<>2]=c|d;Ti(q,e,b)}if((b|0)==-1){break m}h=i[a+4>>2];d=-1;e=-1;c=b+1|0;c=(c>>>0)%3|0?c:b+ -2|0;if((c|0)!=-1){e=i[i[h+12>>2]+(c<<2)>>2]}b=(b-o(g,3)|0?-1:2)+b|0;if((b|0)!=-1){d=i[i[h+12>>2]+(b<<2)>>2]}g=(d|0)==-1;f=(d>>>0)/3|0;c=(e>>>0)/3|0;n:{o:{p:{q:{b=(e|0)==-1;if(!b){b=b?-1:c;b=i[i[a+24>>2]+(b>>>3&536870908)>>2]&1<>2]+(b>>>3&536870908)>>2]>>>b&1){break r}b=0;c=i[i[h>>2]+(d<<2)>>2];if(!(i[i[a+36>>2]+(c>>>3&536870908)>>2]>>>c&1)){b=i[a+88>>2]+(c<<2)|0;c=i[b>>2];i[b>>2]=c+1;b=(c|0)<1?2:1}if(!(!p|(b|0)>i[a+84>>2])){b=d;e=i[a+24>>2];continue}c=o(b,12)+a|0;k=c+52|0;f=i[k>>2];m=c+56|0;s:{if((f|0)!=i[m>>2]){i[f>>2]=d;i[k>>2]=f+4;break s}h=c+48|0;n=i[h>>2];j=f-n|0;l=j>>2;g=l+1|0;if(g>>>0>=1073741824){break f}f=j>>1;f=l>>>0<536870911?f>>>0>>0?g:f:1073741823;c=0;t:{if(!f){break t}if(f>>>0>=1073741824){break e}c=ho(f<<2)}g=c+(l<<2)|0;i[g>>2]=d;f=c+(f<<2)|0;d=g+4|0;if((j|0)>=1){hp(c,n,j)}i[h>>2]=c;i[k>>2]=d;i[m>>2]=f;if(!n){break s}bp(n)}if(i[a+84>>2]<=(b|0)){break r}i[a+84>>2]=b}if(p){break m}b=-1;if((e|0)==-1){break n}}b=i[i[i[a+4>>2]>>2]+(e<<2)>>2]}d=0;if(!(i[i[a+36>>2]+(b>>>3&536870908)>>2]>>>b&1)){b=i[a+88>>2]+(b<<2)|0;d=i[b>>2];i[b>>2]=d+1;d=(d|0)<1?2:1}if((d|0)<=i[a+84>>2]){b=e;e=i[a+24>>2];continue}break}b=o(d,12)+a|0;l=b+52|0;c=i[l>>2];h=b+56|0;u:{if((c|0)!=i[h>>2]){i[c>>2]=e;i[l>>2]=c+4;break u}g=b+48|0;j=i[g>>2];k=c-j|0;m=k>>2;f=m+1|0;if(f>>>0>=1073741824){break d}c=k>>1;c=m>>>0<536870911?c>>>0>>0?f:c:1073741823;b=0;v:{if(!c){break v}if(c>>>0>=1073741824){break c}b=ho(c<<2)}f=b+(m<<2)|0;i[f>>2]=e;c=b+(c<<2)|0;e=f+4|0;if((k|0)>=1){hp(b,j,k)}i[g>>2]=b;i[l>>2]=e;i[h>>2]=c;if(!j){break u}bp(j)}c=i[a+84>>2];if((c|0)<=(d|0)){break l}i[a+84>>2]=d;c=d;break l}c=i[a+84>>2]}if((c|0)<3){continue}break}}return 1}Ho();x()}za(11708);x()}Ho();x()}za(11708);x()}za(11708);x()}Ho();x()}function tn(a,b,c,d,e,f,g,h,j){var k=0,l=0,m=0,n=0,o=0,p=0,q=0,s=0,t=0,u=0;m=F-112|0;F=m;n=h;o=j&2147483647;l=c+ -1|0;k=b+ -1|0;if((k|0)!=-1){l=l+1|0}p=k;t=(k|0)==-1&(l|0)==-1;q=e&2147483647;k=q;s=d;l=(c|0)==(l|0)&p>>>0>>0|l>>>0>>0;p=d+l|0;if(p>>>0>>0){k=k+1|0}l=p+ -1|0;k=k+ -1|0;k=(l|0)!=-1?k+1|0:k;a:{b:{if(!((l|0)==-1&(k|0)==2147418111?t:k>>>0>2147418111)){l=g+ -1|0;k=f+ -1|0;if((k|0)!=-1){l=l+1|0}p=k;t=(k|0)!=-1|(l|0)!=-1;k=o;l=(g|0)==(l|0)&p>>>0>>0|l>>>0>>0;p=l+n|0;if(p>>>0>>0){k=k+1|0}l=p+ -1|0;k=k+ -1|0;k=(l|0)!=-1?k+1|0:k;if((l|0)==-1&(k|0)==2147418111?t:(k|0)==2147418111&(l|0)!=-1|k>>>0<2147418111){break b}}if(!(!s&(q|0)==2147418112?!(b|c):(q|0)==2147418112&s>>>0<0|q>>>0<2147418112)){h=d;j=e|32768;f=b;g=c;break a}if(!(!n&(o|0)==2147418112?!(f|g):(o|0)==2147418112&n>>>0<0|o>>>0<2147418112)){j=j|32768;break a}if(!(b|s|(q^2147418112|c))){k=d;d=!(b^f|d^h|(c^g|e^j^-2147483648));h=d?0:k;j=d?2147450880:e;f=d?0:b;g=d?0:c;break a}if(!(f|n|(o^2147418112|g))){break a}if(!(b|s|(c|q))){if(f|n|(g|o)){break a}f=b&f;g=c&g;h=d&h;j=e&j;break a}if(f|n|(g|o)){break b}f=b;g=c;h=d;j=e;break a}k=(n|0)==(s|0)&(o|0)==(q|0)?(c|0)==(g|0)&f>>>0>b>>>0|g>>>0>c>>>0:(o|0)==(q|0)&n>>>0>s>>>0|o>>>0>q>>>0;q=k?f:b;l=k?g:c;n=k?j:e;s=n;p=k?h:d;n=n&65535;e=k?e:j;u=e;h=k?d:h;t=e>>>16&32767;o=s>>>16&32767;if(!o){d=!(n|p);e=d<<6;j=r(d?q:p)+32|0;d=r(d?l:n);d=e+((d|0)==32?j:d)|0;sn(m+96|0,q,l,p,n,d+ -15|0);p=i[m+104>>2];n=i[m+108>>2];q=i[m+96>>2];o=16-d|0;l=i[m+100>>2]}f=k?b:f;g=k?c:g;j=u&65535;if(!t){b=!(h|j);c=b<<6;d=r(b?f:h)+32|0;b=r(b?g:j);b=c+((b|0)==32?d:b)|0;sn(m+80|0,f,g,h,j,b+ -15|0);t=16-b|0;h=i[m+88>>2];j=i[m+92>>2];g=i[m+84>>2];f=i[m+80>>2]}b=h;k=j<<3|b>>>29;h=b<<3|g>>>29;j=k|524288;b=p;d=n<<3|b>>>29;e=b<<3|l>>>29;p=d;n=s^u;b=f;k=g<<3|b>>>29;b=b<<3;c=k;f=o-t|0;d=b;c:{if(!f){break c}if(f>>>0>127){h=0;j=0;k=0;d=1;break c}sn(m- -64|0,b,c,h,j,128-f|0);vn(m+48|0,b,c,h,j,f);h=i[m+56>>2];j=i[m+60>>2];k=i[m+52>>2];d=i[m+48>>2]|((i[m+64>>2]|i[m+72>>2])!=0|(i[m+68>>2]|i[m+76>>2])!=0)}f=k;p=p|524288;b=q;k=l<<3|b>>>29;c=b<<3;d:{if((n|0)<-1?1:(n|0)<=-1){l=d;b=c-d|0;n=h;q=e-h|0;d=(f|0)==(k|0)&c>>>0>>0|k>>>0>>0;h=q-d|0;c=k-((c>>>0>>0)+f|0)|0;j=(p-((e>>>0>>0)+j|0)|0)-(q>>>0>>0)|0;if(!(b|h|(c|j))){f=0;g=0;h=0;j=0;break a}if(j>>>0>524287){break d}f=b;d=!(h|j);e=d<<6;g=r(d?b:h)+32|0;b=r(d?c:j);b=e+((b|0)==32?g:b)|0;b=b+ -12|0;sn(m+32|0,f,c,h,j,b);o=o-b|0;h=i[m+40>>2];j=i[m+44>>2];b=i[m+32>>2];c=i[m+36>>2];break d}k=f+k|0;b=d;c=b+c|0;if(c>>>0>>0){k=k+1|0}b=c;c=k;g=(f|0)==(k|0)&b>>>0>>0|k>>>0>>0;l=j+p|0;d=e+h|0;if(d>>>0>>0){l=l+1|0}f=d;e=g+d|0;d=l;h=e;j=h>>>0>>0?d+1|0:d;if(!(j&1048576)){break d}b=b&1|((c&1)<<31|b>>>1);c=h<<31|c>>>1;o=o+1|0;h=(j&1)<<31|h>>>1;j=j>>>1|0}l=0;n=s&-2147483648;if((o|0)>=32767){h=l;j=n|2147418112;f=0;g=0;break a}e=0;e:{if((o|0)>0){e=o;break e}sn(m+16|0,b,c,h,j,o+127|0);vn(m,b,c,h,j,1-o|0);b=i[m>>2]|((i[m+16>>2]|i[m+24>>2])!=0|(i[m+20>>2]|i[m+28>>2])!=0);c=i[m+4>>2];h=i[m+8>>2];j=i[m+12>>2]}f=h<<29;d=c>>>3|0;c=(c&7)<<29|b>>>3;d=d|f;k=d;b=b&7;f=b>>>0>4;g=f+c|0;if(g>>>0>>0){k=k+1|0}f=g;g=k;c=(d|0)==(k|0)&f>>>0>>0|k>>>0>>0;d=l|((j&7)<<29|h>>>3);k=n|j>>>3&65535|e<<16;c=d+c|0;if(c>>>0>>0){k=k+1|0}h=c;j=k;f:{if((b|0)==4){b=0;d=g+b|0;c=f&1;e=f+c|0;if(e>>>0>>0){d=d+1|0}f=e;g=d;b=(b|0)==(d|0)&f>>>0>>0|d>>>0>>0;c=b+h|0;if(c>>>0>>0){k=k+1|0}h=c;j=k;break f}if(!b){break a}}}i[a>>2]=f;i[a+4>>2]=g;i[a+8>>2]=h;i[a+12>>2]=j;F=m+112|0}function Ze(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,n=0,o=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;k=F-48|0;F=k;d=i[a+8>>2];if(d+ -2>>>0<=28){i[a+76>>2]=d;e=-1<>2]=d;i[a+80>>2]=e^-1;i[a+92>>2]=(d|0)/2;m[a+88>>2]=p(2)/p(d|0)}i[a+52>>2]=f;r=a+96|0;pf(r);d=i[a+40>>2];e=i[d>>2];f=i[d+4>>2];i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;a:{g=f-e|0;if((g|0)<1){break a}j=i[d>>2];if((j|0)!=i[d+4>>2]){s=a+8|0;t=a+44|0;d=e-f|0;d=((d|0)>(g|0)?d:g)>>>2|0;u=d>>>0>1?d:1;v=c+4|0;while(1){le(t,i[(o<<2)+j>>2],k+8|0);f=i[k+12>>2];g=f>>31;e=i[k+8>>2];l=e>>31;j=i[k+16>>2];h=j>>31;d=0;h=h^j+h;g=h+((g^f+g)+(l^e+l)|0)|0;if(g>>>0>>0){d=1}l=g;b:{if(!(d|g)){n=i[a+92>>2];g=n;break b}n=i[a+92>>2];h=n;q=h>>31;g=vp(up(h,q,e,e>>31),H,l,d);f=vp(up(h,q,f,f>>31),H,l,d);if((j|0)>=0){j=(((g|0)<0?g:0-g|0)+n|0)+((f|0)<0?f:0-f|0)|0;break b}d=g>>31;e=(d+g^d)-n|0;d=f>>31;j=e+(d+f^d)|0}c:{if((g|0)>=0){e=f+n|0;h=j+n|0;break c}d:{if((f|0)<=-1){d=j>>31;e=d+j^d;break d}e=i[a+84>>2]+((j|0)<0?j:0-j|0)|0}if((j|0)<=-1){d=f>>31;h=d+f^d;break c}h=i[a+84>>2]+((f|0)<0?f:0-f|0)|0}e:{if(!(e|h)){d=i[a+84>>2];h=d;l=d;break e}d=i[a+84>>2];f:{g:{h:{i:{if(!e){if((d|0)==(h|0)){d=h;l=d;break e}l=0;if(d|h){break i}h=0;break e}if((d|0)!=(e|0)|h){break h}d=e;h=d;l=d;break e}if((n|0)<(h|0)){h=(n<<1)-h|0;break e}if(d){break f}break g}if((d|0)!=(e|0)){break f}}d=e;if((n|0)<=(h|0)){break f}h=(n<<1)-h|0;l=e;break e}if(!((d|0)!=(h|0)|(n|0)<=(e|0))){l=(n<<1)-e|0;break e}if(h){l=e;break e}h=0;if((n|0)>=(e|0)){l=e;break e}l=(n<<1)-e|0}e=0-j|0;i[k+16>>2]=e;q=0-f|0;i[k+12>>2]=q;i[k+8>>2]=0-g;j:{k:{l:{if((g|0)<=0){g=n-f|0;e=n-j|0;break l}g=j>>31;w=((j|0)<0?j:e)+d|0;e=(f|0)>0;g=e?g+j^g:w;if((j|0)>=1){e=f>>31;e=e+f^e;break l}e=(e?q:f)+d|0}if(!(e|g)){break k}m:{n:{o:{if(!g){if((d|0)==(e|0)){break k}f=0;if(d|e){break o}e=0;break j}if((d|0)==(g|0)?!e:0){break k}if((d|0)==(g|0)){break n}break m}if((n|0)<(e|0)){e=(n<<1)-e|0;break j}if(d){break m}}d=g;if((n|0)<=(e|0)){break m}e=(n<<1)-e|0;f=g;break j}if(!((d|0)!=(e|0)|(n|0)<=(g|0))){f=(n<<1)-g|0;break j}if(e){f=g;break j}e=0;if((n|0)>=(g|0)){f=g;break j}f=(n<<1)-g|0;break j}e=d;f=d}d=(o<<3)+b|0;g=i[d+4>>2];i[k+32>>2]=i[d>>2];i[k+36>>2]=g;i[k+28>>2]=h;i[k+24>>2]=l;ze(k+40|0,s,k+32|0,k+24|0);g=i[k+44>>2];j=i[k+40>>2];l=i[d+4>>2];i[k+32>>2]=i[d>>2];i[k+36>>2]=l;i[k+28>>2]=e;i[k+24>>2]=f;ze(k+40|0,s,k+32|0,k+24|0);f=i[k+44>>2];d=i[k+40>>2];e=i[a+92>>2];p:{if((e|0)<(j|0)){j=j-i[a+80>>2]|0;break p}if((j|0)>=(0-e|0)){break p}j=i[a+80>>2]+j|0}q:{if((e|0)<(g|0)){g=g-i[a+80>>2]|0;break q}if((g|0)>=(0-e|0)){break q}g=i[a+80>>2]+g|0}r:{if((e|0)<(d|0)){d=d-i[a+80>>2]|0;break r}if((d|0)>=(0-e|0)){break r}d=i[a+80>>2]+d|0}s:{if((e|0)<(f|0)){f=f-i[a+80>>2]|0;break s}if((f|0)>=(0-e|0)){break s}f=i[a+80>>2]+f|0}e=o<<1;l=g>>31;l=l+g^l;h=j>>31;h=h+j^h;n=h>>>0>(l^2147483647)>>>0?2147483647:l+h|0;l=f>>31;l=l+f^l;h=d>>31;h=h+d^h;t:{if(n>>>0<(h>>>0>(l^2147483647)>>>0?2147483647:l+h|0)>>>0){qf(r,0);d=(e<<2)+c|0;if((j|0)<=-1){j=i[a+80>>2]+j|0}i[d>>2]=j;if((g|0)>-1){f=g;break t}f=i[a+80>>2]+g|0;break t}qf(r,1);g=(e<<2)+c|0;if((d|0)<=-1){d=i[a+80>>2]+d|0}i[g>>2]=d;if((f|0)>-1){break t}f=i[a+80>>2]+f|0}i[(e<<2)+v>>2]=f;o=o+1|0;if((u|0)==(o|0)){break a}d=i[a+40>>2];j=i[d>>2];if(i[d+4>>2]-j>>2>>>0>o>>>0){continue}break}}Io();x()}F=k+48|0;return 1}function Pe(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,n=0,o=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;k=F-48|0;F=k;d=i[a+8>>2];if(d+ -2>>>0<=28){i[a+76>>2]=d;e=-1<>2]=d;i[a+80>>2]=e^-1;i[a+92>>2]=(d|0)/2;m[a+88>>2]=p(2)/p(d|0)}i[a+52>>2]=f;r=a+96|0;pf(r);d=i[a+40>>2];e=i[d>>2];f=i[d+4>>2];i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;a:{g=f-e|0;if((g|0)<1){break a}j=i[d>>2];if((j|0)!=i[d+4>>2]){s=a+8|0;t=a+44|0;d=e-f|0;d=((d|0)>(g|0)?d:g)>>>2|0;u=d>>>0>1?d:1;v=c+4|0;while(1){Yd(t,i[(o<<2)+j>>2],k+8|0);f=i[k+12>>2];g=f>>31;e=i[k+8>>2];l=e>>31;j=i[k+16>>2];h=j>>31;d=0;h=h^j+h;g=h+((g^f+g)+(l^e+l)|0)|0;if(g>>>0>>0){d=1}l=g;b:{if(!(d|g)){n=i[a+92>>2];g=n;break b}n=i[a+92>>2];h=n;q=h>>31;g=vp(up(h,q,e,e>>31),H,l,d);f=vp(up(h,q,f,f>>31),H,l,d);if((j|0)>=0){j=(((g|0)<0?g:0-g|0)+n|0)+((f|0)<0?f:0-f|0)|0;break b}d=g>>31;e=(d+g^d)-n|0;d=f>>31;j=e+(d+f^d)|0}c:{if((g|0)>=0){e=f+n|0;h=j+n|0;break c}d:{if((f|0)<=-1){d=j>>31;e=d+j^d;break d}e=i[a+84>>2]+((j|0)<0?j:0-j|0)|0}if((j|0)<=-1){d=f>>31;h=d+f^d;break c}h=i[a+84>>2]+((f|0)<0?f:0-f|0)|0}e:{if(!(e|h)){d=i[a+84>>2];h=d;l=d;break e}d=i[a+84>>2];f:{g:{h:{i:{if(!e){if((d|0)==(h|0)){d=h;l=d;break e}l=0;if(d|h){break i}h=0;break e}if((d|0)!=(e|0)|h){break h}d=e;h=d;l=d;break e}if((n|0)<(h|0)){h=(n<<1)-h|0;break e}if(d){break f}break g}if((d|0)!=(e|0)){break f}}d=e;if((n|0)<=(h|0)){break f}h=(n<<1)-h|0;l=e;break e}if(!((d|0)!=(h|0)|(n|0)<=(e|0))){l=(n<<1)-e|0;break e}if(h){l=e;break e}h=0;if((n|0)>=(e|0)){l=e;break e}l=(n<<1)-e|0}e=0-j|0;i[k+16>>2]=e;q=0-f|0;i[k+12>>2]=q;i[k+8>>2]=0-g;j:{k:{l:{if((g|0)<=0){g=n-f|0;e=n-j|0;break l}g=j>>31;w=((j|0)<0?j:e)+d|0;e=(f|0)>0;g=e?g+j^g:w;if((j|0)>=1){e=f>>31;e=e+f^e;break l}e=(e?q:f)+d|0}if(!(e|g)){break k}m:{n:{o:{if(!g){if((d|0)==(e|0)){break k}f=0;if(d|e){break o}e=0;break j}if((d|0)==(g|0)?!e:0){break k}if((d|0)==(g|0)){break n}break m}if((n|0)<(e|0)){e=(n<<1)-e|0;break j}if(d){break m}}d=g;if((n|0)<=(e|0)){break m}e=(n<<1)-e|0;f=g;break j}if(!((d|0)!=(e|0)|(n|0)<=(g|0))){f=(n<<1)-g|0;break j}if(e){f=g;break j}e=0;if((n|0)>=(g|0)){f=g;break j}f=(n<<1)-g|0;break j}e=d;f=d}d=(o<<3)+b|0;g=i[d+4>>2];i[k+32>>2]=i[d>>2];i[k+36>>2]=g;i[k+28>>2]=h;i[k+24>>2]=l;ze(k+40|0,s,k+32|0,k+24|0);g=i[k+44>>2];j=i[k+40>>2];l=i[d+4>>2];i[k+32>>2]=i[d>>2];i[k+36>>2]=l;i[k+28>>2]=e;i[k+24>>2]=f;ze(k+40|0,s,k+32|0,k+24|0);f=i[k+44>>2];d=i[k+40>>2];e=i[a+92>>2];p:{if((e|0)<(j|0)){j=j-i[a+80>>2]|0;break p}if((j|0)>=(0-e|0)){break p}j=i[a+80>>2]+j|0}q:{if((e|0)<(g|0)){g=g-i[a+80>>2]|0;break q}if((g|0)>=(0-e|0)){break q}g=i[a+80>>2]+g|0}r:{if((e|0)<(d|0)){d=d-i[a+80>>2]|0;break r}if((d|0)>=(0-e|0)){break r}d=i[a+80>>2]+d|0}s:{if((e|0)<(f|0)){f=f-i[a+80>>2]|0;break s}if((f|0)>=(0-e|0)){break s}f=i[a+80>>2]+f|0}e=o<<1;l=g>>31;l=l+g^l;h=j>>31;h=h+j^h;n=h>>>0>(l^2147483647)>>>0?2147483647:l+h|0;l=f>>31;l=l+f^l;h=d>>31;h=h+d^h;t:{if(n>>>0<(h>>>0>(l^2147483647)>>>0?2147483647:l+h|0)>>>0){qf(r,0);d=(e<<2)+c|0;if((j|0)<=-1){j=i[a+80>>2]+j|0}i[d>>2]=j;if((g|0)>-1){f=g;break t}f=i[a+80>>2]+g|0;break t}qf(r,1);g=(e<<2)+c|0;if((d|0)<=-1){d=i[a+80>>2]+d|0}i[g>>2]=d;if((f|0)>-1){break t}f=i[a+80>>2]+f|0}i[(e<<2)+v>>2]=f;o=o+1|0;if((u|0)==(o|0)){break a}d=i[a+40>>2];j=i[d>>2];if(i[d+4>>2]-j>>2>>>0>o>>>0){continue}break}}Io();x()}F=k+48|0;return 1}function le(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0;f=F-96|0;F=f;e=i[a+16>>2];g[f+92|0]=1;i[f+88>>2]=b;i[f+84>>2]=b;i[f+80>>2]=e;d=-1;d=(b|0)!=-1?i[i[e>>2]+(b<<2)>>2]:d;h=i[a+20>>2];e=i[h>>2];a:{b:{if(i[h+4>>2]-e>>2>>>0>d>>>0){e=i[i[a+8>>2]+(i[e+(d<<2)>>2]<<2)>>2];d=i[a+4>>2];if(!j[d+84|0]){e=i[i[d+68>>2]+(e<<2)>>2]}i[f+72>>2]=0;i[f+76>>2]=0;h=f- -64|0;i[h>>2]=0;i[h+4>>2]=0;i[f+56>>2]=0;i[f+60>>2]=0;Qd(d,e,g[d+24|0],f+56|0);if((b|0)!=-1){d=b+1|0;h=(d>>>0)%3|0?d:b+ -2|0;m=((b>>>0)%3|0?-1:2)+b|0;n=f+48|0;o=f+40|0;while(1){e=h;d=m;c:{if(!i[a+28>>2]){break c}d=b+1|0;e=(d>>>0)%3|0?d:b+ -2|0;d=b+ -1|0;if((b>>>0)%3|0){break c}d=b+2|0}b=-1;b=(e|0)!=-1?i[i[i[a+16>>2]>>2]+(e<<2)>>2]:b;k=i[a+20>>2];e=i[k>>2];if(i[k+4>>2]-e>>2>>>0<=b>>>0){break b}e=i[i[a+8>>2]+(i[e+(b<<2)>>2]<<2)>>2];b=i[a+4>>2];if(!j[b+84|0]){e=i[i[b+68>>2]+(e<<2)>>2]}i[n>>2]=0;i[n+4>>2]=0;i[o>>2]=0;i[o+4>>2]=0;i[f+32>>2]=0;i[f+36>>2]=0;Qd(b,e,g[b+24|0],f+32|0);b=-1;b=(d|0)!=-1?i[i[i[a+16>>2]>>2]+(d<<2)>>2]:b;e=i[a+20>>2];d=i[e>>2];if(i[e+4>>2]-d>>2>>>0<=b>>>0){break a}d=i[i[a+8>>2]+(i[d+(b<<2)>>2]<<2)>>2];b=i[a+4>>2];if(!j[b+84|0]){d=i[i[b+68>>2]+(d<<2)>>2]}e=f+24|0;i[e>>2]=0;i[e+4>>2]=0;k=f+16|0;i[k>>2]=0;i[k+4>>2]=0;i[f+8>>2]=0;i[f+12>>2]=0;Qd(b,d,g[b+24|0],f+8|0);d=i[f+8>>2];b=i[f+56>>2];s=d-b|0;y=i[f+60>>2];t=i[f+12>>2]-(y+(d>>>0>>0)|0)|0;z=i[f+40>>2];d=i[f+64>>2];D=z-d|0;A=i[f+68>>2];z=i[f+44>>2]-(A+(z>>>0>>0)|0)|0;B=up(s,t,D,z);C=l-B|0;u=u-(H+(l>>>0>>0)|0)|0;l=i[k>>2];B=l-d|0;k=i[k+4>>2]-((l>>>0>>0)+A|0)|0;l=i[f+32>>2];A=l-b|0;y=i[f+36>>2]-((l>>>0>>0)+y|0)|0;d=up(B,k,A,y);l=d+C|0;b=H+u|0;b=l>>>0>>0?b+1|0:b;u=b;E=p;w=s;G=t;b=i[f+48>>2];p=i[f+72>>2];d=p;t=b-d|0;s=i[f+76>>2];C=i[f+52>>2]-(s+(b>>>0>>0)|0)|0;w=up(w,G,t,C);d=E+w|0;b=H+q|0;b=d>>>0>>0?b+1|0:b;q=i[e>>2];w=q-p|0;e=i[e+4>>2]-((q>>>0

>>0)+s|0)|0;q=up(w,e,A,y);p=d-q|0;q=b-(H+(d>>>0>>0)|0)|0;b=up(B,k,t,C);d=r-b|0;b=v-(H+(r>>>0>>0)|0)|0;v=up(w,e,D,z);r=v+d|0;b=H+b|0;b=r>>>0>>0?b+1|0:b;v=b;me(f+80|0);b=i[f+88>>2];if((b|0)!=-1){continue}break}}d=v;b=d>>31;d=d>>31;h=b;b=v+b|0;m=d+r|0;if(m>>>0>>0){b=b+1|0}o=d^m;n=b^h;t=-1;e=2147483647;d=q;b=d>>31;d=d>>31;m=b;b=q+b|0;k=d+p|0;if(k>>>0>>0){b=b+1|0}d=d^k;b=b^m;h=b;m=d^-1;b=b^2147483647;d:{if(!i[a+28>>2]){if((b|0)==(n|0)&o>>>0>m>>>0|n>>>0>b>>>0){break d}b=h+n|0;a=d+o|0;if(a>>>0>>0){b=b+1|0}d=b;h=a;b=u;e=b>>31;o=b>>31;b=o+b|0;k=e+l|0;if(k>>>0>>0){b=b+1|0}e=e^k;m=b^o;n=e+h|0;b=n;h=m^2147483647;a=(h|0)==(d|0)&a>>>0>(e^-1)>>>0|d>>>0>h>>>0;d=a?0:0;b=a?-1:b;a=b;if((b|0)<536870912?1:(b|0)<=536870912?d>>>0<1:0){break d}b=(a|0)/536870912|0;a=b;b=b>>31;l=vp(l,u,a,b);p=vp(p,q,a,b);r=vp(r,v,a,b);break d}e:{if((b|0)==(n|0)&o>>>0>m>>>0|n>>>0>b>>>0){break e}b=h+n|0;a=d+o|0;if(a>>>0>>0){b=b+1|0}m=b;n=a;d=b;h=u;b=h>>31;h=h>>31;k=b;b=u+b|0;s=h+l|0;if(s>>>0>>0){b=b+1|0}h=h^s;b=b^k;o=b;b=b^2147483647;if((b|0)==(d|0)&n>>>0>(h^-1)>>>0|d>>>0>b>>>0){break e}b=m+o|0;d=a+h|0;if(d>>>0>>0){b=b+1|0}t=d;e=b;if(!b&d>>>0<536870913|b>>>0<0){break d}}a=(e&536870911)<<3|t>>>29;b=e>>>29|0;l=vp(l,u,a,b);p=vp(p,q,a,b);r=vp(r,v,a,b)}i[c+8>>2]=l;i[c+4>>2]=p;i[c>>2]=r;F=f+96|0;return}Io();x()}Io();x()}Io();x()}function Yd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0;f=F-96|0;F=f;d=i[a+16>>2];g[f+92|0]=1;i[f+88>>2]=b;i[f+84>>2]=b;i[f+80>>2]=d;h=i[a+20>>2];e=i[h>>2];a:{b:{d=i[i[d+28>>2]+(b<<2)>>2];if(i[h+4>>2]-e>>2>>>0>d>>>0){d=i[i[a+8>>2]+(i[e+(d<<2)>>2]<<2)>>2];e=i[a+4>>2];if(!j[e+84|0]){d=i[i[e+68>>2]+(d<<2)>>2]}i[f+72>>2]=0;i[f+76>>2]=0;h=f- -64|0;i[h>>2]=0;i[h+4>>2]=0;i[f+56>>2]=0;i[f+60>>2]=0;Qd(e,d,g[e+24|0],f+56|0);if((b|0)!=-1){d=b+1|0;h=(d>>>0)%3|0?d:b+ -2|0;m=((b>>>0)%3|0?-1:2)+b|0;n=f+48|0;o=f+40|0;while(1){e=h;d=m;c:{if(!i[a+28>>2]){break c}d=b+1|0;e=(d>>>0)%3|0?d:b+ -2|0;d=b+ -1|0;if((b>>>0)%3|0){break c}d=b+2|0}k=i[a+20>>2];b=i[k>>2];e=i[i[i[a+16>>2]+28>>2]+(e<<2)>>2];if(i[k+4>>2]-b>>2>>>0<=e>>>0){break b}e=i[i[a+8>>2]+(i[b+(e<<2)>>2]<<2)>>2];b=i[a+4>>2];if(!j[b+84|0]){e=i[i[b+68>>2]+(e<<2)>>2]}i[n>>2]=0;i[n+4>>2]=0;i[o>>2]=0;i[o+4>>2]=0;i[f+32>>2]=0;i[f+36>>2]=0;Qd(b,e,g[b+24|0],f+32|0);e=i[a+20>>2];b=i[e>>2];d=i[i[i[a+16>>2]+28>>2]+(d<<2)>>2];if(i[e+4>>2]-b>>2>>>0<=d>>>0){break a}e=i[i[a+8>>2]+(i[b+(d<<2)>>2]<<2)>>2];b=i[a+4>>2];if(!j[b+84|0]){e=i[i[b+68>>2]+(e<<2)>>2]}k=f+24|0;d=k;i[d>>2]=0;i[d+4>>2]=0;r=f+16|0;d=r;i[d>>2]=0;i[d+4>>2]=0;i[f+8>>2]=0;i[f+12>>2]=0;Qd(b,e,g[b+24|0],f+8|0);d=i[f+8>>2];b=i[f+56>>2];t=d-b|0;y=i[f+60>>2];e=i[f+12>>2]-(y+(d>>>0>>0)|0)|0;z=i[f+40>>2];d=i[f+64>>2];D=z-d|0;A=i[f+68>>2];z=i[f+44>>2]-(A+(z>>>0>>0)|0)|0;B=up(t,e,D,z);C=l-B|0;u=u-(H+(l>>>0>>0)|0)|0;l=i[r>>2];B=l-d|0;r=i[r+4>>2]-((l>>>0>>0)+A|0)|0;l=i[f+32>>2];A=l-b|0;y=i[f+36>>2]-((l>>>0>>0)+y|0)|0;d=up(B,r,A,y);l=d+C|0;b=H+u|0;b=l>>>0>>0?b+1|0:b;u=b;E=p;w=t;G=e;b=i[f+48>>2];p=i[f+72>>2];d=p;e=b-d|0;t=i[f+76>>2];C=i[f+52>>2]-(t+(b>>>0>>0)|0)|0;w=up(w,G,e,C);d=E+w|0;b=H+q|0;b=d>>>0>>0?b+1|0:b;q=i[k>>2];w=q-p|0;k=i[k+4>>2]-((q>>>0

>>0)+t|0)|0;q=up(w,k,A,y);p=d-q|0;q=b-(H+(d>>>0>>0)|0)|0;b=up(B,r,e,C);d=s-b|0;b=v-(H+(s>>>0>>0)|0)|0;v=up(w,k,D,z);s=v+d|0;b=H+b|0;b=s>>>0>>0?b+1|0:b;v=b;$d(f+80|0);b=i[f+88>>2];if((b|0)!=-1){continue}break}}d=v;b=d>>31;d=d>>31;h=b;b=v+b|0;m=d+s|0;if(m>>>0>>0){b=b+1|0}o=d^m;n=b^h;r=-1;e=2147483647;d=q;b=d>>31;d=d>>31;m=b;b=q+b|0;k=d+p|0;if(k>>>0>>0){b=b+1|0}d=d^k;b=b^m;h=b;m=d^-1;b=b^2147483647;d:{if(!i[a+28>>2]){if((b|0)==(n|0)&o>>>0>m>>>0|n>>>0>b>>>0){break d}b=h+n|0;a=d+o|0;if(a>>>0>>0){b=b+1|0}d=b;h=a;b=u;e=b>>31;o=b>>31;b=o+b|0;k=e+l|0;if(k>>>0>>0){b=b+1|0}e=e^k;m=b^o;n=e+h|0;b=n;h=m^2147483647;a=(h|0)==(d|0)&a>>>0>(e^-1)>>>0|d>>>0>h>>>0;d=a?0:0;b=a?-1:b;a=b;if((b|0)<536870912?1:(b|0)<=536870912?d>>>0<1:0){break d}b=(a|0)/536870912|0;a=b;b=b>>31;l=vp(l,u,a,b);p=vp(p,q,a,b);s=vp(s,v,a,b);break d}e:{if((b|0)==(n|0)&o>>>0>m>>>0|n>>>0>b>>>0){break e}b=h+n|0;a=d+o|0;if(a>>>0>>0){b=b+1|0}m=b;n=a;d=b;h=u;b=h>>31;h=h>>31;k=b;b=u+b|0;t=h+l|0;if(t>>>0>>0){b=b+1|0}h=h^t;b=b^k;o=b;b=b^2147483647;if((b|0)==(d|0)&n>>>0>(h^-1)>>>0|d>>>0>b>>>0){break e}b=m+o|0;d=a+h|0;if(d>>>0>>0){b=b+1|0}r=d;e=b;if(!b&d>>>0<536870913|b>>>0<0){break d}}a=(e&536870911)<<3|r>>>29;b=e>>>29|0;l=vp(l,u,a,b);p=vp(p,q,a,b);s=vp(s,v,a,b)}i[c+8>>2]=l;i[c+4>>2]=p;i[c>>2]=s;F=f+96|0;return}Io();x()}Io();x()}Io();x()}function bm(a,b,c){var d=0,e=0,f=0,g=0,h=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0;i[a+56>>2]=i[a+52>>2];i[a+44>>2]=i[a+40>>2];a:{b:{c:{g=i[a+64>>2];d=i[g+24>>2];if((d|0)!=i[g+28>>2]){r=a+40|0;s=a+52|0;while(1){g=e;m=i[(q<<2)+d>>2];d:{if((m|0)==-1){break d}if((m|0)<0){d=-1}else{e=(m>>>0)/3|0;d=i[(i[b+96>>2]+o(e,12)|0)+(m-o(e,3)<<2)>>2]}d=j[c+84|0]?d:i[i[c+68>>2]+(d<<2)>>2];e=i[a+56>>2];f=i[a+60>>2];e:{if(e>>>0>>0){i[e>>2]=d;i[a+56>>2]=e+4;break e}n=i[s>>2];p=e-n|0;k=p>>2;l=k+1|0;if(l>>>0>=1073741824){break c}h=f-n|0;f=h>>1;f=h>>2>>>0<536870911?f>>>0>>0?l:f:1073741823;e=0;f:{if(!f){break f}if(f>>>0>=1073741824){break a}e=ho(f<<2)}h=e+(k<<2)|0;i[h>>2]=d;f=e+(f<<2)|0;d=h+4|0;if((p|0)>=1){hp(e,n,p)}i[a+60>>2]=f;i[a+56>>2]=d;i[a+52>>2]=e;if(!n){break e}bp(n)}g:{if(!(i[i[a+12>>2]+(q>>>3&536870908)>>2]>>>q&1)){break g}e=m+1|0;e=(e>>>0)%3|0?e:m+ -2|0;if((e|0)==-1|i[i[a>>2]+(e>>>3&536870908)>>2]>>>e&1){break g}d=i[i[i[a+64>>2]+12>>2]+(e<<2)>>2];if((d|0)==-1){break g}e=d+1|0;d=(e>>>0)%3|0?e:d+ -2|0;if((d|0)==-1){break g}while(1){e=d+1|0;m=d;e=(e>>>0)%3|0?e:d+ -2|0;if((e|0)==-1|i[i[a>>2]+(e>>>3&536870908)>>2]>>>e&1){break g}d=i[i[i[a+64>>2]+12>>2]+(e<<2)>>2];if((d|0)==-1){break g}e=d+1|0;d=(e>>>0)%3|0?e:d+ -2|0;if((d|0)!=-1){continue}break}}i[i[a+28>>2]+(m<<2)>>2]=g;e=i[a+44>>2];h:{if((e|0)!=i[a+48>>2]){i[e>>2]=m;i[a+44>>2]=e+4;break h}l=i[r>>2];k=e-l|0;h=k>>2;f=h+1|0;if(f>>>0>=1073741824){break b}d=k>>1;f=h>>>0<536870911?d>>>0>>0?f:d:1073741823;e=0;i:{if(!f){break i}if(f>>>0>=1073741824){break a}e=ho(f<<2)}d=e+(h<<2)|0;i[d>>2]=m;f=e+(f<<2)|0;d=d+4|0;if((k|0)>=1){hp(e,l,k)}i[a+48>>2]=f;i[a+44>>2]=d;i[a+40>>2]=e;if(!l){break h}bp(l)}e=g+1|0;d=((m>>>0)%3|0?-1:2)+m|0;if((d|0)==-1){break d}d=i[i[i[a+64>>2]+12>>2]+(d<<2)>>2];if((d|0)==-1){break d}d=d+((d>>>0)%3|0?-1:2)|0;if((d|0)==-1|(d|0)==(m|0)){break d}while(1){f=d+1|0;f=(f>>>0)%3|0?f:d+ -2|0;if(i[i[a>>2]+(f>>>3&536870908)>>2]>>>f&1){g=(d|0)<0?-1:i[(i[b+96>>2]+o((d>>>0)/3|0,12)|0)+((d>>>0)%3<<2)>>2];g=j[c+84|0]?g:i[i[c+68>>2]+(g<<2)>>2];f=i[a+56>>2];h=i[a+60>>2];j:{if(f>>>0>>0){i[f>>2]=g;i[a+56>>2]=f+4;break j}l=i[s>>2];n=f-l|0;f=n>>2;p=f+1|0;if(p>>>0>=1073741824){break c}t=f<<2;k=h-l|0;h=k>>1;h=k>>2>>>0<536870911?h>>>0

>>0?p:h:1073741823;f=0;k:{if(!h){break k}if(h>>>0>=1073741824){break a}f=ho(h<<2)}k=t+f|0;i[k>>2]=g;h=f+(h<<2)|0;g=k+4|0;if((n|0)>=1){hp(f,l,n)}i[a+60>>2]=h;i[a+56>>2]=g;i[a+52>>2]=f;if(!l){break j}bp(l)}f=e+1|0;g=i[a+44>>2];l:{if((g|0)!=i[a+48>>2]){i[g>>2]=d;i[a+44>>2]=g+4;break l}n=i[r>>2];p=g-n|0;l=p>>2;k=l+1|0;if(k>>>0>=1073741824){break b}h=p>>1;k=l>>>0<536870911?h>>>0>>0?k:h:1073741823;g=0;m:{if(!k){break m}if(k>>>0>=1073741824){break a}g=ho(k<<2)}h=g+(l<<2)|0;i[h>>2]=d;k=g+(k<<2)|0;h=h+4|0;if((p|0)>=1){hp(g,n,p)}i[a+48>>2]=k;i[a+44>>2]=h;i[a+40>>2]=g;if(!n){break l}bp(n)}g=e;e=f}i[i[a+28>>2]+(d<<2)>>2]=g;d=((d>>>0)%3|0?-1:2)+d|0;if((d|0)==-1){break d}d=i[i[i[a+64>>2]+12>>2]+(d<<2)>>2];if((d|0)==-1){break d}d=d+((d>>>0)%3|0?-1:2)|0;if((d|0)==-1){break d}if((d|0)!=(m|0)){continue}break}}g=i[a+64>>2];d=i[g+24>>2];q=q+1|0;if(q>>>0>2]-d>>2>>>0){continue}break}}return}Ho();x()}Ho();x()}za(16720);x()}function yi(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0;j=F-32|0;F=j;c=i[a+16>>2];i[a+20>>2]=c;a:{if((c|0)!=i[a+24>>2]){i[c>>2]=b;b=c+4|0;i[a+20>>2]=b;e=c;break a}e=ho(4);i[e>>2]=b;b=e+4|0;i[a+24>>2]=b;i[a+20>>2]=b;i[a+16>>2]=e;if(!c){break a}bp(c);b=i[a+20>>2];e=i[a+16>>2]}c=i[a+8>>2];q=i[c+100>>2]-i[c+96>>2]|0;c=(q|0)/12|0;if((b|0)!=(e|0)){t=a+16|0;u=(c|0)>1?c:1;v=a+120|0;m=a+200|0;w=a+72|0;while(1){b:{c:{c=b+ -4|0;b=i[c>>2];if((b|0)==-1){break c}e=i[a+28>>2];d=(b>>>0)/3|0;if(i[e+(d>>>3&268435452)>>2]>>>d&1){break c}h=0;if((q|0)<=0){break b}d:{e:{f:{while(1){g:{i[a+164>>2]=i[a+164>>2]+1;k=(b>>>0)/3|0;n=(b|0)==-1;l=n?-1:k;c=(l>>>3&536870908)+e|0;i[c>>2]=i[c>>2]|1<>2];h:{if((c|0)!=i[a+80>>2]){i[c>>2]=b;i[a+76>>2]=c+4;break h}e=i[w>>2];d=c-e|0;g=d>>2;f=g+1|0;if(f>>>0>=1073741824){break g}p=d>>1;f=g>>>0<536870911?p>>>0>>0?f:p:1073741823;c=0;i:{if(!f){break i}if(f>>>0>=1073741824){break f}c=ho(f<<2)}g=c+(g<<2)|0;i[g>>2]=b;f=c+(f<<2)|0;g=g+4|0;if((d|0)>=1){hp(c,e,d)}i[a+80>>2]=f;i[a+76>>2]=g;i[a+72>>2]=c;if(!e){break h}bp(e)}i[a+320>>2]=b;c=-1;c=(b|0)!=-1?i[i[i[a+12>>2]>>2]+(b<<2)>>2]:c;p=c<<2;g=i[p+i[a+152>>2]>>2];j:{k:{e=i[a+84>>2]+(c>>>3&536870908)|0;d=i[e>>2];c=1<>2]=c|d;if((g|0)!=-1){break k}Ci(m,0);c=-1;if((b|0)==-1){break j}e=b+1|0;b=(e>>>0)%3|0?e:b+ -2|0;if((b|0)==-1){break j}c=i[i[i[a+12>>2]+12>>2]+(b<<2)>>2];break j}l:{m:{n:{if(n){break n}e=-1;c=-1;d=b+1|0;f=(d>>>0)%3|0?d:b+ -2|0;if((f|0)!=-1){c=i[i[i[a+12>>2]+12>>2]+(f<<2)>>2]}d=(b-o(k,3)|0?-1:2)+b|0;if((d|0)!=-1){e=i[i[i[a+12>>2]+12>>2]+(d<<2)>>2]}k=(e|0)==-1;n=k?-1:(e>>>0)/3|0;y=(c>>>0)/3|0;o:{if((f|0)==-1){break o}r=i[i[a+12>>2]+12>>2];f=i[r+(f<<2)>>2];if((f|0)==-1){break o}s=i[a+28>>2];f=(f>>>0)/3|0;if(!(i[s+(f>>>3&268435452)>>2]>>>f&1)){break m}}b=(c|0)==-1;if(!b){oi(a,i[a+164>>2],1,b?-1:y)}p:{if((d|0)==-1){break p}b=i[i[i[a+12>>2]+12>>2]+(d<<2)>>2];if((b|0)==-1){break p}b=(b>>>0)/3|0;if(!(i[i[a+28>>2]+(b>>>3&268435452)>>2]>>>b&1)){break l}}if(k){break n}oi(a,i[a+164>>2],0,n)}Ci(m,7);i[a+20>>2]=i[a+20>>2]+ -4;break b}q:{r:{if((d|0)==-1){break r}d=i[(d<<2)+r>>2];if((d|0)==-1){break r}d=(d>>>0)/3|0;if(!(i[(d>>>3&268435452)+s>>2]>>>d&1)){break q}}if(!k){oi(a,i[a+164>>2],0,n)}Ci(m,3);break j}Ci(m,1);i[a+168>>2]=i[a+168>>2]+1;s:{if((g|0)==-1){break s}d=i[p+i[a+152>>2]>>2];if(i[i[a+140>>2]+(d>>>3&536870908)>>2]>>>d&1){break s}hi(a,b,0)}b=i[a+164>>2];i[j+4>>2]=l;i[j+16>>2]=j+4;pi(j+24|0,v,j+4|0,j+16|0);i[i[j+24>>2]+12>>2]=b;b=i[a+20>>2];i[b+ -4>>2]=e;if((b|0)!=i[a+24>>2]){i[b>>2]=c;i[a+20>>2]=b+4;break b}e=i[t>>2];d=b-e|0;h=d>>2;f=h+1|0;if(f>>>0>=1073741824){break e}l=d>>1;f=h>>>0<536870911?l>>>0>>0?f:l:1073741823;b=0;t:{if(!f){break t}if(f>>>0>=1073741824){break d}b=ho(f<<2)}h=b+(h<<2)|0;i[h>>2]=c;c=b+(f<<2)|0;f=h+4|0;if((d|0)>=1){hp(b,e,d)}i[a+24>>2]=c;i[a+20>>2]=f;i[a+16>>2]=b;if(!e){break b}bp(e);break b}Ci(m,5);c=e}h=h+1|0;if((u|0)==(h|0)){break b}e=i[a+28>>2];b=c;continue}break}Ho();x()}za(11708);x()}Ho();x()}za(11708);x()}i[a+20>>2]=c}b=i[a+20>>2];if((b|0)!=i[a+16>>2]){continue}break}}F=j+32|0;return 1}function ae(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;i[a+8>>2]=e;g=a+32|0;l=i[g>>2];f=i[a+36>>2]-l>>2;a:{if(f>>>0>>0){Bd(g,e-f|0);break a}if(f>>>0<=e>>>0){break a}i[a+36>>2]=l+(e<<2)}b:{if(!d){break b}h=1;g=i[b>>2];c:{if((d|0)<=1){f=g;break c}f=g;while(1){l=i[(h<<2)+b>>2];j=(l|0)<(f|0);f=j?l:f;g=j?g:(l|0)>(g|0)?l:g;h=h+1|0;if((h|0)!=(d|0)){continue}break}}i[a+16>>2]=g;i[a+12>>2]=f;d=(g>>31)-((f>>31)+(g>>>0>>0)|0)|0;f=g-f|0;if(!d&f>>>0>2147483646|d>>>0>0){break b}d=f+1|0;i[a+20>>2]=d;f=(d|0)/2|0;i[a+24>>2]=f;i[a+28>>2]=0-f;if(d&1){break b}i[a+24>>2]=f+ -1}d=(e&1073741823)!=(e|0)?-1:e<<2;l=ip(ho(d),0,d);d=i[a+56>>2];s=i[d>>2];d=i[d+4>>2]-s|0;if((d|0)>=5){t=i[a+48>>2];v=i[a+52>>2];u=d>>2;j=u;while(1){q=j;d:{e:{j=j+ -1|0;if(u>>>0>j>>>0){k=o(e,j);d=i[(j<<2)+s>>2];if((d|0)==-1){break e}d=i[i[t+12>>2]+(d<<2)>>2];if((d|0)==-1){break e}h=-1;g=i[v>>2];f=i[t>>2];p=i[g+(i[f+(d<<2)>>2]<<2)>>2];m=d+1|0;m=(m>>>0)%3|0?m:d+ -2|0;if((m|0)!=-1){m=i[f+(m<<2)>>2]}else{m=-1}d=d+((d>>>0)%3|0?-1:2)|0;if((d|0)!=-1){h=i[f+(d<<2)>>2]}if((p|0)>=(j|0)){break e}d=i[(m<<2)+g>>2];if((d|0)>=(j|0)){break e}f=i[g+(h<<2)>>2];if((f|0)>=(j|0)){break e}if((e|0)>=1){f=o(e,f);d=o(d,e);h=o(e,p);g=0;while(1){i[(g<<2)+l>>2]=(i[(f+g<<2)+b>>2]+i[(d+g<<2)+b>>2]|0)-i[(g+h<<2)+b>>2];g=g+1|0;if((g|0)!=(e|0)){continue}break}}f=i[a+8>>2];if((f|0)<1){break d}d=k<<2;m=d+c|0;r=b+d|0;d=l;h=0;while(1){g=0;f:{if((f|0)<=0){f=i[a+32>>2];break f}while(1){k=g<<2;p=i[k+d>>2];n=i[a+16>>2];g:{if((p|0)>(n|0)){f=i[a+32>>2];i[k+f>>2]=n;break g}f=i[a+32>>2];k=k+f|0;n=i[a+12>>2];if((p|0)<(n|0)){i[k>>2]=n;break g}i[k>>2]=p}g=g+1|0;if((g|0)>2]){continue}break}}g=h<<2;d=g+m|0;g=i[g+r>>2]-i[f+g>>2]|0;i[d>>2]=g;h:{if((g|0)>2]){g=g+i[a+20>>2]|0}else{if((g|0)<=i[a+24>>2]){break h}g=g-i[a+20>>2]|0}i[d>>2]=g}d=f;h=h+1|0;f=i[a+8>>2];if((h|0)<(f|0)){continue}break}break d}Io();x()}f=i[a+8>>2];if((f|0)<1){break d}d=k<<2;m=d+c|0;r=b+d|0;d=(o(q+ -2|0,e)<<2)+b|0;h=0;while(1){g=0;i:{if((f|0)<=0){f=i[a+32>>2];break i}while(1){k=g<<2;p=i[k+d>>2];n=i[a+16>>2];j:{if((p|0)>(n|0)){f=i[a+32>>2];i[k+f>>2]=n;break j}f=i[a+32>>2];k=k+f|0;n=i[a+12>>2];if((p|0)<(n|0)){i[k>>2]=n;break j}i[k>>2]=p}g=g+1|0;if((g|0)>2]){continue}break}}g=h<<2;d=g+m|0;g=i[g+r>>2]-i[f+g>>2]|0;i[d>>2]=g;k:{if((g|0)>2]){g=g+i[a+20>>2]|0}else{if((g|0)<=i[a+24>>2]){break k}g=g-i[a+20>>2]|0}i[d>>2]=g}d=f;h=h+1|0;f=i[a+8>>2];if((h|0)<(f|0)){continue}break}}if((q|0)>2){continue}break}}if((e|0)>=1){ip(l,0,e<<2)}f=i[a+8>>2];if((f|0)>=1){d=l;h=0;while(1){g=0;l:{if((f|0)<=0){f=i[a+32>>2];break l}while(1){e=g<<2;j=i[e+d>>2];q=i[a+16>>2];m:{if((j|0)>(q|0)){f=i[a+32>>2];i[e+f>>2]=q;break m}f=i[a+32>>2];e=e+f|0;q=i[a+12>>2];if((j|0)<(q|0)){i[e>>2]=q;break m}i[e>>2]=j}g=g+1|0;if((g|0)>2]){continue}break}}e=h<<2;d=e+c|0;e=i[b+e>>2]-i[e+f>>2]|0;i[d>>2]=e;n:{if((e|0)>2]){e=e+i[a+20>>2]|0}else{if((e|0)<=i[a+24>>2]){break n}e=e-i[a+20>>2]|0}i[d>>2]=e}d=f;h=h+1|0;f=i[a+8>>2];if((h|0)<(f|0)){continue}break}}bp(l);return 1}function Ad(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;i[a+8>>2]=e;g=a+32|0;l=i[g>>2];f=i[a+36>>2]-l>>2;a:{if(f>>>0>>0){Bd(g,e-f|0);break a}if(f>>>0<=e>>>0){break a}i[a+36>>2]=l+(e<<2)}b:{if(!d){break b}l=1;g=i[b>>2];c:{if((d|0)<=1){f=g;break c}f=g;while(1){h=i[(l<<2)+b>>2];n=(h|0)<(f|0);f=n?h:f;g=n?g:(h|0)>(g|0)?h:g;l=l+1|0;if((l|0)!=(d|0)){continue}break}}i[a+16>>2]=g;i[a+12>>2]=f;d=(g>>31)-((f>>31)+(g>>>0>>0)|0)|0;f=g-f|0;if(!d&f>>>0>2147483646|d>>>0>0){break b}d=f+1|0;i[a+20>>2]=d;f=(d|0)/2|0;i[a+24>>2]=f;i[a+28>>2]=0-f;if(d&1){break b}i[a+24>>2]=f+ -1}d=(e&1073741823)!=(e|0)?-1:e<<2;l=ip(ho(d),0,d);d=i[a+56>>2];t=i[d>>2];d=i[d+4>>2]-t|0;if((d|0)>=5){q=i[a+48>>2];v=i[a+52>>2];u=d>>2;h=u;while(1){n=h;d:{e:{h=h+ -1|0;if(u>>>0>h>>>0){k=o(e,h);d=i[(h<<2)+t>>2];if((d|0)==-1|i[i[q>>2]+(d>>>3&536870908)>>2]>>>d&1){break e}d=i[i[i[q+64>>2]+12>>2]+(d<<2)>>2];if((d|0)==-1){break e}g=i[v>>2];f=i[q+28>>2];j=i[g+(i[f+(d<<2)>>2]<<2)>>2];if((j|0)>=(h|0)){break e}m=d+1|0;m=i[g+(i[f+(((m>>>0)%3|0?m:d+ -2|0)<<2)>>2]<<2)>>2];if((m|0)>=(h|0)){break e}d=i[g+(i[f+(d+((d>>>0)%3|0?-1:2)<<2)>>2]<<2)>>2];if((d|0)>=(h|0)){break e}if((e|0)>=1){d=o(d,e);f=o(e,m);j=o(e,j);g=0;while(1){i[(g<<2)+l>>2]=(i[(d+g<<2)+b>>2]+i[(f+g<<2)+b>>2]|0)-i[(g+j<<2)+b>>2];g=g+1|0;if((g|0)!=(e|0)){continue}break}}f=i[a+8>>2];if((f|0)<1){break d}d=k<<2;r=d+c|0;s=b+d|0;d=l;k=0;while(1){g=0;f:{if((f|0)<=0){f=i[a+32>>2];break f}while(1){j=g<<2;m=i[j+d>>2];p=i[a+16>>2];g:{if((m|0)>(p|0)){f=i[a+32>>2];i[j+f>>2]=p;break g}f=i[a+32>>2];j=j+f|0;p=i[a+12>>2];if((m|0)<(p|0)){i[j>>2]=p;break g}i[j>>2]=m}g=g+1|0;if((g|0)>2]){continue}break}}g=k<<2;d=g+r|0;g=i[g+s>>2]-i[f+g>>2]|0;i[d>>2]=g;h:{if((g|0)>2]){g=g+i[a+20>>2]|0}else{if((g|0)<=i[a+24>>2]){break h}g=g-i[a+20>>2]|0}i[d>>2]=g}d=f;k=k+1|0;f=i[a+8>>2];if((k|0)<(f|0)){continue}break}break d}Io();x()}f=i[a+8>>2];if((f|0)<1){break d}d=k<<2;r=d+c|0;s=b+d|0;d=(o(n+ -2|0,e)<<2)+b|0;k=0;while(1){g=0;i:{if((f|0)<=0){f=i[a+32>>2];break i}while(1){j=g<<2;m=i[j+d>>2];p=i[a+16>>2];j:{if((m|0)>(p|0)){f=i[a+32>>2];i[j+f>>2]=p;break j}f=i[a+32>>2];j=j+f|0;p=i[a+12>>2];if((m|0)<(p|0)){i[j>>2]=p;break j}i[j>>2]=m}g=g+1|0;if((g|0)>2]){continue}break}}g=k<<2;d=g+r|0;g=i[g+s>>2]-i[f+g>>2]|0;i[d>>2]=g;k:{if((g|0)>2]){g=g+i[a+20>>2]|0}else{if((g|0)<=i[a+24>>2]){break k}g=g-i[a+20>>2]|0}i[d>>2]=g}d=f;k=k+1|0;f=i[a+8>>2];if((k|0)<(f|0)){continue}break}}if((n|0)>2){continue}break}}if((e|0)>=1){ip(l,0,e<<2)}f=i[a+8>>2];if((f|0)>=1){d=l;k=0;while(1){g=0;l:{if((f|0)<=0){f=i[a+32>>2];break l}while(1){e=g<<2;h=i[e+d>>2];n=i[a+16>>2];m:{if((h|0)>(n|0)){f=i[a+32>>2];i[e+f>>2]=n;break m}f=i[a+32>>2];e=e+f|0;n=i[a+12>>2];if((h|0)<(n|0)){i[e>>2]=n;break m}i[e>>2]=h}g=g+1|0;if((g|0)>2]){continue}break}}e=k<<2;d=e+c|0;e=i[b+e>>2]-i[e+f>>2]|0;i[d>>2]=e;n:{if((e|0)>2]){e=e+i[a+20>>2]|0}else{if((e|0)<=i[a+24>>2]){break n}e=e-i[a+20>>2]|0}i[d>>2]=e}d=f;k=k+1|0;f=i[a+8>>2];if((k|0)<(f|0)){continue}break}}bp(l);return 1}function bp(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0;a:{if(!a){break a}d=a+ -8|0;b=i[a+ -4>>2];a=b&-8;f=d+a|0;b:{if(b&1){break b}if(!(b&3)){break a}b=i[d>>2];d=d-b|0;if(d>>>0>>0<=255){e=i[d+8>>2];b=b>>>3|0;c=i[d+12>>2];if((c|0)==(e|0)){j=19292,k=i[4823]&yp(-2,b),i[j>>2]=k;break b}i[e+12>>2]=c;i[c+8>>2]=e;break b}h=i[d+24>>2];b=i[d+12>>2];c:{if((b|0)!=(d|0)){c=i[d+8>>2];i[c+12>>2]=b;i[b+8>>2]=c;break c}d:{e=d+20|0;c=i[e>>2];if(c){break d}e=d+16|0;c=i[e>>2];if(c){break d}b=0;break c}while(1){g=e;b=c;e=b+20|0;c=i[e>>2];if(c){continue}e=b+16|0;c=i[b+16>>2];if(c){continue}break}i[g>>2]=0}if(!h){break b}e=i[d+28>>2];c=(e<<2)+19596|0;e:{if(i[c>>2]==(d|0)){i[c>>2]=b;if(b){break e}j=19296,k=i[4824]&yp(-2,e),i[j>>2]=k;break b}i[h+(i[h+16>>2]==(d|0)?16:20)>>2]=b;if(!b){break b}}i[b+24>>2]=h;c=i[d+16>>2];if(c){i[b+16>>2]=c;i[c+24>>2]=b}c=i[d+20>>2];if(!c){break b}i[b+20>>2]=c;i[c+24>>2]=b;break b}b=i[f+4>>2];if((b&3)!=3){break b}i[4825]=a;i[f+4>>2]=b&-2;i[d+4>>2]=a|1;i[a+d>>2]=a;return}if(f>>>0<=d>>>0){break a}b=i[f+4>>2];if(!(b&1)){break a}f:{if(!(b&2)){if((f|0)==i[4829]){i[4829]=d;a=i[4826]+a|0;i[4826]=a;i[d+4>>2]=a|1;if(i[4828]!=(d|0)){break a}i[4825]=0;i[4828]=0;return}if((f|0)==i[4828]){i[4828]=d;a=i[4825]+a|0;i[4825]=a;i[d+4>>2]=a|1;i[a+d>>2]=a;return}a=(b&-8)+a|0;g:{if(b>>>0<=255){c=i[f+8>>2];b=b>>>3|0;e=i[f+12>>2];if((c|0)==(e|0)){j=19292,k=i[4823]&yp(-2,b),i[j>>2]=k;break g}i[c+12>>2]=e;i[e+8>>2]=c;break g}h=i[f+24>>2];b=i[f+12>>2];h:{if((f|0)!=(b|0)){c=i[f+8>>2];i[c+12>>2]=b;i[b+8>>2]=c;break h}i:{e=f+20|0;c=i[e>>2];if(c){break i}e=f+16|0;c=i[e>>2];if(c){break i}b=0;break h}while(1){g=e;b=c;e=b+20|0;c=i[e>>2];if(c){continue}e=b+16|0;c=i[b+16>>2];if(c){continue}break}i[g>>2]=0}if(!h){break g}e=i[f+28>>2];c=(e<<2)+19596|0;j:{if((f|0)==i[c>>2]){i[c>>2]=b;if(b){break j}j=19296,k=i[4824]&yp(-2,e),i[j>>2]=k;break g}i[h+((f|0)==i[h+16>>2]?16:20)>>2]=b;if(!b){break g}}i[b+24>>2]=h;c=i[f+16>>2];if(c){i[b+16>>2]=c;i[c+24>>2]=b}c=i[f+20>>2];if(!c){break g}i[b+20>>2]=c;i[c+24>>2]=b}i[d+4>>2]=a|1;i[a+d>>2]=a;if(i[4828]!=(d|0)){break f}i[4825]=a;return}i[f+4>>2]=b&-2;i[d+4>>2]=a|1;i[a+d>>2]=a}if(a>>>0<=255){a=a>>>3|0;b=(a<<3)+19332|0;c=i[4823];a=1<>2]}i[b+8>>2]=d;i[a+12>>2]=d;i[d+12>>2]=b;i[d+8>>2]=a;return}e=31;i[d+16>>2]=0;i[d+20>>2]=0;if(a>>>0<=16777215){b=a>>>8|0;g=b+1048320>>>16&8;b=b<>>16&4;b=b<>>16&2;b=(b<>>15|0)-(c|(e|g))|0;e=(b<<1|a>>>b+21&1)+28|0}i[d+28>>2]=e;g=(e<<2)+19596|0;l:{m:{c=i[4824];b=1<>2]=d;i[d+24>>2]=g;break n}e=a<<((e|0)==31?0:25-(e>>>1|0)|0);b=i[g>>2];while(1){c=b;if((i[b+4>>2]&-8)==(a|0)){break m}b=e>>>29|0;e=e<<1;g=(c+(b&4)|0)+16|0;b=i[g>>2];if(b){continue}break}i[g>>2]=d;i[d+24>>2]=c}i[d+12>>2]=d;i[d+8>>2]=d;break l}a=i[c+8>>2];i[a+12>>2]=d;i[c+8>>2]=d;i[d+24>>2]=0;i[d+12>>2]=c;i[d+8>>2]=a}a=i[4831]+ -1|0;i[4831]=a;if(a){break a}d=19748;while(1){a=i[d>>2];d=a+8|0;if(a){continue}break}i[4831]=-1}}function jd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,h=0,k=0,m=0,n=0,p=0,q=0,s=0;e=F-32|0;F=e;a:{b:{if(!i[i[a+8>>2]+80>>2]){d=1;break b}g[e+31|0]=254;k=i[a+36>>2];if(k){if(!(I[i[i[a>>2]+40>>2]](a,k)|0)){break b}d=i[a+36>>2];q=e,s=I[i[i[d>>2]+8>>2]](d)|0,g[q+31|0]=s}d=i[c+20>>2];if((d|0)<0?1:(d|0)<=0?l[c+16>>2]<=0:0){ca(c,i[c+4>>2],e+31|0,e+32|0)}d=i[a+36>>2];c:{if(!d){break c}q=e,s=I[i[i[d>>2]+36>>2]](d)|0,g[q+16|0]=s;d=i[c+20>>2];if((d|0)>0?1:(d|0)>=0?l[c+16>>2]>0:0){break c}ca(c,i[c+4>>2],e+16|0,e+17|0)}d=i[a+32>>2];h=i[d+48>>2];n=i[i[d>>2]>>2];k=i[d+80>>2];m=g[d+24|0];d=0;i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;k=o(k,m);if(k){if(k>>>0>=1073741824){break a}f=k<<2;d=ho(f);i[e+16>>2]=d;p=d+f|0;i[e+24>>2]=p;ip(d,0,f);i[e+20>>2]=p}f=h+n|0;h=i[a+36>>2];d:{e:{if(!h){break e}I[i[i[h>>2]+44>>2]](h,f,d,k,m,i[b>>2])|0;d=i[a+36>>2];if(!d){d=i[e+16>>2];break e}if(I[i[i[d>>2]+32>>2]](d)|0){break d}d=i[e+16>>2];f=i[a+36>>2]?d:f}Nj(f,k,d)}f:{g:{h:{i:{j:{d=i[a+4>>2];if(!d){break j}n=i[d+48>>2];d=ho(48);i[e>>2]=d;i[e+4>>2]=34;i[e+8>>2]=-2147483600;g[d+34|0]=0;f=j[2336]|j[2337]<<8;g[d+32|0]=f;g[d+33|0]=f>>>8;f=j[2332]|j[2333]<<8|(j[2334]<<16|j[2335]<<24);h=j[2328]|j[2329]<<8|(j[2330]<<16|j[2331]<<24);g[d+24|0]=h;g[d+25|0]=h>>>8;g[d+26|0]=h>>>16;g[d+27|0]=h>>>24;g[d+28|0]=f;g[d+29|0]=f>>>8;g[d+30|0]=f>>>16;g[d+31|0]=f>>>24;f=j[2324]|j[2325]<<8|(j[2326]<<16|j[2327]<<24);h=j[2320]|j[2321]<<8|(j[2322]<<16|j[2323]<<24);g[d+16|0]=h;g[d+17|0]=h>>>8;g[d+18|0]=h>>>16;g[d+19|0]=h>>>24;g[d+20|0]=f;g[d+21|0]=f>>>8;g[d+22|0]=f>>>16;g[d+23|0]=f>>>24;f=j[2316]|j[2317]<<8|(j[2318]<<16|j[2319]<<24);h=j[2312]|j[2313]<<8|(j[2314]<<16|j[2315]<<24);g[d+8|0]=h;g[d+9|0]=h>>>8;g[d+10|0]=h>>>16;g[d+11|0]=h>>>24;g[d+12|0]=f;g[d+13|0]=f>>>8;g[d+14|0]=f>>>16;g[d+15|0]=f>>>24;f=j[2308]|j[2309]<<8|(j[2310]<<16|j[2311]<<24);h=j[2304]|j[2305]<<8|(j[2306]<<16|j[2307]<<24);g[d|0]=h;g[d+1|0]=h>>>8;g[d+2|0]=h>>>16;g[d+3|0]=h>>>24;g[d+4|0]=f;g[d+5|0]=f>>>8;g[d+6|0]=f>>>16;g[d+7|0]=f>>>24;d=ck(n,e,1);if(g[e+11|0]<=-1){bp(i[e>>2])}if(d){break j}if(!k){break i}d=0;m=i[e+16>>2];b=0;while(1){b=i[m+(d<<2)>>2]|b;d=d+1|0;if((k|0)!=(d|0)){continue}break}if(!b){break i}b=4-(r(b)>>>3|0)|0;break h}g[e|0]=1;d=i[c+20>>2];if((d|0)<0?1:(d|0)<=0?l[c+16>>2]<=0:0){ca(c,i[c+4>>2],e,e+1|0)}d=Xj(e);k=i[a+4>>2];if(k){jg(d,10-_b(i[k+48>>2])|0)}b=kg(i[e+16>>2],o(m,i[b+4>>2]-i[b>>2]>>2),m,d,c);kd(d,i[d+4>>2]);d=0;if(b){break g}break f}b=1}g[e|0]=0;m=i[c+20>>2];d=m;f=i[c+16>>2];if((d|0)<0?1:(d|0)<=0?f>>>0<=0:0){ca(c,i[c+4>>2],e,e+1|0);f=i[c+16>>2];d=i[c+20>>2]}g[e|0]=b;if((d|0)<0?1:(d|0)<=0?f>>>0<=0:0){ca(c,i[c+4>>2],e,e+1|0)}if((Sj(5)|0)!=(b|0)){if(!k){break g}d=0;while(1){m=i[c+20>>2];if((m|0)<0?1:(m|0)<=0?l[c+16>>2]<=0:0){m=i[e+16>>2]+(d<<2)|0;ca(c,i[c+4>>2],m,b+m|0)}d=d+1|0;if((k|0)!=(d|0)){continue}break}break g}b=i[c+20>>2];if((b|0)>0?1:(b|0)>=0?l[c+16>>2]>0:0){break g}b=i[e+16>>2];ca(c,i[c+4>>2],b,b+(k<<2)|0)}d=1;a=i[a+36>>2];if(!a){break f}I[i[i[a>>2]+40>>2]](a,c)|0}a=i[e+16>>2];if(!a){break b}i[e+20>>2]=a;bp(a)}F=e+32|0;return d|0}Ho();x()}function mg(a,b,c,d,e,f){var h=0,k=0,m=0,n=0;n=F-16|0;F=n;a:{if((d|0)<=0){d=1}else{d=r(d)^31;if(d>>>0>17){break a}d=d+1|0}b:{if(!e){break b}h=ho(48);i[n>>2]=h;i[n+4>>2]=33;i[n+8>>2]=-2147483600;g[h+33|0]=0;g[h+32|0]=j[10857];k=j[10853]|j[10854]<<8|(j[10855]<<16|j[10856]<<24);m=j[10849]|j[10850]<<8|(j[10851]<<16|j[10852]<<24);g[h+24|0]=m;g[h+25|0]=m>>>8;g[h+26|0]=m>>>16;g[h+27|0]=m>>>24;g[h+28|0]=k;g[h+29|0]=k>>>8;g[h+30|0]=k>>>16;g[h+31|0]=k>>>24;k=j[10845]|j[10846]<<8|(j[10847]<<16|j[10848]<<24);m=j[10841]|j[10842]<<8|(j[10843]<<16|j[10844]<<24);g[h+16|0]=m;g[h+17|0]=m>>>8;g[h+18|0]=m>>>16;g[h+19|0]=m>>>24;g[h+20|0]=k;g[h+21|0]=k>>>8;g[h+22|0]=k>>>16;g[h+23|0]=k>>>24;k=j[10837]|j[10838]<<8|(j[10839]<<16|j[10840]<<24);m=j[10833]|j[10834]<<8|(j[10835]<<16|j[10836]<<24);g[h+8|0]=m;g[h+9|0]=m>>>8;g[h+10|0]=m>>>16;g[h+11|0]=m>>>24;g[h+12|0]=k;g[h+13|0]=k>>>8;g[h+14|0]=k>>>16;g[h+15|0]=k>>>24;k=j[10829]|j[10830]<<8|(j[10831]<<16|j[10832]<<24);m=j[10825]|j[10826]<<8|(j[10827]<<16|j[10828]<<24);g[h|0]=m;g[h+1|0]=m>>>8;g[h+2|0]=m>>>16;g[h+3|0]=m>>>24;g[h+4|0]=k;g[h+5|0]=k>>>8;g[h+6|0]=k>>>16;g[h+7|0]=k>>>24;h=Sb(e,n);if(g[n+11|0]<=-1){bp(i[n>>2])}if(!h){break b}h=ho(48);i[n>>2]=h;i[n+4>>2]=33;i[n+8>>2]=-2147483600;g[h+33|0]=0;g[h+32|0]=j[10857];k=j[10853]|j[10854]<<8|(j[10855]<<16|j[10856]<<24);m=j[10849]|j[10850]<<8|(j[10851]<<16|j[10852]<<24);g[h+24|0]=m;g[h+25|0]=m>>>8;g[h+26|0]=m>>>16;g[h+27|0]=m>>>24;g[h+28|0]=k;g[h+29|0]=k>>>8;g[h+30|0]=k>>>16;g[h+31|0]=k>>>24;k=j[10845]|j[10846]<<8|(j[10847]<<16|j[10848]<<24);m=j[10841]|j[10842]<<8|(j[10843]<<16|j[10844]<<24);g[h+16|0]=m;g[h+17|0]=m>>>8;g[h+18|0]=m>>>16;g[h+19|0]=m>>>24;g[h+20|0]=k;g[h+21|0]=k>>>8;g[h+22|0]=k>>>16;g[h+23|0]=k>>>24;k=j[10837]|j[10838]<<8|(j[10839]<<16|j[10840]<<24);m=j[10833]|j[10834]<<8|(j[10835]<<16|j[10836]<<24);g[h+8|0]=m;g[h+9|0]=m>>>8;g[h+10|0]=m>>>16;g[h+11|0]=m>>>24;g[h+12|0]=k;g[h+13|0]=k>>>8;g[h+14|0]=k>>>16;g[h+15|0]=k>>>24;k=j[10829]|j[10830]<<8|(j[10831]<<16|j[10832]<<24);m=j[10825]|j[10826]<<8|(j[10827]<<16|j[10828]<<24);g[h|0]=m;g[h+1|0]=m>>>8;g[h+2|0]=m>>>16;g[h+3|0]=m>>>24;g[h+4|0]=k;g[h+5|0]=k>>>8;g[h+6|0]=k>>>16;g[h+7|0]=k>>>24;e=_j(e,n);if(g[n+11|0]<=-1){bp(i[n>>2])}if((e|0)<=3){d=d+ -2|0;break b}if((e|0)<=5){d=d+ -1|0;break b}if((e|0)>=10){d=d+2|0;break b}d=((e|0)>7)+d|0}d=(d|0)>1?d:1;d=(d|0)<18?d:18;i[n+12>>2]=d;g[n|0]=d;e=i[f+20>>2];if((e|0)<0?1:(e|0)<=0?l[f+16>>2]<=0:0){ca(f,i[f+4>>2],n,n+1|0)}h=0;c:{switch(d|0){case 0:case 1:h=pg(a,b,c,f);break a;case 2:h=qg(a,b,c,f);break a;case 3:h=rg(a,b,c,f);break a;case 4:h=sg(a,b,c,f);break a;case 5:h=tg(a,b,c,f);break a;case 6:h=ug(a,b,c,f);break a;case 7:h=vg(a,b,c,f);break a;case 8:h=wg(a,b,c,f);break a;case 9:h=xg(a,b,c,f);break a;case 10:h=yg(a,b,c,f);break a;case 11:h=zg(a,b,c,f);break a;case 12:h=Ag(a,b,c,f);break a;case 13:h=Bg(a,b,c,f);break a;case 14:h=Cg(a,b,c,f);break a;case 15:h=Dg(a,b,c,f);break a;case 16:h=Eg(a,b,c,f);break a;case 17:h=Fg(a,b,c,f);break a;case 18:break c;default:break a}}h=Gg(a,b,c,f)}F=n+16|0;return h}function Tf(a,b,c,d){var e=0,f=0,h=0,k=0,l=0,m=0,n=0,o=0,p=0;k=F-16|0;F=k;h=ho(16);i[k>>2]=h;i[k+4>>2]=15;i[k+8>>2]=-2147483632;g[h+15|0]=0;e=j[10535]|j[10536]<<8|(j[10537]<<16|j[10538]<<24);f=j[10531]|j[10532]<<8|(j[10533]<<16|j[10534]<<24);g[h+7|0]=f;g[h+8|0]=f>>>8;g[h+9|0]=f>>>16;g[h+10|0]=f>>>24;g[h+11|0]=e;g[h+12|0]=e>>>8;g[h+13|0]=e>>>16;g[h+14|0]=e>>>24;e=j[10528]|j[10529]<<8|(j[10530]<<16|j[10531]<<24);f=j[10524]|j[10525]<<8|(j[10526]<<16|j[10527]<<24);g[h|0]=f;g[h+1|0]=f>>>8;g[h+2|0]=f>>>16;g[h+3|0]=f>>>24;g[h+4|0]=e;g[h+5|0]=e>>>8;g[h+6|0]=e>>>16;g[h+7|0]=e>>>24;h=b+4|0;o=$j(h,k,-1);if(g[k+11|0]<=-1){bp(i[k>>2])}p=14012;a:{b:{c:{switch(o+1|0){case 0:if((_b(h)|0)==10){break b}break;case 1:break b;default:break c}}d:{f=i[c+12>>2];l=i[c+8>>2];if((f-l|0)<1){break d}m=b+20|0;while(1){e:{e=i[i[(n<<2)+l>>2]+28>>2];if(e+ -1>>>0>=6){if((e|0)!=9){break e}e=ho(32);i[k>>2]=e;i[k+4>>2]=17;i[k+8>>2]=-2147483616;g[e+17|0]=0;g[e+16|0]=j[10556];f=j[10552]|j[10553]<<8|(j[10554]<<16|j[10555]<<24);l=j[10548]|j[10549]<<8|(j[10550]<<16|j[10551]<<24);g[e+8|0]=l;g[e+9|0]=l>>>8;g[e+10|0]=l>>>16;g[e+11|0]=l>>>24;g[e+12|0]=f;g[e+13|0]=f>>>8;g[e+14|0]=f>>>16;g[e+15|0]=f>>>24;f=j[10544]|j[10545]<<8|(j[10546]<<16|j[10547]<<24);l=j[10540]|j[10541]<<8|(j[10542]<<16|j[10543]<<24);g[e|0]=l;g[e+1|0]=l>>>8;g[e+2|0]=l>>>16;g[e+3|0]=l>>>24;g[e+4|0]=f;g[e+5|0]=f>>>8;g[e+6|0]=f>>>16;g[e+7|0]=f>>>24;f=m;l=i[f>>2];e=h;f:{if(!l){break f}while(1){e=i[l+16>>2]<(n|0);f=e?f:l;l=i[(e<<2)+l>>2];if(l){continue}break}e=h;if((f|0)==(m|0)){break f}e=h;if((n|0)>2]){break f}e=f+20|0;e=Sb(e,k)?e:h}e=$j(e,k,-1);if(g[k+11|0]<=-1){bp(i[k>>2])}if((e|0)<1){break e}l=i[c+8>>2];f=i[c+12>>2]}n=n+1|0;if((n|0)>2){continue}break d}break}if((o|0)!=1){break b}b=ho(32);i[k>>2]=b;i[k+4>>2]=24;i[k+8>>2]=-2147483616;g[b+24|0]=0;c=j[10578]|j[10579]<<8|(j[10580]<<16|j[10581]<<24);d=j[10574]|j[10575]<<8|(j[10576]<<16|j[10577]<<24);g[b+16|0]=d;g[b+17|0]=d>>>8;g[b+18|0]=d>>>16;g[b+19|0]=d>>>24;g[b+20|0]=c;g[b+21|0]=c>>>8;g[b+22|0]=c>>>16;g[b+23|0]=c>>>24;c=j[10570]|j[10571]<<8|(j[10572]<<16|j[10573]<<24);d=j[10566]|j[10567]<<8|(j[10568]<<16|j[10569]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[10562]|j[10563]<<8|(j[10564]<<16|j[10565]<<24);d=j[10558]|j[10559]<<8|(j[10560]<<16|j[10561]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;i[a>>2]=-1;ro(a+4|0,k);if(g[k+11|0]>-1){break a}bp(i[k>>2]);break a}p=13836}m=ho(56);e=m;i[e>>2]=0;i[e+4>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;i[e+24>>2]=0;i[e+28>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;i[e+8>>2]=0;i[e+12>>2]=0;f=xj(e);i[e>>2]=p;aa(f,c);yj(a,f,h,d);if(!i[a>>2]){if(g[a+15|0]<=-1){bp(i[a+4>>2])}c=i[m+52>>2];i[b+44>>2]=0;i[b+40>>2]=c;i[a+8>>2]=0;i[a+12>>2]=0;i[a>>2]=0;i[a+4>>2]=0}I[i[i[m>>2]+4>>2]](f)}F=k+16|0}function Bh(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,h=0,k=0,m=0,n=0;e=F-16|0;F=e;f=i[a+48>>2];b=ho(32);i[e>>2]=b;i[e+4>>2]=20;i[e+8>>2]=-2147483616;g[b+20|0]=0;c=j[11024]|j[11025]<<8|(j[11026]<<16|j[11027]<<24);g[b+16|0]=c;g[b+17|0]=c>>>8;g[b+18|0]=c>>>16;g[b+19|0]=c>>>24;c=j[11020]|j[11021]<<8|(j[11022]<<16|j[11023]<<24);d=j[11016]|j[11017]<<8|(j[11018]<<16|j[11019]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[11012]|j[11013]<<8|(j[11014]<<16|j[11015]<<24);d=j[11008]|j[11009]<<8|(j[11010]<<16|j[11011]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;f=bk(f+24|0,e);if(g[e+11|0]<=-1){bp(i[e>>2])}h=i[a+48>>2];b=ho(32);i[e>>2]=b;i[e+4>>2]=22;i[e+8>>2]=-2147483616;g[b+22|0]=0;c=j[11047]|j[11048]<<8|(j[11049]<<16|j[11050]<<24);d=j[11043]|j[11044]<<8|(j[11045]<<16|j[11046]<<24);g[b+14|0]=d;g[b+15|0]=d>>>8;g[b+16|0]=d>>>16;g[b+17|0]=d>>>24;g[b+18|0]=c;g[b+19|0]=c>>>8;g[b+20|0]=c>>>16;g[b+21|0]=c>>>24;c=j[11041]|j[11042]<<8|(j[11043]<<16|j[11044]<<24);d=j[11037]|j[11038]<<8|(j[11039]<<16|j[11040]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[11033]|j[11034]<<8|(j[11035]<<16|j[11036]<<24);d=j[11029]|j[11030]<<8|(j[11031]<<16|j[11032]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;h=bk(h+24|0,e);if(g[e+11|0]<=-1){bp(i[e>>2])}b=i[a+64>>2];i[a+64>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}b=i[a+56>>2];k=i[b+100>>2];m=i[a+48>>2];n=i[b+96>>2];b=ho(32);i[e>>2]=b;i[e+4>>2]=18;i[e+8>>2]=-2147483616;g[b+18|0]=0;c=j[11068]|j[11069]<<8;g[b+16|0]=c;g[b+17|0]=c>>>8;c=j[11064]|j[11065]<<8|(j[11066]<<16|j[11067]<<24);d=j[11060]|j[11061]<<8|(j[11062]<<16|j[11063]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[11056]|j[11057]<<8|(j[11058]<<16|j[11059]<<24);d=j[11052]|j[11053]<<8|(j[11054]<<16|j[11055]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;b=(k-n|0)/12|0;c=$j(m,e,-1);if(g[e+11|0]<=-1){bp(i[e>>2])}a:{b:{c:{d:{e:{f:{g:{switch(c+1|0){case 3:break f;case 0:break g;case 1:break e;default:break c}}if(!f){break f}if(b>>>0<1e3|(_b(i[a+48>>2])|0)>4|h^1){break d}}b=i[a+44>>2];g[e|0]=2;c=i[b+20>>2];if((c|0)<0?1:(c|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],e,e+1|0)}b=ho(360);si(b);c=i[a+64>>2];i[a+64>>2]=b;if(!c){break b}I[i[i[c>>2]+4>>2]](c);break c}if(!f){break c}}b=i[a+44>>2];g[e|0]=0;c=i[b+20>>2];if((c|0)<0?1:(c|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],e,e+1|0)}b=ho(296);Mh(b);c=i[a+64>>2];i[a+64>>2]=b;if(!c){break b}I[i[i[c>>2]+4>>2]](c)}b=i[a+64>>2];if(!b){break a}}a=I[i[i[b>>2]+8>>2]](b,a)|0;F=e+16|0;return a|0}F=e+16|0;return 0}function cm(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;i[a+56>>2]=i[a+52>>2];i[a+44>>2]=i[a+40>>2];a:{b:{c:{e=i[a+64>>2];d=i[e+24>>2];if((d|0)!=i[e+28>>2]){n=a+40|0;o=a+52|0;while(1){e=c;j=i[(l<<2)+d>>2];d:{if((j|0)==-1){break d}c=i[a+56>>2];e:{if((c|0)!=i[a+60>>2]){i[c>>2]=e;i[a+56>>2]=c+4;break e}h=i[o>>2];g=c-h|0;f=g>>2;d=f+1|0;if(d>>>0>=1073741824){break c}b=g>>1;d=f>>>0<536870911?b>>>0>>0?d:b:1073741823;c=0;f:{if(!d){break f}if(d>>>0>=1073741824){break a}c=ho(d<<2)}b=c+(f<<2)|0;i[b>>2]=e;d=c+(d<<2)|0;b=b+4|0;if((g|0)>=1){hp(c,h,g)}i[a+60>>2]=d;i[a+56>>2]=b;i[a+52>>2]=c;if(!h){break e}bp(h)}g:{if(!(i[i[a+12>>2]+(l>>>3&536870908)>>2]>>>l&1)){break g}c=j+1|0;c=(c>>>0)%3|0?c:j+ -2|0;if((c|0)==-1|i[i[a>>2]+(c>>>3&536870908)>>2]>>>c&1){break g}b=i[i[i[a+64>>2]+12>>2]+(c<<2)>>2];if((b|0)==-1){break g}c=b+1|0;d=(c>>>0)%3|0?c:b+ -2|0;if((d|0)==-1){break g}while(1){c=d+1|0;j=d;c=(c>>>0)%3|0?c:d+ -2|0;if((c|0)==-1|i[i[a>>2]+(c>>>3&536870908)>>2]>>>c&1){break g}b=i[i[i[a+64>>2]+12>>2]+(c<<2)>>2];if((b|0)==-1){break g}c=b+1|0;d=(c>>>0)%3|0?c:b+ -2|0;if((d|0)!=-1){continue}break}}i[i[a+28>>2]+(j<<2)>>2]=e;c=i[a+44>>2];h:{if((c|0)!=i[a+48>>2]){i[c>>2]=j;i[a+44>>2]=c+4;break h}h=i[n>>2];g=c-h|0;f=g>>2;d=f+1|0;if(d>>>0>=1073741824){break b}b=g>>1;d=f>>>0<536870911?b>>>0>>0?d:b:1073741823;c=0;i:{if(!d){break i}if(d>>>0>=1073741824){break a}c=ho(d<<2)}b=c+(f<<2)|0;i[b>>2]=j;d=c+(d<<2)|0;b=b+4|0;if((g|0)>=1){hp(c,h,g)}i[a+48>>2]=d;i[a+44>>2]=b;i[a+40>>2]=c;if(!h){break h}bp(h)}c=e+1|0;b=((j>>>0)%3|0?-1:2)+j|0;if((b|0)==-1){break d}b=i[i[i[a+64>>2]+12>>2]+(b<<2)>>2];if((b|0)==-1){break d}d=b+((b>>>0)%3|0?-1:2)|0;if((d|0)==-1|(d|0)==(j|0)){break d}while(1){b=d+1|0;b=(b>>>0)%3|0?b:d+ -2|0;if(i[i[a>>2]+(b>>>3&536870908)>>2]>>>b&1){e=i[a+56>>2];j:{if((e|0)!=i[a+60>>2]){i[e>>2]=c;i[a+56>>2]=e+4;break j}k=i[o>>2];h=e-k|0;g=h>>2;f=g+1|0;if(f>>>0>=1073741824){break c}b=h>>1;f=g>>>0<536870911?b>>>0>>0?f:b:1073741823;e=0;k:{if(!f){break k}if(f>>>0>=1073741824){break a}e=ho(f<<2)}b=e+(g<<2)|0;i[b>>2]=c;f=e+(f<<2)|0;b=b+4|0;if((h|0)>=1){hp(e,k,h)}i[a+60>>2]=f;i[a+56>>2]=b;i[a+52>>2]=e;if(!k){break j}bp(k)}b=c+1|0;e=i[a+44>>2];l:{if((e|0)!=i[a+48>>2]){i[e>>2]=d;i[a+44>>2]=e+4;break l}m=i[n>>2];k=e-m|0;h=k>>2;g=h+1|0;if(g>>>0>=1073741824){break b}f=k>>1;g=h>>>0<536870911?f>>>0>>0?g:f:1073741823;e=0;m:{if(!g){break m}if(g>>>0>=1073741824){break a}e=ho(g<<2)}f=e+(h<<2)|0;i[f>>2]=d;g=e+(g<<2)|0;f=f+4|0;if((k|0)>=1){hp(e,m,k)}i[a+48>>2]=g;i[a+44>>2]=f;i[a+40>>2]=e;if(!m){break l}bp(m)}e=c;c=b}i[i[a+28>>2]+(d<<2)>>2]=e;b=((d>>>0)%3|0?-1:2)+d|0;if((b|0)==-1){break d}b=i[i[i[a+64>>2]+12>>2]+(b<<2)>>2];if((b|0)==-1){break d}d=b+((b>>>0)%3|0?-1:2)|0;if((d|0)==-1){break d}if((d|0)!=(j|0)){continue}break}}e=i[a+64>>2];d=i[e+24>>2];l=l+1|0;if(l>>>0>2]-d>>2>>>0){continue}break}}return}Ho();x()}Ho();x()}za(16720);x()}function hj(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;if((b|0)==-1){return 1}d=(b>>>0)/3|0;if(!(i[i[a+24>>2]+(d>>>3&268435452)>>2]>>>d&1)){c=i[a+48>>2];i[a+52>>2]=c;a:{if((c|0)!=i[a+56>>2]){i[c>>2]=b;i[a+52>>2]=c+4;break a}f=ho(4);i[f>>2]=b;e=f+4|0;i[a+56>>2]=e;i[a+52>>2]=e;i[a+48>>2]=f;if(!c){break a}bp(c)}e=i[i[a+4>>2]+28>>2];c=b+1|0;f=(c>>>0)%3|0?c:b+ -2|0;c=i[e+(f<<2)>>2];if((c|0)==-1){return 0}h=(b-o(d,3)|0?-1:2)+b|0;d=i[e+(h<<2)>>2];if((d|0)==-1){return 0}b=i[a+36>>2];e=b+(c>>>3&536870908)|0;g=i[e>>2];j=1<>2]=g|j;Ti(a+8|0,c,f);b=i[a+36>>2]}b=(d>>>3&536870908)+b|0;c=i[b>>2];f=1<>2]=c|f;Ti(a+8|0,d,h)}c=i[a+52>>2];if((c|0)==i[a+48>>2]){return 1}l=a+48|0;m=a+8|0;while(1){c=c+ -4|0;b=i[c>>2];d=(b>>>0)/3|0;b:{c:{if((b|0)==-1){break c}f=i[a+24>>2]+(d>>>3&268435452)|0;e=i[f>>2];d=1<>2]=d|e;f=i[a+4>>2];c=i[i[f+28>>2]+(b<<2)>>2];if((c|0)==-1){return 0}while(1){d=b;d:{b=i[a+36>>2]+(c>>>3&536870908)|0;e=i[b>>2];h=1<>2]+(c<<2)>>2];if((g|0)!=-1){f:{j=g+1|0;g=(j>>>0)%3|0?j:g+ -2|0;if((g|0)==-1|i[i[f>>2]+(g>>>3&536870908)>>2]>>>g&1){break f}f=i[i[i[f+64>>2]+12>>2]+(g<<2)>>2];if((f|0)==-1){break f}i[b>>2]=e|h;Ti(m,c,d);b=f+1|0;if((((b>>>0)%3|0?b:f+ -2|0)|0)==-1){break e}b=-1;f=i[a+4>>2];g:{if((d|0)==-1){break g}c=d+1|0;c=(c>>>0)%3|0?c:d+ -2|0;if((c|0)==-1|i[i[f>>2]+(c>>>3&536870908)>>2]>>>c&1){break g}b=i[i[i[f+64>>2]+12>>2]+(c<<2)>>2]}c=(b>>>0)/3|0;g=1<>2];j=c>>>5|0;e=i[d+(j<<2)>>2];break d}}i[b>>2]=e|h;Ti(m,c,d)}h:{i:{if((d|0)==-1){break i}f=i[a+4>>2];b=-1;e=d+1|0;e=(e>>>0)%3|0?e:d+ -2|0;c=-1;j:{if((e|0)==-1){break j}c=-1;if(i[i[f>>2]+(e>>>3&536870908)>>2]>>>e&1){break j}c=i[i[i[f+64>>2]+12>>2]+(e<<2)>>2]}d=d+((d>>>0)%3|0?-1:2)|0;if(!((d|0)==-1|i[i[f>>2]+(d>>>3&536870908)>>2]>>>d&1)){b=i[i[i[f+64>>2]+12>>2]+(d<<2)>>2]}k=(b|0)==-1;h=k?-1:(b>>>0)/3|0;e=(c>>>0)/3|0;g=(c|0)==-1;if(!g){d=i[a+24>>2];g=g?-1:e;j=g>>>5|0;e=i[d+(j<<2)>>2];g=1<>2];j=h>>>5|0;e=i[d+(j<<2)>>2];if(!(g&e)){break d}}c=i[a+52>>2]+ -4|0;i[a+52>>2]=c;break b}if(k){b=c;break d}if(i[(h>>>3&536870908)+d>>2]>>>h&1){b=c;break d}d=i[a+52>>2];i[d+ -4>>2]=b;if((d|0)!=i[a+56>>2]){i[d>>2]=c;c=d+4|0;break c}k:{b=d;d=i[l>>2];f=b-d|0;h=f>>2;e=h+1|0;if(e>>>0<1073741824){g=f>>1;e=h>>>0<536870911?g>>>0>>0?e:g:1073741823;b=0;l:{if(!e){break l}if(e>>>0>=1073741824){break k}b=ho(e<<2)}h=b+(h<<2)|0;i[h>>2]=c;e=b+(e<<2)|0;c=h+4|0;if((f|0)>=1){hp(b,d,f)}i[a+56>>2]=e;i[a+52>>2]=c;i[a+48>>2]=b;if(!d){break b}bp(d);c=i[a+52>>2];break b}Ho();x()}za(11708);x()}i[(j<<2)+d>>2]=e|g;c=i[i[f+28>>2]+(b<<2)>>2];if((c|0)!=-1){continue}break}return 0}i[a+52>>2]=c}if(i[l>>2]!=(c|0)){continue}break}}return 1}function se(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,h=0,k=0,l=0,n=0;e=F-48|0;F=e;h=i[b+12>>2];k=i[i[b+4>>2]+48>>2];c=ho(32);i[e+24>>2]=c;i[e+28>>2]=17;i[e+32>>2]=-2147483616;g[c+17|0]=0;g[c+16|0]=j[6044];f=j[6040]|j[6041]<<8|(j[6042]<<16|j[6043]<<24);d=j[6036]|j[6037]<<8|(j[6038]<<16|j[6039]<<24);g[c+8|0]=d;g[c+9|0]=d>>>8;g[c+10|0]=d>>>16;g[c+11|0]=d>>>24;g[c+12|0]=f;g[c+13|0]=f>>>8;g[c+14|0]=f>>>16;g[c+15|0]=f>>>24;f=j[6032]|j[6033]<<8|(j[6034]<<16|j[6035]<<24);d=j[6028]|j[6029]<<8|(j[6030]<<16|j[6031]<<24);g[c|0]=d;g[c+1|0]=d>>>8;g[c+2|0]=d>>>16;g[c+3|0]=d>>>24;g[c+4|0]=f;g[c+5|0]=f>>>8;g[c+6|0]=f>>>16;g[c+7|0]=f>>>24;a:{b:{f=k+16|0;d=i[f>>2];if(!d){break b}c=f;while(1){l=i[d+16>>2]<(h|0);c=l?c:d;d=i[(l<<2)+d>>2];if(d){continue}break}if((c|0)==(f|0)|(h|0)>2]){break b}c=c+20|0;if(!Sb(c,e+24|0)){break b}c=$j(c,e+24|0,-1);break a}c=$j(k,e+24|0,-1)}if(g[e+35|0]<=-1){bp(i[e+24>>2])}i[e+32>>2]=-1;i[e+36>>2]=1065353216;i[e+24>>2]=-1;i[e+28>>2]=-1;i[e+40>>2]=-1;c=-1<>>0>28){break c}i[e+24>>2]=c+1;c=-2<>2]=c^-1;c=-2-c|0;i[e+32>>2]=c;i[e+40>>2]=(c|0)/2;m[e+36>>2]=p(2)/p(c|0)}h=ff(i[b+12>>2],i[b+4>>2]);k=i[b+12>>2];l=i[i[b+4>>2]+48>>2];c=ho(32);i[e+8>>2]=c;i[e+12>>2]=17;i[e+16>>2]=-2147483616;g[c+17|0]=0;g[c+16|0]=j[6184];f=j[6180]|j[6181]<<8|(j[6182]<<16|j[6183]<<24);d=j[6176]|j[6177]<<8|(j[6178]<<16|j[6179]<<24);g[c+8|0]=d;g[c+9|0]=d>>>8;g[c+10|0]=d>>>16;g[c+11|0]=d>>>24;g[c+12|0]=f;g[c+13|0]=f>>>8;g[c+14|0]=f>>>16;g[c+15|0]=f>>>24;f=j[6172]|j[6173]<<8|(j[6174]<<16|j[6175]<<24);d=j[6168]|j[6169]<<8|(j[6170]<<16|j[6171]<<24);g[c|0]=d;g[c+1|0]=d>>>8;g[c+2|0]=d>>>16;g[c+3|0]=d>>>24;g[c+4|0]=f;g[c+5|0]=f>>>8;g[c+6|0]=f>>>16;g[c+7|0]=f>>>24;d:{e:{f=l+16|0;d=i[f>>2];if(!d){break e}c=f;while(1){n=i[d+16>>2]<(k|0);c=n?c:d;d=i[(n<<2)+d>>2];if(d){continue}break}if((c|0)==(f|0)|(k|0)>2]){break e}c=c+20|0;if(!Sb(c,e+8|0)){break e}c=$j(c,e+8|0,h);break d}c=$j(l,e+8|0,h)}if(g[e+19|0]<=-1){bp(i[e+8>>2])}f:{g:{switch(c|0){case 6:c=i[b+4>>2];b=i[b+12>>2];f=i[i[i[c+4>>2]+8>>2]+(b<<2)>>2];h:{if((I[i[i[c>>2]+8>>2]](c)|0)==1){te(e+8|0,c,b,e+24|0);d=i[e+8>>2];if(d){break h}}d=ho(28);i[d+4>>2]=f;b=i[e+28>>2];i[d+8>>2]=i[e+24>>2];i[d+12>>2]=b;b=i[e+36>>2];i[d+16>>2]=i[e+32>>2];i[d+20>>2]=b;i[d+24>>2]=i[e+40>>2];i[d>>2]=9572}i[a>>2]=d;break f;case 0:c=i[b+4>>2];f=i[i[i[c+4>>2]+8>>2]+(i[b+12>>2]<<2)>>2];if((I[i[i[c>>2]+8>>2]](c)|0)==1){I[i[i[c>>2]+8>>2]](c)|0}b=ho(28);i[b+4>>2]=f;c=i[e+28>>2];i[b+8>>2]=i[e+24>>2];i[b+12>>2]=c;c=i[e+36>>2];i[b+16>>2]=i[e+32>>2];i[b+20>>2]=c;i[b+24>>2]=i[e+40>>2];i[b>>2]=9572;i[a>>2]=b;break f;default:break g}}i[a>>2]=0}F=e+48|0}function oj(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,k=0,m=0,n=0,p=0;f=F-16|0;F=f;c=i[b+56>>2];m=i[c+100>>2]-i[c+96>>2]|0;d=(m|0)/12|0;Jb(d,i[b+44>>2]);Jb(i[i[b+56>>2]+80>>2],i[b+44>>2]);n=i[b+48>>2];c=ho(32);i[f>>2]=c;i[f+4>>2]=21;i[f+8>>2]=-2147483616;g[c+21|0]=0;e=j[13293]|j[13294]<<8|(j[13295]<<16|j[13296]<<24);k=j[13289]|j[13290]<<8|(j[13291]<<16|j[13292]<<24);g[c+13|0]=k;g[c+14|0]=k>>>8;g[c+15|0]=k>>>16;g[c+16|0]=k>>>24;g[c+17|0]=e;g[c+18|0]=e>>>8;g[c+19|0]=e>>>16;g[c+20|0]=e>>>24;e=j[13288]|j[13289]<<8|(j[13290]<<16|j[13291]<<24);k=j[13284]|j[13285]<<8|(j[13286]<<16|j[13287]<<24);g[c+8|0]=k;g[c+9|0]=k>>>8;g[c+10|0]=k>>>16;g[c+11|0]=k>>>24;g[c+12|0]=e;g[c+13|0]=e>>>8;g[c+14|0]=e>>>16;g[c+15|0]=e>>>24;e=j[13280]|j[13281]<<8|(j[13282]<<16|j[13283]<<24);k=j[13276]|j[13277]<<8|(j[13278]<<16|j[13279]<<24);g[c|0]=k;g[c+1|0]=k>>>8;g[c+2|0]=k>>>16;g[c+3|0]=k>>>24;g[c+4|0]=e;g[c+5|0]=e>>>8;g[c+6|0]=e>>>16;g[c+7|0]=e>>>24;e=ck(n,f,0);if(g[f+11|0]<=-1){bp(i[f>>2])}c=i[b+44>>2];a:{if(e){g[f|0]=0;d=i[c+20>>2];if((d|0)<0?1:(d|0)<=0?l[c+16>>2]<=0:0){ca(c,i[c+4>>2],f,f+1|0)}pj(b);break a}g[f|0]=1;e=i[c+20>>2];if((e|0)<0?1:(e|0)<=0?l[c+16>>2]<=0:0){ca(c,i[c+4>>2],f,f+1|0)}e=i[b+56>>2];c=i[e+80>>2];if(c>>>0<=255){if(!m){break a}p=d>>>0>1?d:1;k=f+1|0;m=0;while(1){c=i[b+44>>2];n=i[e+96>>2]+o(m,12)|0;g[f|0]=i[n>>2];d=i[c+20>>2];e=i[c+16>>2];if((d|0)<0?1:(d|0)<=0?e>>>0<=0:0){ca(c,i[c+4>>2],f,k);c=i[b+44>>2];e=i[c+16>>2];d=i[c+20>>2]}g[f|0]=i[n+4>>2];if((d|0)<0?1:(d|0)<=0?e>>>0<=0:0){ca(c,i[c+4>>2],f,k);c=i[b+44>>2];e=i[c+16>>2];d=i[c+20>>2]}g[f|0]=i[n+8>>2];if((d|0)<0?1:(d|0)<=0?e>>>0<=0:0){ca(c,i[c+4>>2],f,k)}m=m+1|0;if((p|0)==(m|0)){break a}e=i[b+56>>2];continue}}if(c>>>0<=65535){if(!m){break a}p=d>>>0>1?d:1;k=f+2|0;m=0;while(1){c=i[b+44>>2];n=i[e+96>>2]+o(m,12)|0;h[f>>1]=i[n>>2];d=i[c+20>>2];e=i[c+16>>2];if((d|0)<0?1:(d|0)<=0?e>>>0<=0:0){ca(c,i[c+4>>2],f,k);c=i[b+44>>2];e=i[c+16>>2];d=i[c+20>>2]}h[f>>1]=i[n+4>>2];if((d|0)<0?1:(d|0)<=0?e>>>0<=0:0){ca(c,i[c+4>>2],f,k);c=i[b+44>>2];e=i[c+16>>2];d=i[c+20>>2]}h[f>>1]=i[n+8>>2];if((d|0)<0?1:(d|0)<=0?e>>>0<=0:0){ca(c,i[c+4>>2],f,k)}m=m+1|0;if((p|0)==(m|0)){break a}e=i[b+56>>2];continue}}if(c>>>0>=2097152){if(!m){break a}k=d>>>0>1?d:1;c=0;while(1){d=i[b+44>>2];m=i[d+20>>2];if((m|0)<0?1:(m|0)<=0?l[d+16>>2]<=0:0){n=d;m=i[d+4>>2];d=i[e+96>>2]+o(c,12)|0;ca(n,m,d,d+12|0)}c=c+1|0;if((k|0)==(c|0)){break a}e=i[b+56>>2];continue}}if(!m){break a}m=d>>>0>1?d:1;c=0;while(1){d=i[e+96>>2]+o(c,12)|0;Jb(i[d>>2],i[b+44>>2]);Jb(i[d+4>>2],i[b+44>>2]);Jb(i[d+8>>2],i[b+44>>2]);c=c+1|0;if((m|0)==(c|0)){break a}e=i[b+56>>2];continue}}i[a>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0;F=f+16|0}function zh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){zh(f,d,c);a=d+4|0;continue c}zh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function yh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){yh(f,d,c);a=d+4|0;continue c}yh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function xh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){xh(f,d,c);a=d+4|0;continue c}xh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function wh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){wh(f,d,c);a=d+4|0;continue c}wh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function vh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){vh(f,d,c);a=d+4|0;continue c}vh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function uh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){uh(f,d,c);a=d+4|0;continue c}uh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function th(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){th(f,d,c);a=d+4|0;continue c}th(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function sh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){sh(f,d,c);a=d+4|0;continue c}sh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function rh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){rh(f,d,c);a=d+4|0;continue c}rh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function qh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){qh(f,d,c);a=d+4|0;continue c}qh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function ph(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){ph(f,d,c);a=d+4|0;continue c}ph(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function oh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){oh(f,d,c);a=d+4|0;continue c}oh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function nh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){nh(f,d,c);a=d+4|0;continue c}nh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function mh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){mh(f,d,c);a=d+4|0;continue c}mh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function lh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){lh(f,d,c);a=d+4|0;continue c}lh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function kh(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){kh(f,d,c);a=d+4|0;continue c}kh(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function Hg(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){Hg(f,d,c);a=d+4|0;continue c}Hg(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function Ah(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0,o=0,p=0;a:{b:while(1){n=b+ -4|0;c:while(1){f=a;d:while(1){e:{f:{g:{h:{i:{j:{k:{l:{a=b-f|0;d=a>>2;switch(d|0){case 0:case 1:break a;case 5:break j;case 4:break k;case 2:break l;case 3:break g;default:break i}}c=i[c>>2];a=i[c>>2];c=i[c+4>>2]-a>>3;d=b+ -4|0;b=i[d>>2];if(c>>>0<=b>>>0){break h}n=c;c=i[f>>2];if(n>>>0<=c>>>0){break h}if(l[a+(b<<3)>>2]>=l[a+(c<<3)>>2]){break a}i[f>>2]=b;i[d>>2]=c;return}Kg(f,f+4|0,f+8|0,b+ -4|0,c);return}Lg(f,f+4|0,f+8|0,f+12|0,b+ -4|0,c);return}if((a|0)<=123){Mg(f,b,c);return}j=((d|0)/2<<2)+f|0;m:{if((a|0)>=3997){a=(d|0)/4<<2;m=Lg(f,a+f|0,j,a+j|0,n,c);break m}m=Ng(f,j,n,c)}a=i[c>>2];h=i[a>>2];g=i[a+4>>2]-h>>3;a=i[f>>2];if(g>>>0<=a>>>0){break e}d=i[j>>2];if(g>>>0<=d>>>0){break e}e=n;k=i[h+(a<<3)>>2];o=i[h+(d<<3)>>2];if(k>>>0>=o>>>0){while(1){e=e+ -4|0;if((e|0)==(f|0)){a=i[n>>2];if(g>>>0<=a>>>0){break e}d=f+4|0;if(k>>>0>2]){break f}if((d|0)==(n|0)){break a}while(1){e=i[d>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){i[d>>2]=a;i[n>>2]=e;d=d+4|0;break f}d=d+4|0;if((n|0)!=(d|0)){continue}break}break a}d=i[e>>2];if(g>>>0<=d>>>0){break e}if(l[h+(d<<3)>>2]>=o>>>0){continue}break}i[f>>2]=d;i[e>>2]=a;m=m+1|0}n:{d=f+4|0;if(d>>>0>=e>>>0){break n}a=i[d>>2];if(g>>>0<=a>>>0){break e}while(1){k=i[j>>2];o=h+(k<<3)|0;while(1){if(g>>>0<=k>>>0){break e}p=i[o>>2];if(l[h+(a<<3)>>2]

>>0){a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break e}break}while(1){e=e+ -4|0;k=i[e>>2];if(g>>>0<=k>>>0){break e}if(l[h+(k<<3)>>2]>=p>>>0){continue}break}if(d>>>0>e>>>0){break n}i[d>>2]=k;i[e>>2]=a;j=(d|0)==(j|0)?e:j;m=m+1|0;a=i[d+4>>2];d=d+4|0;if(g>>>0>a>>>0){continue}break}break e}o:{if((d|0)==(j|0)){break o}a=i[j>>2];if(g>>>0<=a>>>0){break e}e=i[d>>2];if(g>>>0<=e>>>0){break e}if(l[h+(a<<3)>>2]>=l[h+(e<<3)>>2]){break o}i[d>>2]=a;i[j>>2]=e;m=m+1|0}if(!m){e=Og(f,d,c);a=d+4|0;if(Og(a,b,c)){b=d;a=f;if(!e){continue b}break a}if(e){continue c}}if((d-f|0)<(b-d|0)){Ah(f,d,c);a=d+4|0;continue c}Ah(d+4|0,b,c);b=d;a=f;continue b}Io();x()}Ng(f,f+4|0,b+ -4|0,c);break a}if((d|0)==(n|0)){break a}a=n;e=i[f>>2];if(g>>>0<=e>>>0){break e}while(1){j=h+(e<<3)|0;while(1){m=i[d>>2];if(g>>>0<=m>>>0){break e}k=i[j>>2];if(k>>>0>=l[h+(m<<3)>>2]){d=d+4|0;if(g>>>0>e>>>0){continue}break e}break}while(1){a=a+ -4|0;e=i[a>>2];if(g>>>0<=e>>>0){break e}if(k>>>0>2]){continue}break}if(d>>>0>=a>>>0){f=d;continue d}i[d>>2]=e;i[a>>2]=m;d=d+4|0;e=i[f>>2];if(g>>>0>e>>>0){continue}break}}break}break}break}Io();x()}}function Ll(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0,z=0;e=F-48|0;F=e;a:{if(!b){break a}w=a+12|0;g=i[a+4>>2];m=i[a>>2];f=g-m|0;k=f>>2;d=i[a+12>>2];c=i[a+16>>2]-d>>2;b:{if(k>>>0>c>>>0){Ol(w,k-c|0);g=i[a+4>>2];m=i[a>>2];f=g-m|0;k=f>>2;break b}if(k>>>0>=c>>>0){break b}i[a+16>>2]=d+(k<<2)}i[e+40>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;c:{if(!f){break c}if(k>>>0<1073741824){l=ho(f);i[e+36>>2]=l;i[e+32>>2]=l;i[e+40>>2]=(k<<2)+l;d=l;f=0;while(1){c=i[(f<<2)+m>>2];j=d-l>>2;d:{if(c>>>0>>0){break d}i[e+16>>2]=0;d=c+1|0;if(d>>>0>j>>>0){Gb(e+32|0,d-j|0,e+16|0);m=i[a>>2];g=i[a+4>>2];l=i[e+32>>2];break d}if(d>>>0>=j>>>0){break d}i[e+36>>2]=(d<<2)+l}d=(c<<2)+l|0;i[d>>2]=i[d>>2]+1;f=f+1|0;h=g-m|0;k=h>>2;if(f>>>0>=k>>>0){break c}d=i[e+36>>2];continue}}za(16516);x()}i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;e:{f:{if(h){if(k>>>0>=536870912){break f}n=ho(h<<1);i[e+16>>2]=n;c=k<<3;d=c+n|0;i[e+24>>2]=d;ip(n,255,c);i[e+20>>2]=d}i[e+8>>2]=0;i[e>>2]=0;i[e+4>>2]=0;c=i[e+36>>2];g=c-l|0;u=g>>2;g:{if(g){if(u>>>0>=1073741824){break g}p=ho(g);i[e>>2]=p;d=(u<<2)+p|0;i[e+8>>2]=d;f=0;j=ip(p,0,g);i[e+4>>2]=d;d=l-c|0;c=((d|0)>(g|0)?d:g)>>>2|0;d=(g|0)>-1?g:-1;d=o(c,(d|0)<1?d:1);c=d>>>0>1?d:1;h=0;while(1){d=h<<2;i[d+j>>2]=f;f=i[d+l>>2]+f|0;h=h+1|0;if((c|0)!=(h|0)){continue}break}}if(!k){break e}while(1){y=q<<2;g=i[y+m>>2];r=-1;d=q+1|0;c=(d>>>0)%3|0?d:q+ -2|0;if((c|0)!=-1){r=i[(c<<2)+m>>2]}s=-1;f=(q>>>0)%3|0;c=(f?-1:2)+q|0;if((c|0)!=-1){s=i[(c<<2)+m>>2]}h:{if(!(((g|0)!=(s|0)?!((r|0)==(s|0)|(g|0)==(r|0)):0)|f)){i[a+40>>2]=i[a+40>>2]+1;d=q+3|0;break h}c=s<<2;v=i[c+l>>2];i:{j:{if((v|0)<1){break j}f=i[c+p>>2];h=0;while(1){j=(f<<3)+n|0;c=i[j>>2];if((c|0)==-1){break j}k:{if((c|0)!=(r|0)){break k}t=i[j+4>>2];if((t|0)!=-1){c=i[(t<<2)+m>>2]}else{c=-1}if((c|0)==(g|0)){break k}while(1){l:{c=f;h=h+1|0;if((h|0)>=(v|0)){break l}z=(c<<3)+n|0;f=c+1|0;g=(f<<3)+n|0;j=i[g>>2];i[z>>2]=j;i[z+4>>2]=i[g+4>>2];if((j|0)!=-1){continue}}break}i[(c<<3)+n>>2]=-1;if((t|0)==-1){break j}c=i[w>>2];i[c+y>>2]=t;i[c+(t<<2)>>2]=q;break i}f=f+1|0;h=h+1|0;if((v|0)!=(h|0)){continue}break}}f=r<<2;c=i[f+l>>2];if((c|0)<1){break i}f=i[f+p>>2];h=0;while(1){j=(f<<3)+n|0;if(i[j>>2]==-1){i[j>>2]=s;i[j+4>>2]=q;break i}f=f+1|0;h=h+1|0;if((c|0)!=(h|0)){continue}break}}}q=d;if(d>>>0>>0){continue}break}break e}Ho();x()}Ho();x()}i[b>>2]=u;if(p){i[e+4>>2]=p;bp(p)}a=i[e+16>>2];if(a){i[e+20>>2]=a;bp(a)}a=i[e+32>>2];if(!a){break a}i[e+36>>2]=a;bp(a)}F=e+48|0;return(b|0)!=0}function bj(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;if((b|0)==-1){return 1}d=(b>>>0)/3|0;if(!(i[i[a+24>>2]+(d>>>3&268435452)>>2]>>>d&1)){c=i[a+48>>2];i[a+52>>2]=c;a:{if((c|0)!=i[a+56>>2]){i[c>>2]=b;i[a+52>>2]=c+4;break a}e=ho(4);i[e>>2]=b;f=e+4|0;i[a+56>>2]=f;i[a+52>>2]=f;i[a+48>>2]=e;if(!c){break a}bp(c)}e=i[a+4>>2];c=-1;f=b+1|0;f=(f>>>0)%3|0?f:b+ -2|0;if((f|0)!=-1){c=i[i[e>>2]+(f<<2)>>2]}h=(b-o(d,3)|0?-1:2)+b|0;if((h|0)==-1){return 0}if((c|0)==-1){return 0}d=i[i[e>>2]+(h<<2)>>2];if((d|0)==-1){return 0}b=i[a+36>>2];e=b+(c>>>3&536870908)|0;g=i[e>>2];j=1<>2]=g|j;Ti(a+8|0,c,f);b=i[a+36>>2]}b=(d>>>3&536870908)+b|0;c=i[b>>2];e=1<>2]=c|e;Ti(a+8|0,d,h)}d=i[a+52>>2];if((d|0)==i[a+48>>2]){return 1}k=a+48|0;l=a+8|0;while(1){d=d+ -4|0;b=i[d>>2];c=(b>>>0)/3|0;b:{c:{if((b|0)==-1){break c}e=i[a+24>>2]+(c>>>3&268435452)|0;f=i[e>>2];c=1<>2]=c|f;while(1){h=i[a+4>>2];c=i[i[h>>2]+(b<<2)>>2];if((c|0)==-1){return 0}d:{d=i[a+36>>2]+(c>>>3&536870908)|0;e=i[d>>2];f=1<>2]+(c<<2)>>2];if((g|0)!=-1){j=g+1|0;g=(j>>>0)%3|0?j:g+ -2|0;if((g|0)!=-1){h=i[i[h+12>>2]+(g<<2)>>2];if((h|0)!=-1){i[d>>2]=e|f;Ti(l,c,b);c=h+1|0;if((((c>>>0)%3|0?c:h+ -2|0)|0)==-1){break e}d=b+ -2|0;c=b+1|0;b=-1;c=(c>>>0)%3|0?c:d;if((c|0)!=-1){b=i[i[i[a+4>>2]+12>>2]+(c<<2)>>2]}c=(b>>>0)/3|0;g=1<>2];j=c>>>5|0;f=i[e+(j<<2)>>2];break d}}}i[d>>2]=e|f;Ti(l,c,b)}d=-1;e=i[a+4>>2];c=-1;f=b+1|0;f=(f>>>0)%3|0?f:b+ -2|0;if((f|0)!=-1){c=i[i[e+12>>2]+(f<<2)>>2]}b=((b>>>0)%3|0?-1:2)+b|0;if((b|0)!=-1){d=i[i[e+12>>2]+(b<<2)>>2]}b=(d|0)==-1;h=b?-1:(d>>>0)/3|0;f=(c>>>0)/3|0;f:{g=(c|0)==-1;if(!g){e=i[a+24>>2];g=g?-1:f;j=g>>>5|0;f=i[e+(j<<2)>>2];g=1<>2];j=h>>>5|0;f=i[e+(j<<2)>>2];if(!(g&f)){break d}}d=i[a+52>>2]+ -4|0;i[a+52>>2]=d;break b}if(b){b=c;break d}if(i[(h>>>3&536870908)+e>>2]>>>h&1){b=c;break d}b=i[a+52>>2];i[b+ -4>>2]=d;if((b|0)!=i[a+56>>2]){i[b>>2]=c;d=b+4|0;break c}g:{e=i[k>>2];f=b-e|0;h=f>>2;d=h+1|0;if(d>>>0<1073741824){g=f>>1;d=h>>>0<536870911?g>>>0>>0?d:g:1073741823;b=0;h:{if(!d){break h}if(d>>>0>=1073741824){break g}b=ho(d<<2)}h=b+(h<<2)|0;i[h>>2]=c;c=b+(d<<2)|0;d=h+4|0;if((f|0)>=1){hp(b,e,f)}i[a+56>>2]=c;i[a+52>>2]=d;i[a+48>>2]=b;if(!e){break b}bp(e);d=i[a+52>>2];break b}Ho();x()}za(11708);x()}i[(j<<2)+e>>2]=f|g;if((b|0)!=-1){continue}break}return 0}i[a+52>>2]=d}if(i[k>>2]!=(d|0)){continue}break}}return 1}function Bj(a){var b=0,c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0;f=F+ -64|0;F=f;s=a+32|0;b=i[a+12>>2]-i[a+8>>2]>>2;e=i[a+32>>2];d=i[a+36>>2]-e>>2;a:{if(b>>>0>d>>>0){Bd(s,b-d|0);b=i[a+12>>2]-i[a+8>>2]>>2;break a}if(b>>>0>=d>>>0){break a}i[a+36>>2]=e+(b<<2)}g[f+32|0]=0;q=Cj(f+48|0,b,f+32|0);b=i[a+8>>2];c=i[a+12>>2];while(1){b:{c:{if(k>>>0>2>>>0){j=0;h=0;e=0;if((b|0)==(c|0)){break c}while(1){n=1<>>5<<2;d:{if(n&i[r+i[q>>2]>>2]){break d}d=0;p=1;m=e<<2;c=i[m+b>>2];h=i[c+4>>2];if((h|0)!=i[c+8>>2]){while(1){b=0;h=i[(d<<2)+h>>2];e:{if((I[i[i[c>>2]+24>>2]](c,h)|0)<1){break e}while(1){f:{c=i[m+i[a+8>>2]>>2];c=I[i[i[c>>2]+28>>2]](c,h,b)|0;if((c|0)==(e|0)){break f}c=i[i[a+20>>2]+(c<<2)>>2];if(i[i[q>>2]+(c>>>3&536870908)>>2]>>>c&1){break f}p=0;break e}b=b+1|0;c=i[m+i[a+8>>2]>>2];if((b|0)<(I[i[i[c>>2]+24>>2]](c,h)|0)){continue}break}}b=i[a+8>>2];c=i[m+b>>2];h=i[c+4>>2];d=d+1|0;if(d>>>0>2]-h>>2>>>0){continue}break}if(!p){break d}}i[i[s>>2]+(k<<2)>>2]=e;d=r+i[q>>2]|0;i[d>>2]=n|i[d>>2];j=1;k=k+1|0}e=e+1|0;c=i[a+12>>2];h=c-b>>2;if(e>>>0>>0){continue}break}break c}i[f+40>>2]=0;i[f+32>>2]=0;i[f+36>>2]=0;b=i[a+4>>2];d=i[b+12>>2];b=i[b+8>>2];g[f+15|0]=0;h=Cj(f+16|0,d-b>>2,f+15|0);c=i[a+8>>2];d=i[a+12>>2];g:{if((c|0)==(d|0)){l=1;break g}while(1){h:{k=i[i[s>>2]+(t<<2)>>2]<<2;b=i[k+c>>2];e=i[b+8>>2];j=i[b+4>>2];b=e-j|0;if((b|0)<8){break h}c=b>>2;l=i[f+32>>2];d=i[f+36>>2]-l>>2;i:{if(c>>>0>d>>>0){Bd(f+32|0,c-d|0);break i}if(c>>>0>=d>>>0){break i}i[f+36>>2]=l+(c<<2)}d=j-e|0;b=o(((d|0)>(b|0)?d:b)>>>2|0,(b|0)<1?b:1);r=(b|0)>1?b:1;j=0;while(1){j:{if((j|0)<(c|0)){e=i[h>>2];d=0;l=0;while(1){u=1<>>5<<2;k:{if(u&i[n+e>>2]){break k}b=0;e=i[k+i[a+8>>2]>>2];m=i[i[e+4>>2]+(d<<2)>>2];if((I[i[i[e>>2]+24>>2]](e,m)|0)>0){while(1){e=i[k+i[a+8>>2]>>2];p=I[i[i[e>>2]+28>>2]](e,m,b)|0;e=i[h>>2];if(!(i[e+(p>>>3&536870908)>>2]>>>p&1)){break k}b=b+1|0;e=i[k+i[a+8>>2]>>2];if((b|0)<(I[i[i[e>>2]+24>>2]](e,m)|0)){continue}break}}i[i[f+32>>2]+(j<<2)>>2]=d;e=i[h>>2];b=n+e|0;i[b>>2]=u|i[b>>2];l=1;j=j+1|0}d=d+1|0;if((r|0)!=(d|0)){continue}break}break j}Dj(i[k+i[a+8>>2]>>2],f+32|0);c=i[a+8>>2];d=i[a+12>>2];break h}if(!(l^1)|(j|0)>=(c|0)){continue}break}l=0;break g}l=1;t=t+1|0;if(t>>>0>2>>>0){continue}break}}a=i[h>>2];if(a){bp(a)}a=i[f+32>>2];if(!a){break b}i[f+36>>2]=a;bp(a);break b}if(j&1|k>>>0>=h>>>0){continue}}break}a=i[q>>2];if(a){bp(a)}F=f- -64|0;return l}function Ma(a,b){var c=0,d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;e=F-96|0;F=e;i[e+72>>2]=0;i[e+76>>2]=0;i[e+64>>2]=0;i[e+68>>2]=0;i[e+80>>2]=1065353216;c=i[a+80>>2];i[e+40>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;a:{b:{if(!c){break b}if(c>>>0>=1073741824){break a}c=c<<2;h=ho(c);i[e+32>>2]=h;d=c+h|0;i[e+40>>2]=d;ip(h,0,c);i[e+36>>2]=d;while(1){c=b;l=i[c+48>>2];d=i[i[c>>2]>>2];u=p;l=up(i[c+40>>2],i[c+44>>2],p,0)+l|0;c=d+l|0;l=j[c+12|0]|j[c+13|0]<<8|(j[c+14|0]<<16|j[c+15|0]<<24);q=j[c+8|0]|j[c+9|0]<<8|(j[c+10|0]<<16|j[c+11|0]<<24);i[e+56>>2]=q;i[e+60>>2]=l;d=j[c+4|0]|j[c+5|0]<<8|(j[c+6|0]<<16|j[c+7|0]<<24);l=j[c|0]|j[c+1|0]<<8|(j[c+2|0]<<16|j[c+3|0]<<24);i[e+48>>2]=l;i[e+52>>2]=d;s=i[e+52>>2];t=i[e+60>>2];c:{d:{e:{if(!m){break e}d=i[e+64>>2];r=(((l^318)+239^s)+239^q)+239^t;c=r&m+ -1;v=xp(m)>>>0>1;f:{if(!v){break f}c=r;if(c>>>0>>0){break f}c=(r>>>0)%(m>>>0)|0}d=i[(c<<2)+d>>2];if(!d){break e}d=i[d>>2];if(!d){break e}w=m+ -1|0;while(1){k=i[d+4>>2];g:{if((r|0)!=(k|0)){h:{if(!v){k=k&w;break h}if(k>>>0>>0){break h}k=(k>>>0)%(m>>>0)|0}if((c|0)==(k|0)){break g}break e}if(i[d+8>>2]!=(l|0)|i[d+12>>2]!=(s|0)|i[d+16>>2]!=(q|0)){break g}if(i[d+20>>2]==(t|0)){break d}}d=i[d>>2];if(d){continue}break}}i[e+24>>2]=n;i[e+20>>2]=t;i[e+16>>2]=q;i[e+12>>2]=s;i[e+8>>2]=l;_a(e+88|0,e- -64|0,e+8|0,e+8|0);c=i[a+40>>2];hp(i[i[a>>2]>>2]+o(c,n)|0,e+48|0,c);h=i[e+32>>2];i[h+(u<<2)>>2]=n;n=n+1|0;break c}i[(u<<2)+h>>2]=i[d+24>>2]}c=f;f=p+1|0;if(f>>>0<1){c=c+1|0}p=f;f=c;d=i[a+80>>2];if(!c&p>>>0>>0|c>>>0<0){m=i[e+68>>2];continue}break}if((d|0)!=(n|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}c=f-b|0;d=(c|0)>-1?c:-1;f=b-f|0;c=o((d|0)<1?d:1,((f|0)>(c|0)?f:c)>>>2|0);c=c>>>0>1?c:1;d=0;while(1){f=b+(d<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+h>>2];d=d+1|0;if((c|0)!=(d|0)){continue}break}break i}g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;j:{if(d>>>0>b>>>0){Xa(a+68|0,d-b|0,1420);d=i[a+80>>2];break j}if(d>>>0>=b>>>0){break j}i[a+72>>2]=c+(d<<2)}h=i[e+32>>2];if(!d){break i}b=i[a+68>>2];k=0;while(1){c=k<<2;i[c+b>>2]=i[c+h>>2];k=k+1|0;if((k|0)!=(d|0)){continue}break}}i[a+80>>2]=n}if(!h){break b}i[e+36>>2]=h;bp(h)}d=i[e+72>>2];if(d){while(1){a=i[d>>2];bp(d);d=a;if(d){continue}break}}a=i[e+64>>2];i[e+64>>2]=0;if(a){bp(a)}F=e+96|0;return n}Ho();x()}function jh(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*1048576+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==1048576){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;Ah(g,p,k+24|0);g:{if((n|0)<1048576){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)- -1048576;f=0;break g}c=n+ -1048576|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=1048576/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==1048576){n=1048576;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=1048576){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*9.5367431640625e-7)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function ih(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*1048576+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==1048576){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;zh(g,p,k+24|0);g:{if((n|0)<1048576){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)- -1048576;f=0;break g}c=n+ -1048576|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=1048576/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==1048576){n=1048576;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=1048576){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*9.5367431640625e-7)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function hh(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*1048576+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==1048576){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;yh(g,p,k+24|0);g:{if((n|0)<1048576){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)- -1048576;f=0;break g}c=n+ -1048576|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=1048576/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==1048576){n=1048576;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=1048576){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*9.5367431640625e-7)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function gh(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*1048576+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==1048576){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;xh(g,p,k+24|0);g:{if((n|0)<1048576){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)- -1048576;f=0;break g}c=n+ -1048576|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=1048576/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==1048576){n=1048576;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=1048576){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*9.5367431640625e-7)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function eh(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*1048576+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==1048576){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;wh(g,p,k+24|0);g:{if((n|0)<1048576){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)- -1048576;f=0;break g}c=n+ -1048576|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=1048576/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==1048576){n=1048576;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=1048576){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*9.5367431640625e-7)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function vm(a,b){var c=0,d=0,e=0,f=0,h=0,k=0;a:{b:{d=b;h=i[b>>2];if(h){c=i[b+4>>2];if(!c){break b}while(1){d=c;c=i[c>>2];if(c){continue}break}}h=i[d+4>>2];if(h){break b}h=0;e=1;break a}i[h+8>>2]=i[d+8>>2];e=0}f=i[d+8>>2];c=i[f>>2];c:{if((d|0)==(c|0)){i[f>>2]=h;if((a|0)==(d|0)){c=0;a=h;break c}c=i[f+4>>2];break c}i[f+4>>2]=h}d:{e:{f:{k=!j[d+12|0];if((b|0)!=(d|0)){f=i[b+8>>2];i[d+8>>2]=f;i[f+((i[i[b+8>>2]>>2]!=(b|0))<<2)>>2]=d;f=i[b>>2];i[d>>2]=f;i[f+8>>2]=d;f=i[b+4>>2];i[d+4>>2]=f;if(f){i[f+8>>2]=d}g[d+12|0]=j[b+12|0];a=(a|0)==(b|0)?d:a}if(!(k|!a)){if(e){while(1){b=j[c+12|0];g:{e=i[c+8>>2];if(i[e>>2]!=(c|0)){if(!b){g[c+12|0]=1;g[e+12|0]=0;d=i[e+4>>2];b=i[d>>2];i[e+4>>2]=b;if(b){i[b+8>>2]=e}i[d+8>>2]=i[e+8>>2];b=i[e+8>>2];i[(((e|0)!=i[b>>2])<<2)+b>>2]=d;i[d>>2]=e;i[e+8>>2]=d;b=i[c>>2];a=(b|0)==(a|0)?c:a;c=i[b+4>>2]}d=i[c>>2];if(!(j[d+12|0]?0:d)){b=i[c+4>>2];if(j[b+12|0]?0:b){break f}g[c+12|0]=0;c=i[c+8>>2];h:{if((c|0)==(a|0)){c=a;break h}if(j[c+12|0]){break g}}g[c+12|0]=1;return}b=i[c+4>>2];if(b){break f}break e}i:{if(b){b=c;break i}g[c+12|0]=1;g[e+12|0]=0;b=i[c+4>>2];i[e>>2]=b;if(b){i[b+8>>2]=e}i[c+8>>2]=i[e+8>>2];d=i[e+8>>2];j:{if((e|0)==i[d>>2]){i[d>>2]=c;b=i[e>>2];break j}i[d+4>>2]=c}i[c+4>>2]=e;i[e+8>>2]=c;a=(a|0)==(e|0)?c:a}d=i[b>>2];k:{if(!(!d|j[d+12|0])){c=b;break k}c=i[b+4>>2];if(!(j[c+12|0]?0:c)){g[b+12|0]=0;c=i[b+8>>2];if(j[c+12|0]?(c|0)!=(a|0):0){break g}g[c+12|0]=1;return}if(d){if(!j[d+12|0]){c=b;break k}c=i[b+4>>2]}g[c+12|0]=1;g[b+12|0]=0;a=i[c>>2];i[b+4>>2]=a;if(a){i[a+8>>2]=b}i[c+8>>2]=i[b+8>>2];a=i[b+8>>2];i[((i[a>>2]!=(b|0))<<2)+a>>2]=c;i[c>>2]=b;i[b+8>>2]=c;d=b}a=c;c=i[c+8>>2];g[a+12|0]=j[c+12|0];g[c+12|0]=1;g[d+12|0]=1;b=i[c>>2];a=i[b+4>>2];i[c>>2]=a;if(a){i[a+8>>2]=c}i[b+8>>2]=i[c+8>>2];a=i[c+8>>2];i[(((c|0)!=i[a>>2])<<2)+a>>2]=b;i[b+4>>2]=c;i[c+8>>2]=b;return}b=i[c+8>>2];c=i[((i[b>>2]==(c|0))<<2)+b>>2];continue}}g[h+12|0]=1}return}if(j[b+12|0]){break e}d=c;break d}g[d+12|0]=1;g[c+12|0]=0;a=i[d+4>>2];i[c>>2]=a;if(a){i[a+8>>2]=c}i[d+8>>2]=i[c+8>>2];a=i[c+8>>2];i[((i[a>>2]!=(c|0))<<2)+a>>2]=d;i[d+4>>2]=c;i[c+8>>2]=d;b=c}c=i[d+8>>2];g[d+12|0]=j[c+12|0];g[c+12|0]=1;g[b+12|0]=1;b=i[c+4>>2];a=i[b>>2];i[c+4>>2]=a;if(a){i[a+8>>2]=c}i[b+8>>2]=i[c+8>>2];a=i[c+8>>2];i[(((c|0)!=i[a>>2])<<2)+a>>2]=b;i[b>>2]=c;i[c+8>>2]=b}function ch(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*524288+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==524288){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;vh(g,p,k+24|0);g:{if((n|0)<524288){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+524288;f=0;break g}c=n+ -524288|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=524288/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==524288){n=524288;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=524288){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*19073486328125e-19)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function ah(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*262144+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==262144){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;uh(g,p,k+24|0);g:{if((n|0)<262144){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+262144;f=0;break g}c=n+ -262144|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=262144/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==262144){n=262144;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=262144){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*3814697265625e-18)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function Nl(a,b){var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;k=F-48|0;F=k;i[a+36>>2]=b;p=a+24|0;d=i[a+24>>2];c=i[a+28>>2]-d>>2;a:{if(c>>>0>>0){Ol(p,b-c|0);break a}if(c>>>0<=b>>>0){break a}i[a+28>>2]=d+(b<<2)}g[k+16|0]=0;l=Cj(k+32|0,b,k+16|0);c=i[a>>2];d=i[a+4>>2];g[k+8|0]=0;q=Cj(k+16|0,d-c>>2,k+8|0);b:{h=i[a+4>>2];c=i[a>>2];if(h-c>>2>>>0<3){break b}t=a+48|0;while(1){m=o(r,3);e=i[(m<<2)+c>>2];d=m+1|0;c:{d:{if((d|0)==-1){j=0;f=-1;break d}f=i[(d<<2)+c>>2];j=m+2|0;d=-1;if((j|0)==-1){break c}}d=i[(j<<2)+c>>2]}e:{if(!((d|0)==(f|0)|(e|0)==(f|0)|(d|0)==(e|0))){j=i[q>>2];n=0;while(1){d=m+n|0;f:{if(i[(d>>>3&536870908)+j>>2]>>>d&1){break f}h=i[i[a>>2]+(d<<2)>>2];i[k+8>>2]=h;c=1<>2];f=h>>>5|0;j=i[e+(f<<2)>>2];s=c&j;if(s){Pl(p,16512);Pl(t,k+8|0);c=i[l+4>>2];f=i[l+8>>2];if((c|0)==f<<5){if((c+1|0)<=-1){break e}e=l;if(c>>>0<=1073741822){h=c+32&-32;c=f<<6;c=c>>>0>>0?h:c}else{c=2147483647}bd(e,c);c=i[l+4>>2]}i[l+4>>2]=c+1;e=i[l>>2];f=e+(c>>>3&536870908)|0;h=i[f>>2];u=f,v=yp(-2,c)&h,i[u>>2]=v;i[k+8>>2]=b;c=1<>>5|0;j=i[(f<<2)+e>>2];b=b+1|0}i[(f<<2)+e>>2]=c|j;j=i[q>>2];c=d;g:{while(1){if((c|0)==-1){break g}e=(c>>>3&536870908)+j|0;i[e>>2]=i[e>>2]|1<>2];i[i[p>>2]+(e<<2)>>2]=c;if(s){i[i[a>>2]+(c<<2)>>2]=e}h=d;f=c+1|0;c=(f>>>0)%3|0?f:c+ -2|0;e=-1;h:{if((c|0)==-1){break h}f=i[i[a+12>>2]+(c<<2)>>2];e=-1;if((f|0)==-1){break h}c=f+1|0;e=(c>>>0)%3|0?c:f+ -2|0}c=e;if((h|0)!=(c|0)){continue}break}if((d|0)!=-1){break f}}d=d+((d>>>0)%3|0?-1:2)|0;if((d|0)==-1){break f}d=i[i[a+12>>2]+(d<<2)>>2];if((d|0)==-1){break f}c=d+((d>>>0)%3|0?-1:2)|0;if((c|0)==-1){break f}while(1){d=(c>>>3&536870908)+j|0;i[d>>2]=i[d>>2]|1<>2]+(c<<2)>>2]=i[k+8>>2]}d=((c>>>0)%3|0?-1:2)+c|0;if((d|0)==-1){break f}d=i[i[a+12>>2]+(d<<2)>>2];if((d|0)==-1){break f}c=d+((d>>>0)%3|0?-1:2)|0;if((c|0)!=-1){continue}break}}n=n+1|0;if((n|0)!=3){continue}break}c=i[a>>2];h=i[a+4>>2]}r=r+1|0;if(r>>>0<(h-c>>2>>>0)/3>>>0){continue}break b}break}Ho();x()}j=0;i[a+44>>2]=0;b=i[l>>2];c=i[l+4>>2];d=c>>>5|0;f=c&31;if(d|f){h=(d<<2)+b|0;c=0;e=b;while(1){if(!(i[e>>2]>>>c&1)){j=j+1|0;i[a+44>>2]=j}d=(c|0)==31;c=d?0:c+1|0;e=d?e+4|0:e;if((h|0)!=(e|0)|(c|0)!=(f|0)){continue}break}}a=i[q>>2];if(a){bp(a);b=i[l>>2]}if(b){bp(b)}F=k+48|0}function _g(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*65536+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==65536){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;th(g,p,k+24|0);g:{if((n|0)<65536){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+65536;f=0;break g}c=n+ -65536|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=65536/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==65536){n=65536;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=65536){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*152587890625e-16)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function Yg(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*32768+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==32768){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;sh(g,p,k+24|0);g:{if((n|0)<32768){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+32768;f=0;break g}c=n+ -32768|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=32768/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==32768){n=32768;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=32768){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*30517578125e-15)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function Wg(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*8192+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==8192){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;rh(g,p,k+24|0);g:{if((n|0)<8192){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)- -8192;f=0;break g}c=n+ -8192|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=8192/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==8192){n=8192;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=8192){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*.0001220703125)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function ng(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*4096+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==4096){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;Hg(g,p,k+24|0);g:{if((n|0)<4096){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+4096;f=0;break g}c=n+ -4096|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=4096/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==4096){n=4096;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=4096){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*.000244140625)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function Vg(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*4096+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==4096){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;qh(g,p,k+24|0);g:{if((n|0)<4096){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+4096;f=0;break g}c=n+ -4096|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=4096/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==4096){n=4096;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=4096){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*.000244140625)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function Ug(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*4096+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==4096){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;ph(g,p,k+24|0);g:{if((n|0)<4096){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+4096;f=0;break g}c=n+ -4096|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=4096/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==4096){n=4096;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=4096){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*.000244140625)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function Tg(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*4096+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==4096){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;oh(g,p,k+24|0);g:{if((n|0)<4096){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+4096;f=0;break g}c=n+ -4096|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=4096/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==4096){n=4096;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=4096){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*.000244140625)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function Sg(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*4096+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==4096){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;nh(g,p,k+24|0);g:{if((n|0)<4096){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+4096;f=0;break g}c=n+ -4096|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=4096/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==4096){n=4096;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=4096){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*.000244140625)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function Rg(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*4096+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==4096){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;mh(g,p,k+24|0);g:{if((n|0)<4096){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+4096;f=0;break g}c=n+ -4096|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=4096/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==4096){n=4096;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=4096){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*.000244140625)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function Qg(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*4096+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==4096){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;lh(g,p,k+24|0);g:{if((n|0)<4096){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+4096;f=0;break g}c=n+ -4096|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=4096/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==4096){n=4096;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=4096){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*.000244140625)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function Pg(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,r=0,t=0,w=0,y=0,z=0;k=F-32|0;F=k;if((c|0)<=0){j=0}else{while(1){r=h;h=(f<<3)+b|0;l=i[h+4>>2];g=i[h>>2];h=!(l|g)?r:f;e=e+l|0;l=g+m|0;if(l>>>0>>0){e=e+1|0}m=l;f=f+1|0;if((f|0)!=(c|0)){continue}break}j=+(m>>>0)+ +(e>>>0)*4294967296}e=h+1|0;i[a+12>>2]=e;g=i[a>>2];c=i[a+4>>2]-g>>3;a:{if(e>>>0>c>>>0){ig(a,e-c|0);break a}if(e>>>0>=c>>>0){break a}i[a+4>>2]=g+(e<<3)}b:{c:{d:{e:{if((h|0)>=0){g=i[a>>2];f=0;while(1){l=f<<3;m=l+b|0;c=i[m+4>>2];m=i[m>>2];t=(m|0)!=0|(c|0)!=0;l=g+l|0;o=(+(m>>>0)+ +(c>>>0)*4294967296)/j*4096+.5;f:{if(o<4294967296&o>=0){c=~~o>>>0;break f}c=0}c=t?c?c:1:c;i[l>>2]=c;n=c+n|0;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}if((n|0)==4096){break e}}i[k+16>>2]=0;i[k+8>>2]=0;i[k+12>>2]=0;g=0;if(e){if(e>>>0>=1073741824){break b}c=e<<2;g=ho(c);i[k+8>>2]=g;p=c+g|0;i[k+16>>2]=p;ip(g,0,c);i[k+12>>2]=p}if((h|0)>-1){f=0;while(1){i[(f<<2)+g>>2]=f;c=(f|0)==(h|0);f=f+1|0;if(!c){continue}break}}i[k+24>>2]=a;kh(g,p,k+24|0);g:{if((n|0)<4096){c=i[a>>2]+(i[i[k+12>>2]+ -4>>2]<<3)|0;i[c>>2]=(i[c>>2]-n|0)+4096;f=0;break g}c=n+ -4096|0;l=i[k+8>>2];m=(h|0)<1;while(1){h:{if(m){break h}j=4096/+(n|0);p=i[a>>2];g=h;while(1){f=p+(i[(g<<2)+l>>2]<<3)|0;e=i[f>>2];if(e>>>0<=1){if((g|0)!=(h|0)){break h}f=1;break g}t=e+ -1|0;r=f;y=e;z=c;f=e;o=u(j*+(e>>>0));i:{if(q(o)<2147483648){w=~~o;break i}w=-2147483648}f=f-w|0;f=f?f:1;e=(f|0)<(e|0)?f:t;e=(e|0)>(c|0)?z:e;i[r>>2]=y-e;c=c-e|0;n=n-e|0;if((n|0)==4096){n=4096;break h}e=(g|0)>1;g=g+ -1|0;if(e){continue}break}}f=0;if((c|0)>0){continue}break}}c=i[k+8>>2];if(c){i[k+12>>2]=c;bp(c)}if(!((h|0)<0|f)){break d}break c}if((h|0)<0){break c}}e=i[a>>2];f=0;c=0;while(1){g=e+(f<<3)|0;i[g+4>>2]=c;c=i[g>>2]+c|0;g=(f|0)==(h|0);f=f+1|0;if(!g){continue}break}if((c|0)!=4096){break c}f=0;j=0;if((h|0)>=0){c=i[a>>2];while(1){e=f<<3;g=i[e+c>>2];if(g){e=b+e|0;l=i[e+4>>2];e=i[e>>2];j=j+ep(+(g>>>0)*.000244140625)*(+(e>>>0)+ +(l>>>0)*4294967296)}e=(f|0)==(h|0);f=f+1|0;if(!e){continue}break}}b=a;c=a;j=v(-j);j:{if(j<0x10000000000000000&j>=0){e=q(j)>=1?j>0?~~s(u(j*2.3283064365386963e-10),4294967295)>>>0:~~v((j- +(~~j>>>0>>>0))*2.3283064365386963e-10)>>>0:0;h=~~j>>>0;break j}e=0;h=0}i[c+16>>2]=h;i[b+20>>2]=e;Ig(a,d)}F=k+32|0;return}Ho();x()}function Ci(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;m=F-16|0;F=m;i[a+124>>2]=i[a+124>>2]+1;g=-1;d=i[a+120>>2];c=-1;a:{if((d|0)==-1){break a}c=d+1|0;g=(c>>>0)%3|0?c:d+ -2|0;c=d+ -1|0;if((d>>>0)%3|0){break a}c=d+2|0}e=i[a+104>>2];f=i[a+92>>2];h=f+(g<<2)|0;j=e+(i[h>>2]<<2)|0;k=i[j>>2];b:{c:{switch(b|0){case 0:case 1:i[j>>2]=k+ -1;d=(i[(c<<2)+f>>2]<<2)+e|0;i[d>>2]=i[d>>2]+ -1;d=1;if((b|0)!=1){break b}d:{if((c|0)==-1){break d}c=i[i[i[a+88>>2]+12>>2]+(c<<2)>>2];if((c|0)==-1){break d}while(1){e:{e=i[a+64>>2];if(I[i[i[e>>2]+36>>2]](e,(c>>>0)/3|0)|0){break e}e=c+1|0;c=(e>>>0)%3|0?e:c+ -2|0;if((c|0)==-1){d=d+1|0;break e}d=d+1|0;c=i[i[i[a+88>>2]+12>>2]+(c<<2)>>2];if((c|0)!=-1){continue}}break}e=i[a+104>>2];f=i[a+92>>2]}h=a+104|0;i[(i[(i[a+120>>2]<<2)+f>>2]<<2)+e>>2]=d;f=1;f:{if((g|0)==-1){break f}c=i[i[i[a+88>>2]+12>>2]+(g<<2)>>2];if((c|0)==-1){break f}e=i[a+108>>2]-e>>2;while(1){d=i[a+64>>2];g=(c>>>0)/3|0;if(I[i[i[d>>2]+36>>2]](d,g)|0){break f}d=c+1|0;i[i[a+92>>2]+(((d>>>0)%3|0?d:c+ -2|0)<<2)>>2]=e;c=(c-o(g,3)|0?-1:2)+c|0;if((c|0)==-1){f=f+1|0;break f}f=f+1|0;c=i[i[i[a+88>>2]+12>>2]+(c<<2)>>2];if((c|0)!=-1){continue}break}}i[m+12>>2]=f;Di(h,m+12|0);break b;case 5:d=(i[(d<<2)+f>>2]<<2)+e|0;i[d>>2]=i[d>>2]+ -1;d=(i[h>>2]<<2)+e|0;i[d>>2]=i[d>>2]+ -1;c=(i[(c<<2)+f>>2]<<2)+e|0;i[c>>2]=i[c>>2]+ -2;break b;case 3:d=(i[(d<<2)+f>>2]<<2)+e|0;i[d>>2]=i[d>>2]+ -1;d=(i[h>>2]<<2)+e|0;i[d>>2]=i[d>>2]+ -2;c=(i[(c<<2)+f>>2]<<2)+e|0;i[c>>2]=i[c>>2]+ -1;break b;case 7:break c;default:break b}}d=(i[(d<<2)+f>>2]<<2)+e|0;i[d>>2]=i[d>>2]+ -2;d=(i[h>>2]<<2)+e|0;i[d>>2]=i[d>>2]+ -2;c=(i[(c<<2)+f>>2]<<2)+e|0;i[c>>2]=i[c>>2]+ -2}g:{h:{f=i[a+116>>2];i:{if((f|0)==-1){break i}e=i[a+128>>2];c=e;d=i[a+136>>2];if((k|0)>=(c|0)){c=i[a+132>>2];c=(k|0)>(c|0)?c:k}e=o(c-e|0,12)+d|0;g=e+4|0;k=i[(f<<2)+12080>>2];c=i[e+4>>2];j=i[e+8>>2];if(c>>>0>>0){i[c>>2]=k;i[g>>2]=c+4;break i}f=i[e>>2];h=c-f|0;l=h>>2;d=l+1|0;if(d>>>0>=1073741824){break h}j=j-f|0;c=j>>1;d=j>>2>>>0<536870911?c>>>0>>0?d:c:1073741823;c=0;j:{if(!d){break j}if(d>>>0>=1073741824){break g}c=ho(d<<2)}j=e+8|0;l=c+(l<<2)|0;i[l>>2]=k;d=c+(d<<2)|0;k=l+4|0;if((h|0)>=1){hp(c,f,h)}i[e>>2]=c;i[g>>2]=k;i[j>>2]=d;if(!f){break i}bp(f)}i[a+116>>2]=b;F=m+16|0;return}Ho();x()}za(11708);x()}function Ua(a,b){var c=0,d=0,e=0,f=0,h=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0,z=0,A=0;e=F-80|0;F=e;i[e+56>>2]=0;i[e+60>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;i[e+64>>2]=1065353216;d=i[a+80>>2];i[e+32>>2]=0;i[e+24>>2]=0;i[e+28>>2]=0;a:{b:{if(!d){break b}if(d>>>0>=1073741824){break a}d=d<<2;h=ho(d);i[e+24>>2]=h;f=d+h|0;i[e+32>>2]=f;ip(h,0,d);i[e+28>>2]=f;while(1){d=b;c=i[d+48>>2];f=i[i[d>>2]>>2];t=s;c=up(i[d+40>>2],i[d+44>>2],s,0)+c|0;c=f+c|0;d=j[c+4|0]|j[c+5|0]<<8|(j[c+6|0]<<16|j[c+7|0]<<24);p=j[c|0]|j[c+1|0]<<8|(j[c+2|0]<<16|j[c+3|0]<<24);i[e+40>>2]=p;i[e+44>>2]=d;c:{d:{e:{if(!m){break e}c=i[e+48>>2];u=d>>>16|0;v=d&65535;w=((d&65535)<<16|p>>>16)&65535;z=p;q=u^(v^(w^((p^318)&65535)+239)+239)+239;f=q&m+ -1;y=xp(m)>>>0>1;f:{if(!y){break f}f=q;if(f>>>0>>0){break f}f=(q>>>0)%(m>>>0)|0}c=i[(f<<2)+c>>2];if(!c){break e}c=i[c>>2];if(!c){break e}A=m+ -1|0;while(1){l=i[c+4>>2];g:{if((q|0)!=(l|0)){h:{if(!y){l=l&A;break h}if(l>>>0>>0){break h}l=(l>>>0)%(m>>>0)|0}if((f|0)==(l|0)){break g}break e}if(k[c+8>>1]!=(z&65535)|k[c+10>>1]!=(w|0)|k[c+12>>1]!=(v|0)){break g}if(k[c+14>>1]==(u|0)){break d}}c=i[c>>2];if(c){continue}break}}i[e+16>>2]=n;i[e+8>>2]=p;i[e+12>>2]=d;wb(e+72|0,e+48|0,e+8|0,e+8|0);d=i[a+40>>2];hp(i[i[a>>2]>>2]+o(d,n)|0,e+40|0,d);h=i[e+24>>2];i[h+(t<<2)>>2]=n;n=n+1|0;break c}i[(t<<2)+h>>2]=i[c+16>>2]}d=r;f=s+1|0;if(f>>>0<1){d=d+1|0}s=f;r=d;c=i[a+80>>2];if(!d&f>>>0>>0|d>>>0<0){m=i[e+52>>2];continue}break}if((c|0)!=(n|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}d=f-b|0;r=(d|0)>-1?d:-1;f=b-f|0;d=o((r|0)<1?r:1,((f|0)>(d|0)?f:d)>>>2|0);d=d>>>0>1?d:1;c=0;while(1){f=b+(c<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+h>>2];c=c+1|0;if((d|0)!=(c|0)){continue}break}break i}g[a+84|0]=0;d=i[a+68>>2];b=i[a+72>>2]-d>>2;j:{if(c>>>0>b>>>0){Xa(a+68|0,c-b|0,1420);c=i[a+80>>2];break j}if(c>>>0>=b>>>0){break j}i[a+72>>2]=d+(c<<2)}h=i[e+24>>2];if(!c){break i}b=i[a+68>>2];l=0;while(1){d=l<<2;i[d+b>>2]=i[d+h>>2];l=l+1|0;if((l|0)!=(c|0)){continue}break}}i[a+80>>2]=n}if(!h){break b}i[e+28>>2]=h;bp(h)}c=i[e+56>>2];if(c){while(1){a=i[c>>2];bp(c);c=a;if(c){continue}break}}a=i[e+48>>2];i[e+48>>2]=0;if(a){bp(a)}F=e+80|0;return n}Ho();x()}function Fh(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0;g=F-16|0;F=g;b=i[a+64>>2];a:{if(!b){break a}l=I[i[i[b>>2]+32>>2]](b)|0;if(!l){break a}q=(i[l+28>>2]-i[l+24>>2]>>2)-i[l+44>>2]|0;b=i[a+56>>2];d=i[b+8>>2];b:{if((i[b+12>>2]-d|0)<5){break b}i[g+8>>2]=0;i[g>>2]=0;i[g+4>>2]=0;c:{d:{while(1){e:{f:{if(!i[i[(c<<2)+d>>2]+56>>2]){break f}h=I[i[i[a>>2]+56>>2]](a,c)|0;if(!h){break f}if(i[g+8>>2]!=(f|0)){i[f>>2]=h;f=f+4|0;i[g+4>>2]=f;break f}j=i[g>>2];d=f-j|0;f=d>>2;e=f+1|0;if(e>>>0>=1073741824){break e}k=d>>1;e=f>>>0<536870911?k>>>0>>0?e:k:1073741823;b=0;g:{if(!e){break g}if(e>>>0>=1073741824){break d}b=ho(e<<2)}f=b+(f<<2)|0;i[f>>2]=h;e=b+(e<<2)|0;f=f+4|0;if((d|0)>=1){hp(b,j,d)}i[g+8>>2]=e;i[g+4>>2]=f;i[g>>2]=b;if(!j){break f}bp(j)}m=i[a+56>>2];d=i[m+8>>2];c=c+1|0;if((c|0)>2]-d>>2){continue}break c}break}Ho();x()}za(11116);x()}n=i[l+24>>2];c=i[l+28>>2];h:{if((n|0)==(c|0)){k=i[g>>2];break h}b=c-n|0;j=(b|0)>-1?b:-1;c=n-c|0;b=o((j|0)<1?j:1,((c|0)>(b|0)?c:b)>>>2|0);s=b>>>0>1?b:1;k=i[g>>2];b=f-k|0;c=(b|0)>-1?b:-1;d=(c|0)<1?c:1;c=k-f|0;b=o(d,((c|0)>(b|0)?c:b)>>>2|0);t=b>>>0>1?b:1;while(1){e=i[(r<<2)+n>>2];if((e|0)!=-1){i:{if((e|0)<=-1){c=(e>>>0)%3|0;b=-1;break i}b=(e>>>0)/3|0;c=e-o(b,3)|0;b=i[(i[m+96>>2]+o(b,12)|0)+(c<<2)>>2]}p=0;c=e+(c?-1:2)|0;j:{if((c|0)==-1){break j}c=i[i[l+12>>2]+(c<<2)>>2];if((c|0)==-1){break j}j=e;d=c+((c>>>0)%3|0?-1:2)|0;if((d|0)==-1){break j}while(1){c=(d|0)<0?-1:i[(i[m+96>>2]+o((d>>>0)/3|0,12)|0)+((d>>>0)%3<<2)>>2];k:{if((b|0)==(c|0)){c=0;if((f|0)==(k|0)){c=b;break k}l:{while(1){h=i[i[(c<<2)+k>>2]+28>>2];if(i[h+(d<<2)>>2]!=i[h+(j<<2)>>2]){break l}c=c+1|0;if((t|0)!=(c|0)){continue}break}c=b;break k}c=b}p=p+1|0}if((d|0)==(e|0)|(d|0)==-1){break j}b=((d>>>0)%3|0?-1:2)+d|0;if((b|0)==-1){break j}h=i[i[l+12>>2]+(b<<2)>>2];if((h|0)==-1){break j}b=c;j=d;c=h+((h>>>0)%3|0?-1:2)|0;d=c;if((c|0)!=-1){continue}break}}c=1;b=e+1|0;b=(b>>>0)%3|0?b:e+ -2|0;m:{if((b|0)==-1){break m}b=i[i[l+12>>2]+(b<<2)>>2];if((b|0)==-1){break m}c=b+1|0;c=(((c>>>0)%3|0?c:b+ -2|0)|0)==-1}q=(q+p|0)-((c^1)&(p|0)!=0)|0}r=r+1|0;if((s|0)!=(r|0)){continue}break}}if(!k){break b}bp(k)}i[a+52>>2]=q}F=g+16|0}function La(a,b){var c=0,d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;e=F-80|0;F=e;i[e+56>>2]=0;i[e+60>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;i[e+64>>2]=1065353216;c=i[a+80>>2];i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;a:{b:{if(!c){break b}if(c>>>0>=1073741824){break a}c=c<<2;h=ho(c);i[e+16>>2]=h;d=c+h|0;i[e+24>>2]=d;ip(h,0,c);i[e+20>>2]=d;while(1){c=b;d=i[c+48>>2];n=i[i[c>>2]>>2];t=p;d=up(i[c+40>>2],i[c+44>>2],p,0)+d|0;c=n+d|0;q=j[c+8|0]|j[c+9|0]<<8|(j[c+10|0]<<16|j[c+11|0]<<24);i[e+40>>2]=q;d=j[c+4|0]|j[c+5|0]<<8|(j[c+6|0]<<16|j[c+7|0]<<24);r=j[c|0]|j[c+1|0]<<8|(j[c+2|0]<<16|j[c+3|0]<<24);i[e+32>>2]=r;i[e+36>>2]=d;s=i[e+36>>2];c:{d:{e:{if(!l){break e}d=i[e+48>>2];n=((r^318)+239^s)+239^q;c=n&l+ -1;u=xp(l)>>>0>1;f:{if(!u){break f}c=n;if(c>>>0>>0){break f}c=(n>>>0)%(l>>>0)|0}d=i[(c<<2)+d>>2];if(!d){break e}d=i[d>>2];if(!d){break e}v=l+ -1|0;while(1){k=i[d+4>>2];g:{if((n|0)!=(k|0)){h:{if(!u){k=k&v;break h}if(k>>>0>>0){break h}k=(k>>>0)%(l>>>0)|0}if((c|0)==(k|0)){break g}break e}if(i[d+8>>2]!=(r|0)|i[d+12>>2]!=(s|0)){break g}if(i[d+16>>2]==(q|0)){break d}}d=i[d>>2];if(d){continue}break}}i[e+12>>2]=m;i[e+8>>2]=q;i[e+4>>2]=s;i[e>>2]=r;Za(e+72|0,e+48|0,e,e);c=i[a+40>>2];hp(i[i[a>>2]>>2]+o(c,m)|0,e+32|0,c);h=i[e+16>>2];i[h+(t<<2)>>2]=m;m=m+1|0;break c}i[(t<<2)+h>>2]=i[d+20>>2]}c=f;f=p+1|0;if(f>>>0<1){c=c+1|0}p=f;f=c;d=i[a+80>>2];if(!c&p>>>0>>0|c>>>0<0){l=i[e+52>>2];continue}break}if((d|0)!=(m|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}c=f-b|0;d=(c|0)>-1?c:-1;f=b-f|0;c=o((d|0)<1?d:1,((f|0)>(c|0)?f:c)>>>2|0);c=c>>>0>1?c:1;d=0;while(1){f=b+(d<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+h>>2];d=d+1|0;if((c|0)!=(d|0)){continue}break}break i}g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;j:{if(d>>>0>b>>>0){Xa(a+68|0,d-b|0,1420);d=i[a+80>>2];break j}if(d>>>0>=b>>>0){break j}i[a+72>>2]=c+(d<<2)}h=i[e+16>>2];if(!d){break i}b=i[a+68>>2];k=0;while(1){c=k<<2;i[c+b>>2]=i[c+h>>2];k=k+1|0;if((k|0)!=(d|0)){continue}break}}i[a+80>>2]=m}if(!h){break b}i[e+20>>2]=h;bp(h)}d=i[e+56>>2];if(d){while(1){a=i[d>>2];bp(d);d=a;if(d){continue}break}}a=i[e+48>>2];i[e+48>>2]=0;if(a){bp(a)}F=e+80|0;return m}Ho();x()}function rd(a,b,c,d,e){var f=0,g=0,h=0,j=0,k=0;a:{b:{c:{d:{e:{switch(b+ -1|0){case 0:b=ho(60);i[b+4>>2]=c;i[b>>2]=3184;c=i[d+8>>2];f=i[d+12>>2];g=i[d+16>>2];h=i[d+20>>2];j=i[d>>2];k=i[d+4>>2];i[b+40>>2]=0;i[b+32>>2]=0;i[b+36>>2]=0;i[b+24>>2]=g;i[b+28>>2]=h;i[b+16>>2]=c;i[b+20>>2]=f;i[b+8>>2]=j;i[b+12>>2]=k;g=i[d+24>>2];f=i[d+28>>2]-g|0;if(f){d=f>>2;if(d>>>0>=1073741824){break d}c=ho(f);i[b+32>>2]=c;i[b+36>>2]=c;i[b+40>>2]=c+(d<<2);d=b;if((f|0)>=1){c=hp(c,g,f)+f|0}i[d+36>>2]=c}c=i[e+4>>2];i[b+44>>2]=i[e>>2];i[b+48>>2]=c;c=i[e+12>>2];i[b+52>>2]=i[e+8>>2];i[b+56>>2]=c;i[b>>2]=4380;break a;case 3:b=ho(168);i[b+4>>2]=c;i[b>>2]=3184;c=i[d+8>>2];f=i[d+12>>2];g=i[d+16>>2];h=i[d+20>>2];j=i[d>>2];k=i[d+4>>2];i[b+40>>2]=0;i[b+32>>2]=0;i[b+36>>2]=0;i[b+24>>2]=g;i[b+28>>2]=h;i[b+16>>2]=c;i[b+20>>2]=f;i[b+8>>2]=j;i[b+12>>2]=k;g=i[d+24>>2];f=i[d+28>>2]-g|0;if(f){d=f>>2;if(d>>>0>=1073741824){break c}c=ho(f);i[b+32>>2]=c;i[b+36>>2]=c;i[b+40>>2]=c+(d<<2);d=b;if((f|0)>=1){c=hp(c,g,f)+f|0}i[d+36>>2]=c}c=i[e+4>>2];i[b+44>>2]=i[e>>2];i[b+48>>2]=c;c=i[e+12>>2];i[b+52>>2]=i[e+8>>2];i[b+56>>2]=c;i[b+60>>2]=0;i[b+64>>2]=0;i[b>>2]=4804;i[b+68>>2]=0;i[b+72>>2]=0;i[b+76>>2]=0;i[b+80>>2]=0;i[b+84>>2]=0;i[b+88>>2]=0;i[b+92>>2]=0;i[b+96>>2]=0;i[b+100>>2]=0;i[b+104>>2]=0;i[b+108>>2]=0;cg(b+112|0);i[b+160>>2]=0;i[b+152>>2]=0;i[b+156>>2]=0;break a;case 4:b=ho(104);i[b+4>>2]=c;i[b>>2]=3184;c=i[d+8>>2];f=i[d+12>>2];g=i[d+16>>2];h=i[d+20>>2];j=i[d>>2];k=i[d+4>>2];i[b+40>>2]=0;i[b+32>>2]=0;i[b+36>>2]=0;i[b+24>>2]=g;i[b+28>>2]=h;i[b+16>>2]=c;i[b+20>>2]=f;i[b+8>>2]=j;i[b+12>>2]=k;g=i[d+24>>2];f=i[d+28>>2]-g|0;if(f){d=f>>2;if(d>>>0>=1073741824){break b}c=ho(f);i[b+32>>2]=c;i[b+36>>2]=c;i[b+40>>2]=c+(d<<2);d=b;if((f|0)>=1){c=hp(c,g,f)+f|0}i[d+36>>2]=c}c=i[e+4>>2];i[b+44>>2]=i[e>>2];i[b+48>>2]=c;c=e+8|0;d=i[c>>2];f=i[c+4>>2];i[b+84>>2]=0;i[b+76>>2]=0;i[b+80>>2]=0;i[b+60>>2]=0;i[b+64>>2]=0;i[b>>2]=5040;i[b+52>>2]=d;i[b+56>>2]=f;d=i[e+4>>2];i[b+88>>2]=i[e>>2];i[b+92>>2]=d;d=i[c+4>>2];i[b+96>>2]=i[c>>2];i[b+100>>2]=d;break a;case 5:b=ho(144);td(b,c,d,e);break a;default:break e}}i[a>>2]=0;return}Ho();x()}Ho();x()}Ho();x()}i[a>>2]=b}function qd(a,b,c,d,e){var f=0,g=0,h=0,j=0,k=0;a:{b:{c:{d:{e:{switch(b+ -1|0){case 0:b=ho(60);i[b+4>>2]=c;i[b>>2]=3184;c=i[d+8>>2];f=i[d+12>>2];g=i[d+16>>2];h=i[d+20>>2];j=i[d>>2];k=i[d+4>>2];i[b+40>>2]=0;i[b+32>>2]=0;i[b+36>>2]=0;i[b+24>>2]=g;i[b+28>>2]=h;i[b+16>>2]=c;i[b+20>>2]=f;i[b+8>>2]=j;i[b+12>>2]=k;g=i[d+24>>2];f=i[d+28>>2]-g|0;if(f){d=f>>2;if(d>>>0>=1073741824){break d}c=ho(f);i[b+32>>2]=c;i[b+36>>2]=c;i[b+40>>2]=c+(d<<2);d=b;if((f|0)>=1){c=hp(c,g,f)+f|0}i[d+36>>2]=c}c=i[e+4>>2];i[b+44>>2]=i[e>>2];i[b+48>>2]=c;c=i[e+12>>2];i[b+52>>2]=i[e+8>>2];i[b+56>>2]=c;i[b>>2]=2476;break a;case 3:b=ho(168);i[b+4>>2]=c;i[b>>2]=3184;c=i[d+8>>2];f=i[d+12>>2];g=i[d+16>>2];h=i[d+20>>2];j=i[d>>2];k=i[d+4>>2];i[b+40>>2]=0;i[b+32>>2]=0;i[b+36>>2]=0;i[b+24>>2]=g;i[b+28>>2]=h;i[b+16>>2]=c;i[b+20>>2]=f;i[b+8>>2]=j;i[b+12>>2]=k;g=i[d+24>>2];f=i[d+28>>2]-g|0;if(f){d=f>>2;if(d>>>0>=1073741824){break c}c=ho(f);i[b+32>>2]=c;i[b+36>>2]=c;i[b+40>>2]=c+(d<<2);d=b;if((f|0)>=1){c=hp(c,g,f)+f|0}i[d+36>>2]=c}c=i[e+4>>2];i[b+44>>2]=i[e>>2];i[b+48>>2]=c;c=i[e+12>>2];i[b+52>>2]=i[e+8>>2];i[b+56>>2]=c;i[b+60>>2]=0;i[b+64>>2]=0;i[b>>2]=3240;i[b+68>>2]=0;i[b+72>>2]=0;i[b+76>>2]=0;i[b+80>>2]=0;i[b+84>>2]=0;i[b+88>>2]=0;i[b+92>>2]=0;i[b+96>>2]=0;i[b+100>>2]=0;i[b+104>>2]=0;i[b+108>>2]=0;cg(b+112|0);i[b+160>>2]=0;i[b+152>>2]=0;i[b+156>>2]=0;break a;case 4:b=ho(104);i[b+4>>2]=c;i[b>>2]=3184;c=i[d+8>>2];f=i[d+12>>2];g=i[d+16>>2];h=i[d+20>>2];j=i[d>>2];k=i[d+4>>2];i[b+40>>2]=0;i[b+32>>2]=0;i[b+36>>2]=0;i[b+24>>2]=g;i[b+28>>2]=h;i[b+16>>2]=c;i[b+20>>2]=f;i[b+8>>2]=j;i[b+12>>2]=k;g=i[d+24>>2];f=i[d+28>>2]-g|0;if(f){d=f>>2;if(d>>>0>=1073741824){break b}c=ho(f);i[b+32>>2]=c;i[b+36>>2]=c;i[b+40>>2]=c+(d<<2);d=b;if((f|0)>=1){c=hp(c,g,f)+f|0}i[d+36>>2]=c}c=i[e+4>>2];i[b+44>>2]=i[e>>2];i[b+48>>2]=c;c=e+8|0;d=i[c>>2];f=i[c+4>>2];i[b+84>>2]=0;i[b+76>>2]=0;i[b+80>>2]=0;i[b+60>>2]=0;i[b+64>>2]=0;i[b>>2]=3488;i[b+52>>2]=d;i[b+56>>2]=f;d=i[e+4>>2];i[b+88>>2]=i[e>>2];i[b+92>>2]=d;d=i[c+4>>2];i[b+96>>2]=i[c>>2];i[b+100>>2]=d;break a;case 5:b=ho(144);sd(b,c,d,e);break a;default:break e}}i[a>>2]=0;return}Ho();x()}Ho();x()}Ho();x()}i[a>>2]=b}function Qa(a,b){var c=0,d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0;e=F+ -64|0;F=e;i[e+40>>2]=0;i[e+44>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;i[e+48>>2]=1065353216;c=i[a+80>>2];i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;a:{b:{if(!c){break b}if(c>>>0>=1073741824){break a}c=c<<2;h=ho(c);i[e+16>>2]=h;d=c+h|0;i[e+24>>2]=d;ip(h,0,c);i[e+20>>2]=d;while(1){c=b;d=i[c+48>>2];n=i[i[c>>2]>>2];t=q;d=up(i[c+40>>2],i[c+44>>2],q,0)+d|0;c=n+d|0;p=j[c|0]|j[c+1|0]<<8|(j[c+2|0]<<16|j[c+3|0]<<24);i[e+28>>2]=p;r=p>>>8&255;s=p>>>16&255;c:{d:{e:{if(!l){break e}d=i[e+32>>2];u=p>>>24|0;v=p&255;n=u^(((v^318)+239^r)+239^s)+239;c=n&l+ -1;w=xp(l)>>>0>1;f:{if(!w){break f}c=n;if(c>>>0>>0){break f}c=(n>>>0)%(l>>>0)|0}d=i[(c<<2)+d>>2];if(!d){break e}d=i[d>>2];if(!d){break e}y=l+ -1|0;while(1){k=i[d+4>>2];g:{if((n|0)!=(k|0)){h:{if(!w){k=k&y;break h}if(k>>>0>>0){break h}k=(k>>>0)%(l>>>0)|0}if((c|0)==(k|0)){break g}break e}if(j[d+8|0]!=(v|0)|j[d+9|0]!=(r|0)|j[d+10|0]!=(s|0)){break g}if(j[d+11|0]==(u|0)){break d}}d=i[d>>2];if(d){continue}break}}i[e+12>>2]=m;i[e+8>>2]=p&-16776961|s<<16|r<<8;kb(e+56|0,e+32|0,e+8|0,e+8|0);c=i[a+40>>2];hp(i[i[a>>2]>>2]+o(c,m)|0,e+28|0,c);h=i[e+16>>2];i[h+(t<<2)>>2]=m;m=m+1|0;break c}i[(t<<2)+h>>2]=i[d+12>>2]}c=f;f=q+1|0;if(f>>>0<1){c=c+1|0}q=f;f=c;d=i[a+80>>2];if(!c&q>>>0>>0|c>>>0<0){l=i[e+36>>2];continue}break}if((d|0)!=(m|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}c=f-b|0;d=(c|0)>-1?c:-1;f=b-f|0;c=o((d|0)<1?d:1,((f|0)>(c|0)?f:c)>>>2|0);c=c>>>0>1?c:1;d=0;while(1){f=b+(d<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+h>>2];d=d+1|0;if((c|0)!=(d|0)){continue}break}break i}g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;j:{if(d>>>0>b>>>0){Xa(a+68|0,d-b|0,1420);d=i[a+80>>2];break j}if(d>>>0>=b>>>0){break j}i[a+72>>2]=c+(d<<2)}h=i[e+16>>2];if(!d){break i}b=i[a+68>>2];k=0;while(1){c=k<<2;i[c+b>>2]=i[c+h>>2];k=k+1|0;if((k|0)!=(d|0)){continue}break}}i[a+80>>2]=m}if(!h){break b}i[e+20>>2]=h;bp(h)}d=i[e+40>>2];if(d){while(1){a=i[d>>2];bp(d);d=a;if(d){continue}break}}a=i[e+32>>2];i[e+32>>2]=0;if(a){bp(a)}F=e- -64|0;return m}Ho();x()}function jn(a,b,c){var d=0,e=0,f=0,h=0,k=0,m=0,n=0,o=0,q=0,r=0;f=F-48|0;F=f;if(c>>>0<=2){a:{e=b+4|0;c=c<<2;q=i[c+17628>>2];r=i[c+17616>>2];while(1){c=i[b+4>>2];b:{if(c>>>0>2]){i[e>>2]=c+1;c=j[c|0];break b}c=fn(b)}if(an(c)){continue}break}k=1;c:{d:{switch(c+ -43|0){case 0:case 2:break d;default:break c}}k=(c|0)==45?-1:1;c=i[b+4>>2];if(c>>>0>2]){i[e>>2]=c+1;c=j[c|0];break c}c=fn(b)}e:{f:{while(1){if(g[d+17541|0]==(c|32)){g:{if(d>>>0>6){break g}c=i[b+4>>2];if(c>>>0>2]){i[e>>2]=c+1;c=j[c|0];break g}c=fn(b)}d=d+1|0;if((d|0)!=8){continue}break f}break}if((d|0)!=3){if((d|0)==8){break f}if(d>>>0<4){break e}if((d|0)==8){break f}}b=i[b+104>>2];if(b){i[e>>2]=i[e>>2]+ -1}if(d>>>0<4){break f}while(1){if(b){i[e>>2]=i[e>>2]+ -1}d=d+ -1|0;if(d>>>0>3){continue}break}}un(f,p(p(k|0)*p(z)));m=i[f+8>>2];h=i[f+12>>2];n=i[f>>2];o=i[f+4>>2];break a}h:{i:{j:{if(d){break j}d=0;while(1){if(g[d+17550|0]!=(c|32)){break j}k:{if(d>>>0>1){break k}c=i[b+4>>2];if(c>>>0>2]){i[e>>2]=c+1;c=j[c|0];break k}c=fn(b)}d=d+1|0;if((d|0)!=3){continue}break}break i}l:{switch(d|0){case 0:m:{if((c|0)!=48){break m}d=i[b+4>>2];n:{if(d>>>0>2]){i[e>>2]=d+1;d=j[d|0];break n}d=fn(b)}if((d&-33)==88){kn(f+16|0,b,r,q,k);m=i[f+24>>2];h=i[f+28>>2];n=i[f+16>>2];o=i[f+20>>2];break a}if(!i[b+104>>2]){break m}i[e>>2]=i[e>>2]+ -1}ln(f+32|0,b,c,r,q,k);m=i[f+40>>2];h=i[f+44>>2];n=i[f+32>>2];o=i[f+36>>2];break a;case 3:break i;default:break l}}if(i[b+104>>2]){i[e>>2]=i[e>>2]+ -1}i[4805]=28;break h}o:{c=i[b+4>>2];p:{if(c>>>0>2]){i[e>>2]=c+1;c=j[c|0];break p}c=fn(b)}if((c|0)==40){d=1;break o}h=2147450880;if(!i[b+104>>2]){break a}i[e>>2]=i[e>>2]+ -1;break a}while(1){q:{c=i[b+4>>2];r:{if(c>>>0>2]){i[e>>2]=c+1;c=j[c|0];break r}c=fn(b)}if(!(c+ -48>>>0<10|c+ -65>>>0<26|(c|0)==95)){if(c+ -97>>>0>=26){break q}}d=d+1|0;continue}break}h=2147450880;if((c|0)==41){break a}b=i[b+104>>2];if(b){i[e>>2]=i[e>>2]+ -1}if(!d){break a}while(1){d=d+ -1|0;if(b){i[e>>2]=i[e>>2]+ -1}if(d){continue}break}break a}en(b)}}i[a>>2]=n;i[a+4>>2]=o;i[a+8>>2]=m;i[a+12>>2]=h;F=f+48|0}function Ta(a,b){var c=0,d=0,e=0,f=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0;e=F-80|0;F=e;i[e+56>>2]=0;i[e+60>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;i[e+64>>2]=1065353216;c=i[a+80>>2];i[e+32>>2]=0;i[e+24>>2]=0;i[e+28>>2]=0;a:{b:{if(!c){break b}if(c>>>0>=1073741824){break a}c=c<<2;l=ho(c);i[e+24>>2]=l;d=c+l|0;i[e+32>>2]=d;ip(l,0,c);i[e+28>>2]=d;while(1){c=b;d=i[c+48>>2];q=i[i[c>>2]>>2];v=r;d=up(i[c+40>>2],i[c+44>>2],r,0)+d|0;c=q+d|0;s=j[c+4|0]|j[c+5|0]<<8;h[e+44>>1]=s;t=j[c|0]|j[c+1|0]<<8|(j[c+2|0]<<16|j[c+3|0]<<24);i[e+40>>2]=t;u=k[e+42>>1];c:{d:{e:{if(!n){break e}d=i[e+48>>2];q=(((t^318)&65535)+239^u)+239^s;c=q&n+262143;w=xp(n)>>>0>1;f:{if(!w){break f}c=q;if(c>>>0>>0){break f}c=(q>>>0)%(n>>>0)|0}d=i[(c<<2)+d>>2];if(!d){break e}d=i[d>>2];if(!d){break e}y=n+ -1|0;while(1){m=i[d+4>>2];g:{if((q|0)!=(m|0)){h:{if(!w){m=m&y;break h}if(m>>>0>>0){break h}m=(m>>>0)%(n>>>0)|0}if((c|0)==(m|0)){break g}break e}if(k[d+8>>1]!=(t&65535)|k[d+10>>1]!=(u|0)){break g}if(k[d+12>>1]==(s|0)){break d}}d=i[d>>2];if(d){continue}break}}i[e+16>>2]=p;h[e+12>>1]=s;h[e+10>>1]=u;h[e+8>>1]=t;vb(e+72|0,e+48|0,e+8|0,e+8|0);c=i[a+40>>2];hp(i[i[a>>2]>>2]+o(c,p)|0,e+40|0,c);l=i[e+24>>2];i[l+(v<<2)>>2]=p;p=p+1|0;break c}i[(v<<2)+l>>2]=i[d+16>>2]}c=f;f=r+1|0;if(f>>>0<1){c=c+1|0}r=f;f=c;d=i[a+80>>2];if(!c&r>>>0>>0|c>>>0<0){n=i[e+52>>2];continue}break}if((d|0)!=(p|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}c=f-b|0;d=(c|0)>-1?c:-1;f=b-f|0;c=o((d|0)<1?d:1,((f|0)>(c|0)?f:c)>>>2|0);c=c>>>0>1?c:1;d=0;while(1){f=b+(d<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+l>>2];d=d+1|0;if((c|0)!=(d|0)){continue}break}break i}g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;j:{if(d>>>0>b>>>0){Xa(a+68|0,d-b|0,1420);d=i[a+80>>2];break j}if(d>>>0>=b>>>0){break j}i[a+72>>2]=c+(d<<2)}l=i[e+24>>2];if(!d){break i}b=i[a+68>>2];m=0;while(1){c=m<<2;i[c+b>>2]=i[c+l>>2];m=m+1|0;if((m|0)!=(d|0)){continue}break}}i[a+80>>2]=p}if(!l){break b}i[e+28>>2]=l;bp(l)}d=i[e+56>>2];if(d){while(1){a=i[d>>2];bp(d);d=a;if(d){continue}break}}a=i[e+48>>2];i[e+48>>2]=0;if(a){bp(a)}F=e+80|0;return p}Ho();x()}function El(a,b){var c=0,d=0,e=0,f=0;f=F-16|0;F=f;e=ho(32);i[f>>2]=e;i[f+4>>2]=30;i[f+8>>2]=-2147483616;g[e+30|0]=0;c=j[16476]|j[16477]<<8|(j[16478]<<16|j[16479]<<24);d=j[16472]|j[16473]<<8|(j[16474]<<16|j[16475]<<24);g[e+22|0]=d;g[e+23|0]=d>>>8;g[e+24|0]=d>>>16;g[e+25|0]=d>>>24;g[e+26|0]=c;g[e+27|0]=c>>>8;g[e+28|0]=c>>>16;g[e+29|0]=c>>>24;c=j[16470]|j[16471]<<8|(j[16472]<<16|j[16473]<<24);d=j[16466]|j[16467]<<8|(j[16468]<<16|j[16469]<<24);g[e+16|0]=d;g[e+17|0]=d>>>8;g[e+18|0]=d>>>16;g[e+19|0]=d>>>24;g[e+20|0]=c;g[e+21|0]=c>>>8;g[e+22|0]=c>>>16;g[e+23|0]=c>>>24;c=j[16462]|j[16463]<<8|(j[16464]<<16|j[16465]<<24);d=j[16458]|j[16459]<<8|(j[16460]<<16|j[16461]<<24);g[e+8|0]=d;g[e+9|0]=d>>>8;g[e+10|0]=d>>>16;g[e+11|0]=d>>>24;g[e+12|0]=c;g[e+13|0]=c>>>8;g[e+14|0]=c>>>16;g[e+15|0]=c>>>24;c=j[16454]|j[16455]<<8|(j[16456]<<16|j[16457]<<24);d=j[16450]|j[16451]<<8|(j[16452]<<16|j[16453]<<24);g[e|0]=d;g[e+1|0]=d>>>8;g[e+2|0]=d>>>16;g[e+3|0]=d>>>24;g[e+4|0]=c;g[e+5|0]=c>>>8;g[e+6|0]=c>>>16;g[e+7|0]=c>>>24;e=a+4|0;Yj(e,f,b);if(g[f+11|0]<=-1){bp(i[f>>2])}a=ho(32);i[f>>2]=a;i[f+4>>2]=29;i[f+8>>2]=-2147483616;g[a+29|0]=0;c=j[16506]|j[16507]<<8|(j[16508]<<16|j[16509]<<24);d=j[16502]|j[16503]<<8|(j[16504]<<16|j[16505]<<24);g[a+21|0]=d;g[a+22|0]=d>>>8;g[a+23|0]=d>>>16;g[a+24|0]=d>>>24;g[a+25|0]=c;g[a+26|0]=c>>>8;g[a+27|0]=c>>>16;g[a+28|0]=c>>>24;c=j[16501]|j[16502]<<8|(j[16503]<<16|j[16504]<<24);d=j[16497]|j[16498]<<8|(j[16499]<<16|j[16500]<<24);g[a+16|0]=d;g[a+17|0]=d>>>8;g[a+18|0]=d>>>16;g[a+19|0]=d>>>24;g[a+20|0]=c;g[a+21|0]=c>>>8;g[a+22|0]=c>>>16;g[a+23|0]=c>>>24;c=j[16493]|j[16494]<<8|(j[16495]<<16|j[16496]<<24);d=j[16489]|j[16490]<<8|(j[16491]<<16|j[16492]<<24);g[a+8|0]=d;g[a+9|0]=d>>>8;g[a+10|0]=d>>>16;g[a+11|0]=d>>>24;g[a+12|0]=c;g[a+13|0]=c>>>8;g[a+14|0]=c>>>16;g[a+15|0]=c>>>24;c=j[16485]|j[16486]<<8|(j[16487]<<16|j[16488]<<24);d=j[16481]|j[16482]<<8|(j[16483]<<16|j[16484]<<24);g[a|0]=d;g[a+1|0]=d>>>8;g[a+2|0]=d>>>16;g[a+3|0]=d>>>24;g[a+4|0]=c;g[a+5|0]=c>>>8;g[a+6|0]=c>>>16;g[a+7|0]=c>>>24;Yj(e,f,b);if(g[f+11|0]<=-1){bp(i[f>>2])}F=f+16|0}function Ka(a,b){var c=0,d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;e=F-80|0;F=e;i[e+56>>2]=0;i[e+60>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;i[e+64>>2]=1065353216;d=i[a+80>>2];i[e+32>>2]=0;i[e+24>>2]=0;i[e+28>>2]=0;a:{b:{if(!d){break b}if(d>>>0>=1073741824){break a}f=d<<2;l=ho(f);i[e+24>>2]=l;d=f+l|0;i[e+32>>2]=d;ip(l,0,f);i[e+28>>2]=d;while(1){d=b;k=i[d+48>>2];f=i[i[d>>2]>>2];t=q;d=up(i[d+40>>2],i[d+44>>2],q,0)+k|0;k=f+d|0;d=j[k+4|0]|j[k+5|0]<<8|(j[k+6|0]<<16|j[k+7|0]<<24);s=j[k|0]|j[k+1|0]<<8|(j[k+2|0]<<16|j[k+3|0]<<24);i[e+40>>2]=s;i[e+44>>2]=d;c:{d:{e:{if(!n){break e}c=i[e+48>>2];v=d;k=s;r=d^(k^318)+239;f=r&n+ -1;u=xp(n)>>>0>1;f:{if(!u){break f}f=r;if(f>>>0>>0){break f}f=(r>>>0)%(n>>>0)|0}c=i[(f<<2)+c>>2];if(!c){break e}c=i[c>>2];if(!c){break e}w=n+ -1|0;while(1){m=i[c+4>>2];g:{if((r|0)!=(m|0)){h:{if(!u){m=m&w;break h}if(m>>>0>>0){break h}m=(m>>>0)%(n>>>0)|0}if((f|0)==(m|0)){break g}break e}if(i[c+8>>2]!=(k|0)){break g}if(i[c+12>>2]==(v|0)){break d}}c=i[c>>2];if(c){continue}break}}i[e+16>>2]=p;i[e+8>>2]=s;i[e+12>>2]=d;Ya(e+72|0,e+48|0,e+8|0,e+8|0);d=i[a+40>>2];hp(i[i[a>>2]>>2]+o(d,p)|0,e+40|0,d);l=i[e+24>>2];i[l+(t<<2)>>2]=p;p=p+1|0;break c}i[(t<<2)+l>>2]=i[c+16>>2]}d=h;h=q+1|0;if(h>>>0<1){d=d+1|0}q=h;f=h;h=d;c=i[a+80>>2];if(!d&f>>>0>>0|d>>>0<0){n=i[e+52>>2];continue}break}if((c|0)!=(p|0)){i:{if(!j[a+84|0]){h=i[a+72>>2];f=i[a+68>>2];if((h|0)==(f|0)){break i}d=h-f|0;b=(d|0)>-1?d:-1;q=(b|0)<1?b:1;b=f-h|0;b=o(q,((b|0)>(d|0)?b:d)>>>2|0);h=b>>>0>1?b:1;c=0;while(1){b=f+(c<<2)|0;i[b>>2]=i[(i[b>>2]<<2)+l>>2];c=c+1|0;if((h|0)!=(c|0)){continue}break}break i}g[a+84|0]=0;b=i[a+68>>2];h=i[a+72>>2]-b>>2;j:{if(c>>>0>h>>>0){Xa(a+68|0,c-h|0,1420);c=i[a+80>>2];break j}if(c>>>0>=h>>>0){break j}i[a+72>>2]=b+(c<<2)}l=i[e+24>>2];if(!c){break i}h=i[a+68>>2];m=0;while(1){b=m<<2;i[b+h>>2]=i[b+l>>2];m=m+1|0;if((m|0)!=(c|0)){continue}break}}i[a+80>>2]=p}if(!l){break b}i[e+28>>2]=l;bp(l)}c=i[e+56>>2];if(c){while(1){a=i[c>>2];bp(c);c=a;if(c){continue}break}}a=i[e+48>>2];i[e+48>>2]=0;if(a){bp(a)}F=e+80|0;return p}Ho();x()}function dp(a,b,c,d,e,f,g,h,j){var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;k=F-128|0;F=k;a:{b:{c:{if(!qn(f,g,h,j,0,0,0,0)){break c}m=gp(f,g,h,j);s=e>>>16|0;p=s&32767;if((p|0)==32767){break c}if(m){break b}}yn(k+16|0,b,c,d,e,f,g,h,j);e=i[k+16>>2];d=i[k+20>>2];c=i[k+24>>2];b=i[k+28>>2];Cn(k,e,d,c,b,e,d,c,b);d=i[k+8>>2];e=i[k+12>>2];h=i[k>>2];j=i[k+4>>2];break a}m=p<<16;q=d;r=m|e&65535;n=r;o=h;r=j>>>16&32767;l=j&65535|r<<16;if((qn(b,c,q,n,f,g,h,l)|0)<=0){if(qn(b,c,q,n,f,g,o,l)){h=b;j=c;break a}yn(k+112|0,b,c,d,e,0,0,0,0);d=i[k+120>>2];e=i[k+124>>2];h=i[k+112>>2];j=i[k+116>>2];break a}if(p){j=c;h=b}else{yn(k+96|0,b,c,q,n,0,0,0,1081540608);h=i[k+108>>2];n=h;q=i[k+104>>2];p=(h>>>16|0)+ -120|0;j=i[k+100>>2];h=i[k+96>>2]}if(!r){yn(k+80|0,f,g,o,l,0,0,0,1081540608);f=i[k+92>>2];l=f;o=i[k+88>>2];r=(l>>>16|0)+ -120|0;g=i[k+84>>2];f=i[k+80>>2]}t=l&65535|65536;n=n&65535|65536;if((p|0)>(r|0)){while(1){l=q;m=o;u=l-m|0;m=n-((l>>>0>>0)+t|0)|0;l=(g|0)==(j|0)&h>>>0>>0|j>>>0>>0;m=m-(u>>>0>>0)|0;l=u-l|0;d:{if((m|0)>0?1:(m|0)>=0?l>>>0>=0:0){n=h;h=h-f|0;j=j-((n>>>0>>0)+g|0)|0;if(!(h|l|(j|m))){yn(k+32|0,b,c,d,e,0,0,0,0);d=i[k+40>>2];e=i[k+44>>2];h=i[k+32>>2];j=i[k+36>>2];break a}m=m<<1|l>>>31;q=l<<1|j>>>31;break d}m=n<<1|q>>>31;q=q<<1|j>>>31}n=m;m=j<<1|h>>>31;h=h<<1;j=m;p=p+ -1|0;if((p|0)>(r|0)){continue}break}p=r}e:{m=q;r=m-o|0;l=n-((m>>>0>>0)+t|0)|0;m=(g|0)==(j|0)&h>>>0>>0|j>>>0>>0;l=l-(r>>>0>>0)|0;o=r-m|0;if((l|0)<0?1:(l|0)<=0?o>>>0<0:0){o=q;l=n;break e}q=h;h=h-f|0;j=j-((q>>>0>>0)+g|0)|0;if(h|o|(j|l)){break e}yn(k+48|0,b,c,d,e,0,0,0,0);d=i[k+56>>2];e=i[k+60>>2];h=i[k+48>>2];j=i[k+52>>2];break a}if((l|0)==65535|l>>>0<65535){while(1){b=j>>>31|0;p=p+ -1|0;n=j<<1|h>>>31;h=h<<1;j=n;c=b;b=o;l=l<<1|b>>>31;o=c|b<<1;if((l|0)==65536&o>>>0<0|l>>>0<65536){continue}break}}b=s&32768;if((p|0)<=0){yn(k- -64|0,h,j,o,l&65535|(b|p+120)<<16,0,0,0,1065811968);d=i[k+72>>2];e=i[k+76>>2];h=i[k+64>>2];j=i[k+68>>2];break a}d=o;e=l&65535|(b|p)<<16}i[a>>2]=h;i[a+4>>2]=j;i[a+8>>2]=d;i[a+12>>2]=e;F=k+128|0}function Pa(a,b){var c=0,d=0,e=0,f=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;e=F+ -64|0;F=e;i[e+40>>2]=0;i[e+44>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;i[e+48>>2]=1065353216;c=i[a+80>>2];i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;a:{b:{if(!c){break b}if(c>>>0>=1073741824){break a}c=c<<2;k=ho(c);i[e+16>>2]=k;d=c+k|0;i[e+24>>2]=d;ip(k,0,c);i[e+20>>2]=d;while(1){c=b;d=i[c+48>>2];p=i[i[c>>2]>>2];u=q;d=up(i[c+40>>2],i[c+44>>2],q,0)+d|0;c=p+d|0;r=j[c+2|0];g[e+30|0]=r;h[e+28>>1]=j[c|0]|j[c+1|0]<<8;s=j[e+28|0];t=j[e+29|0];c:{d:{e:{if(!m){break e}d=i[e+32>>2];p=((s^318)+239^t)+239^r;c=p&m+ -1;v=xp(m)>>>0>1;f:{if(!v){break f}c=p;if(c>>>0>>0){break f}c=(p>>>0)%(m>>>0)|0}d=i[(c<<2)+d>>2];if(!d){break e}d=i[d>>2];if(!d){break e}w=m+ -1|0;while(1){l=i[d+4>>2];g:{if((p|0)!=(l|0)){h:{if(!v){l=l&w;break h}if(l>>>0>>0){break h}l=(l>>>0)%(m>>>0)|0}if((c|0)==(l|0)){break g}break e}if(j[d+8|0]!=(s|0)|j[d+9|0]!=(t|0)){break g}if(j[d+10|0]==(r|0)){break d}}d=i[d>>2];if(d){continue}break}}i[e+12>>2]=n;g[e+10|0]=r;g[e+9|0]=t;g[e+8|0]=s;jb(e+56|0,e+32|0,e+8|0,e+8|0);c=i[a+40>>2];hp(i[i[a>>2]>>2]+o(c,n)|0,e+28|0,c);k=i[e+16>>2];i[k+(u<<2)>>2]=n;n=n+1|0;break c}i[(u<<2)+k>>2]=i[d+12>>2]}c=f;f=q+1|0;if(f>>>0<1){c=c+1|0}q=f;f=c;d=i[a+80>>2];if(!c&q>>>0>>0|c>>>0<0){m=i[e+36>>2];continue}break}if((d|0)!=(n|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}c=f-b|0;d=(c|0)>-1?c:-1;f=b-f|0;c=o((d|0)<1?d:1,((f|0)>(c|0)?f:c)>>>2|0);c=c>>>0>1?c:1;d=0;while(1){f=b+(d<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+k>>2];d=d+1|0;if((c|0)!=(d|0)){continue}break}break i}g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;j:{if(d>>>0>b>>>0){Xa(a+68|0,d-b|0,1420);d=i[a+80>>2];break j}if(d>>>0>=b>>>0){break j}i[a+72>>2]=c+(d<<2)}k=i[e+16>>2];if(!d){break i}b=i[a+68>>2];l=0;while(1){c=l<<2;i[c+b>>2]=i[c+k>>2];l=l+1|0;if((l|0)!=(d|0)){continue}break}}i[a+80>>2]=n}if(!k){break b}i[e+20>>2]=k;bp(k)}d=i[e+40>>2];if(d){while(1){a=i[d>>2];bp(d);d=a;if(d){continue}break}}a=i[e+32>>2];i[e+32>>2]=0;if(a){bp(a)}F=e- -64|0;return n}Ho();x()}function am(a,b,c,d){var e=0,f=0,h=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0;if($l(a,c)){i[a+88>>2]=0;i[a+92>>2]=0;e=a+84|0;c=i[e>>2];i[e>>2]=0;if(c){bp(c)}i[a+76>>2]=0;i[a+80>>2]=0;e=a+72|0;c=i[e>>2];i[e>>2]=0;if(c){bp(c)}m=i[a+64>>2];if(i[m+4>>2]!=i[m>>2]){c=0;while(1){p=(c>>>0)/3|0;e=Ql(m,p);m=i[a+64>>2];a:{if(e){break a}l=i[i[m+12>>2]+(c<<2)>>2];if((l|0)==-1){e=i[a>>2]+(c>>>3&536870908)|0;i[e>>2]=i[e>>2]|1<>>0)%3|0?l:c+ -2|0;if((l|0)!=-1){e=i[i[m>>2]+(l<<2)>>2]}l=i[a+12>>2];k=l+(e>>>3&536870908)|0;i[k>>2]=i[k>>2]|1<>>0)%3|0?-1:2)+c|0;if((e|0)!=-1){f=i[i[m>>2]+(e<<2)>>2]}e=l+(f>>>3&536870908)|0;i[e>>2]=i[e>>2]|1<>>0>>0){break a}e=-1;k=l+((l>>>0)%3|0?-1:2)|0;q=j[d+84|0];f=-1;n=c+1|0;r=c+ -2|0;h=(n>>>0)%3|0?n:r;if((h|0)>=0){f=(h>>>0)/3|0;f=i[(i[b+96>>2]+o(f,12)|0)+(h-o(f,3)<<2)>>2]}e=(k|0)>=0?i[(i[b+96>>2]+o((k>>>0)/3|0,12)|0)+((k>>>0)%3<<2)>>2]:e;if(!q){s=i[d+68>>2];e=i[s+(e<<2)>>2];f=i[(f<<2)+s>>2]}if((e|0)==(f|0)){e=-1;f=-1;if((h|0)!=-1){f=h+1|0;f=(f>>>0)%3|0?f:h+ -2|0}b:{if((k|0)==-1){break b}if((k>>>0)%3|0){e=k+ -1|0;break b}e=k+2|0}k=-1;h=-1;if((f|0)>=0){h=(f>>>0)/3|0;h=i[(i[b+96>>2]+o(h,12)|0)+(f-o(h,3)<<2)>>2]}if((e|0)>=0){f=(e>>>0)/3|0;k=i[(i[b+96>>2]+o(f,12)|0)+(e-o(f,3)<<2)>>2]}if(!q){e=i[d+68>>2];k=i[e+(k<<2)>>2];h=i[e+(h<<2)>>2]}if((h|0)==(k|0)){break a}}g[a+24|0]=0;e=i[a>>2];f=e+(c>>>3&536870908)|0;i[f>>2]=i[f>>2]|1<>>3&536870908)|0;i[e>>2]=i[e>>2]|1<>>0)%3|0?n:r;if((k|0)!=-1){f=i[i[m>>2]+(k<<2)>>2]}k=i[a+12>>2];h=k+(f>>>3&536870908)|0;i[h>>2]=i[h>>2]|1<>2]+(f<<2)>>2]}f=k+(e>>>3&536870908)|0;i[f>>2]=i[f>>2]|1<>>0)%3|0?h:l+ -2|0;if((h|0)!=-1){f=i[i[m>>2]+(h<<2)>>2]}h=k+(f>>>3&536870908)|0;i[h>>2]=i[h>>2]|1<>>0)%3|0?-1:2)|0;if((f|0)!=-1){e=i[i[m>>2]+(f<<2)>>2]}f=k+(e>>>3&536870908)|0;i[f>>2]=i[f>>2]|1<>>0>2]-i[m>>2]>>2>>>0){continue}break}}if(!(!b|!d)){bm(a,b,d);return}cm(a)}}function Sa(a,b){var c=0,d=0,e=0,f=0,h=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;e=F+ -64|0;F=e;i[e+40>>2]=0;i[e+44>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;i[e+48>>2]=1065353216;c=i[a+80>>2];i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;a:{b:{if(!c){break b}if(c>>>0>=1073741824){break a}c=c<<2;h=ho(c);i[e+16>>2]=h;d=c+h|0;i[e+24>>2]=d;ip(h,0,c);i[e+20>>2]=d;while(1){c=b;d=i[c+48>>2];p=i[i[c>>2]>>2];s=q;d=up(i[c+40>>2],i[c+44>>2],q,0)+d|0;c=p+d|0;r=j[c|0]|j[c+1|0]<<8|(j[c+2|0]<<16|j[c+3|0]<<24);i[e+28>>2]=r;c:{d:{e:{if(!m){break e}d=i[e+32>>2];t=r>>>16|0;u=r&65535;p=t^(u^318)+239;c=p&m+131071;v=xp(m)>>>0>1;f:{if(!v){break f}c=p;if(c>>>0>>0){break f}c=(p>>>0)%(m>>>0)|0}d=i[(c<<2)+d>>2];if(!d){break e}d=i[d>>2];if(!d){break e}w=m+ -1|0;while(1){l=i[d+4>>2];g:{if((p|0)!=(l|0)){h:{if(!v){l=l&w;break h}if(l>>>0>>0){break h}l=(l>>>0)%(m>>>0)|0}if((c|0)==(l|0)){break g}break e}if(k[d+8>>1]!=(u|0)){break g}if(k[d+10>>1]==(t|0)){break d}}d=i[d>>2];if(d){continue}break}}i[e+12>>2]=n;i[e+8>>2]=r;ub(e+56|0,e+32|0,e+8|0,e+8|0);c=i[a+40>>2];hp(i[i[a>>2]>>2]+o(c,n)|0,e+28|0,c);h=i[e+16>>2];i[h+(s<<2)>>2]=n;n=n+1|0;break c}i[(s<<2)+h>>2]=i[d+12>>2]}c=f;f=q+1|0;if(f>>>0<1){c=c+1|0}q=f;f=c;d=i[a+80>>2];if(!c&q>>>0>>0|c>>>0<0){m=i[e+36>>2];continue}break}if((d|0)!=(n|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}c=f-b|0;d=(c|0)>-1?c:-1;f=b-f|0;c=o((d|0)<1?d:1,((f|0)>(c|0)?f:c)>>>2|0);c=c>>>0>1?c:1;d=0;while(1){f=b+(d<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+h>>2];d=d+1|0;if((c|0)!=(d|0)){continue}break}break i}g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;j:{if(d>>>0>b>>>0){Xa(a+68|0,d-b|0,1420);d=i[a+80>>2];break j}if(d>>>0>=b>>>0){break j}i[a+72>>2]=c+(d<<2)}h=i[e+16>>2];if(!d){break i}b=i[a+68>>2];l=0;while(1){c=l<<2;i[c+b>>2]=i[c+h>>2];l=l+1|0;if((l|0)!=(d|0)){continue}break}}i[a+80>>2]=n}if(!h){break b}i[e+20>>2]=h;bp(h)}d=i[e+40>>2];if(d){while(1){a=i[d>>2];bp(d);d=a;if(d){continue}break}}a=i[e+32>>2];i[e+32>>2]=0;if(a){bp(a)}F=e- -64|0;return n}Ho();x()}function xf(a,b,c){var d=0,e=0,f=0,g=0,h=0,k=0,l=0,m=0,n=0,o=0;a:{if(!i[a+8>>2]){break a}d=i[a>>2];l=a+4|0;i[a>>2]=l;i[i[a+4>>2]+8>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;g=i[d+4>>2];f=g?g:d;if(!f){kd(a,f);break a}g=i[f+8>>2];b:{if(!g){break b}d=i[g>>2];if((f|0)==(d|0)){i[g>>2]=0;d=i[g+4>>2];if(!d){e=g;break b}while(1){e=d;d=i[d>>2];if(d){continue}d=i[e+4>>2];if(d){continue}break}break b}i[g+4>>2]=0;if(!d){e=g;break b}while(1){e=d;d=i[d>>2];if(d){continue}d=i[e+4>>2];if(d){continue}break}}c:{if((b|0)==(c|0)){g=f;break c}k=b;while(1){g=e;h=f+16|0;to(h,k+16|0);to(f+28|0,k+28|0);d:{b=i[l>>2];if(!b){b=l;d=b;break d}d=j[f+27|0];e=d<<24>>24<0;d=e?i[f+20>>2]:d;o=e?i[f+16>>2]:h;while(1){h=j[b+27|0];e=h<<24>>24<0;e:{f:{g:{h:{h=e?i[b+20>>2]:h;m=h>>>0>>0?h:d;if(m){n=b+16|0;e=Km(o,e?i[n>>2]:n,m);if(e){break h}}if(d>>>0>>0){break g}break f}if((e|0)>-1){break f}}e=i[b>>2];if(e){break e}d=b;break d}e=i[b+4>>2];if(e){break e}d=b+4|0;break d}b=e;continue}}i[f+8>>2]=b;i[f>>2]=0;i[f+4>>2]=0;i[d>>2]=f;b=i[i[a>>2]>>2];if(b){i[a>>2]=b;f=i[d>>2]}Kf(i[a+4>>2],f);i[a+8>>2]=i[a+8>>2]+1;e=0;i:{if(!g){break i}d=i[g+8>>2];if(!d){break i}b=i[d>>2];if((g|0)==(b|0)){i[d>>2]=0;b=i[d+4>>2];if(!b){e=d;break i}while(1){e=b;b=i[b>>2];if(b){continue}b=i[e+4>>2];if(b){continue}break}break i}i[d+4>>2]=0;if(!b){e=d;break i}while(1){e=b;b=i[b>>2];if(b){continue}b=i[e+4>>2];if(b){continue}break}}d=i[k+4>>2];j:{if(!d){b=i[k+8>>2];if(i[b>>2]==(k|0)){break j}d=k+8|0;while(1){f=i[d>>2];d=f+8|0;b=i[f+8>>2];if((f|0)!=i[b>>2]){continue}break}break j}while(1){b=d;d=i[d>>2];if(d){continue}break}}if((b|0)==(c|0)){break c}k=b;f=g;if(f){continue}break}}kd(a,g);if(!e){break a}while(1){d=i[e+8>>2];if(d){e=d;continue}break}kd(a,e)}if((b|0)!=(c|0)){while(1){e=b;Lf(a,b+16|0);d=i[b+4>>2];k:{if(!d){b=i[e+8>>2];if((e|0)==i[b>>2]){break k}e=e+8|0;while(1){d=i[e>>2];e=d+8|0;b=i[d+8>>2];if((d|0)!=i[b>>2]){continue}break}break k}while(1){b=d;d=i[d>>2];if(d){continue}break}}if((b|0)!=(c|0)){continue}break}}}function Ja(a,b){var c=0,d=0,e=0,f=0,h=0,k=0,l=0,n=0,q=0,r=0,s=p(0),t=0,u=0,v=0,w=0;e=F+ -64|0;F=e;i[e+40>>2]=0;i[e+44>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;i[e+48>>2]=1065353216;c=i[a+80>>2];i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;a:{b:{if(!c){break b}if(c>>>0>=1073741824){break a}c=c<<2;h=ho(c);i[e+16>>2]=h;d=c+h|0;i[e+24>>2]=d;ip(h,0,c);i[e+20>>2]=d;while(1){c=b;d=i[c+48>>2];q=i[i[c>>2]>>2];t=r;d=up(i[c+40>>2],i[c+44>>2],r,0)+d|0;c=q+d|0;s=(Cp(2,j[c|0]|j[c+1|0]<<8|(j[c+2|0]<<16|j[c+3|0]<<24)),Gp());m[e+28>>2]=s;c:{d:{e:{if(!l){break e}d=i[e+32>>2];u=(Fp(s),Bp(2));q=u^318;c=q&l+ -1;v=xp(l)>>>0>1;f:{if(!v){break f}c=q;if(c>>>0>>0){break f}c=(q>>>0)%(l>>>0)|0}d=i[(c<<2)+d>>2];if(!d){break e}d=i[d>>2];if(!d){break e}w=l+ -1|0;while(1){k=i[d+4>>2];g:{if((q|0)!=(k|0)){h:{if(!v){k=k&w;break h}if(k>>>0>>0){break h}k=(k>>>0)%(l>>>0)|0}if((c|0)==(k|0)){break g}break e}if(i[d+8>>2]==(u|0)){break d}}d=i[d>>2];if(d){continue}break}}i[e+12>>2]=n;m[e+8>>2]=s;Wa(e+56|0,e+32|0,e+8|0,e+8|0);c=i[a+40>>2];hp(i[i[a>>2]>>2]+o(c,n)|0,e+28|0,c);h=i[e+16>>2];i[h+(t<<2)>>2]=n;n=n+1|0;break c}i[(t<<2)+h>>2]=i[d+12>>2]}c=f;f=r+1|0;if(f>>>0<1){c=c+1|0}r=f;f=c;d=i[a+80>>2];if(!c&r>>>0>>0|c>>>0<0){l=i[e+36>>2];continue}break}if((d|0)!=(n|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}c=f-b|0;d=(c|0)>-1?c:-1;f=b-f|0;c=o((d|0)<1?d:1,((f|0)>(c|0)?f:c)>>>2|0);c=c>>>0>1?c:1;d=0;while(1){f=b+(d<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+h>>2];d=d+1|0;if((c|0)!=(d|0)){continue}break}break i}g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;j:{if(d>>>0>b>>>0){Xa(a+68|0,d-b|0,1420);d=i[a+80>>2];break j}if(d>>>0>=b>>>0){break j}i[a+72>>2]=c+(d<<2)}h=i[e+16>>2];if(!d){break i}b=i[a+68>>2];k=0;while(1){c=k<<2;i[c+b>>2]=i[c+h>>2];k=k+1|0;if((k|0)!=(d|0)){continue}break}}i[a+80>>2]=n}if(!h){break b}i[e+20>>2]=h;bp(h)}d=i[e+40>>2];if(d){while(1){a=i[d>>2];bp(d);d=a;if(d){continue}break}}a=i[e+32>>2];i[e+32>>2]=0;if(a){bp(a)}F=e- -64|0;return n}Ho();x()}function Oa(a,b){var c=0,d=0,e=0,f=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;e=F+ -64|0;F=e;i[e+40>>2]=0;i[e+44>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;i[e+48>>2]=1065353216;c=i[a+80>>2];i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;a:{b:{if(!c){break b}if(c>>>0>=1073741824){break a}c=c<<2;k=ho(c);i[e+16>>2]=k;d=c+k|0;i[e+24>>2]=d;ip(k,0,c);i[e+20>>2]=d;while(1){c=b;d=i[c+48>>2];p=i[i[c>>2]>>2];s=q;d=up(i[c+40>>2],i[c+44>>2],q,0)+d|0;c=p+d|0;r=j[c|0]|j[c+1|0]<<8;h[e+30>>1]=r;c:{d:{e:{if(!m){break e}d=i[e+32>>2];t=r>>>8|0;u=r&255;p=t^(u^318)+239;c=p&m+ -1;v=xp(m)>>>0>1;f:{if(!v){break f}c=p;if(m>>>0>c>>>0){break f}c=(p>>>0)%(m>>>0)|0}d=i[(c<<2)+d>>2];if(!d){break e}d=i[d>>2];if(!d){break e}w=m+ -1|0;while(1){l=i[d+4>>2];g:{if((p|0)!=(l|0)){h:{if(!v){l=l&w;break h}if(l>>>0>>0){break h}l=(l>>>0)%(m>>>0)|0}if((c|0)==(l|0)){break g}break e}if(j[d+8|0]!=(u|0)){break g}if(j[d+9|0]==(t|0)){break d}}d=i[d>>2];if(d){continue}break}}i[e+12>>2]=n;h[e+8>>1]=r;ib(e+56|0,e+32|0,e+8|0,e+8|0);c=i[a+40>>2];hp(i[i[a>>2]>>2]+o(c,n)|0,e+30|0,c);k=i[e+16>>2];i[k+(s<<2)>>2]=n;n=n+1|0;break c}i[(s<<2)+k>>2]=i[d+12>>2]}c=f;f=q+1|0;if(f>>>0<1){c=c+1|0}q=f;f=c;d=i[a+80>>2];if(!c&q>>>0>>0|c>>>0<0){m=i[e+36>>2];continue}break}if((d|0)!=(n|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}c=f-b|0;d=(c|0)>-1?c:-1;f=b-f|0;c=o((d|0)<1?d:1,((f|0)>(c|0)?f:c)>>>2|0);c=c>>>0>1?c:1;d=0;while(1){f=b+(d<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+k>>2];d=d+1|0;if((c|0)!=(d|0)){continue}break}break i}g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;j:{if(d>>>0>b>>>0){Xa(a+68|0,d-b|0,1420);d=i[a+80>>2];break j}if(d>>>0>=b>>>0){break j}i[a+72>>2]=c+(d<<2)}k=i[e+16>>2];if(!d){break i}b=i[a+68>>2];l=0;while(1){c=l<<2;i[c+b>>2]=i[c+k>>2];l=l+1|0;if((l|0)!=(d|0)){continue}break}}i[a+80>>2]=n}if(!k){break b}i[e+20>>2]=k;bp(k)}d=i[e+40>>2];if(d){while(1){a=i[d>>2];bp(d);d=a;if(d){continue}break}}a=i[e+32>>2];i[e+32>>2]=0;if(a){bp(a)}F=e- -64|0;return n}Ho();x()}function Zf(a,b,c,d,e,f){var h=0,k=0,l=0;h=F-16|0;F=h;i[h+12>>2]=b;b=ho(32);i[h>>2]=b;i[h+4>>2]=17;i[h+8>>2]=-2147483616;g[b+17|0]=0;g[b+16|0]=j[10556];k=j[10552]|j[10553]<<8|(j[10554]<<16|j[10555]<<24);l=j[10548]|j[10549]<<8|(j[10550]<<16|j[10551]<<24);g[b+8|0]=l;g[b+9|0]=l>>>8;g[b+10|0]=l>>>16;g[b+11|0]=l>>>24;g[b+12|0]=k;g[b+13|0]=k>>>8;g[b+14|0]=k>>>16;g[b+15|0]=k>>>24;k=j[10544]|j[10545]<<8|(j[10546]<<16|j[10547]<<24);l=j[10540]|j[10541]<<8|(j[10542]<<16|j[10543]<<24);g[b|0]=l;g[b+1|0]=l>>>8;g[b+2|0]=l>>>16;g[b+3|0]=l>>>24;g[b+4|0]=k;g[b+5|0]=k>>>8;g[b+6|0]=k>>>16;g[b+7|0]=k>>>24;k=a+4|0;Yj(yf(k,h+12|0),h,c);if(g[h+11|0]<=-1){bp(i[h>>2])}a=ho(32);i[h>>2]=a;i[h+4>>2]=19;i[h+8>>2]=-2147483616;g[a+19|0]=0;b=j[10598]|j[10599]<<8|(j[10600]<<16|j[10601]<<24);g[a+15|0]=b;g[a+16|0]=b>>>8;g[a+17|0]=b>>>16;g[a+18|0]=b>>>24;b=j[10595]|j[10596]<<8|(j[10597]<<16|j[10598]<<24);c=j[10591]|j[10592]<<8|(j[10593]<<16|j[10594]<<24);g[a+8|0]=c;g[a+9|0]=c>>>8;g[a+10|0]=c>>>16;g[a+11|0]=c>>>24;g[a+12|0]=b;g[a+13|0]=b>>>8;g[a+14|0]=b>>>16;g[a+15|0]=b>>>24;b=j[10587]|j[10588]<<8|(j[10589]<<16|j[10590]<<24);c=j[10583]|j[10584]<<8|(j[10585]<<16|j[10586]<<24);g[a|0]=c;g[a+1|0]=c>>>8;g[a+2|0]=c>>>16;g[a+3|0]=c>>>24;g[a+4|0]=b;g[a+5|0]=b>>>8;g[a+6|0]=b>>>16;g[a+7|0]=b>>>24;Ff(yf(k,h+12|0),h,e,d);if(g[h+11|0]<=-1){bp(i[h>>2])}a=ho(32);i[h>>2]=a;i[h+4>>2]=18;i[h+8>>2]=-2147483616;g[a+18|0]=0;b=j[10619]|j[10620]<<8;g[a+16|0]=b;g[a+17|0]=b>>>8;b=j[10615]|j[10616]<<8|(j[10617]<<16|j[10618]<<24);c=j[10611]|j[10612]<<8|(j[10613]<<16|j[10614]<<24);g[a+8|0]=c;g[a+9|0]=c>>>8;g[a+10|0]=c>>>16;g[a+11|0]=c>>>24;g[a+12|0]=b;g[a+13|0]=b>>>8;g[a+14|0]=b>>>16;g[a+15|0]=b>>>24;b=j[10607]|j[10608]<<8|(j[10609]<<16|j[10610]<<24);c=j[10603]|j[10604]<<8|(j[10605]<<16|j[10606]<<24);g[a|0]=c;g[a+1|0]=c>>>8;g[a+2|0]=c>>>16;g[a+3|0]=c>>>24;g[a+4|0]=b;g[a+5|0]=b>>>8;g[a+6|0]=b>>>16;g[a+7|0]=b>>>24;Zj(yf(k,h+12|0),h,f);if(g[h+11|0]<=-1){bp(i[h>>2])}F=h+16|0}function Ef(a,b,c,d,e,f){var h=0,k=0,l=0;h=F-16|0;F=h;i[h+12>>2]=b;b=ho(32);i[h>>2]=b;i[h+4>>2]=17;i[h+8>>2]=-2147483616;g[b+17|0]=0;g[b+16|0]=j[10144];k=j[10140]|j[10141]<<8|(j[10142]<<16|j[10143]<<24);l=j[10136]|j[10137]<<8|(j[10138]<<16|j[10139]<<24);g[b+8|0]=l;g[b+9|0]=l>>>8;g[b+10|0]=l>>>16;g[b+11|0]=l>>>24;g[b+12|0]=k;g[b+13|0]=k>>>8;g[b+14|0]=k>>>16;g[b+15|0]=k>>>24;k=j[10132]|j[10133]<<8|(j[10134]<<16|j[10135]<<24);l=j[10128]|j[10129]<<8|(j[10130]<<16|j[10131]<<24);g[b|0]=l;g[b+1|0]=l>>>8;g[b+2|0]=l>>>16;g[b+3|0]=l>>>24;g[b+4|0]=k;g[b+5|0]=k>>>8;g[b+6|0]=k>>>16;g[b+7|0]=k>>>24;k=a+4|0;Yj(yf(k,h+12|0),h,c);if(g[h+11|0]<=-1){bp(i[h>>2])}a=ho(32);i[h>>2]=a;i[h+4>>2]=19;i[h+8>>2]=-2147483616;g[a+19|0]=0;b=j[10161]|j[10162]<<8|(j[10163]<<16|j[10164]<<24);g[a+15|0]=b;g[a+16|0]=b>>>8;g[a+17|0]=b>>>16;g[a+18|0]=b>>>24;b=j[10158]|j[10159]<<8|(j[10160]<<16|j[10161]<<24);c=j[10154]|j[10155]<<8|(j[10156]<<16|j[10157]<<24);g[a+8|0]=c;g[a+9|0]=c>>>8;g[a+10|0]=c>>>16;g[a+11|0]=c>>>24;g[a+12|0]=b;g[a+13|0]=b>>>8;g[a+14|0]=b>>>16;g[a+15|0]=b>>>24;b=j[10150]|j[10151]<<8|(j[10152]<<16|j[10153]<<24);c=j[10146]|j[10147]<<8|(j[10148]<<16|j[10149]<<24);g[a|0]=c;g[a+1|0]=c>>>8;g[a+2|0]=c>>>16;g[a+3|0]=c>>>24;g[a+4|0]=b;g[a+5|0]=b>>>8;g[a+6|0]=b>>>16;g[a+7|0]=b>>>24;Ff(yf(k,h+12|0),h,e,d);if(g[h+11|0]<=-1){bp(i[h>>2])}a=ho(32);i[h>>2]=a;i[h+4>>2]=18;i[h+8>>2]=-2147483616;g[a+18|0]=0;b=j[10182]|j[10183]<<8;g[a+16|0]=b;g[a+17|0]=b>>>8;b=j[10178]|j[10179]<<8|(j[10180]<<16|j[10181]<<24);c=j[10174]|j[10175]<<8|(j[10176]<<16|j[10177]<<24);g[a+8|0]=c;g[a+9|0]=c>>>8;g[a+10|0]=c>>>16;g[a+11|0]=c>>>24;g[a+12|0]=b;g[a+13|0]=b>>>8;g[a+14|0]=b>>>16;g[a+15|0]=b>>>24;b=j[10170]|j[10171]<<8|(j[10172]<<16|j[10173]<<24);c=j[10166]|j[10167]<<8|(j[10168]<<16|j[10169]<<24);g[a|0]=c;g[a+1|0]=c>>>8;g[a+2|0]=c>>>16;g[a+3|0]=c>>>24;g[a+4|0]=b;g[a+5|0]=b>>>8;g[a+6|0]=b>>>16;g[a+7|0]=b>>>24;Zj(yf(k,h+12|0),h,f);if(g[h+11|0]<=-1){bp(i[h>>2])}F=h+16|0}function Va(a,b){var c=0,d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0;e=F+ -64|0;F=e;i[e+40>>2]=0;i[e+44>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;i[e+48>>2]=1065353216;c=i[a+80>>2];i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;a:{b:{if(!c){break b}if(c>>>0>=1073741824){break a}c=c<<2;h=ho(c);i[e+16>>2]=h;d=c+h|0;i[e+24>>2]=d;ip(h,0,c);i[e+20>>2]=d;while(1){c=b;d=i[c+48>>2];n=i[i[c>>2]>>2];r=p;d=up(i[c+40>>2],i[c+44>>2],p,0)+d|0;c=n+d|0;q=j[c|0]|j[c+1|0]<<8|(j[c+2|0]<<16|j[c+3|0]<<24);i[e+28>>2]=q;c:{d:{e:{if(!l){break e}d=i[e+32>>2];n=q^318;c=n&l+ -1;s=xp(l)>>>0>1;f:{if(!s){break f}c=n;if(c>>>0>>0){break f}c=(n>>>0)%(l>>>0)|0}d=i[(c<<2)+d>>2];if(!d){break e}d=i[d>>2];if(!d){break e}t=l+ -1|0;while(1){k=i[d+4>>2];g:{if((n|0)!=(k|0)){h:{if(!s){k=k&t;break h}if(k>>>0>>0){break h}k=(k>>>0)%(l>>>0)|0}if((c|0)==(k|0)){break g}break e}if(i[d+8>>2]==(q|0)){break d}}d=i[d>>2];if(d){continue}break}}i[e+12>>2]=m;i[e+8>>2]=q;Wa(e+56|0,e+32|0,e+8|0,e+8|0);c=i[a+40>>2];hp(i[i[a>>2]>>2]+o(c,m)|0,e+28|0,c);h=i[e+16>>2];i[h+(r<<2)>>2]=m;m=m+1|0;break c}i[(r<<2)+h>>2]=i[d+12>>2]}c=f;f=p+1|0;if(f>>>0<1){c=c+1|0}p=f;f=c;d=i[a+80>>2];if(!c&p>>>0>>0|c>>>0<0){l=i[e+36>>2];continue}break}if((d|0)!=(m|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}c=f-b|0;d=(c|0)>-1?c:-1;f=b-f|0;c=o((d|0)<1?d:1,((f|0)>(c|0)?f:c)>>>2|0);c=c>>>0>1?c:1;d=0;while(1){f=b+(d<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+h>>2];d=d+1|0;if((c|0)!=(d|0)){continue}break}break i}g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;j:{if(d>>>0>b>>>0){Xa(a+68|0,d-b|0,1420);d=i[a+80>>2];break j}if(d>>>0>=b>>>0){break j}i[a+72>>2]=c+(d<<2)}h=i[e+16>>2];if(!d){break i}b=i[a+68>>2];k=0;while(1){c=k<<2;i[c+b>>2]=i[c+h>>2];k=k+1|0;if((k|0)!=(d|0)){continue}break}}i[a+80>>2]=m}if(!h){break b}i[e+20>>2]=h;bp(h)}d=i[e+40>>2];if(d){while(1){a=i[d>>2];bp(d);d=a;if(d){continue}break}}a=i[e+32>>2];i[e+32>>2]=0;if(a){bp(a)}F=e- -64|0;return m}Ho();x()}function lg(a,b,c,d,e){var f=0,h=0,k=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0;k=F-336|0;F=k;ip(k+80|0,0,256);h=i[d+4>>2];p=i[d>>2];if((h|0)!=(p|0)){m=h-p|0;f=(m|0)>-1?m:-1;q=(f|0)<1?f:1;f=p-h|0;f=o(q,((f|0)>(m|0)?f:m)>>>2|0);q=f>>>0>1?f:1;while(1){f=(k+80|0)+(i[p+(n<<2)>>2]<<3)|0;h=f;t=f;m=i[f+4>>2];f=i[f>>2]+1|0;if(f>>>0<1){m=m+1|0}i[t>>2]=f;i[h+4>>2]=m;n=n+1|0;if((q|0)!=(n|0)){continue}break}}r=Tj(k+48|0);i[k+8>>2]=0;i[k+12>>2]=0;h=k+16|0;f=h;i[f>>2]=0;i[f+4>>2]=0;p=k+24|0;f=p;i[f>>2]=0;i[f+4>>2]=0;i[k+32>>2]=0;i[k>>2]=0;i[k+4>>2]=0;i[k+40>>2]=0;i[k+44>>2]=0;ng(k,k+80|0,32,e);m=i[h>>2];f=i[h+4>>2];h=i[e+4>>2]-i[e>>2]|0;i[k+40>>2]=h;i[k+44>>2]=0;t=e;q=m;m=f<<1|m>>>31;f=(q<<1)+39|0;if(f>>>0<39){m=m+1|0}q=f;f=m>>>3|0;m=h+((m&7)<<29|q>>>3)|0;if(m>>>0>>0){f=f+1|0}h=m+8|0;h>>>0<8;Uj(t,h);f=i[e>>2];i[k+28>>2]=0;i[k+32>>2]=16384;i[p>>2]=f+i[k+40>>2];h=b;Vj(r,h<<5,h>>31<<5|h>>>27,0);b=h-c|0;if((b|0)>=0){y=(c|0)<1;f=b;while(1){n=i[k+32>>2];s=i[d>>2];q=i[k>>2]+(i[s+((f|0)/(c|0)<<2)>>2]<<3)|0;p=i[q>>2];m=p<<10;if(n>>>0>=m>>>0){while(1){h=i[k+28>>2];i[k+28>>2]=h+1;g[h+i[k+24>>2]|0]=n;n=i[k+32>>2]>>>8|0;i[k+32>>2]=n;if(n>>>0>=m>>>0){continue}break}s=i[d>>2]}h=(n>>>0)/(p>>>0)|0;i[k+32>>2]=i[q+4>>2]+((h<<12)+(n-o(h,p)|0)|0);n=b-f|0;h=(n|0)/(c|0)|0;if(!y){x=i[(h<<2)+s>>2];v=0;while(1){h=i[r+20>>2];if(!((x|0)<1|((h|0)<0?1:(h|0)<=0?l[r+16>>2]<1:0))){p=i[(n+v<<2)+a>>2];u=i[r+12>>2];s=i[u+4>>2];w=0;while(1){q=s>>>3|0;m=q+i[u>>2]|0;h=j[m|0];t=m;m=s&7;z=t,A=yp(-2,m)&h,g[z|0]=A;h=q+i[u>>2]|0;g[h|0]=j[h|0]|(p>>>w&1)<>2]+1|0;i[u+4>>2]=s;w=w+1|0;if((x|0)!=(w|0)){continue}break}}v=v+1|0;if((v|0)!=(c|0)){continue}break}}f=f-c|0;if((f|0)>-1){continue}break}}og(k,e);Wj(r);a=i[e+20>>2];if((a|0)<0?1:(a|0)<=0?l[e+16>>2]<=0:0){ca(e,i[e+4>>2],i[r>>2],i[r+4>>2])}a=i[k>>2];if(a){i[k+4>>2]=a;bp(a)}a=i[r+12>>2];i[r+12>>2]=0;if(a){bp(a)}a=i[r>>2];if(a){i[r+4>>2]=a;bp(a)}F=k+336|0;return 1}function Ra(a,b){var c=0,d=0,e=0,f=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;e=F+ -64|0;F=e;i[e+40>>2]=0;i[e+44>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;i[e+48>>2]=1065353216;c=i[a+80>>2];i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;a:{b:{if(!c){break b}if(c>>>0>=1073741824){break a}c=c<<2;l=ho(c);i[e+16>>2]=l;d=c+l|0;i[e+24>>2]=d;ip(l,0,c);i[e+20>>2]=d;while(1){c=b;d=i[c+48>>2];q=i[i[c>>2]>>2];t=r;d=up(i[c+40>>2],i[c+44>>2],r,0)+d|0;c=q+d|0;s=j[c|0]|j[c+1|0]<<8;h[e+30>>1]=s;c:{d:{e:{if(!n){break e}d=i[e+32>>2];q=s^318;c=q&n+ -1;u=xp(n)>>>0>1;f:{if(!u){break f}c=q;if(n>>>0>c>>>0){break f}c=(q>>>0)%(n>>>0)|0}d=i[(c<<2)+d>>2];if(!d){break e}d=i[d>>2];if(!d){break e}v=n+ -1|0;while(1){m=i[d+4>>2];g:{if((q|0)!=(m|0)){h:{if(!u){m=m&v;break h}if(m>>>0>>0){break h}m=(m>>>0)%(n>>>0)|0}if((c|0)==(m|0)){break g}break e}if(k[d+8>>1]==(s|0)){break d}}d=i[d>>2];if(d){continue}break}}i[e+12>>2]=p;h[e+8>>1]=s;tb(e+56|0,e+32|0,e+8|0,e+8|0);c=i[a+40>>2];hp(i[i[a>>2]>>2]+o(c,p)|0,e+30|0,c);l=i[e+16>>2];i[l+(t<<2)>>2]=p;p=p+1|0;break c}i[(t<<2)+l>>2]=i[d+12>>2]}c=f;f=r+1|0;if(f>>>0<1){c=c+1|0}r=f;f=c;d=i[a+80>>2];if(!c&r>>>0>>0|c>>>0<0){n=i[e+36>>2];continue}break}if((d|0)!=(p|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}c=f-b|0;d=(c|0)>-1?c:-1;f=b-f|0;c=o((d|0)<1?d:1,((f|0)>(c|0)?f:c)>>>2|0);c=c>>>0>1?c:1;d=0;while(1){f=b+(d<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+l>>2];d=d+1|0;if((c|0)!=(d|0)){continue}break}break i}g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;j:{if(d>>>0>b>>>0){Xa(a+68|0,d-b|0,1420);d=i[a+80>>2];break j}if(d>>>0>=b>>>0){break j}i[a+72>>2]=c+(d<<2)}l=i[e+16>>2];if(!d){break i}b=i[a+68>>2];m=0;while(1){c=m<<2;i[c+b>>2]=i[c+l>>2];m=m+1|0;if((m|0)!=(d|0)){continue}break}}i[a+80>>2]=p}if(!l){break b}i[e+20>>2]=l;bp(l)}d=i[e+40>>2];if(d){while(1){a=i[d>>2];bp(d);d=a;if(d){continue}break}}a=i[e+32>>2];i[e+32>>2]=0;if(a){bp(a)}F=e- -64|0;return p}Ho();x()}function Na(a,b){var c=0,d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0;e=F+ -64|0;F=e;i[e+40>>2]=0;i[e+44>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;i[e+48>>2]=1065353216;c=i[a+80>>2];i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;a:{b:{if(!c){break b}if(c>>>0>=1073741824){break a}c=c<<2;h=ho(c);i[e+16>>2]=h;d=c+h|0;i[e+24>>2]=d;ip(h,0,c);i[e+20>>2]=d;while(1){c=b;d=i[c+48>>2];n=i[i[c>>2]>>2];r=p;d=up(i[c+40>>2],i[c+44>>2],p,0)+d|0;q=j[n+d|0];g[e+31|0]=q;c:{d:{e:{if(!l){break e}d=i[e+32>>2];n=q^318;c=n&l+ -1;s=xp(l)>>>0>1;f:{if(!s){break f}c=n;if(c>>>0>>0){break f}c=(n>>>0)%(l>>>0)|0}d=i[(c<<2)+d>>2];if(!d){break e}d=i[d>>2];if(!d){break e}t=l+ -1|0;while(1){k=i[d+4>>2];g:{if((n|0)!=(k|0)){h:{if(!s){k=k&t;break h}if(k>>>0>>0){break h}k=(k>>>0)%(l>>>0)|0}if((c|0)==(k|0)){break g}break e}if(j[d+8|0]==(q|0)){break d}}d=i[d>>2];if(d){continue}break}}i[e+12>>2]=m;g[e+8|0]=q;hb(e+56|0,e+32|0,e+8|0,e+8|0);c=i[a+40>>2];hp(i[i[a>>2]>>2]+o(c,m)|0,e+31|0,c);h=i[e+16>>2];i[h+(r<<2)>>2]=m;m=m+1|0;break c}i[(r<<2)+h>>2]=i[d+12>>2]}c=f;f=p+1|0;if(f>>>0<1){c=c+1|0}p=f;f=c;d=i[a+80>>2];if(!c&p>>>0>>0|c>>>0<0){l=i[e+36>>2];continue}break}if((d|0)!=(m|0)){i:{if(!j[a+84|0]){f=i[a+72>>2];b=i[a+68>>2];if((f|0)==(b|0)){break i}c=f-b|0;d=(c|0)>-1?c:-1;f=b-f|0;c=o((d|0)<1?d:1,((f|0)>(c|0)?f:c)>>>2|0);c=c>>>0>1?c:1;d=0;while(1){f=b+(d<<2)|0;i[f>>2]=i[(i[f>>2]<<2)+h>>2];d=d+1|0;if((c|0)!=(d|0)){continue}break}break i}g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;j:{if(d>>>0>b>>>0){Xa(a+68|0,d-b|0,1420);d=i[a+80>>2];break j}if(d>>>0>=b>>>0){break j}i[a+72>>2]=c+(d<<2)}h=i[e+16>>2];if(!d){break i}b=i[a+68>>2];k=0;while(1){c=k<<2;i[c+b>>2]=i[c+h>>2];k=k+1|0;if((k|0)!=(d|0)){continue}break}}i[a+80>>2]=m}if(!h){break b}i[e+20>>2]=h;bp(h)}d=i[e+40>>2];if(d){while(1){a=i[d>>2];bp(d);d=a;if(d){continue}break}}a=i[e+32>>2];i[e+32>>2]=0;if(a){bp(a)}F=e- -64|0;return m}Ho();x()}function Ml(a){var b=0,c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;e=F-32|0;F=e;b=i[a>>2];d=i[a+4>>2];g[e|0]=0;o=Cj(e+16|0,d-b>>2,e);i[e+8>>2]=0;i[e>>2]=0;k=i[a>>2];b=i[a+4>>2];a:{b:{while(1){c:{p=0;l=0;if((b|0)==(k|0)){break c}while(1){c=i[o>>2];d:{if(i[c+(l>>>3&536870908)>>2]>>>l&1){break d}d=i[e>>2];i[e+4>>2]=d;b=l;while(1){e:{f=b+1|0;q=b;b=(f>>>0)%3|0?f:b+ -2|0;if((b|0)==-1){break e}b=i[i[a+12>>2]+(b<<2)>>2];if((b|0)==-1){break e}f=b+1|0;b=(f>>>0)%3|0?f:b+ -2|0;if((l|0)==(b|0)|(b|0)==-1){break e}if(!(i[(b>>>3&536870908)+c>>2]>>>b&1)){continue}}break}f=d;b=q;while(1){j=(b>>>3&536870908)+c|0;i[j>>2]=i[j>>2]|1<>>0)%3|0?j:b+ -2|0;m=((b>>>0)%3|0?-1:2)+b|0;if((d|0)!=(f|0)){n=i[(j<<2)+k>>2];b=f;while(1){f:{if((n|0)!=i[b>>2]){break f}c=-1;h=i[b+4>>2];c=(m|0)!=-1?i[i[a+12>>2]+(m<<2)>>2]:c;if((h|0)==(c|0)){break f}d=-1;d=(h|0)!=-1?i[i[a+12>>2]+(h<<2)>>2]:d;if((c|0)!=-1){i[i[a+12>>2]+(c<<2)>>2]=-1}b=i[a+12>>2];if((d|0)!=-1){i[b+(d<<2)>>2]=-1}i[b+(m<<2)>>2]=-1;i[b+(h<<2)>>2]=-1;p=1;break d}b=b+8|0;if((d|0)!=(b|0)){continue}break}}n=m<<2;k=i[n+k>>2];g:{if(i[e+8>>2]!=(d|0)){b=d;i[b>>2]=k;i[b+4>>2]=j;i[e+4>>2]=b+8;break g}d=d-f|0;h=d>>3;c=h+1|0;if(c>>>0>=536870912){break b}b=d>>2;c=h>>>0<268435455?b>>>0>>0?c:b:536870911;b=0;h:{if(!c){break h}if(c>>>0>=536870912){break a}b=ho(c<<3)}h=b+(h<<3)|0;i[h>>2]=k;i[h+4>>2]=j;j=b+(c<<3)|0;c=h+8|0;if((d|0)>=1){hp(b,f,d)}i[e+8>>2]=j;i[e+4>>2]=c;i[e>>2]=b;if(!f){break g}bp(f)}i:{if((m|0)==-1){break i}b=i[n+i[a+12>>2]>>2];if((b|0)==-1){break i}b=b+((b>>>0)%3|0?-1:2)|0;if((q|0)==(b|0)|(b|0)==-1){break i}k=i[a>>2];c=i[o>>2];d=i[e+4>>2];f=i[e>>2];continue}break}k=i[a>>2]}l=l+1|0;b=i[a+4>>2];if(l>>>0>2>>>0){continue}break}if(p){continue}}break}a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[o>>2];if(a){bp(a)}F=e+32|0;return}Ho();x()}za(16516);x()}function Wh(a,b,c){var d=0,e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;e=F+ -64|0;F=e;d=ho(80);o=i[b+8>>2];i[d+12>>2]=0;i[d+16>>2]=0;i[d>>2]=12588;i[d+4>>2]=0;k=d+20|0;f=k;i[f>>2]=0;i[f+4>>2]=0;p=d+28|0;f=p;i[f>>2]=0;i[f+4>>2]=0;i[d+36>>2]=0;i[d+40>>2]=0;l=d+44|0;f=l;i[f>>2]=0;i[f+4>>2]=0;i[d+52>>2]=0;i[d+76>>2]=0;i[d+72>>2]=c;i[d+68>>2]=o;i[d+64>>2]=0;i[d+56>>2]=0;i[d+60>>2]=0;i[d+8>>2]=12752;f=i[b+12>>2];q=e+36|0;h=q;i[h>>2]=0;i[h+4>>2]=0;r=e+28|0;h=r;i[h>>2]=0;i[h+4>>2]=0;m=e+20|0;h=m;i[h>>2]=0;i[h+4>>2]=0;h=e+12|0;i[h>>2]=0;i[h+4>>2]=0;i[e+52>>2]=0;i[e+56>>2]=0;i[e+44>>2]=0;i[e+48>>2]=0;i[e+4>>2]=0;i[e+8>>2]=0;i[e>>2]=12752;i[e+4>>2]=f;j=i[f>>2];n=i[f+4>>2];g[e+63|0]=0;Uh(e+24|0,(n-j>>2>>>0)/3|0,e+63|0);j=i[e+4>>2];n=i[j+28>>2];j=i[j+24>>2];g[e+63|0]=0;Uh(q,n-j>>2,e+63|0);i[m>>2]=d;i[e+16>>2]=o;i[h>>2]=c;i[e+8>>2]=f;i[d+76>>2]=b+72;i[p>>2]=i[m>>2];b=i[h+4>>2];i[k>>2]=i[h>>2];i[k+4>>2]=b;b=i[e+8>>2];i[d+12>>2]=i[e+4>>2];i[d+16>>2]=b;a:{b:{b=d;f=i[r>>2];if(f){h=d+32|0;c:{if(f>>>0<=i[d+40>>2]<<5>>>0){c=f+ -1>>>5|0;f=i[h>>2];break c}c=i[h>>2];if(c){bp(c);i[d+40>>2]=0;i[d+32>>2]=0;i[d+36>>2]=0;f=i[e+28>>2]}if((f|0)<=-1){break b}c=f+ -1>>>5|0;h=c+1|0;f=ho(h<<2);i[d+40>>2]=h;i[d+36>>2]=0;i[d+32>>2]=f}jp(f,i[e+24>>2],(c<<2)+4|0);c=i[e+28>>2]}else{c=0}i[b+36>>2]=c;c=d;f=i[e+40>>2];if(f){d:{if(f>>>0<=i[d+52>>2]<<5>>>0){b=f+ -1>>>5|0;f=i[l>>2];break d}b=i[l>>2];if(b){bp(b);i[d+52>>2]=0;i[d+44>>2]=0;i[d+48>>2]=0;f=i[e+40>>2]}if((f|0)<=-1){break a}b=f+ -1>>>5|0;h=b+1|0;f=ho(h<<2);i[d+52>>2]=h;i[d+48>>2]=0;i[d+44>>2]=f}jp(f,i[e+36>>2],(b<<2)+4|0);b=i[e+40>>2]}else{b=0}i[c+48>>2]=b;Vh(d+56|0,i[e+48>>2],i[e+52>>2]);i[a>>2]=d;i[e>>2]=12752;a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}i[e>>2]=12568;a=i[e+36>>2];if(a){bp(a)}a=i[e+24>>2];if(a){bp(a)}F=e- -64|0;return}Ho();x()}Ho();x()}function ne(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;i[a+8>>2]=e;g=a+32|0;h=i[g>>2];f=i[a+36>>2]-h>>2;a:{if(f>>>0>>0){Bd(g,e-f|0);break a}if(f>>>0<=e>>>0){break a}i[a+36>>2]=h+(e<<2)}b:{if(!d){break b}h=1;f=i[b>>2];c:{if((d|0)<=1){g=f;break c}g=f;while(1){j=i[(h<<2)+b>>2];k=(j|0)<(g|0);g=k?j:g;f=k?f:(j|0)>(f|0)?j:f;h=h+1|0;if((h|0)!=(d|0)){continue}break}}i[a+16>>2]=f;i[a+12>>2]=g;h=f;f=(f>>31)-((g>>31)+(f>>>0>>0)|0)|0;g=h-g|0;if(!f&g>>>0>2147483646|f>>>0>0){break b}f=g+1|0;i[a+20>>2]=f;g=(f|0)/2|0;i[a+24>>2]=g;i[a+28>>2]=0-g;if(f&1){break b}i[a+24>>2]=g+ -1}j=d-e|0;if((j|0)>=1){g=i[a+8>>2];o=0-e<<2;while(1){if((g|0)>=1){d=j<<2;p=d+c|0;n=b+d|0;d=n+o|0;h=0;while(1){f=0;d:{if((g|0)<=0){g=i[a+32>>2];break d}while(1){k=f<<2;l=i[k+d>>2];m=i[a+16>>2];e:{if((l|0)>(m|0)){g=i[a+32>>2];i[k+g>>2]=m;break e}g=i[a+32>>2];k=k+g|0;m=i[a+12>>2];if((l|0)<(m|0)){i[k>>2]=m;break e}i[k>>2]=l}f=f+1|0;if((f|0)>2]){continue}break}}f=h<<2;d=f+p|0;f=i[f+n>>2]-i[f+g>>2]|0;i[d>>2]=f;f:{if((f|0)>2]){f=f+i[a+20>>2]|0}else{if((f|0)<=i[a+24>>2]){break f}f=f-i[a+20>>2]|0}i[d>>2]=f}d=g;h=h+1|0;g=i[a+8>>2];if((h|0)<(g|0)){continue}break}}j=j-e|0;if((j|0)>0){continue}break}}h=0;d=(e&1073741823)!=(e|0)?-1:e<<2;e=ip(ho(d),0,d);g=i[a+8>>2];if((g|0)>=1){d=e;while(1){f=0;g:{if((g|0)<=0){g=i[a+32>>2];break g}while(1){j=f<<2;k=i[j+d>>2];l=i[a+16>>2];h:{if((k|0)>(l|0)){g=i[a+32>>2];i[j+g>>2]=l;break h}g=i[a+32>>2];j=j+g|0;l=i[a+12>>2];if((k|0)<(l|0)){i[j>>2]=l;break h}i[j>>2]=k}f=f+1|0;if((f|0)>2]){continue}break}}f=h<<2;d=f+c|0;f=i[b+f>>2]-i[f+g>>2]|0;i[d>>2]=f;i:{if((f|0)>2]){f=f+i[a+20>>2]|0}else{if((f|0)<=i[a+24>>2]){break i}f=f-i[a+20>>2]|0}i[d>>2]=f}d=g;h=h+1|0;g=i[a+8>>2];if((h|0)<(g|0)){continue}break}}bp(e);return 1}function Wf(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0;a:{if(!i[a+8>>2]){break a}g=i[a>>2];j=a+4|0;i[a>>2]=j;i[i[a+4>>2]+8>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;d=i[g+4>>2];f=d?d:g;if(!f){wf(a,f);break a}g=i[f+8>>2];b:{if(!g){break b}d=i[g>>2];if((f|0)==(d|0)){i[g>>2]=0;d=i[g+4>>2];if(!d){e=g;break b}while(1){e=d;d=i[d>>2];if(d){continue}d=i[e+4>>2];if(d){continue}break}break b}i[g+4>>2]=0;if(!d){e=g;break b}while(1){e=d;d=i[d>>2];if(d){continue}d=i[e+4>>2];if(d){continue}break}}c:{if((b|0)==(c|0)){g=f;break c}h=b;while(1){g=e;i[f+16>>2]=i[h+16>>2];if((f|0)!=(h|0)){xf(f+20|0,i[h+20>>2],h+24|0)}d:{b=i[j>>2];if(!b){b=j;d=b;break d}e=i[f+16>>2];while(1){e:{if((e|0)>2]){d=i[b>>2];if(d){break e}d=b;break d}d=i[b+4>>2];if(d){break e}d=b+4|0;break d}b=d;continue}}i[f+8>>2]=b;i[f>>2]=0;i[f+4>>2]=0;i[d>>2]=f;b=i[i[a>>2]>>2];if(b){i[a>>2]=b;f=i[d>>2]}Kf(i[a+4>>2],f);i[a+8>>2]=i[a+8>>2]+1;e=0;f:{if(!g){break f}d=i[g+8>>2];if(!d){break f}b=i[d>>2];if((g|0)==(b|0)){i[d>>2]=0;b=i[d+4>>2];if(!b){e=d;break f}while(1){e=b;b=i[b>>2];if(b){continue}b=i[e+4>>2];if(b){continue}break}break f}i[d+4>>2]=0;if(!b){e=d;break f}while(1){e=b;b=i[b>>2];if(b){continue}b=i[e+4>>2];if(b){continue}break}}d=i[h+4>>2];g:{if(!d){b=i[h+8>>2];if(i[b>>2]==(h|0)){break g}f=h+8|0;while(1){d=i[f>>2];f=d+8|0;b=i[d+8>>2];if((d|0)!=i[b>>2]){continue}break}break g}while(1){b=d;d=i[d>>2];if(d){continue}break}}if(!g){break c}h=b;f=g;if((b|0)!=(c|0)){continue}break}}wf(a,g);if(!e){break a}while(1){d=i[e+8>>2];if(d){e=d;continue}break}wf(a,e)}if((b|0)!=(c|0)){while(1){e=b;$f(a,b+16|0);d=i[b+4>>2];h:{if(!d){b=i[e+8>>2];if((e|0)==i[b>>2]){break h}e=e+8|0;while(1){d=i[e>>2];e=d+8|0;b=i[d+8>>2];if((d|0)!=i[b>>2]){continue}break}break h}while(1){b=d;d=i[d>>2];if(d){continue}break}}if((b|0)!=(c|0)){continue}break}}}function sf(a,b){var c=0,d=0,e=0,f=0,h=0,j=0,k=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0;f=F-32|0;F=f;m=f;e=i[a>>2];h=i[e>>2];d=i[e+4>>2];n=+(h>>>0)+ +(d>>>0)*4294967296;d=i[e+12>>2]+d|0;e=i[e+8>>2];h=e+h|0;if(h>>>0>>0){d=d+1|0}e=!(d|h);n=n/(+((e?1:h)>>>0)+ +((e?0:d)>>>0)*4294967296)*256+.5;a:{if(n<4294967296&n>=0){d=~~n>>>0;break a}d=0}c=d>>>0<255?d:255;p=!(c&255)+c|0;g[m+23|0]=p;h=i[a+16>>2];r=i[a+12>>2];i[f+16>>2]=0;i[f+8>>2]=0;i[f+12>>2]=0;b:{c:{c=(h-r<<1)- -64|0;if(c){if((c|0)<=-1){break c}k=ho(c);i[f+8>>2]=k;d=c+k|0;i[f+16>>2]=d;ip(k,0,c);i[f+12>>2]=d}c=i[a+28>>2]+ -1|0;if((c|0)<0){e=4096;break b}m=0-p|0;s=m&255;t=i[a+24>>2];e=4096;while(1){q=t&1<>>0>=d<<12>>>0){g[j+k|0]=e;j=j+1|0;e=e>>>8|0}u=256-d|0;d=d<<3;up(i[d+14192>>2],0,e,0);e=((q?0:s)+e|0)+o(u,H+e>>>i[d+14196>>2]|0)|0;c=c+ -1|0;if((c|0)>-1){continue}break}break b}Ho();x()}if((h|0)!=(r|0)){m=0-p|0;s=m&255;while(1){h=h+ -4|0;t=i[h>>2];c=31;while(1){d=c;q=t&1<>>0>=c<<12>>>0){g[j+k|0]=e;j=j+1|0;e=e>>>8|0}u=256-c|0;c=c<<3;up(i[c+14192>>2],0,e,0);e=((q?0:s)+e|0)+o(u,H+e>>>i[c+14196>>2]|0)|0;c=d+ -1|0;if(d){continue}break}if((h|0)!=(r|0)){continue}break}}c=e+ -4096|0;d:{e:{if(c>>>0<=63){e=j+k|0;d=1;break e}if(c>>>0<=16383){c=j+k|0;d=e+12288|0;g[c|0]=d;e=c+1|0;c=d>>>8|0;d=2;break e}if(c>>>0>4194303){break d}c=j+k|0;d=e+8384512|0;g[c|0]=d;g[c+1|0]=d>>>8;e=c+2|0;c=d>>>16|0;d=3}g[e|0]=c;j=d+j|0}c=i[b+20>>2];if((c|0)<0?1:(c|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],f+23|0,f+24|0)}Jb(j,b);c=i[b+20>>2];if((c|0)<0?1:(c|0)<=0?l[b+16>>2]<=0:0){d=b;c=i[b+4>>2];b=i[f+8>>2];ca(d,c,b,b+j|0)}i[f+24>>2]=0;i[f+28>>2]=0;of(a,f+24|0);i[a+24>>2]=0;i[a+28>>2]=0;i[a+16>>2]=i[a+12>>2];a=i[f+8>>2];if(a){i[f+12>>2]=a;bp(a)}F=f+32|0}function ti(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,k=0;e=F-16|0;F=e;i[a+4>>2]=b;i[a+8>>2]=i[b+56>>2];i[a+188>>2]=i[a+184>>2];f=i[b+48>>2];b=ho(32);i[e>>2]=b;i[e+4>>2]=19;i[e+8>>2]=-2147483616;g[b+19|0]=0;c=j[11251]|j[11252]<<8|(j[11253]<<16|j[11254]<<24);g[b+15|0]=c;g[b+16|0]=c>>>8;g[b+17|0]=c>>>16;g[b+18|0]=c>>>24;c=j[11248]|j[11249]<<8|(j[11250]<<16|j[11251]<<24);d=j[11244]|j[11245]<<8|(j[11246]<<16|j[11247]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[11240]|j[11241]<<8|(j[11242]<<16|j[11243]<<24);d=j[11236]|j[11237]<<8|(j[11238]<<16|j[11239]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;b=Sb(f,e);if(g[e+11|0]<=-1){bp(i[e>>2])}f=i[i[a+4>>2]+48>>2];a:{if(b){b=ho(32);i[e>>2]=b;i[e+4>>2]=19;i[e+8>>2]=-2147483616;g[b+19|0]=0;c=j[11251]|j[11252]<<8|(j[11253]<<16|j[11254]<<24);g[b+15|0]=c;g[b+16|0]=c>>>8;g[b+17|0]=c>>>16;g[b+18|0]=c>>>24;c=j[11248]|j[11249]<<8|(j[11250]<<16|j[11251]<<24);d=j[11244]|j[11245]<<8|(j[11246]<<16|j[11247]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[11240]|j[11241]<<8|(j[11242]<<16|j[11243]<<24);d=j[11236]|j[11237]<<8|(j[11238]<<16|j[11239]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;h=a,k=ck(f,e,0),g[h+352|0]=k;if(g[e+11|0]>-1){break a}bp(i[e>>2]);break a}if((_b(f)|0)>=6){g[a+352|0]=1;break a}g[a+352|0]=0}F=e+16|0;return 1}function rf(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;e=i[a>>2];d=e;l=d;f=i[d+12>>2];h=i[d+8>>2];c=c>>>1&1431655765|c<<1&-1431655766;c=c>>>2&858993459|c<<2&-858993460;c=c>>>4&252645135|c<<4&-252645136;k=32-b|0;g=yp(c>>>8&16711935|c<<8&-16711936,16)>>>k|0;c=g-(g>>>1&1431655765)|0;c=(c>>>2&858993459)+(c&858993459)|0;j=o((c>>>4|0)+c&252645135,16843009)>>>24|0;c=j;h=h+c|0;if(h>>>0>>0){f=f+1|0}i[l+8>>2]=h;i[d+12>>2]=f;c=e;d=b-j|0;f=d+i[c>>2]|0;e=i[c+4>>2]+(d>>31)|0;i[c>>2]=f;i[c+4>>2]=f>>>0>>0?e+1|0:e;a:{b:{c:{d:{c=i[a+28>>2];j=32-c|0;e:{f:{if((j|0)>=(b|0)){b=b+c|0;i[a+28>>2]=b;e=-1>>>k<>2]&(e^-1)|e&g<>2]=f;if((b|0)!=32){break e}b=i[a+16>>2];if((b|0)!=i[a+20>>2]){i[b>>2]=f;i[a+16>>2]=b+4;b=0;c=0;break f}c=i[a+12>>2];e=b-c|0;d=e>>2;g=d+1|0;if(g>>>0>=1073741824){break d}j=e>>1;g=d>>>0<536870911?j>>>0>>0?g:j:1073741823;b=0;g:{if(!g){break g}if(g>>>0>=1073741824){break c}b=ho(g<<2)}d=b+(d<<2)|0;i[d>>2]=f;g=b+(g<<2)|0;f=d+4|0;if((e|0)>=1){hp(b,c,e)}i[a+20>>2]=g;i[a+16>>2]=f;i[a+12>>2]=b;b=0;if(!c){c=0;break f}bp(c);c=0;break f}k=i[a+24>>2]&(-1<>2]=k;c=i[a+16>>2];h:{if((c|0)!=i[a+20>>2]){i[c>>2]=k;i[a+16>>2]=c+4;break h}e=i[a+12>>2];f=c-e|0;h=f>>2;d=h+1|0;if(d>>>0>=1073741824){break b}c=f>>1;d=h>>>0<536870911?c>>>0>>0?d:c:1073741823;c=0;i:{if(!d){break i}if(d>>>0>=1073741824){break a}c=ho(d<<2)}h=c+(h<<2)|0;i[h>>2]=k;d=c+(d<<2)|0;k=h+4|0;if((f|0)>=1){hp(c,e,f)}i[a+20>>2]=d;i[a+16>>2]=k;i[a+12>>2]=c;if(!e){break h}bp(e)}c=b-j|0;b=-1>>>32-c&g>>>j}i[a+28>>2]=c;i[a+24>>2]=b}return}Ho();x()}za(10036);x()}Ho();x()}za(10036);x()}function Nh(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,k=0;e=F-16|0;F=e;i[a+4>>2]=b;i[a+8>>2]=i[b+56>>2];i[a+188>>2]=i[a+184>>2];f=i[b+48>>2];b=ho(32);i[e>>2]=b;i[e+4>>2]=19;i[e+8>>2]=-2147483616;g[b+19|0]=0;c=j[11251]|j[11252]<<8|(j[11253]<<16|j[11254]<<24);g[b+15|0]=c;g[b+16|0]=c>>>8;g[b+17|0]=c>>>16;g[b+18|0]=c>>>24;c=j[11248]|j[11249]<<8|(j[11250]<<16|j[11251]<<24);d=j[11244]|j[11245]<<8|(j[11246]<<16|j[11247]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[11240]|j[11241]<<8|(j[11242]<<16|j[11243]<<24);d=j[11236]|j[11237]<<8|(j[11238]<<16|j[11239]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;b=Sb(f,e);if(g[e+11|0]<=-1){bp(i[e>>2])}f=i[i[a+4>>2]+48>>2];a:{if(b){b=ho(32);i[e>>2]=b;i[e+4>>2]=19;i[e+8>>2]=-2147483616;g[b+19|0]=0;c=j[11251]|j[11252]<<8|(j[11253]<<16|j[11254]<<24);g[b+15|0]=c;g[b+16|0]=c>>>8;g[b+17|0]=c>>>16;g[b+18|0]=c>>>24;c=j[11248]|j[11249]<<8|(j[11250]<<16|j[11251]<<24);d=j[11244]|j[11245]<<8|(j[11246]<<16|j[11247]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[11240]|j[11241]<<8|(j[11242]<<16|j[11243]<<24);d=j[11236]|j[11237]<<8|(j[11238]<<16|j[11239]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;h=a,k=ck(f,e,0),g[h+288|0]=k;if(g[e+11|0]>-1){break a}bp(i[e>>2]);break a}if((_b(f)|0)>=6){g[a+288|0]=1;break a}g[a+288|0]=0}F=e+16|0;return 1}function tf(a){var b=0,c=0,d=0,e=0;d=F-16|0;F=d;Xj(a);b=a+16|0;i[b>>2]=0;i[b+4>>2]=0;i[a+12>>2]=b;e=Xj(a+24|0);a=ho(32);i[d>>2]=a;i[d+4>>2]=20;i[d+8>>2]=-2147483616;g[a+20|0]=0;b=j[10408]|j[10409]<<8|(j[10410]<<16|j[10411]<<24);g[a+16|0]=b;g[a+17|0]=b>>>8;g[a+18|0]=b>>>16;g[a+19|0]=b>>>24;b=j[10404]|j[10405]<<8|(j[10406]<<16|j[10407]<<24);c=j[10400]|j[10401]<<8|(j[10402]<<16|j[10403]<<24);g[a+8|0]=c;g[a+9|0]=c>>>8;g[a+10|0]=c>>>16;g[a+11|0]=c>>>24;g[a+12|0]=b;g[a+13|0]=b>>>8;g[a+14|0]=b>>>16;g[a+15|0]=b>>>24;b=j[10396]|j[10397]<<8|(j[10398]<<16|j[10399]<<24);c=j[10392]|j[10393]<<8|(j[10394]<<16|j[10395]<<24);g[a|0]=c;g[a+1|0]=c>>>8;g[a+2|0]=c>>>16;g[a+3|0]=c>>>24;g[a+4|0]=b;g[a+5|0]=b>>>8;g[a+6|0]=b>>>16;g[a+7|0]=b>>>24;Yj(e,d,1);if(g[d+11|0]<=-1){bp(i[d>>2])}a=ho(32);i[d>>2]=a;i[d+4>>2]=22;i[d+8>>2]=-2147483616;g[a+22|0]=0;b=j[10431]|j[10432]<<8|(j[10433]<<16|j[10434]<<24);c=j[10427]|j[10428]<<8|(j[10429]<<16|j[10430]<<24);g[a+14|0]=c;g[a+15|0]=c>>>8;g[a+16|0]=c>>>16;g[a+17|0]=c>>>24;g[a+18|0]=b;g[a+19|0]=b>>>8;g[a+20|0]=b>>>16;g[a+21|0]=b>>>24;b=j[10425]|j[10426]<<8|(j[10427]<<16|j[10428]<<24);c=j[10421]|j[10422]<<8|(j[10423]<<16|j[10424]<<24);g[a+8|0]=c;g[a+9|0]=c>>>8;g[a+10|0]=c>>>16;g[a+11|0]=c>>>24;g[a+12|0]=b;g[a+13|0]=b>>>8;g[a+14|0]=b>>>16;g[a+15|0]=b>>>24;b=j[10417]|j[10418]<<8|(j[10419]<<16|j[10420]<<24);c=j[10413]|j[10414]<<8|(j[10415]<<16|j[10416]<<24);g[a|0]=c;g[a+1|0]=c>>>8;g[a+2|0]=c>>>16;g[a+3|0]=c>>>24;g[a+4|0]=b;g[a+5|0]=b>>>8;g[a+6|0]=b>>>16;g[a+7|0]=b>>>24;Yj(e,d,1);if(g[d+11|0]<=-1){bp(i[d>>2])}F=d+16|0}function Qf(a){var b=0,c=0,d=0,e=0;d=F-16|0;F=d;Xj(a);b=a+16|0;i[b>>2]=0;i[b+4>>2]=0;i[a+12>>2]=b;e=Xj(a+24|0);a=ho(32);i[d>>2]=a;i[d+4>>2]=20;i[d+8>>2]=-2147483616;g[a+20|0]=0;b=j[10676]|j[10677]<<8|(j[10678]<<16|j[10679]<<24);g[a+16|0]=b;g[a+17|0]=b>>>8;g[a+18|0]=b>>>16;g[a+19|0]=b>>>24;b=j[10672]|j[10673]<<8|(j[10674]<<16|j[10675]<<24);c=j[10668]|j[10669]<<8|(j[10670]<<16|j[10671]<<24);g[a+8|0]=c;g[a+9|0]=c>>>8;g[a+10|0]=c>>>16;g[a+11|0]=c>>>24;g[a+12|0]=b;g[a+13|0]=b>>>8;g[a+14|0]=b>>>16;g[a+15|0]=b>>>24;b=j[10664]|j[10665]<<8|(j[10666]<<16|j[10667]<<24);c=j[10660]|j[10661]<<8|(j[10662]<<16|j[10663]<<24);g[a|0]=c;g[a+1|0]=c>>>8;g[a+2|0]=c>>>16;g[a+3|0]=c>>>24;g[a+4|0]=b;g[a+5|0]=b>>>8;g[a+6|0]=b>>>16;g[a+7|0]=b>>>24;Yj(e,d,1);if(g[d+11|0]<=-1){bp(i[d>>2])}a=ho(32);i[d>>2]=a;i[d+4>>2]=22;i[d+8>>2]=-2147483616;g[a+22|0]=0;b=j[10699]|j[10700]<<8|(j[10701]<<16|j[10702]<<24);c=j[10695]|j[10696]<<8|(j[10697]<<16|j[10698]<<24);g[a+14|0]=c;g[a+15|0]=c>>>8;g[a+16|0]=c>>>16;g[a+17|0]=c>>>24;g[a+18|0]=b;g[a+19|0]=b>>>8;g[a+20|0]=b>>>16;g[a+21|0]=b>>>24;b=j[10693]|j[10694]<<8|(j[10695]<<16|j[10696]<<24);c=j[10689]|j[10690]<<8|(j[10691]<<16|j[10692]<<24);g[a+8|0]=c;g[a+9|0]=c>>>8;g[a+10|0]=c>>>16;g[a+11|0]=c>>>24;g[a+12|0]=b;g[a+13|0]=b>>>8;g[a+14|0]=b>>>16;g[a+15|0]=b>>>24;b=j[10685]|j[10686]<<8|(j[10687]<<16|j[10688]<<24);c=j[10681]|j[10682]<<8|(j[10683]<<16|j[10684]<<24);g[a|0]=c;g[a+1|0]=c>>>8;g[a+2|0]=c>>>16;g[a+3|0]=c>>>24;g[a+4|0]=b;g[a+5|0]=b>>>8;g[a+6|0]=b>>>16;g[a+7|0]=b>>>24;Yj(e,d,1);if(g[d+11|0]<=-1){bp(i[d>>2])}F=d+16|0}function ij(a,b){var c=0,d=0,e=0;i[a>>2]=i[b>>2];i[a+4>>2]=i[b+4>>2];i[a+8>>2]=i[b+8>>2];c=b+12|0;i[a+12>>2]=i[c>>2];i[c>>2]=0;i[b+4>>2]=0;i[b+8>>2]=0;c=b+16|0;i[a+16>>2]=i[c>>2];i[a+20>>2]=i[b+20>>2];d=b+24|0;i[a+24>>2]=i[d>>2];i[d>>2]=0;i[c>>2]=0;i[c+4>>2]=0;d=j[b+28|0];e=a+40|0;i[e>>2]=0;c=a+32|0;i[c>>2]=0;i[c+4>>2]=0;g[a+28|0]=d;d=c;c=b+32|0;i[d>>2]=i[c>>2];i[a+36>>2]=i[b+36>>2];d=b+40|0;i[e>>2]=i[d>>2];i[d>>2]=0;i[c>>2]=0;i[c+4>>2]=0;d=a+52|0;i[d>>2]=0;c=a+44|0;i[c>>2]=0;i[c+4>>2]=0;e=c;c=b+44|0;i[e>>2]=i[c>>2];i[a+48>>2]=i[b+48>>2];e=d;d=b+52|0;i[e>>2]=i[d>>2];i[d>>2]=0;i[c>>2]=0;i[c+4>>2]=0;d=a- -64|0;i[d>>2]=0;c=a+56|0;i[c>>2]=0;i[c+4>>2]=0;e=c;c=b+56|0;i[e>>2]=i[c>>2];i[a+60>>2]=i[b+60>>2];e=d;d=b- -64|0;i[e>>2]=i[d>>2];i[d>>2]=0;i[c>>2]=0;i[c+4>>2]=0;i[a+68>>2]=i[b+68>>2];d=i[b+72>>2];e=a+84|0;i[e>>2]=0;c=a+76|0;i[c>>2]=0;i[c+4>>2]=0;i[a+72>>2]=d;d=c;c=b+76|0;i[d>>2]=i[c>>2];i[a+80>>2]=i[b+80>>2];d=b+84|0;i[e>>2]=i[d>>2];i[d>>2]=0;i[c>>2]=0;i[c+4>>2]=0;d=a+96|0;i[d>>2]=0;c=a+88|0;i[c>>2]=0;i[c+4>>2]=0;e=c;c=b+88|0;i[e>>2]=i[c>>2];i[a+92>>2]=i[b+92>>2];e=d;d=b+96|0;i[e>>2]=i[d>>2];i[d>>2]=0;i[c>>2]=0;i[c+4>>2]=0;c=j[b+100|0];d=a+112|0;i[d>>2]=0;i[a+104>>2]=0;i[a+108>>2]=0;g[a+100|0]=c;i[a+104>>2]=i[b+104>>2];i[a+108>>2]=i[b+108>>2];c=b+112|0;i[d>>2]=i[c>>2];i[c>>2]=0;i[b+104>>2]=0;i[b+108>>2]=0;d=a+124|0;i[d>>2]=0;c=a+116|0;i[c>>2]=0;i[c+4>>2]=0;e=c;c=b+116|0;i[e>>2]=i[c>>2];i[a+120>>2]=i[b+120>>2];e=d;d=b+124|0;i[e>>2]=i[d>>2];i[d>>2]=0;i[c>>2]=0;i[c+4>>2]=0;i[a+128>>2]=i[b+128>>2];i[a+132>>2]=i[b+132>>2];return a}function pj(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;g=F-16|0;F=g;i[g+8>>2]=0;i[g>>2]=0;i[g+4>>2]=0;d=i[a+56>>2];c=i[d+96>>2];l=i[d+100>>2]-c|0;d=(l|0)/12|0;a:{if(!l){break a}n=d>>>0>1?d:1;d=0;l=0;b:{while(1){c:{m=o(l,12)+c|0;h=i[m>>2];c=h-j|0;e=c>>31;j=(e^c+e)<<1|c>>>31;d:{if((b|0)!=(d|0)){i[b>>2]=j;b=b+4|0;i[g+4>>2]=b;break d}e=d-f|0;d=e>>2;b=d+1|0;if(b>>>0>=1073741824){break c}k=e>>1;b=d>>>0<536870911?k>>>0>>0?b:k:1073741823;c=0;e:{if(!b){break e}if(b>>>0>=1073741824){break b}c=ho(b<<2)}k=c+(d<<2)|0;i[k>>2]=j;d=c+(b<<2)|0;b=k+4|0;if((e|0)>=1){hp(c,f,e)}i[g+8>>2]=d;i[g+4>>2]=b;i[g>>2]=c;if(f){bp(f)}f=c}k=i[m+4>>2];c=k-h|0;e=c>>31;j=(e^c+e)<<1|c>>>31;f:{if((b|0)!=(d|0)){i[b>>2]=j;b=b+4|0;i[g+4>>2]=b;break f}e=d-f|0;d=e>>2;b=d+1|0;if(b>>>0>1073741823){break c}h=e>>1;b=d>>>0<536870911?h>>>0>>0?b:h:1073741823;c=0;g:{if(!b){break g}if(b>>>0>1073741823){break b}c=ho(b<<2)}h=c+(d<<2)|0;i[h>>2]=j;d=c+(b<<2)|0;b=h+4|0;if((e|0)>=1){hp(c,f,e)}i[g+8>>2]=d;i[g+4>>2]=b;i[g>>2]=c;if(f){bp(f)}f=c}j=i[m+8>>2];c=j-k|0;e=c>>31;m=(e^c+e)<<1|c>>>31;h:{if((b|0)!=(d|0)){i[b>>2]=m;b=b+4|0;i[g+4>>2]=b;break h}e=d-f|0;d=e>>2;b=d+1|0;if(b>>>0>1073741823){break c}h=e>>1;b=d>>>0<536870911?h>>>0>>0?b:h:1073741823;c=0;i:{if(!b){break i}if(b>>>0>1073741823){break b}c=ho(b<<2)}h=c+(d<<2)|0;i[h>>2]=m;d=c+(b<<2)|0;b=h+4|0;if((e|0)>=1){hp(c,f,e)}i[g+8>>2]=d;i[g+4>>2]=b;i[g>>2]=c;if(f){bp(f)}f=c}l=l+1|0;if((n|0)==(l|0)){break a}c=i[i[a+56>>2]+96>>2];continue}break}Ho();x()}za(13344);x()}kg(f,b-f>>2,1,0,i[a+44>>2]);if(f){bp(f)}F=g+16|0}function Qe(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0;h=F-32|0;F=h;d=(e&1073741823)!=(e|0)?-1:e<<2;p=ip(ho(d),0,d);d=i[a+40>>2];g=i[d>>2];a:{d=i[d+4>>2]-g|0;if((d|0)<5){break a}l=d>>2;f=d>>>2|0;d=f+ -1|0;if(l>>>0>d>>>0){q=i[a+32>>2];s=a+8|0;t=i[a+36>>2];while(1){m=o(d,e);b:{c:{g=i[(d<<2)+g>>2];if((g|0)==-1){break c}g=i[i[q+12>>2]+(g<<2)>>2];if((g|0)==-1){break c}j=-1;n=i[t>>2];k=i[q>>2];r=i[n+(i[k+(g<<2)>>2]<<2)>>2];l=g+1|0;l=(l>>>0)%3|0?l:g+ -2|0;if((l|0)!=-1){l=i[(l<<2)+k>>2]}else{l=-1}g=g+((g>>>0)%3|0?-1:2)|0;if((g|0)!=-1){j=i[(g<<2)+k>>2]}if((r|0)>=(d|0)){break c}g=i[(l<<2)+n>>2];if((g|0)>=(d|0)){break c}j=i[n+(j<<2)>>2];if((j|0)>=(d|0)){break c}if((e|0)>=1){j=o(e,j);k=o(e,g);n=o(e,r);g=0;while(1){i[(g<<2)+p>>2]=(i[(g+j<<2)+b>>2]+i[(g+k<<2)+b>>2]|0)-i[(g+n<<2)+b>>2];g=g+1|0;if((g|0)!=(e|0)){continue}break}}g=i[p>>2];j=i[p+4>>2];m=m<<2;k=m+b|0;n=i[k+4>>2];i[h+16>>2]=i[k>>2];i[h+20>>2]=n;i[h+8>>2]=g;i[h+12>>2]=j;break b}g=(o(f+ -2|0,e)<<2)+b|0;j=i[g>>2];g=i[g+4>>2];m=m<<2;k=m+b|0;n=i[k+4>>2];i[h+16>>2]=i[k>>2];i[h+20>>2]=n;i[h+8>>2]=j;i[h+12>>2]=g}ze(h+24|0,s,h+16|0,h+8|0);g=c+m|0;i[g>>2]=i[h+24>>2];i[g+4>>2]=i[h+28>>2];if((f|0)<3){break a}f=d;m=i[a+40>>2];g=i[m>>2];d=d+ -1|0;if(i[m+4>>2]-g>>2>>>0>d>>>0){continue}break}}Io();x()}if((e|0)>=1){ip(p,0,e<<2)}d=i[p>>2];e=i[p+4>>2];f=i[b+4>>2];i[h+16>>2]=i[b>>2];i[h+20>>2]=f;i[h+8>>2]=d;i[h+12>>2]=e;ze(h+24|0,a+8|0,h+16|0,h+8|0);i[c>>2]=i[h+24>>2];i[c+4>>2]=i[h+28>>2];bp(p);F=h+32|0;return 1}function ye(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0;h=F-32|0;F=h;d=(e&1073741823)!=(e|0)?-1:e<<2;n=ip(ho(d),0,d);d=i[a+40>>2];g=i[d>>2];a:{d=i[d+4>>2]-g|0;if((d|0)<5){break a}j=d>>2;f=d>>>2|0;d=f+ -1|0;if(j>>>0>d>>>0){q=i[a+32>>2];r=a+8|0;s=i[a+36>>2];while(1){k=o(d,e);b:{c:{g=i[(d<<2)+g>>2];if((g|0)==-1|i[i[q>>2]+(g>>>3&536870908)>>2]>>>g&1){break c}g=i[i[i[q+64>>2]+12>>2]+(g<<2)>>2];if((g|0)==-1){break c}j=i[s>>2];l=i[q+28>>2];m=i[j+(i[l+(g<<2)>>2]<<2)>>2];if((m|0)>=(d|0)){break c}p=g+1|0;p=i[j+(i[l+(((p>>>0)%3|0?p:g+ -2|0)<<2)>>2]<<2)>>2];if((p|0)>=(d|0)){break c}g=i[j+(i[l+(g+((g>>>0)%3|0?-1:2)<<2)>>2]<<2)>>2];if((g|0)>=(d|0)){break c}if((e|0)>=1){l=o(e,g);j=o(e,p);m=o(e,m);g=0;while(1){i[(g<<2)+n>>2]=(i[(g+l<<2)+b>>2]+i[(g+j<<2)+b>>2]|0)-i[(g+m<<2)+b>>2];g=g+1|0;if((g|0)!=(e|0)){continue}break}}g=i[n>>2];l=i[n+4>>2];k=k<<2;j=k+b|0;m=i[j+4>>2];i[h+16>>2]=i[j>>2];i[h+20>>2]=m;i[h+8>>2]=g;i[h+12>>2]=l;break b}g=(o(f+ -2|0,e)<<2)+b|0;l=i[g>>2];g=i[g+4>>2];k=k<<2;j=k+b|0;m=i[j+4>>2];i[h+16>>2]=i[j>>2];i[h+20>>2]=m;i[h+8>>2]=l;i[h+12>>2]=g}ze(h+24|0,r,h+16|0,h+8|0);g=c+k|0;i[g>>2]=i[h+24>>2];i[g+4>>2]=i[h+28>>2];if((f|0)<3){break a}f=d;k=i[a+40>>2];g=i[k>>2];d=d+ -1|0;if(i[k+4>>2]-g>>2>>>0>d>>>0){continue}break}}Io();x()}if((e|0)>=1){ip(n,0,e<<2)}d=i[n>>2];e=i[n+4>>2];f=i[b+4>>2];i[h+16>>2]=i[b>>2];i[h+20>>2]=f;i[h+8>>2]=d;i[h+12>>2]=e;ze(h+24|0,a+8|0,h+16|0,h+8|0);i[c>>2]=i[h+24>>2];i[c+4>>2]=i[h+28>>2];bp(n);F=h+32|0;return 1}function Nf(a,b,c,d,e){var f=0,g=0,h=0,k=0,l=0,m=0,n=0,o=0,p=0;a:{b:{c:{d:{e:{n=a+4|0;f:{if((n|0)==(b|0)){break f}f=j[b+27|0];h=f<<24>>24<0;k=j[e+11|0];p=k<<24>>24;g=(p|0)<0;f=h?i[b+20>>2]:f;k=g?i[e+4>>2]:k;l=f>>>0>>0;m=l?f:k;if(m){g=g?i[e>>2]:e;o=b+16|0;h=h?i[o>>2]:o;o=Km(g,h,m);if(!o){if(k>>>0>>0){break f}break e}if((o|0)>-1){break e}break f}if(k>>>0>=f>>>0){break d}}h=i[b>>2];d=b;g:{h:{if(i[a>>2]==(b|0)){break h}i:{if(h){f=h;while(1){d=f;f=i[f+4>>2];if(f){continue}break}break i}d=b+8|0;if(i[i[b+8>>2]>>2]==(b|0)){while(1){f=i[d>>2];d=f+8|0;if((f|0)==i[i[f+8>>2]>>2]){continue}break}}d=i[d>>2]}g=j[e+11|0];f=g<<24>>24<0;l=j[d+27|0];k=l<<24>>24<0;j:{g=f?i[e+4>>2]:g;l=k?i[d+20>>2]:l;m=g>>>0>>0?g:l;if(m){n=d+16|0;f=Km(k?i[n>>2]:n,f?i[e>>2]:e,m);if(f){break j}}if(l>>>0>>0){break h}break g}if((f|0)>-1){break g}}if(!h){i[c>>2]=b;return b}i[c>>2]=d;return d+4|0}return Of(a,c,e)}f=Km(h,g,m);if(f){break c}}if(l){break b}break a}if((f|0)>-1){break a}}h=i[b+4>>2];k:{if(h){f=h;while(1){d=f;f=i[f>>2];if(f){continue}break}break k}d=i[b+8>>2];if(i[d>>2]==(b|0)){break k}f=b+8|0;while(1){g=i[f>>2];f=g+8|0;d=i[g+8>>2];if((g|0)!=i[d>>2]){continue}break}}l:{m:{if((d|0)==(n|0)){break m}g=j[d+27|0];f=g<<24>>24<0;n:{g=f?i[d+20>>2]:g;l=g>>>0>>0?g:k;if(l){m=d+16|0;f=Km((p|0)<0?i[e>>2]:e,f?i[m>>2]:m,l);if(f){break n}}if(k>>>0>>0){break m}break l}if((f|0)>-1){break l}}if(!h){i[c>>2]=b;return b+4|0}i[c>>2]=d;return d}return Of(a,c,e)}i[c>>2]=b;i[d>>2]=b;return d}function ge(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0;i[a+8>>2]=e;i[a- -64>>2]=f;g=a+32|0;j=i[g>>2];f=i[a+36>>2]-j>>2;a:{if(f>>>0>>0){Bd(g,e-f|0);break a}if(f>>>0<=e>>>0){break a}i[a+36>>2]=j+(e<<2)}b:{if(!d){break b}j=1;f=i[b>>2];c:{if((d|0)<=1){g=f;break c}g=f;while(1){h=i[(j<<2)+b>>2];k=(h|0)<(g|0);g=k?h:g;f=k?f:(h|0)>(f|0)?h:f;j=j+1|0;if((j|0)!=(d|0)){continue}break}}i[a+16>>2]=f;i[a+12>>2]=g;d=(f>>31)-((g>>31)+(f>>>0>>0)|0)|0;f=f-g|0;if(!d&f>>>0>2147483646|d>>>0>0){break b}d=f+1|0;i[a+20>>2]=d;f=(d|0)/2|0;i[a+24>>2]=f;i[a+28>>2]=0-f;if(d&1){break b}i[a+24>>2]=f+ -1}d=i[a+56>>2];g=i[d>>2];d:{d=i[d+4>>2]-g|0;if((d|0)<1){break d}h=(d>>>2|0)+ -1|0;if(d>>2>>>0>h>>>0){p=a+60|0;j=a+68|0;while(1){he(p,i[(h<<2)+g>>2],b,h);g=i[a+8>>2];if((g|0)>=1){d=o(e,h)<<2;q=d+c|0;r=b+d|0;d=j;k=0;while(1){f=0;e:{if((g|0)<=0){g=i[a+32>>2];break e}while(1){l=f<<2;n=i[l+d>>2];m=i[a+16>>2];f:{if((n|0)>(m|0)){g=i[a+32>>2];i[l+g>>2]=m;break f}g=i[a+32>>2];l=l+g|0;m=i[a+12>>2];if((n|0)<(m|0)){i[l>>2]=m;break f}i[l>>2]=n}f=f+1|0;if((f|0)>2]){continue}break}}f=k<<2;d=f+q|0;f=i[f+r>>2]-i[f+g>>2]|0;i[d>>2]=f;g:{if((f|0)>2]){f=f+i[a+20>>2]|0}else{if((f|0)<=i[a+24>>2]){break g}f=f-i[a+20>>2]|0}i[d>>2]=f}d=g;k=k+1|0;g=i[a+8>>2];if((k|0)<(g|0)){continue}break}}h=h+ -1|0;if((h|0)<0){break d}d=i[a+56>>2];g=i[d>>2];if(i[d+4>>2]-g>>2>>>0>h>>>0){continue}break}}Io();x()}return 1}function Od(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0;i[a+8>>2]=e;i[a- -64>>2]=f;g=a+32|0;j=i[g>>2];f=i[a+36>>2]-j>>2;a:{if(f>>>0>>0){Bd(g,e-f|0);break a}if(f>>>0<=e>>>0){break a}i[a+36>>2]=j+(e<<2)}b:{if(!d){break b}j=1;f=i[b>>2];c:{if((d|0)<=1){g=f;break c}g=f;while(1){h=i[(j<<2)+b>>2];k=(h|0)<(g|0);g=k?h:g;f=k?f:(h|0)>(f|0)?h:f;j=j+1|0;if((j|0)!=(d|0)){continue}break}}i[a+16>>2]=f;i[a+12>>2]=g;d=(f>>31)-((g>>31)+(f>>>0>>0)|0)|0;f=f-g|0;if(!d&f>>>0>2147483646|d>>>0>0){break b}d=f+1|0;i[a+20>>2]=d;f=(d|0)/2|0;i[a+24>>2]=f;i[a+28>>2]=0-f;if(d&1){break b}i[a+24>>2]=f+ -1}d=i[a+56>>2];g=i[d>>2];d:{d=i[d+4>>2]-g|0;if((d|0)<1){break d}h=(d>>>2|0)+ -1|0;if(d>>2>>>0>h>>>0){p=a+60|0;j=a+68|0;while(1){Pd(p,i[(h<<2)+g>>2],b,h);g=i[a+8>>2];if((g|0)>=1){d=o(e,h)<<2;q=d+c|0;r=b+d|0;d=j;k=0;while(1){f=0;e:{if((g|0)<=0){g=i[a+32>>2];break e}while(1){l=f<<2;n=i[l+d>>2];m=i[a+16>>2];f:{if((n|0)>(m|0)){g=i[a+32>>2];i[l+g>>2]=m;break f}g=i[a+32>>2];l=l+g|0;m=i[a+12>>2];if((n|0)<(m|0)){i[l>>2]=m;break f}i[l>>2]=n}f=f+1|0;if((f|0)>2]){continue}break}}f=k<<2;d=f+q|0;f=i[f+r>>2]-i[f+g>>2]|0;i[d>>2]=f;g:{if((f|0)>2]){f=f+i[a+20>>2]|0}else{if((f|0)<=i[a+24>>2]){break g}f=f-i[a+20>>2]|0}i[d>>2]=f}d=g;k=k+1|0;g=i[a+8>>2];if((k|0)<(g|0)){continue}break}}h=h+ -1|0;if((h|0)<0){break d}d=i[a+56>>2];g=i[d>>2];if(i[d+4>>2]-g>>2>>>0>h>>>0){continue}break}}Io();x()}return 1}function ze(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;e=c;f=i[b+16>>2];g=i[e+4>>2]-f|0;i[e>>2]=i[e>>2]-f;i[e+4>>2]=g;e=i[d+4>>2]-f|0;f=i[d>>2]-f|0;i[d>>2]=f;i[d+4>>2]=e;g=e>>31;k=g+e^g;g=f>>31;h=i[b+16>>2];if((k+(g+f^g)|0)>(h|0)){j=i[c+4>>2];l=i[c>>2];a:{b:{if((l|0)>=0){e=1;g=1;if((j|0)>-1){break a}f=1;e=-1;g=-1;if((l|0)>=1){break b}break a}f=-1;e=-1;g=-1;if((j|0)<1){break a}}e=(j|0)<1?-1:1;g=f}f=1;k=-1;m=l<<1;l=o(g,h);m=m-l|0;g=(o(e,g)|0)>-1;e=o(e,h);i[c+4>>2]=((g?0-m|0:m)+e|0)/2;e=(j<<1)-e|0;i[c>>2]=(l+(g?0-e|0:e)|0)/2;g=i[d+4>>2];j=i[d>>2];c:{d:{if((j|0)>=0){e=1;if((g|0)>-1){break c}k=1;f=-1;e=-1;if((j|0)>=1){break d}break c}f=-1;e=-1;if((g|0)<1){break c}}f=(g|0)<1?-1:1;e=k}h=j<<1;k=i[b+16>>2];j=o(k,e);h=h-j|0;m=0-h|0;l=h;h=(o(e,f)|0)>-1;f=o(f,k);e=((h?m:l)+f|0)/2|0;i[d+4>>2]=e;f=(g<<1)-f|0;f=(j+(h?0-f|0:f)|0)/2|0;i[d>>2]=f}k=d;e:{f:{g:{h:{i:{j:{if(!f){if(e){break j}e=0;f=0;break e}if((e|0)<1?(f|0)<=-1:0){break e}if((f|0)<1){break i}if((e|0)<=-1){break g}e=0-i[c+4>>2]|0;i[c>>2]=0-i[c>>2];i[c+4>>2]=e;f=0-i[d>>2]|0;e=0-i[d+4>>2]|0;break f}if((e|0)<1){break g}break h}if((e|0)>0){break h}f=i[d>>2];e=i[d+4>>2];break f}e=c;g=i[e>>2];i[e>>2]=0-i[e+4>>2];i[e+4>>2]=g;f=0-i[d+4>>2]|0;e=i[d>>2];break f}e=0-i[c>>2]|0;i[c>>2]=i[c+4>>2];i[c+4>>2]=e;f=i[d+4>>2];e=0-i[d>>2]|0}i[k>>2]=f;i[k+4>>2]=e}d=i[c+4>>2]-e|0;e=a;c=i[c>>2]-f|0;if((c|0)<=-1){c=c+i[b+4>>2]|0}i[e>>2]=c;if((d|0)<=-1){d=d+i[b+4>>2]|0}i[a+4>>2]=d}function Gi(a){a=a|0;var b=0,c=0,d=0,e=0;i[a>>2]=11424;b=i[a+336>>2];if(b){c=i[a+340>>2];d=b;a:{if((b|0)==(c|0)){break a}while(1){d=c+ -12|0;e=i[d>>2];if(e){i[c+ -8>>2]=e;bp(e)}c=d;if((c|0)!=(b|0)){continue}break}d=i[a+336>>2]}i[a+340>>2]=b;bp(d)}b=i[a+304>>2];if(b){i[a+308>>2]=b;bp(b)}b=i[a+292>>2];if(b){i[a+296>>2]=b;bp(b)}b=a+280|0;c=i[b>>2];i[b>>2]=0;if(c){d=c+ -4|0;b=i[d>>2];if(b){b=c+(b<<5)|0;while(1){b=nf(b+ -32|0);if((c|0)!=(b|0)){continue}break}}bp(d)}b=i[a+268>>2];if(b){i[a+272>>2]=b;bp(b)}c=a+244|0;b=i[c>>2];i[c>>2]=0;if(b){bp(b)}b=i[a+232>>2];if(b){i[a+236>>2]=b;bp(b)}nf(a+200|0);b=i[a+184>>2];if(b){i[a+188>>2]=b;bp(b)}c=i[a+172>>2];if(c){b=i[a+176>>2];d=c;b:{if((c|0)==(b|0)){break b}while(1){d=i[b+ -20>>2];if(d){i[b+ -16>>2]=d;bp(d)}d=b+ -136|0;e=i[b+ -32>>2];if(e){i[b+ -28>>2]=e;bp(e)}ni(b+ -132|0);b=d;if((c|0)!=(b|0)){continue}break}d=i[a+172>>2]}i[a+176>>2]=c;bp(d)}b=i[a+152>>2];if(b){i[a+156>>2]=b;bp(b)}b=i[a+140>>2];if(b){bp(b)}b=i[a+128>>2];if(b){while(1){c=i[b>>2];bp(b);b=c;if(b){continue}break}}b=i[a+120>>2];i[a+120>>2]=0;if(b){bp(b)}b=i[a+108>>2];if(b){i[a+112>>2]=b;bp(b)}b=i[a+96>>2];if(b){i[a+100>>2]=b;bp(b)}b=i[a+84>>2];if(b){bp(b)}b=i[a+72>>2];if(b){i[a+76>>2]=b;bp(b)}b=i[a+52>>2];if(b){i[a+56>>2]=b;bp(b)}b=i[a+40>>2];if(b){i[a+44>>2]=b;bp(b)}b=i[a+28>>2];if(b){bp(b)}b=i[a+16>>2];if(b){i[a+20>>2]=b;bp(b)}b=i[a+12>>2];i[a+12>>2]=0;if(b){bi(b)}return a|0}function _a(a,b,c,d){var e=0,f=0,h=0,j=0,k=p(0),l=0,n=0,o=0,q=0,r=0,s=p(0),t=0,u=0;n=i[c+12>>2];o=i[c+8>>2];q=i[c+4>>2];r=i[c>>2];j=n^(o^(q^(r^318)+239)+239)+239;t=a;e=i[b+4>>2];a:{b:{if(!e){break b}f=i[b>>2];l=xp(e);h=j&e+ -1;c:{if(l>>>0<=1){break c}c=j;h=c;if(c>>>0>>0){break c}h=(j>>>0)%(e>>>0)|0}c=h;h=c;c=i[(c<<2)+f>>2];if(!c){break b}c=i[c>>2];if(!c){break b}u=e+ -1|0;l=l>>>0>1;while(1){f=i[c+4>>2];if((j|0)!=(f|0)){d:{if(!l){f=f&u;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(h|0)){break b}}if(!(i[c+8>>2]!=(r|0)|i[c+12>>2]!=(q|0)|(i[c+16>>2]!=(o|0)|i[c+20>>2]!=(n|0)))){b=0;break a}c=i[c>>2];if(c){continue}break}}c=ho(28);f=i[d+12>>2];i[c+16>>2]=i[d+8>>2];i[c+20>>2]=f;f=i[d+4>>2];i[c+8>>2]=i[d>>2];i[c+12>>2]=f;d=i[d+16>>2];i[c>>2]=0;i[c+4>>2]=j;i[c+24>>2]=d;k=m[b+16>>2];s=p(i[b+12>>2]+1>>>0);e:{if(p(k*p(e>>>0))>>0<3|e<<1;d=b;k=p(v(p(s/k)));f:{if(k=p(0)){h=~~k>>>0;break f}h=0}fb(d,e>>>0>>0?h:e);e=i[b+4>>2];if(!(e&e+ -1)){h=j&e+ -1;break e}if(j>>>0>>0){h=j;break e}h=(j>>>0)%(e>>>0)|0}d=i[b>>2]+(h<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[t+4|0]=b;i[a>>2]=c}function ff(a,b){var c=0,d=0,e=0,f=0,h=0,k=0;d=F-16|0;F=d;a:{if((_b(i[b+48>>2])|0)>9){break a}if((I[i[i[b>>2]+8>>2]](b)|0)!=1){break a}a=i[i[i[b+4>>2]+8>>2]+(a<<2)>>2];e=i[a+56>>2];if((e|0)==3){e=5;if((_b(i[b+48>>2])|0)<4){break a}e=i[a+56>>2]}a=_b(i[b+48>>2]);if((e|0)==1){b:{if((a|0)>3){break b}f=xm(i[b+4>>2],0);a=ym(i[b+4>>2]);if(!a){break b}e=6;a=i[a+28>>2]+ -1|0;c:{if(a>>>0<=10){a=1279>>>a&1;break c}a=0}if(a){break a}h=i[b+48>>2];a=ho(32);i[d>>2]=a;i[d+4>>2]=17;i[d+8>>2]=-2147483616;g[a+17|0]=0;g[a+16|0]=j[9948];b=j[9944]|j[9945]<<8|(j[9946]<<16|j[9947]<<24);c=j[9940]|j[9941]<<8|(j[9942]<<16|j[9943]<<24);g[a+8|0]=c;g[a+9|0]=c>>>8;g[a+10|0]=c>>>16;g[a+11|0]=c>>>24;g[a+12|0]=b;g[a+13|0]=b>>>8;g[a+14|0]=b>>>16;g[a+15|0]=b>>>24;b=j[9936]|j[9937]<<8|(j[9938]<<16|j[9939]<<24);c=j[9932]|j[9933]<<8|(j[9934]<<16|j[9935]<<24);g[a|0]=c;g[a+1|0]=c>>>8;g[a+2|0]=c>>>16;g[a+3|0]=c>>>24;g[a+4|0]=b;g[a+5|0]=b>>>8;g[a+6|0]=b>>>16;g[a+7|0]=b>>>24;d:{e:{c=h+16|0;b=i[c>>2];if(!b){break e}a=c;while(1){k=i[b+16>>2]<(f|0);a=k?a:b;b=i[(k<<2)+b>>2];if(b){continue}break}if((a|0)==(c|0)|(f|0)>2]){break e}a=a+20|0;if(!Sb(a,d)){break e}a=$j(a,d,-1);break d}a=$j(h,d,-1)}if(g[d+11|0]<=-1){bp(i[d>>2])}if((a|0)>0){break a}}e=0;break a}e=0;if((a|0)>7){break a}e=1;if((_b(i[b+48>>2])|0)>1){break a}F=d+16|0;return l[i[b+4>>2]+80>>2]<40?1:4}F=d+16|0;return e}function Th(a,b,c){var d=0,e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;d=F-112|0;F=d;e=ho(120);m=i[b+8>>2];i[e+12>>2]=0;i[e+16>>2]=0;i[e>>2]=12120;i[e+4>>2]=0;i[e+20>>2]=0;i[e+24>>2]=0;i[e+28>>2]=0;i[e+32>>2]=0;i[e+36>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;i[e+56>>2]=0;i[e+60>>2]=0;i[e+8>>2]=12332;h=e- -64|0;i[h>>2]=0;i[h+4>>2]=0;i[e+72>>2]=0;i[e+76>>2]=0;i[e+80>>2]=0;i[e+84>>2]=0;i[e+88>>2]=0;i[e+116>>2]=0;i[e+112>>2]=c;i[e+108>>2]=m;i[e+104>>2]=0;i[e+96>>2]=0;i[e+100>>2]=0;h=i[b+12>>2];i[d+52>>2]=0;n=d+44|0;j=n;i[j>>2]=0;i[j+4>>2]=0;i[d+36>>2]=0;i[d+40>>2]=0;j=d+28|0;i[j>>2]=0;i[j+4>>2]=0;o=d+20|0;f=o;i[f>>2]=0;i[f+4>>2]=0;f=d- -64|0;i[f>>2]=0;i[f+4>>2]=0;i[d+72>>2]=0;i[d+76>>2]=0;f=d+80|0;i[f>>2]=0;i[f+4>>2]=0;i[d+88>>2]=0;i[d+104>>2]=0;i[d+12>>2]=0;i[d+16>>2]=0;i[d+56>>2]=0;i[d+60>>2]=0;i[d+8>>2]=12332;i[d+96>>2]=0;i[d+100>>2]=0;i[d+12>>2]=h;k=i[h>>2];l=i[h+4>>2];g[d+111|0]=0;Uh(d+32|0,(l-k>>2>>>0)/3|0,d+111|0);k=i[d+12>>2];l=i[k+28>>2];k=i[k+24>>2];g[d+111|0]=0;Uh(n,l-k>>2,d+111|0);i[j>>2]=e;i[d+24>>2]=m;i[o>>2]=c;i[d+16>>2]=h;i[e+116>>2]=b+72;Yh(e,d+8|0);i[a>>2]=e;i[d+8>>2]=12332;a=i[d+96>>2];if(a){i[d+100>>2]=a;bp(a)}a=i[f>>2];if(a){i[d+84>>2]=a;bp(a)}a=i[d+68>>2];if(a){i[d+72>>2]=a;bp(a)}a=i[d+56>>2];if(a){i[d+60>>2]=a;bp(a)}i[d+8>>2]=12568;a=i[d+44>>2];if(a){bp(a)}a=i[d+32>>2];if(a){bp(a)}F=d+112|0}function wb(a,b,c,d){var e=0,f=0,h=0,j=0,l=0,n=p(0),o=0,q=0,r=0,s=0,t=p(0),u=0,w=0;q=k[c+6>>1];r=k[c+4>>1];s=k[c+2>>1];l=k[c>>1];j=q^(r^(s^(l^318)+239)+239)+239;u=a;e=i[b+4>>2];a:{b:{if(!e){break b}f=i[b>>2];o=xp(e);h=j&e+ -1;c:{if(o>>>0<=1){break c}c=j;h=c;if(c>>>0>>0){break c}h=(j>>>0)%(e>>>0)|0}c=h;h=c;c=i[(c<<2)+f>>2];if(!c){break b}c=i[c>>2];if(!c){break b}w=e+ -1|0;o=o>>>0>1;while(1){f=i[c+4>>2];if((j|0)!=(f|0)){d:{if(!o){f=f&w;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(h|0)){break b}}if(!(k[c+8>>1]!=(l|0)|k[c+10>>1]!=(s|0)|(k[c+12>>1]!=(r|0)|k[c+14>>1]!=(q|0)))){b=0;break a}c=i[c>>2];if(c){continue}break}}c=ho(20);l=i[d+4>>2];i[c+8>>2]=i[d>>2];i[c+12>>2]=l;d=i[d+8>>2];i[c+4>>2]=j;i[c+16>>2]=d;i[c>>2]=0;n=m[b+16>>2];t=p(i[b+12>>2]+1>>>0);e:{if(p(n*p(e>>>0))>>0<3|e<<1;d=b;n=p(v(p(t/n)));f:{if(n=p(0)){h=~~n>>>0;break f}h=0}Db(d,l>>>0>>0?h:l);e=i[b+4>>2];if(!(e&e+ -1)){h=j&e+ -1;break e}if(j>>>0>>0){h=j;break e}h=(j>>>0)%(e>>>0)|0}d=i[b>>2]+(h<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[u+4|0]=b;i[a>>2]=c}function Vc(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,h=0,k=0,l=0;e=F-16|0;F=e;d=i[b+28>>2];f=i[i[b+4>>2]+(c<<2)>>2];h=i[i[i[d+4>>2]+8>>2]+(f<<2)>>2];b=i[h+28>>2];a:{if(b+ -1>>>0>=6){b:{if((b|0)!=9){break b}k=i[d+48>>2];b=ho(32);i[e>>2]=b;i[e+4>>2]=17;i[e+8>>2]=-2147483616;g[b+17|0]=0;g[b+16|0]=j[2108];c=j[2104]|j[2105]<<8|(j[2106]<<16|j[2107]<<24);d=j[2100]|j[2101]<<8|(j[2102]<<16|j[2103]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[2096]|j[2097]<<8|(j[2098]<<16|j[2099]<<24);d=j[2092]|j[2093]<<8|(j[2094]<<16|j[2095]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;c:{d:{d=k+16|0;b=i[d>>2];if(!b){break d}c=d;while(1){l=i[b+16>>2]<(f|0);c=l?c:b;b=i[(l<<2)+b>>2];if(b){continue}break}if((c|0)==(d|0)|(f|0)>2]){break d}b=c+20|0;if(!Sb(b,e)){break d}b=$j(b,e,-1);break c}b=$j(k,e,-1)}if(g[e+11|0]<=-1){bp(i[e>>2])}if((b|0)<1){break b}if(i[h+56>>2]==1){b=ho(48);i[b>>2]=0;i[b+4>>2]=0;c=b+40|0;i[c>>2]=0;i[c+4>>2]=0;i[b+32>>2]=0;i[b+36>>2]=0;i[b+24>>2]=0;i[b+28>>2]=0;i[b+16>>2]=0;i[b+20>>2]=0;i[b+8>>2]=0;i[b+12>>2]=0;ed(b);i[b+44>>2]=-1;i[c>>2]=1032;i[b>>2]=6056;break a}b=ho(64);$e(b);break a}b=ho(36);Bc(b);break a}b=ho(40);ed(b)}i[a>>2]=b;F=e+16|0}function sa(a,b,c){var d=p(0),e=0,f=0,h=0,j=0,k=p(0),l=0,n=0,o=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,y=0;l=F-16|0;F=l;a:{if(!(i[a+4>>2]!=-1|c+ -1>>>0>29)){i[a+4>>2]=c;e=g[b+24|0];c=0;i[a+20>>2]=0;i[l+8>>2]=0;i[l>>2]=0;i[l+4>>2]=0;if(e){if((e|0)<=-1){break a}c=e<<2;f=ho(c);c=ip(f,0,c)+c|0}h=i[a+8>>2];if(h){i[a+12>>2]=h;bp(h)}i[a+8>>2]=f;i[a+16>>2]=c;i[a+12>>2]=c;c=(e&1073741823)!=(e|0)?-1:e<<2;n=ho(c);c=ho(c);w=i[b+44>>2];o=c;h=i[b+48>>2];c=h;q=i[b>>2];t=i[b+40>>2];r=t;u=hp(o,c+i[q>>2]|0,r);s=hp(f,c+i[q>>2]|0,r);n=hp(n,c+i[q>>2]|0,r);b=i[b+80>>2];if(b>>>0>=2){f=b;o=1;c=0;while(1){b=i[q>>2];j=up(o,c,t,w)+h|0;y=hp(u,b+j|0,r);b=0;if((e|0)>0){while(1){j=b<<2;v=j+s|0;d=m[j+y>>2];if(!(m[v>>2]>d^1)){m[v>>2]=d}j=j+n|0;if(!(m[j>>2]>2]=d}b=b+1|0;if((e|0)!=(b|0)){continue}break}}b=o+1|0;if(b>>>0<1){c=c+1|0}o=b;if((f|0)!=(b|0)|c){continue}break}}c=1;b:{c:{d:{if((e|0)>=1){d=m[s>>2];if(d!=d){break b}b=0;break d}d=m[a+20>>2];c=0;break c}while(1){if(((Fp(d),Bp(2))&2147483647)==2139095040){break b}h=(b<<2)+n|0;f=i[h>>2];k=m[h>>2];if((f&2147483647)==2139095040|k!=k){break b}k=p(k-d);d=m[a+20>>2];if(!(k>d^1)){m[a+20>>2]=k;d=k}b=b+1|0;c=(b|0)<(e|0);if((b|0)==(e|0)){break c}d=m[(b<<2)+s>>2];if(d==d){continue}break}break b}if(d!=p(0)){break b}i[a+20>>2]=1065353216}bp(u);bp(n);f=c^1}F=l+16|0;return f&1}Ho();x()}function Za(a,b,c,d){var e=0,f=0,h=0,j=0,k=p(0),l=0,n=0,o=0,q=0,r=p(0),s=0,t=0;n=i[c+8>>2];o=i[c+4>>2];q=i[c>>2];j=n^(o^(q^318)+239)+239;s=a;e=i[b+4>>2];a:{b:{if(!e){break b}f=i[b>>2];l=xp(e);h=j&e+ -1;c:{if(l>>>0<=1){break c}c=j;h=c;if(c>>>0>>0){break c}h=(j>>>0)%(e>>>0)|0}c=h;h=c;c=i[(c<<2)+f>>2];if(!c){break b}c=i[c>>2];if(!c){break b}t=e+ -1|0;l=l>>>0>1;while(1){f=i[c+4>>2];if((j|0)!=(f|0)){d:{if(!l){f=f&t;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(h|0)){break b}}if(!(i[c+8>>2]!=(q|0)|i[c+12>>2]!=(o|0)|i[c+16>>2]!=(n|0))){b=0;break a}c=i[c>>2];if(c){continue}break}}c=ho(24);i[c+16>>2]=i[d+8>>2];f=i[d+4>>2];i[c+8>>2]=i[d>>2];i[c+12>>2]=f;d=i[d+12>>2];i[c>>2]=0;i[c+4>>2]=j;i[c+20>>2]=d;k=m[b+16>>2];r=p(i[b+12>>2]+1>>>0);e:{if(p(k*p(e>>>0))>>0<3|e<<1;d=b;k=p(v(p(r/k)));f:{if(k=p(0)){h=~~k>>>0;break f}h=0}db(d,e>>>0>>0?h:e);e=i[b+4>>2];if(!(e&e+ -1)){h=j&e+ -1;break e}if(j>>>0>>0){h=j;break e}h=(j>>>0)%(e>>>0)|0}d=i[b>>2]+(h<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[s+4|0]=b;i[a>>2]=c}function Gg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;jh(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=4194304;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=4194304;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<20)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}fh(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function Fg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;ih(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=4194304;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=4194304;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<20)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}fh(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function Eg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;hh(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=4194304;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=4194304;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<20)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}fh(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function Dg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;gh(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=4194304;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=4194304;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<20)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}fh(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function Cg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;eh(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=4194304;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=4194304;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<20)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}fh(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function Bg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;ch(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=2097152;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=2097152;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<19)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}dh(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function Ag(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;ah(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=1048576;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=1048576;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<18)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}bh(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function zg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;_g(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=262144;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=262144;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<16)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}$g(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function yg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;Yg(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=131072;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=131072;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<15)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}Zg(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function xg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;Wg(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=32768;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=32768;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<13)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}Xg(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function wg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;Vg(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=16384;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=16384;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<12)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}og(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function vg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;Ug(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=16384;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=16384;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<12)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}og(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function ug(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;Tg(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=16384;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=16384;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<12)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}og(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function tg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;ng(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=16384;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=16384;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<12)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}og(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function sg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;Sg(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=16384;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=16384;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<12)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}og(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function rg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;Rg(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=16384;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=16384;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<12)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}og(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function qg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;Qg(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=16384;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=16384;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<12)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}og(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function pg(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0;e=F+ -64|0;F=e;i[e+56>>2]=0;i[e+48>>2]=0;i[e+52>>2]=0;a:{f=c+1|0;if(f>>>0>=c>>>0){if(f>>>0>=536870912){break a}f=f<<3;l=ho(f);i[e+48>>2]=l;k=f+l|0;i[e+56>>2]=k;ip(l,0,(c<<3)+8|0);i[e+52>>2]=k}if((b|0)>0){while(1){c=(i[(j<<2)+a>>2]<<3)+l|0;f=c;n=c;h=i[c+4>>2];c=i[c>>2]+1|0;if(c>>>0<1){h=h+1|0}i[n>>2]=c;i[f+4>>2]=h;j=j+1|0;if((j|0)!=(b|0)){continue}break}}i[e+8>>2]=0;i[e+12>>2]=0;f=e+16|0;c=f;i[c>>2]=0;i[c+4>>2]=0;m=e+24|0;c=m;i[c>>2]=0;i[c+4>>2]=0;i[e+32>>2]=0;i[e>>2]=0;i[e+4>>2]=0;i[e+40>>2]=0;i[e+44>>2]=0;Pg(e,l,k-l>>3,d);h=i[f>>2];c=i[f+4>>2];f=i[d+4>>2]-i[d>>2]|0;i[e+40>>2]=f;i[e+44>>2]=0;n=d;k=h;h=c<<1|h>>>31;c=(k<<1)+39|0;if(c>>>0<39){h=h+1|0}k=c;c=h>>>3|0;h=f+((h&7)<<29|k>>>3)|0;if(h>>>0>>0){c=c+1|0}f=h+8|0;f>>>0<8;Uj(n,f);c=i[d>>2];i[e+28>>2]=0;i[e+32>>2]=16384;i[m>>2]=c+i[e+40>>2];if((b|0)>=1){j=16384;while(1){c=b+ -1|0;k=i[e>>2]+(i[(c<<2)+a>>2]<<3)|0;m=i[k>>2];h=m<<10;if(j>>>0>=h>>>0){while(1){f=i[e+28>>2];i[e+28>>2]=f+1;g[f+i[e+24>>2]|0]=j;j=i[e+32>>2]>>>8|0;i[e+32>>2]=j;if(j>>>0>=h>>>0){continue}break}}f=(j>>>0)/(m>>>0)|0;j=i[k+4>>2]+((f<<12)+(j-o(f,m)|0)|0)|0;i[e+32>>2]=j;f=(b|0)>1;b=c;if(f){continue}break}}og(e,d);a=i[e>>2];if(a){i[e+4>>2]=a;bp(a)}a=i[e+48>>2];if(a){i[e+52>>2]=a;bp(a)}F=e- -64|0;return 1}Ho();x()}function kb(a,b,c,d){var e=0,f=0,h=0,k=0,l=p(0),n=0,o=0,q=0,r=0,s=0,t=p(0),u=0,w=0;o=j[c+3|0];q=j[c+2|0];r=j[c+1|0];s=j[c|0];k=o^(q^(r^(s^318)+239)+239)+239;u=a;e=i[b+4>>2];a:{b:{if(!e){break b}f=i[b>>2];n=xp(e);h=k&e+ -1;c:{if(n>>>0<=1){break c}c=k;h=c;if(c>>>0>>0){break c}h=(k>>>0)%(e>>>0)|0}c=h;h=c;c=i[(c<<2)+f>>2];if(!c){break b}c=i[c>>2];if(!c){break b}w=e+ -1|0;n=n>>>0>1;while(1){f=i[c+4>>2];if((k|0)!=(f|0)){d:{if(!n){f=f&w;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(h|0)){break b}}if(!(j[c+8|0]!=(s|0)|j[c+9|0]!=(r|0)|(j[c+10|0]!=(q|0)|j[c+11|0]!=(o|0)))){b=0;break a}c=i[c>>2];if(c){continue}break}}c=ho(16);i[c+8>>2]=i[d>>2];d=i[d+4>>2];i[c+4>>2]=k;i[c+12>>2]=d;i[c>>2]=0;l=m[b+16>>2];t=p(i[b+12>>2]+1>>>0);e:{if(p(l*p(e>>>0))>>0<3|e<<1;d=b;l=p(v(p(t/l)));f:{if(l=p(0)){h=~~l>>>0;break f}h=0}rb(d,e>>>0>>0?h:e);e=i[b+4>>2];if(!(e&e+ -1)){h=k&e+ -1;break e}if(k>>>0>>0){h=k;break e}h=(k>>>0)%(e>>>0)|0}d=i[b>>2]+(h<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[u+4|0]=b;i[a>>2]=c}function vb(a,b,c,d){var e=0,f=0,j=0,l=0,n=p(0),o=0,q=0,r=0,s=0,t=p(0),u=0,w=0;r=k[c+4>>1];s=k[c+2>>1];o=k[c>>1];l=r^(s^(o^318)+239)+239;u=a;e=i[b+4>>2];a:{b:{if(!e){break b}f=i[b>>2];q=xp(e);j=l&e+262143;c:{if(q>>>0<=1){break c}c=l;j=c;if(c>>>0>>0){break c}j=(l>>>0)%(e>>>0)|0}c=j;j=c;c=i[(c<<2)+f>>2];if(!c){break b}c=i[c>>2];if(!c){break b}w=e+ -1|0;q=q>>>0>1;while(1){f=i[c+4>>2];if((l|0)!=(f|0)){d:{if(!q){f=f&w;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(j|0)){break b}}if(!(k[c+8>>1]!=(o|0)|k[c+10>>1]!=(s|0)|k[c+12>>1]!=(r|0))){b=0;break a}c=i[c>>2];if(c){continue}break}}c=ho(20);h[c+12>>1]=k[d+4>>1];i[c+8>>2]=i[d>>2];d=i[d+8>>2];i[c>>2]=0;i[c+4>>2]=l;i[c+16>>2]=d;n=m[b+16>>2];t=p(i[b+12>>2]+1>>>0);e:{if(p(n*p(e>>>0))>>0<3|e<<1;d=b;n=p(v(p(t/n)));f:{if(n=p(0)){j=~~n>>>0;break f}j=0}Bb(d,o>>>0>>0?j:o);e=i[b+4>>2];if(!(e&e+ -1)){j=l&e+ -1;break e}if(l>>>0>>0){j=l;break e}j=(l>>>0)%(e>>>0)|0}d=i[b>>2]+(j<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[u+4|0]=b;i[a>>2]=c}function jb(a,b,c,d){var e=0,f=0,l=0,n=0,o=p(0),q=0,r=0,s=0,t=0,u=p(0),w=0,x=0;r=j[c+2|0];s=j[c+1|0];t=j[c|0];n=r^(s^(t^318)+239)+239;w=a;e=i[b+4>>2];a:{b:{if(!e){break b}f=i[b>>2];q=xp(e);l=n&e+ -1;c:{if(q>>>0<=1){break c}c=n;l=c;if(c>>>0>>0){break c}l=(n>>>0)%(e>>>0)|0}c=l;l=c;c=i[(c<<2)+f>>2];if(!c){break b}c=i[c>>2];if(!c){break b}x=e+ -1|0;q=q>>>0>1;while(1){f=i[c+4>>2];if((n|0)!=(f|0)){d:{if(!q){f=f&x;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(l|0)){break b}}if(!(j[c+8|0]!=(t|0)|j[c+9|0]!=(s|0)|j[c+10|0]!=(r|0))){b=0;break a}c=i[c>>2];if(c){continue}break}}c=ho(16);g[c+10|0]=j[d+2|0];h[c+8>>1]=k[d>>1];d=i[d+4>>2];i[c>>2]=0;i[c+4>>2]=n;i[c+12>>2]=d;o=m[b+16>>2];u=p(i[b+12>>2]+1>>>0);e:{if(p(o*p(e>>>0))>>0<3|e<<1;d=b;o=p(v(p(u/o)));f:{if(o=p(0)){l=~~o>>>0;break f}l=0}pb(d,e>>>0>>0?l:e);e=i[b+4>>2];if(!(e&e+ -1)){l=n&e+ -1;break e}if(n>>>0>>0){l=n;break e}l=(n>>>0)%(e>>>0)|0}d=i[b>>2]+(l<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[w+4|0]=b;i[a>>2]=c}function yf(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0;e=F-32|0;F=e;f=a+16|0;d=i[f>>2];a:{b:{if(!d){break b}h=i[b>>2];c=f;while(1){g=i[d+16>>2]<(h|0);c=g?c:d;d=i[(g<<2)+d>>2];if(d){continue}break}if((c|0)==(f|0)){break b}if((h|0)>=i[c+16>>2]){break a}}k=Xj(e+16|0);b=i[b>>2];j=e+8|0;i[j>>2]=0;i[j+4>>2]=0;i[e>>2]=b;i[e+4>>2]=j;c=i[k>>2];h=k+4|0;if((c|0)!=(h|0)){g=e|4;while(1){b=c;d=c+16|0;Mf(g,j,d,d);d=i[c+4>>2];c:{if(!d){c=i[b+8>>2];if((b|0)==i[c>>2]){break c}d=b+8|0;while(1){b=i[d>>2];d=b+8|0;c=i[b+8>>2];if((b|0)!=i[c>>2]){continue}break}break c}while(1){c=d;d=i[c>>2];if(d){continue}break}}if((c|0)!=(h|0)){continue}break}}d=i[f>>2];d:{if(d){f=a+16|0;g=i[e>>2];while(1){b=i[d+16>>2];e:{if((g|0)<(b|0)){c=i[d>>2];if(c){break e}f=d;break d}if((b|0)>=(g|0)){break d}f=d+4|0;c=i[d+4>>2];if(!c){break d}d=f}f=d;d=c;continue}}d=f}c=i[f>>2];if(!c){c=ho(32);i[c+16>>2]=i[e>>2];i[c+20>>2]=i[e+4>>2];g=i[e+8>>2];i[c+24>>2]=g;b=i[e+12>>2];i[c+28>>2]=b;h=c+24|0;f:{if(!b){i[c+20>>2]=h;break f}i[g+8>>2]=h;i[e+8>>2]=0;i[e+12>>2]=0;i[e+4>>2]=j}i[c+8>>2]=d;i[c>>2]=0;i[c+4>>2]=0;i[f>>2]=c;d=i[i[a+12>>2]>>2];b=c;g:{if(!d){break g}i[a+12>>2]=d;b=i[f>>2]}Kf(i[a+16>>2],b);a=a+20|0;i[a>>2]=i[a>>2]+1}kd(e|4,i[e+8>>2]);kd(k,i[k+4>>2])}F=e+32|0;return c+20|0}function mj(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0;f=F-16|0;F=f;I[i[i[b>>2]+64>>2]](a,b);if(!i[a>>2]){if(g[a+15|0]<=-1){bp(i[a+4>>2])}h=i[b+48>>2];c=ho(32);i[f>>2]=c;i[f+4>>2]=29;i[f+8>>2]=-2147483616;g[c+29|0]=0;d=j[13157]|j[13158]<<8|(j[13159]<<16|j[13160]<<24);e=j[13153]|j[13154]<<8|(j[13155]<<16|j[13156]<<24);g[c+21|0]=e;g[c+22|0]=e>>>8;g[c+23|0]=e>>>16;g[c+24|0]=e>>>24;g[c+25|0]=d;g[c+26|0]=d>>>8;g[c+27|0]=d>>>16;g[c+28|0]=d>>>24;d=j[13152]|j[13153]<<8|(j[13154]<<16|j[13155]<<24);e=j[13148]|j[13149]<<8|(j[13150]<<16|j[13151]<<24);g[c+16|0]=e;g[c+17|0]=e>>>8;g[c+18|0]=e>>>16;g[c+19|0]=e>>>24;g[c+20|0]=d;g[c+21|0]=d>>>8;g[c+22|0]=d>>>16;g[c+23|0]=d>>>24;d=j[13144]|j[13145]<<8|(j[13146]<<16|j[13147]<<24);e=j[13140]|j[13141]<<8|(j[13142]<<16|j[13143]<<24);g[c+8|0]=e;g[c+9|0]=e>>>8;g[c+10|0]=e>>>16;g[c+11|0]=e>>>24;g[c+12|0]=d;g[c+13|0]=d>>>8;g[c+14|0]=d>>>16;g[c+15|0]=d>>>24;d=j[13136]|j[13137]<<8|(j[13138]<<16|j[13139]<<24);e=j[13132]|j[13133]<<8|(j[13134]<<16|j[13135]<<24);g[c|0]=e;g[c+1|0]=e>>>8;g[c+2|0]=e>>>16;g[c+3|0]=e>>>24;g[c+4|0]=d;g[c+5|0]=d>>>8;g[c+6|0]=d>>>16;g[c+7|0]=d>>>24;c=ck(h,f,0);if(g[f+11|0]<=-1){bp(i[f>>2])}if(c){I[i[i[b>>2]+68>>2]](b)}i[a>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0}F=f+16|0}function Ia(a,b){var c=0;c=-1;a:{b:{c:{d:{switch(i[b+28>>2]+ -1|0){case 8:e:{switch(g[b+24|0]+ -1|0){case 0:c=Ja(a,b);break c;case 1:c=Ka(a,b);break c;case 2:c=La(a,b);break c;case 3:break e;default:break b}}c=Ma(a,b);break c;case 0:f:{switch(g[b+24|0]+ -1|0){case 0:c=Na(a,b);break c;case 1:c=Oa(a,b);break c;case 2:c=Pa(a,b);break c;case 3:break f;default:break b}}c=Qa(a,b);break c;case 1:case 10:g:{switch(g[b+24|0]+ -1|0){case 0:c=Na(a,b);break c;case 1:c=Oa(a,b);break c;case 2:c=Pa(a,b);break c;case 3:break g;default:break b}}c=Qa(a,b);break c;case 3:h:{switch(g[b+24|0]+ -1|0){case 0:c=Ra(a,b);break c;case 1:c=Sa(a,b);break c;case 2:c=Ta(a,b);break c;case 3:break h;default:break b}}c=Ua(a,b);break c;case 2:i:{switch(g[b+24|0]+ -1|0){case 0:c=Ra(a,b);break c;case 1:c=Sa(a,b);break c;case 2:c=Ta(a,b);break c;case 3:break i;default:break b}}c=Ua(a,b);break c;case 5:j:{switch(g[b+24|0]+ -1|0){case 0:c=Va(a,b);break c;case 1:c=Ka(a,b);break c;case 2:c=La(a,b);break c;case 3:break j;default:break b}}c=Ma(a,b);break c;case 4:break d;default:break a}}k:{switch(g[b+24|0]+ -1|0){case 0:c=Va(a,b);break c;case 1:c=Ka(a,b);break c;case 2:c=La(a,b);break c;case 3:break k;default:break b}}c=Ma(a,b)}if(c){break a}}c=-1}return c}function Ya(a,b,c,d){var e=0,f=0,h=0,j=0,k=p(0),l=0,n=0,o=0,q=p(0),r=0,s=0;n=i[c+4>>2];o=i[c>>2];j=n^(o^318)+239;r=a;a:{b:{e=i[b+4>>2];if(!e){break b}f=i[b>>2];l=xp(e);h=j&e+ -1;c:{if(l>>>0<=1){break c}c=j;h=c;if(c>>>0>>0){break c}h=(j>>>0)%(e>>>0)|0}c=h;h=c;c=i[(c<<2)+f>>2];if(!c){break b}s=e+ -1|0;l=l>>>0>1;while(1){c=i[c>>2];if(!c){break b}f=i[c+4>>2];if((j|0)!=(f|0)){d:{if(!l){f=f&s;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(h|0)){break b}}if(i[c+8>>2]!=(o|0)|i[c+12>>2]!=(n|0)){continue}break}b=0;break a}c=ho(20);f=i[d+4>>2];i[c+8>>2]=i[d>>2];i[c+12>>2]=f;d=i[d+8>>2];i[c+4>>2]=j;i[c+16>>2]=d;i[c>>2]=0;k=m[b+16>>2];q=p(i[b+12>>2]+1>>>0);e:{if(p(k*p(e>>>0))>>0<3|e<<1;d=b;k=p(v(p(q/k)));f:{if(k=p(0)){h=~~k>>>0;break f}h=0}bb(d,e>>>0>>0?h:e);e=i[b+4>>2];if(!(e&e+ -1)){h=j&e+ -1;break e}if(j>>>0>>0){h=j;break e}h=(j>>>0)%(e>>>0)|0}d=i[b>>2]+(h<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[r+4|0]=b;i[a>>2]=c}function ci(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0;a:{b:{d=i[a+12>>2];e=i[d+4>>2];c=i[d>>2];b=e-c|0;if(!b){break b}e=c-e|0;c=((e|0)>(b|0)?e:b)>>>2|0;b=(b|0)>-1?b:-1;b=o(c,(b|0)<1?b:1);k=b>>>0>1?b:1;l=a+140|0;e=0;while(1){c:{if(Ql(d,(e>>>0)/3|0)){break c}d=i[a+12>>2];if(i[i[d+12>>2]+(e<<2)>>2]!=-1){break c}h=i[a+152>>2];g=h;c=e+1|0;c=(c>>>0)%3|0?c:e+ -2|0;if((c|0)!=-1){d=i[i[d>>2]+(c<<2)>>2]}else{d=-1}b=d<<2;if(i[g+b>>2]!=-1){break c}f=-1;g=i[a+144>>2];d=g;c=i[a+148>>2];if((d|0)==c<<5){if((g+1|0)<=-1){break a}d=l;if(g>>>0<=1073741822){f=g+32&-32;c=c<<6;c=c>>>0>>0?f:c}else{c=2147483647}bd(d,c);h=i[a+152>>2];f=i[b+h>>2];d=i[a+144>>2]}i[a+144>>2]=d+1;c=i[a+140>>2]+(d>>>3&536870908)|0;j=i[c>>2];n=c,p=yp(-2,d)&j,i[n>>2]=p;if((f|0)!=-1){break c}b=b+h|0;j=i[a+12>>2];d=e;while(1){c=d;i[b>>2]=g;d=-1;f=-1;d:{if((c|0)==-1){break d}b=c+1|0;b=(b>>>0)%3|0?b:c+ -2|0;if((b|0)==-1){break d}m=i[j+12>>2];while(1){c=i[(b<<2)+m>>2];if((c|0)!=-1){b=c+1|0;b=(b>>>0)%3|0?b:c+ -2|0;if((b|0)!=-1){continue}break d}break}d=b+1|0;d=(d>>>0)%3|0?d:b+ -2|0;if((d|0)!=-1){f=i[i[j>>2]+(d<<2)>>2]}d=b}b=(f<<2)+h|0;if(i[b>>2]==-1){continue}break}}e=e+1|0;if((k|0)==(e|0)){break b}d=i[a+12>>2];continue}}return 1}Ho();x()}function ub(a,b,c,d){var e=0,f=0,h=0,j=0,l=p(0),n=0,o=0,q=0,r=p(0),s=0,t=0;q=k[c+2>>1];n=k[c>>1];j=q^(n^318)+239;s=a;a:{b:{e=i[b+4>>2];if(!e){break b}f=i[b>>2];o=xp(e);h=j&e+131071;c:{if(o>>>0<=1){break c}c=j;h=c;if(c>>>0>>0){break c}h=(j>>>0)%(e>>>0)|0}c=h;h=c;c=i[(c<<2)+f>>2];if(!c){break b}t=e+ -1|0;o=o>>>0>1;while(1){c=i[c>>2];if(!c){break b}f=i[c+4>>2];if((j|0)!=(f|0)){d:{if(!o){f=f&t;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(h|0)){break b}}if(k[c+8>>1]!=(n|0)|k[c+10>>1]!=(q|0)){continue}break}b=0;break a}c=ho(16);i[c+8>>2]=i[d>>2];d=i[d+4>>2];i[c+4>>2]=j;i[c+12>>2]=d;i[c>>2]=0;l=m[b+16>>2];r=p(i[b+12>>2]+1>>>0);e:{if(p(l*p(e>>>0))>>0<3|e<<1;d=b;l=p(v(p(r/l)));f:{if(l=p(0)){h=~~l>>>0;break f}h=0}zb(d,n>>>0>>0?h:n);e=i[b+4>>2];if(!(e&e+ -1)){h=j&e+ -1;break e}if(j>>>0>>0){h=j;break e}h=(j>>>0)%(e>>>0)|0}d=i[b>>2]+(h<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[s+4|0]=b;i[a>>2]=c}function ib(a,b,c,d){var e=0,f=0,l=0,n=0,o=p(0),q=0,r=0,s=0,t=p(0),u=0,w=0;r=j[c+1|0];s=j[c|0];n=r^(s^318)+239;u=a;a:{b:{e=i[b+4>>2];if(!e){break b}f=i[b>>2];q=xp(e);l=n&e+1023;c:{if(q>>>0<=1){break c}c=n;l=c;if(c>>>0>>0){break c}l=(n>>>0)%(e>>>0)|0}c=l;l=c;c=i[(c<<2)+f>>2];if(!c){break b}w=e+ -1|0;q=q>>>0>1;while(1){c=i[c>>2];if(!c){break b}f=i[c+4>>2];if((n|0)!=(f|0)){d:{if(!q){f=f&w;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(l|0)){break b}}if(j[c+8|0]!=(s|0)|j[c+9|0]!=(r|0)){continue}break}b=0;break a}c=ho(16);h[c+8>>1]=k[d>>1];d=i[d+4>>2];i[c+4>>2]=n;i[c+12>>2]=d;i[c>>2]=0;o=m[b+16>>2];t=p(i[b+12>>2]+1>>>0);e:{if(p(o*p(e>>>0))>>0<3|e<<1;d=b;o=p(v(p(t/o)));f:{if(o=p(0)){l=~~o>>>0;break f}l=0}nb(d,e>>>0>>0?l:e);e=i[b+4>>2];if(!(e&e+ -1)){l=n&e+ -1;break e}if(n>>>0>>0){l=n;break e}l=(n>>>0)%(e>>>0)|0}d=i[b>>2]+(l<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[u+4|0]=b;i[a>>2]=c}function Jm(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;while(1){i[i[a>>2]+(e<<2)>>2]=0;e=e+1|0;if((d|0)!=(e|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}s=b+ -1|0;t=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!t){e=e&s;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}f:{g:{l=e<<2;c=l+i[a>>2]|0;if(i[c>>2]){k=i[d>>2];if(k){break g}k=0;c=d;break f}i[c>>2]=f;f=d;g=e;break e}c=i[a+24>>2];m=i[c+8>>2];n=i[c+12>>2]-m|0;c=n>>>2|0;u=c>>>0>1?c:1;o=i[d+8>>2];c=d;while(1){e=0;if((n|0)>0){p=i[k+8>>2];while(1){h=o;q=p;r=i[(e<<2)+m>>2];if(!j[r+84|0]){h=i[r+68>>2];q=i[h+(p<<2)>>2];h=i[h+(o<<2)>>2]}if((h|0)!=(q|0)){break f}e=e+1|0;if((u|0)!=(e|0)){continue}break}}c=k;h=i[c>>2];k=h;if(h){continue}break}k=0}i[f>>2]=k;i[c>>2]=i[i[i[a>>2]+l>>2]>>2];i[i[i[a>>2]+l>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(16928);x()}function oi(a,b,c,d){var e=0,f=0,h=0,k=0,l=0,m=0,n=0;l=F-16|0;F=l;a:{b:{f=i[a+124>>2];c:{if(!f){break c}e=i[a+120>>2];m=xp(f);h=f+ -1&d;d:{if(m>>>0<=1){break d}h=d;if(f>>>0>d>>>0){break d}h=(d>>>0)%(f>>>0)|0}e=i[e+(h<<2)>>2];if(!e){break c}k=i[e>>2];if(!k){break c}n=f+ -1|0;m=m>>>0>1;while(1){e:{e=i[k+4>>2];f:{if((e|0)!=(d|0)){g:{if(!m){e=e&n;break g}if(e>>>0>>0){break g}e=(e>>>0)%(f>>>0)|0}if((e|0)==(h|0)){break f}break c}if(i[k+8>>2]==(d|0)){break e}}k=i[k>>2];if(k){continue}break c}break}k=i[k+12>>2];if((k|0)==-1){break c}m=c&1;c=i[a+112>>2];if((c|0)!=i[a+116>>2]){g[c+8|0]=m;i[c+4>>2]=b;i[c>>2]=k;b=j[l+13|0]|j[l+14|0]<<8;g[c+9|0]=b;g[c+10|0]=b>>>8;g[c+11|0]=j[l+15|0];i[a+112>>2]=c+12;break c}d=i[a+108>>2];h=c-d|0;c=(h|0)/12|0;e=c+1|0;if(e>>>0>=357913942){break b}n=c<<1;e=c>>>0<178956970?n>>>0>>0?e:n:357913941;f=0;h:{if(!e){break h}if(e>>>0>=357913942){break a}f=ho(o(e,12))}c=f+o(c,12)|0;g[c+8|0]=m;i[c+4>>2]=b;i[c>>2]=k;b=j[l+13|0]|j[l+14|0]<<8;g[c+9|0]=b;g[c+10|0]=b>>>8;g[c+11|0]=j[l+15|0];b=c+o((h|0)/-12|0,12)|0;f=f+o(e,12)|0;c=c+12|0;if((h|0)>=1){hp(b,d,h)}i[a+116>>2]=f;i[a+112>>2]=c;i[a+108>>2]=b;if(!d){break c}bp(d)}F=l+16|0;return}Ho();x()}za(11708);x()}function mn(a){var b=0,c=0,d=0,e=0,f=0;a:{b:{c:{b=i[a+4>>2];d:{if(b>>>0>2]){i[a+4>>2]=b+1;b=j[b|0];break d}b=fn(a)}switch(b+ -43|0){case 0:case 2:break b;default:break c}}c=b+ -48|0;break a}f=(b|0)==45;b=i[a+4>>2];e:{if(b>>>0>2]){i[a+4>>2]=b+1;b=j[b|0];break e}b=fn(a)}c=b+ -48|0;if(!(c>>>0<10|!i[a+104>>2])){i[a+4>>2]=i[a+4>>2]+ -1}}f:{if(c>>>0<10){c=0;while(1){d=o(c,10)+b|0;b=i[a+4>>2];g:{if(b>>>0>2]){i[a+4>>2]=b+1;b=j[b|0];break g}b=fn(a)}e=b+ -48|0;c=d+ -48|0;if((c|0)<214748364?e>>>0<=9:0){continue}break}d=c;c=c>>31;h:{if(e>>>0>=10){break h}while(1){c=up(d,c,10,0);d=c+b|0;b=H;b=d>>>0>>0?b+1|0:b;d=d+ -48|0;c=b+ -1|0;c=d>>>0<4294967248?c+1|0:c;b=i[a+4>>2];i:{if(b>>>0>2]){i[a+4>>2]=b+1;b=j[b|0];break i}b=fn(a)}e=b+ -48|0;if(e>>>0>9){break h}if((c|0)<21474836?1:(c|0)<=21474836?d>>>0<2061584302:0){continue}break}}if(e>>>0<10){while(1){b=i[a+4>>2];j:{if(b>>>0>2]){i[a+4>>2]=b+1;b=j[b|0];break j}b=fn(a)}if(b+ -48>>>0<10){continue}break}}if(i[a+104>>2]){i[a+4>>2]=i[a+4>>2]+ -1}a=d;d=f?0-a|0:a;c=f?0-(c+(0>>0)|0)|0:c;break f}c=-2147483648;if(!i[a+104>>2]){break f}i[a+4>>2]=i[a+4>>2]+ -1;H=-2147483648;return 0}H=c;return d}function mi(a,b){var c=0,d=0,e=0,f=0,h=0,j=0;f=i[a+8>>2];c=i[a+4>>2];if((f-c|0)/136>>>0>=b>>>0){if(b){b=o(b,136)+c|0;while(1){i[c>>2]=-1;_l(c+4|0);i[c+104>>2]=0;i[c+108>>2]=0;g[c+100|0]=1;i[c+112>>2]=0;i[c+116>>2]=0;i[c+120>>2]=0;i[c+124>>2]=0;i[c+128>>2]=0;c=c+136|0;if((b|0)!=(c|0)){continue}break}c=b}i[a+4>>2]=c;return}a:{b:{c:{d=i[a>>2];c=(c-d|0)/136|0;e=c+b|0;if(e>>>0<31580642){h=o(c,136);d=(f-d|0)/136|0;c=d<<1;e=d>>>0<15790320?c>>>0>>0?e:c:31580641;if(e){if(e>>>0>=31580642){break c}j=ho(o(e,136))}d=h+j|0;f=d+o(b,136)|0;c=d;while(1){i[c>>2]=-1;_l(c+4|0);i[c+104>>2]=0;i[c+108>>2]=0;g[c+100|0]=1;i[c+112>>2]=0;i[c+116>>2]=0;i[c+120>>2]=0;i[c+124>>2]=0;i[c+128>>2]=0;c=c+136|0;if((f|0)!=(c|0)){continue}break}h=o(e,136)+j|0;b=i[a>>2];c=i[a+4>>2];if((b|0)==(c|0)){break b}while(1){c=c+ -136|0;d=ij(d+ -136|0,c);if((b|0)!=(c|0)){continue}break}i[a+8>>2]=h;c=i[a+4>>2];i[a+4>>2]=f;b=i[a>>2];i[a>>2]=d;if((b|0)==(c|0)){break a}while(1){a=i[c+ -20>>2];if(a){i[c+ -16>>2]=a;bp(a)}a=c+ -136|0;d=i[c+ -32>>2];if(d){i[c+ -28>>2]=d;bp(d)}ni(c+ -132|0);c=a;if((c|0)!=(b|0)){continue}break}break a}Ho();x()}za(11708);x()}i[a+8>>2]=h;i[a+4>>2]=f;i[a>>2]=d}if(b){bp(b)}}function tb(a,b,c,d){var e=0,f=0,j=0,l=0,n=p(0),o=0,q=0,r=p(0),s=0,t=0;o=k[c>>1];l=o^318;s=a;a:{b:{e=i[b+4>>2];if(!e){break b}f=i[b>>2];q=xp(e);j=l&e+ -1;c:{if(q>>>0<=1){break c}c=l;j=c;if(e>>>0>c>>>0){break c}j=(l>>>0)%(e>>>0)|0}c=j;j=c;c=i[(c<<2)+f>>2];if(!c){break b}t=e+ -1|0;q=q>>>0>1;while(1){c=i[c>>2];if(!c){break b}f=i[c+4>>2];if((l|0)!=(f|0)){d:{if(!q){f=f&t;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(j|0)){break b}}if(k[c+8>>1]!=(o|0)){continue}break}b=0;break a}c=ho(16);h[c+8>>1]=k[d>>1];d=i[d+4>>2];i[c+4>>2]=l;i[c+12>>2]=d;i[c>>2]=0;n=m[b+16>>2];r=p(i[b+12>>2]+1>>>0);e:{if(p(n*p(e>>>0))>>0<3|e<<1;d=b;n=p(v(p(r/n)));f:{if(n=p(0)){j=~~n>>>0;break f}j=0}xb(d,o>>>0>>0?j:o);e=i[b+4>>2];if(!(e&e+ -1)){j=l&e+ -1;break e}if(e>>>0>l>>>0){j=l;break e}j=(l>>>0)%(e>>>0)|0}d=i[b>>2]+(j<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[s+4|0]=b;i[a>>2]=c}function _b(a){var b=0,c=0,d=0,e=0,f=0;e=F-16|0;F=e;b=ho(16);i[e>>2]=b;i[e+4>>2]=14;i[e+8>>2]=-2147483632;g[b+14|0]=0;c=j[1830]|j[1831]<<8|(j[1832]<<16|j[1833]<<24);d=j[1826]|j[1827]<<8|(j[1828]<<16|j[1829]<<24);g[b+6|0]=d;g[b+7|0]=d>>>8;g[b+8|0]=d>>>16;g[b+9|0]=d>>>24;g[b+10|0]=c;g[b+11|0]=c>>>8;g[b+12|0]=c>>>16;g[b+13|0]=c>>>24;c=j[1824]|j[1825]<<8|(j[1826]<<16|j[1827]<<24);d=j[1820]|j[1821]<<8|(j[1822]<<16|j[1823]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;f=$j(a,e,-1);if(g[e+11|0]<=-1){bp(i[e>>2])}b=ho(16);i[e>>2]=b;i[e+4>>2]=14;i[e+8>>2]=-2147483632;g[b+14|0]=0;c=j[1845]|j[1846]<<8|(j[1847]<<16|j[1848]<<24);d=j[1841]|j[1842]<<8|(j[1843]<<16|j[1844]<<24);g[b+6|0]=d;g[b+7|0]=d>>>8;g[b+8|0]=d>>>16;g[b+9|0]=d>>>24;g[b+10|0]=c;g[b+11|0]=c>>>8;g[b+12|0]=c>>>16;g[b+13|0]=c>>>24;c=j[1839]|j[1840]<<8|(j[1841]<<16|j[1842]<<24);d=j[1835]|j[1836]<<8|(j[1837]<<16|j[1838]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;a=$j(a,e,-1);if(g[e+11|0]<=-1){bp(i[e>>2])}F=e+16|0;a=(f|0)<(a|0)?a:f;return(a|0)==-1?5:a}function Wa(a,b,c,d){var e=0,f=0,h=0,j=0,k=p(0),l=0,n=0,o=p(0),q=0,r=0;n=i[c>>2];j=n^318;q=a;a:{b:{e=i[b+4>>2];if(!e){break b}f=i[b>>2];l=xp(e);h=j&e+ -1;c:{if(l>>>0<=1){break c}c=j;h=c;if(c>>>0>>0){break c}h=(j>>>0)%(e>>>0)|0}c=h;h=c;c=i[(c<<2)+f>>2];if(!c){break b}r=e+ -1|0;l=l>>>0>1;while(1){c=i[c>>2];if(!c){break b}f=i[c+4>>2];if((j|0)!=(f|0)){d:{if(!l){f=f&r;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(h|0)){break b}}if(i[c+8>>2]!=(n|0)){continue}break}b=0;break a}c=ho(16);i[c+8>>2]=i[d>>2];d=i[d+4>>2];i[c+4>>2]=j;i[c+12>>2]=d;i[c>>2]=0;k=m[b+16>>2];o=p(i[b+12>>2]+1>>>0);e:{if(p(k*p(e>>>0))>>0<3|e<<1;d=b;k=p(v(p(o/k)));f:{if(k=p(0)){h=~~k>>>0;break f}h=0}$a(d,e>>>0>>0?h:e);e=i[b+4>>2];if(!(e&e+ -1)){h=j&e+ -1;break e}if(j>>>0>>0){h=j;break e}h=(j>>>0)%(e>>>0)|0}d=i[b>>2]+(h<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[q+4|0]=b;i[a>>2]=c}function hb(a,b,c,d){var e=0,f=0,h=0,k=0,l=p(0),n=0,o=0,q=p(0),r=0,s=0;o=j[c|0];k=o^318;r=a;a:{b:{e=i[b+4>>2];if(!e){break b}f=i[b>>2];n=xp(e);h=k&e+ -1;c:{if(n>>>0<=1){break c}c=k;h=c;if(c>>>0>>0){break c}h=(k>>>0)%(e>>>0)|0}c=h;h=c;c=i[(c<<2)+f>>2];if(!c){break b}s=e+ -1|0;n=n>>>0>1;while(1){c=i[c>>2];if(!c){break b}f=i[c+4>>2];if((k|0)!=(f|0)){d:{if(!n){f=f&s;break d}if(f>>>0>>0){break d}f=(f>>>0)%(e>>>0)|0}if((f|0)!=(h|0)){break b}}if(j[c+8|0]!=(o|0)){continue}break}b=0;break a}c=ho(16);g[c+8|0]=j[d|0];d=i[d+4>>2];i[c+4>>2]=k;i[c+12>>2]=d;i[c>>2]=0;l=m[b+16>>2];q=p(i[b+12>>2]+1>>>0);e:{if(p(l*p(e>>>0))>>0<3|e<<1;d=b;l=p(v(p(q/l)));f:{if(l=p(0)){h=~~l>>>0;break f}h=0}lb(d,e>>>0>>0?h:e);e=i[b+4>>2];if(!(e&e+ -1)){h=k&e+ -1;break e}if(k>>>0>>0){h=k;break e}h=(k>>>0)%(e>>>0)|0}d=i[b>>2]+(h<<2)|0;f=i[d>>2];g:{h:{if(!f){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[d>>2]=b+8;d=i[c>>2];if(!d){break g}f=i[d+4>>2];d=e+ -1|0;i:{if(!(d&e)){f=d&f;break i}if(f>>>0>>0){break i}f=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(f<<2)|0;break h}i[c>>2]=i[f>>2]}i[f>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[r+4|0]=b;i[a>>2]=c}function tp(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;a:{b:{c:{d:{e:{f:{g:{h:{i:{j:{g=b;if(g){e=c;if(!e){break j}f=d;if(!f){break i}f=r(f)-r(g)|0;if(f>>>0<=31){break h}break b}if((d|0)==1&c>>>0>=0|d>>>0>1){break b}b=(a>>>0)/(c>>>0)|0;H=0;return b}e=d;if(!a){break g}if(!e){break f}if(e+ -1&e){break f}a=g>>>zp(e)|0;H=0;return a}if(!(e+ -1&e)){break e}j=(r(e)+33|0)-r(g)|0;h=0-j|0;break c}j=f+1|0;h=63-f|0;break c}a=(g>>>0)/(e>>>0)|0;H=0;return a}f=r(e)-r(g)|0;if(f>>>0<31){break d}break b}if((e|0)==1){break a}c=a;a=zp(e);d=a&31;if(32<=(a&63)>>>0){f=0;a=b>>>d|0}else{f=b>>>d|0;a=((1<>>d}H=f;return a}j=f+1|0;h=63-f|0}e=b;g=a;f=j&63;i=f&31;if(32<=(f&63)>>>0){f=0;l=e>>>i|0}else{f=e>>>i|0;l=((1<>>i}a=h&63;h=a&31;if(32<=(a&63)>>>0){e=g<>>32-h|b<>>31;f=e<<1|b>>>31;m=k;e=k;k=f;i=g-((h>>>0>>0)+e|0)|0;e=i>>31;i=i>>31;f=c&i;l=k-f|0;f=m-((d&e)+(k>>>0>>0)|0)|0;e=b<<1|a>>>31;a=n|a<<1;b=e|o;m=0;k=i&1;n=k;j=j+ -1|0;if(j){continue}break}}H=m|(b<<1|a>>>31);return k|a<<1}a=0;b=0}H=b;return a}function Xf(a,b,c){var d=0,e=0,f=0,h=0;f=F-16|0;F=f;d=ho(16);i[f>>2]=d;i[f+4>>2]=14;i[f+8>>2]=-2147483632;g[d+14|0]=0;h=j[10714]|j[10715]<<8|(j[10716]<<16|j[10717]<<24);e=j[10710]|j[10711]<<8|(j[10712]<<16|j[10713]<<24);g[d+6|0]=e;g[d+7|0]=e>>>8;g[d+8|0]=e>>>16;g[d+9|0]=e>>>24;g[d+10|0]=h;g[d+11|0]=h>>>8;g[d+12|0]=h>>>16;g[d+13|0]=h>>>24;h=j[10708]|j[10709]<<8|(j[10710]<<16|j[10711]<<24);e=j[10704]|j[10705]<<8|(j[10706]<<16|j[10707]<<24);g[d|0]=e;g[d+1|0]=e>>>8;g[d+2|0]=e>>>16;g[d+3|0]=e>>>24;g[d+4|0]=h;g[d+5|0]=h>>>8;g[d+6|0]=h>>>16;g[d+7|0]=h>>>24;Yj(a,f,b);if(g[f+11|0]<=-1){bp(i[f>>2])}b=ho(16);i[f>>2]=b;i[f+4>>2]=14;i[f+8>>2]=-2147483632;g[b+14|0]=0;e=j[10729]|j[10730]<<8|(j[10731]<<16|j[10732]<<24);d=j[10725]|j[10726]<<8|(j[10727]<<16|j[10728]<<24);g[b+6|0]=d;g[b+7|0]=d>>>8;g[b+8|0]=d>>>16;g[b+9|0]=d>>>24;g[b+10|0]=e;g[b+11|0]=e>>>8;g[b+12|0]=e>>>16;g[b+13|0]=e>>>24;e=j[10723]|j[10724]<<8|(j[10725]<<16|j[10726]<<24);d=j[10719]|j[10720]<<8|(j[10721]<<16|j[10722]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=e;g[b+5|0]=e>>>8;g[b+6|0]=e>>>16;g[b+7|0]=e>>>24;Yj(a,f,c);if(g[f+11|0]<=-1){bp(i[f>>2])}F=f+16|0}function Cf(a,b,c){var d=0,e=0,f=0,h=0;f=F-16|0;F=f;d=ho(16);i[f>>2]=d;i[f+4>>2]=14;i[f+8>>2]=-2147483632;g[d+14|0]=0;h=j[10446]|j[10447]<<8|(j[10448]<<16|j[10449]<<24);e=j[10442]|j[10443]<<8|(j[10444]<<16|j[10445]<<24);g[d+6|0]=e;g[d+7|0]=e>>>8;g[d+8|0]=e>>>16;g[d+9|0]=e>>>24;g[d+10|0]=h;g[d+11|0]=h>>>8;g[d+12|0]=h>>>16;g[d+13|0]=h>>>24;h=j[10440]|j[10441]<<8|(j[10442]<<16|j[10443]<<24);e=j[10436]|j[10437]<<8|(j[10438]<<16|j[10439]<<24);g[d|0]=e;g[d+1|0]=e>>>8;g[d+2|0]=e>>>16;g[d+3|0]=e>>>24;g[d+4|0]=h;g[d+5|0]=h>>>8;g[d+6|0]=h>>>16;g[d+7|0]=h>>>24;Yj(a,f,b);if(g[f+11|0]<=-1){bp(i[f>>2])}b=ho(16);i[f>>2]=b;i[f+4>>2]=14;i[f+8>>2]=-2147483632;g[b+14|0]=0;e=j[10461]|j[10462]<<8|(j[10463]<<16|j[10464]<<24);d=j[10457]|j[10458]<<8|(j[10459]<<16|j[10460]<<24);g[b+6|0]=d;g[b+7|0]=d>>>8;g[b+8|0]=d>>>16;g[b+9|0]=d>>>24;g[b+10|0]=e;g[b+11|0]=e>>>8;g[b+12|0]=e>>>16;g[b+13|0]=e>>>24;e=j[10455]|j[10456]<<8|(j[10457]<<16|j[10458]<<24);d=j[10451]|j[10452]<<8|(j[10453]<<16|j[10454]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=e;g[b+5|0]=e>>>8;g[b+6|0]=e>>>16;g[b+7|0]=e>>>24;Yj(a,f,c);if(g[f+11|0]<=-1){bp(i[f>>2])}F=f+16|0}function pi(a,b,c,d){var e=0,f=0,h=0,j=0,k=p(0),l=0,n=p(0),o=0,q=0;f=i[c>>2];o=a;a:{b:{e=i[b+4>>2];if(!e){break b}j=i[b>>2];l=xp(e);h=f&e+ -1;c:{if(l>>>0<=1){break c}c=f;h=c;if(c>>>0>>0){break c}h=(f>>>0)%(e>>>0)|0}c=h;h=c;c=i[(c<<2)+j>>2];if(!c){break b}q=e+ -1|0;l=l>>>0>1;while(1){c=i[c>>2];if(!c){break b}j=i[c+4>>2];if((f|0)!=(j|0)){d:{if(!l){j=j&q;break d}if(j>>>0>>0){break d}j=(j>>>0)%(e>>>0)|0}if((j|0)!=(h|0)){break b}}if((f|0)!=i[c+8>>2]){continue}break}b=0;break a}c=ho(16);d=i[i[d>>2]>>2];i[c+12>>2]=0;i[c+8>>2]=d;i[c+4>>2]=f;i[c>>2]=0;k=m[b+16>>2];n=p(i[b+12>>2]+1>>>0);e:{if(p(k*p(e>>>0))>>0<3|e<<1;d=b;k=p(v(p(n/k)));f:{if(k=p(0)){h=~~k>>>0;break f}h=0}jj(d,e>>>0>>0?h:e);e=i[b+4>>2];if(!(e&e+ -1)){h=f&e+ -1;break e}if(f>>>0>>0){h=f;break e}h=(f>>>0)%(e>>>0)|0}f=i[b>>2]+(h<<2)|0;d=i[f>>2];g:{h:{if(!d){i[c>>2]=i[b+8>>2];i[b+8>>2]=c;i[f>>2]=b+8;d=i[c>>2];if(!d){break g}d=i[d+4>>2];f=e+ -1|0;i:{if(!(f&e)){d=d&f;break i}if(d>>>0>>0){break i}d=(d>>>0)%(e>>>0)|0}d=i[b>>2]+(d<<2)|0;break h}i[c>>2]=i[d>>2]}i[d>>2]=c}i[b+12>>2]=i[b+12>>2]+1;b=1}g[o+4|0]=b;i[a>>2]=c}function gd(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0,p=0;f=F-16|0;F=f;a:{c=i[a+4>>2];b:{c:{if(c){if(I[i[i[a>>2]+52>>2]](a,b,i[i[c+4>>2]+80>>2])|0){break c}break b}if(!(I[i[i[a>>2]+52>>2]](a,b,0)|0)){break b}}n=1;if(!j[a+28|0]){break b}e=i[a+32>>2];l=i[a+8>>2];c=i[l+80>>2];i[f+8>>2]=0;i[f>>2]=0;i[f+4>>2]=0;if(c){if(c>>>0>=1073741824){break a}c=c<<2;h=ho(c);i[f>>2]=h;d=c+h|0;i[f+8>>2]=d;ip(h,0,c);i[f+4>>2]=d}c=i[b+4>>2];k=i[b>>2];if((c|0)!=(k|0)){b=c-k|0;d=(b|0)>-1?b:-1;c=k-c|0;b=o((d|0)<1?d:1,((c|0)>(b|0)?c:b)>>>2|0);m=b>>>0>1?b:1;b=0;p=j[l+84|0];while(1){c=i[k+(b<<2)>>2];d=h;if(!p){c=i[i[l+68>>2]+(c<<2)>>2]}i[(c<<2)+d>>2]=b;b=b+1|0;if((m|0)!=(b|0)){continue}break}}d:{if(!j[e+84|0]){break d}b=i[i[i[a+4>>2]+4>>2]+80>>2];g[e+84|0]=0;d=i[e+68>>2];c=i[e+72>>2]-d>>2;if(b>>>0>c>>>0){Xa(e+68|0,b-c|0,2396);break d}if(b>>>0>=c>>>0){break d}i[e+72>>2]=d+(b<<2)}d=i[i[i[a+4>>2]+4>>2]+80>>2];e:{if(d){e=i[e+68>>2];b=0;k=j[l+84|0];while(1){a=b;m=e+(b<<2)|0;c=h;if(!k){a=i[i[l+68>>2]+(b<<2)>>2]}i[m>>2]=i[(a<<2)+c>>2];b=b+1|0;if((d|0)!=(b|0)){continue}break}break e}if(!h){break b}}bp(h)}F=f+16|0;return n|0}Ho();x()}function Ei(a){a=a|0;var b=0,c=0,d=0,e=0;i[a>>2]=11192;b=a+280|0;c=i[b>>2];i[b>>2]=0;if(c){d=c+ -4|0;b=i[d>>2];if(b){b=c+(b<<5)|0;while(1){b=nf(b+ -32|0);if((c|0)!=(b|0)){continue}break}}bp(d)}b=i[a+268>>2];if(b){i[a+272>>2]=b;bp(b)}c=a+244|0;b=i[c>>2];i[c>>2]=0;if(b){bp(b)}b=i[a+232>>2];if(b){i[a+236>>2]=b;bp(b)}nf(a+200|0);b=i[a+184>>2];if(b){i[a+188>>2]=b;bp(b)}c=i[a+172>>2];if(c){b=i[a+176>>2];d=c;a:{if((c|0)==(b|0)){break a}while(1){d=i[b+ -20>>2];if(d){i[b+ -16>>2]=d;bp(d)}d=b+ -136|0;e=i[b+ -32>>2];if(e){i[b+ -28>>2]=e;bp(e)}ni(b+ -132|0);b=d;if((c|0)!=(b|0)){continue}break}d=i[a+172>>2]}i[a+176>>2]=c;bp(d)}b=i[a+152>>2];if(b){i[a+156>>2]=b;bp(b)}b=i[a+140>>2];if(b){bp(b)}b=i[a+128>>2];if(b){while(1){c=i[b>>2];bp(b);b=c;if(b){continue}break}}b=i[a+120>>2];i[a+120>>2]=0;if(b){bp(b)}b=i[a+108>>2];if(b){i[a+112>>2]=b;bp(b)}b=i[a+96>>2];if(b){i[a+100>>2]=b;bp(b)}b=i[a+84>>2];if(b){bp(b)}b=i[a+72>>2];if(b){i[a+76>>2]=b;bp(b)}b=i[a+52>>2];if(b){i[a+56>>2]=b;bp(b)}b=i[a+40>>2];if(b){i[a+44>>2]=b;bp(b)}b=i[a+28>>2];if(b){bp(b)}b=i[a+16>>2];if(b){i[a+20>>2]=b;bp(b)}b=i[a+12>>2];i[a+12>>2]=0;if(b){bi(b)}return a|0}function Dn(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0;g=F-32|0;F=g;e=d&2147483647;j=e;e=e+ -1006698496|0;h=c;f=c;if(c>>>0<0){e=e+1|0}k=f;f=e;e=j+ -1140785152|0;l=h;if(h>>>0<0){e=e+1|0}a:{if((e|0)==(f|0)&k>>>0>>0|f>>>0>>0){e=d<<4|c>>>28;c=c<<4|b>>>28;b=b&268435455;h=b;if((b|0)==134217728&a>>>0>=1|b>>>0>134217728){e=e+1073741824|0;a=c+1|0;if(a>>>0<1){e=e+1|0}f=a;break a}f=c;e=e-((c>>>0<0)+ -1073741824|0)|0;if(a|h^134217728){break a}a=f+(f&1)|0;if(a>>>0>>0){e=e+1|0}f=a;break a}if(!(!h&(j|0)==2147418112?!(a|b):(j|0)==2147418112&h>>>0<0|j>>>0<2147418112)){e=d<<4|c>>>28;f=c<<4|b>>>28;e=e&524287|2146959360;break a}f=0;e=2146435072;if(j>>>0>1140785151){break a}e=0;h=j>>>16|0;if(h>>>0<15249){break a}e=d&65535|65536;sn(g+16|0,a,b,c,e,h+ -15233|0);vn(g,a,b,c,e,15361-h|0);c=i[g+4>>2];a=i[g+8>>2];e=i[g+12>>2]<<4|a>>>28;f=a<<4|c>>>28;a=c&268435455;c=a;b=i[g>>2]|((i[g+16>>2]|i[g+24>>2])!=0|(i[g+20>>2]|i[g+28>>2])!=0);if((a|0)==134217728&b>>>0>=1|a>>>0>134217728){a=f+1|0;if(a>>>0<1){e=e+1|0}f=a;break a}if(b|c^134217728){break a}a=f+(f&1)|0;if(a>>>0>>0){e=e+1|0}f=a}F=g+32|0;Cp(0,f|0);Cp(1,d&-2147483648|e);return+Dp()}function Ub(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,p=0;a:{b:{c:{d:{d=i[a+4>>2];j=i[a>>2];g=(d-j|0)/24|0;e=g+1|0;if(e>>>0<178956971){c=(i[a+8>>2]-j|0)/24|0;f=c<<1;h=c>>>0<89478485?f>>>0>>0?e:f:178956970;if(h){if(h>>>0>=178956971){break d}k=ho(o(h,24))}c=o(g,24)+k|0;i[c>>2]=1140;e=i[b+4>>2];l=c+16|0;i[l>>2]=0;i[c+8>>2]=0;i[c+12>>2]=0;i[c+4>>2]=e;n=i[b+8>>2];f=i[b+12>>2]-n|0;if(f){p=f>>2;if(p>>>0>=1073741824){break c}g=c+12|0;e=ho(f);i[g>>2]=e;i[c+8>>2]=e;i[l>>2]=e+(p<<2);if((f|0)>=1){e=hp(e,n,f)+f|0}i[g>>2]=e}e=o(h,24)+k|0;m[c+20>>2]=m[b+20>>2];g=c+24|0;if((d|0)==(j|0)){break b}while(1){c=c+ -24|0;i[c>>2]=1140;b=i[d+ -20>>2];f=c+16|0;i[f>>2]=0;i[c+8>>2]=0;i[c+12>>2]=0;i[c+4>>2]=b;b=d+ -16|0;i[c+8>>2]=i[b>>2];i[c+12>>2]=i[d+ -12>>2];h=f;f=d+ -8|0;i[h>>2]=i[f>>2];i[f>>2]=0;i[b>>2]=0;i[b+4>>2]=0;m[c+20>>2]=m[d+ -4>>2];d=d+ -24|0;if((j|0)!=(d|0)){continue}break}i[a+8>>2]=e;b=i[a+4>>2];i[a+4>>2]=g;d=i[a>>2];i[a>>2]=c;if((b|0)==(d|0)){break a}while(1){b=b+ -24|0;I[i[i[b>>2]>>2]](b)|0;if((b|0)!=(d|0)){continue}break}break a}Ho();x()}za(1752);x()}Ho();x()}i[a+8>>2]=e;i[a+4>>2]=g;i[a>>2]=c}if(d){bp(d)}}function wi(a,b){var c=0,d=0,e=0,f=0,g=0;i[a+128>>2]=2;i[a+132>>2]=7;i[a+64>>2]=b;b=I[i[i[b>>2]+32>>2]](b)|0;i[a+88>>2]=b;c=i[b+28>>2]-i[b+24>>2]>>2;d=i[a+108>>2];b=i[a+104>>2];e=d-b>>2;a:{if(c>>>0>e>>>0){Bd(a+104|0,c-e|0);b=i[a+104>>2];d=i[a+108>>2];break a}if(c>>>0>=e>>>0){break a}d=(c<<2)+b|0;i[a+108>>2]=d}if((b|0)!=(d|0)){b=0;while(1){d=Rl(i[a+88>>2],b);c=i[a+104>>2];i[c+(b<<2)>>2]=d;b=b+1|0;if(b>>>0>2]-c>>2>>>0){continue}break}}e=a+92|0;b=i[a+88>>2];d=i[b>>2];c=i[b+4>>2]-d>>2;g=i[a+92>>2];f=i[a+96>>2]-g>>2;b:{if(c>>>0>f>>>0){Ai(e,c-f|0);b=i[a+88>>2];d=i[b>>2];break b}if(c>>>0>=f>>>0){break b}i[a+96>>2]=(c<<2)+g}c=i[b+4>>2];if((c|0)!=(d|0)){b=c-d|0;f=(b|0)>-1?b:-1;c=d-c|0;b=o((f|0)<1?f:1,((c|0)>(b|0)?c:b)>>>2|0);c=b>>>0>1?b:1;e=i[e>>2];b=0;while(1){f=b<<2;i[f+e>>2]=i[d+f>>2];b=b+1|0;if((c|0)!=(b|0)){continue}break}}b=(i[a+132>>2]-i[a+128>>2]|0)+1|0;d=i[a+140>>2];e=i[a+136>>2];c=(d-e|0)/12|0;if(b>>>0>c>>>0){Bi(a+136|0,b-c|0);return}if(b>>>0>>0){c=e+o(b,12)|0;if((c|0)!=(d|0)){while(1){b=d+ -12|0;e=i[b>>2];if(e){i[d+ -8>>2]=e;bp(e)}d=b;if((b|0)!=(c|0)){continue}break}}i[a+140>>2]=c}}function hp(a,b,c){var d=0,e=0,f=0;if(c>>>0>=512){E(a|0,b|0,c|0)|0;return a}e=a+c|0;a:{if(!((a^b)&3)){b:{if((c|0)<1){c=a;break b}if(!(a&3)){c=a;break b}c=a;while(1){g[c|0]=j[b|0];b=b+1|0;c=c+1|0;if(c>>>0>=e>>>0){break b}if(c&3){continue}break}}d=e&-4;c:{if(d>>>0<64){break c}f=d+ -64|0;if(c>>>0>f>>>0){break c}while(1){i[c>>2]=i[b>>2];i[c+4>>2]=i[b+4>>2];i[c+8>>2]=i[b+8>>2];i[c+12>>2]=i[b+12>>2];i[c+16>>2]=i[b+16>>2];i[c+20>>2]=i[b+20>>2];i[c+24>>2]=i[b+24>>2];i[c+28>>2]=i[b+28>>2];i[c+32>>2]=i[b+32>>2];i[c+36>>2]=i[b+36>>2];i[c+40>>2]=i[b+40>>2];i[c+44>>2]=i[b+44>>2];i[c+48>>2]=i[b+48>>2];i[c+52>>2]=i[b+52>>2];i[c+56>>2]=i[b+56>>2];i[c+60>>2]=i[b+60>>2];b=b- -64|0;c=c- -64|0;if(c>>>0<=f>>>0){continue}break}}if(c>>>0>=d>>>0){break a}while(1){i[c>>2]=i[b>>2];b=b+4|0;c=c+4|0;if(c>>>0>>0){continue}break}break a}if(e>>>0<4){c=a;break a}d=e+ -4|0;if(d>>>0>>0){c=a;break a}c=a;while(1){g[c|0]=j[b|0];g[c+1|0]=j[b+1|0];g[c+2|0]=j[b+2|0];g[c+3|0]=j[b+3|0];b=b+4|0;c=c+4|0;if(c>>>0<=d>>>0){continue}break}}if(c>>>0>>0){while(1){g[c|0]=j[b|0];b=b+1|0;c=c+1|0;if((e|0)!=(c|0)){continue}break}}return a}function jg(a,b){var c=0,d=0,e=0,f=0;f=F-16|0;F=f;a:{if(b>>>0>10){break a}c=ho(48);i[f>>2]=c;i[f+4>>2]=33;i[f+8>>2]=-2147483600;g[c+33|0]=0;g[c+32|0]=j[10857];d=j[10853]|j[10854]<<8|(j[10855]<<16|j[10856]<<24);e=j[10849]|j[10850]<<8|(j[10851]<<16|j[10852]<<24);g[c+24|0]=e;g[c+25|0]=e>>>8;g[c+26|0]=e>>>16;g[c+27|0]=e>>>24;g[c+28|0]=d;g[c+29|0]=d>>>8;g[c+30|0]=d>>>16;g[c+31|0]=d>>>24;d=j[10845]|j[10846]<<8|(j[10847]<<16|j[10848]<<24);e=j[10841]|j[10842]<<8|(j[10843]<<16|j[10844]<<24);g[c+16|0]=e;g[c+17|0]=e>>>8;g[c+18|0]=e>>>16;g[c+19|0]=e>>>24;g[c+20|0]=d;g[c+21|0]=d>>>8;g[c+22|0]=d>>>16;g[c+23|0]=d>>>24;d=j[10837]|j[10838]<<8|(j[10839]<<16|j[10840]<<24);e=j[10833]|j[10834]<<8|(j[10835]<<16|j[10836]<<24);g[c+8|0]=e;g[c+9|0]=e>>>8;g[c+10|0]=e>>>16;g[c+11|0]=e>>>24;g[c+12|0]=d;g[c+13|0]=d>>>8;g[c+14|0]=d>>>16;g[c+15|0]=d>>>24;d=j[10829]|j[10830]<<8|(j[10831]<<16|j[10832]<<24);e=j[10825]|j[10826]<<8|(j[10827]<<16|j[10828]<<24);g[c|0]=e;g[c+1|0]=e>>>8;g[c+2|0]=e>>>16;g[c+3|0]=e>>>24;g[c+4|0]=d;g[c+5|0]=d>>>8;g[c+6|0]=d>>>16;g[c+7|0]=d>>>24;Yj(a,f,b);if(g[f+11|0]>-1){break a}bp(i[f>>2])}F=f+16|0}function yc(a,b,c){var d=0,e=0,f=0,g=0,h=0;d=32-b|0;c=c<>2];e=32-f|0;d:{if((e|0)>=(b|0)){b=b+f|0;i[a+16>>2]=b;e=i[a+12>>2]|c>>>f;i[a+12>>2]=e;if((b|0)!=32){break d}b=i[a+4>>2];e:{if((b|0)!=i[a+8>>2]){i[b>>2]=e;i[a+4>>2]=b+4;break e}c=i[a>>2];f=b-c|0;g=f>>2;d=g+1|0;if(d>>>0>=1073741824){break c}h=f>>1;d=g>>>0<536870911?h>>>0>>0?d:h:1073741823;b=0;f:{if(!d){break f}if(d>>>0>=1073741824){break b}b=ho(d<<2)}g=b+(g<<2)|0;i[g>>2]=e;d=b+(d<<2)|0;e=g+4|0;if((f|0)>=1){hp(b,c,f)}i[a+8>>2]=d;i[a+4>>2]=e;i[a>>2]=b;if(!c){break e}bp(c)}i[a+12>>2]=0;i[a+16>>2]=0;return}b=b-e|0;i[a+16>>2]=b;h=c>>>d|0;e=i[a+12>>2]|h>>>b;i[a+12>>2]=e;b=i[a+4>>2];g:{if((b|0)!=i[a+8>>2]){i[b>>2]=e;i[a+4>>2]=b+4;break g}c=i[a>>2];f=b-c|0;g=f>>2;d=g+1|0;if(d>>>0>=1073741824){break c}b=f>>1;d=g>>>0<536870911?b>>>0>>0?d:b:1073741823;b=0;h:{if(!d){break h}if(d>>>0>=1073741824){break a}b=ho(d<<2)}g=b+(g<<2)|0;i[g>>2]=e;d=b+(d<<2)|0;e=g+4|0;if((f|0)>=1){hp(b,c,f)}i[a+8>>2]=d;i[a+4>>2]=e;i[a>>2]=b;if(!c){break g}bp(c)}i[a+12>>2]=h<<32-i[a+16>>2]}return}Ho();x()}za(1752);x()}za(1752);x()}function lm(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0;h=F-16|0;F=h;i[a+4>>2]=0;i[a+8>>2]=0;c=a+16|0;i[c>>2]=0;i[c+4>>2]=0;f=a+4|0;i[a>>2]=f;i[a+12>>2]=c;c=i[b>>2];g=b+4|0;if((c|0)!=(g|0)){while(1){e=c;d=c+16|0;mm(a,f,d,d);d=i[c+4>>2];a:{if(!d){c=i[e+8>>2];if((e|0)==i[c>>2]){break a}d=e+8|0;while(1){e=i[d>>2];d=e+8|0;c=i[e+8>>2];if((e|0)!=i[c>>2]){continue}break}break a}while(1){c=d;d=i[c>>2];if(d){continue}break}}if((c|0)!=(g|0)){continue}break}}c=i[b+12>>2];g=b+16|0;if((c|0)!=(g|0)){e=a+12|0;while(1){b=c;k=ho(24);lm(k,i[c+28>>2]);c=c+16|0;f=Of(e,h+12|0,c);d=i[f>>2];if(!d){d=ho(32);ro(d+16|0,c);i[d+28>>2]=0;i[d+8>>2]=i[h+12>>2];i[d>>2]=0;i[d+4>>2]=0;i[f>>2]=d;c=i[i[e>>2]>>2];j=d;b:{if(!c){break b}i[e>>2]=c;j=i[f>>2]}c=j;Kf(i[a+16>>2],c);i[a+20>>2]=i[a+20>>2]+1}d=d+28|0;c=i[d>>2];i[d>>2]=k;if(c){yk(c+12|0,i[c+16>>2]);zk(c,i[c+4>>2]);bp(c)}d=i[b+4>>2];c:{if(!d){c=i[b+8>>2];if((b|0)==i[c>>2]){break c}d=b+8|0;while(1){b=i[d>>2];d=b+8|0;c=i[b+8>>2];if((b|0)!=i[c>>2]){continue}break}break c}while(1){c=d;d=i[c>>2];if(d){continue}break}}if((c|0)!=(g|0)){continue}break}}F=h+16|0}function ii(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;f=d-c|0;if((f|0)<1){return}a:{h=f>>2;k=i[a+8>>2];l=i[a+4>>2];if((h|0)<=k-l>>2){g=l-b|0;e=g>>2;if((h|0)<=(e|0)){f=l;j=d;break a}f=l;j=(e<<2)+c|0;if((j|0)!=(d|0)){e=j;while(1){i[f>>2]=i[e>>2];f=f+4|0;e=e+4|0;if((e|0)!=(d|0)){continue}break}}i[a+4>>2]=f;if((g|0)>=1){break a}return}b:{j=i[a>>2];g=h+(l-j>>2)|0;if(g>>>0<1073741824){k=k-j|0;e=k>>1;g=k>>2>>>0<536870911?e>>>0>>0?g:e:1073741823;e=0;c:{if(!g){break c}if(g>>>0>=1073741824){break b}e=ho(g<<2)}k=b-j|0;m=c;c=c-d|0;c=hp(e+(k>>2<<2)|0,m,o(((c|0)>(f|0)?c:f)>>>2|0,(f|0)<1?f:1)<<2);d=h<<2;h=g<<2;if((k|0)>=1){hp(e,j,k)}f=c+d|0;d=e+h|0;c=l-b|0;if((c|0)>=1){f=hp(f,b,c)+c|0}i[a+8>>2]=d;i[a+4>>2]=f;i[a>>2]=e;if(j){bp(j)}return}Ho();x()}za(11708);x()}e=h<<2;g=f-(e+b|0)|0;d=f;e=f-e|0;if(e>>>0>>0){h=e;while(1){i[d>>2]=i[h>>2];d=d+4|0;h=h+4|0;if(h>>>0>>0){continue}break}}i[a+4>>2]=d;if(g){while(1){f=f+ -4|0;e=e+ -4|0;i[f>>2]=i[e>>2];if((b|0)!=(e|0)){continue}break}}if((c|0)==(j|0)){return}f=b;while(1){i[f>>2]=i[c>>2];f=f+4|0;c=c+4|0;if((j|0)!=(c|0)){continue}break}}function sl(a,b,c){var d=0,e=0,f=0,h=0;e=F+ -64|0;F=e;a:{b:{c:{d:{e:{if(!a){break e}d=mp(b);if(d>>>0>=4294967280){break d}f:{g:{if(d>>>0>=11){h=d+16&-16;f=ho(h);i[e+56>>2]=h|-2147483648;i[e+48>>2]=f;i[e+52>>2]=d;break g}g[e+59|0]=d;f=e+48|0;if(!d){break f}}hp(f,b,d)}g[d+f|0]=0;d=mp(c);if(d>>>0>=4294967280){break c}h:{i:{if(d>>>0>=11){h=d+16&-16;f=ho(h);i[e+40>>2]=h|-2147483648;i[e+32>>2]=f;i[e+36>>2]=d;break i}g[e+43|0]=d;f=e+32|0;if(!d){break h}}hp(f,c,d)}g[d+f|0]=0;d=mp(b);if(d>>>0>=4294967280){break b}j:{k:{if(d>>>0>=11){h=d+16&-16;f=ho(h);i[e+24>>2]=h|-2147483648;i[e+16>>2]=f;i[e+20>>2]=d;break k}g[e+27|0]=d;f=e+16|0;if(!d){break j}}hp(f,b,d)}g[d+f|0]=0;d=mp(c);if(d>>>0>=4294967280){break a}l:{m:{if(d>>>0>=11){f=d+16&-16;b=ho(f);i[e+8>>2]=f|-2147483648;i[e>>2]=b;i[e+4>>2]=d;f=e;break m}g[e+11|0]=d;f=e;b=e;if(!d){break l}}hp(b,c,d)}g[b+d|0]=0;um(a,e+16|0,e);if(g[f+11|0]<=-1){bp(i[e>>2])}if(g[e+27|0]<=-1){bp(i[e+16>>2])}if(g[e+43|0]<=-1){bp(i[e+32>>2])}if(g[e+59|0]>-1){break e}bp(i[e+48>>2])}F=e- -64|0;return(a|0)!=0}qo();x()}qo();x()}qo();x()}qo();x()}function qj(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0;d=F-16|0;F=d;a:{b:{c:{if(!b){b=ho(76);c=ho(12);i[c+8>>2]=i[i[a+4>>2]+80>>2];i[c>>2]=13420;i[c+4>>2]=0;i[d>>2]=c;Mc(b,d,0);i[d+8>>2]=b;c=i[a+12>>2];d:{if(c>>>0>2]){i[d+8>>2]=0;i[c>>2]=b;i[a+12>>2]=c+4;break d}Xh(a+8|0,d+8|0)}a=i[d+8>>2];i[d+8>>2]=0;if(a){I[i[i[a>>2]+4>>2]](a)}a=i[d>>2];i[d>>2]=0;if(!a){break c}I[i[i[a>>2]+4>>2]](a);break c}c=i[i[a+8>>2]>>2];a=i[c+8>>2];e:{if((a|0)!=i[c+12>>2]){i[a>>2]=b;i[c+8>>2]=a+4;break e}f=i[c+4>>2];g=a-f|0;h=g>>2;e=h+1|0;if(e>>>0>=1073741824){break b}a=g>>1;e=h>>>0<536870911?a>>>0>>0?e:a:1073741823;a=0;f:{if(!e){break f}if(e>>>0>=1073741824){break a}a=ho(e<<2)}h=a+(h<<2)|0;i[h>>2]=b;e=a+(e<<2)|0;h=h+4|0;if((g|0)>=1){hp(a,f,g)}i[c+12>>2]=e;i[c+8>>2]=h;i[c+4>>2]=a;if(!f){break e}bp(f)}a=i[c+16>>2];f=i[c+20>>2]-a>>2;g:{if((f|0)>(b|0)){break g}i[d+12>>2]=-1;g=b+1|0;if(g>>>0>f>>>0){a=c+16|0;Gb(a,g-f|0,d+12|0);a=i[a>>2];break g}if(g>>>0>=f>>>0){break g}i[c+20>>2]=(g<<2)+a}i[(b<<2)+a>>2]=(i[c+8>>2]-i[c+4>>2]>>2)+ -1}F=d+16|0;return 1}Ho();x()}za(13344);x()}function Mj(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0;d=F-16|0;F=d;a:{b:{c:{if(!b){b=ho(76);c=ho(12);i[c+8>>2]=i[i[a+4>>2]+80>>2];i[c>>2]=13420;i[c+4>>2]=0;i[d>>2]=c;Mc(b,d,0);i[d+8>>2]=b;c=i[a+12>>2];d:{if(c>>>0>2]){i[d+8>>2]=0;i[c>>2]=b;i[a+12>>2]=c+4;break d}Xh(a+8|0,d+8|0)}a=i[d+8>>2];i[d+8>>2]=0;if(a){I[i[i[a>>2]+4>>2]](a)}a=i[d>>2];i[d>>2]=0;if(!a){break c}I[i[i[a>>2]+4>>2]](a);break c}c=i[i[a+8>>2]>>2];a=i[c+8>>2];e:{if((a|0)!=i[c+12>>2]){i[a>>2]=b;i[c+8>>2]=a+4;break e}f=i[c+4>>2];g=a-f|0;h=g>>2;e=h+1|0;if(e>>>0>=1073741824){break b}a=g>>1;e=h>>>0<536870911?a>>>0>>0?e:a:1073741823;a=0;f:{if(!e){break f}if(e>>>0>=1073741824){break a}a=ho(e<<2)}h=a+(h<<2)|0;i[h>>2]=b;e=a+(e<<2)|0;h=h+4|0;if((g|0)>=1){hp(a,f,g)}i[c+12>>2]=e;i[c+8>>2]=h;i[c+4>>2]=a;if(!f){break e}bp(f)}a=i[c+16>>2];f=i[c+20>>2]-a>>2;g:{if((f|0)>(b|0)){break g}i[d+12>>2]=-1;g=b+1|0;if(g>>>0>f>>>0){a=c+16|0;Gb(a,g-f|0,d+12|0);a=i[a>>2];break g}if(g>>>0>=f>>>0){break g}i[c+20>>2]=(g<<2)+a}i[(b<<2)+a>>2]=(i[c+8>>2]-i[c+4>>2]>>2)+ -1}F=d+16|0;return 1}Ho();x()}za(14116);x()}function _(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0;j=+m[b>>2];k=+m[b+4>>2];n=+m[b+8>>2];h=q(j)+q(k)+q(n);a:{if(h>1e-6^1){j=1;k=0;f=0;break a}h=1/h;k=h*k;j=h*j;f=h*n<0}h=k;g=i[a+16>>2];k=+(g|0);h=u(h*k+.5);b:{if(q(h)<2147483648){b=~~h;break b}b=-2147483648}l=(b|0)<0?b:0-b|0;j=u(j*k+.5);c:{if(q(j)<2147483648){e=~~j;break c}e=-2147483648}o=(e|0)<0;e=l+((o?e:0-e|0)+g|0)|0;l=(e|0)<0?0:e;f=f?0-l|0:l;b=b+(e>>31&((b|0)>0?e:0-e|0))|0;d:{if(!o){e=b+g|0;b=f+g|0;break d}e:{if((b|0)<=-1){e=f>>31;e=e+f^e;break e}e=i[a+8>>2]+((f|0)<0?f:0-f|0)|0}if((f|0)<=-1){f=b;b=b>>31;b=f+b^b;break d}b=i[a+8>>2]+((b|0)<0?b:0-b|0)|0}f:{if(!(b|e)){b=i[a+8>>2];a=b;break f}f=i[a+8>>2];g:{h:{i:{j:{if(!e){if((b|0)==(f|0)){a=b;break f}a=0;if(b|f){break j}b=0;break f}if((e|0)!=(f|0)|b){break i}b=e;a=b;break f}if((g|0)<(b|0)){b=(g<<1)-b|0;break f}if(f){break g}break h}if((e|0)!=(f|0)){break g}}f=e;if((g|0)<=(b|0)){break g}b=(g<<1)-b|0;a=e;break f}if(!((b|0)!=(f|0)|(g|0)<=(e|0))){a=(g<<1)-e|0;break f}if(b){a=e;break f}b=0;if((g|0)>=(e|0)){a=e;break f}a=(g<<1)-e|0}i[c>>2]=a;i[d>>2]=b}function Cm(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,j=0;a:{if((b|0)<0){break a}c=i[a+12>>2];d=i[a+8>>2];if(c-d>>2>>>0<=b>>>0){break a}d=d+(b<<2)|0;f=i[d>>2];h=i[f+60>>2];g=i[f+56>>2];e=d+4|0;b:{if((e|0)!=(c|0)){while(1){j=i[e>>2];i[e>>2]=0;i[d>>2]=j;if(f){Wb(f)}d=d+4|0;e=e+4|0;if((e|0)!=(c|0)){f=i[d>>2];continue}break}c=i[a+12>>2];if((d|0)==(c|0)){break b}}while(1){c=c+ -4|0;f=i[c>>2];i[c>>2]=0;if(f){Wb(f)}if((c|0)!=(d|0)){continue}break}}i[a+12>>2]=d;c=i[a+4>>2];if(c){Dm(c,h)}c:{if((g|0)>4){break c}d:{d=o(g,12)+a|0;c=i[d+20>>2];f=d+24|0;d=i[f>>2];if((c|0)==(d|0)){break d}while(1){if(i[c>>2]==(b|0)){break d}c=c+4|0;if((d|0)!=(c|0)){continue}break}break c}if((c|0)==(d|0)){break c}e=c+4|0;d=d-e|0;if(d){jp(c,e,d)}i[f>>2]=c+d}d=0;while(1){c=o(d,12)+a|0;e=i[c+24>>2];f=i[c+20>>2];if((e|0)!=(f|0)){c=e-f|0;g=(c|0)>-1?c:-1;e=f-e|0;c=o((g|0)<1?g:1,((e|0)>(c|0)?e:c)>>>2|0);e=c>>>0>1?c:1;c=0;while(1){g=f+(c<<2)|0;h=i[g>>2];if((h|0)>(b|0)){i[g>>2]=h+ -1}c=c+1|0;if((e|0)!=(c|0)){continue}break}}d=d+1|0;if((d|0)!=5){continue}break}}}function Kf(a,b){var c=0,d=0,e=0;c=(a|0)==(b|0);g[b+12|0]=c;a:{if(c){break a}while(1){d=i[b+8>>2];if(j[d+12|0]){break a}b:{c=i[d+8>>2];e=i[c>>2];if((e|0)==(d|0)){e=i[c+4>>2];if(!(!e|j[e+12|0])){break b}c:{if(i[d>>2]==(b|0)){b=d;break c}b=i[d+4>>2];e=i[b>>2];i[d+4>>2]=e;a=b;if(e){i[e+8>>2]=d;c=i[d+8>>2]}i[a+8>>2]=c;a=i[d+8>>2];i[(((d|0)!=i[a>>2])<<2)+a>>2]=b;i[b>>2]=d;i[d+8>>2]=b;c=i[b+8>>2]}g[b+12|0]=1;g[c+12|0]=0;a=i[c>>2];b=i[a+4>>2];i[c>>2]=b;if(b){i[b+8>>2]=c}i[a+8>>2]=i[c+8>>2];b=i[c+8>>2];i[((i[b>>2]!=(c|0))<<2)+b>>2]=a;i[a+4>>2]=c;i[c+8>>2]=a;return}if(!(j[e+12|0]|!e)){break b}d:{if(i[d>>2]!=(b|0)){b=d;break d}e=i[b+4>>2];i[d>>2]=e;a=b;if(e){i[e+8>>2]=d;c=i[d+8>>2]}i[a+8>>2]=c;a=i[d+8>>2];i[(((d|0)!=i[a>>2])<<2)+a>>2]=b;i[b+4>>2]=d;i[d+8>>2]=b;c=i[b+8>>2]}g[b+12|0]=1;g[c+12|0]=0;a=i[c+4>>2];b=i[a>>2];i[c+4>>2]=b;if(b){i[b+8>>2]=c}i[a+8>>2]=i[c+8>>2];b=i[c+8>>2];i[((i[b>>2]!=(c|0))<<2)+b>>2]=a;i[a>>2]=c;i[c+8>>2]=a;break a}b=e+12|0;g[d+12|0]=1;g[c+12|0]=(a|0)==(c|0);g[b|0]=1;b=c;if((a|0)!=(b|0)){continue}break}}}function dd(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;g=i[b+4>>2];f=i[b>>2];c=(i[c+4>>2]-g|0)+(i[c>>2]-f<<3)|0;a:{if((c|0)<=0){g=i[d+4>>2];break a}b:{if(!g){g=i[d+4>>2];break b}e=i[d+4>>2];j=32-e|0;l=32-g|0;h=(c|0)<(l|0)?c:l;k=j>>>0>>0?j:h;m=i[d>>2];f=i[f>>2]&(-1<>>l-h);i[m>>2]=i[m>>2]&(-1<>>j-k^-1)|(e>>>0>g>>>0?f<>>g-e|0);e=e+k|0;g=e&31;i[d+4>>2]=g;j=m+(e>>>3&536870908)|0;i[d>>2]=j;e=h-k|0;if((e|0)>=1){i[j>>2]=i[j>>2]&(-1>>>32-e^-1)|f>>>k+i[b+4>>2];i[d+4>>2]=e;g=e}c=c-h|0;f=i[b>>2]+4|0;i[b>>2]=f}k=-1<>2];f=i[f>>2];i[e>>2]=j&i[e>>2]|f<>2]=e+4;i[e+4>>2]=k&i[e+4>>2]|f>>>h;f=i[b>>2]+4|0;i[b>>2]=f;l=(c|0)>63;e=c+ -32|0;c=e;if(l){continue}break}}if((e|0)<1){break a}b=i[d>>2];c=(h|0)<(e|0)?h:e;j=i[b>>2]&(k&-1>>>h-c^-1);h=i[f>>2]&-1>>>32-e;i[b>>2]=j|h<>2]=g;f=b+(f>>>3&536870908)|0;i[d>>2]=f;b=e-c|0;if((b|0)<1){break a}i[f>>2]=i[f>>2]&(-1>>>32-b^-1)|h>>>c;i[d+4>>2]=b;g=b}b=i[d>>2];i[a+4>>2]=g;i[a>>2]=b}function wn(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0;h=F-32|0;F=h;f=d&2147483647;g=f;e=f+ -1065418752|0;f=c;j=f;if(f>>>0<0){e=e+1|0}l=j;k=g+ -1082064896|0;j=f;if(f>>>0<0){k=k+1|0}a:{if((e|0)==(k|0)&l>>>0>>0|e>>>0>>0){g=(d&33554431)<<7|c>>>25;f=0;k=f;j=c&33554431;if(!(!f&(j|0)==16777216?!(a|b):!f&j>>>0<16777216|f>>>0<0)){e=g+1073741825|0;break a}e=g+1073741824|0;if(j^16777216|a|(b|k)){break a}e=(e&1)+e|0;break a}if(!(!f&(g|0)==2147418112?!(a|b):(g|0)==2147418112&f>>>0<0|g>>>0<2147418112)){e=((d&33554431)<<7|c>>>25)&4194303|2143289344;break a}e=2139095040;if(g>>>0>1082064895){break a}e=0;g=g>>>16|0;if(g>>>0<16145){break a}e=d&65535|65536;sn(h+16|0,a,b,c,e,g+ -16129|0);vn(h,a,b,f,e,16257-g|0);b=i[h+8>>2];e=(i[h+12>>2]&33554431)<<7|b>>>25;f=i[h+4>>2];g=f;a=0;j=a;k=i[h>>2]|((i[h+16>>2]|i[h+24>>2])!=0|(i[h+20>>2]|i[h+28>>2])!=0);b=b&33554431;if(!(!a&(b|0)==16777216?!(f|k):!a&b>>>0<16777216|a>>>0<0)){e=e+1|0;break a}if(b^16777216|k|(g|j)){break a}e=(e&1)+e|0}F=h+32|0;return Cp(2,d&-2147483648|e),Gp()}function hi(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0;f=-1;a:{if((b|0)==-1){h=i[a+12>>2];d=1;e=-1;break a}h=i[a+12>>2];d=((b>>>0)%3|0?-1:2)+b|0;b:{if((d|0)==-1){d=1;break b}g=i[h+12>>2];while(1){f=d;e=i[g+(d<<2)>>2];if((e|0)!=-1){f=-1;d=e+1|0;d=(d>>>0)%3|0?d:e+ -2|0;if((d|0)!=-1){continue}}break}d=(e|0)!=-1;e=-1;if((b|0)==-1){break a}}e=i[i[h>>2]+(b<<2)>>2]}b=0;if(c){b=i[a+84>>2]+(e>>>3&536870908)|0;i[b>>2]=i[b>>2]|1<>2]+(e<<2)>>2];g=i[a+140>>2]+(c>>>3&536870908)|0;i[g>>2]=i[g>>2]|1<>>0)%3|0?-1:2)+f|0;c=-1;if((d|0)==-1){break c}c=i[i[h>>2]+(d<<2)>>2]}if((g|0)!=(c|0)){g=i[a+84>>2];while(1){a=f;f=g+(c>>>3&536870908)|0;i[f>>2]=i[f>>2]|1<>>0)%3|0?d:a+ -2|0;if((d|0)==-1){break d}j=i[h+12>>2];while(1){a=i[j+(d<<2)>>2];if((a|0)!=-1){d=a+1|0;d=(d>>>0)%3|0?d:a+ -2|0;if((d|0)!=-1){continue}break d}break}a=((d>>>0)%3|0?-1:2)+d|0;if((a|0)!=-1){c=i[i[h>>2]+(a<<2)>>2]}f=d}b=b+1|0;if((c|0)!=(e|0)){continue}break}}}function xi(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,k=0,l=0,m=0,n=0;a:{b:{if(j[a+352|0]){break b}b=i[a+8>>2];g=i[b+12>>2];h=i[b+8>>2];f=g-h|0;d=(f>>2)+ -1|0;c=i[a+176>>2];b=i[a+172>>2];e=(c-b|0)/136|0;c:{if(d>>>0>e>>>0){mi(a+172|0,d-e|0);break c}if(d>>>0>=e>>>0){break c}d=b+o(d,136)|0;if((d|0)!=(c|0)){while(1){b=i[c+ -20>>2];if(b){i[c+ -16>>2]=b;bp(b)}b=c+ -136|0;e=i[c+ -32>>2];if(e){i[c+ -28>>2]=e;bp(e)}ni(c+ -132|0);c=b;if((b|0)!=(d|0)){continue}break}}i[a+176>>2]=d}if((f|0)==4|(f|0)<1){break b}b=h-g|0;b=((b|0)>(f|0)?b:f)>>>2|0;f=b>>>0>1?b:1;c=0;while(1){d=i[i[i[a+8>>2]+8>>2]+(c<<2)>>2];if(i[d+56>>2]){e=o(n,136);k=e+i[a+172>>2]|0;i[k>>2]=c;g=k+108|0;l=i[k+104>>2];i[g>>2]=l;h=k+112|0;b=i[a+12>>2];b=i[b+4>>2]-i[b>>2]|0;m=b>>2;d:{if(i[h>>2]-l>>2>>>0>=m>>>0){break d}if(m>>>0>=1073741824){break a}b=ho(b);i[k+104>>2]=b;i[g>>2]=b;i[h>>2]=b+(m<<2);if(!l){break d}bp(l)}b=e+i[a+172>>2]|0;i[b+128>>2]=0;am(b+4|0,i[a+8>>2],i[a+12>>2],d);n=n+1|0}c=c+1|0;if((f|0)!=(c|0)){continue}break}}return 1}za(11708);x()}function di(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,k=0,l=0,m=0,n=0;a:{b:{if(j[a+288|0]){break b}b=i[a+8>>2];g=i[b+12>>2];h=i[b+8>>2];f=g-h|0;d=(f>>2)+ -1|0;c=i[a+176>>2];b=i[a+172>>2];e=(c-b|0)/136|0;c:{if(d>>>0>e>>>0){mi(a+172|0,d-e|0);break c}if(d>>>0>=e>>>0){break c}d=b+o(d,136)|0;if((d|0)!=(c|0)){while(1){b=i[c+ -20>>2];if(b){i[c+ -16>>2]=b;bp(b)}b=c+ -136|0;e=i[c+ -32>>2];if(e){i[c+ -28>>2]=e;bp(e)}ni(c+ -132|0);c=b;if((b|0)!=(d|0)){continue}break}}i[a+176>>2]=d}if((f|0)==4|(f|0)<1){break b}b=h-g|0;b=((b|0)>(f|0)?b:f)>>>2|0;f=b>>>0>1?b:1;c=0;while(1){d=i[i[i[a+8>>2]+8>>2]+(c<<2)>>2];if(i[d+56>>2]){e=o(n,136);k=e+i[a+172>>2]|0;i[k>>2]=c;g=k+108|0;l=i[k+104>>2];i[g>>2]=l;h=k+112|0;b=i[a+12>>2];b=i[b+4>>2]-i[b>>2]|0;m=b>>2;d:{if(i[h>>2]-l>>2>>>0>=m>>>0){break d}if(m>>>0>=1073741824){break a}b=ho(b);i[k+104>>2]=b;i[g>>2]=b;i[h>>2]=b+(m<<2);if(!l){break d}bp(l)}b=e+i[a+172>>2]|0;i[b+128>>2]=0;am(b+4|0,i[a+8>>2],i[a+12>>2],d);n=n+1|0}c=c+1|0;if((f|0)!=(c|0)){continue}break}}return 1}za(11708);x()}function eg(a,b,c,d,e){var f=0,g=0,h=0,j=0,k=0,m=0,o=0,p=0,q=0,r=0;o=F-16|0;F=o;h=i[b+28>>2];f=a+8|0;g=i[b+24>>2];i[f>>2]=g;i[f+4>>2]=h;j=i[b+20>>2];i[a>>2]=i[b+16>>2];i[a+4>>2]=j;j=i[b+36>>2];i[a+16>>2]=i[b+32>>2];i[a+20>>2]=j;i[f>>2]=d+g;a:{b:{if((d|0)>=1){j=i[b>>2];f=0;while(1){g=i[b+4>>2]-j>>2;h=i[(f<<2)+c>>2];c:{if(g>>>0>h>>>0){break c}i[o+12>>2]=0;k=h+1|0;if(k>>>0>g>>>0){Gb(b,k-g|0,o+12|0);j=i[b>>2];break c}if(k>>>0>=g>>>0){break c}i[b+4>>2]=(k<<2)+j}k=(h<<2)+j|0;g=i[k>>2];d:{if((g|0)>=2){m=+(g|0);m=ep(m)*m;break d}m=0;if(g){break d}i[a+16>>2]=i[a+16>>2]+1;if(h>>>0<=l[a+12>>2]){break d}i[a+12>>2]=h}h=g+1|0;i[k>>2]=h;p=+(h|0);q=a,r=ep(p)*p-m+n[a>>3],n[q>>3]=r;f=f+1|0;if((f|0)!=(d|0)){continue}break}if(e){break b}if((d|0)<1){break a}a=i[b>>2];f=0;while(1){b=a+(i[(f<<2)+c>>2]<<2)|0;i[b>>2]=i[b>>2]+ -1;f=f+1|0;if((f|0)!=(d|0)){continue}break}break a}if(!e){break a}}d=i[a+4>>2];b=b+16|0;i[b>>2]=i[a>>2];i[b+4>>2]=d;i[b+16>>2]=i[a+16>>2];c=i[a+12>>2];i[b+8>>2]=i[a+8>>2];i[b+12>>2]=c}F=o+16|0}function si(a){i[a+4>>2]=0;i[a+8>>2]=0;i[a>>2]=11424;i[a+72>>2]=0;i[a+76>>2]=0;i[a+12>>2]=0;i[a+16>>2]=0;i[a+20>>2]=0;i[a+24>>2]=0;i[a+28>>2]=0;i[a+32>>2]=0;i[a+36>>2]=0;i[a+40>>2]=0;i[a+44>>2]=0;i[a+48>>2]=0;i[a+52>>2]=0;i[a+56>>2]=0;i[a+60>>2]=0;i[a+64>>2]=0;i[a+80>>2]=0;i[a+84>>2]=0;i[a+88>>2]=0;i[a+92>>2]=0;i[a+96>>2]=0;i[a+100>>2]=0;i[a+104>>2]=0;i[a+108>>2]=0;i[a+112>>2]=0;i[a+116>>2]=0;i[a+120>>2]=0;i[a+124>>2]=0;i[a+128>>2]=0;i[a+132>>2]=0;i[a+136>>2]=1065353216;i[a+156>>2]=0;i[a+160>>2]=0;i[a+148>>2]=0;i[a+152>>2]=0;i[a+140>>2]=0;i[a+144>>2]=0;i[a+168>>2]=0;i[a+172>>2]=0;i[a+164>>2]=-1;i[a+176>>2]=0;i[a+180>>2]=0;i[a+184>>2]=0;i[a+188>>2]=0;i[a+192>>2]=0;mf(a+200|0);Tj(a+232|0);i[a+304>>2]=0;i[a+308>>2]=0;i[a+296>>2]=0;i[a+300>>2]=0;i[a+288>>2]=0;i[a+292>>2]=0;i[a+280>>2]=0;i[a+284>>2]=0;i[a+272>>2]=0;i[a+276>>2]=0;i[a+264>>2]=0;i[a+268>>2]=0;g[a+352|0]=0;i[a+344>>2]=0;i[a+336>>2]=0;i[a+340>>2]=0;i[a+328>>2]=2;i[a+332>>2]=7;i[a+320>>2]=-1;i[a+324>>2]=0;i[a+312>>2]=0;i[a+316>>2]=-1}function vc(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0;b=F-32|0;F=b;c=i[a+16>>2];a:{if(c>>>0>=113){i[a+16>>2]=c+ -113;c=i[a+4>>2];i[b+8>>2]=i[c>>2];i[a+4>>2]=c+4;zc(a,b+8|0);break a}b:{c:{d:{e:{d=i[a+8>>2];e=d-i[a+4>>2]>>2;f=i[a+12>>2];c=f-i[a>>2]|0;if(e>>>0>2>>>0){if((d|0)==(f|0)){break e}g=b,h=ho(4068),i[g+8>>2]=h;zc(a,b+8|0);break a}i[b+24>>2]=a+12;c=c?c>>1:1;if(c>>>0>=1073741824){break d}d=c<<2;c=ho(d);i[b+8>>2]=c;e=c+(e<<2)|0;i[b+16>>2]=e;i[b+20>>2]=c+d;i[b+12>>2]=e;g=b,h=ho(4068),i[g+4>>2]=h;zc(b+8|0,b+4|0);d=i[a+8>>2];if((d|0)==i[a+4>>2]){c=d;break b}while(1){d=d+ -4|0;Ac(b+8|0,d);if(i[a+4>>2]!=(d|0)){continue}break}break c}g=b,h=ho(4068),i[g+8>>2]=h;Ac(a,b+8|0);c=i[a+4>>2];i[b+8>>2]=i[c>>2];i[a+4>>2]=c+4;zc(a,b+8|0);break a}za(1752);x()}c=i[a+8>>2]}e=i[a>>2];i[a>>2]=i[b+8>>2];i[b+8>>2]=e;i[a+4>>2]=i[b+12>>2];i[b+12>>2]=d;i[a+8>>2]=i[b+16>>2];i[b+16>>2]=c;f=i[a+12>>2];i[a+12>>2]=i[b+20>>2];i[b+20>>2]=f;if((c|0)!=(d|0)){i[b+16>>2]=c+(((c-d|0)+ -4>>>2^-1)<<2)}if(!e){break a}bp(e)}F=b+32|0}function Og(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0,n=0;e=1;a:{b:{c:{d:{e:{f:{switch(b-a>>2){case 2:d=i[c>>2];c=i[d>>2];d=i[d+4>>2]-c>>3;f=b+ -4|0;b=i[f>>2];if(d>>>0<=b>>>0){break e}g=d;d=i[a>>2];if(g>>>0<=d>>>0){break e}if(l[c+(b<<3)>>2]>=l[c+(d<<3)>>2]){break a}i[a>>2]=b;i[f>>2]=d;return 1;case 3:Ng(a,a+4|0,b+ -4|0,c);return 1;case 4:Kg(a,a+4|0,a+8|0,b+ -4|0,c);return 1;case 5:Lg(a,a+4|0,a+8|0,a+12|0,b+ -4|0,c);return 1;case 0:case 1:break a;default:break f}}f=a+8|0;Ng(a,a+4|0,f,c);e=a+12|0;if((e|0)==(b|0)){break c}d=i[c>>2];h=i[d>>2];j=i[d+4>>2]-h>>3;while(1){d=e;k=i[d>>2];if(j>>>0<=k>>>0){break d}e=i[f>>2];if(j>>>0<=e>>>0){break d}g=d;g:{m=h+(k<<3)|0;if(l[m>>2]>2]){while(1){h:{i[g>>2]=e;c=f;if((c|0)==(a|0)){c=a;break h}f=c+ -4|0;e=i[f>>2];if(j>>>0<=e>>>0){break d}g=c;if(l[m>>2]>2]){continue}}break}i[c>>2]=k;n=n+1|0;if((n|0)==8){break g}}f=d;e=d+4|0;if((e|0)==(b|0)){break c}continue}break}c=(d+4|0)==(b|0);a=0;break b}Io();x()}Io();x()}a=1}e=a|c}return e&1}function Uf(a,b,c,d){var e=0,f=0,h=0,k=0;k=F-16|0;F=k;e=ho(16);i[k>>2]=e;i[k+4>>2]=15;i[k+8>>2]=-2147483632;g[e+15|0]=0;f=j[10535]|j[10536]<<8|(j[10537]<<16|j[10538]<<24);h=j[10531]|j[10532]<<8|(j[10533]<<16|j[10534]<<24);g[e+7|0]=h;g[e+8|0]=h>>>8;g[e+9|0]=h>>>16;g[e+10|0]=h>>>24;g[e+11|0]=f;g[e+12|0]=f>>>8;g[e+13|0]=f>>>16;g[e+14|0]=f>>>24;f=j[10528]|j[10529]<<8|(j[10530]<<16|j[10531]<<24);h=j[10524]|j[10525]<<8|(j[10526]<<16|j[10527]<<24);g[e|0]=h;g[e+1|0]=h>>>8;g[e+2|0]=h>>>16;g[e+3|0]=h>>>24;g[e+4|0]=f;g[e+5|0]=f>>>8;g[e+6|0]=f>>>16;g[e+7|0]=f>>>24;f=b+4|0;e=$j(f,k,-1);if(g[k+11|0]<=-1){bp(i[k>>2])}a:{b:{c:{switch(e+1|0){case 0:if((_b(f)|0)==10){break b}break;case 2:break c;default:break b}}e=ho(68);lj(e);i[e+64>>2]=0;i[e>>2]=10936;break a}e=ho(64);lj(e);i[e>>2]=13204}i[e+56>>2]=c;aa(e,c);yj(a,e,f,d);if(!i[a>>2]){if(g[a+15|0]<=-1){bp(i[a+4>>2])}i[b+40>>2]=i[e+52>>2];i[b+44>>2]=i[e+60>>2];i[a+8>>2]=0;i[a+12>>2]=0;i[a>>2]=0;i[a+4>>2]=0}I[i[i[e>>2]+4>>2]](e);F=k+16|0}function Kj(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0;e=F-16|0;F=e;a:{b:{c=i[a+8>>2];c:{if((c|0)==i[a+12>>2]){c=ho(76);Qb(c,b);i[e+8>>2]=c;b=i[a+12>>2];d:{if(b>>>0>2]){i[e+8>>2]=0;i[b>>2]=c;i[a+12>>2]=b+4;break d}Xh(a+8|0,e+8|0)}a=i[e+8>>2];i[e+8>>2]=0;if(!a){break c}I[i[i[a>>2]+4>>2]](a);break c}c=i[c>>2];a=i[c+8>>2];e:{if((a|0)!=i[c+12>>2]){i[a>>2]=b;i[c+8>>2]=a+4;break e}f=i[c+4>>2];g=a-f|0;h=g>>2;d=h+1|0;if(d>>>0>=1073741824){break b}a=g>>1;d=h>>>0<536870911?a>>>0>>0?d:a:1073741823;a=0;f:{if(!d){break f}if(d>>>0>=1073741824){break a}a=ho(d<<2)}h=a+(h<<2)|0;i[h>>2]=b;d=a+(d<<2)|0;h=h+4|0;if((g|0)>=1){hp(a,f,g)}i[c+12>>2]=d;i[c+8>>2]=h;i[c+4>>2]=a;if(!f){break e}bp(f)}a=i[c+16>>2];f=i[c+20>>2]-a>>2;g:{if((f|0)>(b|0)){break g}i[e+12>>2]=-1;g=b+1|0;if(g>>>0>f>>>0){a=c+16|0;Gb(a,g-f|0,e+12|0);a=i[a>>2];break g}if(g>>>0>=f>>>0){break g}i[c+20>>2]=(g<<2)+a}i[(b<<2)+a>>2]=(i[c+8>>2]-i[c+4>>2]>>2)+ -1}F=e+16|0;return 1}Ho();x()}za(13936);x()}function gb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}k=b+ -1|0;l=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!l){e=e&k;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;j=h+i[a>>2]|0;if(!i[j>>2]){i[j>>2]=f;f=d;g=e;break e}while(1){f:{e=c;c=i[c>>2];if(!c|i[d+8>>2]!=i[c+8>>2]|(i[d+12>>2]!=i[c+12>>2]|i[d+16>>2]!=i[c+16>>2])){break f}if(i[d+20>>2]==i[c+20>>2]){continue}}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function Eb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,l=0,m=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}l=b+ -1|0;m=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!m){e=e&l;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;j=h+i[a>>2]|0;if(!i[j>>2]){i[j>>2]=f;f=d;g=e;break e}while(1){f:{e=c;c=i[c>>2];if(!c|k[d+8>>1]!=k[c+8>>1]|(k[d+10>>1]!=k[c+10>>1]|k[d+12>>1]!=k[c+12>>1])){break f}if(k[d+14>>1]==k[c+14>>1]){continue}}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function Z(a,b,c,d,e){var f=0,g=0,h=0,k=0,l=0,n=0,o=0,q=0;f=F-48|0;F=f;a=i[a+4>>2];a:{if(a+ -2>>>0>28){break a}l=i[i[e>>2]>>2]+i[e+48>>2]|0;i[f+16>>2]=a;a=-1<>2]=a^-1;a=-2-a|0;i[f+24>>2]=a;i[f+32>>2]=(a|0)/2;m[f+28>>2]=p(2)/p(a|0);g=i[c>>2];if((g|0)!=i[c+4>>2]){a=0;e=0;while(1){d=i[(e<<2)+g>>2];g=f+36|0;n=i[i[b>>2]>>2];o=i[b+48>>2];h=i[b+44>>2];k=i[b+40>>2];q=k;if(!j[b+84|0]){d=i[i[b+68>>2]+(d<<2)>>2]}d=up(q,h,d,0)+o|0;hp(g,d+n|0,k);_(f+16|0,f+36|0,f+12|0,f+8|0);d=a<<2;i[d+l>>2]=i[f+12>>2];i[(d|4)+l>>2]=i[f+8>>2];h=1;a=a+2|0;e=e+1|0;g=i[c>>2];if(e>>>0>2]-g>>2>>>0){continue}break}break a}if(!d){h=1;break a}e=0;a=0;while(1){c=a;g=f+36|0;n=i[i[b>>2]>>2];o=i[b+48>>2];h=i[b+44>>2];k=i[b+40>>2];q=k;if(!j[b+84|0]){c=i[i[b+68>>2]+(a<<2)>>2]}c=up(q,h,c,0)+o|0;hp(g,c+n|0,k);_(f+16|0,f+36|0,f+12|0,f+8|0);c=e<<2;i[c+l>>2]=i[f+12>>2];i[(c|4)+l>>2]=i[f+8>>2];e=e+2|0;h=1;a=a+1|0;if((d|0)!=(a|0)){continue}break}}F=f+48|0;return h}function $l(a,b){var c=0,d=0,e=0,f=0,h=0,j=0;j=F-16|0;F=j;a:{b:{if(b){i[a+88>>2]=0;i[a+92>>2]=0;c=a+84|0;d=i[c>>2];i[c>>2]=0;if(d){bp(d)}i[a+76>>2]=0;i[a+80>>2]=0;c=a+72|0;d=i[c>>2];i[c>>2]=0;if(d){bp(d)}d=i[b>>2];c=i[b+4>>2];g[j+15|0]=0;Uh(a,c-d>>2,j+15|0);c=b+28|0;d=i[c>>2];e=i[b+24>>2];g[j+14|0]=0;Uh(a+12|0,d-e>>2,j+14|0);Sh(a+28|0,i[b+4>>2]-i[b>>2]>>2,16716);d=i[a+52>>2];e=i[c>>2]-i[b+24>>2]|0;c=e>>2;c:{if(i[a+60>>2]-d>>2>>>0>=c>>>0){break c}if(c>>>0>=1073741824){break b}f=i[a+56>>2];h=c<<2;c=ho(e);h=h+c|0;e=f-d|0;f=e+c|0;if((e|0)>=1){hp(c,d,e)}i[a+60>>2]=h;i[a+56>>2]=f;i[a+52>>2]=c;if(!d){break c}bp(d)}d=i[a+40>>2];e=i[b+28>>2]-i[b+24>>2]|0;c=e>>2;d:{if(i[a+48>>2]-d>>2>>>0>=c>>>0){break d}if(c>>>0>=1073741824){break a}f=i[a+44>>2];h=c<<2;c=ho(e);h=h+c|0;e=f-d|0;f=e+c|0;if((e|0)>=1){hp(c,d,e)}i[a+48>>2]=h;i[a+44>>2]=f;i[a+40>>2]=c;if(!d){break d}bp(d)}g[a+24|0]=1;i[a+64>>2]=b}F=j+16|0;return(b|0)!=0}za(16720);x()}za(16720);x()}function oe(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,h=0,k=0;f=F-16|0;F=f;if(!(!fd(a,b,c)|j[i[a+8>>2]+24|0]!=3)){h=i[b+48>>2];b=ho(32);i[f>>2]=b;i[f+4>>2]=17;i[f+8>>2]=-2147483616;g[b+17|0]=0;g[b+16|0]=j[6044];d=j[6040]|j[6041]<<8|(j[6042]<<16|j[6043]<<24);e=j[6036]|j[6037]<<8|(j[6038]<<16|j[6039]<<24);g[b+8|0]=e;g[b+9|0]=e>>>8;g[b+10|0]=e>>>16;g[b+11|0]=e>>>24;g[b+12|0]=d;g[b+13|0]=d>>>8;g[b+14|0]=d>>>16;g[b+15|0]=d>>>24;d=j[6032]|j[6033]<<8|(j[6034]<<16|j[6035]<<24);e=j[6028]|j[6029]<<8|(j[6030]<<16|j[6031]<<24);g[b|0]=e;g[b+1|0]=e>>>8;g[b+2|0]=e>>>16;g[b+3|0]=e>>>24;g[b+4|0]=d;g[b+5|0]=d>>>8;g[b+6|0]=d>>>16;g[b+7|0]=d>>>24;a:{b:{e=h+16|0;b=i[e>>2];if(!b){break b}d=e;while(1){k=i[b+16>>2]<(c|0);d=k?d:b;b=i[(k<<2)+b>>2];if(b){continue}break}if((d|0)==(e|0)|i[d+16>>2]>(c|0)){break b}b=d+20|0;if(!Sb(b,f)){break b}b=$j(b,f,-1);break a}b=$j(h,f,-1)}if(g[f+11|0]<=-1){bp(i[f>>2])}if((b|0)>=1){aa(a+40|0,b)}d=(b|0)>0}F=f+16|0;return d|0}function sb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,k=0,l=0,m=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}l=b+ -1|0;m=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!m){e=e&l;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;k=h+i[a>>2]|0;if(!i[k>>2]){i[k>>2]=f;f=d;g=e;break e}while(1){f:{e=c;c=i[c>>2];if(!c|j[d+8|0]!=j[c+8|0]|(j[d+9|0]!=j[c+9|0]|j[d+10|0]!=j[c+10|0])){break f}if(j[d+11|0]==j[c+11|0]){continue}}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function ua(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,h=0,k=0,m=0,n=0;e=i[a+12>>2];d=i[a+8>>2];f=e-d>>2;b=g[b+24|0];a:{if(f>>>0>>0){ka(a+8|0,b-f|0);d=i[a+8>>2];e=i[a+12>>2];break a}if(f>>>0<=b>>>0){break a}e=(b<<2)+d|0;i[a+12>>2]=e}b=0;h=i[c+12>>2];f=i[c+20>>2];k=i[c+16>>2];m=e-d|0;e=m;n=k+e|0;if(n>>>0>>0){f=f+1|0}b:{if((h|0)<(f|0)?1:(h|0)<=(f|0)?l[c+8>>2]>>0:0){break b}hp(d,k+i[c>>2]|0,m);d=i[c+20>>2];h=e+i[c+16>>2]|0;if(h>>>0>>0){d=d+1|0}e=h;i[c+16>>2]=e;i[c+20>>2]=d;f=i[c+12>>2];h=e+4|0;if(h>>>0<4){d=d+1|0}if((f|0)<(d|0)?1:(f|0)<=(d|0)?l[c+8>>2]>>0:0){break b}d=e+i[c>>2]|0;i[a+20>>2]=j[d|0]|j[d+1|0]<<8|(j[d+2|0]<<16|j[d+3|0]<<24);d=i[c+20>>2];e=d;k=i[c+16>>2];h=k+4|0;if(h>>>0<4){d=d+1|0}i[c+16>>2]=h;i[c+20>>2]=d;f=i[c+12>>2];if((f|0)<(d|0)?1:(f|0)<=(d|0)?l[c+8>>2]<=h>>>0:0){break b}f=j[h+i[c>>2]|0];d=e;e=k+5|0;if(e>>>0<5){d=d+1|0}i[c+16>>2]=e;i[c+20>>2]=d;if(f+ -1>>>0>29){break b}i[a+4>>2]=f;b=1}return b|0}function Sf(a,b,c){var d=0,e=0,f=0;e=F-16|0;F=e;d=i[b+48>>2];a:{if(!d){b=ho(32);i[e>>2]=b;i[e+4>>2]=23;i[e+8>>2]=-2147483616;g[b+23|0]=0;c=j[10519]|j[10520]<<8|(j[10521]<<16|j[10522]<<24);d=j[10515]|j[10516]<<8|(j[10517]<<16|j[10518]<<24);g[b+15|0]=d;g[b+16|0]=d>>>8;g[b+17|0]=d>>>16;g[b+18|0]=d>>>24;g[b+19|0]=c;g[b+20|0]=c>>>8;g[b+21|0]=c>>>16;g[b+22|0]=c>>>24;c=j[10512]|j[10513]<<8|(j[10514]<<16|j[10515]<<24);d=j[10508]|j[10509]<<8|(j[10510]<<16|j[10511]<<24);g[b+8|0]=d;g[b+9|0]=d>>>8;g[b+10|0]=d>>>16;g[b+11|0]=d>>>24;g[b+12|0]=c;g[b+13|0]=c>>>8;g[b+14|0]=c>>>16;g[b+15|0]=c>>>24;c=j[10504]|j[10505]<<8|(j[10506]<<16|j[10507]<<24);d=j[10500]|j[10501]<<8|(j[10502]<<16|j[10503]<<24);g[b|0]=d;g[b+1|0]=d>>>8;g[b+2|0]=d>>>16;g[b+3|0]=d>>>24;g[b+4|0]=c;g[b+5|0]=c>>>8;g[b+6|0]=c>>>16;g[b+7|0]=c>>>24;i[a>>2]=-1;ro(a+4|0,e);if(g[e+11|0]>-1){break a}bp(i[e>>2]);break a}f=i[b+52>>2];if(!f){Tf(a,b,d,c);break a}Uf(a,b,f,c)}F=e+16|0}function Qi(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0;n=i[a+12>>2];d=i[a+108>>2];e=i[d+80>>2];g[b+84|0]=0;f=i[b+68>>2];c=i[b+72>>2]-f>>2;a:{if(e>>>0>c>>>0){Xa(b+68|0,e-c|0,12320);d=i[a+108>>2];e=i[d+80>>2];break a}if(e>>>0>=c>>>0){break a}i[b+72>>2]=f+(e<<2)}m=i[d+96>>2];c=i[d+100>>2]-m|0;d=(c|0)/12|0;if(!c){return 1}p=d>>>0>1?d:1;d=0;b:{while(1){c:{if((d|0)==1431655765){break c}c=i[n>>2]+(o(d,3)<<2)|0;j=i[c>>2];if((j|0)==-1){break c}f=o(d,12)+m|0;h=i[f>>2];if(h>>>0>=e>>>0){break c}l=i[i[a+112>>2]+12>>2];k=i[l+(j<<2)>>2];if(k>>>0>=e>>>0){break c}j=i[b+68>>2];i[j+(h<<2)>>2]=k;h=i[c+4>>2];if((h|0)==-1){break c}k=i[f+4>>2];if(k>>>0>=e>>>0){break c}h=i[(h<<2)+l>>2];if(h>>>0>=e>>>0){break c}i[j+(k<<2)>>2]=h;c=i[c+8>>2];if((c|0)==-1){break c}f=i[f+8>>2];if(f>>>0>=e>>>0){break c}c=i[(c<<2)+l>>2];if(c>>>0>=e>>>0){break c}i[j+(f<<2)>>2]=c;c=1;d=d+1|0;if((p|0)!=(d|0)){continue}break b}break}c=0}return c|0}function $i(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0;n=i[a+12>>2];d=i[a+68>>2];e=i[d+80>>2];g[b+84|0]=0;f=i[b+68>>2];c=i[b+72>>2]-f>>2;a:{if(e>>>0>c>>>0){Xa(b+68|0,e-c|0,12320);d=i[a+68>>2];e=i[d+80>>2];break a}if(e>>>0>=c>>>0){break a}i[b+72>>2]=f+(e<<2)}m=i[d+96>>2];c=i[d+100>>2]-m|0;d=(c|0)/12|0;if(!c){return 1}p=d>>>0>1?d:1;d=0;b:{while(1){c:{if((d|0)==1431655765){break c}c=i[n>>2]+(o(d,3)<<2)|0;j=i[c>>2];if((j|0)==-1){break c}f=o(d,12)+m|0;h=i[f>>2];if(h>>>0>=e>>>0){break c}l=i[i[a+72>>2]+12>>2];k=i[l+(j<<2)>>2];if(k>>>0>=e>>>0){break c}j=i[b+68>>2];i[j+(h<<2)>>2]=k;h=i[c+4>>2];if((h|0)==-1){break c}k=i[f+4>>2];if(k>>>0>=e>>>0){break c}h=i[(h<<2)+l>>2];if(h>>>0>=e>>>0){break c}i[j+(k<<2)>>2]=h;c=i[c+8>>2];if((c|0)==-1){break c}f=i[f+8>>2];if(f>>>0>=e>>>0){break c}c=i[(c<<2)+l>>2];if(c>>>0>=e>>>0){break c}i[j+(f<<2)>>2]=c;c=1;d=d+1|0;if((p|0)!=(d|0)){continue}break b}break}c=0}return c|0}function eb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}k=b+ -1|0;l=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!l){e=e&k;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;j=h+i[a>>2]|0;if(!i[j>>2]){i[j>>2]=f;f=d;g=e;break e}while(1){f:{e=c;c=i[c>>2];if(!c|i[d+8>>2]!=i[c+8>>2]|i[d+12>>2]!=i[c+12>>2]){break f}if(i[d+16>>2]==i[c+16>>2]){continue}}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function Cb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,l=0,m=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}l=b+ -1|0;m=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!m){e=e&l;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;j=h+i[a>>2]|0;if(!i[j>>2]){i[j>>2]=f;f=d;g=e;break e}while(1){f:{e=c;c=i[c>>2];if(!c|k[d+8>>1]!=k[c+8>>1]|k[d+10>>1]!=k[c+10>>1]){break f}if(k[d+12>>1]==k[c+12>>1]){continue}}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function $c(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;e=F-16|0;F=e;a:{c=i[a+4>>2];b:{if(c>>>0>>0){d=i[a+8>>2];f=d<<5;g=b-c|0;c:{if(!(f>>>0>>0|c>>>0>f-g>>>0)){i[a+4>>2]=b;d=c&31;b=i[a>>2]+(c>>>3&536870908)|0;break c}i[e+8>>2]=0;i[e>>2]=0;i[e+4>>2]=0;if((b|0)<=-1){break a}c=e;if(f>>>0<=1073741822){f=b+31&-32;b=d<<6;b=b>>>0>>0?f:b}else{b=2147483647}bd(c,b);d=i[a+4>>2];i[e+4>>2]=d+g;h=i[a>>2];b=i[e>>2];d:{if((d|0)<1){d=0;break d}c=d>>>5|0;f=c<<2;b=jp(b,h,f)+f|0;d=d-(c<<5)|0;e:{if((d|0)<1){d=0;break e}c=-1>>>32-d|0;i[b>>2]=i[b>>2]&(c^-1)|c&i[f+h>>2]}h=i[a>>2]}i[a>>2]=i[e>>2];i[e>>2]=h;c=i[a+4>>2];i[a+4>>2]=i[e+4>>2];i[e+4>>2]=c;c=i[a+8>>2];i[a+8>>2]=i[e+8>>2];i[e+8>>2]=c;if(!h){break c}bp(h)}if(!g){break b}if(d){c=32-d|0;a=c>>>0>g>>>0?g:c;i[b>>2]=i[b>>2]&(-1<>>c-a^-1);g=g-a|0;b=b+4|0}c=g>>>5<<2;a=ip(b,0,c);b=g&31;if(!b){break b}a=a+c|0;i[a>>2]=i[a>>2]&(-1>>>32-b^-1);break b}i[a+4>>2]=b}F=e+16|0;return}Ho();x()}function Ri(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0;g=F-16|0;F=g;b=i[a+4>>2];d=i[b>>2];a:{c=i[a+12>>2];e=i[c+28>>2]-i[c+24>>2]|0;c=e>>2;b:{if(i[b+8>>2]-d>>2>>>0>=c>>>0){break b}if(c>>>0>=1073741824){break a}f=i[b+4>>2];h=c<<2;c=ho(e);h=h+c|0;e=f-d|0;f=e+c|0;if((e|0)>=1){hp(c,d,e)}i[b+8>>2]=h;i[b+4>>2]=f;i[b>>2]=c;if(!d){break b}bp(d)}b=i[a+12>>2];d=i[b+28>>2];b=i[b+24>>2];i[g+12>>2]=0;b=d-b>>2;c=a+96|0;e=i[c>>2];d=i[a+100>>2]-e>>2;c:{if(b>>>0>d>>>0){Gb(c,b-d|0,g+12|0);break c}if(b>>>0>=d>>>0){break c}i[a+100>>2]=e+(b<<2)}e=a+8|0;b=i[a+116>>2];d:{if(b){c=i[b>>2];if((c|0)==i[b+4>>2]){d=1;break d}b=0;while(1){d=Si(e,i[(b<<2)+c>>2]);if(!d){break d}f=i[a+116>>2];c=i[f>>2];b=b+1|0;if(b>>>0>2]-c>>2>>>0){continue}break}break d}d=1;a=i[a+12>>2];a=(i[a+4>>2]-i[a>>2]>>2>>>0)/3|0;if(a>>>0<1){break d}b=0;while(1){d=Si(e,o(b,3));b=b+1|0;if((a|0)==(b|0)){break d}if(d){continue}break}}F=g+16|0;return d|0}za(11708);x()}function cc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;d=F-16|0;F=d;i[a+8>>2]=b;i[a>>2]=0;nc(a+12|0);mf(a+1036|0);hf(a+1068|0);hf(a+1088|0);hf(a+1108|0);i[a+1136>>2]=0;i[a+1128>>2]=0;i[a+1132>>2]=0;a:{if(b){if(b>>>0>=1073741824){break a}e=b<<2;c=ho(e);i[a+1128>>2]=c;f=c+e|0;i[a+1136>>2]=f;ip(c,0,e);i[a+1132>>2]=f}i[a+1140>>2]=0;i[a+1144>>2]=0;i[a+1148>>2]=0;if(b){e=b<<2;c=ho(e);i[a+1140>>2]=c;f=c+e|0;i[a+1148>>2]=f;ip(c,0,e);i[a+1144>>2]=f}i[a+1152>>2]=0;i[a+1156>>2]=0;i[a+1160>>2]=0;if(b){e=b<<2;c=ho(e);i[a+1152>>2]=c;f=c+e|0;i[a+1160>>2]=f;ip(c,0,e);i[a+1156>>2]=f}i[d+8>>2]=0;i[d>>2]=0;i[d+4>>2]=0;g=a+1164|0;e=b<<5|1;if(b){c=b<<2;f=ho(c);i[d>>2]=f;h=c+f|0;i[d+8>>2]=h;ip(f,0,c);i[d+4>>2]=h}oc(g,e,d);c=i[d>>2];if(c){i[d+4>>2]=c;bp(c)}i[d+8>>2]=0;i[d>>2]=0;i[d+4>>2]=0;f=a+1176|0;if(b){b=b<<2;c=ho(b);i[d>>2]=c;g=b+c|0;i[d+8>>2]=g;ip(c,0,b);i[d+4>>2]=g}oc(f,e,d);b=i[d>>2];if(b){i[d+4>>2]=b;bp(b)}F=d+16|0;return a}Ho();x()}function qb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,k=0,l=0,m=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}l=b+ -1|0;m=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!m){e=e&l;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;k=h+i[a>>2]|0;if(!i[k>>2]){i[k>>2]=f;f=d;g=e;break e}while(1){f:{e=c;c=i[c>>2];if(!c|j[d+8|0]!=j[c+8|0]|j[d+9|0]!=j[c+9|0]){break f}if(j[d+10|0]==j[c+10|0]){continue}}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function fj(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0;j=i[a+12>>2];d=i[a+68>>2];e=i[d+80>>2];g[b+84|0]=0;h=i[b+68>>2];c=i[b+72>>2]-h>>2;a:{if(e>>>0>c>>>0){Xa(b+68|0,e-c|0,12320);d=i[a+68>>2];e=i[d+80>>2];break a}if(e>>>0>=c>>>0){break a}i[b+72>>2]=h+(e<<2)}m=i[d+96>>2];c=i[d+100>>2]-m|0;d=(c|0)/12|0;if(!c){return 1}n=d>>>0>1?d:1;p=i[j+28>>2];d=0;b:{while(1){c:{c=(o(d,3)<<2)+p|0;h=i[c>>2];if((h|0)==-1){break c}j=o(d,12)+m|0;k=i[j>>2];if(k>>>0>=e>>>0){break c}f=h<<2;h=i[i[a+72>>2]+12>>2];f=i[f+h>>2];if(f>>>0>=e>>>0){break c}l=k<<2;k=i[b+68>>2];i[l+k>>2]=f;f=i[c+4>>2];if((f|0)==-1){break c}l=i[j+4>>2];if(l>>>0>=e>>>0){break c}f=i[h+(f<<2)>>2];if(f>>>0>=e>>>0){break c}i[k+(l<<2)>>2]=f;c=i[c+8>>2];if((c|0)==-1){break c}j=i[j+8>>2];if(j>>>0>=e>>>0){break c}c=i[h+(c<<2)>>2];if(c>>>0>=e>>>0){break c}i[k+(j<<2)>>2]=c;c=1;d=d+1|0;if((n|0)!=(d|0)){continue}break b}break}c=0}return c|0}function wc(a,b,c,d,e){var f=0,g=0,h=0,j=0,k=0,m=0,n=0,p=0,q=0,r=0,s=0;g=i[a+8>>2];a:{b:{c:{h=i[c>>2];f=i[b>>2];k=h-f|0;if(k>>>0>=64){if(g){break c}b=0;break b}b=0;if(g>>>0<2){break a}a=i[e>>2];c=1;while(1){b=l[a+(b<<2)>>2]>l[a+(c<<2)>>2]?c:b;c=c+1|0;if((g|0)!=(c|0)){continue}break}break a}g=i[b+8>>2];m=i[b+4>>2];n=i[d>>2];p=i[a+1140>>2];q=i[e>>2];r=i[a+1128>>2];while(1){b=j<<2;d=b+r|0;i[d>>2]=0;c=i[a>>2]-i[b+q>>2]|0;i[b+p>>2]=c;if(c){d:{if((f|0)==(h|0)){c=i[d>>2];break d}e=i[b+n>>2]+(1<>2]|0;c=i[d>>2];b=f;while(1){c=(l[s+(o(b,g)<<2)>>2]>>0)+c|0;i[d>>2]=c;b=b+1|0;if((h|0)!=(b|0)){continue}break}}b=k-c|0;i[d>>2]=b>>>0>>0?c:b}j=j+1|0;e=i[a+8>>2];if(j>>>0>>0){continue}break}if(!e){b=0;break b}g=i[a+1140>>2];c=0;d=0;b=0;while(1){f=c<<2;if(i[f+g>>2]){h=i[f+i[a+1128>>2]>>2];f=d>>>0>>0;d=f?h:d;b=f?c:b}c=c+1|0;if((e|0)!=(c|0)){continue}break}}yc(a+1088|0,4,b)}return b}function cb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}k=b+ -1|0;l=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!l){e=e&k;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;j=h+i[a>>2]|0;if(!i[j>>2]){i[j>>2]=f;f=d;g=e;break e}while(1){f:{e=c;c=i[c>>2];if(!c|i[d+8>>2]!=i[c+8>>2]){break f}if(i[d+12>>2]==i[c+12>>2]){continue}}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function Ab(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,l=0,m=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}l=b+ -1|0;m=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!m){e=e&l;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;j=h+i[a>>2]|0;if(!i[j>>2]){i[j>>2]=f;f=d;g=e;break e}while(1){f:{e=c;c=i[c>>2];if(!c|k[d+8>>1]!=k[c+8>>1]){break f}if(k[d+10>>1]==k[c+10>>1]){continue}}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function Am(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;j=a+8|0;d=i[a+12>>2];h=i[a+8>>2];f=d-h>>2;a:{if((f|0)>(b|0)){break a}e=b+1|0;if(e>>>0>f>>>0){Bm(j,e-f|0);break a}if(e>>>0>=f>>>0){break a}f=h+(e<<2)|0;if((f|0)!=(d|0)){while(1){d=d+ -4|0;e=i[d>>2];i[d>>2]=0;if(e){Wb(e)}if((d|0)!=(f|0)){continue}break}}i[a+12>>2]=f}b:{c:{d=i[i[c>>2]+56>>2];d:{if((d|0)>4){break d}d=o(d,12)+a|0;h=d+24|0;a=i[h>>2];k=d+28|0;if((a|0)!=i[k>>2]){i[a>>2]=b;i[h>>2]=a+4;break d}l=d+20|0;d=i[l>>2];f=a-d|0;g=f>>2;e=g+1|0;if(e>>>0>=1073741824){break c}a=f>>1;e=g>>>0<536870911?a>>>0>>0?e:a:1073741823;a=0;e:{if(!e){break e}if(e>>>0>=1073741824){break b}a=ho(e<<2)}g=a+(g<<2)|0;i[g>>2]=b;e=a+(e<<2)|0;g=g+4|0;if((f|0)>=1){hp(a,d,f)}i[l>>2]=a;i[h>>2]=g;i[k>>2]=e;if(!d){break d}bp(d)}d=i[c>>2];i[d+60>>2]=b;a=i[j>>2];i[c>>2]=0;b=a+(b<<2)|0;a=i[b>>2];i[b>>2]=d;if(a){Wb(a)}return}Ho();x()}za(16928);x()}function ob(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,k=0,l=0,m=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}l=b+ -1|0;m=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!m){e=e&l;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;k=h+i[a>>2]|0;if(!i[k>>2]){i[k>>2]=f;f=d;g=e;break e}while(1){f:{e=c;c=i[c>>2];if(!c|j[d+8|0]!=j[c+8|0]){break f}if(j[d+9|0]==j[c+9|0]){continue}}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function Bi(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;e=i[a+8>>2];c=i[a+4>>2];if((e-c|0)/12>>>0>=b>>>0){if(b){b=o(b,12);c=ip(c,0,b-((b+ -12>>>0)%12|0)|0)+b|0}i[a+4>>2]=c;return}a:{b:{c:{h=i[a>>2];f=(c-h|0)/12|0;d=f+b|0;if(d>>>0<357913942){f=o(f,12);e=(e-h|0)/12|0;g=e<<1;e=e>>>0<178956970?g>>>0>>0?d:g:357913941;if(e){if(e>>>0>=357913942){break c}j=ho(o(e,12))}d=f+j|0;b=o(b,12);f=ip(d,0,b-((b+ -12>>>0)%12|0)|0);g=b+f|0;b=o(e,12)+j|0;if((c|0)==(h|0)){break b}while(1){d=d+ -12|0;i[d+8>>2]=0;i[d>>2]=0;i[d+4>>2]=0;c=c+ -12|0;i[d>>2]=i[c>>2];i[d+4>>2]=i[c+4>>2];i[d+8>>2]=i[c+8>>2];i[c+8>>2]=0;i[c>>2]=0;i[c+4>>2]=0;if((c|0)!=(h|0)){continue}break}i[a+8>>2]=b;b=i[a+4>>2];i[a+4>>2]=g;c=i[a>>2];i[a>>2]=d;if((b|0)==(c|0)){break a}while(1){a=b+ -12|0;d=i[a>>2];if(d){i[b+ -8>>2]=d;bp(d)}b=a;if((b|0)!=(c|0)){continue}break}break a}Ho();x()}za(11708);x()}i[a+8>>2]=b;i[a+4>>2]=g;i[a>>2]=f}if(c){bp(c)}}function ec(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;d=F-16|0;F=d;i[a+8>>2]=b;i[a>>2]=0;mf(a+12|0);hf(a+44|0);hf(a- -64|0);hf(a+84|0);i[a+112>>2]=0;i[a+104>>2]=0;i[a+108>>2]=0;a:{if(b){if(b>>>0>=1073741824){break a}e=b<<2;c=ho(e);i[a+104>>2]=c;f=c+e|0;i[a+112>>2]=f;ip(c,0,e);i[a+108>>2]=f}i[a+116>>2]=0;i[a+120>>2]=0;i[a+124>>2]=0;if(b){e=b<<2;c=ho(e);i[a+116>>2]=c;f=c+e|0;i[a+124>>2]=f;ip(c,0,e);i[a+120>>2]=f}i[a+128>>2]=0;i[a+132>>2]=0;i[a+136>>2]=0;if(b){e=b<<2;c=ho(e);i[a+128>>2]=c;f=c+e|0;i[a+136>>2]=f;ip(c,0,e);i[a+132>>2]=f}i[d+8>>2]=0;i[d>>2]=0;i[d+4>>2]=0;g=a+140|0;e=b<<5|1;if(b){c=b<<2;f=ho(c);i[d>>2]=f;h=c+f|0;i[d+8>>2]=h;ip(f,0,c);i[d+4>>2]=h}oc(g,e,d);c=i[d>>2];if(c){i[d+4>>2]=c;bp(c)}i[d+8>>2]=0;i[d>>2]=0;i[d+4>>2]=0;f=a+152|0;if(b){b=b<<2;c=ho(b);i[d>>2]=c;g=b+c|0;i[d+8>>2]=g;ip(c,0,b);i[d+4>>2]=g}oc(f,e,d);b=i[d>>2];if(b){i[d+4>>2]=b;bp(b)}F=d+16|0;return a}Ho();x()}function hc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;d=F-16|0;F=d;i[a+8>>2]=b;i[a>>2]=0;hf(a+12|0);hf(a+32|0);hf(a+52|0);hf(a+72|0);i[a+100>>2]=0;i[a+92>>2]=0;i[a+96>>2]=0;a:{if(b){if(b>>>0>=1073741824){break a}e=b<<2;c=ho(e);i[a+92>>2]=c;f=c+e|0;i[a+100>>2]=f;ip(c,0,e);i[a+96>>2]=f}i[a+104>>2]=0;i[a+108>>2]=0;i[a+112>>2]=0;if(b){e=b<<2;c=ho(e);i[a+104>>2]=c;f=c+e|0;i[a+112>>2]=f;ip(c,0,e);i[a+108>>2]=f}i[a+116>>2]=0;i[a+120>>2]=0;i[a+124>>2]=0;if(b){e=b<<2;c=ho(e);i[a+116>>2]=c;f=c+e|0;i[a+124>>2]=f;ip(c,0,e);i[a+120>>2]=f}i[d+8>>2]=0;i[d>>2]=0;i[d+4>>2]=0;g=a+128|0;e=b<<5|1;if(b){c=b<<2;f=ho(c);i[d>>2]=f;h=c+f|0;i[d+8>>2]=h;ip(f,0,c);i[d+4>>2]=h}oc(g,e,d);c=i[d>>2];if(c){i[d+4>>2]=c;bp(c)}i[d+8>>2]=0;i[d>>2]=0;i[d+4>>2]=0;f=a+140|0;if(b){b=b<<2;c=ho(b);i[d>>2]=c;g=b+c|0;i[d+8>>2]=g;ip(c,0,b);i[d+4>>2]=g}oc(f,e,d);b=i[d>>2];if(b){i[d+4>>2]=b;bp(b)}F=d+16|0;return a}Ho();x()}function $(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=p(0),f=p(0),h=p(0),k=p(0),l=p(0),m=0,n=p(0),o=p(0),r=p(0),s=p(0),u=0;a:{if(i[c+28>>2]!=9|j[c+24|0]!=3){break a}a=i[a+4>>2];if(a+ -2>>>0>28){break a}u=1;m=i[c+80>>2];if(!m){break a}n=p(p(2)/p((1<>2]>>2]+i[c+48>>2]|0;a=i[i[b>>2]>>2]+i[b+48>>2]|0;b=0;while(1){h=p(0);o=p(0);r=p(0);e=p(p(n*p(i[a>>2]))+p(-1));f=p(p(n*p(i[a+4>>2]))+p(-1));l=p(p(p(1)-p(q(e)))-p(q(f)));k=p(t(p(-l),p(0)));s=p(-k);f=p(f+(f>>8;g[c+10|0]=d>>>16;g[c+11|0]=d>>>24;d=(Fp(o),Bp(2));g[c+4|0]=d;g[c+5|0]=d>>>8;g[c+6|0]=d>>>16;g[c+7|0]=d>>>24;d=(Fp(h),Bp(2));g[c|0]=d;g[c+1|0]=d>>>8;g[c+2|0]=d>>>16;g[c+3|0]=d>>>24;c=c+12|0;b=b+1|0;if((m|0)!=(b|0)){continue}break}}return u|0}function td(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0;i[a+4>>2]=b;i[a>>2]=3184;b=c;e=i[b+8>>2];f=i[b+12>>2];h=i[b+16>>2];j=i[b+20>>2];k=i[b>>2];l=i[b+4>>2];i[a+40>>2]=0;g=a+32|0;i[g>>2]=0;i[g+4>>2]=0;i[a+24>>2]=h;i[a+28>>2]=j;i[a+16>>2]=e;i[a+20>>2]=f;i[a+8>>2]=k;i[a+12>>2]=l;a:{b=i[b+28>>2]-i[b+24>>2]|0;if(b){e=b>>2;if(e>>>0>=1073741824){break a}b=ho(b);i[a+32>>2]=b;i[a+36>>2]=b;i[a+40>>2]=b+(e<<2);e=a;f=i[c+24>>2];c=i[c+28>>2]-f|0;if((c|0)>=1){b=hp(b,f,c)+c|0}i[e+36>>2]=b}i[a>>2]=4748;b=i[d+4>>2];i[a+44>>2]=i[d>>2];i[a+48>>2]=b;b=d+8|0;e=i[b+4>>2];i[a+52>>2]=i[b>>2];i[a+56>>2]=e;c=a- -64|0;i[c>>2]=0;i[c+4>>2]=0;i[a+60>>2]=5848;i[a>>2]=5264;c=i[d+4>>2];i[a+72>>2]=i[d>>2];i[a+76>>2]=c;c=i[b+4>>2];i[a+80>>2]=i[b>>2];i[a+84>>2]=c;i[a+104>>2]=1065353216;i[a+108>>2]=-1;i[a+96>>2]=-1;i[a+100>>2]=-1;i[a+88>>2]=1;i[a+92>>2]=-1;i[a+60>>2]=5484;mf(a+112|0);return}Ho();x()}function sd(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0;i[a+4>>2]=b;i[a>>2]=3184;b=c;e=i[b+8>>2];f=i[b+12>>2];h=i[b+16>>2];j=i[b+20>>2];k=i[b>>2];l=i[b+4>>2];i[a+40>>2]=0;g=a+32|0;i[g>>2]=0;i[g+4>>2]=0;i[a+24>>2]=h;i[a+28>>2]=j;i[a+16>>2]=e;i[a+20>>2]=f;i[a+8>>2]=k;i[a+12>>2]=l;a:{b=i[b+28>>2]-i[b+24>>2]|0;if(b){e=b>>2;if(e>>>0>=1073741824){break a}b=ho(b);i[a+32>>2]=b;i[a+36>>2]=b;i[a+40>>2]=b+(e<<2);e=a;f=i[c+24>>2];c=i[c+28>>2]-f|0;if((c|0)>=1){b=hp(b,f,c)+c|0}i[e+36>>2]=b}i[a>>2]=3128;b=i[d+4>>2];i[a+44>>2]=i[d>>2];i[a+48>>2]=b;b=d+8|0;e=i[b+4>>2];i[a+52>>2]=i[b>>2];i[a+56>>2]=e;c=a- -64|0;i[c>>2]=0;i[c+4>>2]=0;i[a+60>>2]=4352;i[a>>2]=3724;c=i[d+4>>2];i[a+72>>2]=i[d>>2];i[a+76>>2]=c;c=i[b+4>>2];i[a+80>>2]=i[b>>2];i[a+84>>2]=c;i[a+104>>2]=1065353216;i[a+108>>2]=-1;i[a+96>>2]=-1;i[a+100>>2]=-1;i[a+88>>2]=1;i[a+92>>2]=-1;i[a+60>>2]=3960;mf(a+112|0);return}Ho();x()}function $d(a){var b=0,c=0,d=0;b=i[a+8>>2];d=i[a>>2];a:{if(j[a+12|0]){b:{c:{d:{e:{if((b|0)==-1){break e}c=b+1|0;b=(c>>>0)%3|0?c:b+ -2|0;if((b|0)==-1|i[i[d>>2]+(b>>>3&536870908)>>2]>>>b&1){break e}b=i[i[i[d+64>>2]+12>>2]+(b<<2)>>2];if((b|0)!=-1){break d}}i[a+8>>2]=-1;break c}c=b+1|0;b=(c>>>0)%3|0?c:b+ -2|0;i[a+8>>2]=b;if((b|0)!=-1){break b}}b=-1;c=i[a+4>>2];f:{if((c|0)==-1){break f}c=c+((c>>>0)%3|0?-1:2)|0;if((c|0)==-1|i[i[d>>2]+(c>>>3&536870908)>>2]>>>c&1){break f}d=i[i[i[d+64>>2]+12>>2]+(c<<2)>>2];if((d|0)==-1){break f}if((d>>>0)%3|0){b=d+ -1|0;break f}b=d+2|0}g[a+12|0]=0;i[a+8>>2]=b;return}if((b|0)!=i[a+4>>2]){break a}i[a+8>>2]=-1;return}c=-1;g:{if((b|0)==-1){break g}b=b+((b>>>0)%3|0?-1:2)|0;if((b|0)==-1|i[i[d>>2]+(b>>>3&536870908)>>2]>>>b&1){break g}b=i[i[i[d+64>>2]+12>>2]+(b<<2)>>2];if((b|0)==-1){break g}if((b>>>0)%3|0){c=b+ -1|0;break g}c=b+2|0}i[a+8>>2]=c}}function $h(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;c=F-16|0;F=c;b=i[i[a+184>>2]+(b<<2)>>2];g[c+15|0]=b;a:{b:{e=i[i[a+4>>2]+44>>2];d=i[e+20>>2];if((d|0)<0?1:(d|0)<=0?l[e+16>>2]<=0:0){ca(e,i[e+4>>2],c+15|0,c+16|0);b=j[c+15|0]}c:{if(b<<24>>24<=-1){e=i[a+68>>2];break c}b=i[i[a+172>>2]+o(b&255,136)>>2];d=I[i[i[a>>2]+40>>2]](a)|0;f=i[a+172>>2]+o(g[c+15|0],136)|0;e=i[f+132>>2];d:{switch(i[i[i[d+56>>2]+84>>2]+(b<<2)>>2]){case 0:break c;case 1:break d;default:break b}}if(!j[f+28|0]){break b}}b=i[i[a+4>>2]+44>>2];g[c+14|0]=0;d=i[b+20>>2];if((d|0)>0?1:(d|0)>=0?l[b+16>>2]>0:0){break a}ca(b,i[b+4>>2],c+14|0,c+15|0);break a}b=i[i[a+4>>2]+44>>2];g[c+13|0]=1;d=i[b+20>>2];if((d|0)>0?1:(d|0)>=0?l[b+16>>2]>0:0){break a}ca(b,i[b+4>>2],c+13|0,c+14|0)}a=i[i[a+4>>2]+44>>2];g[c+12|0]=e;b=i[a+20>>2];if((b|0)<0?1:(b|0)<=0?l[a+16>>2]<=0:0){ca(a,i[a+4>>2],c+12|0,c+13|0)}F=c+16|0;return 1}function kj(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}k=b+ -1|0;l=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!l){e=e&k;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;j=h+i[a>>2]|0;if(!i[j>>2]){i[j>>2]=f;f=d;g=e;break e}while(1){e=c;c=i[c>>2];if(i[d+8>>2]==i[c+8>>2]?c:0){continue}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(11708);x()}function yb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,l=0,m=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}l=b+ -1|0;m=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!m){e=e&l;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;j=h+i[a>>2]|0;if(!i[j>>2]){i[j>>2]=f;f=d;g=e;break e}while(1){e=c;c=i[c>>2];if(k[d+8>>1]==k[c+8>>1]?c:0){continue}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function ab(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}k=b+ -1|0;l=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!l){e=e&k;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;j=h+i[a>>2]|0;if(!i[j>>2]){i[j>>2]=f;f=d;g=e;break e}while(1){e=c;c=i[c>>2];if(i[d+8>>2]==i[c+8>>2]?c:0){continue}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function mb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,k=0,l=0,m=0;a:{b:{if(b){if(b>>>0>=1073741824){break a}c=ho(b<<2);d=i[a>>2];i[a>>2]=c;if(d){bp(d)}i[a+4>>2]=b;d=b>>>0>1?b:1;c=0;while(1){i[i[a>>2]+(c<<2)>>2]=0;c=c+1|0;if((d|0)!=(c|0)){continue}break}f=i[a+8>>2];if(!f){break b}d=a+8|0;g=i[f+4>>2];c=xp(b);c:{if(c>>>0<=1){g=b+ -1&g;break c}if(g>>>0>>0){break c}g=(g>>>0)%(b>>>0)|0}i[i[a>>2]+(g<<2)>>2]=d;d=i[f>>2];if(!d){break b}l=b+ -1|0;m=c>>>0>1;while(1){e=i[d+4>>2];d:{if(!m){e=e&l;break d}if(e>>>0>>0){break d}e=(e>>>0)%(b>>>0)|0}e:{if((e|0)==(g|0)){f=d;break e}c=d;h=e<<2;k=h+i[a>>2]|0;if(!i[k>>2]){i[k>>2]=f;f=d;g=e;break e}while(1){e=c;c=i[c>>2];if(j[d+8|0]==j[c+8|0]?c:0){continue}break}i[f>>2]=c;i[e>>2]=i[i[i[a>>2]+h>>2]>>2];i[i[i[a>>2]+h>>2]>>2]=d}d=i[f>>2];if(d){continue}break}break b}b=i[a>>2];i[a>>2]=0;if(b){bp(b)}i[a+4>>2]=0}return}za(1352);x()}function Dj(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;j=F-16|0;F=j;c=i[a+4>>2];i[a+8>>2]=c;f=i[a+16>>2];i[a+20>>2]=f;a:{b:{g=i[b>>2];m=i[b+4>>2];if((g|0)!=(m|0)){l=a+16|0;b=c;while(1){h=i[g>>2];c:{if(i[a+12>>2]!=(b|0)){i[b>>2]=h;i[a+8>>2]=b+4;break c}k=b-c|0;e=k>>2;d=e+1|0;if(d>>>0>=1073741824){break b}b=k>>1;d=e>>>0<536870911?b>>>0>>0?d:b:1073741823;b=0;d:{if(!d){break d}if(d>>>0>=1073741824){break a}b=ho(d<<2)}e=b+(e<<2)|0;i[e>>2]=h;d=b+(d<<2)|0;e=e+4|0;if((k|0)>=1){hp(b,c,k)}i[a+12>>2]=d;i[a+8>>2]=e;i[a+4>>2]=b;if(!c){break c}bp(c);f=i[l>>2]}b=i[a+20>>2]-f>>2;e:{if((h|0)<(b|0)){break e}i[j+12>>2]=-1;c=h+1|0;if(c>>>0>b>>>0){Gb(l,c-b|0,j+12|0);f=i[l>>2];break e}if(c>>>0>=b>>>0){break e}i[a+20>>2]=(c<<2)+f}b=i[a+8>>2];c=i[a+4>>2];i[(h<<2)+f>>2]=(b-c>>2)+ -1;g=g+4|0;if((m|0)!=(g|0)){continue}break}}F=j+16|0;return}Ho();x()}za(13760);x()}function gf(a,b){var c=0,d=0,e=0,f=0,h=0;f=F-16|0;F=f;c=ho(32);i[f>>2]=c;i[f+4>>2]=17;i[f+8>>2]=-2147483616;g[c+17|0]=0;g[c+16|0]=j[9966];e=j[9962]|j[9963]<<8|(j[9964]<<16|j[9965]<<24);d=j[9958]|j[9959]<<8|(j[9960]<<16|j[9961]<<24);g[c+8|0]=d;g[c+9|0]=d>>>8;g[c+10|0]=d>>>16;g[c+11|0]=d>>>24;g[c+12|0]=e;g[c+13|0]=e>>>8;g[c+14|0]=e>>>16;g[c+15|0]=e>>>24;e=j[9954]|j[9955]<<8|(j[9956]<<16|j[9957]<<24);d=j[9950]|j[9951]<<8|(j[9952]<<16|j[9953]<<24);g[c|0]=d;g[c+1|0]=d>>>8;g[c+2|0]=d>>>16;g[c+3|0]=d>>>24;g[c+4|0]=e;g[c+5|0]=e>>>8;g[c+6|0]=e>>>16;g[c+7|0]=e>>>24;e=b+16|0;d=i[e>>2];a:{if(!d){break a}c=e;while(1){h=i[d+16>>2]<(a|0);c=h?c:d;d=i[(h<<2)+d>>2];if(d){continue}break}if((c|0)==(e|0)|i[c+16>>2]>(a|0)){break a}a=c+20|0;if(!Sb(a,f)){break a}b=a}a=$j(b,f,-1);if(g[f+11|0]<=-1){bp(i[f>>2])}if((a|0)!=-1){F=f+16|0;return a>>>0>6?-2:a}F=f+16|0;return-1}function oa(a,b,c,d){var e=0,f=0,h=0,k=0,l=0,n=0,r=0,s=0,t=0,v=0,w=0,x=p(0),y=0,z=0,A=p(0),B=0,C=0,D=0,E=0;r=F-16|0;F=r;e=i[a+4>>2];h=i[d+48>>2];l=i[i[d>>2]>>2];k=g[b+24|0];f=dk(r+8|0);ek(f,m[a+20>>2],-1<>2];e=i[c+4>>2];if((n|0)!=(e|0)){y=h+l|0;c=e-n|0;h=(c|0)>-1?c:-1;e=n-e|0;c=o((h|0)<1?h:1,((e|0)>(c|0)?e:c)>>>2|0);z=c>>>0>1?c:1;A=m[f>>2];B=i[b>>2];e=i[b+48>>2];C=i[b+68>>2];h=i[b+44>>2];l=i[b+40>>2];D=l;c=0;E=j[b+84|0];while(1){b=i[(s<<2)+n>>2];f=d;t=i[B>>2];v=l;w=h;if(!E){b=i[(b<<2)+C>>2]}b=up(v,w,b,0)+e|0;v=hp(f,b+t|0,D);if((k|0)>=1){w=i[a+8>>2];b=0;while(1){t=y+(c<<2)|0;f=b<<2;x=p(u(p(p(A*p(m[f+v>>2]-m[f+w>>2]))+p(.5))));a:{if(p(q(x))>2]=f;c=c+1|0;b=b+1|0;if((k|0)!=(b|0)){continue}break}}s=s+1|0;if((z|0)!=(s|0)){continue}break}}bp(d);F=r+16|0}function zj(a,b){var c=0,d=0,e=0,f=0,j=0;d=F-16|0;F=d;c=i[b+44>>2];e=i[c+20>>2];if((e|0)<0?1:(e|0)<=0?l[c+16>>2]<=0:0){ca(c,i[c+4>>2],13688,13693)}c=I[i[i[b>>2]+8>>2]](b)|0;g[d+15|0]=c;g[d+14|0]=2;g[d+13|0]=c&255?2:3;c=i[b+44>>2];e=i[c+20>>2];a:{if((e|0)>0?1:(e|0)>=0?l[c+16>>2]>0:0){break a}ca(c,i[c+4>>2],d+14|0,d+15|0);c=i[b+44>>2];e=i[c+20>>2];if((e|0)>0?1:(e|0)>=0?l[c+16>>2]>0:0){break a}ca(c,i[c+4>>2],d+13|0,d+14|0);c=i[b+44>>2];e=i[c+20>>2];if((e|0)>0?1:(e|0)>=0?l[c+16>>2]>0:0){break a}ca(c,i[c+4>>2],d+15|0,d+16|0);c=i[b+44>>2]}f=d,j=I[i[i[b>>2]+12>>2]](b)|0,g[f+12|0]=j;e=i[c+20>>2];if((e|0)<0?1:(e|0)<=0?l[c+16>>2]<=0:0){ca(c,i[c+4>>2],d+12|0,d+13|0)}h[d+10>>1]=(i[i[b+4>>2]+4>>2]!=0)<<15;b=i[b+44>>2];c=i[b+20>>2];if((c|0)<0?1:(c|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],d+10|0,d+12|0)}i[a>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0;F=d+16|0}function ep(a){var b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;a:{b:{c:{d:{Ep(+a);b=Bp(1)|0;c=Bp(0)|0;if((b|0)>0?1:(b|0)>=0?c>>>0>=0:0){e=b;if(b>>>0>1048575){break d}}if(!(b&2147483647|c)){return-1/(a*a)}if((b|0)>-1){break c}return(a-a)/0}if(e>>>0>2146435071){break a}b=1072693248;j=-1023;if((e|0)!=1072693248){b=e;break b}if(c){break b}return 0}Ep(+(a*0x40000000000000));b=Bp(1)|0;c=Bp(0)|0;j=-1077}Cp(0,c|0);c=b+614242|0;Cp(1,(c&1048575)+1072079006|0);d=+Dp()+ -1;f=d*(d*.5);g=d/(d+2);h=g*g;a=h*h;Ep(+(d-f));b=Bp(1)|0;Bp(0)|0;Cp(0,0);Cp(1,b|0);i=+Dp();k=i*1.4426950407214463;l=+((c>>>20|0)+j|0);m=k+l;a=d-i-f+g*(f+(a*(a*(a*.15313837699209373+.22222198432149784)+.3999999999940942)+h*(a*(a*(a*.14798198605116586+.1818357216161805)+.2857142874366239)+.6666666666666735)));a=m+(k+(l-m)+(a*1.4426950407214463+(a+i)*1.6751713164886512e-10))}return a}function ca(a,b,c,d){var e=0,f=0,h=0,k=0,l=0,m=0,n=0;l=d-c|0;if((l|0)<1){return}a:{e=i[a+8>>2];m=i[a+4>>2];if((l|0)<=(e-m|0)){k=m-b|0;if((l|0)<=(k|0)){e=m;h=d;break a}e=m;h=c+k|0;if((h|0)!=(d|0)){f=h;while(1){g[e|0]=j[f|0];e=e+1|0;f=f+1|0;if((f|0)!=(d|0)){continue}break}}i[a+4>>2]=e;if((k|0)>=1){break a}return}h=i[a>>2];d=(m-h|0)+l|0;if((d|0)>-1){k=b-h|0;n=k;f=e-h|0;e=f<<1;e=f>>>0<1073741823?e>>>0>>0?d:e:2147483647;d=0;b:{if(!e){break b}d=ho(e)}c=hp(n+d|0,c,l);if((k|0)>=1){hp(d,h,k)}e=d+e|0;c=c+l|0;f=m-b|0;if((f|0)>=1){c=hp(c,b,f)+f|0}i[a+8>>2]=e;i[a+4>>2]=c;i[a>>2]=d;if(h){bp(h)}return}Ho();x()}k=e-(b+l|0)|0;f=e;d=e-l|0;if(d>>>0>>0){while(1){g[f|0]=j[d|0];f=f+1|0;d=d+1|0;if(d>>>0>>0){continue}break}}i[a+4>>2]=f;if(k){jp(e-k|0,b,k)}if((c|0)==(h|0)){return}f=b;while(1){g[f|0]=j[c|0];f=f+1|0;c=c+1|0;if((h|0)!=(c|0)){continue}break}}function fh(a,b){var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0;j=F-32|0;F=j;h=i[b>>2]+i[a+40>>2]|0;c=i[a+32>>2];d=c+ -4194304|0;a:{if(d>>>0<=63){c=a+28|0;g[i[a+24>>2]+i[c>>2]|0]=d;d=i[c>>2]+1|0;break a}if(d>>>0<=16383){d=a+28|0;e=i[a+24>>2]+i[d>>2]|0;c=c+ -4177920|0;g[e|0]=c;g[e+1|0]=c>>>8;d=i[d>>2]+2|0;break a}if(d>>>0<=4194303){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+4194304|0;g[d+2|0]=c>>>16;g[d+1|0]=c>>>8;g[d|0]=c;d=i[e>>2]+3|0;break a}if(d>>>0<=1073741823){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+ -1077936128|0;g[d|0]=c;g[d+1|0]=c>>>8;g[d+2|0]=c>>>16;g[d+3|0]=c>>>24;d=i[e>>2]+4|0;break a}d=i[a+28>>2]}f=d>>31;e=f;c=d;f=Tj(j);Jg(c,e,f);k=i[f+4>>2]-i[f>>2]|0;jp(k+h|0,h,c);hp(h,i[f>>2],k);d=i[a+44>>2];h=b;l=i[a+40>>2];a=e;b=c+k|0;if(b>>>0>>0){a=a+1|0}c=b;b=l+c|0;Uj(h,b);a=i[f+12>>2];i[f+12>>2]=0;if(a){bp(a)}a=i[f>>2];if(a){i[f+4>>2]=a;bp(a)}F=j+32|0}function dh(a,b){var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0;j=F-32|0;F=j;h=i[b>>2]+i[a+40>>2]|0;c=i[a+32>>2];d=c+ -2097152|0;a:{if(d>>>0<=63){c=a+28|0;g[i[a+24>>2]+i[c>>2]|0]=d;d=i[c>>2]+1|0;break a}if(d>>>0<=16383){d=a+28|0;e=i[a+24>>2]+i[d>>2]|0;c=c+ -2080768|0;g[e|0]=c;g[e+1|0]=c>>>8;d=i[d>>2]+2|0;break a}if(d>>>0<=4194303){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+6291456|0;g[d+2|0]=c>>>16;g[d+1|0]=c>>>8;g[d|0]=c;d=i[e>>2]+3|0;break a}if(d>>>0<=1073741823){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+ -1075838976|0;g[d|0]=c;g[d+1|0]=c>>>8;g[d+2|0]=c>>>16;g[d+3|0]=c>>>24;d=i[e>>2]+4|0;break a}d=i[a+28>>2]}f=d>>31;e=f;c=d;f=Tj(j);Jg(c,e,f);k=i[f+4>>2]-i[f>>2]|0;jp(k+h|0,h,c);hp(h,i[f>>2],k);d=i[a+44>>2];h=b;l=i[a+40>>2];a=e;b=c+k|0;if(b>>>0>>0){a=a+1|0}c=b;b=l+c|0;Uj(h,b);a=i[f+12>>2];i[f+12>>2]=0;if(a){bp(a)}a=i[f>>2];if(a){i[f+4>>2]=a;bp(a)}F=j+32|0}function bh(a,b){var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0;j=F-32|0;F=j;h=i[b>>2]+i[a+40>>2]|0;c=i[a+32>>2];d=c+ -1048576|0;a:{if(d>>>0<=63){c=a+28|0;g[i[a+24>>2]+i[c>>2]|0]=d;d=i[c>>2]+1|0;break a}if(d>>>0<=16383){d=a+28|0;e=i[a+24>>2]+i[d>>2]|0;c=c+ -1032192|0;g[e|0]=c;g[e+1|0]=c>>>8;d=i[d>>2]+2|0;break a}if(d>>>0<=4194303){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+7340032|0;g[d+2|0]=c>>>16;g[d+1|0]=c>>>8;g[d|0]=c;d=i[e>>2]+3|0;break a}if(d>>>0<=1073741823){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+ -1074790400|0;g[d|0]=c;g[d+1|0]=c>>>8;g[d+2|0]=c>>>16;g[d+3|0]=c>>>24;d=i[e>>2]+4|0;break a}d=i[a+28>>2]}f=d>>31;e=f;c=d;f=Tj(j);Jg(c,e,f);k=i[f+4>>2]-i[f>>2]|0;jp(k+h|0,h,c);hp(h,i[f>>2],k);d=i[a+44>>2];h=b;l=i[a+40>>2];a=e;b=c+k|0;if(b>>>0>>0){a=a+1|0}c=b;b=l+c|0;Uj(h,b);a=i[f+12>>2];i[f+12>>2]=0;if(a){bp(a)}a=i[f>>2];if(a){i[f+4>>2]=a;bp(a)}F=j+32|0}function Xg(a,b){var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0;j=F-32|0;F=j;h=i[b>>2]+i[a+40>>2]|0;c=i[a+32>>2];d=c+ -32768|0;a:{if(d>>>0<=63){c=a+28|0;g[i[a+24>>2]+i[c>>2]|0]=d;d=i[c>>2]+1|0;break a}if(d>>>0<=16383){d=a+28|0;e=i[a+24>>2]+i[d>>2]|0;c=c+ -16384|0;g[e|0]=c;g[e+1|0]=c>>>8;d=i[d>>2]+2|0;break a}if(d>>>0<=4194303){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+8355840|0;g[d+2|0]=c>>>16;g[d+1|0]=c>>>8;g[d|0]=c;d=i[e>>2]+3|0;break a}if(d>>>0<=1073741823){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+ -1073774592|0;g[d|0]=c;g[d+1|0]=c>>>8;g[d+2|0]=c>>>16;g[d+3|0]=c>>>24;d=i[e>>2]+4|0;break a}d=i[a+28>>2]}f=d>>31;e=f;c=d;f=Tj(j);Jg(c,e,f);k=i[f+4>>2]-i[f>>2]|0;jp(k+h|0,h,c);hp(h,i[f>>2],k);d=i[a+44>>2];h=b;l=i[a+40>>2];a=e;b=c+k|0;if(b>>>0>>0){a=a+1|0}c=b;b=l+c|0;Uj(h,b);a=i[f+12>>2];i[f+12>>2]=0;if(a){bp(a)}a=i[f>>2];if(a){i[f+4>>2]=a;bp(a)}F=j+32|0} + + + +function Zg(a,b){var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0;j=F-32|0;F=j;h=i[b>>2]+i[a+40>>2]|0;c=i[a+32>>2];d=c+ -131072|0;a:{if(d>>>0<=63){c=a+28|0;g[i[a+24>>2]+i[c>>2]|0]=d;d=i[c>>2]+1|0;break a}if(d>>>0<=16383){d=a+28|0;e=i[a+24>>2]+i[d>>2]|0;c=c+ -114688|0;g[e|0]=c;g[e+1|0]=c>>>8;d=i[d>>2]+2|0;break a}if(d>>>0<=4194303){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+8257536|0;g[d+2|0]=c>>>16;g[d+1|0]=c>>>8;g[d|0]=c;d=i[e>>2]+3|0;break a}if(d>>>0<=1073741823){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+ -1073872896|0;g[d|0]=c;g[d+1|0]=c>>>8;g[d+2|0]=c>>>16;g[d+3|0]=c>>>24;d=i[e>>2]+4|0;break a}d=i[a+28>>2]}f=d>>31;e=f;c=d;f=Tj(j);Jg(c,e,f);k=i[f+4>>2]-i[f>>2]|0;jp(k+h|0,h,c);hp(h,i[f>>2],k);d=i[a+44>>2];h=b;l=i[a+40>>2];a=e;b=c+k|0;if(b>>>0>>0){a=a+1|0}c=b;b=l+c|0;Uj(h,b);a=i[f+12>>2];i[f+12>>2]=0;if(a){bp(a)}a=i[f>>2];if(a){i[f+4>>2]=a;bp(a)}F=j+32|0}function $g(a,b){var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0;j=F-32|0;F=j;h=i[b>>2]+i[a+40>>2]|0;c=i[a+32>>2];d=c+ -262144|0;a:{if(d>>>0<=63){c=a+28|0;g[i[a+24>>2]+i[c>>2]|0]=d;d=i[c>>2]+1|0;break a}if(d>>>0<=16383){d=a+28|0;e=i[a+24>>2]+i[d>>2]|0;c=c+ -245760|0;g[e|0]=c;g[e+1|0]=c>>>8;d=i[d>>2]+2|0;break a}if(d>>>0<=4194303){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+8126464|0;g[d+2|0]=c>>>16;g[d+1|0]=c>>>8;g[d|0]=c;d=i[e>>2]+3|0;break a}if(d>>>0<=1073741823){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+ -1074003968|0;g[d|0]=c;g[d+1|0]=c>>>8;g[d+2|0]=c>>>16;g[d+3|0]=c>>>24;d=i[e>>2]+4|0;break a}d=i[a+28>>2]}f=d>>31;e=f;c=d;f=Tj(j);Jg(c,e,f);k=i[f+4>>2]-i[f>>2]|0;jp(k+h|0,h,c);hp(h,i[f>>2],k);d=i[a+44>>2];h=b;l=i[a+40>>2];a=e;b=c+k|0;if(b>>>0>>0){a=a+1|0}c=b;b=l+c|0;Uj(h,b);a=i[f+12>>2];i[f+12>>2]=0;if(a){bp(a)}a=i[f>>2];if(a){i[f+4>>2]=a;bp(a)}F=j+32|0}function og(a,b){var c=0,d=0,e=0,f=0,h=0,j=0,k=0,l=0;j=F-32|0;F=j;h=i[b>>2]+i[a+40>>2]|0;c=i[a+32>>2];d=c+ -16384|0;a:{if(d>>>0<=63){c=a+28|0;g[i[a+24>>2]+i[c>>2]|0]=d;c=i[c>>2]+1|0;break a}if(d>>>0<=16383){d=a+28|0;e=i[a+24>>2]+i[d>>2]|0;g[e|0]=c;g[e+1|0]=c>>>8;c=i[d>>2]+2|0;break a}if(d>>>0<=4194303){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+8372224|0;g[d+2|0]=c>>>16;g[d+1|0]=c>>>8;g[d|0]=c;c=i[e>>2]+3|0;break a}if(d>>>0<=1073741823){e=a+28|0;d=i[a+24>>2]+i[e>>2]|0;c=c+ -1073758208|0;g[d|0]=c;g[d+1|0]=c>>>8;g[d+2|0]=c>>>16;g[d+3|0]=c>>>24;c=i[e>>2]+4|0;break a}c=i[a+28>>2]}f=c>>31;e=f;d=c;f=Tj(j);Jg(d,e,f);k=i[f+4>>2]-i[f>>2]|0;jp(k+h|0,h,d);hp(h,i[f>>2],k);c=i[a+44>>2];h=b;l=i[a+40>>2];a=e;b=d+k|0;if(b>>>0>>0){a=a+1|0}d=b;b=l+d|0;Uj(h,b);a=i[f+12>>2];i[f+12>>2]=0;if(a){bp(a)}a=i[f>>2];if(a){i[f+4>>2]=a;bp(a)}F=j+32|0}function Ig(a,b){var c=0,d=0,e=0,f=0,h=0,j=0,k=0,m=0,n=0,o=0;d=F-16|0;F=d;Jb(i[a+12>>2],b);a:{if(!i[a+12>>2]){break a}m=d+14|0;n=d+15|0;o=d+16|0;while(1){b:{c:{k=i[a>>2];f=i[k+(h<<3)>>2];if(f>>>0>=64){c=1;j=1;e=1;if(f>>>0<16384){break c}if(f>>>0<=4194303){j=2;e=2;break c}break a}j=1;c=0;e=0;if(f){break c}while(1){d:{if(i[(k+(c+h<<3)|0)+8>>2]){f=c;break d}f=63;c=c+1|0;if((c|0)!=63){continue}}break}g[d+15|0]=f<<2|3;c=i[b+20>>2];if((c|0)<0?1:(c|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],d+15|0,o)}h=f+h|0;break b}g[d+14|0]=f<<2|e;e=i[b+20>>2];if((e|0)<0?1:(e|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],d+14|0,n)}e=0;if(!c){break b}while(1){e=e+1|0;g[d+13|0]=f>>>(e<<3)+ -2;c=i[b+20>>2];if((c|0)<0?1:(c|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],d+13|0,m)}if((e|0)!=(j|0)){continue}break}}h=h+1|0;if(h>>>0>2]){continue}break}}F=d+16|0}function gm(a,b,c){var d=0,e=0,f=0,g=0;Jb(i[c+8>>2],b);d=i[c>>2];g=c+4|0;if((d|0)!=(g|0)){while(1){f=d;if(!hm(b,d+16|0)){return}d=i[f+32>>2]-i[f+28>>2]|0;Jb(d,b);e=i[b+20>>2];if((e|0)<0?1:(e|0)<=0?l[b+16>>2]<=0:0){e=i[f+28>>2];ca(b,i[b+4>>2],e,e+d|0)}e=i[f+4>>2];a:{if(!e){d=i[f+8>>2];if((f|0)==i[d>>2]){break a}e=f+8|0;while(1){f=i[e>>2];e=f+8|0;d=i[f+8>>2];if((f|0)!=i[d>>2]){continue}break}break a}while(1){d=e;e=i[d>>2];if(e){continue}break}}if((d|0)!=(g|0)){continue}break}}Jb(i[c+20>>2],b);d=i[c+12>>2];f=c+16|0;if((d|0)==(f|0)){return}while(1){c=d;if(!hm(b,d+16|0)){return}gm(a,b,i[c+28>>2]);e=i[c+4>>2];b:{if(!e){d=i[c+8>>2];if((c|0)==i[d>>2]){break b}e=c+8|0;while(1){c=i[e>>2];e=c+8|0;d=i[c+8>>2];if((c|0)!=i[d>>2]){continue}break}break b}while(1){d=e;e=i[d>>2];if(e){continue}break}}if((d|0)!=(f|0)){continue}break}}function Ej(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0;a:{b=i[a+4>>2];b=i[b+12>>2]-i[b+8>>2]|0;if((b|0)<=0){b=b>>2;break a}while(1){if(I[i[i[a>>2]+36>>2]](a,c)|0){c=c+1|0;b=i[a+4>>2];b=i[b+12>>2]-i[b+8>>2]>>2;if((c|0)<(b|0)){continue}break a}break}return 0}g=a+20|0;d=i[a+20>>2];c=i[a+24>>2]-d>>2;b:{if(b>>>0>c>>>0){Bd(g,b-c|0);break b}if(b>>>0>=c>>>0){break b}i[a+24>>2]=d+(b<<2)}b=i[a+8>>2];c=i[a+12>>2];if((b|0)==(c|0)){return 1}a=c-b|0;d=(a|0)>-1?a:-1;c=b-c|0;a=o((d|0)<1?d:1,((c|0)>(a|0)?c:a)>>>2|0);h=a>>>0>1?a:1;c=0;while(1){a=i[(c<<2)+b>>2];e=i[a+8>>2];d=i[a+4>>2];if((e|0)!=(d|0)){a=e-d|0;f=(a|0)>-1?a:-1;e=d-e|0;a=o((f|0)<1?f:1,((e|0)>(a|0)?e:a)>>>2|0);e=a>>>0>1?a:1;f=i[g>>2];a=0;while(1){i[f+(i[d+(a<<2)>>2]<<2)>>2]=c;a=a+1|0;if((e|0)!=(a|0)){continue}break}}c=c+1|0;if((h|0)!=(c|0)){continue}break}return 1}function Aj(a){a=a|0;var b=0,c=0,d=0,e=0,f=0;e=F-16|0;F=e;a:{b:{if(!(I[i[i[a>>2]+32>>2]](a)|0)){break b}c=i[a+44>>2];d=i[a+12>>2];b=i[a+8>>2];g[e+15|0]=d-b>>>2;f=i[c+20>>2];if((f|0)<0?1:(f|0)<=0?l[c+16>>2]<=0:0){ca(c,i[c+4>>2],e+15|0,e+16|0);d=i[a+12>>2];b=i[a+8>>2]}if((b|0)!=(d|0)){while(1){c=i[b>>2];if(!(I[i[i[c>>2]+8>>2]](c,a,i[a+4>>2])|0)){break b}b=b+4|0;if((d|0)!=(b|0)){continue}break}}if(!Bj(a)){break b}b=i[a+32>>2];c=i[a+36>>2];if((b|0)==(c|0)){break a}while(1){if(!(I[i[i[a>>2]+40>>2]](a,i[b>>2])|0)){break b}b=b+4|0;if((c|0)!=(b|0)){continue}break}b=i[a+32>>2];c=i[a+36>>2];if((b|0)==(c|0)){break a}while(1){d=i[i[a+8>>2]+(i[b>>2]<<2)>>2];if(!(I[i[i[d>>2]+12>>2]](d,i[a+44>>2])|0)){break b}b=b+4|0;if((c|0)!=(b|0)){continue}break}break a}F=e+16|0;return 0}a=I[i[i[a>>2]+44>>2]](a)|0;F=e+16|0;return a|0}function Mh(a){i[a+4>>2]=0;i[a+8>>2]=0;i[a>>2]=11192;i[a+72>>2]=0;i[a+76>>2]=0;i[a+12>>2]=0;i[a+16>>2]=0;i[a+20>>2]=0;i[a+24>>2]=0;i[a+28>>2]=0;i[a+32>>2]=0;i[a+36>>2]=0;i[a+40>>2]=0;i[a+44>>2]=0;i[a+48>>2]=0;i[a+52>>2]=0;i[a+56>>2]=0;i[a+60>>2]=0;i[a+64>>2]=0;i[a+80>>2]=0;i[a+84>>2]=0;i[a+88>>2]=0;i[a+92>>2]=0;i[a+96>>2]=0;i[a+100>>2]=0;i[a+104>>2]=0;i[a+108>>2]=0;i[a+112>>2]=0;i[a+116>>2]=0;i[a+120>>2]=0;i[a+124>>2]=0;i[a+128>>2]=0;i[a+132>>2]=0;i[a+136>>2]=1065353216;i[a+156>>2]=0;i[a+160>>2]=0;i[a+148>>2]=0;i[a+152>>2]=0;i[a+140>>2]=0;i[a+144>>2]=0;i[a+168>>2]=0;i[a+172>>2]=0;i[a+164>>2]=-1;i[a+176>>2]=0;i[a+180>>2]=0;i[a+184>>2]=0;i[a+188>>2]=0;i[a+192>>2]=0;mf(a+200|0);Tj(a+232|0);g[a+288|0]=0;i[a+280>>2]=0;i[a+284>>2]=0;i[a+272>>2]=0;i[a+276>>2]=0;i[a+264>>2]=0;i[a+268>>2]=0}function lf(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;e=F-16|0;F=e;a:{b:{d=i[a+4>>2];c:{if((d|0)!=i[a+8>>2]){i[d>>2]=i[a+12>>2];c=d+4|0;i[a+4>>2]=c;break c}g=i[a>>2];h=d-g|0;f=h>>2;c=f+1|0;if(c>>>0>=1073741824){break b}j=h>>1;c=f>>>0<536870911?j>>>0>>0?c:j:1073741823;d=0;d:{if(!c){break d}if(c>>>0>=1073741824){break a}d=ho(c<<2)}f=d+(f<<2)|0;i[f>>2]=i[a+12>>2];j=d+(c<<2)|0;c=f+4|0;if((h|0)>=1){hp(d,g,h)}i[a+8>>2]=j;i[a+4>>2]=c;i[a>>2]=d;if(!g){break c}bp(g);c=i[a+4>>2]}d=i[a>>2];i[e+12>>2]=c-d;c=i[b+20>>2];e:{if((c|0)>0?1:(c|0)>=0?l[b+16>>2]>0:0){break e}ca(b,i[b+4>>2],e+12|0,e+16|0);d=i[a>>2];c=i[b+20>>2];if((c|0)>0?1:(c|0)>=0?l[b+16>>2]>0:0){break e}ca(b,i[b+4>>2],d,i[e+12>>2]+d|0);d=i[a>>2]}i[a+12>>2]=0;i[a+16>>2]=0;i[a+4>>2]=d;F=e+16|0;return}Ho();x()}za(9968);x()}function qf(a,b){var c=0,d=0,e=0,f=0;d=i[a>>2];a:{if(b){b=d;c=b;e=i[b+12>>2];d=i[b+8>>2]+1|0;if(d>>>0<1){e=e+1|0}i[c+8>>2]=d;i[b+12>>2]=e;b=i[a+28>>2];i[a+24>>2]=i[a+24>>2]|1<>2];d=i[b>>2]+1|0;if(d>>>0<1){e=e+1|0}i[c>>2]=d;i[b+4>>2]=e;b=i[a+28>>2]}b=b+1|0;i[a+28>>2]=b;b:{c:{if((b|0)==32){b=i[a+16>>2];d:{if((b|0)!=i[a+20>>2]){i[b>>2]=i[a+24>>2];i[a+16>>2]=b+4;break d}d=i[a+12>>2];e=b-d|0;f=e>>2;c=f+1|0;if(c>>>0>=1073741824){break c}b=e>>1;c=f>>>0<536870911?b>>>0>>0?c:b:1073741823;b=0;e:{if(!c){break e}if(c>>>0>=1073741824){break b}b=ho(c<<2)}f=b+(f<<2)|0;i[f>>2]=i[a+24>>2];c=b+(c<<2)|0;f=f+4|0;if((e|0)>=1){hp(b,d,e)}i[a+20>>2]=c;i[a+16>>2]=f;i[a+12>>2]=b;if(!d){break d}bp(d)}i[a+24>>2]=0;i[a+28>>2]=0}return}Ho();x()}za(10036);x()}function Tc(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0,h=0;e=F-16|0;F=e;g=a+36|0;b=i[a+8>>2]-i[a+4>>2]>>2;c=i[a+40>>2];f=i[a+36>>2];d=c-f>>2;a:{if(b>>>0>d>>>0){Uc(g,b-d|0);break a}if(b>>>0>=d>>>0){break a}d=f+(b<<2)|0;if((d|0)!=(c|0)){while(1){c=c+ -4|0;b=i[c>>2];i[c>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}if((c|0)!=(d|0)){continue}break}}i[a+40>>2]=d}if(i[a+8>>2]==i[a+4>>2]){c=1}else{c=0;while(1){I[i[i[a>>2]+56>>2]](e+8|0,a,c);b=i[a+36>>2];d=i[e+8>>2];i[e+8>>2]=0;f=c<<2;h=f+b|0;b=i[h>>2];i[h>>2]=d;if(b){I[i[i[b>>2]+4>>2]](b)}b=i[e+8>>2];i[e+8>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}b=i[f+i[g>>2]>>2];if(b){if(!(!(i[i[a+48>>2]+(c>>>3&536870908)>>2]>>>c&1)|c>>>0>=l[a+52>>2])){Gc(b)}c=c+1|0;if(c>>>0>2]-i[a+4>>2]>>2>>>0){continue}}break}c=(b|0)!=0}F=e+16|0;return c|0}function Ib(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0;d=F-16|0;F=d;e=a+8|0;Jb(i[e>>2]-i[a+4>>2]>>2,b);c=i[a+4>>2];if((c|0)!=i[e>>2]){k=d+13|0;l=d+14|0;m=d+15|0;n=d+16|0;while(1){f=i[i[i[a+32>>2]+8>>2]+(i[(h<<2)+c>>2]<<2)>>2];g[d+15|0]=i[f+56>>2];c=i[b+20>>2];e=i[b+16>>2];if((c|0)<0?1:(c|0)<=0?e>>>0<=0:0){ca(b,i[b+4>>2],d+15|0,n);e=i[b+16>>2];c=i[b+20>>2]}g[d+14|0]=i[f+28>>2];if((c|0)<0?1:(c|0)<=0?e>>>0<=0:0){ca(b,i[b+4>>2],d+14|0,m);e=i[b+16>>2];c=i[b+20>>2]}g[d+13|0]=j[f+24|0];if((c|0)<0?1:(c|0)<=0?e>>>0<=0:0){ca(b,i[b+4>>2],d+13|0,l);e=i[b+16>>2];c=i[b+20>>2]}g[d+12|0]=j[f+32|0];if((c|0)<0?1:(c|0)<=0?e>>>0<=0:0){ca(b,i[b+4>>2],d+12|0,k)}Jb(i[f+60>>2],b);h=h+1|0;c=i[a+4>>2];if(h>>>0>2]-c>>2>>>0){continue}break}}F=d+16|0;return 1}function me(a){var b=0,c=0,d=0;b=i[a+8>>2];d=i[a>>2];a:{if(j[a+12|0]){b:{c:{d:{e:{if((b|0)==-1){break e}c=b+1|0;b=(c>>>0)%3|0?c:b+ -2|0;if((b|0)==-1){break e}b=i[i[d+12>>2]+(b<<2)>>2];if((b|0)!=-1){break d}}i[a+8>>2]=-1;break c}c=b+1|0;b=(c>>>0)%3|0?c:b+ -2|0;i[a+8>>2]=b;if((b|0)!=-1){break b}}c=i[a+4>>2];b=-1;f:{if((c|0)==-1){break f}c=c+((c>>>0)%3|0?-1:2)|0;b=-1;if((c|0)==-1){break f}c=i[i[d+12>>2]+(c<<2)>>2];b=-1;if((c|0)==-1){break f}b=c+ -1|0;if((c>>>0)%3|0){break f}b=c+2|0}g[a+12|0]=0;i[a+8>>2]=b;return}if((b|0)!=i[a+4>>2]){break a}i[a+8>>2]=-1;return}c=-1;g:{if((b|0)==-1){break g}b=b+((b>>>0)%3|0?-1:2)|0;c=-1;if((b|0)==-1){break g}b=i[i[d+12>>2]+(b<<2)>>2];c=-1;if((b|0)==-1){break g}c=b+ -1|0;if((b>>>0)%3|0){break g}c=b+2|0}i[a+8>>2]=c}}function jp(a,b,c){var d=0;a:{if((a|0)==(b|0)){break a}if((b-a|0)-c>>>0<=0-(c<<1)>>>0){return hp(a,b,c)}d=(a^b)&3;b:{c:{if(a>>>0>>0){if(d){d=a;break b}if(!(a&3)){d=a;break c}d=a;while(1){if(!c){break a}g[d|0]=j[b|0];b=b+1|0;c=c+ -1|0;d=d+1|0;if(d&3){continue}break}break c}d:{if(d){break d}if(a+c&3){while(1){if(!c){break a}c=c+ -1|0;d=c+a|0;g[d|0]=j[b+c|0];if(d&3){continue}break}}if(c>>>0<=3){break d}while(1){c=c+ -4|0;i[c+a>>2]=i[b+c>>2];if(c>>>0>3){continue}break}}if(!c){break a}while(1){c=c+ -1|0;g[c+a|0]=j[b+c|0];if(c){continue}break}break a}if(c>>>0<=3){break b}while(1){i[d>>2]=i[b>>2];b=b+4|0;d=d+4|0;c=c+ -4|0;if(c>>>0>3){continue}break}}if(!c){break a}while(1){g[d|0]=j[b|0];d=d+1|0;b=b+1|0;c=c+ -1|0;if(c){continue}break}}return a}function Gm(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;e=i[c>>2];l=i[c+4>>2];if((e|0)!=(l|0)){p=i[b>>2];while(1){c=i[e>>2];m=c<<2;f=i[m+p>>2];if(f>>>0>=d>>>0){d=i[a+12>>2];h=i[a+8>>2];b=d-h|0;if((b|0)>=1){d=h-d|0;b=((d|0)>(b|0)?d:b)>>>2|0;q=b>>>0>1?b:1;d=0;while(1){b=c;n=i[(d<<2)+h>>2];o=i[n+68>>2];r=o+(f<<2)|0;if(!j[n+84|0]){b=i[m+o>>2]}i[r>>2]=b;d=d+1|0;if((q|0)!=(d|0)){continue}break}}d=f+1|0}e=e+4|0;if((l|0)!=(e|0)){continue}break}}e=i[a+12>>2];b=i[a+8>>2];if((e-b|0)>0){while(1){c=i[(k<<2)+b>>2];g[c+84|0]=0;h=i[c+68>>2];f=i[c+72>>2]-h>>2;a:{if(d>>>0>f>>>0){Xa(c+68|0,d-f|0,16924);b=i[a+8>>2];e=i[a+12>>2];break a}if(d>>>0>=f>>>0){break a}i[c+72>>2]=h+(d<<2)}k=k+1|0;if((k|0)>2){continue}break}}}function Uc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;d=i[a+8>>2];c=i[a+4>>2];if(d-c>>2>>>0>=b>>>0){if(b){b=b<<2;c=ip(c,0,b)+b|0}i[a+4>>2]=c;return}a:{b:{c:{g=i[a>>2];f=c-g>>2;e=f+b|0;if(e>>>0<1073741824){d=d-g|0;h=d>>1;e=d>>2>>>0<536870911?h>>>0>>0?e:h:1073741823;if(e){if(e>>>0>=1073741824){break c}j=ho(e<<2)}d=(f<<2)+j|0;f=b<<2;b=ip(d,0,f);f=b+f|0;e=(e<<2)+j|0;if((c|0)==(g|0)){break b}while(1){c=c+ -4|0;b=i[c>>2];i[c>>2]=0;d=d+ -4|0;i[d>>2]=b;if((c|0)!=(g|0)){continue}break}i[a+8>>2]=e;b=i[a+4>>2];i[a+4>>2]=f;c=i[a>>2];i[a>>2]=d;if((b|0)==(c|0)){break a}while(1){b=b+ -4|0;a=i[b>>2];i[b>>2]=0;if(a){I[i[i[a>>2]+4>>2]](a)}if((b|0)!=(c|0)){continue}break}break a}Ho();x()}za(2172);x()}i[a+8>>2]=e;i[a+4>>2]=f;i[a>>2]=b}if(c){bp(c)}}function Ff(a,b,c,d){var e=0,f=0,h=0,k=0;e=F-32|0;F=e;i[e+24>>2]=0;i[e+16>>2]=0;i[e+20>>2]=0;a:{if((d|0)<1){break a}Co(e,m[c>>2]);f=j[e+11|0];h=f<<24>>24<0;Ao(e+16|0,h?i[e>>2]:e,h?i[e+4>>2]:f);if(g[e+11|0]<=-1){bp(i[e>>2])}f=1;if((d|0)==1){break a}while(1){Ao(e+16|0,10466,mp(10466));Co(e,m[(f<<2)+c>>2]);h=j[e+11|0];k=h<<24>>24<0;Ao(e+16|0,k?i[e>>2]:e,k?i[e+4>>2]:h);if(g[e+11|0]<=-1){bp(i[e>>2])}f=f+1|0;if((f|0)!=(d|0)){continue}break}}c=Of(a,e,b);f=i[c>>2];if(!f){f=ho(40);ro(f+16|0,b);i[f+36>>2]=0;i[f+28>>2]=0;i[f+32>>2]=0;i[f+8>>2]=i[e>>2];i[f>>2]=0;i[f+4>>2]=0;i[c>>2]=f;d=i[i[a>>2]>>2];b=f;b:{if(!d){break b}i[a>>2]=d;b=i[c>>2]}Kf(i[a+4>>2],b);i[a+8>>2]=i[a+8>>2]+1}to(f+28|0,e+16|0);if(g[e+27|0]<=-1){bp(i[e+16>>2])}F=e+32|0}function Dl(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0;e=i[a+8>>2];d=i[a+4>>2];if((e-d|0)/12>>>0>=b>>>0){f=a;if(b){a=o(b,12)+d|0;while(1){b=i[c+4>>2];i[d>>2]=i[c>>2];i[d+4>>2]=b;i[d+8>>2]=i[c+8>>2];d=d+12|0;if((a|0)!=(d|0)){continue}break}d=a}i[f+4>>2]=d;return}a:{g=i[a>>2];j=d-g|0;h=(j|0)/12|0;f=h+b|0;if(f>>>0<357913942){d=o(b,12);e=(e-g|0)/12|0;k=e<<1;e=e>>>0<178956970?k>>>0>>0?f:k:357913941;f=0;b:{if(!e){break b}if(e>>>0>=357913942){break a}f=ho(o(e,12))}b=f+o(h,12)|0;h=d+b|0;d=b;while(1){k=i[c+4>>2];i[d>>2]=i[c>>2];i[d+4>>2]=k;i[d+8>>2]=i[c+8>>2];d=d+12|0;if((h|0)!=(d|0)){continue}break}c=f+o(e,12)|0;b=b+o((j|0)/-12|0,12)|0;if((j|0)>=1){hp(b,g,j)}i[a+8>>2]=c;i[a+4>>2]=h;i[a>>2]=b;if(g){bp(g)}return}Ho();x()}za(16382);x()}function $f(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;d=ho(32);c=i[b>>2];i[d+24>>2]=0;i[d+28>>2]=0;i[d+16>>2]=c;f=d+24|0;i[d+20>>2]=f;c=i[b+4>>2];g=b+8|0;if((c|0)!=(g|0)){h=d+20|0;while(1){e=c;b=c+16|0;Mf(h,f,b,b);b=i[c+4>>2];a:{if(!b){c=i[e+8>>2];if((e|0)==i[c>>2]){break a}b=e+8|0;while(1){e=i[b>>2];b=e+8|0;c=i[e+8>>2];if((e|0)!=i[c>>2]){continue}break}break a}while(1){c=b;b=i[b>>2];if(b){continue}break}}if((c|0)!=(g|0)){continue}break}}b=i[a+4>>2];b:{c:{if(b){e=i[d+16>>2];while(1){d:{if((e|0)>2]){c=i[b>>2];if(!c){break c}break d}c=i[b+4>>2];if(c){break d}c=b+4|0;break b}b=c;continue}}b=a+4|0}c=b}i[d+8>>2]=b;i[d>>2]=0;i[d+4>>2]=0;i[c>>2]=d;b=i[i[a>>2]>>2];if(b){i[a>>2]=b;d=i[c>>2]}Kf(i[a+4>>2],d);i[a+8>>2]=i[a+8>>2]+1}function na(a,b,c,d){var e=0,f=0,h=0,k=0,l=0,n=0,o=0,r=0,s=0,t=0,v=0,w=p(0),x=0,y=p(0),z=0,A=0,B=0,C=0,D=0;n=F-16|0;F=n;h=i[a+4>>2];o=i[d+48>>2];d=i[i[d>>2]>>2];f=g[b+24|0];k=dk(n+8|0);ek(k,m[a+20>>2],-1<>2];z=i[b>>2];o=i[b+48>>2];A=i[b+68>>2];k=i[b+44>>2];v=i[b+40>>2];B=v;d=0;C=j[b+84|0];D=(f|0)<1;while(1){b=l;e=h;r=i[z>>2];s=v;t=k;if(!C){b=i[(l<<2)+A>>2]}b=up(s,t,b,0)+o|0;s=hp(e,b+r|0,B);if(!D){t=i[a+8>>2];b=0;while(1){r=(d<<2)+x|0;e=b<<2;w=p(u(p(p(y*p(m[e+s>>2]-m[e+t>>2]))+p(.5))));a:{if(p(q(w))>2]=e;d=d+1|0;b=b+1|0;if((f|0)!=(b|0)){continue}break}}l=l+1|0;if((l|0)!=(c|0)){continue}break}}bp(h);F=n+16|0}function cd(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0;e=F-32|0;F=e;h=i[c>>2];g=i[b>>2];j=i[c+4>>2];c=i[b+4>>2];b=(h-g<<3)+(j-c|0)|0;d=i[a+4>>2];f=b+d|0;i[a+4>>2]=f;a:{if(!(!d|(f+ -1^d+ -1)>>>0>31)){a=i[a>>2];break a}a=i[a>>2];if(f>>>0<=32){i[a>>2]=0;break a}i[(f+ -1>>>3&536870908)+a>>2]=0}a=(d>>>3&536870908)+a|0;d=d&31;b:{if((d|0)==(c|0)){if((b|0)<1){break b}if(c){f=32-c|0;d=(b|0)<(f|0)?b:f;f=-1<>>f-d;i[a>>2]=i[a>>2]&(f^-1)|f&i[g>>2];b=b-d|0;g=g+4|0;a=a+(c+d>>>3&536870908)|0}d=a;c=(b|0)/32|0;a=c<<2;d=jp(d,g,a);b=b-(c<<5)|0;if((b|0)<1){break b}c=a+d|0;b=-1>>>32-b|0;i[c>>2]=i[c>>2]&(b^-1)|b&i[a+g>>2];break b}i[e+28>>2]=c;i[e+24>>2]=g;i[e+20>>2]=j;i[e+16>>2]=h;i[e+12>>2]=d;i[e+8>>2]=a;dd(e,e+24|0,e+16|0,e+8|0)}F=e+32|0}function ip(a,b,c){var d=0,e=0,f=0,h=0;a:{if(!c){break a}d=a+c|0;g[d+ -1|0]=b;g[a|0]=b;if(c>>>0<3){break a}g[d+ -2|0]=b;g[a+1|0]=b;g[d+ -3|0]=b;g[a+2|0]=b;if(c>>>0<7){break a}g[d+ -4|0]=b;g[a+3|0]=b;if(c>>>0<9){break a}d=0-a&3;e=d+a|0;b=o(b&255,16843009);i[e>>2]=b;c=c-d&-4;d=c+e|0;i[d+ -4>>2]=b;if(c>>>0<9){break a}i[e+8>>2]=b;i[e+4>>2]=b;i[d+ -8>>2]=b;i[d+ -12>>2]=b;if(c>>>0<25){break a}i[e+24>>2]=b;i[e+20>>2]=b;i[e+16>>2]=b;i[e+12>>2]=b;i[d+ -16>>2]=b;i[d+ -20>>2]=b;i[d+ -24>>2]=b;i[d+ -28>>2]=b;h=e&4|24;c=c-h|0;if(c>>>0<32){break a}d=b;f=b;b=e+h|0;while(1){i[b+24>>2]=f;i[b+28>>2]=d;i[b+16>>2]=f;i[b+20>>2]=d;i[b+8>>2]=f;i[b+12>>2]=d;i[b>>2]=f;i[b+4>>2]=d;b=b+32|0;c=c+ -32|0;if(c>>>0>31){continue}break}}return a}function Bm(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;d=i[a+8>>2];c=i[a+4>>2];if(d-c>>2>>>0>=b>>>0){if(b){b=b<<2;c=ip(c,0,b)+b|0}i[a+4>>2]=c;return}a:{b:{c:{g=i[a>>2];f=c-g>>2;e=f+b|0;if(e>>>0<1073741824){d=d-g|0;h=d>>1;e=d>>2>>>0<536870911?h>>>0>>0?e:h:1073741823;if(e){if(e>>>0>=1073741824){break c}j=ho(e<<2)}d=(f<<2)+j|0;f=b<<2;b=ip(d,0,f);f=b+f|0;e=(e<<2)+j|0;if((c|0)==(g|0)){break b}while(1){c=c+ -4|0;b=i[c>>2];i[c>>2]=0;d=d+ -4|0;i[d>>2]=b;if((c|0)!=(g|0)){continue}break}i[a+8>>2]=e;b=i[a+4>>2];i[a+4>>2]=f;c=i[a>>2];i[a>>2]=d;if((b|0)==(c|0)){break a}while(1){b=b+ -4|0;a=i[b>>2];i[b>>2]=0;if(a){Wb(a)}if((b|0)!=(c|0)){continue}break}break a}Ho();x()}za(16928);x()}i[a+8>>2]=e;i[a+4>>2]=f;i[a>>2]=b}if(c){bp(c)}}function Wj(a){var b=0,c=0,d=0,e=0,f=0,h=0,k=0;f=F-32|0;F=f;c=i[a+16>>2];b=i[a+20>>2];if((b|0)>0?1:(b|0)>=0?c>>>0>=1:0){b=i[i[a+12>>2]+4>>2]+7|0;if(b>>>0<7){e=1}d=b;b=e>>>3|0;e=e<<29|d>>>3;d=b;if(j[a+24|0]){h=i[a+4>>2];g[f+24|0]=0;b=f;i[b+16>>2]=0;i[b+20>>2]=0;i[b+8>>2]=0;i[b+12>>2]=0;i[b>>2]=0;i[b+4>>2]=0;Jg(e,d,b);c=h-c|0;h=c+ -8|0;d=i[b+4>>2]-i[b>>2]|0;jp(h+d|0,c,e);hp(h,i[b>>2],d);b=a;k=b;c=i[b+20>>2];d=8-d|0;h=d+i[b+16>>2]|0;if(h>>>0>>0){c=c+1|0}i[k+16>>2]=h;i[b+20>>2]=c;b=i[f+12>>2];i[f+12>>2]=0;if(b){bp(b)}b=i[f>>2];if(b){i[f+4>>2]=b;bp(b)}c=i[a+16>>2]}d=i[a>>2];b=i[a+4>>2]-d|0;e=e-c|0;c=b+e|0;a:{if(c>>>0>b>>>0){Qj(a,e);break a}if(c>>>0>=b>>>0){break a}i[a+4>>2]=c+d}i[a+16>>2]=0;i[a+20>>2]=0}F=f+32|0}function ji(a,b){var c=0,d=0,e=0,f=0,g=0;d=F-16|0;F=d;i[d>>2]=b;c=-1;a:{if((b|0)==-1){i[d+4>>2]=-1;break a}c=b+1|0;i[d+4>>2]=(c>>>0)%3|0?c:b+ -2|0;if((b>>>0)%3|0){c=b+ -1|0;break a}c=b+2|0}i[d+8>>2]=c;c=(b|0)==-1?-1:(b>>>0)/3|0;e=i[a+28>>2]+(c>>>3&536870908)|0;i[e>>2]=i[e>>2]|1<>2]+12>>2]+(b<<2)>>2];if((c|0)==-1){break b}c=(c>>>0)/3|0;if(i[i[a+28>>2]+(c>>>3&268435452)>>2]>>>c&1){break b}c=i[a+172>>2];if((c|0)==i[a+176>>2]){break b}e=b>>>5|0;g=1<>2]+(b<<5)|0,(i[i[(o(b,136)+c|0)+4>>2]+(e<<2)>>2]&g)!=0);b=b+1|0;c=i[a+172>>2];if(b>>>0<(i[a+176>>2]-c|0)/136>>>0){continue}break}}f=f+1|0;if((f|0)!=3){b=i[(f<<2)+d>>2];continue}break}F=d+16|0}function fi(a,b,c){var d=0,e=0,f=0;a:{b:{c:{b=o(b,3);if((b|0)==-1){break c}e=i[a+12>>2];f=i[e+12>>2];d=b<<2;if(i[f+d>>2]==-1){break b}a=i[a+152>>2];e=i[e>>2];if(i[a+(i[e+d>>2]<<2)>>2]!=-1){break a}d=b+1|0;b=(d>>>0)%3|0?d:b+ -2|0;if((b|0)==-1){break c}d=b<<2;if(i[d+f>>2]==-1){break b}if(i[a+(i[d+e>>2]<<2)>>2]!=-1){break a}d=b+1|0;b=(d>>>0)%3|0?d:b+ -2|0;if((b|0)==-1){break c}d=b<<2;if(i[d+f>>2]==-1){break b}if(i[a+(i[d+e>>2]<<2)>>2]!=-1){break a}a=b+1|0;i[c>>2]=(a>>>0)%3|0?a:b+ -2|0;return 1}b=-1}i[c>>2]=b;return 0}while(1){d:{a=b;b=((b>>>0)%3|0?-1:2)+b|0;if((b|0)==-1){break d}b=i[(b<<2)+f>>2];if((b|0)==-1){break d}b=b+((b>>>0)%3|0?-1:2)|0;if((b|0)!=-1){continue}}break}i[c>>2]=a+((a>>>0)%3|0?-1:2);return 0}function hn(a,b,c,d,e,f){var g=0;g=F-80|0;F=g;a:{if((f|0)>=16384){yn(g+32|0,b,c,d,e,0,0,0,2147352576);d=i[g+40>>2];e=i[g+44>>2];b=i[g+32>>2];c=i[g+36>>2];if((f|0)<32767){f=f+ -16383|0;break a}yn(g+16|0,b,c,d,e,0,0,0,2147352576);f=((f|0)<49149?f:49149)+ -32766|0;d=i[g+24>>2];e=i[g+28>>2];b=i[g+16>>2];c=i[g+20>>2];break a}if((f|0)>-16383){break a}yn(g- -64|0,b,c,d,e,0,0,0,65536);d=i[g+72>>2];e=i[g+76>>2];b=i[g+64>>2];c=i[g+68>>2];if((f|0)>-32765){f=f+16382|0;break a}yn(g+48|0,b,c,d,e,0,0,0,65536);f=((f|0)>-49146?f:-49146)+32764|0;d=i[g+56>>2];e=i[g+60>>2];b=i[g+48>>2];c=i[g+52>>2]}yn(g,b,c,d,e,0,0,0,f+16383<<16);b=i[g+12>>2];i[a+8>>2]=i[g+8>>2];i[a+12>>2]=b;b=i[g+4>>2];i[a>>2]=i[g>>2];i[a+4>>2]=b;F=g+80|0}function ag(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0;h=F-16|0;F=h;i[h+8>>2]=0;i[h>>2]=0;i[h+4>>2]=0;a:{b:{g=c+1|0;if(g>>>0>=c>>>0){if(g>>>0>=1073741824){break b}j=ho(g<<2);ip(j,0,(c<<2)+4|0)}if((b|0)>0){while(1){g=(i[(f<<2)+a>>2]<<2)+j|0;i[g>>2]=i[g>>2]+1;f=f+1|0;if((f|0)!=(b|0)){continue}break}}f=0;if((c|0)<0){b=0;break a}k=+(b|0);b=0;while(1){a=i[(f<<2)+j>>2];if((a|0)>=1){l=e;e=+(a|0);e=l+ep(e/k)*e;b=b+1|0}a=(c|0)==(f|0);f=f+1|0;if(!a){continue}break}break a}Ho();x()}if(d){i[d>>2]=b}if(j){bp(j)}e=-e;c:{if(q(e)<0x8000000000000000){b=q(e)>=1?e>0?~~s(u(e*2.3283064365386963e-10),4294967295)>>>0:~~v((e- +(~~e>>>0>>>0))*2.3283064365386963e-10)>>>0:0;a=~~e>>>0;break c}b=-2147483648;a=0}F=h+16|0;H=b;return a}function gj(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0;b=i[a+4>>2];d=i[b>>2];a:{c=i[a+12>>2];e=i[c+56>>2]-i[c+52>>2]|0;c=e>>2;b:{if(i[b+8>>2]-d>>2>>>0>=c>>>0){break b}if(c>>>0>=1073741824){break a}f=i[b+4>>2];g=c<<2;c=ho(e);g=g+c|0;e=f-d|0;f=e+c|0;if((e|0)>=1){hp(c,d,e)}i[b+8>>2]=g;i[b+4>>2]=f;i[b>>2]=c;if(!d){break b}bp(d)}e=a+8|0;b=i[a+76>>2];c:{if(b){c=i[b>>2];if((c|0)==i[b+4>>2]){return 1}b=0;while(1){d=hj(e,i[(b<<2)+c>>2]);if(!d){break c}f=i[a+76>>2];c=i[f>>2];b=b+1|0;if(b>>>0>2]-c>>2>>>0){continue}break}break c}d=1;a=i[i[a+12>>2]+64>>2];a=(i[a+4>>2]-i[a>>2]>>2>>>0)/3|0;if(a>>>0<1){break c}b=0;while(1){d=hj(e,o(b,3));b=b+1|0;if((a|0)==(b|0)){break c}if(d){continue}break}}return d|0}za(11708);x()}function Rh(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;h=F-16|0;F=h;a:{b:{c=i[a+8>>2];c:{if((c|0)!=i[a+12>>2]){i[c>>2]=b;i[a+8>>2]=c+4;break c}e=i[a+4>>2];f=c-e|0;g=f>>2;d=g+1|0;if(d>>>0>=1073741824){break b}c=f>>1;d=g>>>0<536870911?c>>>0>>0?d:c:1073741823;c=0;d:{if(!d){break d}if(d>>>0>=1073741824){break a}c=ho(d<<2)}g=c+(g<<2)|0;i[g>>2]=b;d=c+(d<<2)|0;g=g+4|0;if((f|0)>=1){hp(c,e,f)}i[a+12>>2]=d;i[a+8>>2]=g;i[a+4>>2]=c;if(!e){break c}bp(e)}c=i[a+16>>2];e=i[a+20>>2]-c>>2;e:{if((e|0)>(b|0)){break e}i[h+12>>2]=-1;f=b+1|0;if(f>>>0>e>>>0){c=a+16|0;Gb(c,f-e|0,h+12|0);c=i[c>>2];break e}if(f>>>0>=e>>>0){break e}i[a+20>>2]=(f<<2)+c}i[(b<<2)+c>>2]=(i[a+8>>2]-i[a+4>>2]>>2)+ -1;F=h+16|0;return}Ho();x()}za(11708);x()}function km(a,b){var c=0,d=0,e=0,f=0,g=0;a:{b:{c:{e=i[a>>2];f=i[a+4>>2]-e>>2;c=f+1|0;if(c>>>0<1073741824){e=i[a+8>>2]-e|0;g=e>>1;c=e>>2>>>0<536870911?g>>>0>>0?c:g:1073741823;if(c){if(c>>>0>=1073741824){break c}d=ho(c<<2)}e=i[b>>2];i[b>>2]=0;b=(f<<2)+d|0;i[b>>2]=e;e=(c<<2)+d|0;f=b+4|0;d=i[a+4>>2];c=i[a>>2];if((d|0)==(c|0)){break b}while(1){d=d+ -4|0;g=i[d>>2];i[d>>2]=0;b=b+ -4|0;i[b>>2]=g;if((d|0)!=(c|0)){continue}break}i[a+8>>2]=e;d=i[a+4>>2];i[a+4>>2]=f;c=i[a>>2];i[a>>2]=b;if((d|0)==(c|0)){break a}while(1){d=d+ -4|0;a=i[d>>2];i[d>>2]=0;if(a){yk(a+12|0,i[a+16>>2]);zk(a,i[a+4>>2]);bp(a)}if((d|0)!=(c|0)){continue}break}break a}Ho();x()}za(16788);x()}i[a+8>>2]=e;i[a+4>>2]=f;i[a>>2]=b}if(c){bp(c)}}function of(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;c=i[a+8>>2];d=i[a>>2];if(c-d>>3>>>0>=2){e=i[a+4>>2];f=e-d>>3;g=f>>>0<2?f:2;if(g){c=d;while(1){h=i[b+4>>2];i[c>>2]=i[b>>2];i[c+4>>2]=h;c=c+8|0;g=g+ -1|0;if(g){continue}break}}if(f>>>0<2){c=a;a=2-f|0;if(a){a=(a<<3)+e|0;while(1){d=i[b+4>>2];i[e>>2]=i[b>>2];i[e+4>>2]=d;e=e+8|0;if((a|0)!=(e|0)){continue}break}}else{a=e}i[c+4>>2]=a;return}i[a+4>>2]=d+16;return}if(d){i[a+4>>2]=d;bp(d);i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;c=0}d=c>>2;c=c>>3>>>0<268435455?d>>>0<2?2:d:536870911;if(c>>>0<536870912){d=c<<3;c=ho(d);i[a>>2]=c;i[a+8>>2]=c+d;d=c+16|0;e=i[b>>2];b=i[b+4>>2];while(1){i[c>>2]=e;i[c+4>>2]=b;c=c+8|0;if((d|0)!=(c|0)){continue}break}i[a+4>>2]=d;return}Ho();x()}function Vm(a,b,c){a:{if(b>>>0>20){break a}b:{switch(b+ -9|0){case 0:b=i[c>>2];i[c>>2]=b+4;i[a>>2]=i[b>>2];return;case 1:b=i[c>>2];i[c>>2]=b+4;b=i[b>>2];i[a>>2]=b;i[a+4>>2]=b>>31;return;case 2:b=i[c>>2];i[c>>2]=b+4;i[a>>2]=i[b>>2];i[a+4>>2]=0;return;case 3:b=i[c>>2]+7&-8;i[c>>2]=b+8;c=i[b+4>>2];i[a>>2]=i[b>>2];i[a+4>>2]=c;return;case 4:b=i[c>>2];i[c>>2]=b+4;b=h[b>>1];i[a>>2]=b;i[a+4>>2]=b>>31;return;case 5:b=i[c>>2];i[c>>2]=b+4;i[a>>2]=k[b>>1];i[a+4>>2]=0;return;case 6:b=i[c>>2];i[c>>2]=b+4;b=g[b|0];i[a>>2]=b;i[a+4>>2]=b>>31;return;case 7:b=i[c>>2];i[c>>2]=b+4;i[a>>2]=j[b|0];i[a+4>>2]=0;return;case 8:b=i[c>>2]+7&-8;i[c>>2]=b+8;n[a>>3]=n[b>>3];return;case 9:break b;default:break a}}I[369](a,c)}}function aj(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0;b=i[a+4>>2];d=i[b>>2];a:{c=i[a+12>>2];e=i[c+28>>2]-i[c+24>>2]|0;c=e>>2;b:{if(i[b+8>>2]-d>>2>>>0>=c>>>0){break b}if(c>>>0>=1073741824){break a}f=i[b+4>>2];g=c<<2;c=ho(e);g=g+c|0;e=f-d|0;f=e+c|0;if((e|0)>=1){hp(c,d,e)}i[b+8>>2]=g;i[b+4>>2]=f;i[b>>2]=c;if(!d){break b}bp(d)}e=a+8|0;b=i[a+76>>2];c:{if(b){c=i[b>>2];if((c|0)==i[b+4>>2]){return 1}b=0;while(1){d=bj(e,i[(b<<2)+c>>2]);if(!d){break c}f=i[a+76>>2];c=i[f>>2];b=b+1|0;if(b>>>0>2]-c>>2>>>0){continue}break}break c}d=1;a=i[a+12>>2];a=(i[a+4>>2]-i[a>>2]>>2>>>0)/3|0;if(a>>>0<1){break c}b=0;while(1){d=bj(e,o(b,3));b=b+1|0;if((a|0)==(b|0)){break c}if(d){continue}break}}return d|0}za(11708);x()}function rl(a,b,c){var d=0,e=0,f=0,h=0,k=0;f=c-b|0;e=i[a+8>>2];d=i[a>>2];if(f>>>0<=e-d>>>0){h=i[a+4>>2]-d|0;e=h+b|0;k=f>>>0>h>>>0?e:c;if((k|0)!=(b|0)){while(1){g[d|0]=j[b|0];d=d+1|0;b=b+1|0;if((k|0)!=(b|0)){continue}break}}if(f>>>0>h>>>0){b=i[a+4>>2];if((c|0)!=(k|0)){while(1){g[b|0]=j[e|0];b=b+1|0;e=e+1|0;if((e|0)!=(c|0)){continue}break}}i[a+4>>2]=b;return}i[a+4>>2]=d;return}if(d){i[a+4>>2]=d;bp(d);i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;e=0}a:{if((f|0)<=-1){break a}d=e<<1;e=e>>>0<1073741823?d>>>0>>0?f:d:2147483647;if((e|0)<=-1){break a}d=ho(e);i[a>>2]=d;i[a+4>>2]=d;i[a+8>>2]=d+e;if((b|0)!=(c|0)){hp(d,b,f);while(1){d=d+1|0;b=b+1|0;if((c|0)!=(b|0)){continue}break}}i[a+4>>2]=d;return}Ho();x()}function Sh(a,b,c){var d=0,e=0,f=0,g=0,h=0;d=i[a+8>>2];e=i[a>>2];if(d-e>>2>>>0>=b>>>0){f=i[a+4>>2];g=f-e>>2;h=g>>>0>>0?g:b;if(h){d=e;while(1){i[d>>2]=i[c>>2];d=d+4|0;h=h+ -1|0;if(h){continue}break}}if(g>>>0>>0){d=a;a=b-g|0;if(a){a=(a<<2)+f|0;while(1){i[f>>2]=i[c>>2];f=f+4|0;if((a|0)!=(f|0)){continue}break}}else{a=f}i[d+4>>2]=a;return}i[a+4>>2]=(b<<2)+e;return}if(e){i[a+4>>2]=e;bp(e);i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;d=0}a:{if(b>>>0>=1073741824){break a}e=d>>1;d=d>>2>>>0<536870911?e>>>0>>0?b:e:1073741823;if(d>>>0>=1073741824){break a}e=d<<2;d=ho(e);i[a>>2]=d;i[a+8>>2]=d+e;b=(b<<2)+d|0;c=i[c>>2];while(1){i[d>>2]=c;d=d+4|0;if((b|0)!=(d|0)){continue}break}i[a+4>>2]=b;return}Ho();x()}function Hc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0;a:{b:{if((I[i[i[b>>2]+20>>2]](b)|0)>=1){k=a+16|0;while(1){f=xm(i[i[a+4>>2]+4>>2],I[i[i[b>>2]+24>>2]](b,j)|0);if((f|0)!=-1){c=i[a+20>>2];c:{if((c|0)!=i[a+24>>2]){i[c>>2]=f;i[a+20>>2]=c+4;break c}g=i[k>>2];h=c-g|0;e=h>>2;d=e+1|0;if(d>>>0>=1073741824){break b}c=h>>1;d=e>>>0<536870911?c>>>0>>0?d:c:1073741823;c=0;d:{if(!d){break d}if(d>>>0>=1073741824){break a}c=ho(d<<2)}e=c+(e<<2)|0;i[e>>2]=f;d=c+(d<<2)|0;e=e+4|0;if((h|0)>=1){hp(c,g,h)}i[a+24>>2]=d;i[a+20>>2]=e;i[a+16>>2]=c;if(!g){break c}bp(g)}Gj(i[a+4>>2],f);j=j+1|0;if((j|0)<(I[i[i[b>>2]+20>>2]](b)|0)){continue}}break}a=(f|0)!=-1}else{a=1}return a|0}Ho();x()}za(1956);x()}function li(a){var b=0,c=0,d=0,e=0,f=0,h=0,k=0,m=0,n=0,p=0;b=i[a+112>>2]-i[a+108>>2]|0;c=(b|0)/12|0;Jb(c,i[i[a+4>>2]+44>>2]);if(b){f=c>>>0>1?c:1;while(1){b=i[a+108>>2]+o(d,12)|0;Jb(i[b+4>>2]-h|0,i[i[a+4>>2]+44>>2]);Jb(i[b+4>>2]-i[b>>2]|0,i[i[a+4>>2]+44>>2]);h=i[b+4>>2];d=d+1|0;if((f|0)!=(d|0)){continue}break}d=0;Vj(i[i[a+4>>2]+44>>2],c,0,0);f=c>>>0>1?c:1;while(1){c=i[i[a+4>>2]+44>>2];b=i[c+20>>2];if((b|0)>0?1:(b|0)>=0?l[c+16>>2]>=1:0){h=j[(i[a+108>>2]+o(d,12)|0)+8|0];c=i[c+12>>2];e=i[c+4>>2];b=e>>>3|0;k=b+i[c>>2]|0;m=j[k|0];e=e&7;n=k,p=yp(-2,e)&m,g[n|0]=p;b=b+i[c>>2]|0;g[b|0]=j[b|0]|(h&1)<>2]=i[c+4>>2]+1}d=d+1|0;if((f|0)!=(d|0)){continue}break}Wj(i[i[a+4>>2]+44>>2])}return 1}function Hd(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0;f=c-b|0;g=f>>2;d=i[a+8>>2];e=i[a>>2];if(g>>>0<=d-e>>2>>>0){f=i[a+4>>2]-e|0;d=f+b|0;h=f>>2;j=g>>>0>h>>>0?d:c;f=j-b|0;if(f){jp(e,b,f)}if(g>>>0>h>>>0){b=i[a+4>>2];if((c|0)!=(j|0)){while(1){i[b>>2]=i[d>>2];b=b+4|0;d=d+4|0;if((d|0)!=(c|0)){continue}break}}i[a+4>>2]=b;return}i[a+4>>2]=e+f;return}if(e){i[a+4>>2]=e;bp(e);i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;d=0}a:{if(g>>>0>=1073741824){break a}e=d>>1;d=d>>2>>>0<536870911?e>>>0>>0?g:e:1073741823;if(d>>>0>=1073741824){break a}e=d<<2;d=ho(e);i[a>>2]=d;i[a+4>>2]=d;i[a+8>>2]=d+e;if((b|0)!=(c|0)){hp(d,b,f&-4);while(1){d=d+4|0;b=b+4|0;if((c|0)!=(b|0)){continue}break}}i[a+4>>2]=d;return}Ho();x()}function Xh(a,b){var c=0,d=0,e=0,f=0,g=0;a:{b:{c:{e=i[a>>2];f=i[a+4>>2]-e>>2;c=f+1|0;if(c>>>0<1073741824){e=i[a+8>>2]-e|0;g=e>>1;c=e>>2>>>0<536870911?g>>>0>>0?c:g:1073741823;if(c){if(c>>>0>=1073741824){break c}d=ho(c<<2)}e=i[b>>2];i[b>>2]=0;b=(f<<2)+d|0;i[b>>2]=e;e=(c<<2)+d|0;f=b+4|0;d=i[a+4>>2];c=i[a>>2];if((d|0)==(c|0)){break b}while(1){d=d+ -4|0;g=i[d>>2];i[d>>2]=0;b=b+ -4|0;i[b>>2]=g;if((d|0)!=(c|0)){continue}break}i[a+8>>2]=e;d=i[a+4>>2];i[a+4>>2]=f;c=i[a>>2];i[a>>2]=b;if((d|0)==(c|0)){break a}while(1){d=d+ -4|0;a=i[d>>2];i[d>>2]=0;if(a){I[i[i[a>>2]+4>>2]](a)}if((d|0)!=(c|0)){continue}break}break a}Ho();x()}za(11708);x()}i[a+8>>2]=e;i[a+4>>2]=f;i[a>>2]=b}if(c){bp(c)}}function Ng(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,m=0;e=i[d>>2];d=i[e>>2];a:{e=i[e+4>>2]-d>>3;j=i[b>>2];if(e>>>0<=j>>>0){break a}h=i[a>>2];if(e>>>0<=h>>>0){break a}g=i[c>>2];m=i[d+(j<<3)>>2];k=d+(h<<3)|0;b:{c:{if(m>>>0>=l[k>>2]){if(e>>>0<=g>>>0){break a}f=0;if(l[d+(g<<3)>>2]>=m>>>0){break b}i[b>>2]=g;i[c>>2]=j;c=i[b>>2];if(e>>>0<=c>>>0){break a}k=e;e=i[a>>2];if(k>>>0<=e>>>0){break a}f=1;if(l[d+(c<<3)>>2]>=l[d+(e<<3)>>2]){break b}i[a>>2]=c;i[b>>2]=e;break c}if(e>>>0<=g>>>0){break a}if(l[d+(g<<3)>>2]>>0){i[a>>2]=g;i[c>>2]=h;return 1}i[a>>2]=j;i[b>>2]=h;a=i[c>>2];if(e>>>0<=a>>>0){break a}f=1;if(l[d+(a<<3)>>2]>=l[k>>2]){break b}i[b>>2]=a;i[c>>2]=h}f=2}return f}Io();x()}function em(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,k=0,l=0,m=0,n=0,p=0;d=F-16|0;F=d;h=ym(b);a:{if(!h){i[a>>2]=0;break a}c=i[b+100>>2];k=i[b+96>>2];i[d+8>>2]=0;i[d>>2]=0;i[d+4>>2]=0;c=c-k|0;e=(c|0)/12|0;b:{if(!c){break b}if(e>>>0<357913942){g=ho(c);i[d>>2]=g;f=g+o(e,12)|0;i[d+8>>2]=f;b=0;m=ip(g,0,c-((c+ -12>>>0)%12|0)|0);i[d+4>>2]=f;n=e>>>0>1?e:1;p=j[h+84|0];while(1){l=o(b,12);c=l+k|0;e=i[c>>2];c:{if(p){g=c+8|0;c=c+4|0;break c}f=i[h+68>>2];g=f+(i[c+8>>2]<<2)|0;e=i[f+(e<<2)>>2];c=f+(i[c+4>>2]<<2)|0}f=i[c>>2];c=m+l|0;i[c+8>>2]=i[g>>2];i[c+4>>2]=f;i[c>>2]=e;b=b+1|0;if((n|0)!=(b|0)){continue}break}break b}Ho();x()}Jl(a,d);a=i[d>>2];if(!a){break a}i[d+4>>2]=a;bp(a)}F=d+16|0}function Vb(a,b){var c=0,d=0,e=0,f=0,g=0;a:{b:{c:{e=i[a>>2];f=i[a+4>>2]-e>>2;c=f+1|0;if(c>>>0<1073741824){e=i[a+8>>2]-e|0;g=e>>1;c=e>>2>>>0<536870911?g>>>0>>0?c:g:1073741823;if(c){if(c>>>0>=1073741824){break c}d=ho(c<<2)}e=i[b>>2];i[b>>2]=0;b=(f<<2)+d|0;i[b>>2]=e;e=(c<<2)+d|0;f=b+4|0;d=i[a+4>>2];c=i[a>>2];if((d|0)==(c|0)){break b}while(1){d=d+ -4|0;g=i[d>>2];i[d>>2]=0;b=b+ -4|0;i[b>>2]=g;if((d|0)!=(c|0)){continue}break}i[a+8>>2]=e;d=i[a+4>>2];i[a+4>>2]=f;c=i[a>>2];i[a>>2]=b;if((d|0)==(c|0)){break a}while(1){d=d+ -4|0;a=i[d>>2];i[d>>2]=0;if(a){Wb(a)}if((d|0)!=(c|0)){continue}break}break a}Ho();x()}za(1752);x()}i[a+8>>2]=e;i[a+4>>2]=f;i[a>>2]=b}if(c){bp(c)}}function fn(a){var b=0,c=0,d=0,e=0,f=0,h=0,k=0,m=0,n=0;a:{c=i[a+112>>2];d=i[a+116>>2];b:{if(c|d){b=l[a+120>>2]>=c>>>0;c=i[a+124>>2];if((c|0)>(d|0)?1:(c|0)>=(d|0)?b:0){break b}}k=dn(a);if((k|0)>-1){break a}}i[a+104>>2]=0;return-1}m=a;e=i[a+116>>2];b=e;d=i[a+8>>2];c=d;f=i[a+112>>2];c:{if(!(b|f)){break c}c=d;e=(i[a+124>>2]^-1)+b|0;b=i[a+120>>2]^-1;f=b+f|0;if(f>>>0>>0){e=e+1|0}b=f;f=i[a+4>>2];h=d-f|0;n=b>>>0>=h>>>0;h=h>>31;if((e|0)>(h|0)?1:(e|0)>=(h|0)?n:0){break c}c=b+f|0}i[m+104>>2]=c;m=i[a+4>>2];if(d){c=a;e=(d-m|0)+1|0;b=e;d=b+i[a+120>>2]|0;a=i[a+124>>2]+(b>>31)|0;i[c+120>>2]=d;i[c+124>>2]=d>>>0>>0?a+1|0:a}a=m+ -1|0;if(j[a|0]!=(k|0)){g[a|0]=k}return k}function Lg(a,b,c,d,e,f){var g=0,h=0,j=0,k=0;j=Kg(a,b,c,d,f);g=i[f>>2];f=i[g>>2];a:{g=i[g+4>>2]-f>>3;h=i[e>>2];if(g>>>0<=h>>>0){break a}k=i[d>>2];if(g>>>0<=k>>>0){break a}if(l[f+(h<<3)>>2]>2]){i[d>>2]=h;i[e>>2]=k;e=i[d>>2];if(g>>>0<=e>>>0){break a}h=i[c>>2];if(g>>>0<=h>>>0){break a}if(l[f+(e<<3)>>2]>=l[f+(h<<3)>>2]){return j+1|0}i[c>>2]=e;i[d>>2]=h;d=i[c>>2];if(g>>>0<=d>>>0){break a}e=i[b>>2];if(g>>>0<=e>>>0){break a}if(l[f+(d<<3)>>2]>=l[f+(e<<3)>>2]){return j+2|0}i[b>>2]=d;i[c>>2]=e;c=i[b>>2];if(g>>>0<=c>>>0){break a}d=i[a>>2];if(g>>>0<=d>>>0){break a}if(l[f+(c<<3)>>2]>=l[f+(d<<3)>>2]){return j+3|0}i[a>>2]=c;i[b>>2]=d;j=j+4|0}return j}Io();x()}function qn(a,b,c,d,e,f,g,h){var i=0,j=0,k=0,l=0,m=0,n=0;j=1;i=d&2147483647;m=i;k=c;a:{if(!c&(i|0)==2147418112?a|b:(i|0)==2147418112&c>>>0>0|i>>>0>2147418112){break a}l=h&2147483647;n=l;i=g;if(!g&(l|0)==2147418112?e|f:(l|0)==2147418112&g>>>0>0|l>>>0>2147418112){break a}if(!(a|e|(i|k)|(b|f|(m|n)))){return 0}k=d&h;if((k|0)>0?1:(k|0)>=0?(c&g)>>>0>=0:0){j=-1;if((c|0)==(g|0)&(d|0)==(h|0)?(b|0)==(f|0)&a>>>0>>0|b>>>0>>0:(d|0)<(h|0)?1:(d|0)<=(h|0)?c>>>0>>0:0){break a}return(a^e|c^g)!=0|(b^f|d^h)!=0}j=-1;if((c|0)==(g|0)&(d|0)==(h|0)?(b|0)==(f|0)&a>>>0>e>>>0|b>>>0>f>>>0:(d|0)>(h|0)?1:(d|0)>=(h|0)?c>>>0>g>>>0:0){break a}j=(a^e|c^g)!=0|(b^f|d^h)!=0}return j}function Uh(a,b,c){var d=0,e=0,f=0,g=0,h=0;e=F-16|0;F=e;i[a+4>>2]=0;a:{b:{if(!b){break b}f=i[a+8>>2];d=f<<5;c:{if(d>>>0>=b>>>0){i[a+4>>2]=b;break c}i[e+8>>2]=0;i[e>>2]=0;i[e+4>>2]=0;if((b|0)<=-1){break a}h=e;if(d>>>0<=1073741822){g=b+31&-32;d=f<<6;d=d>>>0>>0?g:d}else{d=2147483647}bd(h,d);d=i[a>>2];i[a>>2]=i[e>>2];i[e>>2]=d;f=i[a+4>>2];i[a+4>>2]=b;i[e+4>>2]=f;f=i[a+8>>2];i[a+8>>2]=i[e+8>>2];i[e+8>>2]=f;if(!d){break c}bp(d)}d=b>>>5|0;f=d<<2;a=i[a>>2];if(j[c|0]){a=ip(a,255,f);b=b&31;if(!b){break b}a=a+(d<<2)|0;i[a>>2]=i[a>>2]|-1>>>32-b;break b}a=ip(a,0,f);b=b&31;if(!b){break b}a=a+(d<<2)|0;i[a>>2]=i[a>>2]&(-1>>>32-b^-1)}F=e+16|0;return}Ho();x()}function ki(a){var b=0,c=0,d=0,e=0,f=0,h=0,k=0,m=0,n=0,p=0,q=0,r=0;k=a+32|0;b=i[a+64>>2];b=i[(I[i[i[b>>2]+40>>2]](b)|0)+56>>2];Vj(k,o((i[b+100>>2]-i[b+96>>2]|0)/12|0,3),0,1);a:{b=i[a+68>>2];d=i[a+72>>2]-b|0;if((d|0)<1){break a}d=(d>>>2|0)+ -1|0;while(1){c=i[a+52>>2];b:{if((c|0)<0?1:(c|0)<=0?l[a+48>>2]<1:0){break b}h=i[(d<<2)+b>>2];if(!(171>>>h&1)){break b}n=i[(h<<2)+12048>>2];b=i[a+44>>2];c=i[b+4>>2];f=0;while(1){e=c>>>3|0;m=e+i[b>>2]|0;p=j[m|0];c=c&7;q=m,r=yp(-2,c)&p,g[q|0]=r;e=e+i[b>>2]|0;g[e|0]=j[e|0]|(h>>>f&1)<>2]+1|0;i[b+4>>2]=c;f=f+1|0;if((n|0)!=(f|0)){continue}break}}d=d+ -1|0;if((d|0)<0){break a}b=i[a+68>>2];continue}}Wj(k)}function Yf(a,b,c){var d=0,e=0,f=0;d=F-16|0;F=d;i[d+12>>2]=b;b=ho(32);i[d>>2]=b;i[d+4>>2]=17;i[d+8>>2]=-2147483616;g[b+17|0]=0;g[b+16|0]=j[10556];e=j[10552]|j[10553]<<8|(j[10554]<<16|j[10555]<<24);f=j[10548]|j[10549]<<8|(j[10550]<<16|j[10551]<<24);g[b+8|0]=f;g[b+9|0]=f>>>8;g[b+10|0]=f>>>16;g[b+11|0]=f>>>24;g[b+12|0]=e;g[b+13|0]=e>>>8;g[b+14|0]=e>>>16;g[b+15|0]=e>>>24;e=j[10544]|j[10545]<<8|(j[10546]<<16|j[10547]<<24);f=j[10540]|j[10541]<<8|(j[10542]<<16|j[10543]<<24);g[b|0]=f;g[b+1|0]=f>>>8;g[b+2|0]=f>>>16;g[b+3|0]=f>>>24;g[b+4|0]=e;g[b+5|0]=e>>>8;g[b+6|0]=e>>>16;g[b+7|0]=e>>>24;Yj(yf(a+4|0,d+12|0),d,c);if(g[d+11|0]<=-1){bp(i[d>>2])}F=d+16|0}function Df(a,b,c){var d=0,e=0,f=0;d=F-16|0;F=d;i[d+12>>2]=b;b=ho(32);i[d>>2]=b;i[d+4>>2]=17;i[d+8>>2]=-2147483616;g[b+17|0]=0;g[b+16|0]=j[10144];e=j[10140]|j[10141]<<8|(j[10142]<<16|j[10143]<<24);f=j[10136]|j[10137]<<8|(j[10138]<<16|j[10139]<<24);g[b+8|0]=f;g[b+9|0]=f>>>8;g[b+10|0]=f>>>16;g[b+11|0]=f>>>24;g[b+12|0]=e;g[b+13|0]=e>>>8;g[b+14|0]=e>>>16;g[b+15|0]=e>>>24;e=j[10132]|j[10133]<<8|(j[10134]<<16|j[10135]<<24);f=j[10128]|j[10129]<<8|(j[10130]<<16|j[10131]<<24);g[b|0]=f;g[b+1|0]=f>>>8;g[b+2|0]=f>>>16;g[b+3|0]=f>>>24;g[b+4|0]=e;g[b+5|0]=e>>>8;g[b+6|0]=e>>>16;g[b+7|0]=e>>>24;Yj(yf(a+4|0,d+12|0),d,c);if(g[d+11|0]<=-1){bp(i[d>>2])}F=d+16|0}function id(a,b,c,d,e){var f=0,g=0,h=0,j=0;f=i[i[i[d+4>>2]+8>>2]+(c<<2)>>2];if((b|0)==-1){b=ff(c,d)}if((b|0)==-2){i[a>>2]=0;return}a:{b:{if((I[i[i[d>>2]+8>>2]](d)|0)==1){pd(a,d,b,c,e);if(i[a>>2]){break b}i[a>>2]=0}b=ho(44);i[b+4>>2]=f;i[b>>2]=3184;c=i[e+8>>2];d=i[e+12>>2];f=i[e+16>>2];g=i[e+20>>2];h=i[e>>2];j=i[e+4>>2];i[b+40>>2]=0;i[b+32>>2]=0;i[b+36>>2]=0;i[b+24>>2]=f;i[b+28>>2]=g;i[b+16>>2]=c;i[b+20>>2]=d;i[b+8>>2]=h;i[b+12>>2]=j;f=i[e+24>>2];e=i[e+28>>2]-f|0;if(e){d=e>>2;if(d>>>0>=1073741824){break a}c=ho(e);i[b+32>>2]=c;i[b+36>>2]=c;i[b+40>>2]=c+(d<<2);d=b;if((e|0)>=1){c=hp(c,f,e)+e|0}i[d+36>>2]=c}i[b>>2]=5876;i[a>>2]=b}return}Ho();x()}function la(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0;i[b>>2]=1;f=b+8|0;c=i[b+8>>2];d=i[b+12>>2]-c|0;if(d>>>0<=4294967291){Rj(f,d+4|0);c=i[f>>2]}c=c+d|0;d=i[a+4>>2];g[c|0]=d;g[c+1|0]=d>>>8;g[c+2|0]=d>>>16;g[c+3|0]=d>>>24;c=i[a+8>>2];if((c|0)!=i[a+12>>2]){d=0;while(1){h=(d<<2)+c|0;c=i[b+8>>2];e=i[b+12>>2]-c|0;if(e>>>0<=4294967291){Rj(f,e+4|0);c=i[f>>2]}c=c+e|0;e=i[h>>2];g[c|0]=e;g[c+1|0]=e>>>8;g[c+2|0]=e>>>16;g[c+3|0]=e>>>24;d=d+1|0;c=i[a+8>>2];if(d>>>0>2]-c>>2>>>0){continue}break}}c=i[b+12>>2];b=i[b+8>>2];c=c-b|0;if(c>>>0<=4294967291){Rj(f,c+4|0);b=i[f>>2]}b=b+c|0;a=i[a+20>>2];g[b|0]=a;g[b+1|0]=a>>>8;g[b+2|0]=a>>>16;g[b+3|0]=a>>>24}function Kl(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;f=F-16|0;F=f;i[a+80>>2]=0;i[a+84>>2]=0;c=a+76|0;d=i[c>>2];i[c>>2]=0;if(d){bp(d)}i[a+68>>2]=0;i[a+72>>2]=0;c=a- -64|0;d=i[c>>2];i[c>>2]=0;if(d){bp(d)}c=i[b+4>>2];d=i[b>>2];e=o((c-d|0)/12|0,3);h=i[a>>2];g=i[a+4>>2]-h>>2;a:{if(e>>>0>g>>>0){Ai(a,e-g|0);d=i[b>>2];c=i[b+4>>2];break a}if(e>>>0>=g>>>0){break a}i[a+4>>2]=h+(e<<2)}if((c|0)!=(d|0)){b=(c-d|0)/12|0;g=b>>>0>1?b:1;h=i[a>>2];b=0;while(1){e=o(b,12);c=e+h|0;e=d+e|0;i[c>>2]=i[e>>2];i[c+4>>2]=i[e+4>>2];i[c+8>>2]=i[e+8>>2];b=b+1|0;if((g|0)!=(b|0)){continue}break}}i[f+12>>2]=-1;b=Ll(a,f+12|0);if(b){Ml(a);Nl(a,i[f+12>>2])}F=f+16|0;return b}function Fd(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=F-32|0;F=d;h=1;while(1){g=o(c,12)+a|0;e=g- -64|0;Jb(i[e>>2],b);c=c+1|0;if(i[e>>2]){f=mf(d);pf(f);e=i[e>>2]-c|0;if((e|0)>=0){k=g+60|0;while(1){g=0;while(1){j=e+g|0;qf(f,i[i[k>>2]+(j>>>3&536870908)>>2]>>>j&1);g=g+1|0;if((h|0)!=(g|0)){continue}break}e=e-c|0;if((e|0)>=0){continue}break}}sf(f,b);nf(f)}h=h+1|0;if((c|0)!=4){continue}break}i[d>>2]=i[a+12>>2];h=i[b+20>>2];c=h;f=i[b+16>>2];if((c|0)<0?1:(c|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],d,d+4|0);f=i[b+16>>2];c=i[b+20>>2]}i[d>>2]=i[a+16>>2];if((c|0)<0?1:(c|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],d,d+4|0)}F=d+32|0;return 1}function ja(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,k=0;d=i[b+88>>2];if(!(!d|i[d>>2]!=1)){c=i[d+8>>2];i[a+4>>2]=j[c|0]|j[c+1|0]<<8|(j[c+2|0]<<16|j[c+3|0]<<24);f=a+8|0;h=i[a+8>>2];e=i[a+12>>2]-h>>2;c=g[b+24|0];a:{if(e>>>0>>0){ka(f,c-e|0);c=j[b+24|0];break a}if(e>>>0<=c>>>0){break a}i[a+12>>2]=h+(c<<2)}h=1;e=i[d+8>>2];b:{if(c<<24>>24<1){b=4;break b}b=c&255;k=b>>>0>1?b:1;f=i[f>>2];c=0;b=4;while(1){d=b+e|0;m[f+(c<<2)>>2]=(Cp(2,j[d|0]|j[d+1|0]<<8|(j[d+2|0]<<16|j[d+3|0]<<24)),Gp());b=b+4|0;c=c+1|0;if((k|0)!=(c|0)){continue}break}}c=a;a=b+e|0;m[c+20>>2]=(Cp(2,j[a|0]|j[a+1|0]<<8|(j[a+2|0]<<16|j[a+3|0]<<24)),Gp())}return h|0}function Ce(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=F-32|0;F=d;h=1;while(1){g=o(c,12)+a|0;e=g+48|0;Jb(i[e>>2],b);c=c+1|0;if(i[e>>2]){f=mf(d);pf(f);e=i[e>>2]-c|0;if((e|0)>=0){k=g+44|0;while(1){g=0;while(1){j=e+g|0;qf(f,i[i[k>>2]+(j>>>3&536870908)>>2]>>>j&1);g=g+1|0;if((h|0)!=(g|0)){continue}break}e=e-c|0;if((e|0)>=0){continue}break}}sf(f,b);nf(f)}h=h+1|0;if((c|0)!=4){continue}break}i[d>>2]=i[a+12>>2];h=i[b+20>>2];c=h;f=i[b+16>>2];if((c|0)<0?1:(c|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],d,d+4|0);f=i[b+16>>2];c=i[b+20>>2]}i[d>>2]=i[a+24>>2];if((c|0)<0?1:(c|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],d,d+4|0)}F=d+32|0;return 1}function pa(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,h=0,j=0,k=0,l=p(0),n=0,o=0,q=0,r=0,s=0,t=0,u=0,v=0;h=F-16|0;F=h;if(i[c+28>>2]==9){d=i[a+4>>2];e=g[c+24|0];j=e<<2;k=ho((e&1073741823)!=(e|0)?-1:j);n=dk(h+8|0);f=n;l=m[a+20>>2];d=-1<=1){m[f>>2]=l/p(d|0)}f=(d|0)>0;a:{if(!f){break a}o=i[c+80>>2];if(!o){break a}t=i[i[b>>2]>>2]+i[b+48>>2]|0;u=(e|0)<1;d=0;while(1){if(!u){v=i[a+8>>2];l=m[n>>2];b=0;while(1){q=b<<2;m[q+k>>2]=p(l*p(i[(d<<2)+t>>2]))+m[v+q>>2];d=d+1|0;b=b+1|0;if((e|0)!=(b|0)){continue}break}}hp(i[i[c+64>>2]>>2]+r|0,k,j);r=j+r|0;s=s+1|0;if((s|0)!=(o|0)){continue}break}}bp(k)}F=h+16|0;return f|0}function xn(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0;f=F-16|0;F=f;Ep(+b);h=Bp(1)|0;g=Bp(0)|0;e=h&2147483647;c=e;e=c+ -1048576|0;d=g;if(d>>>0<0){e=e+1|0}a:{if((e|0)==2145386495|e>>>0<2145386495){j=d<<28;e=(c&15)<<28|d>>>4;c=(c>>>4|0)+1006632960|0;d=e;c=d>>>0<0?c+1|0:c;break a}if((c|0)==2146435072&d>>>0>=0|c>>>0>2146435072){j=g<<28;e=g;c=h;g=c>>>4|0;d=(c&15)<<28|e>>>4;c=g|2147418112;break a}if(!(c|d)){d=0;c=0;break a}e=c;c=(c|0)==1&d>>>0<0|c>>>0<1?r(g)+32|0:r(c);sn(f,d,e,0,0,c+49|0);k=i[f>>2];j=i[f+4>>2];d=i[f+8>>2];c=i[f+12>>2]^65536|15372-c<<16}i[a>>2]=k;i[a+4>>2]=j;i[a+8>>2]=d;i[a+12>>2]=h&-2147483648|c;F=f+16|0}function Vh(a,b,c){var d=0,e=0,f=0,g=0,h=0;f=c-b|0;g=f>>2;d=i[a+8>>2];e=i[a>>2];if(g>>>0<=d-e>>2>>>0){f=i[a+4>>2];d=f-e|0;h=d>>2;d=g>>>0>h>>>0?b+d|0:c;if((d|0)!=(b|0)){while(1){i[e>>2]=i[b>>2];e=e+4|0;b=b+4|0;if((d|0)!=(b|0)){continue}break}}if(g>>>0>h>>>0){b=c-d|0;if((b|0)>=1){f=hp(f,d,b)+b|0}i[a+4>>2]=f;return}i[a+4>>2]=e;return}if(e){i[a+4>>2]=e;bp(e);i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;d=0}a:{if(g>>>0>=1073741824){break a}c=d>>1;c=d>>2>>>0<536870911?c>>>0>>0?g:c:1073741823;if(c>>>0>=1073741824){break a}e=c<<2;c=ho(e);i[a>>2]=c;i[a+4>>2]=c;i[a+8>>2]=c+e;if((f|0)>=1){c=hp(c,b,f)+f|0}i[a+4>>2]=c;return}Ho();x()}function bc(a){var b=0,c=0,d=0,e=0;d=i[a+1176>>2];if(d){b=i[a+1180>>2];c=d;a:{if((d|0)==(b|0)){break a}while(1){c=b+ -12|0;e=i[c>>2];if(e){i[b+ -8>>2]=e;bp(e)}b=c;if((b|0)!=(d|0)){continue}break}c=i[a+1176>>2]}i[a+1180>>2]=d;bp(c)}d=i[a+1164>>2];if(d){b=i[a+1168>>2];c=d;b:{if((d|0)==(b|0)){break b}while(1){c=b+ -12|0;e=i[c>>2];if(e){i[b+ -8>>2]=e;bp(e)}b=c;if((b|0)!=(d|0)){continue}break}c=i[a+1164>>2]}i[a+1168>>2]=d;bp(c)}b=i[a+1152>>2];if(b){i[a+1156>>2]=b;bp(b)}b=i[a+1140>>2];if(b){i[a+1144>>2]=b;bp(b)}b=i[a+1128>>2];if(b){i[a+1132>>2]=b;bp(b)}jf(a+1108|0);jf(a+1088|0);jf(a+1068|0);nf(a+1036|0);mc(a+12|0)}function Ti(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0;d=(c>>>0)/3|0;Ui(i[a+12>>2],i[(i[i[a+8>>2]+96>>2]+o(d,12)|0)+(c-o(d,3)<<2)>>2]);f=i[a+4>>2];d=i[f+4>>2];a:{b:{c:{if((d|0)!=i[f+8>>2]){i[d>>2]=c;i[f+4>>2]=d+4;break c}h=i[f>>2];j=d-h|0;g=j>>2;e=g+1|0;if(e>>>0>=1073741824){break b}d=j>>1;e=g>>>0<536870911?d>>>0>>0?e:d:1073741823;d=0;d:{if(!e){break d}if(e>>>0>=1073741824){break a}d=ho(e<<2)}g=d+(g<<2)|0;i[g>>2]=c;c=d+(e<<2)|0;e=g+4|0;if((j|0)>=1){hp(d,h,j)}i[f+8>>2]=c;i[f+4>>2]=e;i[f>>2]=d;if(!h){break c}bp(h)}a=i[a+4>>2];i[i[a+12>>2]+(b<<2)>>2]=i[a+24>>2];i[a+24>>2]=i[a+24>>2]+1;return}Ho();x()}za(11708);x()}function Rm(a,b){var c=0,d=0,e=0,f=0,h=0;c=F-208|0;F=c;i[c+204>>2]=b;b=0;ip(c+160|0,0,40);i[c+200>>2]=i[c+204>>2];a:{if((Sm(0,c+200|0,c+80|0,c+160|0)|0)<0){a=-1;break a}b=i[a+76>>2]>=0?1:b;d=i[a>>2];if(g[a+74|0]<=0){i[a>>2]=d&-33}h=d&32;b:{if(i[a+48>>2]){e=Sm(a,c+200|0,c+80|0,c+160|0);break b}i[a+48>>2]=80;i[a+16>>2]=c+80;i[a+28>>2]=c;i[a+20>>2]=c;d=i[a+44>>2];i[a+44>>2]=c;f=Sm(a,c+200|0,c+80|0,c+160|0);e=f;if(!d){break b}I[i[a+36>>2]](a,0,0)|0;i[a+48>>2]=0;i[a+44>>2]=d;i[a+28>>2]=0;i[a+16>>2]=0;d=i[a+20>>2];i[a+20>>2]=0;e=d?f:-1}d=e;e=a;a=i[a>>2];i[e>>2]=a|h;a=a&32?-1:d;if(!b){break a}}F=c+208|0;return a}function vf(a,b,c){var d=0,e=0,f=0,g=0,h=0;f=F-16|0;F=f;g=Xj(a);d=a+16|0;i[d>>2]=0;i[d+4>>2]=0;i[a+12>>2]=d;d=Xj(a+24|0);if((g|0)!=(b+4|0)){xf(a,i[b+4>>2],b+8|0)}if((d|0)!=(b+28|0)){xf(d,i[b+28>>2],b+32|0)}d=0;i[f+12>>2]=0;e=i[c+8>>2];if((i[c+12>>2]-e|0)>=1){g=b+20|0;while(1){b=i[g>>2];a:{if(!b){break a}h=i[i[(d<<2)+e>>2]+56>>2];d=g;while(1){e=i[b+16>>2]<(h|0);d=e?d:b;b=i[(e<<2)+b>>2];if(b){continue}break}if((d|0)==(g|0)|(h|0)>2]){break a}b=yf(a,f+12|0);if((b|0)==(d+20|0)){break a}xf(b,i[d+20>>2],d+24|0)}d=i[f+12>>2]+1|0;i[f+12>>2]=d;e=i[c+8>>2];if((d|0)>2]-e>>2){continue}break}}F=f+16|0}function _e(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0;f=F-32|0;F=f;d=d-e|0;if((d|0)>=1){j=a+8|0;l=0-e<<2;while(1){k=d<<2;g=k+b|0;h=g+l|0;m=i[h>>2];h=i[h+4>>2];n=i[g+4>>2];i[f+16>>2]=i[g>>2];i[f+20>>2]=n;i[f+8>>2]=m;i[f+12>>2]=h;ze(f+24|0,j,f+16|0,f+8|0);g=c+k|0;i[g>>2]=i[f+24>>2];i[g+4>>2]=i[f+28>>2];d=d-e|0;if((d|0)>0){continue}break}}d=(e&1073741823)!=(e|0)?-1:e<<2;d=ip(ho(d),0,d);e=i[d>>2];g=i[d+4>>2];j=i[b+4>>2];i[f+16>>2]=i[b>>2];i[f+20>>2]=j;i[f+8>>2]=e;i[f+12>>2]=g;ze(f+24|0,a+8|0,f+16|0,f+8|0);i[c>>2]=i[f+24>>2];i[c+4>>2]=i[f+28>>2];bp(d);F=f+32|0;return 1}function dc(a,b,c,d,e){var f=0,g=0,h=0,j=0;f=F-32|0;F=f;i[a>>2]=i[d>>2];d=i[c>>2]-i[b>>2]|0;i[a+4>>2]=d;g=i[e+20>>2];if((g|0)<0?1:(g|0)<=0?l[e+16>>2]<=0:0){d=a+4|0;ca(e,i[e+4>>2],a,d);g=i[e+20>>2];if((g|0)<0?1:(g|0)<=0?l[e+16>>2]<=0:0){ca(e,i[e+4>>2],d,d+4|0)}d=i[d>>2]}if(d){kc(a+12|0);d=a+1068|0;kf(d);g=a+1088|0;kf(g);h=a+1108|0;kf(h);i[f+24>>2]=i[b+8>>2];j=i[b+4>>2];i[f+16>>2]=i[b>>2];i[f+20>>2]=j;i[f+8>>2]=i[c+8>>2];b=i[c+4>>2];i[f>>2]=i[c>>2];i[f+4>>2]=b;pc(a,f+16|0,f);c=0;while(1){sf(((c<<5)+a|0)+12|0,e);c=c+1|0;if((c|0)!=32){continue}break}sf(a+1036|0,e);lf(d,e);lf(g,e);lf(h,e)}F=f+32|0;return 1}function ac(a,b,c,d,e){var f=0,g=0,h=0,j=0;f=F-32|0;F=f;i[a>>2]=i[d>>2];d=i[c>>2]-i[b>>2]|0;i[a+4>>2]=d;g=i[e+20>>2];if((g|0)<0?1:(g|0)<=0?l[e+16>>2]<=0:0){d=a+4|0;ca(e,i[e+4>>2],a,d);g=i[e+20>>2];if((g|0)<0?1:(g|0)<=0?l[e+16>>2]<=0:0){ca(e,i[e+4>>2],d,d+4|0)}d=i[d>>2]}if(d){kc(a+12|0);d=a+1068|0;kf(d);g=a+1088|0;kf(g);h=a+1108|0;kf(h);i[f+24>>2]=i[b+8>>2];j=i[b+4>>2];i[f+16>>2]=i[b>>2];i[f+20>>2]=j;i[f+8>>2]=i[c+8>>2];b=i[c+4>>2];i[f>>2]=i[c>>2];i[f+4>>2]=b;lc(a,f+16|0,f);c=0;while(1){sf(((c<<5)+a|0)+12|0,e);c=c+1|0;if((c|0)!=32){continue}break}sf(a+1036|0,e);lf(d,e);lf(g,e);lf(h,e)}F=f+32|0;return 1}function _f(a,b){var c=0,d=0,e=0,f=0;f=F-16|0;F=f;c=ho(16);i[f>>2]=c;i[f+4>>2]=15;i[f+8>>2]=-2147483632;g[c+15|0]=0;d=j[10535]|j[10536]<<8|(j[10537]<<16|j[10538]<<24);e=j[10531]|j[10532]<<8|(j[10533]<<16|j[10534]<<24);g[c+7|0]=e;g[c+8|0]=e>>>8;g[c+9|0]=e>>>16;g[c+10|0]=e>>>24;g[c+11|0]=d;g[c+12|0]=d>>>8;g[c+13|0]=d>>>16;g[c+14|0]=d>>>24;d=j[10528]|j[10529]<<8|(j[10530]<<16|j[10531]<<24);e=j[10524]|j[10525]<<8|(j[10526]<<16|j[10527]<<24);g[c|0]=e;g[c+1|0]=e>>>8;g[c+2|0]=e>>>16;g[c+3|0]=e>>>24;g[c+4|0]=d;g[c+5|0]=d>>>8;g[c+6|0]=d>>>16;g[c+7|0]=d>>>24;Yj(a+4|0,f,b);if(g[f+11|0]<=-1){bp(i[f>>2])}F=f+16|0}function Gf(a,b){var c=0,d=0,e=0,f=0;f=F-16|0;F=f;c=ho(16);i[f>>2]=c;i[f+4>>2]=15;i[f+8>>2]=-2147483632;g[c+15|0]=0;d=j[10479]|j[10480]<<8|(j[10481]<<16|j[10482]<<24);e=j[10475]|j[10476]<<8|(j[10477]<<16|j[10478]<<24);g[c+7|0]=e;g[c+8|0]=e>>>8;g[c+9|0]=e>>>16;g[c+10|0]=e>>>24;g[c+11|0]=d;g[c+12|0]=d>>>8;g[c+13|0]=d>>>16;g[c+14|0]=d>>>24;d=j[10472]|j[10473]<<8|(j[10474]<<16|j[10475]<<24);e=j[10468]|j[10469]<<8|(j[10470]<<16|j[10471]<<24);g[c|0]=e;g[c+1|0]=e>>>8;g[c+2|0]=e>>>16;g[c+3|0]=e>>>24;g[c+4|0]=d;g[c+5|0]=d>>>8;g[c+6|0]=d>>>16;g[c+7|0]=d>>>24;Yj(a+4|0,f,b);if(g[f+11|0]<=-1){bp(i[f>>2])}F=f+16|0}function Xa(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0;f=i[a+8>>2];d=i[a+4>>2];if(f-d>>2>>>0>=b>>>0){e=a;if(b){a=(b<<2)+d|0;while(1){i[d>>2]=i[c>>2];d=d+4|0;if((a|0)!=(d|0)){continue}break}}else{a=d}i[e+4>>2]=a;return}a:{g=i[a>>2];h=d-g|0;e=h>>2;d=e+b|0;if(d>>>0<1073741824){j=e<<2;f=f-g|0;e=f>>1;f=f>>2>>>0<536870911?e>>>0>>0?d:e:1073741823;e=0;b:{if(!f){break b}if(f>>>0>=1073741824){break a}e=ho(f<<2)}d=j+e|0;b=d+(b<<2)|0;c=i[c>>2];while(1){i[d>>2]=c;d=d+4|0;if((b|0)!=(d|0)){continue}break}c=e+(f<<2)|0;if((h|0)>=1){hp(e,g,h)}i[a+8>>2]=c;i[a+4>>2]=b;i[a>>2]=e;if(g){bp(g)}return}Ho();x()}za(1352);x()}function Gb(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0;f=i[a+8>>2];d=i[a+4>>2];if(f-d>>2>>>0>=b>>>0){e=a;if(b){a=(b<<2)+d|0;while(1){i[d>>2]=i[c>>2];d=d+4|0;if((a|0)!=(d|0)){continue}break}}else{a=d}i[e+4>>2]=a;return}a:{g=i[a>>2];h=d-g|0;e=h>>2;d=e+b|0;if(d>>>0<1073741824){j=e<<2;f=f-g|0;e=f>>1;f=f>>2>>>0<536870911?e>>>0>>0?d:e:1073741823;e=0;b:{if(!f){break b}if(f>>>0>=1073741824){break a}e=ho(f<<2)}d=j+e|0;b=d+(b<<2)|0;c=i[c>>2];while(1){i[d>>2]=c;d=d+4|0;if((b|0)!=(d|0)){continue}break}c=e+(f<<2)|0;if((h|0)>=1){hp(e,g,h)}i[a+8>>2]=c;i[a+4>>2]=b;i[a>>2]=e;if(g){bp(g)}return}Ho();x()}za(1520);x()}function Ol(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;e=i[a+8>>2];c=i[a+4>>2];if(e-c>>2>>>0>=b>>>0){d=a;if(b){a=(b<<2)+c|0;while(1){i[c>>2]=i[4128];c=c+4|0;if((a|0)!=(c|0)){continue}break}}else{a=c}i[d+4>>2]=a;return}a:{f=i[a>>2];g=c-f|0;d=g>>2;c=d+b|0;if(c>>>0<1073741824){h=d<<2;e=e-f|0;d=e>>1;e=e>>2>>>0<536870911?d>>>0>>0?c:d:1073741823;d=0;b:{if(!e){break b}if(e>>>0>=1073741824){break a}d=ho(e<<2)}c=h+d|0;b=c+(b<<2)|0;h=i[4128];while(1){i[c>>2]=h;c=c+4|0;if((b|0)!=(c|0)){continue}break}c=d+(e<<2)|0;if((g|0)>=1){hp(d,f,g)}i[a+8>>2]=c;i[a+4>>2]=b;i[a>>2]=d;if(f){bp(f)}return}Ho();x()}za(16516);x()}function Ie(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0;c=F-48|0;F=c;i[c+40>>2]=i[a- -64>>2];f=i[b+20>>2];if((f|0)<0?1:(f|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],c+40|0,c+44|0)}f=mf(c+8|0);pf(f);if(i[c+40>>2]>=1){d=1;while(1){g=d;d=i[i[a+60>>2]+(e>>>3&536870908)>>2]>>>e|0;qf(f,(g^d^-1)&1);d=d&1;e=e+1|0;if((e|0)>2]){continue}break}}sf(f,b);i[c+44>>2]=i[a+12>>2];d=i[b+20>>2];e=d;d=i[b+16>>2];if((e|0)<0?1:(e|0)<=0?d>>>0<=0:0){ca(b,i[b+4>>2],c+44|0,c+48|0);d=i[b+16>>2];e=i[b+20>>2]}i[c+44>>2]=i[a+24>>2];if((e|0)<0?1:(e|0)<=0?d>>>0<=0:0){ca(b,i[b+4>>2],c+44|0,c+48|0)}nf(f);F=c+48|0;return 1}function Nd(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0;c=F-48|0;F=c;i[c+40>>2]=i[a+80>>2];f=i[b+20>>2];if((f|0)<0?1:(f|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],c+40|0,c+44|0)}f=mf(c+8|0);pf(f);if(i[c+40>>2]>=1){d=1;while(1){g=d;d=i[i[a+76>>2]+(e>>>3&536870908)>>2]>>>e|0;qf(f,(g^d^-1)&1);d=d&1;e=e+1|0;if((e|0)>2]){continue}break}}sf(f,b);i[c+44>>2]=i[a+12>>2];d=i[b+20>>2];e=d;d=i[b+16>>2];if((e|0)<0?1:(e|0)<=0?d>>>0<=0:0){ca(b,i[b+4>>2],c+44|0,c+48|0);d=i[b+16>>2];e=i[b+20>>2]}i[c+44>>2]=i[a+16>>2];if((e|0)<0?1:(e|0)<=0?d>>>0<=0:0){ca(b,i[b+4>>2],c+44|0,c+48|0)}nf(f);F=c+48|0;return 1}function Lf(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,k=0;d=ho(40);c=d+16|0;ro(c,b);ro(d+28|0,b+12|0);b=i[a+4>>2];a:{b:{if(b){e=c;c=j[d+27|0];f=c<<24>>24<0;k=f?i[d+16>>2]:e;f=f?i[d+20>>2]:c;while(1){e=j[b+27|0];c=e<<24>>24<0;c:{d:{e:{f:{e=c?i[b+20>>2]:e;g=e>>>0>>0?e:f;if(g){h=b+16|0;c=Km(k,c?i[h>>2]:h,g);if(c){break f}}if(f>>>0>>0){break e}break d}if((c|0)>-1){break d}}c=i[b>>2];if(!c){break b}break c}c=i[b+4>>2];if(c){break c}c=b+4|0;break a}b=c;continue}}b=a+4|0}c=b}i[d+8>>2]=b;i[d>>2]=0;i[d+4>>2]=0;i[c>>2]=d;b=i[i[a>>2]>>2];if(b){i[a>>2]=b;d=i[c>>2]}Kf(i[a+4>>2],d);i[a+8>>2]=i[a+8>>2]+1}function We(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0;d=F-32|0;F=d;i[a+48>>2]=f;f=i[a+40>>2];g=i[f>>2];a:{f=i[f+4>>2]-g|0;if((f|0)<1){break a}h=f>>2;f=(f>>>2|0)+ -1|0;if(h>>>0>f>>>0){l=a+44|0;m=a+8|0;while(1){he(l,i[(f<<2)+g>>2],b,f);g=i[a+52>>2];h=i[a+56>>2];j=o(e,f)<<2;k=j+b|0;n=i[k+4>>2];i[d+16>>2]=i[k>>2];i[d+20>>2]=n;i[d+8>>2]=g;i[d+12>>2]=h;ze(d+24|0,m,d+16|0,d+8|0);g=c+j|0;i[g>>2]=i[d+24>>2];i[g+4>>2]=i[d+28>>2];f=f+ -1|0;if((f|0)<0){break a}h=i[a+40>>2];g=i[h>>2];if(i[h+4>>2]-g>>2>>>0>f>>>0){continue}break}}Io();x()}F=d+32|0;return 1}function Je(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0;d=F-32|0;F=d;i[a+48>>2]=f;f=i[a+40>>2];g=i[f>>2];a:{f=i[f+4>>2]-g|0;if((f|0)<1){break a}h=f>>2;f=(f>>>2|0)+ -1|0;if(h>>>0>f>>>0){l=a+44|0;m=a+8|0;while(1){Pd(l,i[(f<<2)+g>>2],b,f);g=i[a+52>>2];h=i[a+56>>2];j=o(e,f)<<2;k=j+b|0;n=i[k+4>>2];i[d+16>>2]=i[k>>2];i[d+20>>2]=n;i[d+8>>2]=g;i[d+12>>2]=h;ze(d+24|0,m,d+16|0,d+8|0);g=c+j|0;i[g>>2]=i[d+24>>2];i[g+4>>2]=i[d+28>>2];f=f+ -1|0;if((f|0)<0){break a}h=i[a+40>>2];g=i[h>>2];if(i[h+4>>2]-g>>2>>>0>f>>>0){continue}break}}Io();x()}F=d+32|0;return 1}function Ac(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;a:{f=i[a+4>>2];b:{if((f|0)!=i[a>>2]){c=f;break b}e=i[a+8>>2];c=i[a+12>>2];if(e>>>0>>0){d=((c-e>>2)+1|0)/2<<2;c=d+e|0;g=e-f|0;if(g){c=c-g|0;jp(c,f,g);e=i[a+8>>2]}i[a+4>>2]=c;i[a+8>>2]=e+d;break b}c=c-f|0;d=c?c>>1:1;if(d>>>0>=1073741824){break a}c=d<<2;h=ho(c);g=h+c|0;c=(d+3&-4)+h|0;j=c;d=e-f|0;if(d){j=c+d|0;e=c;d=f;while(1){i[e>>2]=i[d>>2];d=d+4|0;e=e+4|0;if((j|0)!=(e|0)){continue}break}}i[a+12>>2]=g;i[a+8>>2]=j;i[a+4>>2]=c;i[a>>2]=h;if(!f){break b}bp(f);c=i[a+4>>2]}i[c+ -4>>2]=i[b>>2];i[a+4>>2]=i[a+4>>2]+ -4;return}za(1752);x()}function gc(a){var b=0,c=0,d=0,e=0;d=i[a+152>>2];if(d){b=i[a+156>>2];c=d;a:{if((d|0)==(b|0)){break a}while(1){c=b+ -12|0;e=i[c>>2];if(e){i[b+ -8>>2]=e;bp(e)}b=c;if((b|0)!=(d|0)){continue}break}c=i[a+152>>2]}i[a+156>>2]=d;bp(c)}d=i[a+140>>2];if(d){b=i[a+144>>2];c=d;b:{if((d|0)==(b|0)){break b}while(1){c=b+ -12|0;e=i[c>>2];if(e){i[b+ -8>>2]=e;bp(e)}b=c;if((b|0)!=(d|0)){continue}break}c=i[a+140>>2]}i[a+144>>2]=d;bp(c)}b=i[a+128>>2];if(b){i[a+132>>2]=b;bp(b)}b=i[a+116>>2];if(b){i[a+120>>2]=b;bp(b)}b=i[a+104>>2];if(b){i[a+108>>2]=b;bp(b)}jf(a+84|0);jf(a- -64|0);jf(a+44|0);nf(a+12|0)}function Yl(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;e=i[a+8>>2];c=i[a+4>>2];if(e-c>>2>>>0>=b>>>0){d=a;if(b){a=(b<<2)+c|0;while(1){i[c>>2]=1;c=c+4|0;if((a|0)!=(c|0)){continue}break}}else{a=c}i[d+4>>2]=a;return}a:{f=i[a>>2];g=c-f|0;d=g>>2;c=d+b|0;if(c>>>0<1073741824){h=d<<2;e=e-f|0;d=e>>1;e=e>>2>>>0<536870911?d>>>0>>0?c:d:1073741823;d=0;b:{if(!e){break b}if(e>>>0>=1073741824){break a}d=ho(e<<2)}c=h+d|0;b=c+(b<<2)|0;while(1){i[c>>2]=1;c=c+4|0;if((b|0)!=(c|0)){continue}break}c=d+(e<<2)|0;if((g|0)>=1){hp(d,f,g)}i[a+8>>2]=c;i[a+4>>2]=b;i[a>>2]=d;if(f){bp(f)}return}Ho();x()}za(16648);x()}function um(a,b,c){var d=0,e=0,f=0,h=0,k=0,l=0,m=0,n=0;d=F-48|0;F=d;e=uc(a,b);if((e|0)!=(a+4|0)){pm(a,e)}i[d+8>>2]=0;i[d>>2]=0;i[d+4>>2]=0;f=i[c+4>>2];e=g[c+11|0];h=e&255;k=(e|0)<0?f:h;if(k){Qj(d,k);f=i[c+4>>2];l=i[d>>2];h=j[c+11|0];e=h}m=i[c>>2];k=c;c=e<<24>>24<0;hp(l,c?m:k,c?f:h);e=ro(d+16|0,b);i[d+36>>2]=0;i[d+28>>2]=0;i[d+32>>2]=0;c=i[d>>2];f=i[d+4>>2]-c|0;b=0;a:{if(!f){break a}Qj(d+28|0,f);c=i[d>>2];n=i[d+4>>2]-c|0;b=i[d+28>>2]}hp(b,c,n);qm(d+40|0,a,e,d+16|0);a=i[d+28>>2];if(a){i[d+32>>2]=a;bp(a)}if(g[d+27|0]<=-1){bp(i[d+16>>2])}a=i[d>>2];if(a){i[d+4>>2]=a;bp(a)}F=d+48|0}function jc(a){var b=0,c=0,d=0,e=0;d=i[a+140>>2];if(d){b=i[a+144>>2];c=d;a:{if((d|0)==(b|0)){break a}while(1){c=b+ -12|0;e=i[c>>2];if(e){i[b+ -8>>2]=e;bp(e)}b=c;if((b|0)!=(d|0)){continue}break}c=i[a+140>>2]}i[a+144>>2]=d;bp(c)}d=i[a+128>>2];if(d){b=i[a+132>>2];c=d;b:{if((d|0)==(b|0)){break b}while(1){c=b+ -12|0;e=i[c>>2];if(e){i[b+ -8>>2]=e;bp(e)}b=c;if((b|0)!=(d|0)){continue}break}c=i[a+128>>2]}i[a+132>>2]=d;bp(c)}b=i[a+116>>2];if(b){i[a+120>>2]=b;bp(b)}b=i[a+104>>2];if(b){i[a+108>>2]=b;bp(b)}b=i[a+92>>2];if(b){i[a+96>>2]=b;bp(b)}jf(a+72|0);jf(a+52|0);jf(a+32|0);jf(a+12|0)}function xc(a,b){var c=0,d=0,e=0,f=0;e=i[a+16>>2];if(b){i[a+12>>2]=i[a+12>>2]|-2147483648>>>e}b=e+1|0;i[a+16>>2]=b;a:{b:{if((b|0)==32){b=i[a+4>>2];c:{if((b|0)!=i[a+8>>2]){i[b>>2]=i[a+12>>2];i[a+4>>2]=b+4;break c}e=i[a>>2];f=b-e|0;d=f>>2;c=d+1|0;if(c>>>0>=1073741824){break b}b=f>>1;c=d>>>0<536870911?b>>>0>>0?c:b:1073741823;b=0;d:{if(!c){break d}if(c>>>0>=1073741824){break a}b=ho(c<<2)}d=b+(d<<2)|0;i[d>>2]=i[a+12>>2];c=b+(c<<2)|0;d=d+4|0;if((f|0)>=1){hp(b,e,f)}i[a+8>>2]=c;i[a+4>>2]=d;i[a>>2]=b;if(!e){break c}bp(e)}i[a+12>>2]=0;i[a+16>>2]=0}return}Ho();x()}za(1752);x()}function Of(a,b,c){var d=0,e=0,f=0,g=0,h=0,k=0,l=0;f=a+4|0;a:{a=i[a+4>>2];if(a){e=j[c+11|0];d=e<<24>>24<0;k=d?i[c>>2]:c;e=d?i[c+4>>2]:e;while(1){c=j[a+27|0];d=c<<24>>24<0;c=d?i[a+20>>2]:c;l=c>>>0>>0;b:{c:{d:{e:{f:{h=l?c:e;g:{if(h){g=a+16|0;d=d?i[g>>2]:g;g=Km(k,d,h);if(!g){if(e>>>0>>0){break g}break f}if((g|0)>-1){break f}break g}if(e>>>0>=c>>>0){break e}}c=i[a>>2];if(c){break b}i[b>>2]=a;return a}c=Km(d,k,h);if(c){break d}}if(l){break c}break a}if((c|0)>-1){break a}}f=a+4|0;c=i[a+4>>2];if(!c){break a}a=f}f=a;a=c;continue}}i[b>>2]=f;return f}i[b>>2]=a;return f}function zc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;a:{e=i[a+8>>2];b:{if((e|0)!=i[a+12>>2]){break b}f=i[a+4>>2];g=i[a>>2];if(f>>>0>g>>>0){g=((f-g>>2)+1|0)/-2<<2;c=g+f|0;d=e-f|0;if(d){jp(c,f,d);f=i[a+4>>2]}e=c+d|0;i[a+8>>2]=e;i[a+4>>2]=f+g;break b}c=e-g|0;c=c?c>>1:1;if(c>>>0>=1073741824){break a}d=c<<2;h=ho(d);j=h+d|0;d=e-f|0;c=(c&-4)+h|0;e=c;if(d){e=c+d|0;d=c;while(1){i[d>>2]=i[f>>2];f=f+4|0;d=d+4|0;if((d|0)!=(e|0)){continue}break}}i[a+12>>2]=j;i[a+8>>2]=e;i[a+4>>2]=c;i[a>>2]=h;if(!g){break b}bp(g);e=i[a+8>>2]}i[e>>2]=i[b>>2];i[a+8>>2]=i[a+8>>2]+4;return}za(1752);x()}function Fa(a,b){var c=0;c=i[b+4>>2];i[a>>2]=i[b>>2];i[a+4>>2]=c;c=i[b+60>>2];i[a+56>>2]=i[b+56>>2];i[a+60>>2]=c;c=i[b+52>>2];i[a+48>>2]=i[b+48>>2];i[a+52>>2]=c;c=i[b+44>>2];i[a+40>>2]=i[b+40>>2];i[a+44>>2]=c;c=i[b+36>>2];i[a+32>>2]=i[b+32>>2];i[a+36>>2]=c;c=i[b+28>>2];i[a+24>>2]=i[b+24>>2];i[a+28>>2]=c;c=i[b+20>>2];i[a+16>>2]=i[b+16>>2];i[a+20>>2]=c;c=i[b+12>>2];i[a+8>>2]=i[b+8>>2];i[a+12>>2]=c;i[a+88>>2]=0;i[a+64>>2]=0;i[a+68>>2]=0;i[a+72>>2]=0;i[a+76>>2]=0;g[a+77|0]=0;g[a+78|0]=0;g[a+79|0]=0;g[a+80|0]=0;g[a+81|0]=0;g[a+82|0]=0;g[a+83|0]=0;g[a+84|0]=0;return a}function hm(a,b){var c=0,d=0,e=0,f=0,h=0,k=0;d=F-16|0;F=d;a:{b:{c=j[b+11|0];e=c<<24>>24;c:{if((e|0)<=-1){c=i[b+4>>2];if(c>>>0>255){break a}if(!c){break c}break b}if(e){break b}}g[d+15|0]=0;b=i[a+20>>2];if((b|0)<0?1:(b|0)<=0?l[a+16>>2]<=0:0){ca(a,i[a+4>>2],d+15|0,d+16|0)}h=1;break a}g[d+14|0]=c;h=i[a+20>>2];c=h;f=i[a+16>>2];if((c|0)<0?1:(c|0)<=0?f>>>0<=0:0){ca(a,i[a+4>>2],d+14|0,d+15|0);f=i[a+16>>2];c=i[a+20>>2];e=j[b+11|0]}h=1;if((c|0)>0?1:(c|0)>=0?f>>>0>0:0){break a}f=a;k=i[a+4>>2];a=e<<24>>24<0;c=a?i[b>>2]:b;ca(f,k,c,c+(a?i[b+4>>2]:e&255)|0)}F=d+16|0;return h}function tm(a,b,c){var d=0,e=0,f=0,h=0;d=F-48|0;F=d;f=uc(a,b);if((f|0)!=(a+4|0)){pm(a,f)}e=ho(8);i[d>>2]=e;f=e+8|0;i[d+8>>2]=f;i[d+4>>2]=f;f=i[c+4>>2];c=i[c>>2];g[e|0]=c;g[e+1|0]=c>>>8;g[e+2|0]=c>>>16;g[e+3|0]=c>>>24;g[e+4|0]=f;g[e+5|0]=f>>>8;g[e+6|0]=f>>>16;g[e+7|0]=f>>>24;e=0;f=ro(d+16|0,b);i[d+36>>2]=0;i[d+28>>2]=0;i[d+32>>2]=0;b=i[d>>2];c=i[d+4>>2]-b|0;h=0;a:{if(!c){break a}Qj(d+28|0,c);b=i[d>>2];e=i[d+4>>2]-b|0;h=i[d+28>>2]}hp(h,b,e);qm(d+40|0,a,f,d+16|0);a=i[d+28>>2];if(a){i[d+32>>2]=a;bp(a)}if(g[d+27|0]<=-1){bp(i[d+16>>2])}a=i[d>>2];if(a){bp(a)}F=d+48|0}function uc(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,k=0,l=0;g=a+4|0;a=i[a+4>>2];a:{b:{if(!a){break b}d=j[b+11|0];c=d<<24>>24<0;k=c?i[b>>2]:b;e=c?i[b+4>>2]:d;c=g;while(1){b=j[a+27|0];l=b<<24>>24<0;h=l?i[a+20>>2]:b;f=e>>>0>>0;d=f?e:h;c:{if(d){b=a+16|0;b=Km(l?i[b>>2]:b,k,d);if(b){break c}}b=h>>>0>>0?-1:f}c=(b|0)<0?c:a;a=i[(b>>>29&4)+a>>2];if(a){continue}break}if((c|0)==(g|0)){break b}a=j[c+27|0];f=a<<24>>24<0;d:{d=f?i[c+20>>2]:a;b=d>>>0>>0?d:e;if(b){a=c+16|0;a=Km(k,f?i[a>>2]:a,b);if(a){break d}}if(e>>>0>>0){break b}break a}if((a|0)>-1){break a}}c=g}return c}function Cl(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;e=F-16|0;F=e;a:{if(!a){break a}i[e+8>>2]=0;i[e>>2]=0;i[e+4>>2]=0;h=a+96|0;g=i[a+96>>2];d=(i[a+100>>2]-g|0)/12|0;b:{if(d>>>0>>0){Dl(h,b-d|0,e);break b}if(d>>>0>b>>>0){i[a+100>>2]=g+o(b,12)}if(!b){break a}}g=i[h>>2];d=0;while(1){j=o(d,12);f=j+c|0;k=i[f>>2];l=i[f+8>>2];m=i[f+4>>2];f=(i[a+100>>2]-g|0)/12|0;c:{if(f>>>0>d>>>0){d=d+1|0;break c}i[e+8>>2]=0;i[e>>2]=0;i[e+4>>2]=0;d=d+1|0;Dl(h,d-f|0,e);g=i[h>>2]}f=g+j|0;i[f+8>>2]=l;i[f+4>>2]=m;i[f>>2]=k;if((b|0)!=(d|0)){continue}break}}F=e+16|0;return(a|0)!=0}function qm(a,b,c,d){var e=0,f=0,h=0,j=0,k=0,l=0,m=0;h=F-16|0;F=h;l=a;j=Of(b,h+12|0,c);c=i[j>>2];if(c){b=0}else{c=ho(40);e=d+8|0;i[c+24>>2]=i[e>>2];f=i[d+4>>2];i[c+16>>2]=i[d>>2];i[c+20>>2]=f;i[d>>2]=0;i[d+4>>2]=0;i[e>>2]=0;i[c+36>>2]=0;i[c+28>>2]=0;i[c+32>>2]=0;f=i[d+12>>2];k=i[d+16>>2]-f|0;e=0;a:{if(!k){break a}e=c+28|0;Qj(e,k);f=i[d+12>>2];m=i[d+16>>2]-f|0;e=i[e>>2]}hp(e,f,m);i[c+8>>2]=i[h+12>>2];i[c>>2]=0;i[c+4>>2]=0;i[j>>2]=c;e=i[i[b>>2]>>2];d=c;b:{if(!e){break b}i[b>>2]=e;d=i[j>>2]}Kf(i[b+4>>2],d);i[b+8>>2]=i[b+8>>2]+1;b=1}g[l+4|0]=b;i[a>>2]=c;F=h+16|0}function ra(a,b,c){var d=0,e=0,f=0,g=0,h=0;f=c-b|0;g=f>>2;d=i[a+8>>2];e=i[a>>2];if(g>>>0<=d-e>>2>>>0){d=i[a+4>>2]-e|0;f=d>>2;d=g>>>0>f>>>0?b+d|0:c;h=d-b|0;if(h){jp(e,b,h)}if(g>>>0>f>>>0){b=i[a+4>>2];c=c-d|0;if((c|0)>=1){b=hp(b,d,c)+c|0}i[a+4>>2]=b;return}i[a+4>>2]=e+h;return}if(e){i[a+4>>2]=e;bp(e);i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;d=0}a:{if(g>>>0>=1073741824){break a}c=d>>1;c=d>>2>>>0<536870911?c>>>0>>0?g:c:1073741823;if(c>>>0>=1073741824){break a}e=c<<2;c=ho(e);i[a>>2]=c;i[a+4>>2]=c;i[a+8>>2]=c+e;if((f|0)>=1){c=hp(c,b,f)+f|0}i[a+4>>2]=c;return}Ho();x()}function Rl(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;f=-1;a:{if((b|0)==-1){break a}e=i[i[a+24>>2]+(b<<2)>>2];if((e|0)==-1){return 0}f=0;c=1;b=e;while(1){f=f+1|0;h=c;b:{if(c&255){c=0;d=b+1|0;b=(d>>>0)%3|0?d:b+ -2|0;if((b|0)==-1){b=e;break b}d=i[i[a+12>>2]+(b<<2)>>2];if((d|0)==-1){b=e;break b}b=e;g=d+1|0;d=(g>>>0)%3|0?g:d+ -2|0;if((d|0)==-1){break b}c=h;b=d;if((e|0)!=(b|0)){break b}break a}b=((b>>>0)%3|0?-1:2)+b|0;if((b|0)==-1){break a}c=i[i[a+12>>2]+(b<<2)>>2];if((c|0)==-1){break a}if((c>>>0)%3|0){b=c+ -1|0}else{b=c+2|0}c=0}if((b|0)!=-1){continue}break}}return f}function Dm(a,b){var c=0,d=0,e=0,f=0;a:{if((b|0)<0){break a}c=i[a+24>>2];d=i[a+28>>2];if((c|0)==(d|0)){break a}while(1){f=c+4|0;e=i[c>>2];if(i[e+24>>2]==(b|0)){b:{if((d|0)!=(f|0)){while(1){b=i[f>>2];i[f>>2]=0;i[c>>2]=b;if(e){yk(e+12|0,i[e+16>>2]);zk(e,i[e+4>>2]);bp(e)}c=c+4|0;f=f+4|0;if((d|0)!=(f|0)){e=i[c>>2];continue}break}d=i[a+28>>2];if((d|0)==(c|0)){break b}}while(1){d=d+ -4|0;b=i[d>>2];i[d>>2]=0;if(b){yk(b+12|0,i[b+16>>2]);zk(b,i[b+4>>2]);bp(b)}if((c|0)!=(d|0)){continue}break}}i[a+28>>2]=c;return}c=f;if((d|0)!=(c|0)){continue}break}}}function ul(a,b,c,d){var e=0,f=0,h=0,j=0;e=F-32|0;F=e;a:{b:{c:{if(!a){break c}f=mp(b);if(f>>>0>=4294967280){break b}d:{e:{if(f>>>0>=11){j=f+16&-16;h=ho(j);i[e+24>>2]=j|-2147483648;i[e+16>>2]=h;i[e+20>>2]=f;break e}g[e+27|0]=f;h=e+16|0;if(!f){break d}}hp(h,b,f)}g[f+h|0]=0;i[e+8>>2]=0;i[e>>2]=0;i[e+4>>2]=0;if(d){if(d>>>0>=1073741824){break a}f=d<<2;b=ho(f);i[e>>2]=b;d=b+(d<<2)|0;i[e+8>>2]=d;hp(b,c,f);i[e+4>>2]=d}rm(a,e+16|0,e);b=i[e>>2];if(b){i[e+4>>2]=b;bp(b)}if(g[e+27|0]>-1){break c}bp(i[e+16>>2])}F=e+32|0;return(a|0)!=0}qo();x()}Ho();x()}function fd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,j=0;d=F-16|0;F=d;a:{if(!Cc(a,b,c)){break a}if(f=(I[i[i[a>>2]+32>>2]](a)|0)==1,g=0,h=i[i[a+8>>2]+28>>2]+ -1>>>0>5,h?f:g){break a}g=d+8|0,h=a,j=gf(c,i[b+48>>2]),f=i[i[a>>2]+48>>2],I[f](g|0,h|0,j|0);b=i[d+8>>2];i[d+8>>2]=0;c=i[a+36>>2];i[a+36>>2]=b;e=1;if(c){I[i[i[c>>2]+4>>2]](c);b=i[d+8>>2];i[d+8>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}b=i[a+36>>2]}if(!b){break a}if(I[i[i[a>>2]+36>>2]](a,b)|0){break a}b=i[a+36>>2];i[a+36>>2]=0;if(!b){break a}I[i[i[b>>2]+4>>2]](b)}F=d+16|0;return e|0}function sc(a){a=a|0;var b=0,c=0,d=0;i[a>>2]=1596;b=i[a+60>>2];if(b){c=i[a- -64>>2];d=b;a:{if((b|0)==(c|0)){break a}while(1){c=c+ -4|0;d=i[c>>2];i[c>>2]=0;if(d){Wb(d)}if((b|0)!=(c|0)){continue}break}d=i[a+60>>2]}i[a+64>>2]=b;bp(d)}b=i[a+48>>2];if(b){i[a+52>>2]=b;bp(b)}b=i[a+36>>2];if(b){c=i[a+40>>2];d=b;b:{if((b|0)==(c|0)){break b}while(1){c=c+ -24|0;I[i[i[c>>2]>>2]](c)|0;if((b|0)!=(c|0)){continue}break}d=i[a+36>>2]}i[a+40>>2]=b;bp(d)}i[a>>2]=1432;b=i[a+16>>2];if(b){i[a+20>>2]=b;bp(b)}b=i[a+4>>2];if(b){i[a+8>>2]=b;bp(b)}return a|0}function Fc(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,k=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;d=i[a+8>>2];e=i[d+40>>2];h=ho((e|0)>-1?e:-1);a:{g=i[b>>2];k=i[b+4>>2];if((g|0)==(k|0)){break a}o=e+h|0;while(1){f=i[(m<<2)+g>>2];e=h;p=i[i[d>>2]>>2];q=i[d+48>>2];n=i[d+40>>2];r=n;s=i[d+44>>2];if(!j[d+84|0]){f=i[i[d+68>>2]+(f<<2)>>2]}d=e;e=up(r,s,f,0)+q|0;f=hp(d,e+p|0,n);e=i[c+20>>2];if((e|0)<0?1:(e|0)<=0?l[c+16>>2]<=0:0){ca(c,i[c+4>>2],f,o);g=i[b>>2];k=i[b+4>>2]}m=m+1|0;if(m>>>0>=k-g>>2>>>0){break a}d=i[a+8>>2];continue}}bp(h);return 1}function fc(a,b,c,d,e){var f=0,g=0,h=0,j=0,k=0;f=F-32|0;F=f;i[a>>2]=i[d>>2];d=i[c>>2]-i[b>>2]|0;i[a+4>>2]=d;g=i[e+20>>2];if((g|0)<0?1:(g|0)<=0?l[e+16>>2]<=0:0){d=a+4|0;ca(e,i[e+4>>2],a,d);g=i[e+20>>2];if((g|0)<0?1:(g|0)<=0?l[e+16>>2]<=0:0){ca(e,i[e+4>>2],d,d+4|0)}d=i[d>>2]}if(d){d=a+12|0;pf(d);g=a+44|0;kf(g);h=a- -64|0;kf(h);j=a+84|0;kf(j);i[f+24>>2]=i[b+8>>2];k=i[b+4>>2];i[f+16>>2]=i[b>>2];i[f+20>>2]=k;i[f+8>>2]=i[c+8>>2];b=i[c+4>>2];i[f>>2]=i[c>>2];i[f+4>>2]=b;qc(a,f+16|0,f);sf(d,e);lf(g,e);lf(h,e);lf(j,e)}F=f+32|0;return 1}function ic(a,b,c,d,e){var f=0,g=0,h=0,j=0,k=0;f=F-32|0;F=f;i[a>>2]=i[d>>2];d=i[c>>2]-i[b>>2]|0;i[a+4>>2]=d;g=i[e+20>>2];if((g|0)<0?1:(g|0)<=0?l[e+16>>2]<=0:0){d=a+4|0;ca(e,i[e+4>>2],a,d);g=i[e+20>>2];if((g|0)<0?1:(g|0)<=0?l[e+16>>2]<=0:0){ca(e,i[e+4>>2],d,d+4|0)}d=i[d>>2]}if(d){d=a+12|0;kf(d);g=a+32|0;kf(g);h=a+52|0;kf(h);j=a+72|0;kf(j);i[f+24>>2]=i[b+8>>2];k=i[b+4>>2];i[f+16>>2]=i[b>>2];i[f+20>>2]=k;i[f+8>>2]=i[c+8>>2];b=i[c+4>>2];i[f>>2]=i[c>>2];i[f+4>>2]=b;rc(a,f+16|0,f);lf(d,e);lf(g,e);lf(h,e);lf(j,e)}F=f+32|0;return 1}function So(a,b,c){var d=0,e=0,f=0;d=F+ -64|0;F=d;f=i[a>>2];e=i[f+ -4>>2];f=i[f+ -8>>2];i[d+20>>2]=0;i[d+16>>2]=b;i[d+12>>2]=a;i[d+8>>2]=c;b=0;ip(d+24|0,0,39);a=a+f|0;a:{if(Qo(e,c,0)){i[d+56>>2]=1;I[i[i[e>>2]+20>>2]](e,d+8|0,a,a,1,0);b=i[d+32>>2]==1?a:0;break a}I[i[i[e>>2]+24>>2]](e,d+8|0,a,1,0);b:{switch(i[d+44>>2]){case 0:b=i[d+48>>2]==1?i[d+36>>2]==1?i[d+40>>2]==1?i[d+28>>2]:0:0:0;break a;case 1:break b;default:break a}}if(i[d+32>>2]!=1){if(i[d+48>>2]|i[d+36>>2]!=1|i[d+40>>2]!=1){break a}}b=i[d+24>>2]}F=d- -64|0;return b}function ve(a,b,c,d){var e=0,f=0;e=ho(128);i[e+4>>2]=b;b=i[c+4>>2];i[e+8>>2]=i[c>>2];i[e+12>>2]=b;b=i[c+12>>2];i[e+16>>2]=i[c+8>>2];i[e+20>>2]=b;i[e+24>>2]=i[c+16>>2];b=i[d+4>>2];i[e+28>>2]=i[d>>2];i[e+32>>2]=b;b=d+8|0;c=b;f=i[c+4>>2];i[e+36>>2]=i[c>>2];i[e+40>>2]=f;i[e+48>>2]=0;i[e+52>>2]=0;i[e>>2]=8916;c=i[d+4>>2];i[e+56>>2]=i[d>>2];i[e+60>>2]=c;d=i[b+4>>2];c=e- -64|0;i[c>>2]=i[b>>2];i[c+4>>2]=d;i[e+88>>2]=1065353216;i[e+92>>2]=-1;i[e+80>>2]=-1;i[e+84>>2]=-1;i[e+72>>2]=1;i[e+76>>2]=-1;i[e+44>>2]=9160;mf(e+96|0);i[a>>2]=e}function ue(a,b,c,d){var e=0,f=0;e=ho(128);i[e+4>>2]=b;b=i[c+4>>2];i[e+8>>2]=i[c>>2];i[e+12>>2]=b;b=i[c+12>>2];i[e+16>>2]=i[c+8>>2];i[e+20>>2]=b;i[e+24>>2]=i[c+16>>2];b=i[d+4>>2];i[e+28>>2]=i[d>>2];i[e+32>>2]=b;b=d+8|0;c=b;f=i[c+4>>2];i[e+36>>2]=i[c>>2];i[e+40>>2]=f;i[e+48>>2]=0;i[e+52>>2]=0;i[e>>2]=7292;c=i[d+4>>2];i[e+56>>2]=i[d>>2];i[e+60>>2]=c;d=i[b+4>>2];c=e- -64|0;i[c>>2]=i[b>>2];i[c+4>>2]=d;i[e+88>>2]=1065353216;i[e+92>>2]=-1;i[e+80>>2]=-1;i[e+84>>2]=-1;i[e+72>>2]=1;i[e+76>>2]=-1;i[e+44>>2]=7552;mf(e+96|0);i[a>>2]=e}function yl(a,b,c,d,e,f){var g=0,h=0,k=0,l=0,m=0;g=F-16|0;F=g;a:{if(!a){b=-1;break a}h=ho(96);Ga(Ea(h),b,d<<24>>24,f,c);i[g>>2]=h;i[g+8>>2]=0;h=zm(a,g);b=i[g>>2];i[g>>2]=0;if(b){Wb(b)}if(c){k=i[i[a+8>>2]+(h<<2)>>2];b=0;while(1){f=b;l=i[i[k>>2]>>2];if(!j[k+84|0]){f=i[i[k+68>>2]+(b<<2)>>2]}m=f;f=i[k+40>>2];hp(o(m,f)+l|0,(o(b,d)<<1)+e|0,f);b=b+1|0;if((c|0)!=(b|0)){continue}break}}d=i[a+80>>2];b:{c:{if(!d){i[a+80>>2]=c;break c}b=-1;if((c|0)!=(d|0)){break b}}b=h}a=i[g+8>>2];i[g+8>>2]=0;if(!a){break a}Wb(a)}F=g+16|0;return b}function wl(a,b,c,d,e,f){var g=0,h=0,k=0,l=0,m=0;g=F-16|0;F=g;a:{if(!a){b=-1;break a}h=ho(96);Ga(Ea(h),b,d<<24>>24,f,c);i[g>>2]=h;i[g+8>>2]=0;h=zm(a,g);b=i[g>>2];i[g>>2]=0;if(b){Wb(b)}if(c){k=i[i[a+8>>2]+(h<<2)>>2];b=0;while(1){f=b;l=i[i[k>>2]>>2];if(!j[k+84|0]){f=i[i[k+68>>2]+(b<<2)>>2]}m=f;f=i[k+40>>2];hp(o(m,f)+l|0,(o(b,d)<<2)+e|0,f);b=b+1|0;if((c|0)!=(b|0)){continue}break}}d=i[a+80>>2];b:{c:{if(!d){i[a+80>>2]=c;break c}b=-1;if((c|0)!=(d|0)){break b}}b=h}a=i[g+8>>2];i[g+8>>2]=0;if(!a){break a}Wb(a)}F=g+16|0;return b}function Kg(a,b,c,d,e){var f=0,g=0,h=0,j=0;h=Ng(a,b,c,e);f=i[e>>2];e=i[f>>2];a:{f=i[f+4>>2]-e>>3;g=i[d>>2];if(f>>>0<=g>>>0){break a}j=i[c>>2];if(f>>>0<=j>>>0){break a}if(l[e+(g<<3)>>2]>2]){i[c>>2]=g;i[d>>2]=j;d=i[c>>2];if(f>>>0<=d>>>0){break a}g=i[b>>2];if(f>>>0<=g>>>0){break a}if(l[e+(d<<3)>>2]>=l[e+(g<<3)>>2]){return h+1|0}i[b>>2]=d;i[c>>2]=g;c=i[b>>2];if(f>>>0<=c>>>0){break a}d=i[a>>2];if(f>>>0<=d>>>0){break a}if(l[e+(c<<3)>>2]>=l[e+(d<<3)>>2]){return h+2|0}i[a>>2]=c;i[b>>2]=d;h=h+3|0}return h}Io();x()}function xl(a,b,c,d,e,f){var g=0,h=0,k=0,l=0,m=0;g=F-16|0;F=g;a:{if(!a){b=-1;break a}h=ho(96);Ga(Ea(h),b,d<<24>>24,f,c);i[g>>2]=h;i[g+8>>2]=0;h=zm(a,g);b=i[g>>2];i[g>>2]=0;if(b){Wb(b)}if(c){k=i[i[a+8>>2]+(h<<2)>>2];b=0;while(1){f=b;l=i[i[k>>2]>>2];if(!j[k+84|0]){f=i[i[k+68>>2]+(b<<2)>>2]}m=f;f=i[k+40>>2];hp(o(m,f)+l|0,o(b,d)+e|0,f);b=b+1|0;if((c|0)!=(b|0)){continue}break}}d=i[a+80>>2];b:{c:{if(!d){i[a+80>>2]=c;break c}b=-1;if((c|0)!=(d|0)){break b}}b=h}a=i[g+8>>2];i[g+8>>2]=0;if(!a){break a}Wb(a)}F=g+16|0;return b}function pd(a,b,c,d,e){var f=0,g=0,h=0,j=0;f=F-32|0;F=f;h=i[i[i[b+4>>2]+8>>2]+(d<<2)>>2];a:{b:{if((I[i[i[b>>2]+8>>2]](b)|0)!=1|c+ -1>>>0>5){break b}j=I[i[i[b>>2]+52>>2]](b)|0;g=I[i[i[b>>2]+60>>2]](b,d)|0;if(!(g?j:0)){i[a>>2]=0;break a}d=I[i[i[b>>2]+56>>2]](b,d)|0;if(d){b=i[b+56>>2];i[f+20>>2]=d;i[f+16>>2]=b;i[f+28>>2]=g;i[f+24>>2]=g+12;qd(a,c,h,e,f+16|0);if(!i[a>>2]){break b}break a}b=i[b+56>>2];i[f+20>>2]=j;i[f+16>>2]=b;i[f+28>>2]=g;i[f+24>>2]=g+12;rd(a,c,h,e,f+16|0);if(i[a>>2]){break a}}i[a>>2]=0}F=f+32|0}function Yo(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;if(Qo(a,i[b+8>>2],e)){Xo(b,c,d);return}a:{if(Qo(a,i[b>>2],e)){if(!(i[b+20>>2]!=(c|0)?i[b+16>>2]!=(c|0):0)){if((d|0)!=1){break a}i[b+32>>2]=1;return}i[b+32>>2]=d;b:{if(i[b+44>>2]==4){break b}h[b+52>>1]=0;a=i[a+8>>2];I[i[i[a>>2]+20>>2]](a,b,c,c,1,e);if(j[b+53|0]){i[b+44>>2]=3;if(!j[b+52|0]){break b}break a}i[b+44>>2]=4}i[b+20>>2]=c;i[b+40>>2]=i[b+40>>2]+1;if(i[b+36>>2]!=1|i[b+24>>2]!=2){break a}g[b+54|0]=1;return}a=i[a+8>>2];I[i[i[a>>2]+24>>2]](a,b,c,d,e)}}function vn(a,b,c,d,e,f){var g=0,h=0,j=0,k=0;a:{if(f&64){c=f+ -64|0;b=c&31;if(32<=(c&63)>>>0){c=0;b=e>>>b|0}else{c=e>>>b|0;b=((1<>>b}d=0;e=0;break a}if(!f){break a}h=e;j=d;k=64-f|0;g=k&31;if(32<=(k&63)>>>0){h=j<>>32-g|h<>>0){g=0;b=c>>>b|0}else{g=c>>>b|0;b=((1<>>b}b=k|b;c=g|h;g=d;d=f&31;if(32<=(f&63)>>>0){h=0;d=e>>>d|0}else{h=e>>>d|0;d=((1<>>d}e=h}i[a>>2]=b;i[a+4>>2]=c;i[a+8>>2]=d;i[a+12>>2]=e}function rm(a,b,c){var d=0,e=0,f=0,h=0;d=F-48|0;F=d;e=uc(a,b);if((e|0)!=(a+4|0)){pm(a,e)}i[d+8>>2]=0;i[d>>2]=0;i[d+4>>2]=0;e=i[c>>2];f=i[c+4>>2]-e|0;if(f){Qj(d,f);e=i[c>>2];c=i[d>>2]}else{c=0}hp(c,e,f);f=ro(d+16|0,b);i[d+36>>2]=0;i[d+28>>2]=0;i[d+32>>2]=0;e=0;b=i[d>>2];c=i[d+4>>2]-b|0;h=0;a:{if(!c){break a}Qj(d+28|0,c);e=i[d+28>>2];b=i[d>>2];h=i[d+4>>2]-b|0}c=h;hp(e,b,c);qm(d+40|0,a,f,d+16|0);a=i[d+28>>2];if(a){i[d+32>>2]=a;bp(a)}if(g[d+27|0]<=-1){bp(i[d+16>>2])}a=i[d>>2];if(a){i[d+4>>2]=a;bp(a)}F=d+48|0}function rn(a,b,c,d,e){var f=0,g=0,h=0,i=0,j=0;h=-1;f=d&2147483647;i=f;g=c;a:{if(!c&(f|0)==2147418112?a|b:(f|0)==2147418112&c>>>0>0|f>>>0>2147418112){break a}f=e&2147483647;j=f;if((f|0)==2147418112?0:f>>>0>2147418112){break a}if(!(a|g|(i|j|b))){return 0}g=d&e;if((g|0)>0?1:(g|0)>=0){if(!c&(d|0)==(e|0)?!b&a>>>0<0|b>>>0<0:(d|0)<(e|0)?1:(d|0)<=(e|0)?c>>>0<0:0){break a}return(a|c)!=0|(d^e|b)!=0}if(!c&(d|0)==(e|0)?!b&a>>>0>0|b>>>0>0:(d|0)>(e|0)?1:(d|0)>=(e|0)?c>>>0>0:0){break a}h=(a|c)!=0|(d^e|b)!=0}return h}function bd(a,b){var c=0,d=0;c=F-32|0;F=c;a:{b:{if(i[a+8>>2]<<5>>>0>=b>>>0){break b}i[c+24>>2]=0;i[c+16>>2]=0;i[c+20>>2]=0;if((b|0)<=-1){break a}b=(b+ -1>>>5|0)+1|0;d=ho(b<<2);i[c+24>>2]=b;i[c+20>>2]=0;i[c+16>>2]=d;b=i[a>>2];i[c+12>>2]=0;i[c+8>>2]=b;d=i[a+4>>2];i[c+4>>2]=d&31;i[c>>2]=b+(d>>>3&536870908);cd(c+16|0,c+8|0,c);b=i[a>>2];i[a>>2]=i[c+16>>2];i[c+16>>2]=b;d=i[a+4>>2];i[a+4>>2]=i[c+20>>2];i[c+20>>2]=d;d=i[a+8>>2];i[a+8>>2]=i[c+24>>2];i[c+24>>2]=d;if(!b){break b}bp(b)}F=c+32|0;return}Ho();x()}function om(a,b,c){var d=0,e=0,f=0,h=0;d=F-48|0;F=d;e=uc(a,b);if((e|0)!=(a+4|0)){pm(a,e)}e=ho(4);i[d>>2]=e;f=e+4|0;i[d+8>>2]=f;i[d+4>>2]=f;c=i[c>>2];g[e|0]=c;g[e+1|0]=c>>>8;g[e+2|0]=c>>>16;g[e+3|0]=c>>>24;f=ro(d+16|0,b);i[d+36>>2]=0;i[d+28>>2]=0;i[d+32>>2]=0;e=0;b=i[d>>2];c=i[d+4>>2]-b|0;h=0;a:{if(!c){break a}Qj(d+28|0,c);e=i[d+28>>2];b=i[d>>2];h=i[d+4>>2]-b|0}c=h;hp(e,b,c);qm(d+40|0,a,f,d+16|0);a=i[d+28>>2];if(a){i[d+32>>2]=a;bp(a)}if(g[d+27|0]<=-1){bp(i[d+16>>2])}a=i[d>>2];if(a){bp(a)}F=d+48|0}function fm(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0;c=F-16|0;F=c;d=i[b+100>>2];h=i[b+96>>2];i[c+8>>2]=0;i[c>>2]=0;i[c+4>>2]=0;d=d-h|0;e=(d|0)/12|0;a:{if(d){if(e>>>0>=357913942){break a}f=ho(d);i[c>>2]=f;g=f+o(e,12)|0;i[c+8>>2]=g;b=0;f=ip(f,0,d-((d+ -12>>>0)%12|0)|0);i[c+4>>2]=g;g=e>>>0>1?e:1;while(1){e=o(b,12);d=e+h|0;j=i[d+4>>2];k=i[d>>2];e=e+f|0;i[e+8>>2]=i[d+8>>2];i[e>>2]=k;i[e+4>>2]=j;b=b+1|0;if((g|0)!=(b|0)){continue}break}}Jl(a,c);a=i[c>>2];if(a){i[c+4>>2]=a;bp(a)}F=c+16|0;return}Ho();x()}function sn(a,b,c,d,e,f){var g=0,h=0,j=0,k=0;a:{if(f&64){d=b;e=f+ -64|0;b=e&31;if(32<=(e&63)>>>0){e=d<>>32-b|c<>>0){h=g<>>32-d|e<>>0){f=0;d=d>>>e|0}else{f=d>>>e|0;d=((1<>>e}d=k|d;e=f|h;f=b;b=j&31;if(32<=(j&63)>>>0){h=f<>>32-b|c<>2]=b;i[a+4>>2]=c;i[a+8>>2]=d;i[a+12>>2]=e}function te(a,b,c,d){var e=0,f=0,g=0,h=0;e=F-32|0;F=e;g=i[i[i[b+4>>2]+8>>2]+(c<<2)>>2];a:{b:{if((I[i[i[b>>2]+8>>2]](b)|0)!=1){break b}h=I[i[i[b>>2]+52>>2]](b)|0;f=I[i[i[b>>2]+60>>2]](b,c)|0;if(!(f?h:0)){i[a>>2]=0;break a}c=I[i[i[b>>2]+56>>2]](b,c)|0;if(c){b=i[b+56>>2];i[e+20>>2]=c;i[e+16>>2]=b;i[e+28>>2]=f;i[e+24>>2]=f+12;ue(a,g,d,e+16|0);if(!i[a>>2]){break b}break a}b=i[b+56>>2];i[e+20>>2]=h;i[e+16>>2]=b;i[e+28>>2]=f;i[e+24>>2]=f+12;ve(a,g,d,e+16|0);if(i[a>>2]){break a}}i[a>>2]=0}F=e+32|0}function oc(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;a:{b:{if(b){if(b>>>0>=357913942){break b}e=o(b,12);b=ho(e);i[a>>2]=b;i[a+4>>2]=b;f=b+e|0;i[a+8>>2]=f;g=i[c>>2];d=i[c+4>>2]-g|0;h=d>>2;j=h>>>0<1073741824;k=(d|0)<1;l=d>>>2<<2;while(1){i[b+8>>2]=0;i[b>>2]=0;i[b+4>>2]=0;if(d){if(!j){break a}c=ho(d);i[b>>2]=c;i[b+4>>2]=c;i[b+8>>2]=c+(h<<2);e=b;if(!k){c=hp(c,g,d)+l|0}i[e+4>>2]=c}b=b+12|0;if((f|0)!=(b|0)){continue}break}i[a+4>>2]=f}return}Ho();x()}Ho();x()}function Mg(a,b,c){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,m=0;e=a+8|0;Ng(a,a+4|0,e,c);a:{d=a+12|0;if((d|0)!=(b|0)){c=i[c>>2];f=i[c>>2];g=i[c+4>>2]-f>>3;while(1){h=i[d>>2];if(g>>>0<=h>>>0){break a}j=d;d=i[e>>2];if(g>>>0<=d>>>0){break a}k=j;m=(h<<3)+f|0;if(l[m>>2]>2]){while(1){b:{i[k>>2]=d;c=e;if((c|0)==(a|0)){c=a;break b}e=c+ -4|0;d=i[e>>2];if(g>>>0<=d>>>0){break a}k=c;if(l[m>>2]>2]){continue}}break}i[c>>2]=h}e=j;d=e+4|0;if((d|0)!=(b|0)){continue}break}}return}Io();x()}function vj(a){a=a|0;var b=0,c=0,d=0,e=0,f=0,g=0;a:{c=i[a+8>>2];b:{if((c|0)<0){break b}d=i[a+4>>2];b=i[d>>2];e=i[d+4>>2]-b>>2;c:{if(c>>>0>e>>>0){wj(d,c-e|0);f=i[a+8>>2];break c}f=c;if(c>>>0>=e>>>0){break c}i[d+4>>2]=b+(c<<2);f=c}g=f;if((f|0)<1){break b}a=i[a+4>>2];b=i[a+4>>2];d=i[a>>2];a=b-d|0;e=(a|0)>-1?a:-1;b=d-b|0;b=o((e|0)<1?e:1,((b|0)>(a|0)?b:a)>>>2|0);a=0;while(1){if((a|0)==(b|0)){break a}i[d+(a<<2)>>2]=a;a=a+1|0;if((a|0)<(g|0)){continue}break}}return(c^-1)>>>31|0}Io();x()}function Vl(a){a=a|0;var b=0,c=0,d=0,e=0;i[a>>2]=16864;b=i[a+68>>2];if(b){i[a+72>>2]=b;bp(b)}b=i[a+56>>2];if(b){i[a+60>>2]=b;bp(b)}b=i[a+44>>2];if(b){i[a+48>>2]=b;bp(b)}b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}b=i[a+20>>2];if(b){i[a+24>>2]=b;bp(b)}d=i[a+8>>2];if(d){b=d;c=i[a+12>>2];e=b;a:{if((b|0)==(c|0)){break a}while(1){c=c+ -4|0;b=i[c>>2];i[c>>2]=0;if(b){Wb(b)}if((c|0)!=(d|0)){continue}break}e=i[a+8>>2]}b=e;i[a+12>>2]=d;bp(b)}b=i[a+4>>2];i[a+4>>2]=0;if(b){Al(b)}return a|0}function Bl(a,b,c){var d=0,e=0,f=0;e=F-16|0;F=e;a:{if(!c|(!a|(b|0)<0)){break a}f=i[a+8>>2];if(i[a+12>>2]-f>>2<=(b|0)){break a}if(!i[a+4>>2]){d=ho(36);i[d+4>>2]=0;i[d+8>>2]=0;i[d+16>>2]=0;i[d+20>>2]=0;i[d+32>>2]=0;i[d+24>>2]=0;i[d+28>>2]=0;i[d>>2]=d+4;i[d+12>>2]=d+16;i[a+4>>2]=d}d=i[i[(b<<2)+f>>2]+60>>2];b=ho(28);lm(b,c);i[b+24>>2]=d;a=i[a+4>>2];i[e+8>>2]=b;jm(a,e+8|0);a=i[e+8>>2];i[e+8>>2]=0;d=1;if(!a){break a}yk(a+12|0,i[a+16>>2]);zk(a,i[a+4>>2]);bp(a)}F=e+16|0;return d}function Wc(a){a=a|0;var b=0,c=0,d=0,e=0;i[a>>2]=2032;b=i[a+72>>2];i[a+72>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}b=i[a+60>>2];if(b){i[a- -64>>2]=b;bp(b)}b=i[a+48>>2];if(b){bp(b)}d=i[a+36>>2];if(d){b=d;c=i[a+40>>2];e=b;a:{if((b|0)==(c|0)){break a}while(1){c=c+ -4|0;b=i[c>>2];i[c>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}if((c|0)!=(d|0)){continue}break}e=i[a+36>>2]}b=e;i[a+40>>2]=d;bp(b)}i[a>>2]=1432;b=i[a+16>>2];if(b){i[a+20>>2]=b;bp(b)}b=i[a+4>>2];if(b){i[a+8>>2]=b;bp(b)}return a|0}function zi(a){var b=0,c=0,d=0,e=0,f=0;d=a+32|0;sf(a,d);c=i[a+80>>2];a:{if(!c){break a}b=1;if(i[a+84>>2]<1){break a}sf(c,d);if(i[a+84>>2]<2){break a}while(1){sf(i[a+80>>2]+(b<<5)|0,d);b=b+1|0;if((b|0)>2]){continue}break}}b=i[a+136>>2];if((b|0)!=i[a+140>>2]){c=0;while(1){e=o(c,12);b=e+b|0;Jb(i[b+4>>2]-i[b>>2]>>2,d);b=i[a+136>>2];f=e+b|0;e=i[f>>2];f=i[f+4>>2]-e|0;if(f){kg(e,f>>2,1,0,d);b=i[a+136>>2]}c=c+1|0;if(c>>>0<(i[a+140>>2]-b|0)/12>>>0){continue}break}}}function Sb(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;a=i[a+4>>2];if(a){c=j[b+11|0];d=c<<24>>24<0;g=d?i[b>>2]:b;b=d?i[b+4>>2]:c;while(1){c=j[a+27|0];d=c<<24>>24<0;c=d?i[a+20>>2]:c;h=c>>>0>>0;a:{b:{f=h?c:b;c:{if(f){e=a+16|0;d=d?i[e>>2]:e;e=Km(g,d,f);d:{if(!e){if(b>>>0>=c>>>0){break d}break a}if((e|0)<=-1){break a}}c=Km(d,g,f);if(!c){break c}if((c|0)<=-1){break b}return 1}if(b>>>0>>0){break a}}if(h){break b}return 1}a=a+4|0}a=i[a>>2];if(a){continue}break}}return 0}function wj(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;e=i[a+8>>2];c=i[a+4>>2];if(e-c>>2>>>0>=b>>>0){if(b){b=b<<2;c=ip(c,0,b)+b|0}i[a+4>>2]=c;return}a:{f=i[a>>2];h=c-f|0;g=h>>2;d=g+b|0;if(d>>>0<1073741824){c=0;g=g<<2;e=e-f|0;j=e>>1;d=e>>2>>>0<536870911?j>>>0>>0?d:j:1073741823;if(d){if(d>>>0>=1073741824){break a}c=ho(d<<2)}b=b<<2;b=ip(c+g|0,0,b)+b|0;d=(d<<2)+c|0;if((h|0)>=1){hp(c,f,h)}i[a+8>>2]=d;i[a+4>>2]=b;i[a>>2]=c;if(f){bp(f)}return}Ho();x()}za(13344);x()}function Ai(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;e=i[a+8>>2];c=i[a+4>>2];if(e-c>>2>>>0>=b>>>0){if(b){b=b<<2;c=ip(c,0,b)+b|0}i[a+4>>2]=c;return}a:{f=i[a>>2];h=c-f|0;g=h>>2;d=g+b|0;if(d>>>0<1073741824){c=0;g=g<<2;e=e-f|0;j=e>>1;d=e>>2>>>0<536870911?j>>>0>>0?d:j:1073741823;if(d){if(d>>>0>=1073741824){break a}c=ho(d<<2)}b=b<<2;b=ip(c+g|0,0,b)+b|0;d=(d<<2)+c|0;if((h|0)>=1){hp(c,f,h)}i[a+8>>2]=d;i[a+4>>2]=b;i[a>>2]=c;if(f){bp(f)}return}Ho();x()}za(11708);x()}function ka(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;e=i[a+8>>2];c=i[a+4>>2];if(e-c>>2>>>0>=b>>>0){if(b){b=b<<2;c=ip(c,0,b)+b|0}i[a+4>>2]=c;return}a:{f=i[a>>2];h=c-f|0;g=h>>2;d=g+b|0;if(d>>>0<1073741824){c=0;g=g<<2;e=e-f|0;j=e>>1;d=e>>2>>>0<536870911?j>>>0>>0?d:j:1073741823;if(d){if(d>>>0>=1073741824){break a}c=ho(d<<2)}b=b<<2;b=ip(c+g|0,0,b)+b|0;d=(d<<2)+c|0;if((h|0)>=1){hp(c,f,h)}i[a+8>>2]=d;i[a+4>>2]=b;i[a>>2]=c;if(f){bp(f)}return}Ho();x()}za(1244);x()}function Bd(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;e=i[a+8>>2];c=i[a+4>>2];if(e-c>>2>>>0>=b>>>0){if(b){b=b<<2;c=ip(c,0,b)+b|0}i[a+4>>2]=c;return}a:{f=i[a>>2];h=c-f|0;g=h>>2;d=g+b|0;if(d>>>0<1073741824){c=0;g=g<<2;e=e-f|0;j=e>>1;d=e>>2>>>0<536870911?j>>>0>>0?d:j:1073741823;if(d){if(d>>>0>=1073741824){break a}c=ho(d<<2)}b=b<<2;b=ip(c+g|0,0,b)+b|0;d=(d<<2)+c|0;if((h|0)>=1){hp(c,f,h)}i[a+8>>2]=d;i[a+4>>2]=b;i[a>>2]=c;if(f){bp(f)}return}Ho();x()}za(2400);x()}function Vj(a,b,c,d){var e=0,f=0,h=0;a:{if((c|0)<0?1:(c|0)<=0?b>>>0<1:0){break a}e=i[a+20>>2];if((e|0)>0?1:(e|0)>=0?l[a+16>>2]>0:0){break a}g[a+24|0]=d;e=a;f=a;b=b+7|0;if(b>>>0<7){c=c+1|0}c=vp(b,c,8,0);i[f+16>>2]=c;i[e+20>>2]=H;e=i[a>>2];b=i[a+4>>2]-e|0;h=c;c=b;f=c+8|0;d=d?f:c;f=h+d|0;c=f;b:{if(b>>>0>>0){Qj(a,c-b|0);e=i[a>>2];break b}if(b>>>0<=c>>>0){break b}i[a+4>>2]=c+e}b=ho(8);i[b+4>>2]=0;i[b>>2]=d+e;c=i[a+12>>2];i[a+12>>2]=b;if(!c){break a}bp(c)}}function ig(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;e=i[a+8>>2];c=i[a+4>>2];if(e-c>>3>>>0>=b>>>0){if(b){b=b<<3;c=ip(c,0,b)+b|0}i[a+4>>2]=c;return}a:{f=i[a>>2];h=c-f|0;g=h>>3;d=g+b|0;if(d>>>0<536870912){c=0;g=g<<3;e=e-f|0;j=e>>2;d=e>>3>>>0<268435455?j>>>0>>0?d:j:536870911;if(d){if(d>>>0>=536870912){break a}c=ho(d<<3)}b=b<<3;b=ip(c+g|0,0,b)+b|0;d=(d<<3)+c|0;if((h|0)>=1){hp(c,f,h)}i[a+8>>2]=d;i[a+4>>2]=b;i[a>>2]=c;if(f){bp(f)}return}Ho();x()}za(10734);x()}function Di(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;c=i[a+4>>2];e=i[a+8>>2];if(c>>>0>>0){i[c>>2]=i[b>>2];i[a+4>>2]=c+4;return}a:{f=i[a>>2];g=c-f|0;c=g>>2;d=c+1|0;if(d>>>0<1073741824){h=c<<2;e=e-f|0;c=e>>1;d=e>>2>>>0<536870911?c>>>0>>0?d:c:1073741823;c=0;b:{if(!d){break b}if(d>>>0>=1073741824){break a}c=ho(d<<2)}e=h+c|0;i[e>>2]=i[b>>2];b=c+(d<<2)|0;d=e+4|0;if((g|0)>=1){hp(c,f,g)}i[a+8>>2]=b;i[a+4>>2]=d;i[a>>2]=c;if(f){bp(f)}return}Ho();x()}za(11708);x()}function un(a,b){var c=0,d=0,e=0,f=0,g=0,h=0,j=0;e=F-16|0;F=e;f=(Fp(b),Bp(2));c=f&2147483647;a:{if(c+ -8388608>>>0<=2130706431){d=c;c=c>>>7|0;d=d<<25;c=c+1065353216|0;g=d;c=d>>>0<0?c+1|0:c;break a}if(c>>>0>=2139095040){c=f;d=c>>>7|0;g=c<<25;c=d|2147418112;break a}if(!c){c=0;break a}d=c;c=r(c);sn(e,d,0,0,0,c+81|0);h=i[e>>2];j=i[e+4>>2];g=i[e+8>>2];c=i[e+12>>2]^65536|16265-c<<16}i[a>>2]=h;i[a+4>>2]=j;i[a+8>>2]=g;i[a+12>>2]=f&-2147483648|c;F=e+16|0}function ei(a){var b=0,c=0,d=0,e=0;pf(a);c=i[a+84>>2];a:{if((c|0)<1){break a}b=c<<5;d=ho((c|0)!=(c&134217727)?-1:b|4);i[d>>2]=c;d=d+4|0;c=d+b|0;b=d;while(1){b=mf(b)+32|0;if((c|0)!=(b|0)){continue}break}e=i[a+80>>2];i[a+80>>2]=d;if(e){c=e+ -4|0;d=i[c>>2];if(d){b=e+(d<<5)|0;while(1){b=nf(b+ -32|0);if((e|0)!=(b|0)){continue}break}}bp(c)}if(i[a+84>>2]<1){break a}b=0;while(1){pf(i[a+80>>2]+(b<<5)|0);b=b+1|0;if((b|0)>2]){continue}break}}}function Om(a,b){a:{if(a){if(b>>>0<=127){break a}b:{if(!i[i[4790]>>2]){if((b&-128)==57216){break a}break b}if(b>>>0<=2047){g[a+1|0]=b&63|128;g[a|0]=b>>>6|192;return 2}if(!((b&-8192)!=57344?b>>>0>=55296:0)){g[a+2|0]=b&63|128;g[a|0]=b>>>12|224;g[a+1|0]=b>>>6&63|128;return 3}if(b+ -65536>>>0<=1048575){g[a+3|0]=b&63|128;g[a|0]=b>>>18|240;g[a+2|0]=b>>>6&63|128;g[a+1|0]=b>>>12&63|128;return 4}}i[4805]=25;a=-1}else{a=1}return a}g[a|0]=b;return 1}function md(a,b,c,d){var e=0,f=0,h=0,j=0;e=F-80|0;F=e;f=Ba(e+16|0);h=i[i[a+8>>2]+56>>2];j=c<<24>>24;c=o(Sj(5),c);Ca(f,h,0,j,5,c,c>>31);c=ho(96);f=Fa(c,f);i[e+8>>2]=c;Ha(f,b);i[e+8>>2]=0;b=i[a+32>>2];i[a+32>>2]=c;if(b){Wb(b)}a:{if(!d){break a}a=i[a+32>>2];g[a+84|0]=0;c=i[a+68>>2];b=i[a+72>>2]-c>>2;if(b>>>0>>0){Xa(a+68|0,d-b|0,2396);break a}if(b>>>0<=d>>>0){break a}i[a+72>>2]=c+(d<<2)}a=i[e+8>>2];i[e+8>>2]=0;if(a){Wb(a)}F=e+80|0}function ta(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;d=F-16|0;F=d;e=i[a+4>>2];a:{if((e|0)==-1){break a}c=i[b+20>>2];if((c|0)>0?1:(c|0)>=0?l[b+16>>2]>0:0){break a}ca(b,i[b+4>>2],i[a+8>>2],i[a+12>>2]);c=i[b+20>>2];if((c|0)>0?1:(c|0)>=0?l[b+16>>2]>=1:0){break a}c=a+20|0;ca(b,i[b+4>>2],c,c+4|0);c=i[b+20>>2];f=i[b+16>>2];g[d+15|0]=i[a+4>>2];if((c|0)>0?1:(c|0)>=0?f>>>0>0:0){break a}ca(b,i[b+4>>2],d+15|0,d+16|0)}F=d+16|0;return(e|0)!=-1|0}function Ui(a,b){var c=0,d=0,e=0,f=0,g=0;d=i[a+4>>2];a=i[d+4>>2];if((a|0)!=i[d+8>>2]){i[a>>2]=b;i[d+4>>2]=a+4;return}a:{f=i[d>>2];g=a-f|0;e=g>>2;c=e+1|0;if(c>>>0<1073741824){a=g>>1;c=e>>>0<536870911?a>>>0>>0?c:a:1073741823;a=0;b:{if(!c){break b}if(c>>>0>=1073741824){break a}a=ho(c<<2)}e=a+(e<<2)|0;i[e>>2]=b;b=a+(c<<2)|0;c=e+4|0;if((g|0)>=1){hp(a,f,g)}i[d+8>>2]=b;i[d+4>>2]=c;i[d>>2]=a;if(f){bp(f)}return}Ho();x()}za(11708);x()}function Pl(a,b){var c=0,d=0,e=0,f=0,g=0;c=i[a+4>>2];if((c|0)!=i[a+8>>2]){i[c>>2]=i[b>>2];i[a+4>>2]=c+4;return}a:{f=i[a>>2];g=c-f|0;e=g>>2;d=e+1|0;if(d>>>0<1073741824){c=g>>1;d=e>>>0<536870911?c>>>0>>0?d:c:1073741823;c=0;b:{if(!d){break b}if(d>>>0>=1073741824){break a}c=ho(d<<2)}e=c+(e<<2)|0;i[e>>2]=i[b>>2];b=c+(d<<2)|0;d=e+4|0;if((g|0)>=1){hp(c,f,g)}i[a+8>>2]=b;i[a+4>>2]=d;i[a>>2]=c;if(f){bp(f)}return}Ho();x()}za(16516);x()}function Tb(a,b,c,d){var e=0,f=0,h=0,k=p(0);e=F-32|0;F=e;h=a+4|0;a=uc(a,b);a:{if((h|0)==(a|0)){break a}f=ro(e+16|0,a+28|0);b=g[f+11|0];b:{c:{if((b|0)<=-1){if(!i[f+4>>2]){break b}a=i[f>>2];break c}a=e+16|0;if(!b){break a}}if((c|0)>=1){b=0;while(1){d:{k=nn(a,e+12|0);h=a;a=i[e+12>>2];if((h|0)==(a|0)){break d}m[(b<<2)+d>>2]=k;b=b+1|0;if((c|0)!=(b|0)){continue}}break}b=j[f+11|0]}if(b<<24>>24>-1){break a}}bp(i[f>>2])}F=e+32|0}function Cj(a,b,c){var d=0,e=0,f=0;i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;a:{b:{if(!b){break b}if((b|0)<=-1){break a}e=b+ -1>>>5|0;f=e+1|0;d=ho(f<<2);i[a+8>>2]=f;i[a>>2]=d;f=j[c|0];i[a+4>>2]=b;i[(b>>>0<33?d:d+(e<<2)|0)>>2]=0;c=b>>>5|0;e=c<<2;if(f){d=ip(d,255,e);b=b&31;if(!b){break b}c=d+(c<<2)|0;i[c>>2]=i[c>>2]|-1>>>32-b;return a}d=ip(d,0,e);b=b&31;if(!b){break b}c=d+(c<<2)|0;i[c>>2]=i[c>>2]&(-1>>>32-b^-1)}return a}Ho();x()}function mm(a,b,c,d){var e=0,f=0,g=0,h=0,j=0;f=F-16|0;F=f;c=Nf(a,b,f+12|0,f+8|0,c);if(!i[c>>2]){b=ho(40);ro(b+16|0,d);i[b+36>>2]=0;i[b+28>>2]=0;i[b+32>>2]=0;g=i[d+12>>2];h=i[d+16>>2]-g|0;e=0;a:{if(!h){break a}e=b+28|0;Qj(e,h);g=i[d+12>>2];j=i[d+16>>2]-g|0;e=i[e>>2]}hp(e,g,j);i[b+8>>2]=i[f+12>>2];i[b>>2]=0;i[b+4>>2]=0;i[c>>2]=b;d=i[i[a>>2]>>2];if(d){i[a>>2]=d;b=i[c>>2]}Kf(i[a+4>>2],b);i[a+8>>2]=i[a+8>>2]+1}F=f+16|0}function Zj(a,b,c){var d=0,e=0,f=0,h=0;e=F-16|0;F=e;Co(e,c);f=Of(a,e+12|0,b);d=i[f>>2];if(!d){d=ho(40);ro(d+16|0,b);i[d+36>>2]=0;i[d+28>>2]=0;i[d+32>>2]=0;i[d+8>>2]=i[e+12>>2];i[d>>2]=0;i[d+4>>2]=0;i[f>>2]=d;b=i[i[a>>2]>>2];h=d;a:{if(!b){break a}i[a>>2]=b;h=i[f>>2]}b=h;Kf(i[a+4>>2],b);i[a+8>>2]=i[a+8>>2]+1}a=d+28|0;if(g[d+39|0]<=-1){bp(i[a>>2])}b=i[e+4>>2];i[a>>2]=i[e>>2];i[a+4>>2]=b;i[a+8>>2]=i[e+8>>2];F=e+16|0}function zb(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){Ab(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}Ab(a,b)}}function xb(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){yb(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}yb(a,b)}}function rb(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){sb(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}sb(a,b)}}function pb(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){qb(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}qb(a,b)}}function nb(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){ob(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}ob(a,b)}}function lb(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){mb(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}mb(a,b)}}function jj(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){kj(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}kj(a,b)}}function fb(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){gb(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}gb(a,b)}}function db(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){eb(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}eb(a,b)}}function bb(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){cb(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}cb(a,b)}}function Nm(a,b){var c=0;c=(b|0)!=0;a:{b:{c:{if(!b|!(a&3)){break c}while(1){if(!j[a|0]){break b}a=a+1|0;b=b+ -1|0;c=(b|0)!=0;if(!b){break c}if(a&3){continue}break}}if(!c){break a}}d:{if(!j[a|0]|b>>>0<4){break d}while(1){c=i[a>>2];if((c^-1)&c+ -16843009&-2139062144){break d}a=a+4|0;b=b+ -4|0;if(b>>>0>3){continue}break}}if(!b){break a}while(1){if(!j[a|0]){return a}a=a+1|0;b=b+ -1|0;if(b){continue}break}}return 0}function Gl(a,b,c,d){var e=0,f=0,h=0;f=F-48|0;F=f;a:{if(!b){a=0;break a}e=Tj(f+16|0);h=0;b:{if((xm(b,0)|0)==-1){break b}if(c){h=0;if(!(I[i[i[b>>2]+16>>2]](b)|0)){break b}I[i[i[b>>2]+20>>2]](b)}uf(f,a,b,e);a=i[f>>2];if(g[f+15|0]<=-1){bp(i[f+4>>2])}h=0;if(a){break b}rl(d,i[e>>2],i[e+4>>2]);h=i[e+4>>2]-i[e>>2]|0}a=h;b=i[e+12>>2];i[e+12>>2]=0;if(b){bp(b)}b=i[e>>2];if(!b){break a}i[e+4>>2]=b;bp(b)}F=f+48|0;return a}function Fm(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){Jm(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+20>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}Jm(a,b)}}function Db(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){Eb(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}Eb(a,b)}}function Bb(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){Cb(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}Cb(a,b)}}function $a(a,b){var c=0,d=0,e=p(0),f=0;c=2;a:{if((b|0)==1){break a}c=b;if(!(b+ -1&b)){break a}c=Fn(b)}d=i[a+4>>2];if(c>>>0>d>>>0){ab(a,c);return}b:{if(c>>>0>=d>>>0){break b}f=d>>>0<3;e=p(v(p(p(l[a+12>>2])/m[a+16>>2])));c:{if(e=p(0)){b=~~e>>>0;break c}b=0}d:{e:{if(f){break e}if(xp(d)>>>0>1){break e}b=b>>>0<2?b:1<<32-r(b+ -1|0);break d}b=Fn(b)}b=c>>>0>>0?b:c;if(b>>>0>=d>>>0){break b}ab(a,b)}}function Yj(a,b,c){var d=0,e=0,f=0;d=F-16|0;F=d;Bo(d,c);e=Of(a,d+12|0,b);c=i[e>>2];if(!c){c=ho(40);ro(c+16|0,b);i[c+36>>2]=0;i[c+28>>2]=0;i[c+32>>2]=0;i[c+8>>2]=i[d+12>>2];i[c>>2]=0;i[c+4>>2]=0;i[e>>2]=c;b=i[i[a>>2]>>2];f=c;a:{if(!b){break a}i[a>>2]=b;f=i[e>>2]}b=f;Kf(i[a+4>>2],b);i[a+8>>2]=i[a+8>>2]+1}a=c+28|0;if(g[c+39|0]<=-1){bp(i[a>>2])}b=i[d+4>>2];i[a>>2]=i[d>>2];i[a+4>>2]=b;i[a+8>>2]=i[d+8>>2];F=d+16|0}function Hh(a){a=a|0;var b=0,c=0,d=0,e=0;i[a>>2]=10936;b=i[a+64>>2];i[a+64>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}i[a>>2]=13484;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}b=i[a+20>>2];if(b){i[a+24>>2]=b;bp(b)}d=i[a+8>>2];if(d){b=d;c=i[a+12>>2];e=b;a:{if((b|0)==(c|0)){break a}while(1){c=c+ -4|0;b=i[c>>2];i[c>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}if((c|0)!=(d|0)){continue}break}e=i[a+8>>2]}b=e;i[a+12>>2]=d;bp(b)}return a|0}function Jl(a,b){var c=0,d=0,e=0;d=F-16|0;F=d;e=ho(88);c=e;i[c>>2]=0;i[c+4>>2]=0;i[c+64>>2]=0;i[c+68>>2]=0;i[c+56>>2]=0;i[c+48>>2]=0;i[c+52>>2]=0;i[c+40>>2]=0;i[c+44>>2]=0;i[c+32>>2]=0;i[c+36>>2]=0;i[c+24>>2]=0;i[c+28>>2]=0;i[c+16>>2]=0;i[c+20>>2]=0;i[c+8>>2]=0;i[c+12>>2]=0;i[c+72>>2]=0;i[c+76>>2]=0;i[c+80>>2]=0;i[c+84>>2]=0;i[c+60>>2]=c;a:{if(Kl(c,b)){i[a>>2]=e;break a}i[a>>2]=0;i[d+8>>2]=0;bi(e)}F=d+16|0}function Ih(a){a=a|0;var b=0,c=0,d=0,e=0;i[a>>2]=10936;b=i[a+64>>2];i[a+64>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}i[a>>2]=13484;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}b=i[a+20>>2];if(b){i[a+24>>2]=b;bp(b)}d=i[a+8>>2];if(d){b=d;c=i[a+12>>2];e=b;a:{if((b|0)==(c|0)){break a}while(1){c=c+ -4|0;b=i[c>>2];i[c>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}if((c|0)!=(d|0)){continue}break}e=i[a+8>>2]}b=e;i[a+12>>2]=d;bp(b)}bp(a)}function Fl(a,b,c){var d=0,e=0,f=0;e=F-48|0;F=e;a:{if(!b){a=0;break a}d=Tj(e+16|0);f=0;b:{if((xm(b,0)|0)==-1){break b}f=0;if(!(I[i[i[b>>2]+16>>2]](b)|0)){break b}I[i[i[b>>2]+20>>2]](b);Af(e,a,b,d);a=i[e>>2];if(g[e+15|0]<=-1){bp(i[e+4>>2])}f=0;if(a){break b}rl(c,i[d>>2],i[d+4>>2]);f=i[d+4>>2]-i[d>>2]|0}a=f;b=i[d+12>>2];i[d+12>>2]=0;if(b){bp(b)}b=i[d>>2];if(!b){break a}i[d+4>>2]=b;bp(b)}F=e+48|0;return a}function kc(a){pf(a);pf(a+32|0);pf(a- -64|0);pf(a+96|0);pf(a+128|0);pf(a+160|0);pf(a+192|0);pf(a+224|0);pf(a+256|0);pf(a+288|0);pf(a+320|0);pf(a+352|0);pf(a+384|0);pf(a+416|0);pf(a+448|0);pf(a+480|0);pf(a+512|0);pf(a+544|0);pf(a+576|0);pf(a+608|0);pf(a+640|0);pf(a+672|0);pf(a+704|0);pf(a+736|0);pf(a+768|0);pf(a+800|0);pf(a+832|0);pf(a+864|0);pf(a+896|0);pf(a+928|0);pf(a+960|0);pf(a+992|0);pf(a+1024|0)}function vo(a,b,c,d,e,f,h,j){var k=0,l=0,m=0;k=F-16|0;F=k;if((b^-1)+ -17>>>0>=c>>>0){l=Yn(a);a:{if(2147483623>b>>>0){i[k+8>>2]=b<<1;i[k+12>>2]=b+c;c=ao(i[Mn(k+12|0,k+8|0)>>2]);break a}c=-18}m=c+1|0;c=bo(m);if(e){Ln(c,l,e)}if(h){Ln(c+e|0,j,h)}d=d-f|0;j=d-e|0;if(j){Ln((c+e|0)+h|0,(e+l|0)+f|0,j)}if((b|0)!=10){bp(l)}co(a,c);eo(a,m);b=a;a=d+h|0;aa(b,a);g[k+7|0]=0;_n(a+c|0,k+7|0);F=k+16|0;return}qo();x()}function on(a,b,c,d){var e=0,f=0,g=0,h=0,j=0,k=0,l=0;e=F-160|0;F=e;ip(e+16|0,0,144);i[e+92>>2]=-1;i[e+60>>2]=b;i[e+24>>2]=-1;i[e+20>>2]=b;en(e+16|0);jn(e,e+16|0,d);d=i[e+8>>2];g=i[e+12>>2];h=i[e>>2];j=i[e+4>>2];if(c){k=c;f=b;b=i[e+20>>2]-i[e+24>>2]|0;l=b;c=b+i[e+136>>2]|0;b=i[e+140>>2]+(b>>31)|0;b=c>>>0>>0?b+1|0:b;i[k>>2]=!(b|c)?f:f+c|0}i[a>>2]=h;i[a+4>>2]=j;i[a+8>>2]=d;i[a+12>>2]=g;F=e+160|0}function Af(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;e=F-96|0;F=e;f=Rf(e+40|0,c);vf(e,b,c);Vf(f,e);kd(e+24|0,i[e+28>>2]);wf(e+12|0,i[e+16>>2]);kd(e,i[e+4>>2]);Sf(a,f,d);if(!i[a>>2]){if(g[a+15|0]<=-1){bp(i[a+4>>2])}i[b+40>>2]=i[f+40>>2];i[b+44>>2]=i[f+44>>2];i[a+8>>2]=0;i[a+12>>2]=0;i[a>>2]=0;i[a+4>>2]=0}i[f>>2]=10308;kd(f+28|0,i[f+32>>2]);wf(f+16|0,i[f+20>>2]);kd(f+4|0,i[f+8>>2]);F=e+96|0}function nc(a){mf(a);mf(a+32|0);mf(a- -64|0);mf(a+96|0);mf(a+128|0);mf(a+160|0);mf(a+192|0);mf(a+224|0);mf(a+256|0);mf(a+288|0);mf(a+320|0);mf(a+352|0);mf(a+384|0);mf(a+416|0);mf(a+448|0);mf(a+480|0);mf(a+512|0);mf(a+544|0);mf(a+576|0);mf(a+608|0);mf(a+640|0);mf(a+672|0);mf(a+704|0);mf(a+736|0);mf(a+768|0);mf(a+800|0);mf(a+832|0);mf(a+864|0);mf(a+896|0);mf(a+928|0);mf(a+960|0);mf(a+992|0)}function mc(a){nf(a+992|0);nf(a+960|0);nf(a+928|0);nf(a+896|0);nf(a+864|0);nf(a+832|0);nf(a+800|0);nf(a+768|0);nf(a+736|0);nf(a+704|0);nf(a+672|0);nf(a+640|0);nf(a+608|0);nf(a+576|0);nf(a+544|0);nf(a+512|0);nf(a+480|0);nf(a+448|0);nf(a+416|0);nf(a+384|0);nf(a+352|0);nf(a+320|0);nf(a+288|0);nf(a+256|0);nf(a+224|0);nf(a+192|0);nf(a+160|0);nf(a+128|0);nf(a+96|0);nf(a- -64|0);nf(a+32|0);nf(a)}function Oc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,h=0,j=0,k=0;d=F-16|0;F=d;f=Ib(a,b);a:{if(!f){break a}c=i[a+36>>2];if((c|0)==i[a+40>>2]){break a}h=d+16|0;while(1){c=i[(e<<2)+c>>2];j=d,k=I[i[i[c>>2]+32>>2]](c)|0,g[j+15|0]=k;c=i[b+20>>2];if((c|0)<0?1:(c|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],d+15|0,h)}e=e+1|0;c=i[a+36>>2];if(e>>>0>2]-c>>2>>>0){continue}break}}F=d+16|0;return f|0}function Mi(a,b){var c=0,d=0,e=0;a:{if((a|0)!=(b|0)){e=a;c=i[b+4>>2];d=0;b:{if(!c){break b}c:{if(c>>>0<=i[a+8>>2]<<5>>>0){d=i[a>>2];break c}d=i[a>>2];if(d){bp(d);i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;c=i[b+4>>2]}if((c|0)<=-1){break a}c=(c+ -1>>>5|0)+1|0;d=ho(c<<2);i[a+8>>2]=c;i[a+4>>2]=0;i[a>>2]=d;c=i[b+4>>2]}jp(d,i[b>>2],(c+ -1>>>3&536870908)+4|0);d=i[b+4>>2]}i[e+4>>2]=d}return}Ho();x()}function Il(a,b,c){var d=0,e=0,f=0;d=F-48|0;F=d;e=i[a+4>>2];a:{if(!e){break a}if(b){if(!(I[i[i[e>>2]+16>>2]](e)|0)){break a}b=i[a+4>>2];I[i[i[b>>2]+20>>2]](b)}b=Tj(d+16|0);Sf(d,i[a>>2],b);a=i[d>>2];if(g[d+15|0]<=-1){bp(i[d+4>>2])}if(!a){rl(c,i[b>>2],i[b+4>>2]);f=i[b+4>>2]-i[b>>2]|0}a=i[b+12>>2];i[b+12>>2]=0;if(a){bp(a)}a=i[b>>2];if(!a){break a}i[b+4>>2]=a;bp(a)}F=d+48|0;return f}function Yh(a,b){var c=0;c=i[b+8>>2];i[a+12>>2]=i[b+4>>2];i[a+16>>2]=c;i[a+28>>2]=i[b+20>>2];c=i[b+16>>2];i[a+20>>2]=i[b+12>>2];i[a+24>>2]=c;Mi(a+32|0,b+24|0);Mi(a+44|0,b+36|0);if((a+8|0)==(b|0)){i[a+92>>2]=i[b+84>>2];return}Vh(a+56|0,i[b+48>>2],i[b+52>>2]);Vh(a+68|0,i[b+60>>2],i[b- -64>>2]);Vh(a+80|0,i[b+72>>2],i[b+76>>2]);i[a+92>>2]=i[b+84>>2];ra(a+96|0,i[b+88>>2],i[b+92>>2])}function pm(a,b){var c=0,d=0,e=0;c=i[b+4>>2];a:{if(!c){d=i[b+8>>2];if(i[d>>2]==(b|0)){break a}e=b+8|0;while(1){c=i[e>>2];e=c+8|0;d=i[c+8>>2];if((c|0)!=i[d>>2]){continue}break}break a}while(1){d=c;c=i[c>>2];if(c){continue}break}}if(i[a>>2]==(b|0)){i[a>>2]=d}i[a+8>>2]=i[a+8>>2]+ -1;vm(i[a+4>>2],b);a=i[b+28>>2];if(a){i[b+32>>2]=a;bp(a)}if(g[b+27|0]<=-1){bp(i[b+16>>2])}bp(b)}function ld(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,h=0,k=0;d=i[a+8>>2];e=g[d+24|0];md(a,i[b+4>>2]-i[b>>2]>>2,e,c);c=i[b>>2];f=i[b+4>>2];if((c|0)==(f|0)){return 1}a=i[a+32>>2];h=i[i[a>>2]>>2]+i[a+48>>2]|0;b=0;while(1){a=i[c>>2];k=d;if(!j[d+84|0]){a=i[i[d+68>>2]+(a<<2)>>2]}a=Xb(k,a,g[d+24|0],(b<<2)+h|0);if(a){b=b+e|0;c=c+4|0;if((f|0)!=(c|0)){continue}}break}return a|0}function Yb(a,b){a=a|0;b=b|0;var c=0,d=0;c=i[a+36>>2];if((c|0)!=i[a+40>>2]){while(1){c=o(d,24)+c|0;I[i[i[c>>2]+28>>2]](c,b)|0;d=d+1|0;c=i[a+36>>2];if(d>>>0<(i[a+40>>2]-c|0)/24>>>0){continue}break}}c=i[a+48>>2];if((c|0)!=i[a+52>>2]){d=0;while(1){c=i[(d<<2)+c>>2];Jb((c|0)>-1?c<<1:(c^-1)<<1|1,b);d=d+1|0;c=i[a+48>>2];if(d>>>0>2]-c>>2>>>0){continue}break}}return 1}function Jg(a,b,c){var d=0,e=0,f=0;d=F-16|0;F=d;e=a;g[d+15|0]=a&127;a:{if(!b&a>>>0>=128|b>>>0>0){g[d+15|0]=e|128;e=i[c+20>>2];f=0;if((e|0)>0?1:(e|0)>=0?l[c+16>>2]>0:0){break a}ca(c,i[c+4>>2],d+15|0,d+16|0);f=Jg((b&127)<<25|a>>>7,b>>>7|0,c);break a}a=i[c+20>>2];if((a|0)<0?1:(a|0)<=0?l[c+16>>2]<=0:0){ca(c,i[c+4>>2],d+15|0,d+16|0);f=1;break a}f=0}e=f;F=d+16|0;return e}function Qj(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;e=i[a+8>>2];c=i[a+4>>2];if(e-c>>>0>=b>>>0){if(b){c=ip(c,0,b)+b|0}i[a+4>>2]=c;return}f=i[a>>2];g=c-f|0;d=g+b|0;if((d|0)>-1){c=0;e=e-f|0;h=e<<1;d=e>>>0<1073741823?h>>>0>>0?d:h:2147483647;if(d){c=ho(d)}d=c+d|0;b=ip(c+g|0,0,b)+b|0;if((g|0)>=1){hp(c,f,g)}i[a+8>>2]=d;i[a+4>>2]=b;i[a>>2]=c;if(f){bp(f)}return}Ho();x()}function lp(a,b,c){var d=0,e=0,f=0;a:{d=i[c+16>>2];if(!d){if(kp(c)){break a}d=i[c+16>>2]}f=i[c+20>>2];if(d-f>>>0>>0){I[i[c+36>>2]](c,a,b)|0;return}b:{if(g[c+75|0]<0){break b}d=b;while(1){e=d;if(!e){break b}d=e+ -1|0;if(j[d+a|0]!=10){continue}break}if(I[i[c+36>>2]](c,a,e)>>>0>>0){break a}a=a+e|0;b=b-e|0;f=i[c+20>>2]}hp(f,a,b);i[c+20>>2]=i[c+20>>2]+b}}function Bn(a,b,c,d,e){var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;j=a;k=e;e=0;g=e;f=c;c=0;e=up(k,e,f,c);l=H;h=b;m=up(d,0,b,0);b=H;d=up(d,n,f,c);c=b+d|0;b=H;b=c>>>0>>0?b+1|0:b;f=b;d=b+e|0;b=l;b=d>>>0>>0?b+1|0:b;f=d;e=b;b=up(h,o,k,g)+c|0;d=H;g=b;d=b>>>0>>0?d+1|0:d;h=d;d=d+f|0;if(d>>>0>>0){e=e+1|0}i[j+8>>2]=d;i[j+12>>2]=e;i[a>>2]=m;i[a+4>>2]=g}function vl(a,b,c){var d=0,e=0,f=0,h=0;d=F-16|0;F=d;a:{b:{if(!a){break b}e=mp(b);if(e>>>0>=4294967280){break a}c:{d:{if(e>>>0>=11){f=e+16&-16;h=ho(f);i[d+8>>2]=f|-2147483648;i[d>>2]=h;i[d+4>>2]=e;f=d;break d}g[d+11|0]=e;f=d;h=d;if(!e){break c}}hp(h,b,e)}g[e+h|0]=0;sm(a,d,c);if(g[f+11|0]>-1){break b}bp(i[d>>2])}F=d+16|0;return(a|0)!=0}qo();x()}function tl(a,b,c){var d=0,e=0,f=0,h=0;d=F-16|0;F=d;a:{b:{if(!a){break b}e=mp(b);if(e>>>0>=4294967280){break a}c:{d:{if(e>>>0>=11){f=e+16&-16;h=ho(f);i[d+8>>2]=f|-2147483648;i[d>>2]=h;i[d+4>>2]=e;f=d;break d}g[d+11|0]=e;f=d;h=d;if(!e){break c}}hp(h,b,e)}g[e+h|0]=0;nm(a,d,c);if(g[f+11|0]>-1){break b}bp(i[d>>2])}F=d+16|0;return(a|0)!=0}qo();x()}function nj(a){a=a|0;var b=0,c=0,d=0,e=0;i[a>>2]=13484;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}b=i[a+20>>2];if(b){i[a+24>>2]=b;bp(b)}d=i[a+8>>2];if(d){b=d;c=i[a+12>>2];e=b;a:{if((b|0)==(c|0)){break a}while(1){c=c+ -4|0;b=i[c>>2];i[c>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}if((c|0)!=(d|0)){continue}break}e=i[a+8>>2]}b=e;i[a+12>>2]=d;bp(b)}return a|0}function Fb(a,b){var c=0,d=0,e=0,f=0;e=F-16|0;F=e;i[a+4>>2]=0;i[a+8>>2]=0;i[a>>2]=1432;d=a+12|0;c=d;i[c>>2]=0;i[c+4>>2]=0;i[a+20>>2]=0;i[a+24>>2]=0;i[a+28>>2]=0;i[a+32>>2]=0;c=ho(4);i[c>>2]=b;f=d;d=c+4|0;i[f>>2]=d;f=a+8|0;i[f>>2]=d;i[a+4>>2]=c;i[e+12>>2]=-1;Gb(a+16|0,b+1|0,e+12|0);i[i[a+16>>2]+(b<<2)>>2]=(i[f>>2]-i[a+4>>2]>>2)+ -1;F=e+16|0}function Wo(a,b,c,d){g[a+53|0]=1;a:{if(i[a+4>>2]!=(c|0)){break a}g[a+52|0]=1;c=i[a+16>>2];if(!c){i[a+36>>2]=1;i[a+24>>2]=d;i[a+16>>2]=b;if((d|0)!=1|i[a+48>>2]!=1){break a}g[a+54|0]=1;return}if((b|0)==(c|0)){c=i[a+24>>2];if((c|0)==2){i[a+24>>2]=d;c=d}if(i[a+48>>2]!=1|(c|0)!=1){break a}g[a+54|0]=1;return}g[a+54|0]=1;i[a+36>>2]=i[a+36>>2]+1}}function Ro(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=F+ -64|0;F=d;e=1;a:{if(Qo(a,b,0)){break a}e=0;if(!b){break a}b=So(b,18792,18840);e=0;if(!b){break a}ip(d+8|4,0,52);i[d+56>>2]=1;i[d+20>>2]=-1;i[d+16>>2]=a;i[d+8>>2]=b;I[i[i[b>>2]+28>>2]](b,d+8|0,i[c>>2],1);a=i[d+32>>2];if((a|0)==1){i[c>>2]=i[d+24>>2]}e=(a|0)==1}a=e;F=d- -64|0;return a|0}function Pn(a,b,c){var d=0,e=0;d=F-160|0;F=d;hp(d+8|0,18056,144);a:{b:{if(b+ -1>>>0>=2147483647){if(b){break b}b=1;a=d+159|0}i[d+52>>2]=a;i[d+28>>2]=a;e=-2-a|0;b=b>>>0>e>>>0?e:b;i[d+56>>2]=b;a=a+b|0;i[d+36>>2]=a;i[d+24>>2]=a;a=Rm(d+8|0,c);if(!b){break a}b=i[d+28>>2];g[b-((b|0)==i[d+24>>2])|0]=0;break a}i[4805]=61;a=-1}F=d+160|0;return a}function tj(a){a=a|0;var b=0,c=0,d=0,e=0;i[a>>2]=13484;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}b=i[a+20>>2];if(b){i[a+24>>2]=b;bp(b)}d=i[a+8>>2];if(d){b=d;c=i[a+12>>2];e=b;a:{if((b|0)==(c|0)){break a}while(1){c=c+ -4|0;b=i[c>>2];i[c>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}if((c|0)!=(d|0)){continue}break}e=i[a+8>>2]}b=e;i[a+12>>2]=d;bp(b)}bp(a)}function Ic(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0;c=1;a:{if((I[i[i[b>>2]+20>>2]](b)|0)<1){break a}c=0;while(1){e=xm(i[i[a+4>>2]+4>>2],I[i[i[b>>2]+24>>2]](b,d)|0);if((e|0)==-1){break a}if(!(g=b,h=Hj(i[a+4>>2],e),f=i[i[b>>2]+28>>2],I[f](g|0,h|0)|0)){break a}d=d+1|0;if((d|0)<(I[i[i[b>>2]+20>>2]](b)|0)){continue}break}c=1}return c|0}function Jb(a,b){var c=0,d=0,e=0;c=F-16|0;F=c;g[c+15|0]=a&127;a:{if(a>>>0>=128){g[c+15|0]=a|128;e=i[b+20>>2];d=0;if((e|0)>0?1:(e|0)>=0?l[b+16>>2]>0:0){break a}ca(b,i[b+4>>2],c+15|0,c+16|0);d=Jb(a>>>7|0,b);break a}a=i[b+20>>2];if((a|0)<0?1:(a|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],c+15|0,c+16|0);d=1;break a}d=0}a=d;F=c+16|0;return a}function Nc(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0;a:{if(!Hb(a,b,c)){break a}if(!(I[i[i[a>>2]+52>>2]](a)|0)){break a}e=i[a+4>>2];if((e|0)==i[a+8>>2]){return 1}c=0;while(1){d=c<<2;f=i[d+i[a+36>>2]>>2];d=I[i[i[f>>2]+8>>2]](f,b,i[d+e>>2])|0;if(!d){break a}c=c+1|0;e=i[a+4>>2];if(c>>>0>2]-e>>2>>>0){continue}break}}return d|0}function Zh(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=F-16|0;F=d;i[a+4>>2]=b;b=i[b+64>>2];e=i[b>>2];b=i[b+4>>2];g[d+15|0]=0;Uh(a+24|0,(b-e>>2>>>0)/3|0,d+15|0);b=i[a+4>>2];e=i[b+56>>2];b=i[b+52>>2];g[d+14|0]=0;Uh(a+36|0,e-b>>2,d+14|0);b=i[c+12>>2];i[a+16>>2]=i[c+8>>2];i[a+20>>2]=b;b=i[c+4>>2];i[a+8>>2]=i[c>>2];i[a+12>>2]=b;F=d+16|0}function mp(a){var b=0,c=0,d=0;a:{b:{b=a;if(!(b&3)){break b}if(!j[a|0]){return 0}while(1){b=b+1|0;if(!(b&3)){break b}if(j[b|0]){continue}break}break a}while(1){c=b;b=b+4|0;d=i[c>>2];if(!((d^-1)&d+ -16843009&-2139062144)){continue}break}if(!(d&255)){return c-a|0}while(1){d=j[c+1|0];b=c+1|0;c=b;if(d){continue}break}}return b-a|0}function _l(a){i[a>>2]=0;i[a+4>>2]=0;i[a+28>>2]=0;i[a+32>>2]=0;g[a+24|0]=1;i[a+16>>2]=0;i[a+20>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0;i[a+36>>2]=0;i[a+40>>2]=0;i[a+44>>2]=0;i[a+48>>2]=0;i[a+52>>2]=0;i[a+56>>2]=0;i[a+60>>2]=0;i[a+64>>2]=0;i[a+72>>2]=0;i[a+76>>2]=0;i[a+80>>2]=0;i[a+84>>2]=0;i[a+88>>2]=0;i[a+92>>2]=0;i[a+68>>2]=a}function Zo(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;if(Qo(a,i[b+8>>2],e)){Xo(b,c,d);return}a:{if(!Qo(a,i[b>>2],e)){break a}if(!(i[b+20>>2]!=(c|0)?i[b+16>>2]!=(c|0):0)){if((d|0)!=1){break a}i[b+32>>2]=1;return}i[b+20>>2]=c;i[b+32>>2]=d;i[b+40>>2]=i[b+40>>2]+1;if(!(i[b+36>>2]!=1|i[b+24>>2]!=2)){g[b+54|0]=1}i[b+44>>2]=4}}function Wd(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;c=F-16|0;F=c;i[c+12>>2]=i[a+12>>2];d=i[b+20>>2];e=d;f=i[b+16>>2];if((d|0)<0?1:(d|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],c+12|0,c+16|0);f=i[b+16>>2];e=i[b+20>>2]}i[c+8>>2]=i[a+16>>2];if((e|0)<0?1:(e|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],c+8|0,c+12|0)}sf(a+112|0,b);F=c+16|0;return 1}function Oe(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;c=F-16|0;F=c;i[c+12>>2]=i[a+12>>2];d=i[b+20>>2];e=d;f=i[b+16>>2];if((d|0)<0?1:(d|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],c+12|0,c+16|0);f=i[b+16>>2];e=i[b+20>>2]}i[c+8>>2]=i[a+24>>2];if((e|0)<0?1:(e|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],c+8|0,c+12|0)}sf(a+96|0,b);F=c+16|0;return 1}function Li(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=F-16|0;F=d;i[a+4>>2]=b;e=i[b>>2];b=i[b+4>>2];g[d+15|0]=0;Uh(a+24|0,(b-e>>2>>>0)/3|0,d+15|0);b=i[a+4>>2];e=i[b+28>>2];b=i[b+24>>2];g[d+14|0]=0;Uh(a+36|0,e-b>>2,d+14|0);b=i[c+12>>2];i[a+16>>2]=i[c+8>>2];i[a+20>>2]=b;b=i[c+4>>2];i[a+8>>2]=i[c>>2];i[a+12>>2]=b;F=d+16|0}function _c(a,b){a=a|0;b=b|0;var c=0,d=0;c=i[a+16>>2];a:{if(i[a+20>>2]-c>>2<=(b|0)){break a}b=i[c+(b<<2)>>2];if((b|0)<0){break a}if(l[a+52>>2]<=b>>>0){$c(a+48|0,b+1|0)}d=1;c=i[a+48>>2]+(b>>>3&536870908)|0;i[c>>2]=i[c>>2]|1<>2];a=i[a+36>>2];if(c-a>>2>>>0<=b>>>0){break a}Gc(i[a+(b<<2)>>2])}return d|0}function Al(a){var b=0,c=0,d=0,e=0;if(a){d=i[a+24>>2];if(d){b=d;c=i[a+28>>2];e=b;a:{if((c|0)==(b|0)){break a}while(1){c=c+ -4|0;b=i[c>>2];i[c>>2]=0;if(b){yk(b+12|0,i[b+16>>2]);zk(b,i[b+4>>2]);bp(b)}if((c|0)!=(d|0)){continue}break}e=i[a+24>>2]}b=e;i[a+28>>2]=d;bp(b)}yk(a+12|0,i[a+16>>2]);zk(a,i[a+4>>2]);bp(a)}}function fp(a){var b=0;b=1;a:{if((a|0)>=1024){b=8.98846567431158e+307;if((a|0)<2047){a=a+ -1023|0;break a}b=z;a=((a|0)<3069?a:3069)+ -2046|0;break a}if((a|0)>-1023){break a}b=2.2250738585072014e-308;if((a|0)>-2045){a=a+1022|0;break a}b=0;a=((a|0)>-3066?a:-3066)+2044|0}Cp(0,0);Cp(1,a+1023<<20);return b*+Dp()}function zd(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;c=F-16|0;F=c;i[c+12>>2]=i[a+12>>2];d=i[b+20>>2];e=d;f=i[b+16>>2];if((d|0)<0?1:(d|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],c+12|0,c+16|0);f=i[b+16>>2];e=i[b+20>>2]}i[c+8>>2]=i[a+16>>2];if((e|0)<0?1:(e|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],c+8|0,c+12|0)}F=c+16|0;return 1}function xe(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;c=F-16|0;F=c;i[c+12>>2]=i[a+12>>2];d=i[b+20>>2];e=d;f=i[b+16>>2];if((d|0)<0?1:(d|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],c+12|0,c+16|0);f=i[b+16>>2];e=i[b+20>>2]}i[c+8>>2]=i[a+24>>2];if((e|0)<0?1:(e|0)<=0?f>>>0<=0:0){ca(b,i[b+4>>2],c+8|0,c+12|0)}F=c+16|0;return 1}function Tl(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;Gm(a,b,c);d=i[a+96>>2];a=i[a+100>>2];if((d|0)!=(a|0)){a=(a-d|0)/12|0;e=a>>>0>1?a:1;b=i[b>>2];c=0;while(1){a=o(c,12)+d|0;i[a>>2]=i[b+(i[a>>2]<<2)>>2];i[a+4>>2]=i[b+(i[a+4>>2]<<2)>>2];i[a+8>>2]=i[b+(i[a+8>>2]<<2)>>2];c=c+1|0;if((e|0)!=(c|0)){continue}break}}}function Xl(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=F-16|0;F=d;e=i[c>>2];i[c>>2]=0;i[d+8>>2]=e;Am(a,b,d+8|0);c=i[d+8>>2];i[d+8>>2]=0;if(c){Wb(c)}e=i[a+84>>2];c=i[a+88>>2]-e>>2;a:{if((c|0)>(b|0)){break a}b=b+1|0;if(b>>>0>c>>>0){Yl(a+84|0,b-c|0);break a}if(b>>>0>=c>>>0){break a}i[a+88>>2]=e+(b<<2)}F=d+16|0}function An(a,b){var c=0,d=0,e=0,f=0,g=0,h=0;d=F-16|0;F=d;g=a;h=a;a:{if(!b){b=0;break a}c=b>>31;e=c+b^c;c=r(e);sn(d,e,0,0,0,c+81|0);c=(i[d+12>>2]^65536)+(16414-c<<16)|0;e=0+i[d+8>>2]|0;if(e>>>0>>0){c=c+1|0}f=b&-2147483648|c;c=i[d+4>>2];b=i[d>>2]}i[h>>2]=b;i[g+4>>2]=c;i[a+8>>2]=e;i[a+12>>2]=f;F=d+16|0}function zo(a,b,c,d,e){var f=0,g=0,h=0;f=F-16|0;F=f;if(-17-b>>>0>=c>>>0){g=Yn(a);a:{if(2147483623>b>>>0){i[f+8>>2]=b<<1;i[f+12>>2]=b+c;c=ao(i[Mn(f+12|0,f+8|0)>>2]);break a}c=-18}h=c+1|0;c=bo(h);if(e){Ln(c,g,e)}d=d-e|0;if(d){Ln(c+e|0,e+g|0,d)}if((b|0)!=10){bp(g)}co(a,c);eo(a,h);F=f+16|0;return}qo();x()}function da(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,k=0;h=i[c+8>>2];f=i[c+16>>2];g=i[c+12>>2];d=g;e=i[c+20>>2];if((d|0)>(e|0)?1:(d|0)>=(e|0)?h>>>0>f>>>0:0){b=j[f+i[c>>2]|0];d=e;k=f+1|0;if(k>>>0<1){d=d+1|0}i[c+16>>2]=k;i[c+20>>2]=d;i[a+4>>2]=b}return((g|0)>(e|0)?1:(g|0)>=(e|0)?h>>>0>f>>>0:0)|0}function Ql(a,b){var c=0,d=0,e=0,f=0;a:{if((b|0)==-1){break a}b=o(b,3);if((b|0)==-1){break a}d=-1;e=i[a>>2];f=i[e+(b<<2)>>2];a=-1;c=b+1|0;c=(c>>>0)%3|0?c:b+ -2|0;if((c|0)!=-1){a=i[(c<<2)+e>>2]}b=b+((b>>>0)%3|0?-1:2)|0;if((b|0)!=-1){d=i[(b<<2)+e>>2]}return(a|0)==(f|0)|(d|0)==(f|0)|(a|0)==(d|0)}return 1}function bn(a){var b=0,c=0,d=0,e=0,f=0;while(1){b=a;a=b+1|0;if(an(g[b|0])){continue}break}a:{b:{c:{c=g[b|0];switch(c+ -43|0){case 0:break b;case 2:break c;default:break a}}e=1}c=g[a|0];b=a;f=e}if(Mm(c)){while(1){d=(o(d,10)-g[b|0]|0)+48|0;a=g[b+1|0];b=b+1|0;if(Mm(a)){continue}break}}return f?d:0-d|0}function Ym(a,b,c){var d=0,e=0,f=0;a:{if((b|0)==1&a>>>0<0|b>>>0<1){d=a;break a}while(1){d=wp(a,b,10,0);e=H;f=e;e=up(d,e,10,0);c=c+ -1|0;g[c|0]=a-e|48;e=b>>>0>9;a=d;b=f;if(e){continue}break}}if(d){while(1){c=c+ -1|0;a=(d>>>0)/10|0;g[c|0]=d-o(a,10)|48;b=d>>>0>9;d=a;if(b){continue}break}}return c}function hd(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0,f=0;d=F-48|0;F=d;f=i[b+4>>2];b=i[b+12>>2];i[d+40>>2]=0;e=d+32|0;i[e>>2]=0;i[e+4>>2]=0;i[d+24>>2]=0;i[d+28>>2]=0;i[d+16>>2]=0;i[d+20>>2]=0;i[d+8>>2]=0;i[d+12>>2]=0;id(d,c,b,f,d+8|0);b=i[e>>2];if(b){i[d+36>>2]=b;bp(b)}i[a>>2]=i[d>>2];F=d+48|0}function cf(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;c=F-16|0;F=c;d=a+40|0;Aa(c+8|0,d,i[a+8>>2],i[b+4>>2]-i[b>>2]>>2);d=ma(d,i[a+8>>2],b,i[c+8>>2]);a:{if(!d){break a}e=i[c+8>>2];i[c+8>>2]=0;b=i[a+32>>2];i[a+32>>2]=e;if(!b){break a}Wb(b)}a=i[c+8>>2];i[c+8>>2]=0;if(a){Wb(a)}F=c+16|0;return d|0}function Oi(a){a=a|0;var b=0;i[a+8>>2]=12332;i[a>>2]=12120;b=i[a+96>>2];if(b){i[a+100>>2]=b;bp(b)}b=i[a+80>>2];if(b){i[a+84>>2]=b;bp(b)}b=i[a+68>>2];if(b){i[a+72>>2]=b;bp(b)}b=i[a+56>>2];if(b){i[a+60>>2]=b;bp(b)}i[a+8>>2]=12568;b=i[a+44>>2];if(b){bp(b)}b=i[a+32>>2];if(b){bp(b)}return a|0}function qe(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;c=F-16|0;F=c;d=a+40|0;Aa(c+8|0,d,i[a+8>>2],i[b+4>>2]-i[b>>2]>>2);d=Y(d,i[a+8>>2],b,i[c+8>>2]);a:{if(!d){break a}e=i[c+8>>2];i[c+8>>2]=0;b=i[a+32>>2];i[a+32>>2]=e;if(!b){break a}Wb(b)}a=i[c+8>>2];i[c+8>>2]=0;if(a){Wb(a)}F=c+16|0;return d|0}function Aa(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,h=0,j=0;e=F+ -64|0;F=e;f=I[i[i[b>>2]+44>>2]](b,c)|0;b=I[i[i[b>>2]+40>>2]](b,c)|0;h=Ba(e);j=i[c+56>>2];c=b;b=o(Sj(b),f);Ca(h,j,0,f<<24>>24,c,b,b>>31);b=ho(96);c=Fa(b,h);i[a>>2]=b;Ha(c,d);g[b+84|0]=1;i[b+72>>2]=i[b+68>>2];F=e- -64|0}function Do(a,b,c){var d=0,e=0,f=0,g=0,h=0;f=F-16|0;F=f;g=+c;d=Sn(b);while(1){a:{e=Yn(b);n[f>>3]=g;h=b;e=Rn(e,d+1|0,f);b:{if((e|0)>=0){if(e>>>0<=d>>>0){break a}d=e;break b}d=d<<1|1}Un(h,d);continue}break}Un(b,e);d=i[b+4>>2];i[a>>2]=i[b>>2];i[a+4>>2]=d;i[a+8>>2]=i[b+8>>2];Vn(b);F=f+16|0}function be(a){a=a|0;var b=0;i[a>>2]=4804;b=i[a+152>>2];if(b){i[a+156>>2]=b;bp(b)}b=i[a+112>>2];if(b){i[a+116>>2]=b;bp(b)}b=i[a+96>>2];if(b){bp(b)}b=i[a+84>>2];if(b){bp(b)}b=i[a+72>>2];if(b){bp(b)}b=i[a+60>>2];if(b){bp(b)}i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}return a|0}function Pi(a){a=a|0;var b=0;i[a+8>>2]=12332;i[a>>2]=12120;b=i[a+96>>2];if(b){i[a+100>>2]=b;bp(b)}b=i[a+80>>2];if(b){i[a+84>>2]=b;bp(b)}b=i[a+68>>2];if(b){i[a+72>>2]=b;bp(b)}b=i[a+56>>2];if(b){i[a+60>>2]=b;bp(b)}i[a+8>>2]=12568;b=i[a+44>>2];if(b){bp(b)}b=i[a+32>>2];if(b){bp(b)}bp(a)}function Cd(a){a=a|0;var b=0;i[a>>2]=3240;b=i[a+152>>2];if(b){i[a+156>>2]=b;bp(b)}b=i[a+112>>2];if(b){i[a+116>>2]=b;bp(b)}b=i[a+96>>2];if(b){bp(b)}b=i[a+84>>2];if(b){bp(b)}b=i[a+72>>2];if(b){bp(b)}b=i[a+60>>2];if(b){bp(b)}i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}return a|0}function go(a,b,c){var d=0,e=0,f=0,h=0;f=F-16|0;F=f;e=c-b|0;if(e>>>0<=4294967279){a:{if(e>>>0<=10){$n(a,e);d=a;break a}h=ao(e)+1|0;d=bo(h);co(a,d);eo(a,h);aa(a,e)}while(1){if((b|0)!=(c|0)){_n(d,b);d=d+1|0;b=b+1|0;continue}break}g[f+15|0]=0;_n(d,f+15|0);F=f+16|0;return}qo();x()}function gg(a){var b=0,c=0;c=i[a+8>>2];if((c|0)>=2){b=+(c|0);b=v(ep(b)*b-n[a>>3]);if(q(b)<0x8000000000000000){H=q(b)>=1?b>0?~~s(u(b*2.3283064365386963e-10),4294967295)>>>0:~~v((b- +(~~b>>>0>>>0))*2.3283064365386963e-10)>>>0:0;return~~b>>>0}a=-2147483648}else{a=0}H=a;return 0}function Ni(a){a=a|0;var b=0;i[a>>2]=12332;b=i[a+88>>2];if(b){i[a+92>>2]=b;bp(b)}b=i[a+72>>2];if(b){i[a+76>>2]=b;bp(b)}b=i[a+60>>2];if(b){i[a- -64>>2]=b;bp(b)}b=i[a+48>>2];if(b){i[a+52>>2]=b;bp(b)}i[a>>2]=12568;b=i[a+36>>2];if(b){bp(b)}b=i[a+24>>2];if(b){bp(b)}return a|0}function En(a,b){var c=0,d=0,e=0,f=0;i[a>>2]=0;i[a+4>>2]=0;e=a;f=a;if(b){a=b;c=r(a);d=(c|32)+113&63;b=d&31;if(32<=(d&63)>>>0){d=a<>>32-b;b=a<>>0>>0?a+1|0:a}else{a=0;b=0}i[f+8>>2]=b;i[e+12>>2]=a}function Hl(a,b){var c=0;i[a+4>>2]=b;i[a>>2]=0;a:{b:{if(!b){break b}c=So(b,16916,16636);if(!c){break b}b=ho(56);Rf(b,c);c=i[a>>2];i[a>>2]=b;if(!c){break a}I[i[i[c>>2]+4>>2]](c);return a}c=ho(56);Pf(c,b);b=i[a>>2];i[a>>2]=c;if(!b){break a}I[i[i[b>>2]+4>>2]](b)}return a}function Vi(a){a=a|0;var b=0;i[a>>2]=12332;b=i[a+88>>2];if(b){i[a+92>>2]=b;bp(b)}b=i[a+72>>2];if(b){i[a+76>>2]=b;bp(b)}b=i[a+60>>2];if(b){i[a- -64>>2]=b;bp(b)}b=i[a+48>>2];if(b){i[a+52>>2]=b;bp(b)}i[a>>2]=12568;b=i[a+36>>2];if(b){bp(b)}b=i[a+24>>2];if(b){bp(b)}bp(a)}function Pj(a,b,c){var d=0;if((c|0)<0?1:(c|0)<=0?b>>>0<0:0){return 0}d=i[a>>2];c=i[a+4>>2]-d|0;a:{if(c>>>0>>0){Qj(a,b-c|0);break a}if(c>>>0<=b>>>0){break a}i[a+4>>2]=b+d}b=a+24|0;c=b;d=b;a=i[b+4>>2];b=i[b>>2]+1|0;if(b>>>0<1){a=a+1|0}i[d>>2]=b;i[c+4>>2]=a;return 1}function Ha(a,b){var c=0,d=0;a:{if(i[a+64>>2]){break a}d=ho(32);Oj(d);c=i[a+64>>2];i[a+64>>2]=d;if(!c){break a}d=i[c>>2];if(d){i[c+4>>2]=d;bp(d)}bp(c)}c=Sj(i[a+28>>2]);c=o(c,g[a+24|0]);d=c;c=c>>31;if(Pj(i[a+64>>2],up(d,c,b,0),H)){Da(a,i[a+64>>2],d,c);i[a+80>>2]=b}}function Qm(a,b){var c=0,d=0,e=0;Ep(+a);c=Bp(1)|0;d=Bp(0)|0;e=c;c=c>>>20&2047;if((c|0)!=2047){if(!c){c=b;if(a==0){b=0}else{a=Qm(a*0x10000000000000000,b);b=i[b>>2]+ -64|0}i[c>>2]=b;return a}i[b>>2]=c+ -1022;Cp(0,d|0);Cp(1,e&-2146435073|1071644672);a=+Dp()}return a}function df(a){a=a|0;var b=0;i[a+40>>2]=1140;i[a>>2]=9756;b=i[a+48>>2];if(b){i[a+52>>2]=b;bp(b)}i[a>>2]=2248;b=i[a+36>>2];i[a+36>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}i[a>>2]=1860;b=i[a+32>>2];i[a+32>>2]=0;if(b){Wb(b)}b=i[a+16>>2];if(b){i[a+20>>2]=b;bp(b)}return a|0}function bi(a){var b=0;if(a){b=i[a+76>>2];if(b){i[a+80>>2]=b;bp(b)}b=i[a- -64>>2];if(b){i[a+68>>2]=b;bp(b)}b=i[a+48>>2];if(b){i[a+52>>2]=b;bp(b)}b=i[a+24>>2];if(b){i[a+28>>2]=b;bp(b)}b=i[a+12>>2];if(b){i[a+16>>2]=b;bp(b)}b=i[a>>2];if(b){i[a+4>>2]=b;bp(b)}bp(a)}}function Mf(a,b,c,d){var e=0;e=F-16|0;F=e;c=Nf(a,b,e+12|0,e+8|0,c);if(!i[c>>2]){b=ho(40);ro(b+16|0,d);ro(b+28|0,d+12|0);i[b+8>>2]=i[e+12>>2];i[b>>2]=0;i[b+4>>2]=0;i[c>>2]=b;d=i[i[a>>2]>>2];if(d){i[a>>2]=d;b=i[c>>2]}Kf(i[a+4>>2],b);i[a+8>>2]=i[a+8>>2]+1}F=e+16|0}function ni(a){var b=0;b=i[a+84>>2];if(b){i[a+88>>2]=b;bp(b)}b=i[a+72>>2];if(b){i[a+76>>2]=b;bp(b)}b=i[a+52>>2];if(b){i[a+56>>2]=b;bp(b)}b=i[a+40>>2];if(b){i[a+44>>2]=b;bp(b)}b=i[a+28>>2];if(b){i[a+32>>2]=b;bp(b)}b=i[a+12>>2];if(b){bp(b)}a=i[a>>2];if(a){bp(a)}}function uf(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;e=F-96|0;F=e;f=Pf(e+40|0,c);vf(e,b,c);Vf(f,e);kd(e+24|0,i[e+28>>2]);wf(e+12|0,i[e+16>>2]);kd(e,i[e+4>>2]);Sf(a,f,d);i[f>>2]=10308;kd(f+28|0,i[f+32>>2]);wf(f+16|0,i[f+20>>2]);kd(f+4|0,i[f+8>>2]);F=e+96|0}function ef(a){a=a|0;var b=0;i[a+40>>2]=1140;i[a>>2]=9756;b=i[a+48>>2];if(b){i[a+52>>2]=b;bp(b)}i[a>>2]=2248;b=i[a+36>>2];i[a+36>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}i[a>>2]=1860;b=i[a+32>>2];i[a+32>>2]=0;if(b){Wb(b)}b=i[a+16>>2];if(b){i[a+20>>2]=b;bp(b)}bp(a)}function cn(a){var b=0,c=0;b=j[a+74|0];g[a+74|0]=b+ -1|b;if(l[a+20>>2]>l[a+28>>2]){I[i[a+36>>2]](a,0,0)|0}i[a+28>>2]=0;i[a+16>>2]=0;i[a+20>>2]=0;b=i[a>>2];if(b&4){i[a>>2]=b|32;return-1}c=i[a+44>>2]+i[a+48>>2]|0;i[a+8>>2]=c;i[a+4>>2]=c;return b<<27>>31}function sp(a,b,c,d){var e=0,f=0,g=0,h=0;e=b;f=e>>31;e=e>>31;a=a^e;g=a-e|0;h=(b^f)-((a>>>0>>0)+f|0)|0;e=d;f=e>>31;e=e>>31;a=c^e;e=wp(g,h,a-e|0,(d^f)-((a>>>0>>0)+f|0)|0);b=b^d;c=b>>31;a=b>>31;b=e^a;d=b-a|0;H=(c^H)-((b>>>0>>0)+c|0)|0;return d}function Pc(a,b){a=a|0;b=b|0;var c=0,d=0;c=i[a+72>>2];a:{if(!c){break a}i[c+4>>2]=a+60;if(!(I[i[i[c>>2]+12>>2]](c)|0)){break a}if(!(I[i[i[a>>2]+40>>2]](a)|0)){break a}if(!(I[i[i[a>>2]+44>>2]](a,b)|0)){break a}d=I[i[i[a>>2]+48>>2]](a,b)|0}return d|0}function Ca(a,b,c,d,e,f,h){var j=0;i[a>>2]=c;if(c){j=i[c+20>>2];i[a+8>>2]=i[c+16>>2];i[a+12>>2]=j;j=i[c+28>>2];i[a+16>>2]=i[c+24>>2];i[a+20>>2]=j}i[a+56>>2]=b;i[a+48>>2]=0;i[a+52>>2]=0;i[a+40>>2]=f;i[a+44>>2]=h;g[a+32|0]=0;i[a+28>>2]=e;g[a+24|0]=d}function Oh(a,b){a=a|0;b=b|0;var c=0,d=0;a:{c=i[a+172>>2];a=i[a+176>>2];if((c|0)!=(a|0)){d=(a-c|0)/136|0;a=0;while(1){if(i[o(a,136)+c>>2]==(b|0)){break a}a=a+1|0;if(a>>>0>>0){continue}break}}return 0}a=o(a,136)+c|0;return(j[a+100|0]?a+4|0:0)|0}function Rc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;c=i[a+36>>2];if((c|0)==i[a+40>>2]){return 1}f=a+60|0;while(1){c=i[(d<<2)+c>>2];e=I[i[i[c>>2]+20>>2]](c,f,b)|0;if(e){d=d+1|0;c=i[a+36>>2];if(d>>>0>2]-c>>2>>>0){continue}}break}return e|0}function Ph(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;a:{d=i[a+172>>2];c=i[a+176>>2];if((d|0)!=(c|0)){e=(c-d|0)/136|0;c=0;while(1){if(i[o(c,136)+d>>2]==(b|0)){break a}c=c+1|0;if(c>>>0>>0){continue}break}}return a+40|0}return(o(c,136)+d|0)+104|0}function Hm(a){a=a|0;var b=0,c=0,d=0;b=1;a:{if(!i[a+80>>2]){break a}c=i[a+8>>2];if((i[a+12>>2]-c|0)<=0){break a}while(1){b=i[(d<<2)+c>>2];b=Ia(b,b);if(b){d=d+1|0;c=i[a+8>>2];if((d|0)>2]-c>>2){continue}}break}b=(b|0)!=0}return b|0}function Jn(a,b,c){var d=0,e=0,f=0,g=0;e=F-16|0;F=e;b=b-a>>2;while(1){if(b){i[e+12>>2]=a;d=e+12|0;f=d;g=i[d>>2];d=b>>>1|0;i[f>>2]=g+(d<<2);f=(d^-1)+b|0;b=d;d=Kn(i[e+12>>2],c);b=d?f:b;a=d?i[e+12>>2]+4|0:a;continue}break}F=e+16|0;return a}function im(a,b,c){var d=0,e=0,f=0;if(c){d=c+28|0;Jb(i[d>>2]-i[c+24>>2]>>2,b);e=i[c+24>>2];f=i[d>>2];if((e|0)!=(f|0)){while(1){d=i[e>>2];if(d){Jb(i[d+24>>2],b);gm(a,b,d)}e=e+4|0;if((f|0)!=(e|0)){continue}break}}gm(a,b,c)}return(c|0)!=0}function Fj(a){a=a|0;var b=0,c=0,d=0;b=i[a+32>>2];d=i[a+36>>2];if((b|0)==(d|0)){return 1}while(1){a:{c=i[i[a+8>>2]+(i[b>>2]<<2)>>2];c=I[i[i[c>>2]+20>>2]](c,i[a+44>>2])|0;b=b+4|0;if((d|0)==(b|0)){break a}if(c){continue}}break}return c|0}function Jj(a,b){a=a|0;b=b|0;var c=0,d=0;c=F-16|0;F=c;i[c+12>>2]=i[i[b+4>>2]+80>>2];b=i[b+44>>2];d=i[b+20>>2];if((d|0)<0?1:(d|0)<=0?l[b+16>>2]<=0:0){ca(b,i[b+4>>2],c+12|0,c+16|0)}i[a>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0;F=c+16|0}function Qc(a){a=a|0;var b=0,c=0,d=0,e=0;b=i[a+36>>2];if((b|0)==i[a+40>>2]){return 1}e=a+60|0;while(1){b=i[(c<<2)+b>>2];d=I[i[i[b>>2]+16>>2]](b,e)|0;if(d){c=c+1|0;b=i[a+36>>2];if(c>>>0>2]-b>>2>>>0){continue}}break}return d|0}function Re(a){a=a|0;var b=0;i[a>>2]=8408;b=i[a+136>>2];if(b){i[a+140>>2]=b;bp(b)}b=i[a+96>>2];if(b){i[a+100>>2]=b;bp(b)}b=i[a+80>>2];if(b){bp(b)}b=i[a+68>>2];if(b){bp(b)}b=i[a+56>>2];if(b){bp(b)}b=i[a+44>>2];if(b){bp(b)}return a|0}function Ae(a){a=a|0;var b=0;i[a>>2]=6760;b=i[a+136>>2];if(b){i[a+140>>2]=b;bp(b)}b=i[a+96>>2];if(b){i[a+100>>2]=b;bp(b)}b=i[a+80>>2];if(b){bp(b)}b=i[a+68>>2];if(b){bp(b)}b=i[a+56>>2];if(b){bp(b)}b=i[a+44>>2];if(b){bp(b)}return a|0}function Wb(a){a=a|0;var b=0,c=0;if(a){b=i[a+88>>2];i[a+88>>2]=0;if(b){c=i[b+8>>2];if(c){i[b+12>>2]=c;bp(c)}bp(b)}b=i[a+68>>2];if(b){i[a+72>>2]=b;bp(b)}b=i[a+64>>2];i[a+64>>2]=0;if(b){c=i[b>>2];if(c){i[b+4>>2]=c;bp(c)}bp(b)}bp(a)}}function Sc(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;c=i[a+36>>2];if((c|0)==i[a+40>>2]){return 1}while(1){c=i[(d<<2)+c>>2];e=I[i[i[c>>2]+24>>2]](c,b)|0;if(e){d=d+1|0;c=i[a+36>>2];if(d>>>0>2]-c>>2>>>0){continue}}break}return e|0}function Se(a){a=a|0;var b=0;i[a>>2]=8408;b=i[a+136>>2];if(b){i[a+140>>2]=b;bp(b)}b=i[a+96>>2];if(b){i[a+100>>2]=b;bp(b)}b=i[a+80>>2];if(b){bp(b)}b=i[a+68>>2];if(b){bp(b)}b=i[a+56>>2];if(b){bp(b)}b=i[a+44>>2];if(b){bp(b)}bp(a)}function Be(a){a=a|0;var b=0;i[a>>2]=6760;b=i[a+136>>2];if(b){i[a+140>>2]=b;bp(b)}b=i[a+96>>2];if(b){i[a+100>>2]=b;bp(b)}b=i[a+80>>2];if(b){bp(b)}b=i[a+68>>2];if(b){bp(b)}b=i[a+56>>2];if(b){bp(b)}b=i[a+44>>2];if(b){bp(b)}bp(a)}function Ga(a,b,c,d,e){var f=0,h=0,j=0;j=ho(32);f=Oj(j);h=i[a+64>>2];i[a+64>>2]=j;j=a;if(h){f=i[h>>2];if(f){i[h+4>>2]=f;bp(f)}bp(h);f=i[a+64>>2]}h=b;b=o(Sj(d),c);Ca(j,h,f,c,d,b,b>>31);Ha(a,e);g[a+84|0]=1;i[a+72>>2]=i[a+68>>2]}function zm(a,b){var c=0,d=0,e=0,f=0;c=F-16|0;F=c;d=i[a+12>>2];e=i[a+8>>2];f=i[b>>2];i[b>>2]=0;i[c+8>>2]=f;I[i[i[a>>2]+8>>2]](a,d-e>>2,c+8|0);b=i[c+8>>2];i[c+8>>2]=0;if(b){Wb(b)}F=c+16|0;return(i[a+12>>2]-i[a+8>>2]>>2)+ -1|0}function rp(a,b,c,d){var e=0,f=0,g=0,h=0,i=0,j=0;e=c>>>16|0;f=a>>>16|0;j=o(e,f);g=c&65535;h=a&65535;i=o(g,h);f=(i>>>16|0)+o(f,g)|0;e=(f&65535)+o(e,h)|0;a=(o(b,c)+j|0)+o(a,d)+(f>>>16)+(e>>>16)|0;b=i&65535|e<<16;H=a;return b}function ba(a,b){a=a|0;b=b|0;var c=0,d=0;c=F-16|0;F=c;a=i[a+4>>2];a:{if((a|0)==-1){break a}g[c+15|0]=a;d=i[b+20>>2];if((d|0)>0?1:(d|0)>=0?l[b+16>>2]>0:0){break a}ca(b,i[b+4>>2],c+15|0,c+16|0)}F=c+16|0;return(a|0)!=-1|0}function Ao(a,b,c){var d=0,e=0,f=0;f=F-16|0;F=f;d=Tn(a);e=Sn(a);a:{if(d-e>>>0>=c>>>0){if(!c){break a}d=Yn(a);Ln(d+e|0,b,c);b=a;a=c+e|0;fo(b,a);g[f+15|0]=0;_n(a+d|0,f+15|0);break a}vo(a,d,(c+e|0)-d|0,e,e,0,c,b)}F=f+16|0}function Rj(a,b){var c=0,d=0;d=i[a>>2];c=i[a+4>>2]-d|0;a:{if(c>>>0>>0){Qj(a,b-c|0);break a}if(c>>>0<=b>>>0){break a}i[a+4>>2]=b+d}b=a+24|0;c=b;d=b;a=i[b+4>>2];b=i[b>>2]+1|0;if(b>>>0<1){a=a+1|0}i[d>>2]=b;i[c+4>>2]=a}function so(a,b,c){var d=0,e=0,f=0;d=F-16|0;F=d;if(4294967279>=c>>>0){a:{if(c>>>0<=10){$n(a,c);e=a;break a}f=ao(c)+1|0;e=bo(f);co(a,e);eo(a,f);aa(a,c)}Ln(e,b,c);g[d+15|0]=0;_n(c+e|0,d+15|0);F=d+16|0;return}qo();x()}function To(a,b,c){var d=0;d=i[a+16>>2];if(!d){i[a+36>>2]=1;i[a+24>>2]=c;i[a+16>>2]=b;return}a:{if((b|0)==(d|0)){if(i[a+24>>2]!=2){break a}i[a+24>>2]=c;return}g[a+54|0]=1;i[a+24>>2]=2;i[a+36>>2]=i[a+36>>2]+1}}function X(a,b){a=a|0;b=b|0;var c=0,d=0;i[b>>2]=2;c=i[b+8>>2];d=i[b+12>>2]-c|0;if(d>>>0<=4294967291){b=b+8|0;Rj(b,d+4|0);c=i[b>>2]}b=c+d|0;a=i[a+4>>2];g[b|0]=a;g[b+1|0]=a>>>8;g[b+2|0]=a>>>16;g[b+3|0]=a>>>24}function nf(a){var b=0,c=0;c=F-16|0;F=c;i[c+8>>2]=0;i[c+12>>2]=0;of(a,c+8|0);i[a+24>>2]=0;i[a+28>>2]=0;b=i[a+12>>2];i[a+16>>2]=b;if(b){i[a+16>>2]=b;bp(b)}b=i[a>>2];if(b){i[a+4>>2]=b;bp(b)}F=c+16|0;return a}function ad(a,b){a=a|0;b=b|0;var c=0,d=0;d=i[a+16>>2];a:{if(i[a+20>>2]-d>>2<=(b|0)){break a}b=i[(b<<2)+d>>2];if((b|0)<0){break a}a=i[i[a+36>>2]+(b<<2)>>2];c=i[a+32>>2];if(c){break a}c=i[a+8>>2]}return c|0}function Zm(a,b,c,d,e){var f=0;f=F-256|0;F=f;if(!(e&73728|(c|0)<=(d|0))){c=c-d|0;d=c>>>0<256;ip(f,b&255,d?c:256);if(!d){while(1){Tm(a,f,256);c=c+ -256|0;if(c>>>0>255){continue}break}}Tm(a,f,c)}F=f+256|0}function uo(a,b,c){var d=0,e=0,f=0,h=0;e=F-16|0;F=e;d=Tn(a);a:{if(d>>>0>=c>>>0){f=Yn(a);h=f;d=c;if(c){jp(h,b,d)}g[e+15|0]=0;_n(c+f|0,e+15|0);fo(a,c);break a}f=a;a=Sn(a);vo(f,d,c-d|0,a,0,a,c,b)}F=e+16|0}function Zc(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;d=-1;e=i[a+16>>2];a:{if(i[a+20>>2]-e>>2<=(b|0)){break a}b=i[(b<<2)+e>>2];if((b|0)<0){break a}d=i[i[i[i[a+36>>2]+(b<<2)>>2]+16>>2]+(c<<2)>>2]}return d|0}function Yc(a,b){a=a|0;b=b|0;var c=0,d=0;d=i[a+16>>2];c=0;a:{if(i[a+20>>2]-d>>2<=(b|0)){break a}b=i[(b<<2)+d>>2];c=0;if((b|0)<0){break a}a=i[i[a+36>>2]+(b<<2)>>2];c=i[a+20>>2]-i[a+16>>2]>>2}return c|0}function Bc(a){i[a+32>>2]=0;i[a+12>>2]=-1;i[a+4>>2]=0;i[a+8>>2]=0;i[a>>2]=1860;i[a+16>>2]=0;i[a+20>>2]=0;g[a+21|0]=0;g[a+22|0]=0;g[a+23|0]=0;g[a+24|0]=0;g[a+25|0]=0;g[a+26|0]=0;g[a+27|0]=0;g[a+28|0]=0}function xo(a,b){var c=0,d=0,e=0,f=0;e=F-16|0;F=e;if(b){d=Tn(a);c=Sn(a);f=c+b|0;if(d-c>>>0>>0){zo(a,d,f-d|0,c,c)}d=c;c=Yn(a);d=d+c|0;if(b){ip(d,0,b)}fo(a,f);g[e+15|0]=0;_n(c+f|0,e+15|0)}F=e+16|0}function xj(a){i[a+52>>2]=0;i[a>>2]=13484;i[a+4>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0;i[a+16>>2]=0;i[a+20>>2]=0;i[a+24>>2]=0;i[a+28>>2]=0;i[a+32>>2]=0;i[a+36>>2]=0;i[a+40>>2]=0;i[a+44>>2]=0;return a} + + + +function nd(a){a=a|0;var b=0;i[a>>2]=2248;b=i[a+36>>2];i[a+36>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}i[a>>2]=1860;b=i[a+32>>2];i[a+32>>2]=0;if(b){Wb(b)}b=i[a+16>>2];if(b){i[a+20>>2]=b;bp(b)}return a|0}function Hj(a,b){var c=0,d=0;a:{if((b|0)<0){break a}c=i[a+4>>2];if(i[c+12>>2]-i[c+8>>2]>>2<=(b|0)){break a}a=i[i[a+8>>2]+(i[i[a+20>>2]+(b<<2)>>2]<<2)>>2];d=I[i[i[a>>2]+36>>2]](a,b)|0}return d}function od(a){a=a|0;var b=0;i[a>>2]=2248;b=i[a+36>>2];i[a+36>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}i[a>>2]=1860;b=i[a+32>>2];i[a+32>>2]=0;if(b){Wb(b)}b=i[a+16>>2];if(b){i[a+20>>2]=b;bp(b)}bp(a)}function Ea(a){Ba(a);i[a+64>>2]=0;i[a+68>>2]=0;i[a+88>>2]=0;i[a+72>>2]=0;i[a+76>>2]=0;g[a+77|0]=0;g[a+78|0]=0;g[a+79|0]=0;g[a+80|0]=0;g[a+81|0]=0;g[a+82|0]=0;g[a+83|0]=0;g[a+84|0]=0;return a}function Mc(a,b,c){Fb(a,c);i[a+36>>2]=0;i[a+40>>2]=0;i[a>>2]=2032;i[a+44>>2]=0;i[a+48>>2]=0;i[a+52>>2]=0;i[a+56>>2]=0;i[a+60>>2]=0;i[a+64>>2]=0;i[a+68>>2]=0;c=i[b>>2];i[b>>2]=0;i[a+72>>2]=c}function Da(a,b,c,d){var e=0;i[a>>2]=b;e=i[b+20>>2];i[a+8>>2]=i[b+16>>2];i[a+12>>2]=e;e=i[b+24>>2];b=i[b+28>>2];i[a+48>>2]=0;i[a+52>>2]=0;i[a+40>>2]=c;i[a+44>>2]=d;i[a+16>>2]=e;i[a+20>>2]=b}function kp(a){var b=0;b=j[a+74|0];g[a+74|0]=b+ -1|b;b=i[a>>2];if(b&8){i[a>>2]=b|32;return-1}i[a+4>>2]=0;i[a+8>>2]=0;b=i[a+44>>2];i[a+28>>2]=b;i[a+20>>2]=b;i[a+16>>2]=b+i[a+48>>2];return 0}function Ba(a){i[a+8>>2]=0;i[a+12>>2]=0;i[a>>2]=0;i[a+40>>2]=0;i[a+44>>2]=0;i[a+28>>2]=9;g[a+24|0]=1;i[a+56>>2]=-1;i[a+60>>2]=0;i[a+16>>2]=0;i[a+20>>2]=0;i[a+48>>2]=0;i[a+52>>2]=0;return a}function On(a,b){var c=0,d=0;c=j[a|0];d=j[b|0];a:{if(!c|(c|0)!=(d|0)){break a}while(1){d=j[b+1|0];c=j[a+1|0];if(!c){break a}b=b+1|0;a=a+1|0;if((c|0)==(d|0)){continue}break}}return c-d|0}function Zl(a,b){a=a|0;b=b|0;var c=0,d=0;Cm(a,b);a:{if((b|0)<0){break a}c=i[a+88>>2];d=i[a+84>>2];if(c-d>>2<=(b|0)){break a}b=d+(b<<2)|0;d=b+4|0;c=c-d|0;if(c){jp(b,d,c)}i[a+88>>2]=b+c}}function zn(a,b,c,d,e,f,g,h,j){var k=0;k=F-16|0;F=k;tn(k,b,c,d,e,f,g,h,j^-2147483648);b=i[k+4>>2];i[a>>2]=i[k>>2];i[a+4>>2]=b;b=i[k+12>>2];i[a+8>>2]=i[k+8>>2];i[a+12>>2]=b;F=k+16|0}function yk(a,b){var c=0;if(b){yk(a,i[b>>2]);yk(a,i[b+4>>2]);c=b+28|0;a=i[c>>2];i[c>>2]=0;if(a){yk(a+12|0,i[a+16>>2]);zk(a,i[a+4>>2]);bp(a)}if(g[b+27|0]<=-1){bp(i[b+16>>2])}bp(b)}}function cp(a){var b=0,c=0;b=i[4804];c=a+3&-4;a=b+c|0;a:{if(a>>>0<=b>>>0?(c|0)>=1:0){break a}if(a>>>0>Ip()<<16>>>0){if(!(D(a|0)|0)){break a}}i[4804]=a;return b}i[4805]=48;return-1}function Gj(a,b){var c=0;a:{if((b|0)<0){break a}c=i[a+4>>2];if(i[c+12>>2]-i[c+8>>2]>>2<=(b|0)){break a}a=i[i[a+8>>2]+(i[i[a+20>>2]+(b<<2)>>2]<<2)>>2];I[i[i[a>>2]+32>>2]](a,b)|0}}function ro(a,b){var c=0,d=0,e=0;d=F-16|0;F=d;c=a;a:{if(!Wn(b)){i[c+8>>2]=i[b+8>>2];e=i[b+4>>2];i[c>>2]=i[b>>2];i[c+4>>2]=e;break a}so(a,i[b>>2],i[b+4>>2])}F=d+16|0;return a}function dj(a){a=a|0;var b=0;i[a+8>>2]=11784;i[a>>2]=12880;b=i[a+56>>2];if(b){i[a+60>>2]=b;bp(b)}i[a+8>>2]=12036;b=i[a+44>>2];if(b){bp(b)}b=i[a+32>>2];if(b){bp(b)}return a|0}function Zi(a){a=a|0;var b=0;i[a+8>>2]=12752;i[a>>2]=12588;b=i[a+56>>2];if(b){i[a+60>>2]=b;bp(b)}i[a+8>>2]=12568;b=i[a+44>>2];if(b){bp(b)}b=i[a+32>>2];if(b){bp(b)}return a|0}function ej(a){a=a|0;var b=0;i[a+8>>2]=11784;i[a>>2]=12880;b=i[a+56>>2];if(b){i[a+60>>2]=b;bp(b)}i[a+8>>2]=12036;b=i[a+44>>2];if(b){bp(b)}b=i[a+32>>2];if(b){bp(b)}bp(a)}function _i(a){a=a|0;var b=0;i[a+8>>2]=12752;i[a>>2]=12588;b=i[a+56>>2];if(b){i[a+60>>2]=b;bp(b)}i[a+8>>2]=12568;b=i[a+44>>2];if(b){bp(b)}b=i[a+32>>2];if(b){bp(b)}bp(a)}function Km(a,b,c){var d=0,e=0,f=0;a:{if(!c){break a}while(1){d=j[a|0];e=j[b|0];if((d|0)==(e|0)){b=b+1|0;a=a+1|0;c=c+ -1|0;if(c){continue}break a}break}f=d-e|0}return f}function Qb(a,b){Fb(a,b);i[a+36>>2]=0;i[a+40>>2]=0;i[a>>2]=1596;i[a+44>>2]=0;i[a+48>>2]=0;i[a+52>>2]=0;i[a+56>>2]=0;i[a+60>>2]=0;i[a+64>>2]=0;i[a+68>>2]=0;i[a+72>>2]=0}function zl(a,b){var c=0;a:{if(i[a+4>>2]|!a){break a}c=ho(36);lm(c,b);i[c+32>>2]=0;i[c+24>>2]=0;i[c+28>>2]=0;b=i[a+4>>2];i[a+4>>2]=c;c=1;if(!b){break a}Al(b)}return c}function Fo(a,b,c,d){var e=0,f=0,g=0,h=0;f=a;a:{b:{e=c-b|0;if((e|0)<=9){if((Go(d)|0)>(e|0)){break b}}g=a,h=jo(d,b),i[g>>2]=h;a=0;break a}i[a>>2]=c;a=61}i[f+4>>2]=a}function hg(a){var b=0,c=0,d=0;b=i[a+12>>2];a=i[a+16>>2];c=(((b-a|0)+1|0)/64|0)+a<<3;b=c;a=a<<3;d=a;b=b+a|0;a=(c>>31)+(a>>31)|0;a=b>>>0>>0?a+1|0:a;H=a;return b}function W(a,b){a=a|0;b=b|0;var c=0;b=i[b+88>>2];if(!(!b|i[b>>2]!=2)){c=a;a=i[b+8>>2];i[c+4>>2]=j[a|0]|j[a+1|0]<<8|(j[a+2|0]<<16|j[a+3|0]<<24);c=1}return c|0}function Gh(a){a=a|0;var b=0;a:{b=i[a+64>>2];if(!b){break a}b=I[i[i[b>>2]+32>>2]](b)|0;if(!b){break a}i[a+60>>2]=((i[b+4>>2]-i[b>>2]>>2>>>0)/3|0)-i[b+40>>2]}}function Mb(a,b){a=a|0;b=b|0;a:{if(!(I[i[i[a>>2]+40>>2]](a)|0)){break a}if(!(I[i[i[a>>2]+44>>2]](a,b)|0)){break a}return I[i[i[a>>2]+48>>2]](a,b)|0}return 0}function yo(a,b){var c=0,d=0;c=F-16|0;F=c;a:{if(Wn(a)){d=i[a>>2];g[c+15|0]=0;_n(b+d|0,c+15|0);aa(a,b);break a}g[c+14|0]=0;_n(a+b|0,c+14|0);$n(a,b)}F=c+16|0}function _h(a){a=a|0;var b=0;i[a>>2]=11784;b=i[a+48>>2];if(b){i[a+52>>2]=b;bp(b)}i[a>>2]=12036;b=i[a+36>>2];if(b){bp(b)}b=i[a+24>>2];if(b){bp(b)}return a|0}function Yi(a){a=a|0;var b=0;i[a>>2]=12752;b=i[a+48>>2];if(b){i[a+52>>2]=b;bp(b)}i[a>>2]=12568;b=i[a+36>>2];if(b){bp(b)}b=i[a+24>>2];if(b){bp(b)}return a|0}function Um(a){var b=0,c=0,d=0;if(Mm(g[i[a>>2]])){while(1){b=i[a>>2];d=g[b|0];i[a>>2]=b+1;c=(o(c,10)+d|0)+ -48|0;if(Mm(g[b+1|0])){continue}break}}return c}function Nj(a,b,c){var d=0,e=0,f=0;if((b|0)>0){while(1){f=d<<2;e=i[f+a>>2];i[c+f>>2]=(e|0)>-1?e<<1:(e^-1)<<1|1;d=d+1|0;if((d|0)!=(b|0)){continue}break}}}function cj(a){a=a|0;var b=0;i[a>>2]=12752;b=i[a+48>>2];if(b){i[a+52>>2]=b;bp(b)}i[a>>2]=12568;b=i[a+36>>2];if(b){bp(b)}b=i[a+24>>2];if(b){bp(b)}bp(a)}function Ii(a){a=a|0;var b=0;i[a>>2]=11784;b=i[a+48>>2];if(b){i[a+52>>2]=b;bp(b)}i[a>>2]=12036;b=i[a+36>>2];if(b){bp(b)}b=i[a+24>>2];if(b){bp(b)}bp(a)}function jo(a,b){var c=0,d=0;if(a>>>0<=99999999){return ko(b,a)}c=(a>>>0)/1e8|0;d=a-o(c,1e8)|0;a=(d>>>0)/1e4|0;return mo(mo(lo(b,c),a),d-o(a,1e4)|0)}function jm(a,b){var c=0,d=0;a:{d=i[b>>2];if(!d){break a}c=i[a+28>>2];if(c>>>0>2]){i[b>>2]=0;i[c>>2]=d;i[a+28>>2]=c+4;break a}km(a+24|0,b)}}function bk(a,b){a:{b=uc(a,b);if((b|0)==(a+4|0)){break a}a=b+28|0;if(g[b+39|0]<=-1){a=i[a>>2]}a=bn(a);if((a|0)==-1){break a}return(a|0)!=0}return 0}function _o(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;if(Qo(a,i[b+8>>2],f)){Wo(b,c,d,e);return}a=i[a+8>>2];I[i[i[a>>2]+20>>2]](a,b,c,d,e,f)}function Ud(a){a=a|0;var b=0;a:{if(!i[a- -64>>2]|!i[a+68>>2]|(!i[a+44>>2]|!i[a+48>>2])){break a}if(!i[a+52>>2]){break a}b=i[a+56>>2]!=0}return b|0}function ck(a,b,c){b=uc(a,b);a:{if((b|0)==(a+4|0)){break a}a=b+28|0;if(g[b+39|0]<=-1){a=i[a>>2]}a=bn(a);if((a|0)==-1){break a}c=(a|0)!=0}return c}function Me(a){a=a|0;var b=0;a:{if(!i[a+48>>2]|!i[a+52>>2]|(!i[a+28>>2]|!i[a+32>>2])){break a}if(!i[a+36>>2]){break a}b=i[a+40>>2]!=0}return b|0}function Qn(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0;e=i[a+20>>2];d=i[a+16>>2]-e|0;d=d>>>0>c>>>0?c:d;hp(e,b,d);i[a+20>>2]=d+i[a+20>>2];return c|0}function gp(a,b,c,d){var e=0,f=0;f=d&65535;d=d>>>16&32767;a:{if((d|0)!=32767){e=4;if(d){break a}return a|c|(b|f)?3:2}e=!(a|c|(b|f))}return e}function dn(a){var b=0,c=0;b=F-16|0;F=b;c=-1;a:{if(cn(a)){break a}if((I[i[a+32>>2]](a,b+15|0,1)|0)!=1){break a}c=j[b+15|0]}F=b+16|0;return c}function ym(a){var b=0,c=0;b=i[a+20>>2];a:{if((i[a+24>>2]-b|0)<1){break a}b=i[b>>2];if((b|0)==-1){break a}c=i[i[a+8>>2]+(b<<2)>>2]}return c}function xm(a,b){var c=0;c=-1;a:{if((b|0)==-1|(b|0)>4){break a}b=o(b,12)+a|0;a=i[b+20>>2];if((i[b+24>>2]-a|0)<1){break a}c=i[a>>2]}return c}function $m(a,b){a=a|0;b=b|0;var c=0,d=0,e=0;c=b;b=i[b>>2]+15&-16;i[c>>2]=b+16;d=a,e=Dn(i[b>>2],i[b+4>>2],i[b+8>>2],i[b+12>>2]),n[d>>3]=e}function Wm(a,b,c,d){if(a|b){while(1){c=c+ -1|0;g[c|0]=j[(a&15)+17488|0]|d;a=(b&15)<<28|a>>>4;b=b>>>4|0;if(a|b){continue}break}}return c}function Ul(a){a=a|0;var b=0;i[a>>2]=16592;b=i[a+96>>2];if(b){i[a+100>>2]=b;bp(b)}b=i[a+84>>2];if(b){i[a+88>>2]=b;bp(b)}Vl(a);return a|0}function Co(a,b){var c=0,d=0;c=F-16|0;F=c;d=F-16|0;F=d;Vn(c);F=d+16|0;Un(c,Tn(c));Do(a,c,b);a=c;if(Wn(c)){d=i[a>>2];Xn(a);bp(d)}F=c+16|0}function no(a,b){var c=0;if(b>>>0<=99){return lo(a,b)}if(b>>>0<=999){c=a;a=(b>>>0)/100|0;return po(oo(c,a),b-o(a,100)|0)}return mo(a,b)}function Wl(a){a=a|0;var b=0;i[a>>2]=16592;b=i[a+96>>2];if(b){i[a+100>>2]=b;bp(b)}b=i[a+84>>2];if(b){i[a+88>>2]=b;bp(b)}Vl(a);bp(a)}function zk(a,b){if(b){zk(a,i[b>>2]);zk(a,i[b+4>>2]);a=i[b+28>>2];if(a){i[b+32>>2]=a;bp(a)}if(g[b+27|0]<=-1){bp(i[b+16>>2])}bp(b)}}function pf(a){var b=0;b=F-16|0;F=b;i[b+8>>2]=0;i[b+12>>2]=0;of(a,b+8|0);i[a+24>>2]=0;i[a+28>>2]=0;i[a+16>>2]=i[a+12>>2];F=b+16|0}function en(a){var b=0,c=0;i[a+112>>2]=0;i[a+116>>2]=0;b=i[a+8>>2];c=b-i[a+4>>2]|0;i[a+120>>2]=c;i[a+124>>2]=c>>31;i[a+104>>2]=b}function ee(a){a=a|0;var b=0;i[a>>2]=5040;b=i[a+76>>2];if(b){bp(b)}i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}return a|0}function Vf(a,b){var c=0;c=a+4|0;if((c|0)!=(b|0)){xf(c,i[b>>2],b+4|0);Wf(a+16|0,i[b+12>>2],b+16|0);xf(a+28|0,i[b+24>>2],b+28|0)}}function Jc(a){a=a|0;var b=0;i[a>>2]=1860;b=i[a+32>>2];i[a+32>>2]=0;if(b){Wb(b)}b=i[a+16>>2];if(b){i[a+20>>2]=b;bp(b)}return a|0}function Id(a){a=a|0;var b=0;i[a>>2]=3488;b=i[a+76>>2];if(b){bp(b)}i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}return a|0}function io(a,b){var c=0,d=0,e=0,f=0;c=mp(b);d=ho(c+13|0);i[d+8>>2]=0;i[d+4>>2]=c;i[d>>2]=c;e=a,f=hp(d+12|0,b,c+1|0),i[e>>2]=f}function Kb(a){a=a|0;var b=0;i[a>>2]=1432;b=i[a+16>>2];if(b){i[a+20>>2]=b;bp(b)}b=i[a+4>>2];if(b){i[a+8>>2]=b;bp(b)}return a|0}function kd(a,b){if(b){kd(a,i[b>>2]);kd(a,i[b+4>>2]);if(g[b+39|0]<=-1){bp(i[b+28>>2])}if(g[b+27|0]<=-1){bp(i[b+16>>2])}bp(b)}}function ak(a,b){var c=p(0);b=uc(a,b);if((b|0)!=(a+4|0)){a=b+28|0;if(g[b+39|0]<=-1){a=i[a>>2]}c=p(pn(a))}else{c=p(1)}return c}function Vo(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;if(Qo(a,i[b+8>>2],0)){To(b,c,d);return}a=i[a+8>>2];I[i[i[a>>2]+28>>2]](a,b,c,d)}function fe(a){a=a|0;var b=0;i[a>>2]=5040;b=i[a+76>>2];if(b){bp(b)}i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}bp(a)}function Sl(a){wm(a);i[a+84>>2]=0;i[a+88>>2]=0;i[a>>2]=16592;i[a+92>>2]=0;i[a+96>>2]=0;i[a+100>>2]=0;i[a+104>>2]=0;return a}function Kc(a){a=a|0;var b=0;i[a>>2]=1860;b=i[a+32>>2];i[a+32>>2]=0;if(b){Wb(b)}b=i[a+16>>2];if(b){i[a+20>>2]=b;bp(b)}bp(a)}function Jd(a){a=a|0;var b=0;i[a>>2]=3488;b=i[a+76>>2];if(b){bp(b)}i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}bp(a)}function mf(a){i[a>>2]=0;i[a+4>>2]=0;i[a+24>>2]=0;i[a+28>>2]=0;i[a+16>>2]=0;i[a+20>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0;return a}function ho(a){var b=0;a=a?a:1;a:{while(1){b=ap(a);if(b){break a}b=i[4822];if(b){I[b|0]();continue}break}C();x()}return b}function Xm(a,b,c){if(a|b){while(1){c=c+ -1|0;g[c|0]=a&7|48;a=(b&7)<<29|a>>>3;b=b>>>3|0;if(a|b){continue}break}}return c}function Uj(a,b){var c=0,d=0;d=i[a>>2];c=i[a+4>>2]-d|0;if(c>>>0>>0){Qj(a,b-c|0);return}if(c>>>0>b>>>0){i[a+4>>2]=b+d}}function wk(){var a=0;a=ho(24);i[a+4>>2]=0;i[a+8>>2]=0;i[a+16>>2]=0;i[a+20>>2]=0;i[a>>2]=a+4;i[a+12>>2]=a+16;return a|0}function nn(a,b){var c=0,d=p(0);c=F-16|0;F=c;on(c,a,b,0);d=wn(i[c>>2],i[c+4>>2],i[c+8>>2],i[c+12>>2]);F=c+16|0;return d}function qa(a,b,c,d,e){var f=0;f=b+ -1|0;if(f>>>0<=29){i[a+4>>2]=b;ra(a+8|0,c,(d<<2)+c|0);m[a+20>>2]=e}return f>>>0<30}function ma(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;if(i[c>>2]==i[c+4>>2]){na(a,b,i[d+80>>2],d);return 1}oa(a,b,c,d);return 1}function Bo(a,b){var c=0;c=F-32|0;F=c;Eo(c+8|0,c+21|0,c+32|0,b);b=F-16|0;F=b;go(a,c+21|0,i[c+8>>2]);F=b+16|0;F=c+32|0}function ie(a){a=a|0;var b=0;i[a>>2]=5264;nf(a+112|0);i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}return a|0}function gn(a,b,c,d,e,f,g,h,j){i[a>>2]=b;i[a+4>>2]=c;i[a+8>>2]=d;i[a+12>>2]=e&65535|(j>>>16&32768|e>>>16&32767)<<16}function Rf(a,b){i[a>>2]=10308;Qf(a+4|0);i[a+40>>2]=0;i[a+44>>2]=0;i[a+52>>2]=b;i[a+48>>2]=b;i[a>>2]=10492;return a}function Rd(a){a=a|0;var b=0;i[a>>2]=3724;nf(a+112|0);i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}return a|0}function Pf(a,b){i[a>>2]=10308;Qf(a+4|0);i[a+40>>2]=0;i[a+44>>2]=0;i[a+52>>2]=0;i[a+48>>2]=b;i[a>>2]=10492;return a}function Cc(a,b,c){a=a|0;b=b|0;c=c|0;i[a+4>>2]=b;b=i[i[i[b+4>>2]+8>>2]+(c<<2)>>2];i[a+12>>2]=c;i[a+8>>2]=b;return 1}function $e(a){ed(a);i[a+48>>2]=0;i[a+52>>2]=0;i[a+44>>2]=-1;i[a+40>>2]=1140;i[a>>2]=9756;i[a+56>>2]=0;i[a+60>>2]=0}function pn(a){var b=0,c=0;b=F-16|0;F=b;on(b,a,0,1);c=Dn(i[b>>2],i[b+4>>2],i[b+8>>2],i[b+12>>2]);F=b+16|0;return c}function cg(a){i[a+16>>2]=0;i[a+20>>2]=0;i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;i[a+24>>2]=0;i[a+28>>2]=0;i[a+32>>2]=0}function Ld(a){a=a|0;var b=0;if(!(!i[a+60>>2]|!i[a+44>>2]|(!i[a+48>>2]|!i[a+52>>2]))){b=i[a+56>>2]!=0}return b|0}function Ge(a){a=a|0;var b=0;if(!(!i[a+44>>2]|!i[a+28>>2]|(!i[a+32>>2]|!i[a+36>>2]))){b=i[a+40>>2]!=0}return b|0}function al(a){a=a|0;if(a){i[a>>2]=10384;kd(a+28|0,i[a+32>>2]);Bf(a+16|0,i[a+20>>2]);kd(a+4|0,i[a+8>>2]);bp(a)}}function je(a){a=a|0;var b=0;i[a>>2]=5264;nf(a+112|0);i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}bp(a)}function bg(a,b){var c=0,d=0;if(!(!a|!b|(a|0)==(b|0))){c=+(b>>>0)/+(a>>>0);d=1-c;c=-(c*ep(c)+d*ep(d))}return c}function Sd(a){a=a|0;var b=0;i[a>>2]=3724;nf(a+112|0);i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}bp(a)}function Qo(a,b,c){if(!c){return i[a+4>>2]==i[b+4>>2]}if((a|0)==(b|0)){return 1}return!On(i[a+4>>2],i[b+4>>2])}function zf(a){a=a|0;i[a>>2]=10308;kd(a+28|0,i[a+32>>2]);wf(a+16|0,i[a+20>>2]);kd(a+4|0,i[a+8>>2]);return a|0}function Oj(a){i[a+16>>2]=0;i[a+20>>2]=0;i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;i[a+24>>2]=0;i[a+28>>2]=0;return a}function Hf(a){a=a|0;i[a>>2]=10384;kd(a+28|0,i[a+32>>2]);Bf(a+16|0,i[a+20>>2]);kd(a+4|0,i[a+8>>2]);return a|0}function Tj(a){i[a>>2]=0;i[a+4>>2]=0;g[a+24|0]=0;i[a+16>>2]=0;i[a+20>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0;return a}function Mo(a){var b=0,c=0;a:{b=i[a>>2]+ -12|0;c=b+8|0;a=i[c>>2]+ -1|0;i[c>>2]=a;if((a|0)>-1){break a}bp(b)}}function ko(a,b){var c=0;if(b>>>0<=9999){return no(a,b)}c=a;a=(b>>>0)/1e4|0;return mo(no(c,a),b-o(a,1e4)|0)}function za(a){var b=0;b=A(8)|0;i[b>>2]=18596;i[b>>2]=18640;io(b+4|0,a);i[b>>2]=18688;B(b|0,18720,13);x()}function Tk(){var a=0;a=ho(48);i[a>>2]=10384;tf(a+4|0);i[a+40>>2]=0;i[a+44>>2]=0;i[a>>2]=10112;return a|0}function _j(a,b){b=uc(a,b);if((b|0)==(a+4|0)){return-1}a=b+28|0;if(g[b+39|0]<=-1){a=i[a>>2]}return bn(a)}function Jf(a){a=a|0;i[a>>2]=10308;kd(a+28|0,i[a+32>>2]);wf(a+16|0,i[a+20>>2]);kd(a+4|0,i[a+8>>2]);bp(a)}function If(a){a=a|0;i[a>>2]=10384;kd(a+28|0,i[a+32>>2]);Bf(a+16|0,i[a+20>>2]);kd(a+4|0,i[a+8>>2]);bp(a)}function Wi(a){a=a|0;var b=0;i[a>>2]=12568;b=i[a+36>>2];if(b){bp(b)}b=i[a+24>>2];if(b){bp(b)}return a|0}function Ji(a){a=a|0;var b=0;i[a>>2]=12036;b=i[a+36>>2];if(b){bp(b)}b=i[a+24>>2];if(b){bp(b)}return a|0}function $j(a,b,c){b=uc(a,b);if((b|0)!=(a+4|0)){a=b+28|0;if(g[b+39|0]<=-1){a=i[a>>2]}c=bn(a)}return c}function xd(a){a=a|0;var b=0;if(!(!i[a+52>>2]|(!i[a+44>>2]|!i[a+48>>2]))){b=i[a+56>>2]!=0}return b|0}function we(a){a=a|0;var b=0;if(!(!i[a+36>>2]|(!i[a+28>>2]|!i[a+32>>2]))){b=i[a+40>>2]!=0}return b|0}function Xi(a){a=a|0;var b=0;i[a>>2]=12568;b=i[a+36>>2];if(b){bp(b)}b=i[a+24>>2];if(b){bp(b)}bp(a)}function Ki(a){a=a|0;var b=0;i[a>>2]=12036;b=i[a+36>>2];if(b){bp(b)}b=i[a+24>>2];if(b){bp(b)}bp(a)}function Vd(a,b){a=a|0;b=b|0;var c=0;if(!(i[b+56>>2]|j[b+24|0]!=3)){i[a- -64>>2]=b;c=1}return c|0}function jf(a){var b=0;i[a+12>>2]=0;i[a+16>>2]=0;b=i[a>>2];i[a+4>>2]=b;if(b){i[a+4>>2]=b;bp(b)}}function Ne(a,b){a=a|0;b=b|0;var c=0;if(!(i[b+56>>2]|j[b+24|0]!=3)){i[a+48>>2]=b;c=1}return c|0}function Md(a,b){a=a|0;b=b|0;var c=0;if(!(i[b+56>>2]|j[b+24|0]!=3)){i[a+60>>2]=b;c=1}return c|0}function He(a,b){a=a|0;b=b|0;var c=0;if(!(i[b+56>>2]|j[b+24|0]!=3)){i[a+44>>2]=b;c=1}return c|0}function $o(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;if(Qo(a,i[b+8>>2],f)){Wo(b,c,d,e)}}function Go(a){var b=0;b=o(32-r(a|1)|0,1233)>>>12|0;return(b-(l[(b<<2)+18496>>2]>a>>>0)|0)+1|0}function Vn(a){var b=0;b=a;a=0;while(1){if((a|0)!=3){i[(a<<2)+b>>2]=0;a=a+1|0;continue}break}}function ao(a){var b=0;if(a>>>0>=11){b=a+16&-16;a=b+ -1|0;a=(a|0)==11?b:a}else{a=10}return a}function ud(a){a=a|0;var b=0;i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}return a|0}function va(a){a=a|0;var b=0;i[a>>2]=1140;b=i[a+8>>2];if(b){i[a+12>>2]=b;bp(b)}return a|0}function kl(a){a=a|0;var b=0;if(a){b=i[a>>2];i[a>>2]=0;if(b){I[i[i[b>>2]+4>>2]](b)}bp(a)}}function yp(a,b){var c=0,d=0;c=b&31;d=(-1>>>c&a)<>>a}function Eo(a,b,c,d){if(!((b|0)==(c|0)|(d|0)>-1)){g[b|0]=45;d=0-d|0;b=b+1|0}Fo(a,b,c,d)}function sj(a){a=a|0;var b=0;b=a;a=i[a+56>>2];i[b+60>>2]=(i[a+100>>2]-i[a+96>>2]|0)/12}function xp(a){var b=0,c=0;while(1){c=b;if(a){a=a-1&a;b=b+1|0;continue}break}return c}function vd(a){a=a|0;var b=0;i[a>>2]=3184;b=i[a+32>>2];if(b){i[a+36>>2]=b;bp(b)}bp(a)}function Pk(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;return wl(b,c,d,e,f,6)|0}function Ok(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;return wl(b,c,d,e,f,5)|0}function Nk(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;return yl(b,c,d,e,f,4)|0}function Mk(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;return yl(b,c,d,e,f,3)|0}function Lk(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;return xl(b,c,d,e,f,2)|0}function Kk(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;return xl(b,c,d,e,f,1)|0}function Jk(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;return wl(b,c,d,e,f,9)|0}function wa(a){a=a|0;var b=0;i[a>>2]=1140;b=i[a+8>>2];if(b){i[a+12>>2]=b;bp(b)}bp(a)}function el(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=p(f);Zf(i[a>>2],b,c,d,e,f)}function Rn(a,b,c){var d=0;d=F-16|0;F=d;i[d+12>>2]=c;a=Pn(a,b,c);F=d+16|0;return a}function wf(a,b){if(b){wf(a,i[b>>2]);wf(a,i[b+4>>2]);kd(b+20|0,i[b+24>>2]);bp(b)}}function Bf(a,b){if(b){Bf(a,i[b>>2]);Bf(a,i[b+4>>2]);kd(b+20|0,i[b+24>>2]);bp(b)}}function Eh(a,b){a=a|0;b=b|0;var c=0;c=a;a=i[b+64>>2];I[i[i[a>>2]+28>>2]](c,a)}function Wk(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=p(f);Ef(a,b,c,d,e,f)}function Uo(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;if(Qo(a,i[b+8>>2],0)){To(b,c,d)}}function Ue(a){a=a|0;var b=0;i[a>>2]=8668;b=i[a+60>>2];if(b){bp(b)}return a|0}function Ee(a){a=a|0;var b=0;i[a>>2]=7032;b=i[a+60>>2];if(b){bp(b)}return a|0}function ri(a,b){a=a|0;b=b|0;return i[i[a+28>>2]+(b>>>3&536870908)>>2]>>>b&1}function nm(a,b,c){var d=0;d=F-16|0;F=d;i[d+12>>2]=c;om(a,b,d+12|0);F=d+16|0}function mo(a,b){var c=0;c=a;a=(b>>>0)/100|0;return po(po(c,a),b-o(a,100)|0)}function wo(a,b){var c=0;c=Sn(a);if(c>>>0>>0){xo(a,b-c|0);return}yo(a,b)}function Lh(a,b){a=a|0;b=b|0;a=i[a+64>>2];return I[i[i[a>>2]+16>>2]](a,b)|0}function Kh(a,b){a=a|0;b=b|0;a=i[a+64>>2];return I[i[i[a>>2]+12>>2]](a,b)|0}function Ij(a,b){a=a|0;b=b|0;i[a>>2]=0;i[a+4>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0}function Dk(a){a=a|0;var b=0;if(a){b=i[a>>2];if(b){i[a+4>>2]=b;bp(b)}bp(a)}}function Dh(a,b){a=a|0;b=b|0;a=i[a+64>>2];return I[i[i[a>>2]+24>>2]](a,b)|0}function Ch(a,b){a=a|0;b=b|0;a=i[a+64>>2];return I[i[i[a>>2]+20>>2]](a,b)|0}function Ak(){var a=0;a=ho(12);i[a+8>>2]=0;i[a>>2]=0;i[a+4>>2]=0;return a|0}function sm(a,b,c){var d=0;d=F-16|0;F=d;n[d+8>>3]=c;tm(a,b,d+8|0);F=d+16|0}function po(a,b){b=k[(b<<1)+18272>>1];g[a|0]=b;g[a+1|0]=b>>>8;return a+2|0}function hf(a){i[a>>2]=0;i[a+4>>2]=0;i[a+16>>2]=0;i[a+8>>2]=0;i[a+12>>2]=0}function Y(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return Z(a,b,c,i[d+80>>2],d)|0}function Sj(a){a=a+ -1|0;if(a>>>0<=10){return i[(a<<2)+16240>>2]}return-1}function Nn(a,b){var c=0,d=0;c=F-16|0;F=c;d=Kn(a,b);F=c+16|0;return d?b:a}function Hk(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;return ul(b,c,d,e)|0}function Ve(a){a=a|0;var b=0;i[a>>2]=8668;b=i[a+60>>2];if(b){bp(b)}bp(a)}function Fe(a){a=a|0;var b=0;i[a>>2]=7032;b=i[a+60>>2];if(b){bp(b)}bp(a)}function Ec(a,b,c){a=a|0;b=b|0;c=c|0;return I[i[i[a>>2]+44>>2]](a,b,c)|0}function xk(a){a=a|0;if(a){yk(a+12|0,i[a+16>>2]);zk(a,i[a+4>>2]);bp(a)}}function uj(a,b){a=a|0;b=b|0;g[b+84|0]=1;i[b+72>>2]=i[b+68>>2];return 1}function Zd(a,b){a=a|0;b=b|0;if(b>>>0<=1){i[a+28>>2]=b}return b>>>0<2|0}function Hb(a,b,c){a=a|0;b=b|0;c=c|0;i[a+32>>2]=c;i[a+28>>2]=b;return 1}function Xo(a,b,c){if(!(i[a+28>>2]==1|i[a+4>>2]!=(b|0))){i[a+28>>2]=c}}function Hn(a,b,c){var d=0;d=F-16|0;F=d;a=Jn(a,b,c);F=d+16|0;return a}function _k(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return Gl(a,b,c,d)|0}function Jh(a){a=a|0;a=i[a+64>>2];return I[i[i[a>>2]+32>>2]](a)|0}function Sk(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return Cl(b,c,d)|0}function Rk(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return Bl(b,c,d)|0}function Gk(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return tl(b,c,d)|0}function Fk(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return sl(b,c,d)|0}function Dc(a,b){a=a|0;b=b|0;i[a+12>>2]=-1;i[a+8>>2]=b;return 1}function qp(a,b){a=a|0;b=b|0;if(!i[4947]){i[4948]=b;i[4947]=a}}function bo(a){if(4294967295>>0){za(18200);x()}return ho(a)}function Ik(a,b,c,d){a=a|0;b=b|0;c=c|0;d=+d;return vl(b,c,d)|0}function lj(a){xj(a);i[a+56>>2]=0;i[a+60>>2]=0;i[a>>2]=13060}function lo(a,b){if(b>>>0<=9){return oo(a,b)}return po(a,b)}function Xj(a){i[a+4>>2]=0;i[a+8>>2]=0;i[a>>2]=a+4;return a}function uk(a){a=a|0;return(i[a+100>>2]-i[a+96>>2]|0)/12|0}function kf(a){i[a+12>>2]=0;i[a+16>>2]=0;i[a+4>>2]=i[a>>2]}function Sn(a){if(Wn(a)){return i[a+4>>2]}return j[a+11|0]}function zp(a){if(a){return 31-r(a+ -1^a)|0}return 32} + + + +function Tn(a){if(Wn(a)){a=Xn(a)+ -1|0}else{a=10}return a}function fl(a,b,c){a=a|0;b=b|0;c=c|0;Xf(i[a>>2]+4|0,b,c)}function hl(a,b,c){a=a|0;b=b|0;c=c|0;return Il(a,b,c)|0}function Zk(a,b,c){a=a|0;b=b|0;c=c|0;return Fl(a,b,c)|0}function Xe(a){a=a|0;i[a>>2]=8916;nf(a+96|0);return a|0}function Lo(a){a=a|0;i[a>>2]=18640;Mo(a+4|0);return a|0}function Ke(a){a=a|0;i[a>>2]=7292;nf(a+96|0);return a|0}function Qk(a,b,c){a=a|0;b=b|0;c=c|0;return zl(b,c)|0}function wm(a){i[a>>2]=16864;ip(a+4|0,0,80);return a}function to(a,b){if((a|0)!=(b|0)){uo(a,Yn(b),Sn(b))}}function dl(a,b,c){a=a|0;b=b|0;c=c|0;Yf(i[a>>2],b,c)}function rk(a){a=a|0;return i[a+12>>2]-i[a+8>>2]>>2}function rj(a){a=a|0;i[a+52>>2]=i[i[a+56>>2]+80>>2]}function pe(a,b){a=a|0;b=b|0;return ba(a+40|0,b)|0}function bf(a,b){a=a|0;b=b|0;return ta(a+40|0,b)|0}function Ye(a){a=a|0;i[a>>2]=8916;nf(a+96|0);bp(a)}function Xk(a,b,c){a=a|0;b=b|0;c=c|0;Cf(a+4|0,b,c)}function Lj(a){a=a|0;i[a+52>>2]=i[i[a+4>>2]+80>>2]}function Le(a){a=a|0;i[a>>2]=7292;nf(a+96|0);bp(a)}function Bk(a,b){a=a|0;b=b|0;return g[i[a>>2]+b|0]}function sk(a){a=a|0;if(a){I[i[i[a>>2]+4>>2]](a)}}function fo(a,b){if(Wn(a)){aa(a,b);return}$n(a,b)}function Yn(a){if(Wn(a)){return i[a>>2]}return a}function Pm(a,b){if(!a){return 0}return Om(a,b)}function Ck(a){a=a|0;return i[a+4>>2]-i[a>>2]|0}function ed(a){Bc(a);i[a+36>>2]=0;i[a>>2]=2248}function Vk(a,b,c){a=a|0;b=b|0;c=c|0;Df(a,b,c)}function Tm(a,b,c){if(!(j[a|0]&32)){lp(b,c,a)}}function ya(a,b){a=a|0;b=b|0;return g[b+24|0]}function pp(a){a=a|0;a=F-a&-16;F=a;return a|0}function Ob(a,b,c){a=a|0;b=b|0;c=c|0;return-1}function jl(a){a=a|0;return i[i[a>>2]+44>>2]}function il(a){a=a|0;return i[i[a>>2]+40>>2]}function wp(a,b,c,d){a=tp(a,b,c,d);return a}function vp(a,b,c,d){a=sp(a,b,c,d);return a}function up(a,b,c,d){a=rp(a,b,c,d);return a}function gl(a,b){a=a|0;b=b|0;El(i[a>>2],b)}function dk(a){i[a>>2]=1065353216;return a}function cl(a,b){a=a|0;b=b|0;_f(i[a>>2],b)}function an(a){return(a|0)==32|a+ -9>>>0<5}function Xn(a){return i[a+8>>2]&2147483647}function vk(a,b){a=a|0;b=b|0;i[a+80>>2]=b}function oo(a,b){g[a|0]=b+48;return a+1|0}function bl(a){a=a|0;return Hl(ho(8),a)|0}function eo(a,b){i[a+8>>2]=b|-2147483648}function Kn(a,b){return l[a>>2]>2]}function qi(a){a=a|0;return i[a+12>>2]}function pk(a){a=a|0;return i[a+60>>2]}function ok(a){a=a|0;return i[a+48>>2]}function nk(a){a=a|0;return i[a+40>>2]}function kk(a){a=a|0;return i[a+56>>2]}function jk(a){a=a|0;return i[a+80>>2]}function _d(a){a=a|0;return i[a+28>>2]}function $k(a){a=a|0;return i[a+44>>2]}function yd(a,b){a=a|0;b=b|0;return-1}function wd(a){a=a|0;return i[a+4>>2]}function mk(a){a=a|0;return j[a+32|0]}function lk(a){a=a|0;return g[a+24|0]}function ia(a,b){a=a|0;b=b|0;return 2}function ha(a,b){a=a|0;b=b|0;return 6}function Wn(a){return j[a+11|0]>>>7|0}function Pb(a,b){a=a|0;b=b|0;return 1}function Oo(a){a=a|0;return i[a+4>>2]}function Nb(a,b){a=a|0;b=b|0;return 0}function Yk(a,b){a=a|0;b=b|0;El(a,b)}function Uk(a,b){a=a|0;b=b|0;Gf(a,b)}function ek(a,b,c){m[a>>2]=p(c|0)/b}function Mm(a){return a+ -48>>>0<10}function Ln(a,b,c){if(c){hp(a,b,c)}}function Gn(a,b,c){return Hn(a,b,c)}function tk(){return Sl(ho(108))|0}function fg(a,b,c,d){eg(a,b,c,d,1)}function dg(a,b,c,d){eg(a,b,c,d,0)}function qk(){return wm(ho(84))|0}function ik(){return Ea(ho(96))|0}function hk(){return Ba(ho(64))|0}function gk(a){a=a|0;if(a){bp(a)}}function Ko(a){a=a|0;return 18573}function Po(a){a=a|0;Lo(a);bp(a)}function ea(a){a=a|0;return a|0}function Mn(a,b){return Nn(a,b)}function tc(a){a=a|0;bp(sc(a))}function ce(a){a=a|0;bp(be(a))}function _n(a,b){g[a|0]=j[b|0]}function Xc(a){a=a|0;bp(Wc(a))}function No(a){a=a|0;bp(Lo(a))}function Im(a){a=a|0;bp(Vl(a))}function Hi(a){a=a|0;bp(Gi(a))}function Fi(a){a=a|0;bp(Ei(a))}function Dd(a){a=a|0;bp(Cd(a))}function xa(a){a=a|0;return 1}function re(a){a=a|0;return 3}function ga(a){a=a|0;return 2}function Td(a){a=a|0;return 6}function Lc(a){a=a|0;return 0}function Kd(a){a=a|0;return 5}function Ed(a){a=a|0;return 4}function aa(a,b){i[a+4>>2]=b}function Ek(){return ho(1)|0}function $n(a,b){g[a+11|0]=b}function qo(){za(18472);x()}function Ho(){za(18536);x()}function fk(){return 16284}function fa(a){a=a|0;bp(a)}function co(a,b){i[a>>2]=b}function Lm(){return 19220}function Gc(a){g[a+28|0]=1}function op(a){a=a|0;F=a}function np(){return F|0}function dm(a,b){em(a,b)}function Un(a,b){wo(a,b)}function Lb(a){a=a|0;x()}function ql(){return 4}function pl(){return 3}function ol(){return 2}function nl(){return 1}function ml(){return 0}function ll(){return-1}function Io(){In();x()}function In(){C();x()}function Zn(a){a=a|0}function Jo(){x()}function V(){} +// EMSCRIPTEN_END_FUNCS +var I=Ap([null,ea,fa,ga,W,X,Y,$,ba,da,Aa,ha,ia,Lo,va,wa,xa,ja,la,ma,pa,ta,ua,ha,ya,Kb,Lb,Hb,Ib,Jo,Mb,Nb,Ob,Nb,Nb,xa,Pb,sc,tc,ga,Rb,Zb,Yb,Jc,Kc,Cc,Dc,Pb,Ec,Pb,Lc,Lc,Hc,Ic,Fc,Wc,Xc,Nc,Oc,Lc,Pc,Yc,Zc,_c,ad,Qc,Rc,Sc,Tc,Vc,nd,od,fd,gd,xa,jd,hd,ld,ud,vd,xa,wd,xd,Lc,yd,Nb,Lc,xa,zd,Ad,Lb,Lb,Cd,Dd,Ed,xd,Fd,Gd,Id,Jd,Kd,Ld,xa,Nb,Md,Nd,Od,Rd,Sd,Td,Ud,xa,Nb,Vd,Wd,Xd,ea,fa,Zd,_d,Yd,Lb,vd,xa,xd,ae,Lb,be,ce,Ed,xd,Fd,de,ee,fe,Kd,Ld,xa,Nb,Md,Nd,ge,ie,je,Td,Ud,xa,Nb,Vd,Wd,ke,ea,fa,Zd,_d,le,Lb,vd,Lc,xa,ne,nd,od,oe,pe,xa,re,se,qe,ea,fa,xa,wd,we,Lc,yd,Nb,xa,re,xe,ye,Ae,Be,Ed,we,Ce,De,Ee,Fe,Kd,Ge,xa,Nb,He,Ie,Je,Ke,Le,Td,Me,xa,Nb,Ne,Oe,Pe,ea,fa,Zd,_d,Yd,fa,xa,we,Qe,Re,Se,Ed,we,Ce,Te,Ue,Ve,Kd,Ge,xa,Nb,He,Ie,We,Xe,Ye,Td,Me,xa,Nb,Ne,Oe,Ze,ea,fa,Zd,_d,le,fa,Lc,xa,_e,df,ef,af,bf,xa,ga,cf,Hf,If,uf,Af,zf,Jf,If,Jf,Hh,Ih,xa,xa,Bh,xa,mj,Aj,Ej,Ch,Dh,Fj,Fh,Jh,Kh,Lh,Eh,Gh,Ei,Fi,Nh,Oh,Ph,Qh,$h,ai,qi,ri,wd,Gi,Hi,ti,Oh,Ph,ui,$h,vi,qi,ri,wd,_h,Ii,Zh,Ji,Ki,Oi,Pi,Qi,Ri,Ni,Vi,Li,Wi,Xi,Zi,_i,$i,aj,Yi,cj,dj,ej,fj,gj,nj,Lb,xa,Pb,Lc,Nb,Nb,tj,Lc,qj,rj,oj,sj,ea,fa,uj,vj,Lb,Lc,Ij,tj,xa,Jj,Kj,Lj,tj,Lc,Jj,Mj,Lj,Ul,Wl,Xl,Zl,Hm,Em,Tl,Vl,Im,Am,Cm,Gm,_m,$m,Qn,ea,fa,Ko,No,Oo,Po,ea,fa,Zn,Zn,Ro,$o,Zo,Uo,fa,_o,Yo,Vo]);function Ip(){return f.byteLength/65536|0}function Jp(Tp){Tp=Tp|0;var J=Ip()|0;var K=J+Tp|0;if(J=0;--P){O[48+P]=52+P;O[65+P]=P;O[97+P]=26+P}O[43]=62;O[47]=63;function Kp(Up,Vp,Wp){var Q,R,P=0,S=Vp,T=Wp.length,U=Vp+(T*3>>2)-(Wp[T-2]=="=")-(Wp[T-1]=="=");for(;P>4;if(S>2;if(S=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}var WASM_PAGE_SIZE=65536;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var STACK_BASE=5262688;var INITIAL_INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_INITIAL_MEMORY/WASM_PAGE_SIZE,"maximum":2147483648/WASM_PAGE_SIZE})}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_INITIAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPreMain(cb){__ATMAIN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="draco_encoder.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}var binary=tryParseAsDataURI(wasmBinaryFile);if(binary){return binary}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"env":asmLibraryArg,"wasi_snapshot_preview1":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmTable=Module["asm"]["__indirect_function_table"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var tempDouble;var tempI64;function array_bounds_check_error(idx,size){throw"Array index "+idx+" out of bounds: [0,"+size+")"}function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function demangle(func){return func}function demangleAll(text){var regex=/\b_Z[\w\d_]+/g;return text.replace(regex,function(x){var y=demangle(x);return x===y?x:y+" ["+x+"]"})}function dynCallLegacy(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}return Module["dynCall_"+sig].call(null,ptr)}function jsStackTrace(){var error=new Error;if(!error.stack){try{throw new Error}catch(e){error=e}if(!error.stack){return"(no stack trace available)"}}return error.stack.toString()}var ExceptionInfoAttrs={DESTRUCTOR_OFFSET:0,REFCOUNT_OFFSET:4,TYPE_OFFSET:8,CAUGHT_OFFSET:12,RETHROWN_OFFSET:13,SIZE:16};function ___cxa_allocate_exception(size){return _malloc(size+ExceptionInfoAttrs.SIZE)+ExceptionInfoAttrs.SIZE}function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-ExceptionInfoAttrs.SIZE;this.set_type=function(type){HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]=type};this.get_type=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]};this.set_destructor=function(destructor){HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]=destructor};this.get_destructor=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]};this.set_refcount=function(refcount){HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=refcount};this.set_caught=function(caught){caught=caught?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]=caught};this.get_caught=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]=rethrown};this.get_rethrown=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]!=0};this.init=function(type,destructor){this.set_type(type);this.set_destructor(destructor);this.set_refcount(0);this.set_caught(false);this.set_rethrown(false)};this.add_ref=function(){var value=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=value+1};this.release_ref=function(){var prev=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=prev-1;return prev===1}}var exceptionLast=0;function __ZSt18uncaught_exceptionv(){return __ZSt18uncaught_exceptionv.uncaught_exceptions>0}function ___cxa_throw(ptr,type,destructor){var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;if(!("uncaught_exception"in __ZSt18uncaught_exceptionv)){__ZSt18uncaught_exceptionv.uncaught_exceptions=1}else{__ZSt18uncaught_exceptionv.uncaught_exceptions++}throw ptr}function _abort(){abort()}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ASSERTIONS=false;function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}function intArrayToString(array){var ret=[];for(var i=0;i255){if(ASSERTIONS){assert(false,"Character code "+chr+" ("+String.fromCharCode(chr)+") at offset "+i+" not in 0x00-0xFF.")}chr&=255}ret.push(String.fromCharCode(chr))}return ret.join("")}var decodeBase64=typeof atob==="function"?atob:function(input){var keyStr="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";var output="";var chr1,chr2,chr3;var enc1,enc2,enc3,enc4;var i=0;input=input.replace(/[^A-Za-z0-9\+\/\=]/g,"");do{enc1=keyStr.indexOf(input.charAt(i++));enc2=keyStr.indexOf(input.charAt(i++));enc3=keyStr.indexOf(input.charAt(i++));enc4=keyStr.indexOf(input.charAt(i++));chr1=enc1<<2|enc2>>4;chr2=(enc2&15)<<4|enc3>>2;chr3=(enc3&3)<<6|enc4;output=output+String.fromCharCode(chr1);if(enc3!==64){output=output+String.fromCharCode(chr2)}if(enc4!==64){output=output+String.fromCharCode(chr3)}}while(i0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run();function WrapperObject(){}WrapperObject.prototype=Object.create(WrapperObject.prototype);WrapperObject.prototype.constructor=WrapperObject;WrapperObject.prototype.__class__=WrapperObject;WrapperObject.__cache__={};Module["WrapperObject"]=WrapperObject;function getCache(__class__){return(__class__||WrapperObject).__cache__}Module["getCache"]=getCache;function wrapPointer(ptr,__class__){var cache=getCache(__class__);var ret=cache[ptr];if(ret)return ret;ret=Object.create((__class__||WrapperObject).prototype);ret.ptr=ptr;return cache[ptr]=ret}Module["wrapPointer"]=wrapPointer;function castObject(obj,__class__){return wrapPointer(obj.ptr,__class__)}Module["castObject"]=castObject;Module["NULL"]=wrapPointer(0);function destroy(obj){if(!obj["__destroy__"])throw"Error: Cannot destroy object. (Did you create it yourself?)";obj["__destroy__"]();delete getCache(obj.__class__)[obj.ptr]}Module["destroy"]=destroy;function compare(obj1,obj2){return obj1.ptr===obj2.ptr}Module["compare"]=compare;function getPointer(obj){return obj.ptr}Module["getPointer"]=getPointer;function getClass(obj){return obj.__class__}Module["getClass"]=getClass;var ensureCache={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(ensureCache.needed){for(var i=0;i=ensureCache.size){assert(len>0);ensureCache.needed+=len;ret=Module["_malloc"](len);ensureCache.temps.push(ret)}else{ret=ensureCache.buffer+ensureCache.pos;ensureCache.pos+=len}return ret},copy:function(array,view,offset){offset>>>=0;var bytes=view.BYTES_PER_ELEMENT;switch(bytes){case 2:offset>>>=1;break;case 4:offset>>>=2;break;case 8:offset>>>=3;break}for(var i=0;i>>0,$jscomp.propertyToPolyfillSymbol[h]=$jscomp.IS_SYMBOL_NATIVE? +$jscomp.global.Symbol(h):$jscomp.POLYFILL_PREFIX+l+"$"+h),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[h],{configurable:!0,writable:!0,value:n})))}; +$jscomp.polyfill("Promise",function(k){function n(){this.batch_=null}function l(f){return f instanceof h?f:new h(function(q,v){q(f)})}if(k&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&"undefined"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf("[native code]")))return k;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)}; +var p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q=A}},"es6","es3"); +$jscomp.polyfill("Array.prototype.copyWithin",function(k){function n(l){l=Number(l);return Infinity===l||-Infinity===l?l:l|0}return k?k:function(l,p,h){var A=this.length;l=n(l);p=n(p);h=void 0===h?A:n(h);l=0>l?Math.max(A+l,0):Math.min(l,A);p=0>p?Math.max(A+p,0):Math.min(p,A);h=0>h?Math.max(A+h,0):Math.min(h,A);if(lp;)--h in this?this[--l]=this[h]:delete this[--l];return this}},"es6","es3"); +$jscomp.typedArrayCopyWithin=function(k){return k?k:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5"); +$jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5"); +var DracoDecoderModule=function(){var k="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(k=k||__filename);return function(n){function l(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b,c){var d=b+c;for(c=b;e[c]&&!(c>=d);)++c;if(16g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}return d}function h(e,b){return e?p(ea,e,b):""}function A(e){va=e;a.HEAP8=Y=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ea=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=V=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function f(e){if(a.onAbort)a.onAbort(e); +e="Aborted("+e+")";da(e);wa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ja(e);throw e;}function q(e){try{if(e==P&&fa)return new Uint8Array(fa);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){f(b)}}function v(){if(!fa&&(xa||ha)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return q(P)}); +if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return q(P)})}function z(e){for(;0>2]=b};this.get_type=function(){return V[this.ptr+4>>2]};this.set_destructor=function(b){V[this.ptr+8>>2]=b};this.get_destructor=function(){return V[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){Y[this.ptr+ +12>>0]=b?1:0};this.get_caught=function(){return 0!=Y[this.ptr+12>>0]};this.set_rethrown=function(b){Y[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=Y[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){V[this.ptr+ +16>>2]=b};this.get_adjusted_ptr=function(){return V[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ya(this.get_type()))return V[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function ba(e){function b(){if(!ka&&(ka=!0,a.calledRun=!0,!wa)){za=!0;z(oa);Aa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)Ba.unshift(a.postRun.shift());z(Ba)}}if(!(0=d?b++:2047>=d?b+=2:55296<=d&&57343>= +d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0=u){var X=e.charCodeAt(++g);u=65536+((u&1023)<<10)|X&1023}if(127>=u){if(c>=d)break;b[c++]=u}else{if(2047>=u){if(c+1>=d)break;b[c++]=192|u>>6}else{if(65535>=u){if(c+2>=d)break;b[c++]=224|u>>12}else{if(c+3>=d)break;b[c++]=240|u>>18;b[c++]=128|u>>12&63}b[c++]=128|u>>6&63}b[c++]=128|u&63}}b[c]=0}e=r.alloc(b,Y);r.copy(b,Y,e);return e}return e}function pa(e){if("object"===typeof e){var b= +r.alloc(e,Y);r.copy(e,Y,b);return b}return e}function Z(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=Da();x(S)[this.ptr]=this}function Q(){this.ptr=Ea();x(Q)[this.ptr]=this}function W(){this.ptr=Fa();x(W)[this.ptr]=this}function w(){this.ptr=Ga();x(w)[this.ptr]=this}function C(){this.ptr=Ha();x(C)[this.ptr]=this}function F(){this.ptr=Ia();x(F)[this.ptr]=this}function G(){this.ptr=Ja();x(G)[this.ptr]=this}function E(){this.ptr=Ka();x(E)[this.ptr]=this}function T(){this.ptr= +La();x(T)[this.ptr]=this}function B(){throw"cannot construct a Status, no constructor in IDL";}function H(){this.ptr=Ma();x(H)[this.ptr]=this}function I(){this.ptr=Na();x(I)[this.ptr]=this}function J(){this.ptr=Oa();x(J)[this.ptr]=this}function K(){this.ptr=Pa();x(K)[this.ptr]=this}function L(){this.ptr=Qa();x(L)[this.ptr]=this}function M(){this.ptr=Ra();x(M)[this.ptr]=this}function N(){this.ptr=Sa();x(N)[this.ptr]=this}function y(){this.ptr=Ta();x(y)[this.ptr]=this}function m(){this.ptr=Ua();x(m)[this.ptr]= +this}n=n||{};var a="undefined"!=typeof n?n:{},Aa,ja;a.ready=new Promise(function(e,b){Aa=e;ja=b});var Va=!1,Wa=!1;a.onRuntimeInitialized=function(){Va=!0;if(Wa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Wa=!0;if(Va&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3=e[1]?!0:0!=e[0]||10>>=0;if(2147483648=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{try{la.grow(g-va.byteLength+65535>>>16);A(la.buffer);var u=1;break a}catch(X){}u=void 0}if(u)return!0}return!1},g:function(e){return 52},e:function(e,b,c,d,g){return 70},d:function(e,b,c,d){for(var g=0,u=0;u>2],cb=V[b+4>>2];b+=8;for(var ra=0;ra>2]=g;return 0}};(function(){function e(g,u){a.asm=g.exports;la=a.asm.i;A(la.buffer);oa.unshift(a.asm.j);aa--;a.monitorRunDependencies&&a.monitorRunDependencies(aa);0==aa&&(null!==qa&&(clearInterval(qa),qa=null),ia&&(g=ia,ia=null,g()))}function b(g){e(g.instance)}function c(g){return v().then(function(u){return WebAssembly.instantiate(u,d)}).then(function(u){return u}).then(g,function(u){da("failed to asynchronously prepare wasm: "+u);f(u)})}var d={a:zd};aa++;a.monitorRunDependencies&&a.monitorRunDependencies(aa); +if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ja(g)}(function(){return fa||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")||P.startsWith("file://")||Ya||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(u){da("wasm streaming compile failed: "+u);da("falling back to ArrayBuffer instantiation"); +return c(b)})})})().catch(ja);return{}})();a.___wasm_call_ctors=function(){return(a.___wasm_call_ctors=a.asm.j).apply(null,arguments)};var db=a._emscripten_bind_VoidPtr___destroy___0=function(){return(db=a._emscripten_bind_VoidPtr___destroy___0=a.asm.l).apply(null,arguments)},Da=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=function(){return(Da=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.m).apply(null,arguments)},eb=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(eb=a._emscripten_bind_DecoderBuffer_Init_2= +a.asm.n).apply(null,arguments)},fb=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return(fb=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.o).apply(null,arguments)},Ea=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Ea=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=a.asm.p).apply(null,arguments)},gb=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return(gb=a._emscripten_bind_AttributeTransformData_transform_type_0= +a.asm.q).apply(null,arguments)},hb=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return(hb=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.r).apply(null,arguments)},Fa=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Fa=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=a.asm.s).apply(null,arguments)},ib=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(ib=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.t).apply(null, +arguments)},Ga=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ga=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.u).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_size_0=function(){return(jb=a._emscripten_bind_PointAttribute_size_0=a.asm.v).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=function(){return(kb=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.w).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute_attribute_type_0= +function(){return(lb=a._emscripten_bind_PointAttribute_attribute_type_0=a.asm.x).apply(null,arguments)},mb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(mb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.y).apply(null,arguments)},nb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(nb=a._emscripten_bind_PointAttribute_num_components_0=a.asm.z).apply(null,arguments)},ob=a._emscripten_bind_PointAttribute_normalized_0=function(){return(ob=a._emscripten_bind_PointAttribute_normalized_0= +a.asm.A).apply(null,arguments)},pb=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return(pb=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.B).apply(null,arguments)},qb=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(qb=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.C).apply(null,arguments)},rb=a._emscripten_bind_PointAttribute_unique_id_0=function(){return(rb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.D).apply(null,arguments)},sb=a._emscripten_bind_PointAttribute___destroy___0= +function(){return(sb=a._emscripten_bind_PointAttribute___destroy___0=a.asm.E).apply(null,arguments)},Ha=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Ha=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.F).apply(null,arguments)},tb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=function(){return(tb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.G).apply(null, +arguments)},ub=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return(ub=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.H).apply(null,arguments)},vb=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(vb=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.I).apply(null,arguments)},wb=a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(wb=a._emscripten_bind_AttributeQuantizationTransform_range_0= +a.asm.J).apply(null,arguments)},xb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return(xb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.K).apply(null,arguments)},Ia=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ia=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=a.asm.L).apply(null,arguments)},yb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1= +function(){return(yb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.M).apply(null,arguments)},zb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(zb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.N).apply(null,arguments)},Ab=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(Ab=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.O).apply(null,arguments)},Ja=a._emscripten_bind_PointCloud_PointCloud_0= +function(){return(Ja=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.P).apply(null,arguments)},Bb=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(Bb=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.Q).apply(null,arguments)},Cb=a._emscripten_bind_PointCloud_num_points_0=function(){return(Cb=a._emscripten_bind_PointCloud_num_points_0=a.asm.R).apply(null,arguments)},Db=a._emscripten_bind_PointCloud___destroy___0=function(){return(Db=a._emscripten_bind_PointCloud___destroy___0=a.asm.S).apply(null, +arguments)},Ka=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ka=a._emscripten_bind_Mesh_Mesh_0=a.asm.T).apply(null,arguments)},Eb=a._emscripten_bind_Mesh_num_faces_0=function(){return(Eb=a._emscripten_bind_Mesh_num_faces_0=a.asm.U).apply(null,arguments)},Fb=a._emscripten_bind_Mesh_num_attributes_0=function(){return(Fb=a._emscripten_bind_Mesh_num_attributes_0=a.asm.V).apply(null,arguments)},Gb=a._emscripten_bind_Mesh_num_points_0=function(){return(Gb=a._emscripten_bind_Mesh_num_points_0=a.asm.W).apply(null, +arguments)},Hb=a._emscripten_bind_Mesh___destroy___0=function(){return(Hb=a._emscripten_bind_Mesh___destroy___0=a.asm.X).apply(null,arguments)},La=a._emscripten_bind_Metadata_Metadata_0=function(){return(La=a._emscripten_bind_Metadata_Metadata_0=a.asm.Y).apply(null,arguments)},Ib=a._emscripten_bind_Metadata___destroy___0=function(){return(Ib=a._emscripten_bind_Metadata___destroy___0=a.asm.Z).apply(null,arguments)},Jb=a._emscripten_bind_Status_code_0=function(){return(Jb=a._emscripten_bind_Status_code_0= +a.asm._).apply(null,arguments)},Kb=a._emscripten_bind_Status_ok_0=function(){return(Kb=a._emscripten_bind_Status_ok_0=a.asm.$).apply(null,arguments)},Lb=a._emscripten_bind_Status_error_msg_0=function(){return(Lb=a._emscripten_bind_Status_error_msg_0=a.asm.aa).apply(null,arguments)},Mb=a._emscripten_bind_Status___destroy___0=function(){return(Mb=a._emscripten_bind_Status___destroy___0=a.asm.ba).apply(null,arguments)},Ma=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ma= +a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm.ca).apply(null,arguments)},Nb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Nb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.da).apply(null,arguments)},Ob=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Ob=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.ea).apply(null,arguments)},Pb=a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Pb=a._emscripten_bind_DracoFloat32Array___destroy___0= +a.asm.fa).apply(null,arguments)},Na=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return(Na=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.ga).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Qb=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.ha).apply(null,arguments)},Rb=a._emscripten_bind_DracoInt8Array_size_0=function(){return(Rb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ia).apply(null,arguments)},Sb=a._emscripten_bind_DracoInt8Array___destroy___0= +function(){return(Sb=a._emscripten_bind_DracoInt8Array___destroy___0=a.asm.ja).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ka).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Tb=a._emscripten_bind_DracoUInt8Array_GetValue_1=a.asm.la).apply(null,arguments)},Ub=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Ub=a._emscripten_bind_DracoUInt8Array_size_0= +a.asm.ma).apply(null,arguments)},Vb=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return(Vb=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.na).apply(null,arguments)},Pa=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(Pa=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.oa).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt16Array_GetValue_1=function(){return(Wb=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.pa).apply(null,arguments)},Xb= +a._emscripten_bind_DracoInt16Array_size_0=function(){return(Xb=a._emscripten_bind_DracoInt16Array_size_0=a.asm.qa).apply(null,arguments)},Yb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Yb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.ra).apply(null,arguments)},Qa=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Qa=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=a.asm.sa).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt16Array_GetValue_1= +function(){return(Zb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.ta).apply(null,arguments)},$b=a._emscripten_bind_DracoUInt16Array_size_0=function(){return($b=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.ua).apply(null,arguments)},ac=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(ac=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.va).apply(null,arguments)},Ra=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=function(){return(Ra=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0= +a.asm.wa).apply(null,arguments)},bc=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return(bc=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.xa).apply(null,arguments)},cc=a._emscripten_bind_DracoInt32Array_size_0=function(){return(cc=a._emscripten_bind_DracoInt32Array_size_0=a.asm.ya).apply(null,arguments)},dc=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(dc=a._emscripten_bind_DracoInt32Array___destroy___0=a.asm.za).apply(null,arguments)},Sa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0= +function(){return(Sa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=a.asm.Aa).apply(null,arguments)},ec=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(ec=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.Ba).apply(null,arguments)},fc=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(fc=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.Ca).apply(null,arguments)},gc=a._emscripten_bind_DracoUInt32Array___destroy___0=function(){return(gc=a._emscripten_bind_DracoUInt32Array___destroy___0= +a.asm.Da).apply(null,arguments)},Ta=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return(Ta=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Ea).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(hc=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Fa).apply(null,arguments)},ic=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(ic=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=a.asm.Ga).apply(null,arguments)},jc= +a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(jc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=a.asm.Ha).apply(null,arguments)},kc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(kc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ia).apply(null,arguments)},lc=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(lc=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Ja).apply(null,arguments)},mc=a._emscripten_bind_MetadataQuerier_NumEntries_1= +function(){return(mc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ka).apply(null,arguments)},nc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(nc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.La).apply(null,arguments)},oc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(oc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.Ma).apply(null,arguments)},Ua=a._emscripten_bind_Decoder_Decoder_0=function(){return(Ua=a._emscripten_bind_Decoder_Decoder_0= +a.asm.Na).apply(null,arguments)},pc=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(pc=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Oa).apply(null,arguments)},qc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(qc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.Pa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(rc=a._emscripten_bind_Decoder_GetAttributeId_2=a.asm.Qa).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetAttributeIdByName_2= +function(){return(sc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=a.asm.Ra).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(tc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Sa).apply(null,arguments)},uc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(uc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Ta).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2= +a.asm.Ua).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(wc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Va).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(xc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Wa).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=function(){return(yc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Xa).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2= +function(){return(zc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Ya).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(Ac=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Za).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(Bc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm._a).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(Cc= +a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm.$a).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(Dc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.ab).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=a.asm.bb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(Fc= +a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm.cb).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(Gc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.db).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Hc=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=a.asm.eb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3= +function(){return(Ic=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.fb).apply(null,arguments)},Jc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Jc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.gb).apply(null,arguments)},Kc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Kc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=a.asm.hb).apply(null,arguments)},Lc=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5= +function(){return(Lc=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.ib).apply(null,arguments)},Mc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Mc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.jb).apply(null,arguments)},Nc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Nc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=a.asm.kb).apply(null,arguments)},Oc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2= +function(){return(Oc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.lb).apply(null,arguments)},Pc=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Pc=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.mb).apply(null,arguments)},Qc=a._emscripten_bind_Decoder___destroy___0=function(){return(Qc=a._emscripten_bind_Decoder___destroy___0=a.asm.nb).apply(null,arguments)},Rc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=function(){return(Rc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM= +a.asm.ob).apply(null,arguments)},Sc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Sc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.pb).apply(null,arguments)},Tc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Tc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=a.asm.qb).apply(null,arguments)},Uc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM= +function(){return(Uc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.rb).apply(null,arguments)},Vc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Vc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.sb).apply(null,arguments)},Wc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Wc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=a.asm.tb).apply(null,arguments)},Xc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL= +function(){return(Xc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.ub).apply(null,arguments)},Yc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Yc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.vb).apply(null,arguments)},Zc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Zc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=a.asm.wb).apply(null,arguments)},$c=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC= +function(){return($c=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.xb).apply(null,arguments)},ad=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(ad=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.yb).apply(null,arguments)},bd=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(bd=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=a.asm.zb).apply(null,arguments)},cd=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH= +function(){return(cd=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.Ab).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(dd=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.Bb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(ed=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.Cb).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(fd=a._emscripten_enum_draco_DataType_DT_UINT8= +a.asm.Db).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_INT16=function(){return(gd=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Eb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(hd=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Fb).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(id=a._emscripten_enum_draco_DataType_DT_INT32=a.asm.Gb).apply(null,arguments)},jd=a._emscripten_enum_draco_DataType_DT_UINT32= +function(){return(jd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Hb).apply(null,arguments)},kd=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(kd=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Ib).apply(null,arguments)},ld=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(ld=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Jb).apply(null,arguments)},md=a._emscripten_enum_draco_DataType_DT_FLOAT32=function(){return(md=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Kb).apply(null, +arguments)},nd=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(nd=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Lb).apply(null,arguments)},od=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(od=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Mb).apply(null,arguments)},pd=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(pd=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=a.asm.Nb).apply(null,arguments)},qd=a._emscripten_enum_draco_StatusCode_OK=function(){return(qd= +a._emscripten_enum_draco_StatusCode_OK=a.asm.Ob).apply(null,arguments)},rd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(rd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Pb).apply(null,arguments)},sd=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(sd=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Qb).apply(null,arguments)},td=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=function(){return(td=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER= +a.asm.Rb).apply(null,arguments)},ud=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(ud=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Sb).apply(null,arguments)},vd=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(vd=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Tb).apply(null,arguments)},bb=a._malloc=function(){return(bb=a._malloc=a.asm.Ub).apply(null,arguments)};a._free=function(){return(a._free=a.asm.Vb).apply(null,arguments)}; +var ya=a.___cxa_is_pointer_type=function(){return(ya=a.___cxa_is_pointer_type=a.asm.Wb).apply(null,arguments)};a.___start_em_js=15856;a.___stop_em_js=15954;var ka;ia=function b(){ka||ba();ka||(ia=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0=r.size?(0>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;gb.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD; +case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule); diff --git a/modules/draco/src/workers/draco-writer-worker-node.ts b/modules/draco/src/workers/draco-writer-worker-node.ts index f8ef3f156f..34f49ee088 100644 --- a/modules/draco/src/workers/draco-writer-worker-node.ts +++ b/modules/draco/src/workers/draco-writer-worker-node.ts @@ -3,9 +3,9 @@ import '@loaders.gl/polyfills'; import {WorkerBody, WorkerMessagePayload} from '@loaders.gl/worker-utils'; import {DracoWriter} from '../draco-writer'; -(() => { +(async () => { // Check that we are actually in a worker thread - if (!WorkerBody.inWorkerThread()) { + if (!(await WorkerBody.inWorkerThread())) { return; } diff --git a/modules/draco/src/workers/draco-writer-worker.ts b/modules/draco/src/workers/draco-writer-worker.ts index d318e37f29..19dbe99243 100644 --- a/modules/draco/src/workers/draco-writer-worker.ts +++ b/modules/draco/src/workers/draco-writer-worker.ts @@ -1,9 +1,9 @@ import {WorkerBody, WorkerMessagePayload} from '@loaders.gl/worker-utils'; import {DracoWriter} from '../draco-writer'; -(() => { +(async () => { // Check that we are actually in a worker thread - if (!WorkerBody.inWorkerThread()) { + if (!(await WorkerBody.inWorkerThread())) { return; } diff --git a/modules/draco/test/draco-writer.spec.ts b/modules/draco/test/draco-writer.spec.ts index d1e46e6a4a..bbf3d82d4d 100644 --- a/modules/draco/test/draco-writer.spec.ts +++ b/modules/draco/test/draco-writer.spec.ts @@ -96,6 +96,7 @@ test('DracoWriter#encode(bunny.drc)', async (t) => { /** * Cannot import draco_encoder module: * Refused to execute script from 'https://raw.githubusercontent.com/google/draco/1.4.1/javascript/draco_encoder.js' because its MIME type ('') is not executable. + * [Error: Failed to execute 'importScripts' on 'WorkerGlobalScope': The script at 'https://raw.githubusercontent.com/google/draco/1.4.1/javascript/draco_encoder.js' failed to load. */ test.skip('DracoWriter#Worker$encode(bunny.drc)', async (t) => { if (!isBrowser) { diff --git a/modules/draco/test/index.ts b/modules/draco/test/index.ts index cfa9ba4651..720e29a297 100644 --- a/modules/draco/test/index.ts +++ b/modules/draco/test/index.ts @@ -1,6 +1,8 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import './lib/utils/get-draco-schema.spec'; + import './draco-loader.spec'; import './draco-writer.spec'; import './draco-compression-ratio.spec'; diff --git a/modules/draco/tsconfig.json b/modules/draco/tsconfig.json index fd8ad4e7bd..680aa8a0ef 100644 --- a/modules/draco/tsconfig.json +++ b/modules/draco/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.module.json", "include": ["src/**/*"], - "exclude": ["node_modules"], + "exclude": ["node_modules", "src/libs"], "compilerOptions": { "composite": true, "rootDir": "src", diff --git a/modules/excel/README.md b/modules/excel/README.md index a96feb0119..2d03ab5979 100644 --- a/modules/excel/README.md +++ b/modules/excel/README.md @@ -1,4 +1,4 @@ -# @loaders.gl/json +# @loaders.gl/excel This module contains a table loader for the JSON and line delimited JSON formats. diff --git a/modules/excel/package.json b/modules/excel/package.json index 93c05fe042..59176997ee 100644 --- a/modules/excel/package.json +++ b/modules/excel/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/excel", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loader for Excel files", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -22,8 +23,15 @@ "Spreadsheets" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -31,14 +39,14 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle && npm run build-worker", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-worker", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/excel-worker.ts --bundle --outfile=dist/excel-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", - "xlsx": "^0.17.0" + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "xlsx": "^0.18.5" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/excel/src/bundle.ts b/modules/excel/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/excel/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/excel/src/excel-loader.ts b/modules/excel/src/excel-loader.ts index 6d416676f9..aeba397116 100644 --- a/modules/excel/src/excel-loader.ts +++ b/modules/excel/src/excel-loader.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Loader, LoaderOptions} from '@loaders.gl/loader-utils'; import type {ObjectRowTable} from '@loaders.gl/schema'; @@ -39,5 +40,3 @@ export const ExcelLoader: Loader = { binary: true, options: DEFAULT_EXCEL_LOADER_OPTIONS }; - -export const _typecheckLoader: Loader = ExcelLoader; diff --git a/modules/excel/src/index.ts b/modules/excel/src/index.ts index c8fe864f37..56a53b5140 100644 --- a/modules/excel/src/index.ts +++ b/modules/excel/src/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {LoaderWithParser} from '@loaders.gl/loader-utils'; import type {ObjectRowTable} from '@loaders.gl/schema'; diff --git a/modules/excel/test/excel-loader.spec.ts b/modules/excel/test/excel-loader.spec.ts index 868ab27442..644d340eab 100644 --- a/modules/excel/test/excel-loader.spec.ts +++ b/modules/excel/test/excel-loader.spec.ts @@ -1,5 +1,6 @@ import test from 'tape-promise/tape'; import {load, loadInBatches} from '@loaders.gl/core'; +import type {ObjectRowTable, ObjectRowTableBatch} from '@loaders.gl/schema'; import {ExcelLoader} from '@loaders.gl/excel'; import {CSVLoader} from '@loaders.gl/csv'; @@ -8,29 +9,32 @@ const ZIPCODES_XLSB_PATH = '@loaders.gl/excel/test/data/zipcodes.xlsb'; const ZIPCODES_CSV_PATH = '@loaders.gl/excel/test/data/zipcodes.csv'; test('ExcelLoader#load(ZIPCODES)', async (t) => { - const csvData = await load(ZIPCODES_CSV_PATH, CSVLoader, { + const csvTable = (await load(ZIPCODES_CSV_PATH, CSVLoader, { csv: {shape: 'object-row-table'} - }); - // Property 'length' does not exist on type 'ArrayRowTable | ObjectRowTable | GeoJSONRowTable | ColumnarTable | ArrowTable'. - // Property 'length' does not exist on type 'ArrayRowTable'.ts(2339) - // t.equal(csvData.length, 42049, 'CSV (reference): Correct number of row received'); + })) as ObjectRowTable; let table = await load(ZIPCODES_XLSB_PATH, ExcelLoader); t.equal(table.data.length, 42049, 'XLSB: Correct number of row received'); - t.deepEqual(table.data[0], csvData[0], 'XLSB: Data corresponds to CSV'); + t.deepEqual(table.data[0], csvTable.data[0], 'XLSB: Data corresponds to CSV'); table = await load(ZIPCODES_XLSX_PATH, ExcelLoader); t.equal(table.data.length, 42049, 'XLSX: Correct number of row received'); - t.deepEqual(table.data[100], csvData[100], 'XLSX: Data corresponds to CSV'); + t.deepEqual(table.data[100], csvTable.data[100], 'XLSX: Data corresponds to CSV'); t.end(); }); test('ExcelLoader#loadInBatches (on worker)', async (t) => { - // Workers are not supported under Node at the moment. - const batches = await loadInBatches(ZIPCODES_XLSX_PATH, ExcelLoader); + // This masquerades an atomic loader as batches + const batches = (await loadInBatches( + ZIPCODES_XLSX_PATH, + ExcelLoader + )) as unknown as AsyncIterable; + let firstBatch: ObjectRowTableBatch | null = null; for await (const batch of batches) { - t.equal(batch.data.data.length, 42049, 'XLSX: Correct number of row received'); + firstBatch = firstBatch || batch; } + t.equal(firstBatch?.shape, 'object-row-table', 'XLSX: correct batch type received'); + t.equal(firstBatch?.data.length, 42049, 'XLSX: Correct batch row count received'); t.end(); }); diff --git a/modules/flatgeobuf/package.json b/modules/flatgeobuf/package.json index 56dd05cf36..c3ba6003c1 100644 --- a/modules/flatgeobuf/package.json +++ b/modules/flatgeobuf/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/flatgeobuf", "description": "Loader for FlatGeobuf", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -19,8 +20,15 @@ "Mapbox Vector Tiles" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -28,15 +36,15 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-worker && npm run build-worker --env.dev && npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", - "build-worker": "esbuild src/workers/flatgeobuf-worker.ts --bundle --outfile=dist/flatgeobuf-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" + "pre-build": "npm run build-worker && npm run build-worker --env.dev && npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "# ocular-bundle ./src/index.ts", + "build-worker": "# esbuild src/workers/flatgeobuf-worker.ts --bundle --outfile=dist/flatgeobuf-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { - "@loaders.gl/gis": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@math.gl/proj4": "^3.3.1", - "flatgeobuf": "3.6.5" + "@loaders.gl/gis": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@math.gl/proj4": "^4.0.0", + "flatgeobuf": "3.27.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/flatgeobuf/src/bundle.ts b/modules/flatgeobuf/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/flatgeobuf/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/flatgeobuf/src/flatgeobuf-loader.ts b/modules/flatgeobuf/src/flatgeobuf-loader.ts index f72bff5366..f436797c93 100644 --- a/modules/flatgeobuf/src/flatgeobuf-loader.ts +++ b/modules/flatgeobuf/src/flatgeobuf-loader.ts @@ -1,10 +1,20 @@ -import type {Loader} from '@loaders.gl/loader-utils'; +import type {Loader, LoaderOptions} from '@loaders.gl/loader-utils'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; -export const FlatGeobufLoader = { +export type FlatGeobufLoaderOptions = LoaderOptions & { + flatgeobuf?: { + shape?: 'geojson-table' | 'columnar-table' | 'binary'; + }; + gis?: { + reproject?: boolean; + _targetCrs?: string; + }; +}; + +export const FlatGeobufLoader: Loader = { id: 'flatgeobuf', name: 'FlatGeobuf', module: 'flatgeobuf', @@ -15,10 +25,10 @@ export const FlatGeobufLoader = { category: 'geometry', options: { flatgeobuf: { - // Set to GeoJSON for backwards compatibility - shape: 'geojson' + shape: 'geojson-table' + }, + gis: { + reproject: false } } }; - -export const _typecheckFlatGeobufLoader: Loader = FlatGeobufLoader; diff --git a/modules/flatgeobuf/src/index.ts b/modules/flatgeobuf/src/index.ts index efad01eaf1..5b60bc00a5 100644 --- a/modules/flatgeobuf/src/index.ts +++ b/modules/flatgeobuf/src/index.ts @@ -1,15 +1,15 @@ import type {LoaderWithParser} from '@loaders.gl/loader-utils'; +import type {FlatGeobufLoaderOptions} from './flatgeobuf-loader'; import {FlatGeobufLoader as FlatGeobufWorkerLoader} from './flatgeobuf-loader'; import {parseFlatGeobuf, parseFlatGeobufInBatches} from './lib/parse-flatgeobuf'; export {FlatGeobufWorkerLoader}; -export const FlatGeobufLoader = { +export const FlatGeobufLoader: LoaderWithParser = { ...FlatGeobufWorkerLoader, parse: async (arrayBuffer, options) => parseFlatGeobuf(arrayBuffer, options), parseSync: parseFlatGeobuf, + // @ts-expect-error this is a stream parser not an async iterator parser parseInBatchesFromStream: parseFlatGeobufInBatches, binary: true }; - -export const _typecheckFlatGeobufLoader: LoaderWithParser = FlatGeobufLoader; diff --git a/modules/flatgeobuf/src/lib/binary-geometries.ts b/modules/flatgeobuf/src/lib/binary-geometries.ts index afd9d75442..44ef3311d2 100644 --- a/modules/flatgeobuf/src/lib/binary-geometries.ts +++ b/modules/flatgeobuf/src/lib/binary-geometries.ts @@ -1,21 +1,73 @@ -import {GeometryType} from 'flatgeobuf/lib/cjs/header_generated'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {Geometry as FGBGeometry, Feature as FGBFeature} from 'flatgeobuf'; +// import {GeometryType} from 'flatgeobuf/generic'; +// Copy geometry type as it is hard to access the export +export declare enum GeometryType { + Unknown = 0, + Point = 1, + LineString = 2, + Polygon = 3, + MultiPoint = 4, + MultiLineString = 5, + MultiPolygon = 6, + GeometryCollection = 7, + CircularString = 8, + CompoundCurve = 9, + CurvePolygon = 10, + MultiCurve = 11, + MultiSurface = 12, + Curve = 13, + Surface = 14, + PolyhedralSurface = 15, + TIN = 16, + Triangle = 17 +} + +export function fgbToBinaryFeature(geometry: FGBFeature | null, type: GeometryType) { + const fgbGeometry: FGBGeometry | null = geometry?.geometry() || null; + return fgbToBinaryGeometry(fgbGeometry, type); +} + +export function fgbToBinaryGeometry(geometry: FGBGeometry | null, type: GeometryType) { + if (geometry === null) { + return null; + } + switch (type) { + case GeometryType.Point: + case GeometryType.MultiPoint: + return parsePoint(geometry); + case GeometryType.LineString: + case GeometryType.MultiLineString: + return parseLines(geometry); + case GeometryType.Polygon: + return parsePolygons(geometry); + case GeometryType.MultiPolygon: + return parseMultiPolygons(geometry); + default: + throw new Error(`Unimplemented geometry type: ${type}`); + } +} // Parse Point to flat array -function parsePoint(geometry) { +function parsePoint(geometry: FGBGeometry) { const xy = geometry.xyArray(); const z = geometry.zArray(); + // @ts-expect-error TODO handle null geometries const positions = blitArrays(xy, z); return {positions}; } -function parseLines(geometry) { +function parseLines(geometry: FGBGeometry) { const xy = geometry.xyArray(); const z = geometry.zArray(); - const positions = blitArrays(xy, z); + const positions = blitArrays(xy!, z!); // If endsArray is null, a single LineString. Otherwise, contains the end // indices of each part of the MultiLineString. geometry.endsArray() omits the // initial 0 that we have in our internal format. + // @ts-expect-error TODO handle null geometries const ends = (geometry.endsArray() && Array.from(geometry.endsArray())) || [xy.length / 2]; ends.unshift(0); @@ -27,19 +79,21 @@ function parseLines(geometry) { }; } -function parsePolygons(geometry) { +function parsePolygons(geometry: FGBGeometry) { const xy = geometry.xyArray(); const z = geometry.zArray(); + // @ts-expect-error TODO handle null geometries const positions = blitArrays(xy, z); // If endsArray is null, a simple Polygon with no inner rings. Otherwise, // contains the end indices of each ring of the Polygon. geometry.endsArray() // omits the initial 0 that we have in our internal format. + // @ts-expect-error TODO handle null geometries const ends = (geometry.endsArray() && Array.from(geometry.endsArray())) || [xy.length / 2]; ends.unshift(0); const primitivePolygonIndices = {value: new Uint16Array(ends), size: 1}; - const polygonIndices = {value: new Uint16Array([0, xy.length / 2]), size: 1}; + const polygonIndices = {value: new Uint16Array([0, xy!.length / 2]), size: 1}; return { positions, @@ -49,7 +103,7 @@ function parsePolygons(geometry) { } // eslint-disable-next-line max-statements -function parseMultiPolygons(geometry) { +function parseMultiPolygons(geometry: FGBGeometry) { // Create arrays for each geometry part, then concatenate const parsedParts: any[] = []; let nPositions = 0; @@ -58,6 +112,7 @@ function parseMultiPolygons(geometry) { for (let i = 0; i < geometry.partsLength(); i++) { const part = geometry.parts(i); + // @ts-expect-error TODO handle null geometries const polygon = parsePolygons(part); nPositions += polygon.positions.value.length; @@ -107,7 +162,7 @@ function parseMultiPolygons(geometry) { } // Combine xy and z arrays -function blitArrays(xy, z) { +function blitArrays(xy: Float64Array, z: Float64Array): {value: Float64Array; size: number} { if (!z) { return {value: xy, size: 2}; } @@ -125,20 +180,3 @@ function blitArrays(xy, z) { } return {value: xyz, size: 3}; } - -export function fromGeometry(geometry, type) { - switch (type) { - case GeometryType.Point: - case GeometryType.MultiPoint: - return parsePoint(geometry); - case GeometryType.LineString: - case GeometryType.MultiLineString: - return parseLines(geometry); - case GeometryType.Polygon: - return parsePolygons(geometry); - case GeometryType.MultiPolygon: - return parseMultiPolygons(geometry); - default: - throw new Error(`Unimplemented geometry type: ${type}`); - } -} diff --git a/modules/flatgeobuf/src/lib/parse-flatgeobuf.ts b/modules/flatgeobuf/src/lib/parse-flatgeobuf.ts index aa95bd1255..cc07674523 100644 --- a/modules/flatgeobuf/src/lib/parse-flatgeobuf.ts +++ b/modules/flatgeobuf/src/lib/parse-flatgeobuf.ts @@ -1,25 +1,32 @@ -// @ts-nocheck +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import {Proj4Projection} from '@math.gl/proj4'; import {transformGeoJsonCoords} from '@loaders.gl/gis'; -import {deserialize as deserializeGeoJson} from 'flatgeobuf/lib/cjs/geojson'; -import {deserialize as deserializeGeneric} from 'flatgeobuf/lib/cjs/generic'; -import {parseProperties as parsePropertiesBinary} from 'flatgeobuf/lib/cjs/generic/feature'; +import type {FlatGeobufLoaderOptions} from '../flatgeobuf-loader'; +import type {GeoJSONTable, Feature, Table} from '@loaders.gl/schema'; +import {fgbToBinaryGeometry} from './binary-geometries'; -import {fromGeometry as binaryFromGeometry} from './binary-geometries'; -import {FlatGeobufLoaderOptions} from './types'; -import {GeoJSONRowTable, Feature} from '@loaders.gl/schema'; +import {Feature as FBGFeature, HeaderMeta as FGBHeader} from 'flatgeobuf'; +import * as geojson from 'flatgeobuf/lib/mjs/geojson.js'; +import * as generic from 'flatgeobuf/lib/mjs/generic.js'; +import {parseProperties as parsePropertiesBinary} from 'flatgeobuf/lib/mjs/generic/feature'; +const deserializeGeoJson = geojson.deserialize; +const deserializeGeneric = generic.deserialize; +// const parsePropertiesBinary = FlatgeobufFeature.parseProperties; // TODO: reproject binary features -function binaryFromFeature(feature, header) { +function binaryFromFeature(feature: FBGFeature, header: FGBHeader) { const geometry = feature.geometry(); // FlatGeobuf files can only hold a single geometry type per file, otherwise // GeometryType is GeometryCollection // I believe geometry.type() is null (0) except when the geometry type isn't // known in the header? - const geometryType = header.geometryType || geometry.type(); - const parsedGeometry = binaryFromGeometry(geometry, geometryType); + const geometryType = header.geometryType || geometry?.type(); + const parsedGeometry = fgbToBinaryGeometry(geometry, geometryType!); + // @ts-expect-error this looks wrong parsedGeometry.properties = parsePropertiesBinary(feature, header.columns); // TODO: wrap binary data either in points, lines, or polygons key @@ -32,39 +39,49 @@ function binaryFromFeature(feature, header) { * @param arrayBuffer A FlatGeobuf arrayBuffer * @return A GeoJSON geometry object */ -export function parseFlatGeobuf(arrayBuffer: ArrayBuffer, options?: FlatGeobufLoaderOptions) { - const shape = options?.gis?.format || options?.flatgeobuf?.shape; +export function parseFlatGeobuf( + arrayBuffer: ArrayBuffer, + options?: FlatGeobufLoaderOptions +): Table { + const shape = options?.flatgeobuf?.shape; switch (shape) { - case 'geojson-row-table': { - const table: GeoJSONRowTable = { - shape: 'geojson-row-table', - data: parseFlatGeobufToGeoJSON(arrayBuffer, options) + case 'geojson-table': { + const features = parseFlatGeobufToGeoJSON(arrayBuffer, options); + const table: GeoJSONTable = { + shape: 'geojson-table', + type: 'FeatureCollection', + features }; return table; } + case 'columnar-table': // binary + some JS arrays - return {shape: 'columnar-table', data: parseFlatGeobufToBinary(arrayBuffer, options)}; - case 'geojson': - return parseFlatGeobufToGeoJSON(arrayBuffer, options); + const binary = parseFlatGeobufToBinary(arrayBuffer, options); + // @ts-expect-error + return {shape: 'columnar-table', data: binary}; + case 'binary': + // @ts-expect-error return parseFlatGeobufToBinary(arrayBuffer, options); + default: throw new Error(shape); } } -function parseFlatGeobufToBinary(arrayBuffer: ArrayBuffer, options: FlatGeobufLoaderOptions) { +function parseFlatGeobufToBinary(arrayBuffer: ArrayBuffer, options: FlatGeobufLoaderOptions = {}) { // TODO: reproject binary features // const {reproject = false, _targetCrs = 'WGS84'} = (options && options.gis) || {}; const array = new Uint8Array(arrayBuffer); - return deserializeGeneric(array, binaryFromFeature); + // @ts-expect-error + return deserializeGeneric(array, fgbToBinaryGeometry); } function parseFlatGeobufToGeoJSON( arrayBuffer: ArrayBuffer, - options: FlatGeobufLoaderOptions + options: FlatGeobufLoaderOptions = {} ): Feature[] { if (arrayBuffer.byteLength === 0) { return []; @@ -75,7 +92,8 @@ function parseFlatGeobufToGeoJSON( const arr = new Uint8Array(arrayBuffer); let headerMeta; - const {features} = deserializeGeoJson(arr, false, (header) => { + // @ts-expect-error this looks wrong + const {features} = deserializeGeoJson(arr, undefined, (header) => { headerMeta = header; }); @@ -105,17 +123,22 @@ function parseFlatGeobufToGeoJSON( */ // eslint-disable-next-line complexity export function parseFlatGeobufInBatches(stream, options: FlatGeobufLoaderOptions) { - if (options && options.gis && options.gis.format === 'binary') { - return parseFlatGeobufInBatchesToBinary(stream, options); + const shape = options.flatgeobuf?.shape; + switch (shape) { + case 'binary': + return parseFlatGeobufInBatchesToBinary(stream, options); + case 'geojson-table': + return parseFlatGeobufInBatchesToGeoJSON(stream, options); + default: + throw new Error(shape); } - - return parseFlatGeobufInBatchesToGeoJSON(stream, options); } function parseFlatGeobufInBatchesToBinary(stream, options: FlatGeobufLoaderOptions) { // TODO: reproject binary streaming features // const {reproject = false, _targetCrs = 'WGS84'} = (options && options.gis) || {}; + // @ts-expect-error const iterator = deserializeGeneric(stream, binaryFromFeature); return iterator; } @@ -125,12 +148,13 @@ async function* parseFlatGeobufInBatchesToGeoJSON(stream, options: FlatGeobufLoa const {reproject = false, _targetCrs = 'WGS84'} = (options && options.gis) || {}; let headerMeta; - const iterator = deserializeGeoJson(stream, false, (header) => { + const iterator = deserializeGeoJson(stream, undefined, (header) => { headerMeta = header; }); let projection; let firstRecord = true; + // @ts-expect-error this looks wrong for await (const feature of iterator) { if (firstRecord) { const crs = headerMeta && headerMeta.crs; diff --git a/modules/flatgeobuf/src/lib/types.ts b/modules/flatgeobuf/src/lib/types.ts deleted file mode 100644 index f6d47e496c..0000000000 --- a/modules/flatgeobuf/src/lib/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type {LoaderOptions} from '@loaders.gl/loader-utils'; - -export type FlatGeobufLoaderOptions = LoaderOptions & { - flatgeobuf?: { - shape?: 'geojson-row-table' | 'columnar-table' | 'geojson' | 'binary'; - }; - gis?: { - reproject?: boolean; - _targetCrs?: string; - /** @deprecated Use options.flatgeobuf.shape */ - format?: 'geojson-row-table' | 'columnar-table' | 'geojson' | 'binary'; - }; -}; diff --git a/modules/flatgeobuf/test/flatgeobuf-loader.spec.js b/modules/flatgeobuf/test/flatgeobuf-loader.spec.ts similarity index 79% rename from modules/flatgeobuf/test/flatgeobuf-loader.spec.js rename to modules/flatgeobuf/test/flatgeobuf-loader.spec.ts index 0c2e4570de..e0f68d3a0d 100644 --- a/modules/flatgeobuf/test/flatgeobuf-loader.spec.js +++ b/modules/flatgeobuf/test/flatgeobuf-loader.spec.ts @@ -9,8 +9,8 @@ setLoaderOptions({ }); test('FlatGeobufLoader#load', async (t) => { - const features = await load(FLATGEOBUF_COUNTRIES_DATA_URL, FlatGeobufLoader, {worker: false}); - t.ok(features.length); // , 179); + const geojsonTable = await load(FLATGEOBUF_COUNTRIES_DATA_URL, FlatGeobufLoader, {worker: false}); + t.equal(geojsonTable.features.length, 179); t.end(); }); @@ -20,7 +20,7 @@ test('FlatGeobufLoader#loadInBatches', async (t) => { }); t.ok(iterator); - const features = []; + const features: any[] = []; for await (const feature of iterator) { features.push(feature); } diff --git a/modules/flatgeobuf/test/index.js b/modules/flatgeobuf/test/index.ts similarity index 100% rename from modules/flatgeobuf/test/index.js rename to modules/flatgeobuf/test/index.ts diff --git a/modules/geopackage/package.json b/modules/geopackage/package.json index 9333bf437d..06900581ae 100644 --- a/modules/geopackage/package.json +++ b/modules/geopackage/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/geopackage", "description": "GeoPackage data loaders", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -16,22 +17,42 @@ "GeoPackage" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", "dist", "README.md" ], + "browser": { + "fs": false + }, + "scripts_comments": [ + "build-bundle: ocular-bundle does not seem to respect --external and --define options" + ], + "scripts": { + "pre-build": "npm run build-worker && npm run build-worker --env.dev && npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "# ocular-bundle ./src/index.ts --external:{util,fs,path} --define:__VERSION__=\\\"$npm_package_version\\\"", + "build-worker": "# esbuild src/workers/geopackage-worker.ts --bundle --outfile=dist/geopackage-worker.js --external:{util,fs,path} --define:__VERSION__=\\\"$npm_package_version\\\"" + }, "dependencies": { "@babel/runtime": "^7.3.1", - "@loaders.gl/gis": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", - "@loaders.gl/wkt": "4.0.0-alpha.13", - "@math.gl/proj4": "^3.5.1", - "@types/sql.js": "^1.4.2", - "sql.js": "1.5.0" + "@loaders.gl/gis": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "@loaders.gl/wkt": "4.0.3", + "@math.gl/proj4": "^4.0.0", + "@types/sql.js": "^1.4.5", + "fs": "^0.0.1-security", + "path": "^0.12.7", + "sql.js": "1.8.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/geopackage/src/bundle.ts b/modules/geopackage/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/geopackage/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/geopackage/src/geopackage-loader.ts b/modules/geopackage/src/geopackage-loader.ts index e950bed171..55621c75d8 100644 --- a/modules/geopackage/src/geopackage-loader.ts +++ b/modules/geopackage/src/geopackage-loader.ts @@ -1,5 +1,9 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; -import parseGeoPackage, {DEFAULT_SQLJS_CDN} from './lib/parse-geopackage'; +import {Tables, GeoJSONTable} from '@loaders.gl/schema'; +import {parseGeoPackage, DEFAULT_SQLJS_CDN} from './lib/parse-geopackage'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. @@ -7,19 +11,45 @@ import parseGeoPackage, {DEFAULT_SQLJS_CDN} from './lib/parse-geopackage'; const VERSION = 'latest'; export type GeoPackageLoaderOptions = LoaderOptions & { + /** Options for the geopackage loader */ geopackage?: { - // Use null in Node - sqlJsCDN: string | null; + /** Shape of returned data */ + shape?: 'geojson-table' | 'tables'; + /** Name of table to load (defaults to first table), unless shape==='tables' */ + table?: string; + /** Use null in Node */ + sqlJsCDN?: string | null; }; gis?: { reproject?: boolean; _targetCrs?: string; - format?: 'geojson' | 'tables'; }; }; -/** Geopackage loader */ -export const GeoPackageLoader: LoaderWithParser = { +export const GeoPackageLoader: LoaderWithParser< + GeoJSONTable | Tables, + never, + GeoPackageLoaderOptions +> = { + id: 'geopackage', + name: 'GeoPackage', + module: 'geopackage', + version: VERSION, + extensions: ['gpkg'], + mimeTypes: ['application/geopackage+sqlite3'], + category: 'geometry', + parse: parseGeoPackage, + options: { + geopackage: { + sqlJsCDN: DEFAULT_SQLJS_CDN, + shape: 'tables' + }, + gis: {} + } +}; + +/** Geopackage loader * +export const GeoPackageTableLoader: LoaderWithParser, never, GeoPackageLoaderOptions> = { id: 'geopackage', name: 'GeoPackage', module: 'geopackage', @@ -30,10 +60,10 @@ export const GeoPackageLoader: LoaderWithParser = { parse: parseGeoPackage, options: { geopackage: { - sqlJsCDN: DEFAULT_SQLJS_CDN + sqlJsCDN: DEFAULT_SQLJS_CDN, }, gis: { - format: 'tables' } } }; +*/ diff --git a/modules/geopackage/src/index.ts b/modules/geopackage/src/index.ts index 9ed79e11a4..ea06d29b2d 100644 --- a/modules/geopackage/src/index.ts +++ b/modules/geopackage/src/index.ts @@ -1 +1,4 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + export {GeoPackageLoader} from './geopackage-loader'; diff --git a/modules/geopackage/src/lib/parse-geopackage.ts b/modules/geopackage/src/lib/parse-geopackage.ts index b78e22091e..1b57f13e04 100644 --- a/modules/geopackage/src/lib/parse-geopackage.ts +++ b/modules/geopackage/src/lib/parse-geopackage.ts @@ -1,18 +1,12 @@ /* eslint-disable camelcase, @typescript-eslint/no-use-before-define */ -import type {GeoPackageLoaderOptions} from '../geopackage-loader'; -import initSqlJs, {SqlJsStatic, Database, Statement} from 'sql.js'; +import {isBrowser} from '@loaders.gl/loader-utils'; import {WKBLoader} from '@loaders.gl/wkt'; -import { - Schema, - Field, - Geometry, - DataType, - Tables, - ObjectRowTable, - Feature -} from '@loaders.gl/schema'; +import {Schema, Field, Geometry, DataType, Tables, GeoJSONTable, Feature} from '@loaders.gl/schema'; import {binaryToGeometry, transformGeoJsonCoords} from '@loaders.gl/gis'; import {Proj4Projection} from '@math.gl/proj4'; +import initSqlJs, {SqlJsStatic, Database, Statement} from 'sql.js'; + +import type {GeoPackageLoaderOptions} from '../geopackage-loader'; import { GeometryColumnsRow, ContentsRow, @@ -26,9 +20,15 @@ import { GeoPackageGeometryTypes } from './types'; -// We pin to the same version as sql.js that we use. -// As of March 2022, versions 1.6.0, 1.6.1, and 1.6.2 of sql.js appeared not to work. -export const DEFAULT_SQLJS_CDN = 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.5.0/'; +const SQL_JS_VERSION = '1.8.0'; + +/** + * We pin to the same version as sql.js that we use. + * As of March 2022, versions 1.6.0, 1.6.1, and 1.6.2 of sql.js appeared not to work. + */ +export const DEFAULT_SQLJS_CDN = isBrowser + ? `https://cdnjs.cloudflare.com/ajax/libs/sql.js/${SQL_JS_VERSION}/` + : null; // https://www.geopackage.org/spec121/#flags_layout const ENVELOPE_BYTE_LENGTHS = { @@ -44,7 +44,7 @@ const ENVELOPE_BYTE_LENGTHS = { }; // Documentation: https://www.geopackage.org/spec130/index.html#table_column_data_types -const SQL_TYPE_MAPPING: {[type in SQLiteTypes | GeoPackageGeometryTypes]: DataType} = { +const SQL_TYPE_MAPPING: Record = { BOOLEAN: 'bool', TINYINT: 'int8', SMALLINT: 'int16', @@ -68,39 +68,51 @@ const SQL_TYPE_MAPPING: {[type in SQLiteTypes | GeoPackageGeometryTypes]: DataTy GEOMETRYCOLLECTION: 'binary' }; -export default async function parseGeoPackage( +export async function parseGeoPackage( arrayBuffer: ArrayBuffer, options?: GeoPackageLoaderOptions -): Promise | Record> { +): Promise> { const {sqlJsCDN = DEFAULT_SQLJS_CDN} = options?.geopackage || {}; - const {reproject = false, _targetCrs = 'WGS84', format = 'tables'} = options?.gis || {}; + const {reproject = false, _targetCrs = 'WGS84'} = options?.gis || {}; const db = await loadDatabase(arrayBuffer, sqlJsCDN); const tables = listVectorTables(db); const projections = getProjections(db); - // Mapping from tableName to geojson feature collection - const outputTables: Tables = { - shape: 'tables', - tables: [] - }; + const selectedTable = tables.find((table) => table.table_name === options?.geopackage?.table); + const tableName = selectedTable ? selectedTable.table_name : tables[0].table_name; - for (const table of tables) { - const {table_name: tableName} = table; - outputTables.tables.push({ - name: tableName, - table: getVectorTable(db, tableName, projections, { + const shape = options?.geopackage?.shape; + switch (shape) { + case 'geojson-table': + return getVectorTable(db, tableName, projections, { reproject, _targetCrs - }) - }); - } + }); + + case 'tables': + // Mapping from tableName to geojson feature collection + const outputTables: Tables = { + shape: 'tables', + tables: [] + }; + + for (const table of tables) { + const {table_name: tableName} = table; + outputTables.tables.push({ + name: tableName, + table: getVectorTable(db, tableName, projections, { + reproject, + _targetCrs + }) + }); + } - if (format === 'geojson') { - return formatTablesAsGeojson(outputTables); - } + return outputTables; - return outputTables; + default: + throw new Error(shape); + } } /** @@ -165,7 +177,7 @@ function getVectorTable( tableName: string, projections: ProjectionMapping, {reproject, _targetCrs}: {reproject: boolean; _targetCrs: string} -): ObjectRowTable { +): GeoJSONTable { const dataColumns = getDataColumns(db, tableName); const geomColumn = getGeometryColumn(db, tableName); const featureIdColumn = getFeatureIdName(db, tableName); @@ -183,7 +195,7 @@ function getVectorTable( }); } - const geojsonFeatures: object[] = []; + const geojsonFeatures: Feature[] = []; for (const row of values) { const geojsonFeature = constructGeoJsonFeature( columns, @@ -199,13 +211,21 @@ function getVectorTable( const schema = getSchema(db, tableName); if (projection) { return { - shape: 'object-row-table', - data: transformGeoJsonCoords(geojsonFeatures, projection.project), + shape: 'geojson-table', + type: 'FeatureCollection', + // @ts-expect-error TODO - null geometries causing problems... + features: transformGeoJsonCoords(geojsonFeatures, projection.project), schema }; } - return {data: geojsonFeatures, schema, shape: 'object-row-table'}; + return { + shape: 'geojson-table', + schema, + type: 'FeatureCollection', + // @ts-expect-error TODO - null features + features: geojsonFeatures + }; } /** @@ -373,8 +393,9 @@ function parseGeometry(arrayBuffer: ArrayBuffer): Geometry | null { // Loaders should not depend on `core` and the context passed to the main loader doesn't include a // `parseSync` option, so instead we call parseSync directly on WKBLoader - const binaryGeometry = WKBLoader.parseSync(arrayBuffer.slice(wkbOffset)); + const binaryGeometry = WKBLoader.parseSync?.(arrayBuffer.slice(wkbOffset)); + // @ts-expect-error return binaryToGeometry(binaryGeometry); } @@ -474,11 +495,3 @@ function getSchema(db: Database, tableName: string): Schema { return {fields, metadata: {}}; } - -function formatTablesAsGeojson(tables: Tables): Record { - const geojsonMap = {}; - for (const table of tables.tables) { - geojsonMap[table.name] = table.table.data; - } - return geojsonMap; -} diff --git a/modules/geopackage/src/workers/geopackage-worker.ts b/modules/geopackage/src/workers/geopackage-worker.ts new file mode 100644 index 0000000000..745bbfeb56 --- /dev/null +++ b/modules/geopackage/src/workers/geopackage-worker.ts @@ -0,0 +1,7 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {createLoaderWorker} from '@loaders.gl/loader-utils'; +import {GeoPackageLoader} from '../geopackage-loader'; + +createLoaderWorker(GeoPackageLoader); diff --git a/modules/geopackage/test/geopackage-loader.spec.ts b/modules/geopackage/test/geopackage-loader.spec.ts index c9d01d8368..3fa4182fa1 100644 --- a/modules/geopackage/test/geopackage-loader.spec.ts +++ b/modules/geopackage/test/geopackage-loader.spec.ts @@ -1,71 +1,60 @@ import test from 'tape-promise/tape'; -import {load, fetchFile, isBrowser} from '@loaders.gl/core'; +import {load, fetchFile} from '@loaders.gl/core'; import {GeoPackageLoader} from '@loaders.gl/geopackage'; -import {Tables, ObjectRowTable, Feature} from '@loaders.gl/schema'; +// import {Tables, ObjectRowTable, Feature} from '@loaders.gl/schema'; const GPKG_RIVERS = '@loaders.gl/geopackage/test/data/rivers_small.gpkg'; const GPKG_RIVERS_GEOJSON = '@loaders.gl/geopackage/test/data/rivers_small.geojson'; -const sqlJsCDN = isBrowser ? 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.5.0/' : null; - -test('GeoPackageLoader#load file as table', async (t) => { - const result: Tables = await load(GPKG_RIVERS, GeoPackageLoader, { - geopackage: {sqlJsCDN} +test('GeoPackageLoader#load file as tables', async (t) => { + const result = await load(GPKG_RIVERS, GeoPackageLoader, { + geopackage: { + shape: 'tables' + } }); const response = await fetchFile(GPKG_RIVERS_GEOJSON); const json = await response.json(); - const tableName = result.tables[0].name; - const table = result.tables[0].table; - - t.equal(tableName, 'FEATURESriversds', 'loaded correct table name'); - t.equal(table.data.length, 1, 'Correct number of rows received'); - t.deepEqual(table.data[0], json.features[0], 'GeoPackage matches GeoJSON from OGR'); - - t.ok(table.schema); - t.equal(table.schema?.fields.length, 5); - - t.end(); -}); - -test('GeoPackageLoader#load file as geojson', async (t) => { - const result: Record = await load(GPKG_RIVERS, GeoPackageLoader, { - geopackage: {sqlJsCDN}, - gis: {format: 'geojson'} - }); - - const response = await fetchFile(GPKG_RIVERS_GEOJSON); - const json = await response.json(); + t.equal(result.shape, 'tables'); + if (result.shape === 'tables') { + const tableName = result.tables[0].name; + const table = result.tables[0].table; - const tableName = Object.keys(result)[0]; - const features = result[tableName]; + t.equal(tableName, 'FEATURESriversds', 'loaded correct table name'); + t.equal(table.features.length, 1, 'Correct number of rows received'); + t.deepEqual(table.features[0], json.features[0], 'GeoPackage matches GeoJSON from OGR'); - t.equal(tableName, 'FEATURESriversds', 'loaded correct table name'); - t.equal(features.length, 1, 'Correct number of rows received'); - t.deepEqual(features[0], json.features[0], 'GeoPackage matches GeoJSON from OGR'); + t.ok(table.schema); + t.equal(table.schema?.fields.length, 5); + } t.end(); }); test('GeoPackageLoader#load file and reproject to WGS84', async (t) => { - const result: Tables = await load(GPKG_RIVERS, GeoPackageLoader, { - geopackage: {sqlJsCDN}, + const result = await load(GPKG_RIVERS, GeoPackageLoader, { + geopackage: {shape: 'tables'}, gis: {reproject: true, _targetCrs: 'WGS84'} }); - const tableName = result.tables[0].name; - const table = result.tables[0].table; - - t.equal(tableName, 'FEATURESriversds', 'loaded correct table name'); - t.ok( - table.data[0].geometry.coordinates.every((coord) => insideBbox(coord, [-180, -90, 180, 90])), - 'All coordinates in WGS84 lon-lat bounding box' - ); - - t.ok(table.schema); - t.equal(table.schema?.fields.length, 5); - + t.equal(result.shape, 'tables'); + if (result.shape === 'tables') { + const tableName = result.tables[0].name; + const table = result.tables[0].table; + + t.equal(tableName, 'FEATURESriversds', 'loaded correct table name'); + t.ok( + // @ts-expect-error ignore geometry collection + table.features[0].geometry.coordinates.every((coord) => + insideBbox(coord, [-180, -90, 180, 90]) + ), + 'All coordinates in WGS84 lon-lat bounding box' + ); + + t.ok(table.schema); + t.equal(table.schema?.fields.length, 5); + } t.end(); }); diff --git a/modules/geopackage/test/index.js b/modules/geopackage/test/index.js deleted file mode 100644 index 3a4caf52fd..0000000000 --- a/modules/geopackage/test/index.js +++ /dev/null @@ -1 +0,0 @@ -import './geopackage-loader.spec'; diff --git a/modules/geopackage/test/index.ts b/modules/geopackage/test/index.ts new file mode 100644 index 0000000000..5cb4ef5a17 --- /dev/null +++ b/modules/geopackage/test/index.ts @@ -0,0 +1,3 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +import './geopackage-loader.spec'; diff --git a/modules/geotiff/package.json b/modules/geotiff/package.json index ef3f5245e4..3a6b1e37a9 100644 --- a/modules/geotiff/package.json +++ b/modules/geotiff/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/geotiff", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loaders for tiff and geotiff", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "geotiff" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -30,8 +38,8 @@ "README.md" ], "scripts": { - "pre-build": "# npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --outfile=dist/dist.min.js --bundle --minify --sourcemap --external:{fs,http,https}" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies-disabled": { "geotiff": "ilan-gold/geotiff.js#ilan-gold/viv_094" diff --git a/modules/geotiff/src/bundle.ts b/modules/geotiff/src/bundle.ts deleted file mode 100644 index 0ccdac56a7..0000000000 --- a/modules/geotiff/src/bundle.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as moduleExports from './index'; -// @ts-ignore -globalThis.loaders = globalThis.loaders || {}; -// @ts-ignore -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/geotiff/src/lib/utils/Pool.ts b/modules/geotiff/src/lib/utils/Pool.ts index c7923a06b7..9fa452ddb8 100644 --- a/modules/geotiff/src/lib/utils/Pool.ts +++ b/modules/geotiff/src/lib/utils/Pool.ts @@ -11,7 +11,7 @@ const defaultPoolSize = globalThis?.navigator?.hardwareConcurrency ?? 4; /** * Pool for workers to decode chunks of the images. - * This is a line-for-line copy of GeoTIFFs old implementation: https://github.com/geotiffjs/geotiff.js/blob/v1.0.0-beta.6/src/pool.js + * This is a line-for-line copy of GeoTIFFs old implementation: https://github.com/geotiffjs/geotiff.js/blob/v1.0.0-beta.8/src/pool.js */ export default class Pool { workers: Worker[]; diff --git a/modules/geotiff/src/types.ts b/modules/geotiff/src/types.ts index 66b927935b..cc1777cd27 100644 --- a/modules/geotiff/src/types.ts +++ b/modules/geotiff/src/types.ts @@ -1,7 +1,7 @@ import {DTYPE_LOOKUP} from './lib/ome/ome-utils'; -export type Dtype = typeof DTYPE_LOOKUP[keyof typeof DTYPE_LOOKUP]; -export type TypedArray = InstanceType; +export type Dtype = (typeof DTYPE_LOOKUP)[keyof typeof DTYPE_LOOKUP]; +export type TypedArray = InstanceType<(typeof globalThis)[`${Dtype}Array`]>; export interface PixelData { data: TypedArray; diff --git a/modules/gis/package.json b/modules/gis/package.json index a077ede6e0..2277185feb 100644 --- a/modules/gis/package.json +++ b/modules/gis/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/gis", "description": "Helpers for GIS category data", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -16,8 +17,15 @@ "GeoJSON" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -25,14 +33,14 @@ "README.md" ], "dependencies": { - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", "@mapbox/vector-tile": "^1.3.1", - "@math.gl/polygon": "^3.5.1", + "@math.gl/polygon": "^4.0.0", "pbf": "^3.2.1" }, "devDependencies": { - "@math.gl/proj4": "^3.5.1" + "@math.gl/proj4": "^4.0.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/gis/src/bundle.ts b/modules/gis/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/gis/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/gis/src/index.ts b/modules/gis/src/index.ts index bf4238e451..31b0a12309 100644 --- a/modules/gis/src/index.ts +++ b/modules/gis/src/index.ts @@ -1,8 +1,23 @@ // Types from `@loaders.gl/schema` -// Functions -export {flatGeojsonToBinary} from './lib/flat-geojson-to-binary'; -export {geojsonToBinary} from './lib/geojson-to-binary'; -export {geojsonToFlatGeojson} from './lib/geojson-to-flat-geojson'; -export {binaryToGeojson, binaryToGeoJson, binaryToGeometry} from './lib/binary-to-geojson'; -export {transformBinaryCoords, transformGeoJsonCoords} from './lib/transform'; +// Geo Metadata +// import {default as GEOPARQUET_METADATA_SCHEMA} from './lib/geo/geoparquet-metadata-schema.json'; +// export {GEOPARQUET_METADATA_SCHEMA}; +export {GEOPARQUET_METADATA_JSON_SCHEMA} from './lib/geo/geoparquet-metadata-schema'; + +export type {GeoMetadata} from './lib/geo/geoparquet-metadata'; +export {getGeoMetadata, setGeoMetadata, unpackGeoMetadata} from './lib/geo/geoparquet-metadata'; +export {unpackJSONStringMetadata} from './lib/geo/geoparquet-metadata'; + +export type {GeoArrowEncoding, GeoArrowMetadata} from './lib/geo/geoarrow-metadata'; +export {getGeometryColumnsFromSchema} from './lib/geo/geoarrow-metadata'; + +// Table conversion +export {convertWKBTableToGeoJSON} from './lib/tables/convert-table-to-geojson'; + +// Binary Geometries +export {flatGeojsonToBinary} from './lib/binary-features/flat-geojson-to-binary'; +export {geojsonToBinary} from './lib/binary-features/geojson-to-binary'; +export {geojsonToFlatGeojson} from './lib/binary-features/geojson-to-flat-geojson'; +export {binaryToGeojson, binaryToGeometry} from './lib/binary-features/binary-to-geojson'; +export {transformBinaryCoords, transformGeoJsonCoords} from './lib/binary-features/transform'; diff --git a/modules/gis/src/lib/binary-to-geojson.ts b/modules/gis/src/lib/binary-features/binary-to-geojson.ts similarity index 81% rename from modules/gis/src/lib/binary-to-geojson.ts rename to modules/gis/src/lib/binary-features/binary-to-geojson.ts index d65652a312..9f2f6b1c04 100644 --- a/modules/gis/src/lib/binary-to-geojson.ts +++ b/modules/gis/src/lib/binary-features/binary-to-geojson.ts @@ -1,10 +1,17 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type { BinaryGeometry, - BinaryFeatures, BinaryGeometryType, - BinaryPointFeatures, - BinaryLineFeatures, - BinaryPolygonFeatures, + BinaryPointGeometry, + BinaryLineGeometry, + BinaryPolygonGeometry, + BinaryFeatureCollection, + BinaryFeature, + // BinaryPointFeature, + // BinaryLineFeature, + // BinaryPolygonFeature, BinaryAttribute } from '@loaders.gl/schema'; import type {Feature, Geometry, Position, GeoJsonProperties} from '@loaders.gl/schema'; @@ -34,7 +41,7 @@ type BinaryToGeoJsonOptions = { * @return GeoJSON objects */ export function binaryToGeojson( - data: BinaryFeatures, + data: BinaryFeatureCollection, options?: BinaryToGeoJsonOptions ): Feature[] | Feature { const globalFeatureId = options?.globalFeatureId; @@ -44,28 +51,12 @@ export function binaryToGeojson( return parseFeatures(data, options?.type); } -/** @deprecated use `binaryToGeojson` or `binaryToGeometry` instead */ -export function binaryToGeoJson( - data: BinaryGeometry | BinaryFeatures, - type?: BinaryGeometryType, - format: 'feature' | 'geometry' = 'feature' -): Geometry | Feature[] { - switch (format) { - case 'feature': - return parseFeatures(data as BinaryFeatures, type); - case 'geometry': - return binaryToGeometry(data as BinaryGeometry); - default: - throw new Error(format); - } -} - /** * Return a single feature from a binary geometry representation as GeoJSON * @param data geometry data in binary representation * @return GeoJSON feature */ -function getSingleFeature(data: BinaryFeatures, globalFeatureId: number): Feature { +function getSingleFeature(data: BinaryFeatureCollection, globalFeatureId: number): Feature { const dataArray = normalizeInput(data); for (const data of dataArray) { let lastIndex = 0; @@ -93,7 +84,7 @@ function getSingleFeature(data: BinaryFeatures, globalFeatureId: number): Featur throw new Error(`featureId:${globalFeatureId} not found`); } -function parseFeatures(data: BinaryFeatures, type?: BinaryGeometryType): Feature[] { +function parseFeatures(data: BinaryFeatureCollection, type?: BinaryGeometryType): Feature[] { const dataArray = normalizeInput(data, type); return parseFeatureCollection(dataArray); } @@ -117,22 +108,10 @@ export function binaryToGeometry( } } -type BinaryFeature = BinaryPointFeatures | BinaryLineFeatures | BinaryPolygonFeatures; -type BinaryFeaturesArray = BinaryFeature[]; - // Normalize features // Return an array of data objects, each of which have a type key -function normalizeInput(data: BinaryFeatures, type?: BinaryGeometryType): BinaryFeaturesArray { - const isHeterogeneousType = Boolean(data.points || data.lines || data.polygons); - - if (!isHeterogeneousType) { - // @ts-expect-error This is a legacy check which allowed `data` to be an instance of the values - // here. Aka the new data.points, data.lines, or data.polygons. - data.type = type || parseType(data); - return [data] as BinaryFeaturesArray; - } - - const features: BinaryFeaturesArray = []; +function normalizeInput(data: BinaryFeatureCollection, type?: BinaryGeometryType): BinaryFeature[] { + const features: BinaryFeature[] = []; if (data.points) { data.points.type = 'Point'; features.push(data.points); @@ -150,7 +129,7 @@ function normalizeInput(data: BinaryFeatures, type?: BinaryGeometryType): Binary } /** Parse input binary data and return an array of GeoJSON Features */ -function parseFeatureCollection(dataArray): Feature[] { +function parseFeatureCollection(dataArray: BinaryFeature[]): Feature[] { const features: Feature[] = []; for (const data of dataArray) { if (data.featureIds.value.length === 0) { @@ -180,7 +159,7 @@ function parseFeatureCollection(dataArray): Feature[] { } /** Parse input binary data and return a single GeoJSON Feature */ -function parseFeature(data, startIndex?: number, endIndex?: number): Feature { +function parseFeature(data: BinaryFeature, startIndex?: number, endIndex?: number): Feature { const geometry = binaryToGeometry(data, startIndex, endIndex); const properties = parseProperties(data, startIndex, endIndex); const fields = parseFields(data, startIndex, endIndex); @@ -203,7 +182,7 @@ function parseProperties(data, startIndex: number = 0, endIndex?: number): GeoJs /** Parse binary data of type Polygon */ function polygonToGeoJson( - data, + data: BinaryPolygonGeometry, startIndex: number = -Infinity, endIndex: number = Infinity ): Polygon | MultiPolygon { @@ -245,7 +224,7 @@ function polygonToGeoJson( /** Parse binary data of type LineString */ function lineStringToGeoJson( - data, + data: BinaryLineGeometry, startIndex: number = -Infinity, endIndex: number = Infinity ): LineString | MultiLineString { @@ -268,7 +247,7 @@ function lineStringToGeoJson( } /** Parse binary data of type Point */ -function pointToGeoJson(data, startIndex, endIndex): Point | MultiPoint { +function pointToGeoJson(data: BinaryPointGeometry, startIndex, endIndex): Point | MultiPoint { const {positions} = data; const coordinates = ringToGeoJson(positions, startIndex, endIndex); const multi = coordinates.length > 1; @@ -306,16 +285,3 @@ function ringToGeoJson( } return ringCoordinates; } - -// Deduce geometry type of data object -function parseType(data) { - if (data.pathIndices) { - return 'LineString'; - } - - if (data.polygonIndices) { - return 'Polygon'; - } - - return 'Point'; -} diff --git a/modules/gis/src/lib/extract-geometry-info.ts b/modules/gis/src/lib/binary-features/extract-geometry-info.ts similarity index 100% rename from modules/gis/src/lib/extract-geometry-info.ts rename to modules/gis/src/lib/binary-features/extract-geometry-info.ts diff --git a/modules/gis/src/lib/flat-geojson-to-binary-types.ts b/modules/gis/src/lib/binary-features/flat-geojson-to-binary-types.ts similarity index 98% rename from modules/gis/src/lib/flat-geojson-to-binary-types.ts rename to modules/gis/src/lib/binary-features/flat-geojson-to-binary-types.ts index d57c806f0d..7b2156c5e2 100644 --- a/modules/gis/src/lib/flat-geojson-to-binary-types.ts +++ b/modules/gis/src/lib/binary-features/flat-geojson-to-binary-types.ts @@ -47,7 +47,7 @@ export type Polygons = { positions: Float32Array | Float64Array; polygonIndices: Uint16Array | Uint32Array; primitivePolygonIndices: Uint16Array | Uint32Array; - triangles: number[]; + triangles?: number[]; globalFeatureIds: Uint16Array | Uint32Array; featureIds: Uint16Array | Uint32Array; numericProps: {[key: string]: TypedArray}; diff --git a/modules/gis/src/lib/flat-geojson-to-binary.ts b/modules/gis/src/lib/binary-features/flat-geojson-to-binary.ts similarity index 95% rename from modules/gis/src/lib/flat-geojson-to-binary.ts rename to modules/gis/src/lib/binary-features/flat-geojson-to-binary.ts index c715f7804e..cfe995023d 100644 --- a/modules/gis/src/lib/flat-geojson-to-binary.ts +++ b/modules/gis/src/lib/binary-features/flat-geojson-to-binary.ts @@ -2,7 +2,8 @@ import {earcut} from '@math.gl/polygon'; import type { BinaryAttribute, - BinaryFeatures, + BinaryFeatureCollection, + BinaryPolygonFeature, FlatFeature, FlatPoint, FlatLineString, @@ -40,7 +41,8 @@ export function flatGeojsonToBinary( }, { numericPropKeys: (options && options.numericPropKeys) || numericPropKeys, - PositionDataType: options ? options.PositionDataType : Float32Array + PositionDataType: options ? options.PositionDataType : Float32Array, + triangulate: options ? options.triangulate : true } ); } @@ -51,6 +53,7 @@ export function flatGeojsonToBinary( export type FlatGeojsonToBinaryOptions = { numericPropKeys?: string[]; PositionDataType?: Float32ArrayConstructor | Float64ArrayConstructor; + triangulate?: boolean; }; export const TEST_EXPORTS = { @@ -91,7 +94,7 @@ function extractNumericPropTypes(features: FlatFeature[]): { * @param options * @returns an accessor object with value and size keys */ -// eslint-disable-next-line complexity +// eslint-disable-next-line complexity, max-statements function fillArrays( features: FlatFeature[], geometryInfo: GeojsonGeometryInfo & { @@ -112,7 +115,7 @@ function fillArrays( propArrayTypes, coordLength } = geometryInfo; - const {numericPropKeys = [], PositionDataType = Float32Array} = options; + const {numericPropKeys = [], PositionDataType = Float32Array, triangulate = true} = options; const hasGlobalId = features[0] && 'id' in features[0]; const GlobalFeatureIdsDataType = features.length > 65535 ? Uint32Array : Uint16Array; const points: Points = { @@ -154,7 +157,6 @@ function fillArrays( ? new Uint32Array(polygonRingsCount + 1) : new Uint16Array(polygonRingsCount + 1), positions: new PositionDataType(polygonPositionsCount * coordLength), - triangles: [], globalFeatureIds: new GlobalFeatureIdsDataType(polygonPositionsCount), featureIds: polygonFeaturesCount > 65535 @@ -165,6 +167,10 @@ function fillArrays( fields: [] }; + if (triangulate) { + polygons.triangles = []; + } + // Instantiate numeric properties arrays; one value per vertex for (const object of [points, lines, polygons]) { for (const propName of numericPropKeys) { @@ -423,6 +429,10 @@ function triangulatePolygon( coordLength }: {startPosition: number; endPosition: number; coordLength: number} ): void { + if (!polygons.triangles) { + return; + } + const start = startPosition * coordLength; const end = endPosition * coordLength; @@ -434,7 +444,6 @@ function triangulatePolygon( const holes = indices.slice(1).map((n: number) => (n - offset) / coordLength); // Compute triangulation - // @ts-expect-error TODO can earcut handle binary arrays? Add tests? const triangles = earcut(polygonPositions, holes, coordLength, areas); // Indices returned by triangulation are relative to start @@ -475,8 +484,9 @@ function makeAccessorObjects( lines: Lines, polygons: Polygons, coordLength: number -): BinaryFeatures { - return { +): BinaryFeatureCollection { + const binaryFeatures: BinaryFeatureCollection = { + shape: 'binary-feature-collection', points: { ...points, positions: {value: points.positions, size: coordLength}, @@ -497,12 +507,17 @@ function makeAccessorObjects( positions: {value: polygons.positions, size: coordLength}, polygonIndices: {value: polygons.polygonIndices, size: 1}, primitivePolygonIndices: {value: polygons.primitivePolygonIndices, size: 1}, - triangles: {value: new Uint32Array(polygons.triangles), size: 1}, globalFeatureIds: {value: polygons.globalFeatureIds, size: 1}, featureIds: {value: polygons.featureIds, size: 1}, numericProps: wrapProps(polygons.numericProps, 1) - } + } as BinaryPolygonFeature // triangles not expected }; + + if (binaryFeatures.polygons && polygons.triangles) { + binaryFeatures.polygons.triangles = {value: new Uint32Array(polygons.triangles), size: 1}; + } + + return binaryFeatures; } /** diff --git a/modules/gis/src/lib/geojson-to-binary.ts b/modules/gis/src/lib/binary-features/geojson-to-binary.ts similarity index 77% rename from modules/gis/src/lib/geojson-to-binary.ts rename to modules/gis/src/lib/binary-features/geojson-to-binary.ts index 8f755ad1d9..2f1203b9f5 100644 --- a/modules/gis/src/lib/geojson-to-binary.ts +++ b/modules/gis/src/lib/binary-features/geojson-to-binary.ts @@ -1,5 +1,5 @@ import type {Feature} from '@loaders.gl/schema'; -import type {BinaryFeatures} from '@loaders.gl/schema'; +import type {BinaryFeatureCollection} from '@loaders.gl/schema'; import {extractGeometryInfo} from './extract-geometry-info'; import {geojsonToFlatGeojson} from './geojson-to-flat-geojson'; @@ -12,6 +12,7 @@ export type GeojsonToBinaryOptions = { fixRingWinding: boolean; numericPropKeys?: string[]; PositionDataType?: Float32ArrayConstructor | Float64ArrayConstructor; + triangulate?: boolean; }; /** @@ -23,14 +24,15 @@ export type GeojsonToBinaryOptions = { */ export function geojsonToBinary( features: Feature[], - options: GeojsonToBinaryOptions = {fixRingWinding: true} -): BinaryFeatures { + options: GeojsonToBinaryOptions = {fixRingWinding: true, triangulate: true} +): BinaryFeatureCollection { const geometryInfo = extractGeometryInfo(features); const coordLength = geometryInfo.coordLength; const {fixRingWinding} = options; const flatFeatures = geojsonToFlatGeojson(features, {coordLength, fixRingWinding}); return flatGeojsonToBinary(flatFeatures, geometryInfo, { numericPropKeys: options.numericPropKeys, - PositionDataType: options.PositionDataType || Float32Array + PositionDataType: options.PositionDataType || Float32Array, + triangulate: options.triangulate }); } diff --git a/modules/gis/src/lib/geojson-to-flat-geojson.ts b/modules/gis/src/lib/binary-features/geojson-to-flat-geojson.ts similarity index 100% rename from modules/gis/src/lib/geojson-to-flat-geojson.ts rename to modules/gis/src/lib/binary-features/geojson-to-flat-geojson.ts diff --git a/modules/gis/src/lib/transform.ts b/modules/gis/src/lib/binary-features/transform.ts similarity index 79% rename from modules/gis/src/lib/transform.ts rename to modules/gis/src/lib/binary-features/transform.ts index 61b4283aa9..90f5029f08 100644 --- a/modules/gis/src/lib/transform.ts +++ b/modules/gis/src/lib/binary-features/transform.ts @@ -1,4 +1,4 @@ -import type {BinaryFeatures, BinaryGeometry} from '@loaders.gl/schema'; +import type {BinaryFeatureCollection, BinaryGeometry, Feature} from '@loaders.gl/schema'; type TransformCoordinate = (coord: number[]) => number[]; @@ -9,9 +9,9 @@ type TransformCoordinate = (coord: number[]) => number[]; * @return Transformed binary features */ export function transformBinaryCoords( - binaryFeatures: BinaryFeatures, + binaryFeatures: BinaryFeatureCollection, transformCoordinate: TransformCoordinate -): BinaryFeatures { +): BinaryFeatureCollection { if (binaryFeatures.points) { transformBinaryGeometryPositions(binaryFeatures.points, transformCoordinate); } @@ -44,9 +44,9 @@ function transformBinaryGeometryPositions(binaryGeometry: BinaryGeometry, fn: Tr * @return Transformed GeoJSON features */ export function transformGeoJsonCoords( - features: object[], + features: Feature[], fn: (coord: number[]) => number[] -): object[] { +): Feature[] { for (const feature of features) { // @ts-ignore feature.geometry.coordinates = coordMap(feature.geometry.coordinates, fn); @@ -54,16 +54,16 @@ export function transformGeoJsonCoords( return features; } -function coordMap(array, fn) { +function coordMap(array: unknown, fn: (coord: number[]) => number[]): unknown[] { if (isCoord(array)) { - return fn(array); + return fn(array as number[]); } - return array.map((item) => { + return (array as unknown[]).map((item) => { return coordMap(item, fn); }); } -function isCoord(array) { - return Number.isFinite(array[0]) && Number.isFinite(array[1]); +function isCoord(array: unknown) { + return Array.isArray(array) && Number.isFinite(array[0]) && Number.isFinite(array[1]); } diff --git a/modules/gis/src/lib/geo/geoarrow-metadata.ts b/modules/gis/src/lib/geo/geoarrow-metadata.ts new file mode 100644 index 0000000000..96fd66a473 --- /dev/null +++ b/modules/gis/src/lib/geo/geoarrow-metadata.ts @@ -0,0 +1,81 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {Schema, Field} from '@loaders.gl/schema'; + +export type GeoArrowEncoding = + | 'geoarrow.multipolygon' + | 'geoarrow.polygon' + | 'geoarrow.multilinestring' + | 'geoarrow.linestring' + | 'geoarrow.multipoint' + | 'geoarrow.point' + | 'geoarrow.wkb' + | 'geoarrow.wkt'; + +/** Array containing all encodings */ +const GEOARROW_ENCODINGS = [ + 'geoarrow.multipolygon', + 'geoarrow.polygon', + 'geoarrow.multilinestring', + 'geoarrow.linestring', + 'geoarrow.multipoint', + 'geoarrow.point', + 'geoarrow.wkb', + 'geoarrow.wkt' +]; + +const GEOARROW_COLUMN_METADATA_ENCODING = 'ARROW:extension:name'; +const GEOARROW_COLUMN_METADATA_METADATA = 'ARROW:extension:metadata'; + +/** Geospatial metadata for one column, extracted from Apache Arrow metadata */ +export type GeoArrowMetadata = { + encoding?: GeoArrowEncoding; + crs?: Record; + egdes?: 'spherical'; + [key: string]: unknown; +}; + +/** + * get geometry columns from arrow table + */ +export function getGeometryColumnsFromSchema(schema: Schema): Record { + const geometryColumns: Record = {}; + for (const field of schema.fields) { + const metadata = getGeometryMetadataForField(field); + if (metadata) { + geometryColumns[field.name] = metadata; + } + } + return geometryColumns; +} + +export function getGeometryMetadataForField(field: Field): GeoArrowMetadata | null { + let metadata: GeoArrowMetadata | null = null; + + // Check for GeoArrow metadata + const columnMetadata = field.metadata?.[GEOARROW_COLUMN_METADATA_METADATA]; + if (columnMetadata) { + try { + metadata = JSON.parse(columnMetadata); + } catch (error) { + // eslint-disable-next-line no-console + console.warn('Failed to parse GeoArrow metadata', error); + } + } + + // Check for GeoArrow column encoding + let geoEncoding = field.metadata?.[GEOARROW_COLUMN_METADATA_ENCODING]; + if (geoEncoding) { + geoEncoding = geoEncoding.toLowerCase(); + if (!GEOARROW_ENCODINGS.includes(geoEncoding)) { + // eslint-disable-next-line no-console + console.warn(`Invalid GeoArrow encoding: ${geoEncoding}`); + } else { + metadata = metadata || ({} as GeoArrowMetadata); + metadata.encoding = geoEncoding as GeoArrowEncoding; + } + } + + return metadata || null; +} diff --git a/modules/gis/src/lib/geo/geoparquet-metadata-schema.json b/modules/gis/src/lib/geo/geoparquet-metadata-schema.json new file mode 100644 index 0000000000..c331857ead --- /dev/null +++ b/modules/gis/src/lib/geo/geoparquet-metadata-schema.json @@ -0,0 +1,60 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GeoParquet", + "description": "Parquet metadata included in the geo field.", + "type": "object", + "required": ["version", "primary_column", "columns"], + "properties": { + "version": {"type": "string", "const": "1.0.0-beta.1"}, + "primary_column": {"type": "string", "minLength": 1}, + "columns": { + "type": "object", + "minProperties": 1, + "patternProperties": { + ".+": { + "type": "object", + "required": ["encoding", "geometry_types"], + "properties": { + "encoding": {"type": "string", "const": "WKB"}, + "geometry_types": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "pattern": "^(GeometryCollection|(Multi)?(Point|LineString|Polygon))( Z)?$" + } + }, + "crs": { + "oneOf": [ + { + "$ref": "https://proj.org/schemas/v0.5/projjson.schema.json" + }, + {"type": "null"} + ] + }, + "edges": {"type": "string", "enum": ["planar", "spherical"]}, + "orientation": {"type": "string", "const": "counterclockwise"}, + "bbox": { + "type": "array", + "items": {"type": "number"}, + "oneOf": [ + { + "description": "2D bbox consisting of (xmin, ymin, xmax, ymax)", + "minItems": 4, + "maxItems": 4 + }, + { + "description": "3D bbox consisting of (xmin, ymin, zmin, xmax, ymax, zmax)", + "minItems": 6, + "maxItems": 6 + } + ] + }, + "epoch": {"type": "number"} + } + } + }, + "additionalProperties": false + } + } +} diff --git a/modules/parquet/src/lib/geo/geoparquet-schema.ts b/modules/gis/src/lib/geo/geoparquet-metadata-schema.ts similarity index 96% rename from modules/parquet/src/lib/geo/geoparquet-schema.ts rename to modules/gis/src/lib/geo/geoparquet-metadata-schema.ts index e24911bd2d..324cab3c17 100644 --- a/modules/parquet/src/lib/geo/geoparquet-schema.ts +++ b/modules/gis/src/lib/geo/geoparquet-metadata-schema.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors /* eslint-disable camelcase */ @@ -7,7 +8,7 @@ * @see https://github.com/geoarrow/geoarrow/blob/main/metadata.md * @see https://github.com/opengeospatial/geoparquet/blob/main/format-specs/geoparquet.md */ -export default { +export const GEOPARQUET_METADATA_JSON_SCHEMA = { $schema: 'http://json-schema.org/draft-07/schema#', title: 'GeoParquet', description: 'Parquet metadata included in the geo field.', diff --git a/modules/gis/src/lib/geo/geoparquet-metadata.ts b/modules/gis/src/lib/geo/geoparquet-metadata.ts new file mode 100644 index 0000000000..4f034057ee --- /dev/null +++ b/modules/gis/src/lib/geo/geoparquet-metadata.ts @@ -0,0 +1,189 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +import {Schema, Field} from '@loaders.gl/schema'; + +/* eslint-disable camelcase */ + +/** + * A geoarrow / geoparquet geo metadata object + * (stored in stringified form in the top level metadata 'geo' key) + * @see https://github.com/opengeospatial/geoparquet/blob/main/format-specs/geoparquet.md + * @see https://github.com/geoarrow/geoarrow + * */ +export type GeoMetadata = { + version?: string; + primary_column?: string; + columns: Record; + [key: string]: unknown; +}; + +/** A geoarrow / geoparquet geo metadata for one geometry column */ +export type GeoColumnMetadata = { + encoding: 'wkb' | 'wkt'; + geometry_types: GeoParquetGeometryType[]; + crs?: object | null; + orientation?: 'counterclockwise'; + bbox?: [number, number, number, number] | [number, number, number, number, number, number]; + edges?: 'planar' | 'spherical'; + epoch?: number; + [key: string]: unknown; +}; + +/** A GeoParquet metadata geometry type */ +export type GeoParquetGeometryType = + | 'Point' + | 'LineString' + | 'Polygon' + | 'MultiPoint' + | 'MultiLineString' + | 'MultiPolygon' + | 'GeometryCollection' + | 'Point Z' + | 'LineString Z' + | 'Polygon Z' + | 'MultiPoint Z' + | 'MultiLineString Z' + | 'MultiPolygon Z' + | 'GeometryCollection Z'; + +// GEO METADATA + +/** + * Reads the GeoMetadata object from the metadata + * @note geoarrow / parquet schema is stringified into a single key-value pair in the parquet metadata + */ +export function getGeoMetadata(schema: Schema): GeoMetadata | null { + const geoMetadata = parseJSONStringMetadata(schema, 'geo'); + if (!geoMetadata) { + return null; + } + for (const column of Object.values(geoMetadata.columns || {})) { + if (column.encoding) { + column.encoding = column.encoding.toLowerCase(); + } + } + return geoMetadata as GeoMetadata; +} + +/** + * Stores a geoarrow / geoparquet geo metadata object in the schema + * @note geoarrow / geoparquet geo metadata is a single stringified JSON field + */ +export function setGeoMetadata(schema: Schema, geoMetadata: GeoMetadata): void { + const stringifiedGeoMetadata = JSON.stringify(geoMetadata); + schema.metadata.geo = stringifiedGeoMetadata; +} + +/** + * Unpacks geo metadata into separate metadata fields (parses the long JSON string) + * @note geoarrow / parquet schema is stringified into a single key-value pair in the parquet metadata + */ +export function unpackGeoMetadata(schema: Schema): void { + const geoMetadata = getGeoMetadata(schema); + if (!geoMetadata) { + return; + } + + // Store Parquet Schema Level Metadata + + const {version, primary_column, columns} = geoMetadata; + if (version) { + schema.metadata['geo.version'] = version; + } + + if (primary_column) { + schema.metadata['geo.primary_column'] = primary_column; + } + + // store column names as comma separated list + schema.metadata['geo.columns'] = Object.keys(columns || {}).join(''); + + for (const [columnName, columnMetadata] of Object.entries(columns || {})) { + const field = schema.fields.find((field) => field.name === columnName); + if (field) { + if (field.name === primary_column) { + setFieldMetadata(field, 'geo.primary_field', 'true'); + } + unpackGeoFieldMetadata(field, columnMetadata); + } + } +} + +// eslint-disable-next-line complexity +function unpackGeoFieldMetadata(field: Field, columnMetadata): void { + for (const [key, value] of Object.entries(columnMetadata || {})) { + switch (key) { + case 'geometry_types': + setFieldMetadata(field, `geo.${key}`, (value as string[]).join(',')); + break; + case 'bbox': + setFieldMetadata(field, `geo.crs.${key}`, JSON.stringify(value)); + break; + case 'crs': + // @ts-ignore + for (const [crsKey, crsValue] of Object.entries(value || {})) { + switch (crsKey) { + case 'id': + const crsId = + typeof crsValue === 'object' + ? // @ts-ignore + `${crsValue?.authority}:${crsValue?.code}` + : JSON.stringify(crsValue); + setFieldMetadata(field, `geo.crs.${crsKey}`, crsId); + break; + default: + setFieldMetadata( + field, + `geo.crs.${crsKey}`, + typeof crsValue === 'string' ? crsValue : JSON.stringify(crsValue) + ); + break; + } + } + break; + case 'edges': + default: + setFieldMetadata( + field, + `geo.${key}`, + typeof value === 'string' ? value : JSON.stringify(value) + ); + } + } +} + +function setFieldMetadata(field: Field, key: string, value: string): void { + field.metadata = field.metadata || {}; + field.metadata[key] = value; +} + +// HELPERS + +/** Parse a key with stringified arrow metadata */ +export function parseJSONStringMetadata( + schema: Schema, + metadataKey: string +): Record | null { + const stringifiedMetadata = schema.metadata[metadataKey]; + if (!stringifiedMetadata) { + return null; + } + + try { + const metadata = JSON.parse(stringifiedMetadata); + if (!metadata || typeof metadata !== 'object') { + return null; + } + return metadata; + } catch { + return null; + } +} + +export function unpackJSONStringMetadata(schema: Schema, metadataKey: string): void { + const json = parseJSONStringMetadata(schema, metadataKey); + for (const [key, value] of Object.entries(json || {})) { + schema.metadata[`${metadataKey}.${key}`] = + typeof value === 'string' ? value : JSON.stringify(value); + } +} diff --git a/modules/gis/src/lib/tables/convert-table-to-geojson.ts b/modules/gis/src/lib/tables/convert-table-to-geojson.ts new file mode 100644 index 0000000000..b41482f4d8 --- /dev/null +++ b/modules/gis/src/lib/tables/convert-table-to-geojson.ts @@ -0,0 +1,61 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {LoaderWithParser} from '@loaders.gl/loader-utils'; +import type {ArrayRowTable, GeoJSONTable, ObjectRowTable, Schema} from '@loaders.gl/schema'; +import type {Feature, Geometry} from '@loaders.gl/schema'; +import {getTableLength, getTableRowAsObject} from '@loaders.gl/schema'; + +import {GeoColumnMetadata, getGeoMetadata} from '../geo/geoparquet-metadata'; + +/** TODO - move to loaders.gl/gis? */ +export function convertWKBTableToGeoJSON( + table: ArrayRowTable | ObjectRowTable, + schema: Schema, + loaders: LoaderWithParser[] +): GeoJSONTable { + const geoMetadata = getGeoMetadata(schema); + const primaryColumn = geoMetadata?.primary_column; + if (!primaryColumn) { + throw new Error('no geometry column'); + } + const columnMetadata = geoMetadata.columns[primaryColumn]; + + const features: Feature[] = []; + + const length = getTableLength(table); + for (let rowIndex = 0; rowIndex < length; rowIndex++) { + const row = getTableRowAsObject(table, rowIndex); + const geometry = parseGeometry(row[primaryColumn], columnMetadata, loaders); + delete row[primaryColumn]; + const feature: Feature = {type: 'Feature', geometry: geometry!, properties: row}; + features.push(feature); + } + + return {shape: 'geojson-table', schema, type: 'FeatureCollection', features}; +} + +function parseGeometry( + geometry: unknown, + columnMetadata: GeoColumnMetadata, + loaders: LoaderWithParser[] +): Geometry | null { + switch (columnMetadata.encoding) { + case 'wkt': + const wktLoader = loaders.find((loader) => loader.id === 'wkt'); + return wktLoader?.parseTextSync?.(geometry as string) || null; + case 'wkb': + default: + const wkbLoader = loaders.find((loader) => loader.id === 'wkb'); + const arrayBuffer = ArrayBuffer.isView(geometry) + ? geometry.buffer.slice(geometry.byteOffset, geometry.byteOffset + geometry.byteLength) + : (geometry as ArrayBuffer); + const geojson = wkbLoader?.parseSync?.(arrayBuffer, { + wkb: {shape: 'geometry'} + }) as unknown as Geometry; + return geojson; // binaryGeometry ? binaryToGeometry(binaryGeometry) : null; + // const binaryGeometry = WKBLoader.parseSync?.(geometry); + // ts-ignore + // return binaryGeometry ? binaryToGeometry(binaryGeometry) : null; + } +} diff --git a/modules/gis/test/binary-to-geojson.spec.js b/modules/gis/test/binary-features/binary-to-geojson.spec.ts similarity index 65% rename from modules/gis/test/binary-to-geojson.spec.js rename to modules/gis/test/binary-features/binary-to-geojson.spec.ts index 6fc82e26f8..61d3a59058 100644 --- a/modules/gis/test/binary-to-geojson.spec.js +++ b/modules/gis/test/binary-features/binary-to-geojson.spec.ts @@ -1,27 +1,32 @@ -/** @typedef {import('@loaders.gl/schema').BinaryFeatures} BinaryFeatures */ -/** @typedef {import('@loaders.gl/schema').BinaryGeometry} BinaryGeometry */ /* eslint-disable max-depth */ import test from 'tape-promise/tape'; +import type {BinaryFeatureCollection, FeatureCollection} from '@loaders.gl/schema'; import {fetchFile} from '@loaders.gl/core'; -import {binaryToGeojson, binaryToGeoJson, binaryToGeometry} from '@loaders.gl/gis'; +import {binaryToGeojson, binaryToGeometry} from '@loaders.gl/gis'; -import GEOMETRY_TEST_CASES from '@loaders.gl/gis/test/data/geometry'; -import EMPTY_BINARY_DATA from '@loaders.gl/gis/test/data/empty_binary'; +import {GEOMETRY_TEST_CASES} from '@loaders.gl/gis/test/data/binary-features/geometry-test-cases'; +import {EMPTY_BINARY_DATA} from '@loaders.gl/gis/test/data/binary-features/empty_binary'; -const FEATURE_COLLECTION_TEST_CASES = '@loaders.gl/gis/test/data/featurecollection.json'; +const FEATURE_COLLECTION_TEST_CASES = + '@loaders.gl/gis/test/data/binary-features/featurecollection.json'; + +type FeatureCollectionTestCase = { + geoJSON: FeatureCollection; + binary: BinaryFeatureCollection; +}; test('binary-to-geojson feature collections', async (t) => { const response = await fetchFile(FEATURE_COLLECTION_TEST_CASES); - const json = await response.json(); + const json = (await response.json()) as Record; // `mixed` test case fails test, disable until we land fix - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars const {mixed, ...TEST_CASES} = parseTestCases(json); for (const testCase of Object.values(TEST_CASES)) { if (testCase.geoJSON && testCase.binary) { t.deepEqual(binaryToGeojson(testCase.binary), testCase.geoJSON.features); - t.deepEqual(binaryToGeoJson(testCase.binary), testCase.geoJSON.features); + // t.deepEqual(binaryToGeoJson(testCase.binary), testCase.geoJSON.features); } } @@ -30,11 +35,9 @@ test('binary-to-geojson feature collections', async (t) => { test('binary-to-geojson geometries', (t) => { for (const testCase of GEOMETRY_TEST_CASES) { - /** @type {BinaryGeometry} */ - // @ts-ignore const binaryData = testCase.binary; t.deepEqual(binaryToGeometry(binaryData), testCase.geoJSON); - t.deepEqual(binaryToGeoJson(binaryData, binaryData.type, 'geometry'), testCase.geoJSON); + // t.deepEqual(binaryToGeoJson(binaryData, binaryData.type, 'geometry'), testCase.geoJSON); } t.end(); @@ -43,11 +46,10 @@ test('binary-to-geojson geometries', (t) => { test('binary-to-geojson !isHeterogeneousType', async (t) => { const response = await fetchFile(FEATURE_COLLECTION_TEST_CASES); const json = await response.json(); - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars const {mixed, ...TEST_CASES} = parseTestCases(json); for (const testCase of Object.values(TEST_CASES)) { - /** @type {BinaryFeatures} */ - const binaryData = testCase.binary.points || testCase.binary.lines || testCase.binary.polygons; + const binaryData = testCase.binary; t.deepEqual(binaryToGeojson(binaryData), testCase.geoJSON.features); } @@ -94,23 +96,34 @@ test('binary-to-geojson getSingleFeature fail', async (t) => { t.end(); }); -// Mutates input object -function parseTestCases(testCases) { - for (const value of Object.values(testCases)) { +/** @note Mutatis mutandis - Mutates input object */ +function parseTestCases( + testCases: Record +): Record { + for (const testCase of Object.values(testCases)) { // Convert `binary`, an object with typed arrays output, into typed arrays // from regular arrays - if (value.binary) { - for (const data of Object.values(value.binary)) { + if (testCase.binary) { + for (const data of Object.values(testCase.binary)) { + if (data === 'binary-feature-collection') { + continue; // eslint-disable-line + } if (data.positions) { data.positions.value = new Float32Array(data.positions.value); } + // @ts-expect-error if (data.pathIndices) { + // @ts-expect-error data.pathIndices.value = new Uint16Array(data.pathIndices.value); } + // @ts-expect-error if (data.primitivePolygonIndices) { + // @ts-expect-error data.primitivePolygonIndices.value = new Uint16Array(data.primitivePolygonIndices.value); } + // @ts-expect-error if (data.polygonIndices) { + // @ts-expect-error data.polygonIndices.value = new Uint16Array(data.polygonIndices.value); } } diff --git a/modules/gis/test/geojson-to-binary.spec.js b/modules/gis/test/binary-features/geojson-to-binary.spec.ts similarity index 93% rename from modules/gis/test/geojson-to-binary.spec.js rename to modules/gis/test/binary-features/geojson-to-binary.spec.ts index c4017001c8..77c480e8d9 100644 --- a/modules/gis/test/geojson-to-binary.spec.js +++ b/modules/gis/test/binary-features/geojson-to-binary.spec.ts @@ -3,22 +3,23 @@ import test from 'tape-promise/tape'; import {fetchFile} from '@loaders.gl/core'; import {geojsonToBinary} from '@loaders.gl/gis'; -import {extractGeometryInfo} from '@loaders.gl/gis/lib/extract-geometry-info'; -import {TEST_EXPORTS} from '@loaders.gl/gis/lib/flat-geojson-to-binary'; +import {extractGeometryInfo} from '@loaders.gl/gis/lib/binary-features/extract-geometry-info'; +import {TEST_EXPORTS} from '@loaders.gl/gis/lib/binary-features/flat-geojson-to-binary'; const {extractNumericPropTypes} = TEST_EXPORTS; // Sample GeoJSON data derived from examples in GeoJSON specification // https://tools.ietf.org/html/rfc7946#appendix-A // All features have 2D coordinates -const FEATURES_2D = '@loaders.gl/gis/test/data/2d_features.json'; +const FEATURES_2D = '@loaders.gl/gis/test/data/binary-features/2d_features.json'; // All features have 3D coordinates -const FEATURES_3D = '@loaders.gl/gis/test/data/3d_features.json'; +const FEATURES_3D = '@loaders.gl/gis/test/data/binary-features/3d_features.json'; // Some features have 3D coordinates -const FEATURES_MIXED = '@loaders.gl/gis/test/data/mixed_features.json'; +const FEATURES_MIXED = '@loaders.gl/gis/test/data/binary-features/mixed_features.json'; // Example GeoJSON with no properties -const GEOJSON_NO_PROPERTIES = '@loaders.gl/gis/test/data/geojson_no_properties.json'; +const GEOJSON_NO_PROPERTIES = + '@loaders.gl/gis/test/data/binary-features/geojson_no_properties.json'; test('gis#geojson-to-binary geometry info 2D features, no properties', async (t) => { const response = await fetchFile(FEATURES_2D); @@ -395,6 +396,19 @@ test('gis#geojson-to-binary with empty properties', async (t) => { t.end(); }); +test('gis#geojson-to-binary triangulation', async (t) => { + const response = await fetchFile(GEOJSON_NO_PROPERTIES); + const {features} = await response.json(); + const binary = geojsonToBinary(features); + + t.ok(binary.polygons.triangles); + t.deepEqual(binary.polygons.triangles.value, [3, 0, 1, 1, 2, 3]); + + const binaryNoTriangles = geojsonToBinary(features, {triangulate: false}); + t.notOk(binaryNoTriangles.polygons.triangles); + t.end(); +}); + function flatten(arr) { return arr.reduce(function (flat, toFlatten) { return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten); diff --git a/modules/gis/test/geojson-to-flat-geojson.spec.js b/modules/gis/test/binary-features/geojson-to-flat-geojson.spec.ts similarity index 98% rename from modules/gis/test/geojson-to-flat-geojson.spec.js rename to modules/gis/test/binary-features/geojson-to-flat-geojson.spec.ts index ceccf0e215..d309a95ff1 100644 --- a/modules/gis/test/geojson-to-flat-geojson.spec.js +++ b/modules/gis/test/binary-features/geojson-to-flat-geojson.spec.ts @@ -6,11 +6,11 @@ import {geojsonToFlatGeojson} from '@loaders.gl/gis'; // Sample GeoJSON data derived from examples in GeoJSON specification // https://tools.ietf.org/html/rfc7946#appendix-A // All features have 2D coordinates -const FEATURES_2D = '@loaders.gl/gis/test/data/2d_features.json'; +const FEATURES_2D = '@loaders.gl/gis/test/data/binary-features/2d_features.json'; // All features have 3D coordinates -const FEATURES_3D = '@loaders.gl/gis/test/data/3d_features.json'; +const FEATURES_3D = '@loaders.gl/gis/test/data/binary-features/3d_features.json'; // Some features have 3D coordinates -const FEATURES_MIXED = '@loaders.gl/gis/test/data/mixed_features.json'; +const FEATURES_MIXED = '@loaders.gl/gis/test/data/binary-features/mixed_features.json'; test('gis#geojson-to-flat-geojson 2D', async (t) => { const response = await fetchFile(FEATURES_2D); diff --git a/modules/gis/test/binary-features/gis.bench.ts b/modules/gis/test/binary-features/gis.bench.ts new file mode 100644 index 0000000000..93100c64d4 --- /dev/null +++ b/modules/gis/test/binary-features/gis.bench.ts @@ -0,0 +1,21 @@ +import {JSONLoader} from '@loaders.gl/json'; +import {load} from '@loaders.gl/core'; + +import {geojsonToBinary} from '@loaders.gl/gis'; + +// const GEOJSON_URL = '@loaders.gl/json/test/data/geojson-big.json'; +const GEOJSON_POLYGONS_URL = '@loaders.gl/mvt/test/data/geojson-vt/us-states.json'; + +export default async function gisBench(suite) { + suite.group('geojson-to-binary'); + + // @ts-expect-error + const {features} = await load(GEOJSON_POLYGONS_URL, JSONLoader); + const options = {multiplier: features.length, unit: 'features'}; + suite.addAsync('geojsonToBinary(triangulate=true)', options, async () => { + geojsonToBinary(features); + }); + suite.addAsync('geojsonToBinary(triangulate=false)', options, async () => { + geojsonToBinary(features, {fixRingWinding: true, triangulate: false}); + }); +} diff --git a/modules/gis/test/transform.spec.js b/modules/gis/test/binary-features/transform.spec.ts similarity index 84% rename from modules/gis/test/transform.spec.js rename to modules/gis/test/binary-features/transform.spec.ts index db90ce51c9..7750b97b50 100644 --- a/modules/gis/test/transform.spec.js +++ b/modules/gis/test/binary-features/transform.spec.ts @@ -1,11 +1,11 @@ -/** @typedef {import('@loaders.gl/schema').BinaryFeatures} BinaryFeatures */ import test from 'tape-promise/tape'; +import {BinaryFeatureCollection, Feature} from '@loaders.gl/schema'; import {transformBinaryCoords, transformGeoJsonCoords} from '@loaders.gl/gis'; import {Proj4Projection} from '@math.gl/proj4'; test('gis#reproject GeoJSON', (t) => { const projection = new Proj4Projection({from: 'WGS84', to: 'EPSG:3857'}); - const inputGeoJson = [ + const inputGeoJson: Feature[] = [ { type: 'Feature', geometry: { @@ -15,7 +15,7 @@ test('gis#reproject GeoJSON', (t) => { properties: {} } ]; - const expectedGeoJson = [ + const expectedGeoJson: Feature[] = [ { type: 'Feature', geometry: { @@ -33,8 +33,8 @@ test('gis#reproject GeoJSON', (t) => { test('gis#reproject binary', (t) => { const projection = new Proj4Projection({from: 'WGS84', to: 'EPSG:3857'}); - /** @type {BinaryFeatures} */ - const binaryData = { + const binaryData: BinaryFeatureCollection = { + shape: 'binary-feature-collection', points: { type: 'Point', positions: {value: new Float32Array([-74, 41]), size: 2}, @@ -46,8 +46,9 @@ test('gis#reproject binary', (t) => { properties: [{string1: 'a'}, {string1: 'b'}] } }; - /** @type {BinaryFeatures} */ - const expectedBinaryData = { + + const expectedBinaryData: BinaryFeatureCollection = { + shape: 'binary-feature-collection', points: { type: 'Point', positions: {value: new Float32Array([-8237642.318702244, 5012341.663847514]), size: 2}, diff --git a/modules/gis/test/data/2d_features.json b/modules/gis/test/data/binary-features/2d_features.json similarity index 100% rename from modules/gis/test/data/2d_features.json rename to modules/gis/test/data/binary-features/2d_features.json diff --git a/modules/gis/test/data/3d_features.json b/modules/gis/test/data/binary-features/3d_features.json similarity index 100% rename from modules/gis/test/data/3d_features.json rename to modules/gis/test/data/binary-features/3d_features.json diff --git a/modules/gis/test/data/empty_binary.js b/modules/gis/test/data/binary-features/empty_binary.ts similarity index 85% rename from modules/gis/test/data/empty_binary.js rename to modules/gis/test/data/binary-features/empty_binary.ts index f73e0b25c8..b9d4e2d4c5 100644 --- a/modules/gis/test/data/empty_binary.js +++ b/modules/gis/test/data/binary-features/empty_binary.ts @@ -1,5 +1,7 @@ -/** @type {import('@loaders.gl/schema').BinaryFeatures} */ -const EMPTY_BINARY_DATA = { +import {BinaryFeatureCollection} from '@loaders.gl/schema'; + +export const EMPTY_BINARY_DATA: BinaryFeatureCollection = { + shape: 'binary-feature-collection', points: { type: 'Point', positions: {value: new Float32Array(), size: -Infinity}, @@ -28,5 +30,3 @@ const EMPTY_BINARY_DATA = { properties: [] } }; - -export default EMPTY_BINARY_DATA; diff --git a/modules/gis/test/data/featurecollection.json b/modules/gis/test/data/binary-features/featurecollection.json similarity index 98% rename from modules/gis/test/data/featurecollection.json rename to modules/gis/test/data/binary-features/featurecollection.json index ac0f42f9cc..309ad754f0 100644 --- a/modules/gis/test/data/featurecollection.json +++ b/modules/gis/test/data/binary-features/featurecollection.json @@ -30,6 +30,7 @@ ] }, "binary": { + "shape": "binary-feature-collection", "points": { "positions": {"value": [100.0, 0.0, 100.0, 0.0, 101.0, 1.0], "size": 2}, "globalFeatureIds": {"value": [0, 1, 1], "size": 1}, @@ -72,6 +73,7 @@ ] }, "binary": { + "shape": "binary-feature-collection", "lines": { "positions": { "value": [100.0, 0.0, 101.0, 1.0, 100.0, 0.0, 101.0, 1.0, 102.0, 2.0, 103.0, 3.0], @@ -137,6 +139,7 @@ ] }, "binary": { + "shape": "binary-feature-collection", "polygons": { "positions": { "value": [ @@ -359,6 +362,7 @@ ] }, "binary": { + "shape": "binary-feature-collection", "points": { "positions": {"value": [100,0,100,0,101,1], "size":2}, "globalFeatureIds": {"value": [1,3,3], "size": 1}, diff --git a/modules/gis/test/data/geojson_no_properties.json b/modules/gis/test/data/binary-features/geojson_no_properties.json similarity index 100% rename from modules/gis/test/data/geojson_no_properties.json rename to modules/gis/test/data/binary-features/geojson_no_properties.json diff --git a/modules/gis/test/data/geometry.ts b/modules/gis/test/data/binary-features/geometry-test-cases.ts similarity index 92% rename from modules/gis/test/data/geometry.ts rename to modules/gis/test/data/binary-features/geometry-test-cases.ts index 817c05cac5..484901fd82 100644 --- a/modules/gis/test/data/geometry.ts +++ b/modules/gis/test/data/binary-features/geometry-test-cases.ts @@ -1,8 +1,13 @@ -/** @typedef {import('@loaders.gl/schema').BinaryGeometry} BinaryGeometry*/ -/** @typedef {import('@loaders.gl/schema').Geometry} Geometry */ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +import {BinaryGeometry, Geometry} from '@loaders.gl/schema'; -/** @type {{binary: BinaryGeometry, geoJSON: Geometry}[]} */ -export default [ +export type GeometryTestCase = { + binary: BinaryGeometry; + geoJSON: Geometry; +}; + +export const GEOMETRY_TEST_CASES: GeometryTestCase[] = [ { binary: { positions: {value: new Float32Array([100, 0]), size: 2}, diff --git a/modules/gis/test/data/mixed_features.json b/modules/gis/test/data/binary-features/mixed_features.json similarity index 100% rename from modules/gis/test/data/mixed_features.json rename to modules/gis/test/data/binary-features/mixed_features.json diff --git a/modules/gis/test/gis.bench.js b/modules/gis/test/gis.bench.js deleted file mode 100644 index 7eb7121550..0000000000 --- a/modules/gis/test/gis.bench.js +++ /dev/null @@ -1,18 +0,0 @@ -import {JSONLoader} from '@loaders.gl/json'; -import {load} from '@loaders.gl/core'; - -import {geojsonToBinary} from '@loaders.gl/gis'; - -const GEOJSON_URL = '@loaders.gl/json/test/data/geojson-big.json'; - -export default async function gisBench(suite) { - suite.group('geojson-to-binary'); - - // @ts-expect-error - const {features} = await load(GEOJSON_URL, JSONLoader); - const options = {multiplier: 308, unit: 'features'}; - - suite.addAsync('geojsonToBinary - GeoJSON to Binary conversion', options, async () => { - geojsonToBinary(features); - }); -} diff --git a/modules/gis/test/index.js b/modules/gis/test/index.js deleted file mode 100644 index 345184a09a..0000000000 --- a/modules/gis/test/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import './binary-to-geojson.spec'; -import './geojson-to-flat-geojson.spec'; -import './geojson-to-binary.spec'; -import './transform.spec'; diff --git a/modules/gis/test/index.ts b/modules/gis/test/index.ts new file mode 100644 index 0000000000..df530b6f61 --- /dev/null +++ b/modules/gis/test/index.ts @@ -0,0 +1,4 @@ +import './binary-features/binary-to-geojson.spec'; +import './binary-features/geojson-to-flat-geojson.spec'; +import './binary-features/geojson-to-binary.spec'; +import './binary-features/transform.spec'; diff --git a/modules/gltf/package.json b/modules/gltf/package.json index a868776ec8..5cf6f004d9 100644 --- a/modules/gltf/package.json +++ b/modules/gltf/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/gltf", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loader for the glTF format", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -21,8 +22,15 @@ "glTF" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -31,15 +39,15 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { - "@loaders.gl/draco": "4.0.0-alpha.13", - "@loaders.gl/images": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/textures": "4.0.0-alpha.13", - "@math.gl/core": "^3.5.1" + "@loaders.gl/draco": "4.0.3", + "@loaders.gl/images": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/textures": "4.0.3", + "@math.gl/core": "^4.0.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/gltf/src/bundle.ts b/modules/gltf/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/gltf/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/gltf/src/glb-loader.ts b/modules/gltf/src/glb-loader.ts index c81debaeda..7d6e5f1aab 100644 --- a/modules/gltf/src/glb-loader.ts +++ b/modules/gltf/src/glb-loader.ts @@ -4,8 +4,11 @@ import type {ParseGLBOptions} from './lib/parsers/parse-glb'; import {VERSION} from './lib/utils/version'; import {parseGLBSync} from './lib/parsers/parse-glb'; +/** GLB loader options */ export type GLBLoaderOptions = LoaderOptions & { + /** GLB Parser Options */ glb?: ParseGLBOptions; + /** GLB specific: byteOffset to start parsing from */ byteOffset?: number; }; @@ -40,6 +43,3 @@ function parseSync(arrayBuffer: ArrayBuffer, options?: GLBLoaderOptions): GLB { parseGLBSync(glb, arrayBuffer, byteOffset, options?.glb); return glb; } - -// TYPE TESTS - TODO find a better way than exporting junk -export const _TypecheckGLBLoader: LoaderWithParser = GLBLoader; diff --git a/modules/gltf/src/glb-writer.ts b/modules/gltf/src/glb-writer.ts index 07b90d43f6..b160ed0eec 100644 --- a/modules/gltf/src/glb-writer.ts +++ b/modules/gltf/src/glb-writer.ts @@ -1,6 +1,7 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import type {Writer, WriterOptions} from '@loaders.gl/loader-utils'; +import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import type {GLB} from './lib/types/glb-types'; import type {GLBEncodeOptions} from './lib/encoders/encode-glb'; import {encodeGLBSync} from './lib/encoders/encode-glb'; @@ -14,7 +15,7 @@ export type GLBWriterOptions = WriterOptions & { * GLB exporter * GLB is the binary container format for GLTF */ -export const GLBWriter: Writer = { +export const GLBWriter: WriterWithEncoder = { name: 'GLB', id: 'glb', module: 'gltf', @@ -23,12 +24,12 @@ export const GLBWriter: Writer = { extensions: ['glb'], mimeTypes: ['model/gltf-binary'], binary: true, - - encodeSync, - options: { glb: {} - } + }, + + encode: async (glb, options: GLBWriterOptions = {}) => encodeSync(glb, options), + encodeSync }; function encodeSync(glb, options) { @@ -44,6 +45,3 @@ function encodeSync(glb, options) { return arrayBuffer; } - -// TYPE TESTS - TODO find a better way than exporting junk -export const _TypecheckGLBLoader: Writer = GLBWriter; diff --git a/modules/gltf/src/gltf-loader.ts b/modules/gltf/src/gltf-loader.ts index 446ef44854..193998a068 100644 --- a/modules/gltf/src/gltf-loader.ts +++ b/modules/gltf/src/gltf-loader.ts @@ -45,16 +45,6 @@ export const GLTFLoader: LoaderWithParser encodeSync(gltf, options), + encodeSync }; function encodeSync(gltf, options: GLTFWriterOptions = {}) { diff --git a/modules/gltf/src/index.ts b/modules/gltf/src/index.ts index 900d88dbfe..bd767b3883 100644 --- a/modules/gltf/src/index.ts +++ b/modules/gltf/src/index.ts @@ -22,11 +22,11 @@ export type { GLTF_KHR_draco_mesh_compression, GLTF_KHR_texture_basisu, GLTF_EXT_meshopt_compression, - GLTF_EXT_texture_webp, - // 3DTiles extensions - GLTF_EXT_mesh_features, - GLTF_EXT_mesh_features_featureId, - GLTF_EXT_mesh_features_featureIdTexture, + GLTF_EXT_texture_webp +} from './lib/types/gltf-json-schema'; + +// 3DTiles extensions +export type { GLTF_EXT_feature_metadata_GLTF, GLTF_EXT_feature_metadata_Schema, GLTF_EXT_feature_metadata_Class, @@ -45,7 +45,25 @@ export type { GLTF_EXT_feature_metadata_FeatureIdAttributeFeatureIds, GLTF_EXT_feature_metadata_FeatureIdTexture, GLTF_EXT_feature_metadata_FeatureIdTextureAccessor -} from './lib/types/gltf-json-schema'; +} from './lib/types/gltf-ext-feature-metadata-schema'; + +export type { + GLTF_EXT_structural_metadata_GLTF, + GLTF_EXT_structural_metadata_Schema, + GLTF_EXT_structural_metadata_PropertyTable, + GLTF_EXT_structural_metadata_PropertyTexture, + GLTF_EXT_structural_metadata_Class, + GLTF_EXT_structural_metadata_ClassProperty +} from './lib/types/gltf-ext-structural-metadata-schema'; + +export type { + GLTF_EXT_mesh_features, + GLTF_EXT_mesh_features_featureId +} from './lib/types/gltf-ext-mesh-features-schema'; + +export {name as EXT_MESH_FEATURES} from './lib/extensions/EXT_mesh_features'; +export {name as EXT_STRUCTURAL_METADATA} from './lib/extensions/EXT_structural_metadata'; +export {name as EXT_FEATURE_METADATA} from './lib/extensions/deprecated/EXT_feature_metadata'; // Postprocessed types (modified GLTF types) export type { @@ -59,7 +77,7 @@ export type { GLTFTexturePostprocessed } from './lib/types/gltf-postprocessed-schema'; -export type {GLTFWithBuffers} from './lib/types/gltf-types'; +export type {GLTFWithBuffers, FeatureTableJson} from './lib/types/gltf-types'; // glTF loader/writer definition objects export {GLTFLoader} from './gltf-loader'; @@ -73,12 +91,3 @@ export {GLBWriter} from './glb-writer'; export {GLTFScenegraph} from './lib/api/gltf-scenegraph'; export {postProcessGLTF} from './lib/api/post-process-gltf'; export {getMemoryUsageGLTF as _getMemoryUsageGLTF} from './lib/gltf-utils/gltf-utils'; - -/** @deprecated */ -// export type {GLTFMesh as Mesh} from './lib/types/gltf-json-schema'; -/** @deprecated */ -// export type {GLTFNodePostprocessed as Node} from './lib/types/gltf-postprocessed-schema'; -/** @deprecated */ -// export type {GLTFAccessorPostprocessed as Accessor} from './lib/types/gltf-postprocessed-schema'; -// /** @deprecated */ -// export type {GLTFImagePostprocessed as Image} from './lib/types/gltf-postprocessed-schema'; diff --git a/modules/gltf/src/lib/api/gltf-extensions.ts b/modules/gltf/src/lib/api/gltf-extensions.ts index 8521b42abb..28174e9f26 100644 --- a/modules/gltf/src/lib/api/gltf-extensions.ts +++ b/modules/gltf/src/lib/api/gltf-extensions.ts @@ -5,6 +5,10 @@ import type {GLTFLoaderOptions} from '../../gltf-loader'; // GLTF 1.0 extensions (decode only) // import * as KHR_binary_gltf from './KHR_draco_mesh_compression'; +// GLTF 2.0 Vendor extensions +import * as EXT_mesh_features from '../extensions/EXT_mesh_features'; +import * as EXT_structural_metadata from '../extensions/EXT_structural_metadata'; + // GLTF 2.0 Khronos extensions (decode/encode) import * as EXT_meshopt_compression from '../extensions/EXT_meshopt_compression'; import * as EXT_texture_webp from '../extensions/EXT_texture_webp'; @@ -18,8 +22,6 @@ import * as KHR_materials_unlit from '../extensions/deprecated/KHR_materials_unl import * as KHR_techniques_webgl from '../extensions/deprecated/KHR_techniques_webgl'; import * as EXT_feature_metadata from '../extensions/deprecated/EXT_feature_metadata'; -// Vendor extensions - type GLTFExtensionPlugin = { name: string; preprocess?: (gltfData: {json: GLTF}, options: GLTFLoaderOptions, context) => void; @@ -45,6 +47,8 @@ export const EXTENSIONS: GLTFExtensionPlugin[] = [ // KHR_binary_gltf, // 2.0 + EXT_structural_metadata, + EXT_mesh_features, EXT_meshopt_compression, EXT_texture_webp, // Basisu should come after webp, we want basisu to be preferred if both are provided diff --git a/modules/gltf/src/lib/api/gltf-scenegraph.ts b/modules/gltf/src/lib/api/gltf-scenegraph.ts index 801914c4f2..316acb7530 100644 --- a/modules/gltf/src/lib/api/gltf-scenegraph.ts +++ b/modules/gltf/src/lib/api/gltf-scenegraph.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {GLTFWithBuffers} from '../types/gltf-types'; import type { @@ -19,11 +20,9 @@ import type { import {getBinaryImageMetadata} from '@loaders.gl/images'; import {padToNBytes, copyToArray} from '@loaders.gl/loader-utils'; import {assert} from '../utils/assert'; -import { - getAccessorArrayTypeAndLength, - getAccessorTypeFromSize, - getComponentTypeFromArray -} from '../gltf-utils/gltf-utils'; +import {getAccessorTypeFromSize, getComponentTypeFromArray} from '../gltf-utils/gltf-utils'; + +import {getTypedArrayForAccessor as _getTypedArrayForAccessor} from '../gltf-utils/get-typed-array'; type Extension = {[key: string]: any}; @@ -80,9 +79,9 @@ export class GLTFScenegraph { return data; } - getExtraData(key: string): {[key: string]: unknown} { + getExtraData(key: string): unknown { // TODO - Data is already unpacked by GLBParser - const extras = this.json.extras || {}; + const extras = (this.json.extras || {}) as Record; return extras[key]; } @@ -168,16 +167,16 @@ export class GLTFScenegraph { return this.getObject('buffers', index) as GLTFBuffer; } - getObject(array: string, index: number | object): object { + getObject(array: string, index: number | object): Record { // check if already resolved if (typeof index === 'object') { - return index; + return index as Record; } const object = this.json[array] && (this.json[array] as {}[])[index]; if (!object) { throw new Error(`glTF file error: Could not find ${array}[${index}]`); // eslint-disable-line } - return object; + return object as Record; } /** @@ -205,18 +204,8 @@ export class GLTFScenegraph { */ getTypedArrayForAccessor(accessor: number | object): any { // @ts-ignore - accessor = this.getAccessor(accessor); - // @ts-ignore - const bufferView = this.getBufferView(accessor.bufferView); - const buffer = this.getBuffer(bufferView.buffer); - // @ts-ignore - const arrayBuffer = buffer.data; - - // Create a new typed array as a view into the combined buffer - const {ArrayType, length} = getAccessorArrayTypeAndLength(accessor, bufferView); - // @ts-ignore - const byteOffset = bufferView.byteOffset + accessor.byteOffset; - return new ArrayType(arrayBuffer, byteOffset, length); + const gltfAccessor = this.getAccessor(accessor); + return _getTypedArrayForAccessor(this.gltf.json, this.gltf.buffers, gltfAccessor); } /** accepts accessor index or accessor object diff --git a/modules/gltf/src/lib/api/post-process-gltf.ts b/modules/gltf/src/lib/api/post-process-gltf.ts index f2aa897d26..edd5b9a717 100644 --- a/modules/gltf/src/lib/api/post-process-gltf.ts +++ b/modules/gltf/src/lib/api/post-process-gltf.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {GLTFWithBuffers} from '../types/gltf-types'; import type {ParseGLTFOptions} from '../parsers/parse-gltf'; diff --git a/modules/gltf/src/lib/extensions/EXT_mesh_features.ts b/modules/gltf/src/lib/extensions/EXT_mesh_features.ts new file mode 100644 index 0000000000..6510de8b3a --- /dev/null +++ b/modules/gltf/src/lib/extensions/EXT_mesh_features.ts @@ -0,0 +1,93 @@ +// GLTF EXTENSION: EXT_mesh_features +// https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features +/* eslint-disable camelcase */ +import type {NumericArray} from '@loaders.gl/loader-utils'; +import type {GLTF, GLTFMeshPrimitive} from '../types/gltf-json-schema'; +import {GLTFLoaderOptions} from '../../gltf-loader'; +import type { + GLTF_EXT_mesh_features, + GLTF_EXT_mesh_features_featureId +} from '../types/gltf-ext-mesh-features-schema'; + +import {GLTFScenegraph} from '../api/gltf-scenegraph'; +import {getPrimitiveTextureData} from './utils/3d-tiles-utils'; + +const EXT_MESH_FEATURES_NAME = 'EXT_mesh_features'; +export const name = EXT_MESH_FEATURES_NAME; + +export async function decode(gltfData: {json: GLTF}, options: GLTFLoaderOptions): Promise { + const scenegraph = new GLTFScenegraph(gltfData); + decodeExtMeshFeatures(scenegraph, options); +} + +/** + * Decodes feature metadata from extension. + * @param {GLTFScenegraph} scenegraph - Instance of the class for structured access to GLTF data. + * @param {GLTFLoaderOptions} options - GLTFLoader options. + */ +function decodeExtMeshFeatures(scenegraph: GLTFScenegraph, options: GLTFLoaderOptions): void { + const json = scenegraph.gltf.json; + if (!json.meshes) { + return; + } + + // Iterate through all meshes/primitives. + for (const mesh of json.meshes) { + for (const primitive of mesh.primitives) { + processMeshPrimitiveFeatures(scenegraph, primitive, options); + } + } +} + +/** + * Takes data from EXT_mesh_features and store it in 'data' property of featureIds. + * If combined with EXT_structural_metadata, corresponding data are taken from the property tables of that extension. + * @param {GLTFScenegraph} scenegraph - Instance of the class for structured access to GLTF data. + * @param {GLTFMeshPrimitive} primitive - Primitive that contains extensions. + * @param {GLTFLoaderOptions} options - GLTFLoader options. + */ +function processMeshPrimitiveFeatures( + scenegraph: GLTFScenegraph, + primitive: GLTFMeshPrimitive, + options: GLTFLoaderOptions +): void { + // Processing of mesh primitive features requires buffers to be loaded. + if (!options?.gltf?.loadBuffers) { + return; + } + + const extension = primitive.extensions?.[EXT_MESH_FEATURES_NAME] as GLTF_EXT_mesh_features; + const featureIds: GLTF_EXT_mesh_features_featureId[] = extension?.featureIds; + + if (!featureIds) { + return; + } + + for (const featureId of featureIds) { + let featureIdData: NumericArray; + // Process "Feature ID by Vertex" + if (typeof featureId.attribute !== 'undefined') { + const accessorKey = `_FEATURE_ID_${featureId.attribute}`; + const accessorIndex = primitive.attributes[accessorKey]; + featureIdData = scenegraph.getTypedArrayForAccessor(accessorIndex); + } + + // Process "Feature ID by Texture Coordinates" + else if (typeof featureId.texture !== 'undefined' && options?.gltf?.loadImages) { + featureIdData = getPrimitiveTextureData(scenegraph, featureId.texture, primitive); + } + + // Process "Feature ID by Index" + else { + /* + When both featureId.attribute and featureId.texture are undefined, + then the feature ID value for each vertex is given implicitly, via the index of the vertex. + In this case, the featureCount must match the number of vertices of the mesh primitive. + */ + // TODO: At the moment of writing we don't have a tileset with the data of that kind. Implement it later. + featureIdData = []; + } + + featureId.data = featureIdData; + } +} diff --git a/modules/gltf/src/lib/extensions/EXT_meshopt_compression.ts b/modules/gltf/src/lib/extensions/EXT_meshopt_compression.ts index 880176b130..1f97f99960 100644 --- a/modules/gltf/src/lib/extensions/EXT_meshopt_compression.ts +++ b/modules/gltf/src/lib/extensions/EXT_meshopt_compression.ts @@ -19,7 +19,7 @@ export const name = EXT_MESHOPT_COMPRESSION; export async function decode(gltfData: {json: GLTF}, options: GLTFLoaderOptions) { const scenegraph = new GLTFScenegraph(gltfData); - if (!options?.gltf?.decompressMeshes) { + if (!options?.gltf?.decompressMeshes || !options.gltf?.loadBuffers) { return; } diff --git a/modules/gltf/src/lib/extensions/EXT_structural_metadata.ts b/modules/gltf/src/lib/extensions/EXT_structural_metadata.ts new file mode 100644 index 0000000000..347972b1e2 --- /dev/null +++ b/modules/gltf/src/lib/extensions/EXT_structural_metadata.ts @@ -0,0 +1,703 @@ +// GLTF EXTENSION: EXT_structural_metadata +// https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata +/* eslint-disable camelcase */ +import type {BigTypedArray, TypedArray} from '@loaders.gl/schema'; +import type {GLTF, GLTFTextureInfoMetadata, GLTFMeshPrimitive} from '../types/gltf-json-schema'; +import type { + GLTF_EXT_structural_metadata_Schema, + GLTF_EXT_structural_metadata_ClassProperty, + GLTF_EXT_structural_metadata_Enum, + GLTF_EXT_structural_metadata_EnumValue, + GLTF_EXT_structural_metadata_PropertyTable, + GLTF_EXT_structural_metadata_GLTF, + GLTF_EXT_structural_metadata_PropertyTexture, + GLTF_EXT_structural_metadata_PropertyTable_Property, + GLTF_EXT_structural_metadata_Primitive +} from '../types/gltf-ext-structural-metadata-schema'; +import type {GLTFLoaderOptions} from '../../gltf-loader'; + +import {GLTFScenegraph} from '../api/gltf-scenegraph'; +import { + convertRawBufferToMetadataArray, + getPrimitiveTextureData, + primitivePropertyDataToAttributes, + getArrayElementByteSize, + NumericComponentType, + getOffsetsForProperty, + parseVariableLengthArrayNumeric, + parseFixedLengthArrayNumeric, + getPropertyDataString +} from './utils/3d-tiles-utils'; + +const EXT_STRUCTURAL_METADATA_NAME = 'EXT_structural_metadata'; +export const name = EXT_STRUCTURAL_METADATA_NAME; + +export async function decode(gltfData: {json: GLTF}, options: GLTFLoaderOptions): Promise { + const scenegraph = new GLTFScenegraph(gltfData); + decodeExtStructuralMetadata(scenegraph, options); +} + +/* +// Example of the extension. +// See more info at https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata +const extensions = { + "extensions": { + "EXT_structural_metadata": { + "schema": { + "classes": { + "tree": { + "name": "Tree", + "description": "Woody, perennial plant.", + "properties": { + "species": { + "description": "Type of tree.", + "type": "ENUM", + "enumType": "speciesEnum", + "required": true + }, + "age": { + "description": "The age of the tree, in years", + "type": "SCALAR", + "componentType": "UINT8", + "required": true + } + } + } + }, + "enums": { + "speciesEnum": { + "name": "Species", + "description": "An example enum for tree species.", + // valueType is not defined here. Default is "UINT16" + "values": [ + { "name": "Unspecified", "value": 0 }, + { "name": "Oak", "value": 1 } + ] + } + } + }, + "propertyTables": [{ + "name": "tree_survey_2021-09-29", + "class": "tree", + "count": 10, // The number of elements in each property array (in `species`, in `age`). + "properties": { + "species": { + "values": 0, // It's an index of the buffer view containing property values. + }, + "age": { + "values": 1 + } + } + }] + } + } +} +*/ + +/** + * Decodes feature metadata from extension. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param options - GLTFLoader options. + */ +function decodeExtStructuralMetadata(scenegraph: GLTFScenegraph, options: GLTFLoaderOptions): void { + // Decoding metadata involves buffers processing. + // So, if buffers have not been loaded, there is no reason to process metadata. + if (!options.gltf?.loadBuffers) { + return; + } + const extension: GLTF_EXT_structural_metadata_GLTF | null = scenegraph.getExtension( + EXT_STRUCTURAL_METADATA_NAME + ); + if (!extension) { + return; + } + + if (options.gltf?.loadImages) { + decodePropertyTextures(scenegraph, extension); + } + + decodePropertyTables(scenegraph, extension); +} + +/** + * Processes the data stored in the textures + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param extension - Top-level extension. + */ +function decodePropertyTextures( + scenegraph: GLTFScenegraph, + extension: GLTF_EXT_structural_metadata_GLTF +): void { + const propertyTextures = extension.propertyTextures; + const json = scenegraph.gltf.json; + if (propertyTextures && json.meshes) { + // Iterate through all meshes/primitives. + for (const mesh of json.meshes) { + for (const primitive of mesh.primitives) { + processPrimitivePropertyTextures(scenegraph, propertyTextures, primitive, extension); + } + } + } +} + +/** + * Processes the data stored in the property tables. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param extension - Top-level extension. + */ +function decodePropertyTables( + scenegraph: GLTFScenegraph, + extension: GLTF_EXT_structural_metadata_GLTF +): void { + const schema = extension.schema; + if (!schema) { + return; + } + const schemaClasses = schema.classes; + const propertyTables = extension.propertyTables; + if (schemaClasses && propertyTables) { + for (const schemaName in schemaClasses) { + const propertyTable = findPropertyTableByClass(propertyTables, schemaName); + if (propertyTable) { + processPropertyTable(scenegraph, schema, propertyTable); + } + } + } +} + +/** + * Finds the property table by class name. + * @param propertyTables - propertyTable definition taken from the top-level extension. + * @param schemaClassName - class name in the extension schema. + */ +function findPropertyTableByClass( + propertyTables: GLTF_EXT_structural_metadata_PropertyTable[], + schemaClassName: string +): GLTF_EXT_structural_metadata_PropertyTable | null { + for (const propertyTable of propertyTables) { + if (propertyTable.class === schemaClassName) { + return propertyTable; + } + } + + return null; +} + +/** + * Takes data from property textures reffered by the primitive. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param propertyTextures - propertyTexture definition taken from the top-level extention. + * @param primitive - Primitive object. + * @param extension - Top-level extension. + */ +function processPrimitivePropertyTextures( + scenegraph: GLTFScenegraph, + propertyTextures: GLTF_EXT_structural_metadata_PropertyTexture[], + primitive: GLTFMeshPrimitive, + extension: GLTF_EXT_structural_metadata_GLTF +): void { + if (!propertyTextures) { + return; + } + const primitiveExtension: GLTF_EXT_structural_metadata_Primitive = primitive.extensions?.[ + EXT_STRUCTURAL_METADATA_NAME + ] as GLTF_EXT_structural_metadata_Primitive; + const primitivePropertyTextureIndices = primitiveExtension?.propertyTextures; + if (!primitivePropertyTextureIndices) { + return; + } + + for (const primitivePropertyTextureIndex of primitivePropertyTextureIndices) { + const propertyTexture = propertyTextures[primitivePropertyTextureIndex]; + processPrimitivePropertyTexture(scenegraph, propertyTexture, primitive, extension); + } +} + +/** + * Takes property data from the texture pointed by the primitive and appends them to `exension.data`. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param propertyTexture - propertyTexture definition taken from the top-level extension. + * @param primitive - Primitive object. + * @param extension - Top-level extension. + */ +function processPrimitivePropertyTexture( + scenegraph: GLTFScenegraph, + propertyTexture: GLTF_EXT_structural_metadata_PropertyTexture, + primitive: GLTFMeshPrimitive, + extension: GLTF_EXT_structural_metadata_GLTF +): void { + if (!propertyTexture.properties) { + return; + } + + if (!extension.dataAttributeNames) { + extension.dataAttributeNames = []; + } + + /* Iterate through all properties defined in propertyTexture, e.g. "speed" and "direction": + { + "class": "wind", + "properties": { + "speed": { + "index": 0, + "texCoord": 0, + "channels": [0] + }, + "direction": { + "index": 0, + "texCoord": 0, + "channels": [1, 2] + } + } + } + */ + const className = propertyTexture.class; + for (const propertyName in propertyTexture.properties) { + // propertyName has values like "speed", "direction" + // Make attributeName as a combination of the class name and the propertyName like "wind_speed" or "wind_direction" + const attributeName = `${className}_${propertyName}`; + const textureInfoTopLevel: GLTFTextureInfoMetadata | undefined = + propertyTexture.properties?.[propertyName]; + if (!textureInfoTopLevel) { + // eslint-disable-next-line no-continue + continue; + } + + // The data taken from all meshes/primitives (the same property, e.g. "speed" or "direction") will be combined into one array and saved in textureInfoTopLevel.data + // Initially textureInfoTopLevel.data will be initialized with an empty array. + if (!textureInfoTopLevel.data) { + textureInfoTopLevel.data = []; + } + const featureTextureTable: number[] = textureInfoTopLevel.data as number[]; + + const propertyData: number[] | null = getPrimitiveTextureData( + scenegraph, + textureInfoTopLevel, + primitive + ); + if (propertyData === null) { + // eslint-disable-next-line no-continue + continue; + } + primitivePropertyDataToAttributes( + scenegraph, + attributeName, + propertyData, + featureTextureTable, + primitive + ); + textureInfoTopLevel.data = featureTextureTable; + extension.dataAttributeNames.push(attributeName); + } +} + +/** + * Navigates through all properies in the property table, gets properties data, + * and put the data to `propertyTable.data` as an array. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param schema - schema object. + * @param propertyTable - propertyTable definition taken from the top-level extension. + */ +function processPropertyTable( + scenegraph: GLTFScenegraph, + schema: GLTF_EXT_structural_metadata_Schema, + propertyTable: GLTF_EXT_structural_metadata_PropertyTable +): void { + const schemaClass = schema.classes?.[propertyTable.class]; + if (!schemaClass) { + throw new Error( + `Incorrect data in the EXT_structural_metadata extension: no schema class with name ${propertyTable.class}` + ); + } + + const numberOfElements = propertyTable.count; // `propertyTable.count` is a number of elements in each property array. + + for (const propertyName in schemaClass.properties) { + const classProperty = schemaClass.properties[propertyName]; + const propertyTableProperty: GLTF_EXT_structural_metadata_PropertyTable_Property | undefined = + propertyTable.properties?.[propertyName]; + + if (propertyTableProperty) { + // Getting all elements (`numberOfElements`) of the array in the `propertyTableProperty` + const data = getPropertyDataFromBinarySource( + scenegraph, + schema, + classProperty, + numberOfElements, + propertyTableProperty + ); + propertyTableProperty.data = data; + } + } +} + +/** + * Decodes a propertyTable column from binary source based on property type. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param schema - Schema object. + * @param classProperty - class property object. + * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table. + * @param propertyTableProperty - propertyTable's property metadata. + * @returns {string[] | number[] | string[][] | number[][]} + */ +function getPropertyDataFromBinarySource( + scenegraph: GLTFScenegraph, + schema: GLTF_EXT_structural_metadata_Schema, + classProperty: GLTF_EXT_structural_metadata_ClassProperty, + numberOfElements: number, + propertyTableProperty: GLTF_EXT_structural_metadata_PropertyTable_Property +): string[] | BigTypedArray | string[][] | BigTypedArray[] { + let data: string[] | BigTypedArray | string[][] | BigTypedArray[] = []; + const valuesBufferView = propertyTableProperty.values; + const valuesDataBytes: Uint8Array = scenegraph.getTypedArrayForBufferView(valuesBufferView); + + const arrayOffsets = getArrayOffsetsForProperty( + scenegraph, + classProperty, + propertyTableProperty, + numberOfElements + ); + const stringOffsets = getStringOffsetsForProperty( + scenegraph, + propertyTableProperty, + numberOfElements + ); + + switch (classProperty.type) { + case 'SCALAR': + case 'VEC2': + case 'VEC3': + case 'VEC4': + case 'MAT2': + case 'MAT3': + case 'MAT4': { + data = getPropertyDataNumeric(classProperty, numberOfElements, valuesDataBytes, arrayOffsets); + break; + } + case 'BOOLEAN': { + // TODO: implement it as soon as we have the corresponding tileset + throw new Error(`Not implemented - classProperty.type=${classProperty.type}`); + } + case 'STRING': { + data = getPropertyDataString(numberOfElements, valuesDataBytes, arrayOffsets, stringOffsets); + break; + } + case 'ENUM': { + data = getPropertyDataENUM( + schema, + classProperty, + numberOfElements, + valuesDataBytes, + arrayOffsets + ); + break; + } + default: + throw new Error(`Unknown classProperty type ${classProperty.type}`); + } + + return data; +} + +/** + * Parses propertyTable.property.arrayOffsets that are offsets of sub-arrays in a flatten array of values. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param classProperty - class property object. + * @param propertyTableProperty - propertyTable's property metadata. + * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table. + * @returns Typed array with offset values. + * @see https://github.com/CesiumGS/glTF/blob/2976f1183343a47a29e4059a70961371cd2fcee8/extensions/2.0/Vendor/EXT_structural_metadata/schema/propertyTable.property.schema.json#L21 + */ +function getArrayOffsetsForProperty( + scenegraph: GLTFScenegraph, + classProperty: GLTF_EXT_structural_metadata_ClassProperty, + propertyTableProperty: GLTF_EXT_structural_metadata_PropertyTable_Property, + numberOfElements: number +): TypedArray | null { + if ( + classProperty.array && + // `count` is a number of array elements. May only be defined when `array` is true. + // If `count` is NOT defined, it's a VARIABLE-length array + typeof classProperty.count === 'undefined' && + // `arrayOffsets` is an index of the buffer view containing offsets for variable-length arrays. + typeof propertyTableProperty.arrayOffsets !== 'undefined' + ) { + // Data are in a VARIABLE-length array + return getOffsetsForProperty( + scenegraph, + propertyTableProperty.arrayOffsets, + propertyTableProperty.arrayOffsetType || 'UINT32', + numberOfElements + ); + } + return null; +} + +/** + * Parses propertyTable.property.stringOffsets. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param propertyTableProperty - propertyTable's property metadata. + * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table. + * @returns Typed array with offset values. + * @see https://github.com/CesiumGS/glTF/blob/2976f1183343a47a29e4059a70961371cd2fcee8/extensions/2.0/Vendor/EXT_structural_metadata/schema/propertyTable.property.schema.json#L29C10-L29C23 + */ +function getStringOffsetsForProperty( + scenegraph: GLTFScenegraph, + propertyTableProperty: GLTF_EXT_structural_metadata_PropertyTable_Property, + numberOfElements: number +): TypedArray | null { + if ( + typeof propertyTableProperty.stringOffsets !== 'undefined' // `stringOffsets` is an index of the buffer view containing offsets for strings. + ) { + // Data are in a FIXED-length array + return getOffsetsForProperty( + scenegraph, + propertyTableProperty.stringOffsets, + propertyTableProperty.stringOffsetType || 'UINT32', + numberOfElements + ); + } + return null; +} + +/** + * Decodes properties of SCALAR, VEC-N, MAT-N types from binary sourse. + * @param classProperty - class property object. + * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table. + * @param valuesDataBytes - Data taken from values property of the property table property. + * @param arrayOffsets - Offsets for variable-length arrays. It's null for fixed-length arrays or scalar types. + * @returns Property values in a typed array or in an array of typed arrays. + */ +function getPropertyDataNumeric( + classProperty: GLTF_EXT_structural_metadata_ClassProperty, + numberOfElements: number, + valuesDataBytes: Uint8Array, + arrayOffsets: TypedArray | null +): BigTypedArray | BigTypedArray[] { + const isArray = classProperty.array; + const arrayCount = classProperty.count; + + const elementSize = getArrayElementByteSize(classProperty.type, classProperty.componentType); + const elementCount = valuesDataBytes.byteLength / elementSize; + + let valuesData: BigTypedArray; + if (classProperty.componentType) { + valuesData = convertRawBufferToMetadataArray( + valuesDataBytes, + classProperty.type, + // The datatype of the element's components. Only applicable to `SCALAR`, `VECN`, and `MATN` types. + classProperty.componentType as NumericComponentType, + elementCount + ); + } else { + // The spec doesn't provide any info what to do if componentType is not set. + valuesData = valuesDataBytes; + } + + if (isArray) { + if (arrayOffsets) { + // VARIABLE-length array + return parseVariableLengthArrayNumeric( + valuesData, + numberOfElements, + arrayOffsets, + valuesDataBytes.length, + elementSize + ); + } + if (arrayCount) { + // FIXED-length array + return parseFixedLengthArrayNumeric(valuesData, numberOfElements, arrayCount); + } + return []; + } + + return valuesData; +} + +/** + * Decodes properties of enum type from binary source. + * @param schema - Schema object. + * @param classProperty - Class property object. + * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table. + * @param valuesDataBytes - Data taken from values property of the property table property. + * @param arrayOffsets - Offsets for variable-length arrays. It's null for fixed-length arrays or scalar types. + * @returns Strings array of nested strings array. + */ +function getPropertyDataENUM( + schema: GLTF_EXT_structural_metadata_Schema, + classProperty: GLTF_EXT_structural_metadata_ClassProperty, + numberOfElements: number, + valuesDataBytes: Uint8Array, + arrayOffsets: TypedArray | null +): string[] | string[][] { + const enumType = classProperty.enumType; + // Enum ID as declared in the `enums` dictionary. Required when `type` is `ENUM`. + if (!enumType) { + throw new Error( + 'Incorrect data in the EXT_structural_metadata extension: classProperty.enumType is not set for type ENUM' + ); + } + + const enumEntry: GLTF_EXT_structural_metadata_Enum | undefined = schema.enums?.[enumType]; + if (!enumEntry) { + throw new Error( + `Incorrect data in the EXT_structural_metadata extension: schema.enums does't contain ${enumType}` + ); + } + + const enumValueType = enumEntry.valueType || 'UINT16'; + const elementSize = getArrayElementByteSize(classProperty.type, enumValueType); + const elementCount = valuesDataBytes.byteLength / elementSize; + let valuesData: BigTypedArray | null = convertRawBufferToMetadataArray( + valuesDataBytes, + classProperty.type, + enumValueType, + elementCount + ); + if (!valuesData) { + valuesData = valuesDataBytes; + } + + if (classProperty.array) { + if (arrayOffsets) { + // VARIABLE-length array + return parseVariableLengthArrayENUM({ + valuesData, + numberOfElements, + arrayOffsets, + valuesDataBytesLength: valuesDataBytes.length, + elementSize, + enumEntry + }); + } + + const arrayCount = classProperty.count; + if (arrayCount) { + // FIXED-length array + return parseFixedLengthArrayENUM(valuesData, numberOfElements, arrayCount, enumEntry); + } + return []; + } + + // Single value (not an array) + return getEnumsArray(valuesData, 0, numberOfElements, enumEntry); +} + +/** + * Parses variable length nested ENUM arrays. + * @param params.valuesData - Values in a flat typed array. + * @param params.numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table. + * @param params.arrayOffsets - Offsets for variable-length arrays. It's null for fixed-length arrays or scalar types. + * @param params.valuesDataBytesLength - Byte length of values array. + * @param params.elementSize - Single element byte size. + * @param params.enumEntry - Enums dictionary. + * @returns Nested strings array. + */ +function parseVariableLengthArrayENUM(params: { + valuesData: BigTypedArray; + numberOfElements: number; + arrayOffsets: TypedArray; + valuesDataBytesLength: number; + elementSize: number; + enumEntry: GLTF_EXT_structural_metadata_Enum; +}): string[][] { + const { + valuesData, + numberOfElements, + arrayOffsets, + valuesDataBytesLength, + elementSize, + enumEntry + } = params; + const attributeValueArray: string[][] = []; + for (let index = 0; index < numberOfElements; index++) { + const arrayOffset = arrayOffsets[index]; + const arrayByteSize = arrayOffsets[index + 1] - arrayOffsets[index]; + if (arrayByteSize + arrayOffset > valuesDataBytesLength) { + break; + } + + const typedArrayOffset = arrayOffset / elementSize; + const elementCount = arrayByteSize / elementSize; + const array: string[] = getEnumsArray(valuesData, typedArrayOffset, elementCount, enumEntry); + attributeValueArray.push(array); + } + return attributeValueArray; +} + +/** + * Parses fixed length ENUM arrays. + * @param valuesData - Values in a flat typed array. + * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table. + * @param arrayCount - Nested arrays length. + * @param enumEntry - Enums dictionary. + * @returns Nested strings array. + */ +function parseFixedLengthArrayENUM( + valuesData: BigTypedArray, + numberOfElements: number, + arrayCount: number, + enumEntry: GLTF_EXT_structural_metadata_Enum +): string[][] { + const attributeValueArray: string[][] = []; + for (let index = 0; index < numberOfElements; index++) { + const elementOffset = arrayCount * index; + const array: string[] = getEnumsArray(valuesData, elementOffset, arrayCount, enumEntry); + attributeValueArray.push(array); + } + return attributeValueArray; +} + +/** + * Parses ENUM values into a string array. + * @param valuesData - Values in a flat typed array. + * @param offset - Offset to start parse from. + * @param count - Values length to parse. + * @param enumEntry - Enums dictionary. + * @returns Array of strings with parsed ENUM names. + */ +function getEnumsArray( + valuesData: BigTypedArray, + offset: number, + count: number, + enumEntry: GLTF_EXT_structural_metadata_Enum +): string[] { + const array: string[] = []; + for (let i = 0; i < count; i++) { + // At the moment we don't support BigInt. It requires additional calculations logic + // and might be an issue in Safari + if (valuesData instanceof BigInt64Array || valuesData instanceof BigUint64Array) { + array.push(''); + } else { + const value = valuesData[offset + i]; + + const enumObject = getEnumByValue(enumEntry, value); + if (enumObject) { + array.push(enumObject.name); + } else { + array.push(''); + } + } + } + return array; +} + +/** + * Looks up ENUM whose `value` property matches the specified number in the parameter `value`. + * @param {GLTF_EXT_structural_metadata_Enum} enumEntry - ENUM entry containing the array of possible enums. + * @param {number} value - The value of the ENUM to locate. + * @returns {GLTF_EXT_structural_metadata_EnumValue | null} ENUM matcihng the specified value or null of no ENUM object was found. + */ +function getEnumByValue( + enumEntry: GLTF_EXT_structural_metadata_Enum, + value: number +): GLTF_EXT_structural_metadata_EnumValue | null { + for (const enumValue of enumEntry.values) { + if (enumValue.value === value) { + return enumValue; + } + } + + return null; +} diff --git a/modules/gltf/src/lib/extensions/KHR_draco_mesh_compression.ts b/modules/gltf/src/lib/extensions/KHR_draco_mesh_compression.ts index 9f69faf5af..60168710ed 100644 --- a/modules/gltf/src/lib/extensions/KHR_draco_mesh_compression.ts +++ b/modules/gltf/src/lib/extensions/KHR_draco_mesh_compression.ts @@ -2,7 +2,12 @@ // Only TRIANGLES: 0x0004 and TRIANGLE_STRIP: 0x0005 are supported /* eslint-disable camelcase */ -/* eslint-disable camelcase */ +import type {LoaderContext} from '@loaders.gl/loader-utils'; +import {sliceArrayBuffer, parseFromContext} from '@loaders.gl/loader-utils'; + +import {DracoLoader} from '@loaders.gl/draco'; +import {DracoLoaderOptions} from '@loaders.gl/draco'; + import type { GLTF, GLTFAccessor, @@ -11,10 +16,6 @@ import type { } from '../types/gltf-json-schema'; import type {GLTFLoaderOptions} from '../../gltf-loader'; -import type {LoaderContext} from '@loaders.gl/loader-utils'; -import {DracoLoader} from '@loaders.gl/draco'; -import {DracoLoaderOptions, DracoMesh} from '@loaders.gl/draco'; -import {sliceArrayBuffer} from '@loaders.gl/loader-utils'; import {GLTFScenegraph} from '../api/gltf-scenegraph'; import {getGLTFAccessors, getGLTFAccessor} from '../gltf-utils/gltf-attribute-utils'; @@ -99,12 +100,11 @@ async function decompressPrimitive( // TODO - remove when `parse` is fixed to handle `byteOffset`s const bufferCopy = sliceArrayBuffer(buffer.buffer, buffer.byteOffset); // , buffer.byteLength); - const {parse} = context; const dracoOptions: DracoLoaderOptions = {...options}; // TODO - remove hack: The entire tileset might be included, too expensive to serialize delete dracoOptions['3d-tiles']; - const decodedData = (await parse(bufferCopy, DracoLoader, dracoOptions, context)) as DracoMesh; + const decodedData = await parseFromContext(bufferCopy, DracoLoader, dracoOptions, context); const decodedAttributes: {[key: string]: GLTFAccessor} = getGLTFAccessors(decodedData.attributes); diff --git a/modules/gltf/src/lib/extensions/KHR_texture_transform.ts b/modules/gltf/src/lib/extensions/KHR_texture_transform.ts index 98cf5b7c05..30637e4532 100644 --- a/modules/gltf/src/lib/extensions/KHR_texture_transform.ts +++ b/modules/gltf/src/lib/extensions/KHR_texture_transform.ts @@ -61,7 +61,7 @@ type TransformParameters = { export async function decode(gltfData: GLTFWithBuffers, options: GLTFLoaderOptions) { const gltfScenegraph = new GLTFScenegraph(gltfData); const hasExtension = gltfScenegraph.hasExtension(EXT_MESHOPT_TRANSFORM); - if (!hasExtension) { + if (!hasExtension || !options.gltf?.loadBuffers) { return; } const materials = gltfData.json.materials || []; diff --git a/modules/gltf/src/lib/extensions/deprecated/EXT_feature_metadata.ts b/modules/gltf/src/lib/extensions/deprecated/EXT_feature_metadata.ts index a028e32df9..ca24e2ef66 100644 --- a/modules/gltf/src/lib/extensions/deprecated/EXT_feature_metadata.ts +++ b/modules/gltf/src/lib/extensions/deprecated/EXT_feature_metadata.ts @@ -1,24 +1,34 @@ /* eslint-disable camelcase */ +import type {GLTF, GLTFTextureInfoMetadata} from '../../types/gltf-json-schema'; import type { - GLTF, GLTF_EXT_feature_metadata_Class, GLTF_EXT_feature_metadata_ClassProperty, GLTF_EXT_feature_metadata_FeatureTable, GLTF_EXT_feature_metadata_FeatureTableProperty, GLTF_EXT_feature_metadata_FeatureTexture, GLTF_EXT_feature_metadata_GLTF, - GLTF_EXT_feature_metadata_TextureAccessor -} from '../../types/gltf-json-schema'; + GLTF_EXT_feature_metadata_TextureAccessor, + GLTF_EXT_feature_metadata_Schema +} from '../../types/gltf-ext-feature-metadata-schema'; +import type {BigTypedArray, TypedArray} from '@loaders.gl/schema'; import {GLTFScenegraph} from '../../api/gltf-scenegraph'; -import {getImageData} from '@loaders.gl/images'; import {GLTFMeshPrimitive} from '../../types/gltf-json-schema'; -import {getComponentTypeFromArray} from '../../gltf-utils/gltf-utils'; import {GLTFLoaderOptions} from '../../../gltf-loader'; +import { + convertRawBufferToMetadataArray, + getPrimitiveTextureData, + primitivePropertyDataToAttributes, + getArrayElementByteSize, + NumericComponentType, + getOffsetsForProperty, + parseVariableLengthArrayNumeric, + parseFixedLengthArrayNumeric, + getPropertyDataString +} from '../utils/3d-tiles-utils'; /** Extension name */ -const EXT_FEATURE_METADATA = 'EXT_feature_metadata'; - -export const name = EXT_FEATURE_METADATA; +const EXT_FEATURE_METADATA_NAME = 'EXT_feature_metadata'; +export const name = EXT_FEATURE_METADATA_NAME; export async function decode(gltfData: {json: GLTF}, options: GLTFLoaderOptions): Promise { const scenegraph = new GLTFScenegraph(gltfData); @@ -26,33 +36,49 @@ export async function decode(gltfData: {json: GLTF}, options: GLTFLoaderOptions) } /** - * Decodes feature metadata from extension - * @param scenegraph + * Decodes feature metadata from extension. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param options - GLTFLoader options. */ function decodeExtFeatureMetadata(scenegraph: GLTFScenegraph, options: GLTFLoaderOptions): void { + // Decoding metadata involves buffers processing. + // So, if buffers have not been loaded, there is no reason to process metadata. + if (!options.gltf?.loadBuffers) { + return; + } const extension: GLTF_EXT_feature_metadata_GLTF | null = - scenegraph.getExtension(EXT_FEATURE_METADATA); - if (!extension) return; + scenegraph.getExtension(EXT_FEATURE_METADATA_NAME); + if (!extension) { + return; + } - const schemaClasses = extension.schema?.classes; + if (options.gltf?.loadImages) { + decodePropertyTextures(scenegraph, extension); + } - const {featureTables} = extension; - if (schemaClasses && featureTables) { - for (const schemaName in schemaClasses) { - const schemaClass = schemaClasses[schemaName]; - const featureTable = findFeatureTableByName(featureTables, schemaName); + decodePropertyTables(scenegraph, extension); +} - if (featureTable) { - handleFeatureTableProperties(scenegraph, featureTable, schemaClass); - } - } +/** + * Processes the data stored in the textures + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param extension - Top-level extension. + */ +function decodePropertyTextures( + scenegraph: GLTFScenegraph, + extension: GLTF_EXT_feature_metadata_GLTF +): void { + const schema = extension.schema; + if (!schema) { + return; } + const schemaClasses = schema.classes; const {featureTextures} = extension; - if (schemaClasses && featureTextures && options.gltf?.loadImages) { + if (schemaClasses && featureTextures) { for (const schemaName in schemaClasses) { const schemaClass = schemaClasses[schemaName]; - const featureTexture = findFeatureTextureByName(featureTextures, schemaName); + const featureTexture = findFeatureTextureByClass(featureTextures, schemaName); if (featureTexture) { handleFeatureTextureProperties(scenegraph, featureTexture, schemaClass); @@ -62,37 +88,114 @@ function decodeExtFeatureMetadata(scenegraph: GLTFScenegraph, options: GLTFLoade } /** - * Navigate throw all properies in feature table and gets properties data. - * @param scenegraph - * @param featureTable - * @param schemaClass + * Processes the data stored in the property tables. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param extension - Top-level extension. */ -function handleFeatureTableProperties( +function decodePropertyTables( scenegraph: GLTFScenegraph, - featureTable: GLTF_EXT_feature_metadata_FeatureTable, - schemaClass: GLTF_EXT_feature_metadata_Class + extension: GLTF_EXT_feature_metadata_GLTF +): void { + const schema = extension.schema; + if (!schema) { + return; + } + const schemaClasses = schema.classes; + const propertyTables = extension.featureTables; + if (schemaClasses && propertyTables) { + for (const schemaName in schemaClasses) { + const propertyTable = findPropertyTableByClass(propertyTables, schemaName); + if (propertyTable) { + processPropertyTable(scenegraph, schema, propertyTable); + } + } + } +} + +/** + * Finds the property table by class name. + * @param propertyTables - propertyTable definition taken from the top-level extension. + * @param schemaClassName - class name in the extension schema. + */ +function findPropertyTableByClass( + propertyTables: {[key: string]: GLTF_EXT_feature_metadata_FeatureTable}, + schemaClassName: string +): GLTF_EXT_feature_metadata_FeatureTable | null { + for (const propertyTableName in propertyTables) { + const propertyTable = propertyTables[propertyTableName]; + if (propertyTable.class === schemaClassName) { + return propertyTable; + } + } + + return null; +} + +function findFeatureTextureByClass( + featureTextures: {[key: string]: GLTF_EXT_feature_metadata_FeatureTexture}, + schemaClassName: string +): GLTF_EXT_feature_metadata_FeatureTexture | null { + for (const featureTexturesName in featureTextures) { + const featureTable = featureTextures[featureTexturesName]; + + if (featureTable.class === schemaClassName) { + return featureTable; + } + } + + return null; +} + +/** + * Navigates through all properies in the property table, gets properties data, + * and put the data to `propertyTable.data` as an array. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param schema - schema object. + * @param propertyTable - propertyTable definition taken from the top-level extension. + */ +function processPropertyTable( + scenegraph: GLTFScenegraph, + schema: GLTF_EXT_feature_metadata_Schema, + propertyTable: GLTF_EXT_feature_metadata_FeatureTable ): void { + // Though 'class' is not required by spec, it doesn't make any scence when it's not provided. + // So, bale out here. + if (!propertyTable.class) { + return; + } + + const schemaClass = schema.classes?.[propertyTable.class]; + if (!schemaClass) { + throw new Error( + `Incorrect data in the EXT_structural_metadata extension: no schema class with name ${propertyTable.class}` + ); + } + + const numberOfElements = propertyTable.count; // `propertyTable.count` is a number of elements in each property array. + for (const propertyName in schemaClass.properties) { - const schemaProperty = schemaClass.properties[propertyName]; - const featureTableProperty = featureTable?.properties?.[propertyName]; - const numberOfFeatures = featureTable.count; + const classProperty = schemaClass.properties[propertyName]; + const propertyTableProperty: GLTF_EXT_feature_metadata_FeatureTableProperty | undefined = + propertyTable.properties?.[propertyName]; - if (featureTableProperty) { + if (propertyTableProperty) { + // Getting all elements (`numberOfElements`) of the array in the `propertyTableProperty` const data = getPropertyDataFromBinarySource( scenegraph, - schemaProperty, - numberOfFeatures, - featureTableProperty + schema, + classProperty, + numberOfElements, + propertyTableProperty ); - featureTableProperty.data = data; + propertyTableProperty.data = data; } } } /** - * Navigate throw all properies in feature texture and gets properties data. - * Data will be stored in featureTexture.properties[propertyName].data - * @param scenegraph + * Navigates through all properies in feature texture and gets properties data. + * Data will be stored in featureTexture.properties[propertyName].data. + * @param scenegraph - Instance of the class for structured access to GLTF data. * @param featureTexture * @param schemaClass */ @@ -114,38 +217,194 @@ function handleFeatureTextureProperties( } /** - * Decode properties from binary sourse based on property type. - * @param scenegraph + * Decodes properties from binary sourse based on property type. + * @param scenegraph - Instance of the class for structured access to GLTF data. * @param schemaProperty * @param numberOfFeatures * @param featureTableProperty */ function getPropertyDataFromBinarySource( scenegraph: GLTFScenegraph, - schemaProperty: GLTF_EXT_feature_metadata_ClassProperty, + schema: GLTF_EXT_feature_metadata_Schema, + classProperty: GLTF_EXT_feature_metadata_ClassProperty, numberOfFeatures: number, featureTableProperty: GLTF_EXT_feature_metadata_FeatureTableProperty -): Uint8Array | string[] { +): string[] | BigTypedArray | string[][] | BigTypedArray[] { + let data: string[] | BigTypedArray | string[][] | BigTypedArray[] = []; const bufferView = featureTableProperty.bufferView; - // TODO think maybe we shouldn't get data only in Uint8Array format. const dataArray: Uint8Array = scenegraph.getTypedArrayForBufferView(bufferView); - switch (schemaProperty.type) { - case 'STRING': { - // stringOffsetBufferView should be available for string type. - const stringOffsetBufferView = featureTableProperty.stringOffsetBufferView!; - const offsetsData = scenegraph.getTypedArrayForBufferView(stringOffsetBufferView); - return getStringAttributes(dataArray, offsetsData, numberOfFeatures); + const arrayOffsets = getArrayOffsetsForProperty( + scenegraph, + classProperty, + featureTableProperty, + numberOfFeatures + ); + const stringOffsets = getStringOffsetsForProperty( + scenegraph, + classProperty, + featureTableProperty, + numberOfFeatures + ); + + if (classProperty.type === 'STRING' || classProperty.componentType === 'STRING') { + data = getPropertyDataString(numberOfFeatures, dataArray, arrayOffsets, stringOffsets); + } else if (isNumericProperty(classProperty)) { + data = getPropertyDataNumeric(classProperty, numberOfFeatures, dataArray, arrayOffsets); + } + + return data; +} + +/** + * Parses propertyTable.property.arrayOffsets that are offsets of sub-arrays in a flatten array of values. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param classProperty - class property object. + * @param propertyTableProperty - propertyTable's property metadata. + * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table. + * @returns Typed array with offset values. + * @see https://github.com/CesiumGS/glTF/blob/2976f1183343a47a29e4059a70961371cd2fcee8/extensions/2.0/Vendor/EXT_structural_metadata/schema/propertyTable.property.schema.json#L21 + */ +function getArrayOffsetsForProperty( + scenegraph: GLTFScenegraph, + classProperty: GLTF_EXT_feature_metadata_ClassProperty, + propertyTableProperty: GLTF_EXT_feature_metadata_FeatureTableProperty, + numberOfElements: number +): TypedArray | null { + /* + If ARRAY is used, then componentType must also be specified. + ARRAY is a fixed-length array when componentCount is defined, and variable-length otherwise. +*/ + if ( + classProperty.type === 'ARRAY' && + // `componentCount` is a number of fixed-length array elements. + // If `componentCount` is NOT defined, it's a VARIABLE-length array + typeof classProperty.componentCount === 'undefined' && + // `arrayOffsetBufferView` is an index of the buffer view containing offsets for variable-length arrays. + typeof propertyTableProperty.arrayOffsetBufferView !== 'undefined' + ) { + // Data are in a VARIABLE-length array + return getOffsetsForProperty( + scenegraph, + propertyTableProperty.arrayOffsetBufferView, + propertyTableProperty.offsetType || 'UINT32', // offsetType is used both for stringOffsetBufferView and arrayOffsetBufferView + numberOfElements + ); + } + return null; +} + +/** + * Parses featureTable.property.stringOffsetBufferView. + * String offsets is an array of offsets of strings in the united array of characters. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param propertyTableProperty - propertyTable's property metadata. + * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table. + * @returns Typed array of offset values. The number of offsets in the array is equal to `numberOfElements` plus one. + * @see https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureTable.property.schema.json#L50C10-L50C32 + */ +function getStringOffsetsForProperty( + scenegraph: GLTFScenegraph, + classProperty: GLTF_EXT_feature_metadata_ClassProperty, + propertyTableProperty: GLTF_EXT_feature_metadata_FeatureTableProperty, + numberOfElements: number +): TypedArray | null { + if ( + typeof propertyTableProperty.stringOffsetBufferView !== 'undefined' // `stringOffsetBufferView` is an index of the buffer view containing offsets for strings. + ) { + // Data are in a FIXED-length array + return getOffsetsForProperty( + scenegraph, + propertyTableProperty.stringOffsetBufferView, + propertyTableProperty.offsetType || 'UINT32', // offsetType is used both for stringOffsetBufferView and arrayOffsetBufferView + numberOfElements + ); + } + return null; +} + +/** + * Checks if the feature table property is of numeric type. + * @param schemaPropertyType - feature table property + * @returns true if property is numeric, else - false + */ +function isNumericProperty(schemaProperty: GLTF_EXT_feature_metadata_ClassProperty): boolean { + const types = [ + 'UINT8', + 'INT16', + 'UINT16', + 'INT32', + 'UINT32', + 'INT64', + 'UINT64', + 'FLOAT32', + 'FLOAT64' + ]; + return ( + types.includes(schemaProperty.type) || + (typeof schemaProperty.componentType !== 'undefined' && + types.includes(schemaProperty.componentType)) + ); +} + +/** + * Decodes properties of numeric types from binary sourse. + * @param classProperty - class property object. + * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table. + * @param valuesDataBytes - Data taken from values property of the property table property. + * @param arrayOffsets - Offsets for variable-length arrays. It's null for fixed-length arrays or scalar types. + * @returns Property values in a typed array or in an array of typed arrays. + */ +function getPropertyDataNumeric( + classProperty: GLTF_EXT_feature_metadata_ClassProperty, + numberOfElements: number, + valuesDataBytes: Uint8Array, + arrayOffsets: TypedArray | null +): BigTypedArray | BigTypedArray[] { + const isArray = classProperty.type === 'ARRAY'; + const arrayCount = classProperty.componentCount; + + /* + We are getting Numeric data. So, + the component type can be one of NumericComponentType, + the attribute type should be 'SCALAR' + */ + const attributeType = 'SCALAR'; + const componentType = classProperty.componentType || classProperty.type; + const elementSize = getArrayElementByteSize(attributeType, componentType); + const elementCount = valuesDataBytes.byteLength / elementSize; + + const valuesData: BigTypedArray = convertRawBufferToMetadataArray( + valuesDataBytes, + attributeType, + componentType as NumericComponentType, + elementCount + ); + + if (isArray) { + if (arrayOffsets) { + // VARIABLE-length array + return parseVariableLengthArrayNumeric( + valuesData, + numberOfElements, + arrayOffsets, + valuesDataBytes.length, + elementSize + ); + } + if (arrayCount) { + // FIXED-length array + return parseFixedLengthArrayNumeric(valuesData, numberOfElements, arrayCount); } - default: + return []; } - return dataArray; + return valuesData; } /** - * Get properties from texture associated with all mesh primitives. - * @param scenegraph + * Gets properties from texture associated with all mesh primitives. + * @param scenegraph - Instance of the class for structured access to GLTF data. * @param featureTextureProperty * @param attributeName * @returns Feature texture data @@ -174,16 +433,14 @@ function getPropertyDataFromTexture( return featureTextureTable; } -// eslint-disable-next-line max-statements /** * Processes data encoded in the texture associated with the primitive. This data will be accessible through the attributes. - * @param scenegraph + * @param scenegraph - Instance of the class for structured access to GLTF data. * @param attributeName * @param featureTextureProperty * @param featureTextureTable * @param primitive */ -// eslint-disable-next-line max-statements function processPrimitiveTextures( scenegraph: GLTFScenegraph, attributeName: string, @@ -191,216 +448,23 @@ function processPrimitiveTextures( featureTextureTable: number[], primitive: GLTFMeshPrimitive ): void { - /* - texture.index is an index for the "textures" array. - The texture object referenced by this index looks like this: - { - "sampler": 0, - "source": 0 - } - "sampler" is an index for the "samplers" array - "source" is an index for the "images" array that contains data. These data are stored in rgba channels of the image. - - texture.texCoord is a number-suffix (like 1) for an attribute like "TEXCOORD_1" in meshes.primitives - The value of "TEXCOORD_1" is an accessor that is used to get coordinates. These coordinates ared used to get data from the image. - */ - const json = scenegraph.gltf.json; - const textureData: number[] = []; - const texCoordAccessorKey = `TEXCOORD_${featureTextureProperty.texture.texCoord}`; - const texCoordAccessorIndex = primitive.attributes[texCoordAccessorKey]; - const texCoordBufferView = scenegraph.getBufferView(texCoordAccessorIndex); - const texCoordArray: Uint8Array = scenegraph.getTypedArrayForBufferView(texCoordBufferView); - - const textureCoordinates: Float32Array = new Float32Array( - texCoordArray.buffer, - texCoordArray.byteOffset, - texCoordArray.length / 4 - ); - // textureCoordinates contains UV coordinates of the actual data stored in the texture - // accessor.count is a number of UV pairs (they are stored as VEC2) - - const textureIndex = featureTextureProperty.texture.index; - const texture = json.textures?.[textureIndex]; - const imageIndex = texture?.source; - if (typeof imageIndex !== 'undefined') { - const image = json.images?.[imageIndex]; - const mimeType = image?.mimeType; - const parsedImage = scenegraph.gltf.images?.[imageIndex]; - if (parsedImage) { - for (let index = 0; index < textureCoordinates.length; index += 2) { - const value = getImageValueByCoordinates( - parsedImage, - mimeType, - textureCoordinates, - index, - featureTextureProperty.channels - ); - textureData.push(value); - } - } - } - /* - featureTextureTable will contain unique values, e.g. - textureData = [24, 35, 28, 24] - featureTextureTable = [24, 35, 28] - featureIndices will contain indices hat refer featureTextureTable, e.g. - featureIndices = [0, 1, 2, 0] - */ - const featureIndices: number[] = []; - for (const texelData of textureData) { - let index = featureTextureTable.findIndex((item) => item === texelData); - if (index === -1) { - index = featureTextureTable.push(texelData) - 1; - } - featureIndices.push(index); - } - const typedArray = new Uint32Array(featureIndices); - const bufferIndex = - scenegraph.gltf.buffers.push({ - arrayBuffer: typedArray.buffer, - byteOffset: 0, - byteLength: typedArray.byteLength - }) - 1; - const bufferViewIndex = scenegraph.addBufferView(typedArray, bufferIndex, 0); - const accessorIndex = scenegraph.addAccessor(bufferViewIndex, { - size: 1, - componentType: getComponentTypeFromArray(typedArray), - count: typedArray.length - }); - primitive.attributes[attributeName] = accessorIndex; -} - -function getImageValueByCoordinates( - parsedImage: any, - mimeType: string | undefined, - textureCoordinates: Float32Array, - index: number, - channels: string -) { - const CHANNELS_MAP = { - r: {offset: 0, shift: 0}, - g: {offset: 1, shift: 8}, - b: {offset: 2, shift: 16}, - a: {offset: 3, shift: 24} + const textureInfoTopLevel: GLTFTextureInfoMetadata = { + channels: featureTextureProperty.channels, + ...featureTextureProperty.texture }; - - const u = textureCoordinates[index]; - const v = textureCoordinates[index + 1]; - - let components = 1; - if (mimeType && (mimeType.indexOf('image/jpeg') !== -1 || mimeType.indexOf('image/png') !== -1)) - components = 4; - const offset = coordinatesToOffset(u, v, parsedImage, components); - let value = 0; - for (const c of channels) { - const map = CHANNELS_MAP[c]; - const val = getVal(parsedImage, offset + map.offset); - value |= val << map.shift; - } - return value; -} - -function getVal(parsedImage: any, offset: number): number { - const imageData = getImageData(parsedImage); - if (imageData.data.length <= offset) { - throw new Error(`${imageData.data.length} <= ${offset}`); - } - return imageData.data[offset]; -} - -function coordinatesToOffset( - u: number, - v: number, - parsedImage: any, - componentsCount: number = 1 -): number { - const w = parsedImage.width; - const iX = emod(u) * (w - 1); - const indX = Math.round(iX); - - const h = parsedImage.height; - const iY = emod(v) * (h - 1); - const indY = Math.round(iY); - const components = parsedImage.components ? parsedImage.components : componentsCount; - // components is a number of channels in the image - const offset = (indY * w + indX) * components; - return offset; -} - -// The following is taken from tile-converter\src\i3s-converter\helpers\batch-ids-extensions.ts -/** - * Handle UVs if they are out of range [0,1]. - * @param n - * @param m - */ -function emod(n: number): number { - const a = ((n % 1) + 1) % 1; - return a; -} - -/** - * Find the feature table by class name. - * @param featureTables - * @param schemaClassName - */ -function findFeatureTableByName( - featureTables: {[key: string]: GLTF_EXT_feature_metadata_FeatureTable}, - schemaClassName: string -): GLTF_EXT_feature_metadata_FeatureTable | null { - for (const featureTableName in featureTables) { - const featureTable = featureTables[featureTableName]; - - if (featureTable.class === schemaClassName) { - return featureTable; - } - } - - return null; -} - -function findFeatureTextureByName( - featureTextures: {[key: string]: GLTF_EXT_feature_metadata_FeatureTexture}, - schemaClassName: string -): GLTF_EXT_feature_metadata_FeatureTexture | null { - for (const featureTexturesName in featureTextures) { - const featureTable = featureTextures[featureTexturesName]; - - if (featureTable.class === schemaClassName) { - return featureTable; - } - } - - return null; -} - -/** - * Getting string attributes from binary data. - * Spec - https://github.com/CesiumGS/3d-tiles/tree/main/specification/Metadata#strings - * @param data - * @param offsetsData - * @param stringsCount - */ -function getStringAttributes( - data: Uint8Array, - offsetsData: Uint8Array, - stringsCount: number -): string[] { - const stringsArray: string[] = []; - const textDecoder = new TextDecoder('utf8'); - - let stringOffset = 0; - const bytesPerStringSize = 4; - - for (let index = 0; index < stringsCount; index++) { - // TODO check if it is multiplication on bytesPerStringSize is valid operation? - const stringByteSize = - offsetsData[(index + 1) * bytesPerStringSize] - offsetsData[index * bytesPerStringSize]; - const stringData = data.subarray(stringOffset, stringByteSize + stringOffset); - const stringAttribute = textDecoder.decode(stringData); - - stringsArray.push(stringAttribute); - stringOffset += stringByteSize; + const propertyData: number[] | null = getPrimitiveTextureData( + scenegraph, + textureInfoTopLevel, + primitive + ); + if (!propertyData) { + return; } - - return stringsArray; + primitivePropertyDataToAttributes( + scenegraph, + attributeName, + propertyData, + featureTextureTable, + primitive + ); } diff --git a/modules/gltf/src/lib/extensions/utils/3d-tiles-utils.ts b/modules/gltf/src/lib/extensions/utils/3d-tiles-utils.ts new file mode 100644 index 0000000000..4053251389 --- /dev/null +++ b/modules/gltf/src/lib/extensions/utils/3d-tiles-utils.ts @@ -0,0 +1,430 @@ +/** + * loaders.gl, MIT license + * + * Shared code for 3DTiles extensions: + * * EXT_feature_metadata + * * EXT_mesh_features + * * EXT_structural_metadata + */ + +import type {GLTFTextureInfoMetadata, GLTFMeshPrimitive} from '../../types/gltf-json-schema'; +import type {BigTypedArray, TypedArray} from '@loaders.gl/schema'; +import type {ImageType} from '@loaders.gl/images'; + +import {GLTFScenegraph} from '../../api/gltf-scenegraph'; +import {getComponentTypeFromArray} from '../../gltf-utils/gltf-utils'; +import {getImageData} from '@loaders.gl/images'; +import {emod} from '@loaders.gl/math'; + +export type NumericComponentType = + | 'INT8' + | 'UINT8' + | 'INT16' + | 'UINT16' + | 'INT32' + | 'UINT32' + | 'INT64' + | 'UINT64' + | 'FLOAT32' + | 'FLOAT64'; + +const ATTRIBUTE_TYPE_TO_COMPONENTS = { + SCALAR: 1, + VEC2: 2, + VEC3: 3, + VEC4: 4, + MAT2: 4, + MAT3: 9, + MAT4: 16, + BOOLEAN: 1, + STRING: 1, + ENUM: 1 +}; + +const ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY = { + INT8: Int8Array, + UINT8: Uint8Array, + INT16: Int16Array, + UINT16: Uint16Array, + INT32: Int32Array, + UINT32: Uint32Array, + INT64: BigInt64Array, + UINT64: BigUint64Array, + FLOAT32: Float32Array, + FLOAT64: Float64Array +}; + +const ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE = { + INT8: 1, + UINT8: 1, + INT16: 2, + UINT16: 2, + INT32: 4, + UINT32: 4, + INT64: 8, + UINT64: 8, + FLOAT32: 4, + FLOAT64: 8 +}; + +export function getArrayElementByteSize(attributeType, componentType): number { + return ( + ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[componentType] * + ATTRIBUTE_TYPE_TO_COMPONENTS[attributeType] + ); +} + +/** + * Gets offset array from `arrayOffsets` or `stringOffsets`. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param bufferViewIndex - Buffer view index + * @param offsetType - The type of values in `arrayOffsets` or `stringOffsets`. + * @param numberOfElements - The number of elements in each property array. + * @returns Array of values offsets. The number of offsets in the array is equal to `numberOfElements` plus one. + */ +export function getOffsetsForProperty( + scenegraph: GLTFScenegraph, + bufferViewIndex: number, + offsetType: 'UINT8' | 'UINT16' | 'UINT32' | 'UINT64' | string, + numberOfElements: number +): TypedArray | null { + if ( + offsetType !== 'UINT8' && + offsetType !== 'UINT16' && + offsetType !== 'UINT32' && + offsetType !== 'UINT64' + ) { + return null; + } + const arrayOffsetsBytes = scenegraph.getTypedArrayForBufferView(bufferViewIndex); + const arrayOffsets = convertRawBufferToMetadataArray( + arrayOffsetsBytes, + 'SCALAR', // offsets consist of ONE component + offsetType, + numberOfElements + 1 // The number of offsets is equal to the property table `count` plus one. + ); + + // We don't support BigInt offsets at the moment. It requires additional logic and potential issues in Safari + if (arrayOffsets instanceof BigInt64Array || arrayOffsets instanceof BigUint64Array) { + return null; + } + return arrayOffsets; +} + +/** + * Converts raw bytes that are in the buffer to an array of the type defined by the schema. + * @param data - Raw bytes in the buffer. + * @param attributeType - SCALAR, VECN, MATN. + * @param componentType - Type of the component in elements, e.g. 'UINT8' or 'FLOAT32'. + * @param elementCount - Number of elements in the array. Default value is 1. + * @returns Data array + */ +export function convertRawBufferToMetadataArray( + data: Uint8Array, + attributeType: string, + componentType: NumericComponentType, + elementCount: number = 1 +): BigTypedArray { + const numberOfComponents = ATTRIBUTE_TYPE_TO_COMPONENTS[attributeType]; + const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[componentType]; + const size = ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[componentType]; + const length = elementCount * numberOfComponents; + const byteLength = length * size; + let buffer = data.buffer; + let offset = data.byteOffset; + if (offset % size !== 0) { + const bufferArray = new Uint8Array(buffer); + buffer = bufferArray.slice(offset, offset + byteLength).buffer; + offset = 0; + } + return new ArrayType(buffer, offset, length); +} + +/** + * Processes data encoded in the texture associated with the primitive. + * @param scenegraph - Instance of the class for structured access to GLTF data. + * @param textureInfo - Reference to the texture where extension data are stored. + * @param primitive - Primitive object in the mesh. + * @returns Array of data taken. Null if data can't be taken from the texture. + */ +export function getPrimitiveTextureData( + scenegraph: GLTFScenegraph, + textureInfo: GLTFTextureInfoMetadata, + primitive: GLTFMeshPrimitive +): number[] { + /* + texture.index is an index for the "textures" array. + The texture object referenced by this index looks like this: + { + "sampler": 0, + "source": 0 + } + "sampler" is an index for the "samplers" array + "source" is an index for the "images" array that contains data stored in rgba channels of the image. + + texture.texCoord is a number-suffix (like 1) for an attribute like "TEXCOORD_1" in meshes.primitives + The value of "TEXCOORD_1" is an accessor that is used to get coordinates. + These coordinates are being used to get data from the image. + + Default for texture.texCoord is 0 + @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/specification/2.0/schema/textureInfo.schema.json + */ + const texCoordAccessorKey = `TEXCOORD_${textureInfo.texCoord || 0}`; + const texCoordAccessorIndex = primitive.attributes[texCoordAccessorKey]; + const textureCoordinates: TypedArray = scenegraph.getTypedArrayForAccessor(texCoordAccessorIndex); + + const json = scenegraph.gltf.json; + const textureIndex: number = textureInfo.index; + const imageIndex = json.textures?.[textureIndex]?.source; + if (typeof imageIndex !== 'undefined') { + const mimeType = json.images?.[imageIndex]?.mimeType; + const parsedImage = scenegraph.gltf.images?.[imageIndex]; + // Checking for width is to prevent handling Un-processed images (e.g. [analyze] stage, where loadImages option is set to false) + if (parsedImage && typeof parsedImage.width !== 'undefined') { + const textureData: number[] = []; + for (let index = 0; index < textureCoordinates.length; index += 2) { + const value = getImageValueByCoordinates( + parsedImage, + mimeType, + textureCoordinates, + index, + textureInfo.channels + ); + textureData.push(value); + } + return textureData; + } + } + return []; +} + +/** + * Puts property data to attributes. + * It creates corresponding buffer, bufferView and accessor + * so the data can be accessed like regular data stored in buffers. + * @param scenegraph - Scenegraph object. + * @param attributeName - Name of the attribute. + * @param propertyData - Property data to store. + * @param featureTable - Array where unique data from the property data are being stored. + * @param primitive - Primitive object. + */ +export function primitivePropertyDataToAttributes( + scenegraph: GLTFScenegraph, + attributeName: string, + propertyData: number[], + featureTable: number[], + primitive: GLTFMeshPrimitive +): void { + // No reason to create an empty buffer if there is no property data to store. + if (!propertyData?.length) { + return; + } + /* + featureTable will contain unique values, e.g. + propertyData = [24, 35, 28, 24] + featureTable = [24, 35, 28] + featureIndices will contain indices that refer featureTextureTable, e.g. + featureIndices = [0, 1, 2, 0] + */ + const featureIndices: number[] = []; + for (const texelData of propertyData) { + let index = featureTable.findIndex((item) => item === texelData); + if (index === -1) { + index = featureTable.push(texelData) - 1; + } + featureIndices.push(index); + } + const typedArray = new Uint32Array(featureIndices); + const bufferIndex = + scenegraph.gltf.buffers.push({ + arrayBuffer: typedArray.buffer, + byteOffset: typedArray.byteOffset, + byteLength: typedArray.byteLength + }) - 1; + const bufferViewIndex = scenegraph.addBufferView(typedArray, bufferIndex, 0); + const accessorIndex = scenegraph.addAccessor(bufferViewIndex, { + size: 1, + componentType: getComponentTypeFromArray(typedArray), + count: typedArray.length + }); + primitive.attributes[attributeName] = accessorIndex; +} + +/** + * Gets the value from the texture by coordinates provided. + * @param parsedImage - Image where the data are stored. + * @param mimeType - MIME type. + * @param textureCoordinates - uv coordinates to access data in the image. + * @param index - Index of uv coordinates in the array textureCoordinates. + * @param channels - Image channels where data are stored. + * Channels of an RGBA texture are numbered 0..3 respectively. + * For Ext_mesh_features and EXT_strucural_metadata the channels default is [0] + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features/schema/featureIdTexture.schema.json + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/propertyTexture.property.schema.json + * @returns Value taken from the image. + */ +function getImageValueByCoordinates( + parsedImage: ImageType, + mimeType: string | undefined, + textureCoordinates: TypedArray, + index: number, + channels: number[] | string = [0] +) { + const CHANNELS_MAP = { + r: {offset: 0, shift: 0}, + g: {offset: 1, shift: 8}, + b: {offset: 2, shift: 16}, + a: {offset: 3, shift: 24} + }; + + const u = textureCoordinates[index]; + const v = textureCoordinates[index + 1]; + + let components = 1; + if (mimeType && (mimeType.indexOf('image/jpeg') !== -1 || mimeType.indexOf('image/png') !== -1)) + components = 4; + const offset = coordinatesToOffset(u, v, parsedImage, components); + let value: number = 0; + for (const c of channels) { + /* + According to the EXT_feature_metadata extension specification: + Channels are labeled by rgba and are swizzled with a string of 1-4 characters. + According to the EXT_mesh_features extension specification: + The channels array contains non-negative integer values corresponding to channels of the source texture that the feature ID consists of. + Channels of an RGBA texture are numbered 0–3 respectively. + Function getImageValueByCoordinates is used to process both extensions. + So, there should be possible to get the element of CHANNELS_MAP by either index (0, 1, 2, 3) or key (r, g, b, a). + */ + const map = typeof c === 'number' ? Object.values(CHANNELS_MAP)[c] : CHANNELS_MAP[c]; + const imageOffset = offset + map.offset; + const imageData = getImageData(parsedImage); + if (imageData.data.length <= imageOffset) { + throw new Error(`${imageData.data.length} <= ${imageOffset}`); + } + const imageValue = imageData.data[imageOffset]; + value |= imageValue << map.shift; + } + return value; +} + +/** + * Retrieves the offset in the image where the data are stored. + * @param u - u-coordinate. + * @param v - v-coordinate. + * @param parsedImage - Image where the data are stored. + * @param componentsCount - Number of components the data consists of. + * @returns Offset in the image where the data are stored. + */ +function coordinatesToOffset( + u: number, + v: number, + parsedImage: any, + componentsCount: number = 1 +): number { + const w = parsedImage.width; + const iX = emod(u) * (w - 1); + const indX = Math.round(iX); + + const h = parsedImage.height; + const iY = emod(v) * (h - 1); + const indY = Math.round(iY); + const components = parsedImage.components ? parsedImage.components : componentsCount; + // components is a number of channels in the image + const offset = (indY * w + indX) * components; + return offset; +} + +/** + * Parses variable-length array data. + * In this case every value of the property in the table will be an array + * of arbitrary length. + * @param valuesData - Values in a flat typed array. + * @param numberOfElements - Number of rows in the property table. + * @param arrayOffsets - Offsets of nested arrays in the flat values array. + * @param valuesDataBytesLength - Data byte length. + * @param valueSize - Value size in bytes. + * @returns Array of typed arrays. + */ +export function parseVariableLengthArrayNumeric( + valuesData: BigTypedArray, + numberOfElements: number, + arrayOffsets: TypedArray, + valuesDataBytesLength: number, + valueSize: number +): BigTypedArray[] { + const attributeValueArray: BigTypedArray[] = []; + for (let index = 0; index < numberOfElements; index++) { + const arrayOffset = arrayOffsets[index]; + const arrayByteSize = arrayOffsets[index + 1] - arrayOffsets[index]; + if (arrayByteSize + arrayOffset > valuesDataBytesLength) { + break; + } + const typedArrayOffset = arrayOffset / valueSize; + const elementCount = arrayByteSize / valueSize; + attributeValueArray.push(valuesData.slice(typedArrayOffset, typedArrayOffset + elementCount)); + } + return attributeValueArray; +} + +/** + * Parses fixed-length array data. + * In this case every value of the property in the table will be an array + * of constant length equal to `arrayCount`. + * @param valuesData - Values in a flat typed array. + * @param numberOfElements - Number of rows in the property table. + * @param arrayCount - Nested arrays length. + * @returns Array of typed arrays. + */ +export function parseFixedLengthArrayNumeric( + valuesData: BigTypedArray, + numberOfElements: number, + arrayCount: number +): BigTypedArray[] { + const attributeValueArray: BigTypedArray[] = []; + for (let index = 0; index < numberOfElements; index++) { + const elementOffset = index * arrayCount; + attributeValueArray.push(valuesData.slice(elementOffset, elementOffset + arrayCount)); + } + return attributeValueArray; +} + +/** + * Decodes properties of string type from binary source. + * @param numberOfElements - The number of elements in each property array that propertyTableProperty contains. It's a number of rows in the table. + * @param valuesDataBytes - Data taken from values property of the property table property. + * @param arrayOffsets - Offsets for variable-length arrays. It's null for fixed-length arrays or scalar types. + * @param stringOffsets - Index of the buffer view containing offsets for strings. It should be available for string type. + * @returns String property values + */ +export function getPropertyDataString( + numberOfElements: number, + valuesDataBytes: Uint8Array, + arrayOffsets: TypedArray | null, + stringOffsets: TypedArray | null +): string[] | string[][] { + if (arrayOffsets) { + // TODO: implement it as soon as we have the corresponding tileset + throw new Error('Not implemented - arrayOffsets for strings is specified'); + } + + if (stringOffsets) { + const stringsArray: string[] = []; + const textDecoder = new TextDecoder('utf8'); + + let stringOffset = 0; + for (let index = 0; index < numberOfElements; index++) { + const stringByteSize = stringOffsets[index + 1] - stringOffsets[index]; + + if (stringByteSize + stringOffset <= valuesDataBytes.length) { + const stringData = valuesDataBytes.subarray(stringOffset, stringByteSize + stringOffset); + const stringAttribute = textDecoder.decode(stringData); + + stringsArray.push(stringAttribute); + stringOffset += stringByteSize; + } + } + + return stringsArray; + } + return []; +} diff --git a/modules/gltf/src/lib/gltf-utils/get-typed-array.ts b/modules/gltf/src/lib/gltf-utils/get-typed-array.ts index be4d5ad03d..ed029f9785 100644 --- a/modules/gltf/src/lib/gltf-utils/get-typed-array.ts +++ b/modules/gltf/src/lib/gltf-utils/get-typed-array.ts @@ -1,5 +1,8 @@ // TODO - GLTFScenegraph should use these import {assert} from '../utils/assert'; +import type {TypedArray} from '@loaders.gl/schema'; +import type {GLTF, GLTFExternalBuffer, GLTFAccessor} from '../types/gltf-types'; +import {getAccessorArrayTypeAndLength} from './gltf-utils'; // accepts buffer view index or buffer view object // returns a `Uint8Array` @@ -24,18 +27,54 @@ export function getTypedArrayForImageData(json, buffers, imageIndex) { return getTypedArrayForBufferView(json, buffers, bufferViewIndex); } -/* -// accepts accessor index or accessor object -// returns a typed array with type that matches the types -export function getTypedArrayForAccessor(accessor) { - accessor = this.getAccessor(accessor); - const bufferView = this.getBufferView(accessor.bufferView); - const buffer = this.getBuffer(bufferView.buffer); - const arrayBuffer = buffer.data; - - // Create a new typed array as a view into the combined buffer - const {ArrayType, length} = getAccessorArrayTypeAndLength(accessor, bufferView); - const byteOffset = bufferView.byteOffset + accessor.byteOffset; - return new ArrayType(arrayBuffer, byteOffset, length); +/** + * Gets data pointed by the accessor. + * @param json - json part of gltf content of a GLTF tile. + * @param buffers - Array containing buffers of data. + * @param accessor - accepts accessor index or accessor object. + * @returns {TypedArray} Typed array with type matching the type of data poited by the accessor. + */ +// eslint-disable-next-line complexity +export function getTypedArrayForAccessor( + json: GLTF, + buffers: GLTFExternalBuffer[], + accessor: GLTFAccessor | number +): TypedArray { + const gltfAccessor = typeof accessor === 'number' ? json.accessors?.[accessor] : accessor; + if (!gltfAccessor) { + throw new Error(`No gltf accessor ${JSON.stringify(accessor)}`); + } + const bufferView = json.bufferViews?.[gltfAccessor.bufferView || 0]; + if (!bufferView) { + throw new Error(`No gltf buffer view for accessor ${bufferView}`); + } + // Get `arrayBuffer` the `bufferView` looks at + const {arrayBuffer, byteOffset: bufferByteOffset} = buffers[bufferView.buffer]; + // Resulting byteOffset is sum of the buffer, accessor and bufferView byte offsets + const byteOffset = + (bufferByteOffset || 0) + (gltfAccessor.byteOffset || 0) + (bufferView.byteOffset || 0); + // Deduce TypedArray type and its length from `accessor` and `bufferView` data + const {ArrayType, length, componentByteSize, numberOfComponentsInElement} = + getAccessorArrayTypeAndLength(gltfAccessor, bufferView); + // 'length' is a whole number of components of all elements in the buffer pointed by the accessor + // Multiplier to calculate the address of the element in the arrayBuffer + const elementByteSize = componentByteSize * numberOfComponentsInElement; + const elementAddressScale = bufferView.byteStride || elementByteSize; + // Creare an array of component's type where all components (not just elements) will reside + if (typeof bufferView.byteStride === 'undefined' || bufferView.byteStride === elementByteSize) { + // No iterleaving + const result: TypedArray = new ArrayType(arrayBuffer, byteOffset, length); + return result; + } + // Iterleaving + const result: TypedArray = new ArrayType(length); + for (let i = 0; i < gltfAccessor.count; i++) { + const values = new ArrayType( + arrayBuffer, + byteOffset + i * elementAddressScale, + numberOfComponentsInElement + ); + result.set(values, i * numberOfComponentsInElement); + } + return result; } -*/ diff --git a/modules/gltf/src/lib/gltf-utils/gltf-utils.ts b/modules/gltf/src/lib/gltf-utils/gltf-utils.ts index e05bae3eb4..ad57595178 100644 --- a/modules/gltf/src/lib/gltf-utils/gltf-utils.ts +++ b/modules/gltf/src/lib/gltf-utils/gltf-utils.ts @@ -1,5 +1,7 @@ import {assert} from '../utils/assert'; + import type {GLTFPostprocessed} from '../types/gltf-postprocessed-schema'; +import {BYTES, COMPONENTS} from '../gltf-utils/gltf-constants'; /** * Memory needed to store texture and all mipmap levels 1 + 1/4 + 1/16 + 1/64 + ... @@ -16,8 +18,6 @@ type TypedArrayConstructor = | Uint16ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor - | Int32ArrayConstructor - | Uint32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor; @@ -82,7 +82,9 @@ export function getAccessorArrayTypeAndLength(accessor, bufferView) { const length = accessor.count * components; const byteLength = accessor.count * components * bytesPerComponent; assert(byteLength >= 0 && byteLength <= bufferView.byteLength); - return {ArrayType, length, byteLength}; + const componentByteSize = BYTES[accessor.componentType]; + const numberOfComponentsInElement = COMPONENTS[accessor.type]; + return {ArrayType, length, byteLength, componentByteSize, numberOfComponentsInElement}; } /** diff --git a/modules/gltf/src/lib/parsers/parse-gltf.ts b/modules/gltf/src/lib/parsers/parse-gltf.ts index 6c3c184d8f..dfa4d747a8 100644 --- a/modules/gltf/src/lib/parsers/parse-gltf.ts +++ b/modules/gltf/src/lib/parsers/parse-gltf.ts @@ -5,7 +5,8 @@ import type {GLTFWithBuffers} from '../types/gltf-types'; import type {GLB} from '../types/glb-types'; import type {ParseGLBOptions} from './parse-glb'; -import {parseJSON, sliceArrayBuffer} from '@loaders.gl/loader-utils'; +import type {ImageType, TextureLevel} from '@loaders.gl/schema'; +import {parseJSON, sliceArrayBuffer, parseFromContext} from '@loaders.gl/loader-utils'; import {ImageLoader} from '@loaders.gl/images'; import {BasisLoader, selectSupportedBasisFormat} from '@loaders.gl/textures'; @@ -23,9 +24,8 @@ export type ParseGLTFOptions = ParseGLBOptions & { loadBuffers?: boolean; decompressMeshes?: boolean; excludeExtensions?: string[]; - /** @deprecated not supported in v4. `postProcessGLTF()` must be called by the application */ - postProcess?: false; + postProcess?: never; }; /** Check if an array buffer appears to contain GLTF data */ @@ -198,13 +198,14 @@ async function loadImage( options, context: LoaderContext ) { - const {fetch, parse} = context; - let arrayBuffer; if (image.uri && !image.hasOwnProperty('bufferView')) { const uri = resolveUrl(image.uri, options); + + const {fetch} = context; const response = await fetch(uri); + arrayBuffer = await response.arrayBuffer(); image.bufferView = { data: arrayBuffer @@ -219,16 +220,21 @@ async function loadImage( assert(arrayBuffer, 'glTF image has no data'); // Call `parse` - let parsedImage = await parse( + let parsedImage = (await parseFromContext( arrayBuffer, [ImageLoader, BasisLoader], - {mimeType: image.mimeType, basis: options.basis || {format: selectSupportedBasisFormat()}}, + { + ...options, + mimeType: image.mimeType, + basis: options.basis || {format: selectSupportedBasisFormat()} + }, context - ); + )) as ImageType | TextureLevel[][]; if (parsedImage && parsedImage[0]) { parsedImage = { compressed: true, + // @ts-expect-error mipmaps: false, width: parsedImage[0].width, height: parsedImage[0].height, @@ -240,5 +246,6 @@ async function loadImage( // Store the loaded image gltf.images = gltf.images || []; + // @ts-expect-error TODO - sort out image typing asap gltf.images[index] = parsedImage; } diff --git a/modules/gltf/src/lib/types/gltf-ext-feature-metadata-schema.ts b/modules/gltf/src/lib/types/gltf-ext-feature-metadata-schema.ts new file mode 100644 index 0000000000..ef075034e2 --- /dev/null +++ b/modules/gltf/src/lib/types/gltf-ext-feature-metadata-schema.ts @@ -0,0 +1,470 @@ +import {GLTFTextureInfo} from './gltf-json-schema'; + +/* eslint-disable camelcase */ + +/** + * EXT_feature_metadata extension types + * This extension has glTF-level metadata and primitive-level feature indexing and segmentation metadata + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata + * + * glTF-level metadata + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#gltf-extension-1 + * JSON Schema - https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata/schema/glTF.EXT_feature_metadata.schema.json + */ +export type GLTF_EXT_feature_metadata_GLTF = { + /** An object defining classes and enums. */ + schema?: GLTF_EXT_feature_metadata_Schema; + /** A uri to an external schema file. */ + schemaUri?: string; + /** An object containing statistics about features. */ + statistics?: GLTF_EXT_feature_metadata_Statistics; + /** A dictionary, where each key is a feature table ID and each value is an object defining the feature table. */ + featureTables?: { + [key: string]: GLTF_EXT_feature_metadata_FeatureTable; + }; + /** A dictionary, where each key is a feature texture ID and each value is an object defining the feature texture. */ + featureTextures?: { + [key: string]: GLTF_EXT_feature_metadata_FeatureTexture; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * An object defining classes and enums. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#schema + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/schema.schema.json + */ +export type GLTF_EXT_feature_metadata_Schema = { + /** The name of the schema. */ + name?: string; + /** The description of the schema. */ + description?: string; + /** Application-specific version of the schema. */ + version?: string; + /** A dictionary, where each key is a class ID and each value is an object defining the class. */ + classes?: { + [key: string]: GLTF_EXT_feature_metadata_Class; + }; + /** A dictionary, where each key is an enum ID and each value is an object defining the values for the enum. */ + enums?: { + [key: string]: GLTF_EXT_feature_metadata_Enum; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * A class containing a set of properties. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#class + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/class.schema.json + */ +export type GLTF_EXT_feature_metadata_Class = { + /** The name of the class, e.g. for display purposes. */ + name?: string; + /** The description of the class. */ + description?: string; + /** A dictionary, where each key is a property ID and each value is an object defining the property. */ + properties: { + [key: string]: GLTF_EXT_feature_metadata_ClassProperty; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * A class property. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#class-property + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/class.property.schema.json + */ +export type GLTF_EXT_feature_metadata_ClassProperty = { + /** The name of the property, e.g. for display purposes. */ + name?: string; + /** The description of the property. */ + description?: string; + /** + * The property type. If ENUM is used, then enumType must also be specified. + * If ARRAY is used, then componentType must also be specified. + * ARRAY is a fixed-length array when componentCount is defined, and variable-length otherwise. + */ + type: + | 'INT8' + | 'UINT8' + | 'INT16' + | 'UINT16' + | 'INT32' + | 'UINT32' + | 'INT64' + | 'UINT64' + | 'FLOAT32' + | 'FLOAT64' + | 'BOOLEAN' + | 'STRING' + | 'ENUM' + | 'ARRAY' + | string; + /** + * An enum ID as declared in the enums dictionary. + * This value must be specified when type or componentType is ENUM. + */ + enumType?: string; + /** + * When type is ARRAY this indicates the type of each component of the array. + * If ENUM is used, then enumType must also be specified. + */ + componentType?: + | 'INT8' + | 'UINT8' + | 'INT16' + | 'UINT16' + | 'INT32' + | 'UINT32' + | 'INT64' + | 'UINT64' + | 'FLOAT32' + | 'FLOAT64' + | 'BOOLEAN' + | 'STRING' + | 'ENUM' + | string; + /** The number of components per element for ARRAY elements. */ + componentCount?: number; + /** + * Specifies whether integer values are normalized. + * This applies both when type is an integer type, or when type is ARRAY with a componentType that is an integer type. + * For unsigned integer types, values are normalized between [0.0, 1.0]. + * For signed integer types, values are normalized between [-1.0, 1.0]. + * For all other types, this property is ignored. + */ + normalized: boolean; + /** + * Maximum allowed values for property values. + * Only applicable for numeric types and fixed-length arrays of numeric types. + * For numeric types this is a single number. + * For fixed-length arrays this is an array with componentCount number of elements. + * The normalized property has no effect on these values: they always correspond to the integer values. + */ + max?: number | number[]; + /** + * Minimum allowed values for property values. + * Only applicable for numeric types and fixed-length arrays of numeric types. + * For numeric types this is a single number. + * For fixed-length arrays this is an array with componentCount number of elements. + * The normalized property has no effect on these values: they always correspond to the integer values. + */ + min?: number | number[]; + /** + * A default value to use when the property value is not defined. + * If used, optional must be set to true. + * The type of the default value must match the property definition: For BOOLEAN use true or false. + * For STRING use a JSON string. For a numeric type use a JSON number. + * For ENUM use the enum name, not the integer value. + * For ARRAY use a JSON array containing values matching the componentType. + */ + default?: boolean | number | string | number[]; + /** If true, this property is optional. */ + optional?: boolean; // default false; + /** + * An identifier that describes how this property should be interpreted. + * The semantic cannot be used by other properties in the class. + */ + semantic?: string; + extensions?: Record; + extras?: unknown; +}; + +/** + * An object defining the values of an enum. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#enum + * JSON Schema - https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata/schema/enum.schema.json + */ +export type GLTF_EXT_feature_metadata_Enum = { + /** The name of the enum, e.g. for display purposes. */ + name?: string; + /** The description of the enum. */ + description?: string; + /** The type of the integer enum value. */ + valueType?: 'INT8' | 'UINT8' | 'INT16' | 'UINT16' | 'INT32' | 'UINT32' | 'INT64' | 'UINT64'; // default: "UINT16" + /** An array of enum values. Duplicate names or duplicate integer values are not allowed. */ + values: GLTF_EXT_feature_metadata_EnumValue[]; + extensions?: Record; + extras?: unknown; + [key: string]: unknown; +}; + +/** + * An enum value. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#enum-value + * JSON Schema - https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata/schema/enum.value.schema.json + */ +export type GLTF_EXT_feature_metadata_EnumValue = { + /** The name of the enum value. */ + name: string; + /** The description of the enum value. */ + description?: string; + /** The integer enum value. */ + value: number; // default: "UINT16" + extensions?: Record; + extras?: unknown; + [key: string]: unknown; +}; + +/** + * A feature table defined by a class and property values stored in arrays. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#feature-table + * JSON Schenma - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureTable.schema.json + */ +export type GLTF_EXT_feature_metadata_FeatureTable = { + /** The class that property values conform to. The value must be a class ID declared in the classes dictionary. */ + class?: string; + /** The number of features, as well as the number of elements in each property array. */ + count: number; + /** + * A dictionary, where each key corresponds to a property ID in the class properties dictionary + * and each value is an object describing where property values are stored. + * Optional properties may be excluded from this dictionary. + */ + properties?: { + [key: string]: GLTF_EXT_feature_metadata_FeatureTableProperty; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * An array of binary property values. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#feature-table-property + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureTable.property.schema.json + */ +export type GLTF_EXT_feature_metadata_FeatureTableProperty = { + /** + * The index of the buffer view containing property values. + * The data type of property values is determined by the property definition: + * When type is BOOLEAN values are packed into a bitfield. + * When type is STRING values are stored as byte sequences and decoded as UTF-8 strings. + * When type is a numeric type values are stored as the provided type. + * When type is ENUM values are stored as the enum's valueType. + * Each enum value in the buffer must match one of the allowed values in the enum definition. + * When type is ARRAY elements are packed tightly together and the data type is based on the componentType following the same rules as above. + * arrayOffsetBufferView is required for variable-size arrays + * and stringOffsetBufferView is required for strings (for variable-length arrays of strings, both are required) + * The buffer view byteOffset must be aligned to a multiple of 8 bytes. + * If the buffer view's buffer is the GLB-stored BIN chunk the byte offset is measured relative to the beginning of the GLB. + * Otherwise it is measured relative to the beginning of the buffer. + */ + bufferView: number; + + /** The type of values in arrayOffsetBufferView and stringOffsetBufferView. */ + offsetType?: 'UINT8' | 'UINT16' | 'UINT32' | 'UINT64' | string; // default: "UINT32" + /** + * The index of the buffer view containing offsets for variable-length arrays. + * The number of offsets is equal to the feature table count plus one. + * The offsets represent the start positions of each array, with the last offset representing the position after the last array. + * The array length is computed using the difference between the current offset and the subsequent offset. + * If componentType is STRING the offsets index into the string offsets array (stored in stringOffsetBufferView), + * otherwise they index into the property array (stored in bufferView). + * The data type of these offsets is determined by offsetType. + * The buffer view byteOffset must be aligned to a multiple of 8 bytes in the same manner as the main bufferView + */ + arrayOffsetBufferView?: number; + /** + * The index of the buffer view containing offsets for strings. + * The number of offsets is equal to the number of string components plus one. + * The offsets represent the byte offsets of each string in the main bufferView, + * with the last offset representing the byte offset after the last string. + * The string byte length is computed using the difference between the current offset and the subsequent offset. + * The data type of these offsets is determined by offsetType. + * The buffer view byteOffset must be aligned to a multiple of 8 bytes in the same manner as the main bufferView. + */ + stringOffsetBufferView?: number; + extensions?: Record; + extras?: unknown; + /** This is not part of the spec. GLTFLoader loads feature tables data into this property */ + data: unknown; +}; + +/** + * Features whose property values are stored directly in texture channels. This is not to be confused with feature ID textures which store feature IDs for use with a feature table. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#feature-texture + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureTexture.schema.json + */ +export type GLTF_EXT_feature_metadata_FeatureTexture = { + /** The class this feature texture conforms to. The value must be a class ID declared in the classes dictionary. */ + class: string; + /** + * A dictionary, where each key corresponds to a property ID in the class properties dictionary + * and each value describes the texture channels containing property values. + */ + properties: { + [key: string]: GLTF_EXT_feature_metadata_TextureAccessor; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * A description of how to access property values from the color channels of a texture. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#texture-accessor + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/textureAccessor.schema.json + */ +export type GLTF_EXT_feature_metadata_TextureAccessor = { + /** Texture channels containing property values. Channels are labeled by rgba and are swizzled with a string of 1-4 characters. */ + channels: string; + /** The glTF texture and texture coordinates to use. */ + texture: GLTFTextureInfo; + extensions?: Record; + extras?: unknown; + /** This is not part of the spec. GLTFLoader loads feature tables data into this property */ + data: unknown; +}; + +/** + * Statistics about features. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#statistics-1 + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/statistics.schema.json + */ +export type GLTF_EXT_feature_metadata_Statistics = { + /** + * A dictionary, where each key is a class ID declared in the classes dictionary + * and each value is an object containing statistics about features that conform to the class. + */ + classes?: { + [key: string]: GLTF_EXT_feature_metadata_StatisticsClass; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * Statistics about features that conform to the class. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#class-statistics + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/statistics.class.property.schema.json + */ +export type GLTF_EXT_feature_metadata_StatisticsClass = { + /** The number of features that conform to the class. */ + count?: number; + /** + * A dictionary, where each key is a class ID declared in the classes dictionary + * and each value is an object containing statistics about property values. + */ + properties?: { + [key: string]: GLTF_EXT_feature_metadata_StatisticsClassProperty; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * min, max, mean, median, standardDeviation, variance, sum are + * only applicable for numeric types and fixed-length arrays of numeric types. + * For numeric types this is a single number. + * For fixed-length arrays this is an array with componentCount number of elements. + * The normalized property has no effect on these values. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#property-statistics + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/statistics.class.property.schema.json + */ +export type GLTF_EXT_feature_metadata_StatisticsClassProperty = { + /** The minimum property value. */ + min?: number | number[]; + /** The maximum property value. */ + max?: number | number[]; + /** The arithmetic mean of the property values. */ + mean?: number | number[]; + /** The median of the property values. */ + median?: number | number[]; + /** The standard deviation of the property values. */ + standardDeviation?: number | number[]; + /** The variance of the property values. */ + variance?: number | number[]; + /** The sum of the property values. */ + sum?: number | number[]; + /** + * A dictionary, where each key corresponds to an enum name and each value is the number of occurrences of that enum. + * Only applicable when type or componentType is ENUM. + * For fixed-length arrays, this is an array with componentCount number of elements. + */ + occurrences: { + [key: string]: number | number[]; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * EXT_feature_metadata extension types + * This extension has glTF-level metadata and primitive-level (feature indexing and segmentation) metadata + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata + * + * primitive-level metadata + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#primitive-extension + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/mesh.primitive.EXT_feature_metadata.schema.json + */ +export type GLTF_EXT_feature_metadata_Primitive = { + /** Feature ids definition in attributes */ + featureIdAttributes?: GLTF_EXT_feature_metadata_FeatureIdAttribute[]; + /** Feature ids definition in textures */ + featureIdTextures?: GLTF_EXT_feature_metadata_FeatureIdTexture[]; + /** An array of IDs of feature textures from the root EXT_feature_metadata object. */ + featureTextures?: string[]; + extensions?: Record; + extras?: unknown; +}; + +/** + * Attribute which described featureIds definition. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#feature-id-attribute + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureIdAttribute.schema.json + */ +export type GLTF_EXT_feature_metadata_FeatureIdAttribute = { + /** Name of feature table */ + featureTable: string; + /** Described how feature ids are defined */ + featureIds: GLTF_EXT_feature_metadata_FeatureIdAttributeFeatureIds; + extensions?: Record; + extras?: unknown; +}; + +/** + * Defining featureIds by attributes or implicitly. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#primitive-extensionfeatureidattributes + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureIdAttribute.featureIds.schema.json + */ +export type GLTF_EXT_feature_metadata_FeatureIdAttributeFeatureIds = { + /** Name of attribute where featureIds are defined */ + attribute?: string; + /** Sets a constant feature ID for each vertex. The default is 0. */ + constant?: number; + /** Sets the rate at which feature IDs increment. + * If divisor is zero then constant is used. + * If divisor is greater than zero the feature ID increments once per divisor sets of vertices, starting at constant. + * The default is 0 + */ + divisor?: number; +}; + +/** + * An object describing a texture used for storing per-texel feature IDs. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#feature-id-texture + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureIdTexture.schema.json + */ +export type GLTF_EXT_feature_metadata_FeatureIdTexture = { + /** The ID of the feature table in the model's root `EXT_feature_metadata.featureTables` dictionary. */ + featureTable: string; + /** A description of the texture and channel to use for feature IDs. The `channels` property must have a single channel. Furthermore, + * feature IDs must be whole numbers in the range `[0, count - 1]` (inclusive), where `count` is the total number of features + * in the feature table. Texel values must be read as integers. Texture filtering should be disabled when fetching feature IDs. + */ + featureIds: GLTF_EXT_feature_metadata_FeatureIdTextureAccessor; +}; + +/** + * A description of how to access property values from the color channels of a texture. + * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#featureidtexturefeatureids + * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/textureAccessor.schema.json + */ +export type GLTF_EXT_feature_metadata_FeatureIdTextureAccessor = { + /** gLTF textureInfo object - https://github.com/CesiumGS/glTF/blob/3d-tiles-next/specification/2.0/schema/textureInfo.schema.json */ + texture: GLTFTextureInfo; + /** Must be a single channel ("r", "g", "b", or "a") */ + channels: 'r' | 'g' | 'b' | 'a'; +}; diff --git a/modules/gltf/src/lib/types/gltf-ext-mesh-features-schema.ts b/modules/gltf/src/lib/types/gltf-ext-mesh-features-schema.ts new file mode 100644 index 0000000000..930819df1b --- /dev/null +++ b/modules/gltf/src/lib/types/gltf-ext-mesh-features-schema.ts @@ -0,0 +1,48 @@ +import {GLTFTextureInfoMetadata} from './gltf-json-schema'; +/* eslint-disable camelcase */ + +/** + * EXT_mesh_features extension types + * This is a primitive-level extension. + * An object describing feature IDs for a mesh primitive. + * @see https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features + * or https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_mesh_features/schema/mesh.primitive.EXT_mesh_features.schema.json + */ +export type GLTF_EXT_mesh_features = { + /** An array of feature ID sets. */ + featureIds: GLTF_EXT_mesh_features_featureId[]; + extensions?: Record; + extras?: unknown; +}; + +/** + * Feature IDs stored in an attribute or texture. + * @see https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_mesh_features/schema/featureId.schema.json + */ +export type GLTF_EXT_mesh_features_featureId = { + /** The number of unique features in the attribute or texture. */ + featureCount: number; + /** A value that indicates that no feature is associated with this vertex or texel. */ + nullFeatureId?: number; + /** A label assigned to this feature ID set. Labels must be alphanumeric identifiers matching the regular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`. */ + label?: string; + /** + * An attribute containing feature IDs. + * When `attribute` and `texture` are omitted the feature IDs are assigned to vertices by their index. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features/schema/featureIdAttribute.schema.json + * An integer value used to construct a string in the format `_FEATURE_ID_` which is a reference to a key in `mesh.primitives.attributes` + * (e.g. a value of `0` corresponds to `_FEATURE_ID_0`). + */ + attribute?: number; + /** A texture containing feature IDs. + * @see https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_mesh_features/schema/featureIdTexture.schema.json + */ + texture?: GLTFTextureInfoMetadata; + /** The index of the property table containing per-feature property values. Only applicable when using the `EXT_structural_metadata` extension. */ + propertyTable?: number; + extensions?: Record; + extras?: unknown; + + /** This is not part of the spec. GLTFLoader loads feature tables data into this property */ + data?: unknown; +}; diff --git a/modules/gltf/src/lib/types/gltf-ext-structural-metadata-schema.ts b/modules/gltf/src/lib/types/gltf-ext-structural-metadata-schema.ts new file mode 100644 index 0000000000..cb59fdeae6 --- /dev/null +++ b/modules/gltf/src/lib/types/gltf-ext-structural-metadata-schema.ts @@ -0,0 +1,378 @@ +import {GLTFTextureInfoMetadata} from './gltf-json-schema'; + +/* eslint-disable camelcase */ + +/** + * glTF extension that provides structural metadata about vertices, texels, and features in a glTF asset. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/glTF.EXT_structural_metadata.schema.json + */ +export type GLTF_EXT_structural_metadata_GLTF = { + /** An object defining classes and enums. */ + schema?: GLTF_EXT_structural_metadata_Schema; + /** A uri to an external schema file. */ + schemaUri?: string; + /** An array of property table definitions, which may be referenced by index. */ + propertyTables?: GLTF_EXT_structural_metadata_PropertyTable[]; + /** An array of property texture definitions, which may be referenced by index. */ + propertyTextures?: GLTF_EXT_structural_metadata_PropertyTexture[]; + /** "An array of property attribute definitions, which may be referenced by index. */ + propertyAttributes?: GLTF_EXT_structural_metadata_PropertyAttribute[]; + /** This is not part of the spec. GLTFLoader loads names of attributes crated into this property */ + dataAttributeNames?: string[]; +}; + +/** + * An object defining classes and enums. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/schema.schema.json + */ +export type GLTF_EXT_structural_metadata_Schema = { + /** Unique identifier for the schema. Schema IDs must be alphanumeric identifiers matching the regular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`. */ + id: string; + /** The name of the schema. */ + name?: string; + /** The description of the schema. */ + description?: string; + /** Application-specific version of the schema. */ + version?: string; + /** A dictionary, where each key is a class ID and each value is an object defining the class. */ + classes?: { + [key: string]: GLTF_EXT_structural_metadata_Class; + }; + /** A dictionary, where each key is an enum ID and each value is an object defining the values for the enum. */ + enums?: { + [key: string]: GLTF_EXT_structural_metadata_Enum; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * An object defining the values of an enum. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/enum.schema.json + */ +export type GLTF_EXT_structural_metadata_Enum = { + /** The name of the enum, e.g. for display purposes. */ + name?: string; + /** The description of the enum. */ + description?: string; + /** + * The type of the integer enum value. + * Default value is 'UINT16' + */ + valueType?: 'INT8' | 'UINT8' | 'INT16' | 'UINT16' | 'INT32' | 'UINT32' | 'INT64' | 'UINT64'; + /** An array of enum values. Duplicate names or duplicate integer values are not allowed. */ + values: GLTF_EXT_structural_metadata_EnumValue[]; + extensions?: Record; + extras?: unknown; +}; + +/** + * An enum value. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/enum.value.schema.json + */ +export type GLTF_EXT_structural_metadata_EnumValue = { + /** The name of the enum value. */ + name: string; + /** The description of the enum value. */ + description?: string; + /** The integer enum value. */ + value: number; + extensions?: Record; + extras?: unknown; +}; + +/** + * A class containing a set of properties. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/class.schema.json + */ +export type GLTF_EXT_structural_metadata_Class = { + /** The name of the class, e.g. for display purposes. */ + name?: string; + /** The description of the class. */ + description?: string; + /** + * A dictionary, where each key is a property ID and each value is an object defining the property. + * Property IDs must be alphanumeric identifiers matching the regular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`. + */ + properties: { + [key: string]: GLTF_EXT_structural_metadata_ClassProperty; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * A class property. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/class.property.schema.json + */ +export type GLTF_EXT_structural_metadata_ClassProperty = { + /** The name of the property, e.g. for display purposes. */ + name?: string; + + /** The description of the property. */ + description?: string; + + /** The element type. */ + type: + | 'SCALAR' + | 'VEC2' + | 'VEC3' + | 'VEC4' + | 'MAT2' + | 'MAT3' + | 'MAT4' + | 'BOOLEAN' + | 'STRING' + | 'ENUM' + | string; + + /** The datatype of the element's components. Only applicable to `SCALAR`, `VECN`, and `MATN` types. */ + componentType?: + | 'INT8' + | 'UINT8' + | 'INT16' + | 'UINT16' + | 'INT32' + | 'UINT32' + | 'INT64' + | 'UINT64' + | 'FLOAT32' + | 'FLOAT64' + | string; + + /** Enum ID as declared in the `enums` dictionary. Required when `type` is `ENUM`. */ + enumType?: string; + + /** + * Whether the property is an array. + * When `count` is defined the property is a fixed-length array. Otherwise the property is a variable-length array. + */ + array?: boolean; + + /** The number of array elements. May only be defined when `array` is true. */ + count?: number; + + /** + * Specifies whether integer values are normalized. + * Only applicable to `SCALAR`, `VECN`, and `MATN` types with integer component types. + * For unsigned integer component types, values are normalized between `[0.0, 1.0]`. + * For signed integer component types, values are normalized between `[-1.0, 1.0]`. + * For all other component types, this property must be false. + */ + normalized?: boolean; + + /** + * An offset to apply to property values. + * Only applicable to `SCALAR`, `VECN`, and `MATN` types when the component type is `FLOAT32` or `FLOAT64`, or when the property is `normalized`. + */ + offset?: number | number[]; + + /** + * A scale to apply to property values. + * Only applicable to `SCALAR`, `VECN`, and `MATN` types when the component type is `FLOAT32` or `FLOAT64`, or when the property is `normalized`. + */ + scale?: number | number[]; + + /** + * Maximum allowed value for the property. + * Only applicable to `SCALAR`, `VECN`, and `MATN` types. + * This is the maximum of all property values, after the transforms based on the `normalized`, `offset`, and `scale` properties have been applied. + */ + max?: number | number[]; + + /** + * Minimum allowed value for the property. + * Only applicable to `SCALAR`, `VECN`, and `MATN` types. + * This is the minimum of all property values, after the transforms based on the `normalized`, `offset`, and `scale` properties have been applied. + */ + min?: number | number[]; + + default?: boolean | number | string | number[]; + /** + * If required, the property must be present in every entity conforming to the class. + * If not required, individual entities may include `noData` values, or the entire property may be omitted. + * As a result, `noData` has no effect on a required property. + * Client implementations may use required properties to make performance optimizations. + * Default value is false. + */ + required?: boolean; + + /** + * A `noData` value represents missing data — also known as a sentinel value — wherever it appears. + * `BOOLEAN` properties may not specify `noData` values. + * This is given as the plain property value, without the transforms from the `normalized`, `offset`, and `scale` properties. + * Must not be defined if `required` is true. + */ + noData?: number | string | number[] | string[]; + + /** + * An identifier that describes how this property should be interpreted. + * The semantic cannot be used by other properties in the class. + */ + semantic?: string; + extensions?: Record; + extras?: unknown; +}; + +/** + * Properties conforming to a class, organized as property values stored in binary columnar arrays. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/propertyTable.schema.json + */ +export type GLTF_EXT_structural_metadata_PropertyTable = { + /** The name of the property table, e.g. for display purposes. */ + name?: string; + /** The class that property values conform to. The value must be a class ID declared in the `classes` dictionary. */ + class: string; + /** The number of elements in each property array. */ + count: number; + /** + * A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary + * and each value is an object describing where property values are stored. + * Required properties must be included in this dictionary. + */ + properties?: { + [key: string]: GLTF_EXT_structural_metadata_PropertyTable_Property; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * An array of binary property values. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/propertyTable.property.schema.json + */ +export type GLTF_EXT_structural_metadata_PropertyTable_Property = { + /** + * The index of the buffer view containing property values. + * The data type of property values is determined by the property definition: + * When `type` is `BOOLEAN` values are packed into a bitstream. + * When `type` is `STRING` values are stored as byte sequences and decoded as UTF-8 strings. + * When `type` is `SCALAR`, `VECN`, or `MATN` the values are stored as the provided `componentType` + * and the buffer view `byteOffset` must be aligned to a multiple of the `componentType` size. + * When `type` is `ENUM` values are stored as the enum's `valueType` + * and the buffer view `byteOffset` must be aligned to a multiple of the `valueType` size. + * Each enum value in the array must match one of the allowed values in the enum definition. + * `arrayOffsets` is required for variable-length arrays and `stringOffsets` is required for strings (for variable-length arrays of strings, both are required). + */ + values: number; + /** + * The index of the buffer view containing offsets for variable-length arrays. + * The number of offsets is equal to the property table `count` plus one. + * The offsets represent the start positions of each array, with the last offset representing the position after the last array. + * The array length is computed using the difference between the subsequent offset and the current offset. + * If `type` is `STRING` the offsets index into the string offsets array (stored in `stringOffsets`), otherwise they index into the property array (stored in `values`). + * The data type of these offsets is determined by `arrayOffsetType`. + * The buffer view `byteOffset` must be aligned to a multiple of the `arrayOffsetType` size. + */ + arrayOffsets?: number; + /** + * The index of the buffer view containing offsets for strings. + * The number of offsets is equal to the number of string elements plus one. + * The offsets represent the byte offsets of each string in the property array (stored in `values`), with the last offset representing the byte offset after the last string. + * The string byte length is computed using the difference between the subsequent offset and the current offset. + * The data type of these offsets is determined by `stringOffsetType`. + * The buffer view `byteOffset` must be aligned to a multiple of the `stringOffsetType` size. + */ + stringOffsets?: number; + /** + * The type of values in `arrayOffsets`. + * Default value is 'UINT32' + */ + arrayOffsetType?: 'UINT8' | 'UINT16' | 'UINT32' | 'UINT64' | string; + + /** + * The type of values in `stringOffsets`. + * Default value is 'UINT32' + */ + stringOffsetType?: 'UINT8' | 'UINT16' | 'UINT32' | 'UINT64' | string; + /** + * An offset to apply to property values. + * Only applicable when the component type is `FLOAT32` or `FLOAT64`, or when the property is `normalized`. + * Overrides the class property's `offset` if both are defined. + */ + offset?: number | number[]; + /** + * A scale to apply to property values. + * Only applicable when the component type is `FLOAT32` or `FLOAT64`, or when the property is `normalized`. + * Overrides the class property's `scale` if both are defined. + */ + scale?: number | number[]; + /** + * Maximum value present in the property values. + * Only applicable to `SCALAR`, `VECN`, and `MATN` types. + * This is the maximum of all property values, after the transforms based on the `normalized`, `offset`, and `scale` properties have been applied. + */ + max?: number | number[]; + /** + * Minimum value present in the property values. + * Only applicable to `SCALAR`, `VECN`, and `MATN` types. + * This is the minimum of all property values, after the transforms based on the `normalized`, `offset`, and `scale` properties have been applied. + */ + min?: number | number[]; + extensions?: Record; + extras?: unknown; + /** This is not part of the spec. GLTFLoader loads feature tables data into this property */ + data?: unknown; +}; + +/** + * Properties conforming to a class, organized as property values stored in textures. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/propertyTexture.schema.json + */ +export type GLTF_EXT_structural_metadata_PropertyTexture = { + /** The name of the property texture, e.g. for display purposes. */ + name?: string; + /** The class that property values conform to. The value must be a class ID declared in the `classes` dictionary. */ + class: string; + /** + * A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary + * and each value is an object describing where property values are stored. + * Required properties must be included in this dictionary. + * + * https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata + * Each property that is defined in the propertyTexture object extends the glTF textureInfo object. + * The texCoord specifies a texture coordinate set in the referring primitive. + * The index is the index of the glTF texture object that stores the actual data. Additionally, + * each property specifies an array of channels, which are the indices of the texture channels providing data for the respective property. + * Channels of an RGBA texture are numbered 0..3 respectively. + */ + properties?: { + [key: string]: GLTFTextureInfoMetadata; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * Properties conforming to a class, organized as property values stored in attributes. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/propertyAttribute.schema.json + */ +export type GLTF_EXT_structural_metadata_PropertyAttribute = { + /** The name of the property attribute, e.g. for display purposes. */ + name?: string; + /** The class that property values conform to. The value must be a class ID declared in the `classes` dictionary. */ + class: string; + /** + * "A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary + * and each value is an object describing where property values are stored. + * Required properties must be included in this dictionary. + */ + properties?: { + [key: string]: unknown; + }; + extensions?: Record; + extras?: unknown; +}; + +/** + * Structural metadata about a glTF primitive. + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/mesh.primitive.EXT_structural_metadata.schema.json + */ +export type GLTF_EXT_structural_metadata_Primitive = { + /** An array of indexes of property textures in the root `EXT_structural_metadata` object. */ + propertyTextures?: number[]; + /** An array of indexes of property attributes in the root `EXT_structural_metadata` object. */ + propertyAttributes?: number[]; + extensions?: Record; + extras?: unknown; +}; diff --git a/modules/gltf/src/lib/types/gltf-json-schema.ts b/modules/gltf/src/lib/types/gltf-json-schema.ts index 753f9e3d3a..061694ee7d 100644 --- a/modules/gltf/src/lib/types/gltf-json-schema.ts +++ b/modules/gltf/src/lib/types/gltf-json-schema.ts @@ -358,6 +358,8 @@ export type GLTFTextureInfo = { index: GLTFId; /** * The set index of texture's TEXCOORD attribute used for texture coordinate mapping. + * Default is 0 + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/specification/2.0/schema/textureInfo.schema.json */ texCoord?: number; extensions?: Record; @@ -365,6 +367,22 @@ export type GLTFTextureInfo = { // [k: string]: any; }; +/** + * Extended GLTFTextureInfo that is used in EXT_structural_metadata and EXT_mesh_features + * https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata + * https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features + */ +export type GLTFTextureInfoMetadata = GLTFTextureInfo & { + /** + * For EXT_structural_metadata and Ext_mesh_features the channels default is [0] + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features/schema/featureIdTexture.schema.json + * @see https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata/schema/propertyTexture.property.schema.json + */ + channels: number[] | string; + /** For internal usage */ + data?: unknown; +}; + /** * A set of parameter values that are used to define the metallic-roughness material model from Physically-Based Rendering (PBR) methodology. */ @@ -486,7 +504,7 @@ export type GLTFMeshPrimitive = { targets?: { [k: string]: GLTFId; }[]; - extensions?: Record; + extensions?: Record; extras?: any; // [k: string]: any; }; @@ -792,529 +810,3 @@ export type GLTF_MSFT_texture_dds = { source: GLTFId; extras?: any; }; - -/** - * EXT_mesh_features extension types - * This is a primitive-level extension - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features - * - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_mesh_features/schema/mesh.primitive.EXT_mesh_features.schema.json - * An object describing feature IDs for a mesh primitive. - */ -export type GLTF_EXT_mesh_features = { - /** An array of feature ID sets. */ - featureIds: GLTF_EXT_mesh_features_featureId[]; - extensions?: Record; - extras?: any; -}; - -/** - * JSON Schema https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_mesh_features/schema/featureId.schema.json - * Feature IDs stored in an attribute or texture. - */ -export type GLTF_EXT_mesh_features_featureId = { - /** The number of unique features in the attribute or texture. */ - featureCount: number; - /** A value that indicates that no feature is associated with this vertex or texel. */ - nullFeatureId: number; - /** A label assigned to this feature ID set. Labels must be alphanumeric identifiers matching the regular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`. */ - label: string; - /** - * An attribute containing feature IDs. When `attribute` and `texture` are omitted the feature IDs are assigned to vertices by their index. - * Schema https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features/schema/featureIdAttribute.schema.json - * An integer value used to construct a string in the format `_FEATURE_ID_` which is a reference to a key in `mesh.primitives.attributes` - * (e.g. a value of `0` corresponds to `_FEATURE_ID_0`). - */ - attribute: number; - /** A texture containing feature IDs. */ - texture: any; - /** The index of the property table containing per-feature property values. Only applicable when using the `EXT_structural_metadata` extension. */ - propertyTable: number; - extensions?: Record; - extras?: any; -}; - -/** - * JSON Schema https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_mesh_features/schema/featureIdTexture.schema.json - * Feature ID Texture in EXT_mesh_features - */ -export type GLTF_EXT_mesh_features_featureIdTexture = { - /** - * Texture channels containing feature IDs, identified by index. Feature IDs may be packed into multiple channels if a single channel does not have sufficient - * bit depth to represent all feature ID values. The values are packed in little-endian order. - */ - channels: number[]; - /** Texture index in the glTF textures array */ - index: number; - /** Textcoord index in the primitive attribute (eg. 0 for TEXTCOORD_0, 1 for TEXTCOORD_1 etc...) */ - texCoord: number; - extensions: Record; - extras: any; -}; - -/** - * EXT_feature_metadata extension types - * This extension has glTF-level metadata and primitive-level feature indexing and segmentation metadata - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata - * - * glTF-level metadata - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#gltf-extension-1 - * JSON Schema - https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata/schema/glTF.EXT_feature_metadata.schema.json - */ -export type GLTF_EXT_feature_metadata_GLTF = { - /** An object defining classes and enums. */ - schema?: GLTF_EXT_feature_metadata_Schema; - /** A uri to an external schema file. */ - schemaUri?: string; - /** An object containing statistics about features. */ - statistics?: GLTF_EXT_feature_metadata_Statistics; - /** A dictionary, where each key is a feature table ID and each value is an object defining the feature table. */ - featureTables?: { - [key: string]: GLTF_EXT_feature_metadata_FeatureTable; - }; - /** A dictionary, where each key is a feature texture ID and each value is an object defining the feature texture. */ - featureTextures?: { - [key: string]: GLTF_EXT_feature_metadata_FeatureTexture; - }; - extensions?: Record; - extras?: any; -}; - -/** - * An object defining classes and enums. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#schema - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/schema.schema.json - */ -export type GLTF_EXT_feature_metadata_Schema = { - /** The name of the schema. */ - name?: string; - /** The description of the schema. */ - description?: string; - /** Application-specific version of the schema. */ - version?: string; - /** A dictionary, where each key is a class ID and each value is an object defining the class. */ - classes?: { - [key: string]: GLTF_EXT_feature_metadata_Class; - }; - /** A dictionary, where each key is an enum ID and each value is an object defining the values for the enum. */ - enums?: { - [key: string]: GLTF_EXT_feature_metadata_Enum; - }; - extensions?: Record; - extras?: any; -}; - -/** - * A class containing a set of properties. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#class - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/class.schema.json - */ -export type GLTF_EXT_feature_metadata_Class = { - /** The name of the class, e.g. for display purposes. */ - name?: string; - /** The description of the class. */ - description?: string; - /** A dictionary, where each key is a property ID and each value is an object defining the property. */ - properties: { - [key: string]: GLTF_EXT_feature_metadata_ClassProperty; - }; - extensions?: Record; - extras?: any; -}; - -/** - * A class property. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#class-property - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/class.property.schema.json - */ -export type GLTF_EXT_feature_metadata_ClassProperty = { - /** The name of the property, e.g. for display purposes. */ - name?: string; - /** The description of the property. */ - description?: string; - /** - * The property type. If ENUM is used, then enumType must also be specified. - * If ARRAY is used, then componentType must also be specified. - * ARRAY is a fixed-length array when componentCount is defined, and variable-length otherwise. - */ - type: - | 'INT8' - | 'UINT8' - | 'INT16' - | 'UINT16' - | 'INT32' - | 'UINT32' - | 'INT64' - | 'UINT64' - | 'FLOAT32' - | 'FLOAT64' - | 'BOOLEAN' - | 'STRING' - | 'ENUM' - | 'ARRAY' - | string; - /** - * An enum ID as declared in the enums dictionary. - * This value must be specified when type or componentType is ENUM. - */ - enumType?: string; - /** - * When type is ARRAY this indicates the type of each component of the array. - * If ENUM is used, then enumType must also be specified. - */ - componentType?: - | 'INT8' - | 'UINT8' - | 'INT16' - | 'UINT16' - | 'INT32' - | 'UINT32' - | 'INT64' - | 'UINT64' - | 'FLOAT32' - | 'FLOAT64' - | 'BOOLEAN' - | 'STRING' - | 'ENUM' - | string; - /** The number of components per element for ARRAY elements. */ - componentCount?: number; - /** - * Specifies whether integer values are normalized. - * This applies both when type is an integer type, or when type is ARRAY with a componentType that is an integer type. - * For unsigned integer types, values are normalized between [0.0, 1.0]. - * For signed integer types, values are normalized between [-1.0, 1.0]. - * For all other types, this property is ignored. - */ - normalized: boolean; - /** - * Maximum allowed values for property values. - * Only applicable for numeric types and fixed-length arrays of numeric types. - * For numeric types this is a single number. - * For fixed-length arrays this is an array with componentCount number of elements. - * The normalized property has no effect on these values: they always correspond to the integer values. - */ - max?: number | number[]; - /** - * Minimum allowed values for property values. - * Only applicable for numeric types and fixed-length arrays of numeric types. - * For numeric types this is a single number. - * For fixed-length arrays this is an array with componentCount number of elements. - * The normalized property has no effect on these values: they always correspond to the integer values. - */ - min?: number | number[]; - /** - * A default value to use when the property value is not defined. - * If used, optional must be set to true. - * The type of the default value must match the property definition: For BOOLEAN use true or false. - * For STRING use a JSON string. For a numeric type use a JSON number. - * For ENUM use the enum name, not the integer value. - * For ARRAY use a JSON array containing values matching the componentType. - */ - default?: boolean | number | string | number[]; - /** If true, this property is optional. */ - optional?: boolean; // default false; - /** - * An identifier that describes how this property should be interpreted. - * The semantic cannot be used by other properties in the class. - */ - semantic?: string; - extensions?: Record; - extras?: any; -}; - -/** - * An object defining the values of an enum. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#enum - * JSON Schema - https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata/schema/enum.schema.json - */ -export type GLTF_EXT_feature_metadata_Enum = { - /** The name of the enum, e.g. for display purposes. */ - name?: string; - /** The description of the enum. */ - description?: string; - /** The type of the integer enum value. */ - valueType?: 'INT8' | 'UINT8' | 'INT16' | 'UINT16' | 'INT32' | 'UINT32' | 'INT64' | 'UINT64'; // default: "UINT16" - /** An array of enum values. Duplicate names or duplicate integer values are not allowed. */ - values: GLTF_EXT_feature_metadata_EnumValue[]; - extensions?: Record; - extras?: any; - [key: string]: any; -}; - -/** - * An enum value. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#enum-value - * JSON Schema - https://github.com/CesiumGS/glTF/blob/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata/schema/enum.value.schema.json - */ -export type GLTF_EXT_feature_metadata_EnumValue = { - /** The name of the enum value. */ - name: string; - /** The description of the enum value. */ - description?: string; - /** The integer enum value. */ - value: number; // default: "UINT16" - extensions?: Record; - extras?: any; - [key: string]: any; -}; - -/** - * A feature table defined by a class and property values stored in arrays. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#feature-table - * JSON Schenma - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureTable.schema.json - */ -export type GLTF_EXT_feature_metadata_FeatureTable = { - /** The class that property values conform to. The value must be a class ID declared in the classes dictionary. */ - class?: string; - /** The number of features, as well as the number of elements in each property array. */ - count: number; - /** - * A dictionary, where each key corresponds to a property ID in the class properties dictionary - * and each value is an object describing where property values are stored. - * Optional properties may be excluded from this dictionary. - */ - properties?: { - [key: string]: GLTF_EXT_feature_metadata_FeatureTableProperty; - }; - extensions?: Record; - extras?: any; -}; - -/** - * An array of binary property values. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#feature-table-property - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureTable.property.schema.json - */ -export type GLTF_EXT_feature_metadata_FeatureTableProperty = { - /** - * The index of the buffer view containing property values. - * The data type of property values is determined by the property definition: - * When type is BOOLEAN values are packed into a bitfield. - * When type is STRING values are stored as byte sequences and decoded as UTF-8 strings. - * When type is a numeric type values are stored as the provided type. - * When type is ENUM values are stored as the enum's valueType. - * Each enum value in the buffer must match one of the allowed values in the enum definition. - * When type is ARRAY elements are packed tightly together and the data type is based on the componentType following the same rules as above. - * arrayOffsetBufferView is required for variable-size arrays - * and stringOffsetBufferView is required for strings (for variable-length arrays of strings, both are required) - * The buffer view byteOffset must be aligned to a multiple of 8 bytes. - * If the buffer view's buffer is the GLB-stored BIN chunk the byte offset is measured relative to the beginning of the GLB. - * Otherwise it is measured relative to the beginning of the buffer. - */ - bufferView: number; - - /** The type of values in arrayOffsetBufferView and stringOffsetBufferView. */ - offsetType?: string; // default: "UINT32" - /** - * The index of the buffer view containing offsets for variable-length arrays. - * The number of offsets is equal to the feature table count plus one. - * The offsets represent the start positions of each array, with the last offset representing the position after the last array. - * The array length is computed using the difference between the current offset and the subsequent offset. - * If componentType is STRING the offsets index into the string offsets array (stored in stringOffsetBufferView), - * otherwise they index into the property array (stored in bufferView). - * The data type of these offsets is determined by offsetType. - * The buffer view byteOffset must be aligned to a multiple of 8 bytes in the same manner as the main bufferView - */ - arrayOffsetBufferView?: number; - /** - * The index of the buffer view containing offsets for strings. - * The number of offsets is equal to the number of string components plus one. - * The offsets represent the byte offsets of each string in the main bufferView, - * with the last offset representing the byte offset after the last string. - * The string byte length is computed using the difference between the current offset and the subsequent offset. - * The data type of these offsets is determined by offsetType. - * The buffer view byteOffset must be aligned to a multiple of 8 bytes in the same manner as the main bufferView. - */ - stringOffsetBufferView?: number; - /** This is not part of the spec. GLTFLoader loads feature tables data into this property */ - data: any; - extensions?: Record; - extras?: any; -}; - -/** - * Features whose property values are stored directly in texture channels. This is not to be confused with feature ID textures which store feature IDs for use with a feature table. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#feature-texture - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureTexture.schema.json - */ -export type GLTF_EXT_feature_metadata_FeatureTexture = { - /** The class this feature texture conforms to. The value must be a class ID declared in the classes dictionary. */ - class: string; - /** - * A dictionary, where each key corresponds to a property ID in the class properties dictionary - * and each value describes the texture channels containing property values. - */ - properties: { - [key: string]: GLTF_EXT_feature_metadata_TextureAccessor; - }; - extensions?: Record; - extras?: any; -}; - -/** - * A description of how to access property values from the color channels of a texture. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#texture-accessor - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/textureAccessor.schema.json - */ -export type GLTF_EXT_feature_metadata_TextureAccessor = { - /** Texture channels containing property values. Channels are labeled by rgba and are swizzled with a string of 1-4 characters. */ - channels: string; - /** The glTF texture and texture coordinates to use. */ - texture: GLTFTextureInfo; - /** This is not part of the spec. GLTFLoader loads feature tables data into this property */ - data: any; - extensions?: Record; - extras?: any; -}; - -/** - * Statistics about features. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#statistics-1 - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/statistics.schema.json - */ -export type GLTF_EXT_feature_metadata_Statistics = { - /** - * A dictionary, where each key is a class ID declared in the classes dictionary - * and each value is an object containing statistics about features that conform to the class. - */ - classes?: { - [key: string]: GLTF_EXT_feature_metadata_StatisticsClass; - }; - extensions?: Record; - extras?: any; -}; - -/** - * Statistics about features that conform to the class. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#class-statistics - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/statistics.class.property.schema.json - */ -export type GLTF_EXT_feature_metadata_StatisticsClass = { - /** The number of features that conform to the class. */ - count?: number; - /** - * A dictionary, where each key is a class ID declared in the classes dictionary - * and each value is an object containing statistics about property values. - */ - properties?: { - [key: string]: GLTF_EXT_feature_metadata_StatisticsClassProperty; - }; - extensions?: Record; - extras?: any; -}; - -/** - * min, max, mean, median, standardDeviation, variance, sum are - * only applicable for numeric types and fixed-length arrays of numeric types. - * For numeric types this is a single number. - * For fixed-length arrays this is an array with componentCount number of elements. - * The normalized property has no effect on these values. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#property-statistics - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/statistics.class.property.schema.json - */ -export type GLTF_EXT_feature_metadata_StatisticsClassProperty = { - /** The minimum property value. */ - min?: number | number[]; - /** The maximum property value. */ - max?: number | number[]; - /** The arithmetic mean of the property values. */ - mean?: number | number[]; - /** The median of the property values. */ - median?: number | number[]; - /** The standard deviation of the property values. */ - standardDeviation?: number | number[]; - /** The variance of the property values. */ - variance?: number | number[]; - /** The sum of the property values. */ - sum?: number | number[]; - /** - * A dictionary, where each key corresponds to an enum name and each value is the number of occurrences of that enum. - * Only applicable when type or componentType is ENUM. - * For fixed-length arrays, this is an array with componentCount number of elements. - */ - occurrences: { - [key: string]: number | number[]; - }; - extensions?: Record; - extras?: any; -}; - -/** - * EXT_feature_metadata extension types - * This extension has glTF-level metadata and primitive-level (feature indexing and segmentation) metadata - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata - * - * primitive-level metadata - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#primitive-extension - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/mesh.primitive.EXT_feature_metadata.schema.json - */ -export type GLTF_EXT_feature_metadata_Primitive = { - /** Feature ids definition in attributes */ - featureIdAttributes?: GLTF_EXT_feature_metadata_FeatureIdAttribute[]; - /** Feature ids definition in textures */ - featureIdTextures?: GLTF_EXT_feature_metadata_FeatureIdTexture[]; - /** An array of IDs of feature textures from the root EXT_feature_metadata object. */ - featureTextures?: string[]; - extensions?: Record; - extras?: any; -}; - -/** - * Attribute which described featureIds definition. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#feature-id-attribute - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureIdAttribute.schema.json - */ -export type GLTF_EXT_feature_metadata_FeatureIdAttribute = { - /** Name of feature table */ - featureTable: string; - /** Described how feature ids are defined */ - featureIds: GLTF_EXT_feature_metadata_FeatureIdAttributeFeatureIds; - extensions?: Record; - extras?: any; -}; - -/** - * Defining featureIds by attributes or implicitly. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#primitive-extensionfeatureidattributes - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureIdAttribute.featureIds.schema.json - */ -export type GLTF_EXT_feature_metadata_FeatureIdAttributeFeatureIds = { - /** Name of attribute where featureIds are defined */ - attribute?: string; - /** Sets a constant feature ID for each vertex. The default is 0. */ - constant?: number; - /** Sets the rate at which feature IDs increment. - * If divisor is zero then constant is used. - * If divisor is greater than zero the feature ID increments once per divisor sets of vertices, starting at constant. - * The default is 0 - */ - divisor?: number; -}; - -/** - * An object describing a texture used for storing per-texel feature IDs. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#feature-id-texture - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/featureIdTexture.schema.json - */ -export type GLTF_EXT_feature_metadata_FeatureIdTexture = { - /** The ID of the feature table in the model's root `EXT_feature_metadata.featureTables` dictionary. */ - featureTable: string; - /** A description of the texture and channel to use for feature IDs. The `channels` property must have a single channel. Furthermore, - * feature IDs must be whole numbers in the range `[0, count - 1]` (inclusive), where `count` is the total number of features - * in the feature table. Texel values must be read as integers. Texture filtering should be disabled when fetching feature IDs. - */ - featureIds: GLTF_EXT_feature_metadata_FeatureIdTextureAccessor; -}; - -/** - * A description of how to access property values from the color channels of a texture. - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#featureidtexturefeatureids - * JSON Schema - https://github.com/CesiumGS/glTF/blob/c38f7f37e894004353c15cd0481bc5b7381ce841/extensions/2.0/Vendor/EXT_feature_metadata/schema/textureAccessor.schema.json - */ -export type GLTF_EXT_feature_metadata_FeatureIdTextureAccessor = { - /** gLTF textureInfo object - https://github.com/CesiumGS/glTF/blob/3d-tiles-next/specification/2.0/schema/textureInfo.schema.json */ - texture: GLTFTextureInfo; - /** Must be a single channel ("r", "g", "b", or "a") */ - channels: 'r' | 'g' | 'b' | 'a'; -}; diff --git a/modules/gltf/src/lib/types/gltf-types.ts b/modules/gltf/src/lib/types/gltf-types.ts index f3267e02f1..3993f82066 100644 --- a/modules/gltf/src/lib/types/gltf-types.ts +++ b/modules/gltf/src/lib/types/gltf-types.ts @@ -13,7 +13,7 @@ export type GLTFWithBuffers = { images?: GLTFExternalImage[]; }; -type GLTFExternalBuffer = { +export type GLTFExternalBuffer = { arrayBuffer: ArrayBuffer; byteOffset: number; byteLength: number; @@ -29,6 +29,10 @@ type GLTFExternalImage = data: Uint8Array; }; +export type FeatureTableJson = { + [key: string]: any[]; +}; + export type { GLTF, GLTFAccessor, diff --git a/modules/gltf/test/index.js b/modules/gltf/test/index.ts similarity index 88% rename from modules/gltf/test/index.js rename to modules/gltf/test/index.ts index 6a55b28b8d..db0c9787e8 100644 --- a/modules/gltf/test/index.js +++ b/modules/gltf/test/index.ts @@ -16,6 +16,8 @@ import './lib/extensions/KHR_lights_punctual.spec'; import './lib/extensions/KHR_materials_unlit.spec'; import './lib/extensions/KHR_texture_transform.spec'; import './lib/extensions/EXT_feature_metadata.spec'; +import './lib/extensions/EXT_structural_metadata.spec'; +import './lib/extensions/EXT_mesh_features.spec'; import './glb-loader.spec'; import './glb-writer.spec'; diff --git a/modules/gltf/test/lib/api/gltf-scenegraph-accessors.spec.ts b/modules/gltf/test/lib/api/gltf-scenegraph-accessors.spec.ts index e504201b88..f7d67a0a25 100644 --- a/modules/gltf/test/lib/api/gltf-scenegraph-accessors.spec.ts +++ b/modules/gltf/test/lib/api/gltf-scenegraph-accessors.spec.ts @@ -1,9 +1,9 @@ /* eslint-disable max-len */ import test from 'tape-promise/tape'; -import {GLTFScenegraph} from '@loaders.gl/gltf'; -import {GLTFLoader} from '@loaders.gl/gltf'; +import {GLTFScenegraph, GLTFLoader, GLTFAccessor} from '@loaders.gl/gltf'; import {load} from '@loaders.gl/core'; +import type {TypedArray} from '@loaders.gl/schema'; // Extracted from Cesium 3D Tiles const GLB_TILE_WITH_DRACO_URL = '@loaders.gl/gltf/test/data/3d-tiles/143.glb'; @@ -79,3 +79,55 @@ test('GLTFScenegraph#BufferView indices resolve correctly', async (t) => { t.end(); }); + +test('GLTFScenegraph#Typed Arrays should be taken by Accessor', async (t) => { + const GLB_ACCESSOR_URL = '@loaders.gl/gltf/test/data/glb/DamagedHelmet.glb'; + const testDataSet = [ + { + accessorIndex: 0, + accessorCountExpected: 46356, + arrayExpected: [0, 1, 2, 2, 3, 0, 3, 2] + }, + { + accessorIndex: 1, + accessorCountExpected: 14556, + arrayExpected: [ + -0.6119945645332336, -0.03094087541103363, 0.48309004306793213, -0.5795046091079712, + 0.05627411603927612, 0.5217580199241638, -0.5735836029052734, 0.06353411078453064 + ] + } + ]; + const gltfWithBuffers = await load(GLB_ACCESSOR_URL, GLTFLoader); + const gltfScenegraph = new GLTFScenegraph(gltfWithBuffers); + + for (const testData of testDataSet) { + let typedArray: TypedArray = gltfScenegraph.getTypedArrayForAccessor(testData.accessorIndex); + t.deepEquals( + typedArray.slice(0, 8), + testData.arrayExpected, + 'typed array taken by accessor as a number' + ); + + const accessor: GLTFAccessor = gltfScenegraph.getAccessor(testData.accessorIndex); + t.equals(accessor.count, testData.accessorCountExpected, 'first accessor taken'); + + typedArray = gltfScenegraph.getTypedArrayForAccessor(accessor); + t.deepEquals( + typedArray.slice(0, 8), + testData.arrayExpected, + 'typed array taken by accessor as an object' + ); + + if (accessor.bufferView === 0) { + accessor.bufferView = undefined; + // default bufferView should be 0 + typedArray = gltfScenegraph.getTypedArrayForAccessor(accessor); + t.deepEquals( + typedArray.slice(0, 8), + testData.arrayExpected, + 'typed array taken by accessor as object with the bufferView set to undefined' + ); + } + } + t.end(); +}); diff --git a/modules/gltf/test/lib/extensions/EXT_feature_metadata.spec.js b/modules/gltf/test/lib/extensions/EXT_feature_metadata.spec.js deleted file mode 100644 index 0e78f0a0a0..0000000000 --- a/modules/gltf/test/lib/extensions/EXT_feature_metadata.spec.js +++ /dev/null @@ -1,247 +0,0 @@ -/* eslint-disable camelcase */ -import test from 'tape-promise/tape'; - -// @ts-expect-error -import {decodeExtensions} from '@loaders.gl/gltf/lib/api/gltf-extensions'; - -test('gltf#EXT_feature_metadata - Should do nothing if no "EXT_feature_metadata" extension', async (t) => { - const gltfWithFeatureTextures = { - json: { - extensionsUsed: ['EXT_feature_metadata'], - extensions: {} - } - }; - - await decodeExtensions(gltfWithFeatureTextures); - - const expectedResult = { - json: { - extensionsUsed: ['EXT_feature_metadata'], - extensions: {} - } - }; - - // Modifies input - t.deepEqual(gltfWithFeatureTextures.json, expectedResult.json); - t.end(); -}); - -test('gltf#EXT_feature_metadata - Should handle String feature attributes', async (t) => { - const GLTF_WITH_STRING_PROPERTIES = { - buffers: [ - { - // 'windowroofdoor' encoded string - arrayBuffer: new Uint8Array([ - 119, 105, 110, 100, 111, 119, 114, 111, 111, 102, 100, 111, 111, 114, 0, 0, 0, 0, 6, 0, 0, - 0, 10, 0, 0, 0, 14, 0, 0, 0 - ]).buffer, - byteOffset: 0, - byteLength: 30 - } - ], - json: { - bufferViews: [ - // Strings Buffer View - {buffer: 0, byteOffset: 0, byteLength: 14}, - // Strings Offsets Buffer View - {buffer: 0, byteOffset: 14, byteLength: 16} - ], - extensionsUsed: ['EXT_feature_metadata'], - extensions: { - EXT_feature_metadata: { - schema: { - classes: { - label: { - properties: { - component: {type: 'STRING'} - } - } - } - }, - featureTables: { - labels: { - class: 'label', - count: 3, - properties: { - component: {bufferView: 0, stringOffsetBufferView: 1} - } - } - } - } - } - } - }; - - await decodeExtensions(GLTF_WITH_STRING_PROPERTIES); - - const expectedResult = { - buffers: [ - { - // 'windowroofdoor' encoded string - arrayBuffer: new Uint8Array([ - 119, 105, 110, 100, 111, 119, 114, 111, 111, 102, 100, 111, 111, 114, 0, 0, 0, 0, 6, 0, 0, - 0, 10, 0, 0, 0, 14, 0, 0, 0 - ]).buffer, - byteOffset: 0, - byteLength: 30 - } - ], - json: { - bufferViews: [ - // Strings Buffer View - {buffer: 0, byteOffset: 0, byteLength: 14}, - // Strings Offsets Buffer View - {buffer: 0, byteOffset: 14, byteLength: 16} - ], - extensionsUsed: ['EXT_feature_metadata'], - extensions: { - EXT_feature_metadata: { - schema: { - classes: { - label: { - properties: { - component: {type: 'STRING'} - } - } - } - }, - featureTables: { - labels: { - class: 'label', - count: 3, - properties: { - component: { - bufferView: 0, - stringOffsetBufferView: 1, - data: ['window', 'roof', 'door'] - } - } - } - } - } - } - } - }; - - // Modifies input - t.deepEqual(GLTF_WITH_STRING_PROPERTIES.json, expectedResult.json); - t.end(); -}); - -test('gltf#EXT_feature_metadata - Should handle feature texture attributes', async (t) => { - const GLTF_WITH_TEXTURES = { - buffers: [ - { - arrayBuffer: new Float32Array([0.1, 0.1, 0.1, 0.9, 0.9, 0.1, 0.9, 0.9]).buffer, - byteOffset: 0, - byteLength: 32 - } - ], - images: [ - { - name: 'first', - components: 4, - height: 2, - width: 2, - data: new Uint8Array([24, 24, 24, 255, 28, 28, 28, 255, 35, 35, 35, 255, 24, 24, 24, 255]) - } - ], - - json: { - accessors: [ - { - bufferView: 0, - componentType: 5125, - count: 8, - type: 'SCALAR' - } - ], - buffers: [ - { - buffer: 0, - byteOffset: 0, - byteLength: 32 - } - ], - bufferViews: [{buffer: 0, byteOffset: 0, byteLength: 32}], - extensionsUsed: ['EXT_feature_metadata'], - extensions: { - EXT_feature_metadata: { - schema: { - classes: { - 'r3dm::uncertainty_ce90sum': { - properties: { - 'r3dm::uncertainty_ce90sum': { - type: 'UINT8' - } - } - } - } - }, - featureTextures: { - 'r3dm::uncertainty_ce90sum': { - class: 'r3dm::uncertainty_ce90sum', - properties: { - 'r3dm::uncertainty_ce90sum': { - channels: 'r', - texture: { - index: 0, - texCoord: 0 - } - } - } - } - }, - extras: { - draftVersion: '1.0.0' - } - } - }, - textures: [ - { - sampler: 0, - source: 0 - } - ], - meshes: [ - { - primitives: [ - { - attributes: { - TEXCOORD_0: 0 - }, - indices: 3, - material: 0, - mode: 4, - extensions: { - EXT_feature_metadata: { - featureTextures: ['r3dm::uncertainty_ce90sum'] - } - } - } - ] - } - ] - } - }; - - await decodeExtensions(GLTF_WITH_TEXTURES, {gltf: {loadImages: true}}); - - const expectedResult = { - buf: { - arrayBuffer: new Uint32Array([0, 1, 2, 0]).buffer, - byteOffset: 0, - byteLength: 16 - }, - data: [24, 35, 28] - }; - - t.deepEqual(GLTF_WITH_TEXTURES.buffers[1], expectedResult.buf); - t.deepEqual( - GLTF_WITH_TEXTURES.json.extensions.EXT_feature_metadata.featureTextures[ - 'r3dm::uncertainty_ce90sum' - ].properties['r3dm::uncertainty_ce90sum'].data, - expectedResult.data - ); - t.end(); -}); diff --git a/modules/gltf/test/lib/extensions/EXT_feature_metadata.spec.ts b/modules/gltf/test/lib/extensions/EXT_feature_metadata.spec.ts new file mode 100644 index 0000000000..a25bbdf834 --- /dev/null +++ b/modules/gltf/test/lib/extensions/EXT_feature_metadata.spec.ts @@ -0,0 +1,628 @@ +/* eslint-disable camelcase */ +import test from 'tape-promise/tape'; + +// @ts-expect-error +import {decodeExtensions} from '@loaders.gl/gltf/lib/api/gltf-extensions'; + +test('gltf#EXT_feature_metadata - Should do nothing if no "EXT_feature_metadata" extension', async (t) => { + const gltfWithFeatureTextures = { + json: { + extensionsUsed: ['EXT_feature_metadata'], + extensions: {} + } + }; + + const options = {gltf: {loadImages: true, loadBuffers: true}}; + await decodeExtensions(gltfWithFeatureTextures, options); + + const expectedResult = { + json: { + extensionsUsed: ['EXT_feature_metadata'], + extensions: {} + } + }; + + // Modifies input + t.deepEqual(gltfWithFeatureTextures.json, expectedResult.json); + t.end(); +}); + +test('gltf#EXT_feature_metadata - Should handle String feature attributes', async (t) => { + const GLTF_WITH_STRING_PROPERTIES = { + buffers: [ + { + // 'windowroofdoor' encoded string + arrayBuffer: new Uint8Array([ + 119, 105, 110, 100, 111, 119, 114, 111, 111, 102, 100, 111, 111, 114, 0, 0, 0, 0, 6, 0, 0, + 0, 10, 0, 0, 0, 14, 0, 0, 0 + ]).buffer, + byteOffset: 0, + byteLength: 30 + } + ], + json: { + bufferViews: [ + // Strings Buffer View + {buffer: 0, byteOffset: 0, byteLength: 14}, + // Strings Offsets Buffer View + {buffer: 0, byteOffset: 14, byteLength: 16} + ], + extensionsUsed: ['EXT_feature_metadata'], + extensions: { + EXT_feature_metadata: { + schema: { + classes: { + label: { + properties: { + component: {type: 'STRING'} + } + } + } + }, + featureTables: { + labels: { + class: 'label', + count: 3, + properties: { + component: {bufferView: 0, stringOffsetBufferView: 1} + } + } + } + } + } + } + }; + + const options = {gltf: {loadImages: true, loadBuffers: true}}; + await decodeExtensions(GLTF_WITH_STRING_PROPERTIES, options); + + const expectedResult = { + buffers: [ + { + // 'windowroofdoor' encoded string + arrayBuffer: new Uint8Array([ + 119, 105, 110, 100, 111, 119, 114, 111, 111, 102, 100, 111, 111, 114, 0, 0, 0, 0, 6, 0, 0, + 0, 10, 0, 0, 0, 14, 0, 0, 0 + ]).buffer, + byteOffset: 0, + byteLength: 30 + } + ], + json: { + bufferViews: [ + // Strings Buffer View + {buffer: 0, byteOffset: 0, byteLength: 14}, + // Strings Offsets Buffer View + {buffer: 0, byteOffset: 14, byteLength: 16} + ], + extensionsUsed: ['EXT_feature_metadata'], + extensions: { + EXT_feature_metadata: { + schema: { + classes: { + label: { + properties: { + component: {type: 'STRING'} + } + } + } + }, + featureTables: { + labels: { + class: 'label', + count: 3, + properties: { + component: { + bufferView: 0, + stringOffsetBufferView: 1, + data: ['window', 'roof', 'door'] + } + } + } + } + } + } + } + }; + + // Modifies input + t.deepEqual(GLTF_WITH_STRING_PROPERTIES.json, expectedResult.json); + t.end(); +}); + +test('gltf#EXT_feature_metadata - Should handle number feature attributes', async (t) => { + const binaryBufferData = [ + 65, 76, 48, 49, 51, 65, 76, 48, 49, 51, 65, 76, 48, 49, 51, 65, 76, 48, 49, 51, 65, 76, 48, 49, + 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 10, 0, 0, 0, 15, 0, 0, 0, 20, 0, 0, 0, 25, 0, + 0, 0, 193, 189, 240, 255, 193, 189, 240, 255, 193, 189, 240, 255, 193, 189, 240, 255, 193, 189, + 240, 255, 0, 0, 0, 0, 97, 114, 101, 97, 108, 97, 114, 101, 97, 108, 97, 114, 101, 97, 108, 97, + 114, 101, 97, 108, 97, 114, 101, 97, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 10, 0, 0, + 0, 15, 0, 0, 0, 20, 0, 0, 0, 25, 0, 0, 0, 110, 31, 47, 64, 110, 47, 144, 64, 92, 4, 175, 64, 88, + 15, 203, 64, 18, 98, 212, 64, 0, 0, 0, 0, 109, 97, 120, 97, 114, 109, 97, 120, 97, 114, 109, 97, + 120, 97, 114, 109, 97, 120, 97, 114, 109, 97, 120, 97, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 0, 0, 0, 10, 0, 0, 0, 15, 0, 0, 0, 20, 0, 0, 0, 25, 0, 0, 0, 41, 41, 41, 41, 41 + ]; + const GLTF_WITH_EXTENSION = { + buffers: [ + { + arrayBuffer: new Uint8Array(binaryBufferData).buffer, + byteOffset: 0, + byteLength: binaryBufferData.length + } + ], + json: { + extensionsUsed: ['EXT_feature_metadata'], + bufferViews: [ + {buffer: 0, byteOffset: 0, byteLength: 25}, + {buffer: 0, byteOffset: 32, byteLength: 24}, + {buffer: 0, byteOffset: 56, byteLength: 20}, + {buffer: 0, byteOffset: 80, byteLength: 25}, + {buffer: 0, byteOffset: 112, byteLength: 24}, + {buffer: 0, byteOffset: 136, byteLength: 20}, + {buffer: 0, byteOffset: 160, byteLength: 25}, + {buffer: 0, byteOffset: 192, byteLength: 24}, + {buffer: 0, byteOffset: 216, byteLength: 5} + ], + buffers: [{byteLength: 26894}], + extensions: { + EXT_feature_metadata: { + schema: { + classes: { + buildings: { + properties: { + f_code: {type: 'STRING'}, + ffn: {type: 'INT32'}, + geom_type: {type: 'STRING'}, + hgt: {type: 'FLOAT32'}, + src: {type: 'STRING'}, + ssr: {type: 'UINT8'} + } + } + } + }, + featureTables: { + buildings: { + class: 'buildings', + count: 5, + properties: { + f_code: {bufferView: 0, offsetType: 'UINT32', stringOffsetBufferView: 1}, + ffn: {bufferView: 2}, + geom_type: {bufferView: 3, offsetType: 'UINT32', stringOffsetBufferView: 4}, + hgt: {bufferView: 5}, + src: {bufferView: 6, offsetType: 'UINT32', stringOffsetBufferView: 7}, + ssr: {bufferView: 8} + } + } + } + } + } + } + }; + + const options = {gltf: {loadImages: true, loadBuffers: true}}; + await decodeExtensions(GLTF_WITH_EXTENSION, options); + + const expectedJson = { + extensionsUsed: ['EXT_feature_metadata'], + bufferViews: [ + {buffer: 0, byteOffset: 0, byteLength: 25}, + {buffer: 0, byteOffset: 32, byteLength: 24}, + {buffer: 0, byteOffset: 56, byteLength: 20}, + {buffer: 0, byteOffset: 80, byteLength: 25}, + {buffer: 0, byteOffset: 112, byteLength: 24}, + {buffer: 0, byteOffset: 136, byteLength: 20}, + {buffer: 0, byteOffset: 160, byteLength: 25}, + {buffer: 0, byteOffset: 192, byteLength: 24}, + {buffer: 0, byteOffset: 216, byteLength: 5} + ], + buffers: [{byteLength: 26894}], + extensions: { + EXT_feature_metadata: { + schema: { + classes: { + buildings: { + properties: { + f_code: {type: 'STRING'}, + ffn: {type: 'INT32'}, + geom_type: {type: 'STRING'}, + hgt: {type: 'FLOAT32'}, + src: {type: 'STRING'}, + ssr: {type: 'UINT8'} + } + } + } + }, + featureTables: { + buildings: { + class: 'buildings', + count: 5, + properties: { + f_code: { + bufferView: 0, + offsetType: 'UINT32', + stringOffsetBufferView: 1, + data: ['AL013', 'AL013', 'AL013', 'AL013', 'AL013'] + }, + ffn: { + bufferView: 2, + data: new Int32Array([-999999, -999999, -999999, -999999, -999999]) + }, + geom_type: { + bufferView: 3, + offsetType: 'UINT32', + stringOffsetBufferView: 4, + data: ['areal', 'areal', 'areal', 'areal', 'areal'] + }, + hgt: { + bufferView: 5, + data: new Float32Array([ + 2.736293315887451, 4.505789756774902, 5.469282150268555, 6.345623016357422, + 6.636971473693848 + ]) + }, + src: { + bufferView: 6, + offsetType: 'UINT32', + stringOffsetBufferView: 7, + data: ['maxar', 'maxar', 'maxar', 'maxar', 'maxar'] + }, + ssr: {bufferView: 8, data: new Uint8Array([41, 41, 41, 41, 41])} + } + } + } + } + } + }; + + // Modifies input + t.deepEqual(GLTF_WITH_EXTENSION.json, expectedJson); + + t.end(); +}); + +test('gltf#EXT_feature_metadata - Should handle feature texture attributes', async (t) => { + const GLTF_WITH_TEXTURES = { + buffers: [ + { + arrayBuffer: new Float32Array([0.1, 0.1, 0.1, 0.9, 0.9, 0.1, 0.9, 0.9]).buffer, + byteOffset: 0, + byteLength: 32 + } + ], + images: [ + { + name: 'first', + components: 4, + height: 2, + width: 2, + data: new Uint8Array([24, 24, 24, 255, 28, 28, 28, 255, 35, 35, 35, 255, 24, 24, 24, 255]) + } + ], + + json: { + accessors: [ + { + bufferView: 0, + componentType: 5126, + count: 4, + type: 'VEC2' + } + ], + buffers: [ + { + buffer: 0, + byteOffset: 0, + byteLength: 32 + } + ], + bufferViews: [{buffer: 0, byteOffset: 0, byteLength: 32}], + extensionsUsed: ['EXT_feature_metadata'], + extensions: { + EXT_feature_metadata: { + schema: { + classes: { + 'r3dm::uncertainty_ce90sum': { + properties: { + 'r3dm::uncertainty_ce90sum': { + type: 'UINT8' + } + } + } + } + }, + featureTextures: { + 'r3dm::uncertainty_ce90sum': { + class: 'r3dm::uncertainty_ce90sum', + properties: { + 'r3dm::uncertainty_ce90sum': { + channels: 'r', + texture: { + index: 0, + texCoord: 0 + } + } + } + } + }, + extras: { + draftVersion: '1.0.0' + } + } + }, + textures: [ + { + sampler: 0, + source: 0 + } + ], + meshes: [ + { + primitives: [ + { + attributes: { + TEXCOORD_0: 0 + }, + indices: 3, + material: 0, + mode: 4, + extensions: { + EXT_feature_metadata: { + featureTextures: ['r3dm::uncertainty_ce90sum'] + } + } + } + ] + } + ] + } + }; + + const options = {gltf: {loadImages: true, loadBuffers: true}}; + await decodeExtensions(GLTF_WITH_TEXTURES, options); + + const expectedResult = { + buf: { + arrayBuffer: new Uint32Array([0, 1, 2, 0]).buffer, + byteOffset: 0, + byteLength: 16 + }, + data: [24, 35, 28] + }; + + t.deepEqual(GLTF_WITH_TEXTURES.buffers[1], expectedResult.buf); + + const featureTextures = GLTF_WITH_TEXTURES.json.extensions.EXT_feature_metadata.featureTextures; + t.deepEqual( + // @ts-expect-error + featureTextures['r3dm::uncertainty_ce90sum'].properties['r3dm::uncertainty_ce90sum'].data, + expectedResult.data + ); + t.end(); +}); + +test('gltf#EXT_feature_metadata - Should handle arrays', async (t) => { + const binaryBufferData = [ + 0, 0, 0, 125, 125, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 100, 100, 0, 125, 63, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 100, 100, 0, 255, 255, 0, 255, 125, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 0, 0, 250, 0, 0, 255, 125, 63, 255, 0, 63, 170, 0, 0, 125, 0, 125, 255, 0, 170, + 0, 255, 86, 117, 79, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 156, 191, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 125, 0, 255, 255, 0, 255, 0, 0, 0, 255, 255, 125, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 125, 125, 255, 125, 255, 0, 0, 0, 255, 0, 125, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 125, 63, 255, 0, 0, 0, 0, 0, 125, 0, 125, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 125, 63, 255, 0, 0, 0, 0, 0, 125, 0, + 125, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 125, 63, 255, 0, 0, 0, 0, 0, + 125, 0, 125, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 101, 118, 101, 114, 95, 99, 108, 97, 115, + 115, 105, 102, 105, 101, 100, 117, 110, 100, 101, 102, 98, 117, 105, 108, 100, 105, 110, 103, + 117, 110, 116, 114, 117, 115, 116, 101, 100, 95, 103, 114, 111, 117, 110, 100, 109, 109, 95, + 111, 98, 106, 101, 99, 116, 117, 110, 116, 114, 117, 115, 116, 101, 100, 95, 103, 114, 111, 117, + 110, 100, 103, 114, 97, 115, 115, 108, 97, 110, 100, 98, 97, 114, 114, 101, 110, 95, 103, 114, + 111, 117, 110, 100, 109, 105, 115, 99, 95, 115, 110, 111, 119, 118, 101, 103, 101, 116, 97, 116, + 105, 111, 110, 118, 101, 103, 95, 109, 105, 120, 118, 101, 103, 95, 101, 118, 101, 114, 103, + 114, 101, 101, 110, 118, 101, 103, 95, 100, 101, 99, 105, 100, 117, 111, 117, 115, 118, 101, + 103, 95, 115, 99, 114, 117, 98, 118, 101, 103, 95, 99, 111, 110, 105, 102, 101, 114, 118, 101, + 103, 95, 98, 114, 111, 97, 100, 108, 101, 97, 102, 118, 101, 103, 95, 111, 95, 114, 111, 97, + 100, 118, 101, 103, 95, 111, 95, 98, 117, 105, 108, 100, 105, 110, 103, 118, 101, 103, 95, 111, + 95, 98, 114, 105, 100, 103, 101, 119, 97, 116, 101, 114, 119, 97, 116, 101, 114, 95, 115, 119, + 105, 109, 109, 105, 110, 103, 112, 111, 111, 108, 109, 109, 95, 103, 114, 111, 117, 110, 100, + 114, 111, 97, 100, 100, 105, 114, 116, 95, 114, 111, 97, 100, 101, 108, 101, 118, 97, 116, 101, + 100, 95, 114, 111, 97, 100, 101, 108, 101, 118, 97, 116, 101, 100, 95, 114, 97, 105, 108, 119, + 97, 121, 114, 97, 105, 108, 119, 97, 121, 114, 117, 110, 119, 97, 121, 108, 111, 119, 95, 118, + 101, 103, 101, 116, 97, 116, 105, 111, 110, 108, 111, 119, 95, 118, 101, 103, 95, 101, 118, 101, + 114, 103, 114, 101, 101, 110, 108, 111, 119, 95, 118, 101, 103, 95, 100, 101, 99, 105, 100, 117, + 111, 117, 115, 108, 111, 119, 95, 118, 101, 103, 95, 99, 111, 110, 105, 102, 101, 114, 108, 111, + 119, 95, 118, 101, 103, 95, 98, 114, 111, 97, 100, 108, 101, 97, 102, 109, 101, 100, 105, 117, + 109, 95, 118, 101, 103, 101, 116, 97, 116, 105, 111, 110, 109, 101, 100, 105, 117, 109, 95, 118, + 101, 103, 95, 101, 118, 101, 114, 103, 114, 101, 101, 110, 109, 101, 100, 105, 117, 109, 95, + 118, 101, 103, 95, 100, 101, 99, 105, 100, 117, 111, 117, 115, 109, 101, 100, 105, 117, 109, 95, + 118, 101, 103, 95, 99, 111, 110, 105, 102, 101, 114, 109, 101, 100, 105, 117, 109, 95, 118, 101, + 103, 95, 98, 114, 111, 97, 100, 108, 101, 97, 102, 104, 105, 103, 104, 95, 118, 101, 103, 101, + 116, 97, 116, 105, 111, 110, 104, 105, 103, 104, 95, 118, 101, 103, 95, 101, 118, 101, 114, 103, + 114, 101, 101, 110, 104, 105, 103, 104, 95, 118, 101, 103, 95, 100, 101, 99, 105, 100, 117, 111, + 117, 115, 104, 105, 103, 104, 95, 118, 101, 103, 95, 99, 111, 110, 105, 102, 101, 114, 104, 105, + 103, 104, 95, 118, 101, 103, 95, 98, 114, 111, 97, 100, 108, 101, 97, 102, 0, 0, 0, 0, 16, 0, 0, + 0, 21, 0, 0, 0, 21, 0, 0, 0, 21, 0, 0, 0, 21, 0, 0, 0, 21, 0, 0, 0, 29, 0, 0, 0, 45, 0, 0, 0, + 54, 0, 0, 0, 54, 0, 0, 0, 54, 0, 0, 0, 54, 0, 0, 0, 54, 0, 0, 0, 54, 0, 0, 0, 54, 0, 0, 0, 54, + 0, 0, 0, 54, 0, 0, 0, 54, 0, 0, 0, 54, 0, 0, 0, 54, 0, 0, 0, 70, 0, 0, 0, 79, 0, 0, 0, 92, 0, 0, + 0, 92, 0, 0, 0, 92, 0, 0, 0, 101, 0, 0, 0, 101, 0, 0, 0, 101, 0, 0, 0, 101, 0, 0, 0, 101, 0, 0, + 0, 101, 0, 0, 0, 101, 0, 0, 0, 101, 0, 0, 0, 101, 0, 0, 0, 101, 0, 0, 0, 101, 0, 0, 0, 101, 0, + 0, 0, 101, 0, 0, 0, 101, 0, 0, 0, 101, 0, 0, 0, 111, 0, 0, 0, 118, 0, 0, 0, 131, 0, 0, 0, 144, + 0, 0, 0, 153, 0, 0, 0, 164, 0, 0, 0, 177, 0, 0, 0, 187, 0, 0, 0, 201, 0, 0, 0, 213, 0, 0, 0, + 213, 0, 0, 0, 213, 0, 0, 0, 213, 0, 0, 0, 213, 0, 0, 0, 213, 0, 0, 0, 213, 0, 0, 0, 213, 0, 0, + 0, 213, 0, 0, 0, 213, 0, 0, 0, 213, 0, 0, 0, 218, 0, 0, 0, 218, 0, 0, 0, 218, 0, 0, 0, 218, 0, + 0, 0, 218, 0, 0, 0, 236, 0, 0, 0, 236, 0, 0, 0, 236, 0, 0, 0, 236, 0, 0, 0, 236, 0, 0, 0, 236, + 0, 0, 0, 236, 0, 0, 0, 236, 0, 0, 0, 236, 0, 0, 0, 236, 0, 0, 0, 236, 0, 0, 0, 236, 0, 0, 0, + 236, 0, 0, 0, 236, 0, 0, 0, 236, 0, 0, 0, 245, 0, 0, 0, 249, 0, 0, 0, 249, 0, 0, 0, 2, 1, 0, 0, + 2, 1, 0, 0, 2, 1, 0, 0, 15, 1, 0, 0, 15, 1, 0, 0, 15, 1, 0, 0, 31, 1, 0, 0, 38, 1, 0, 0, 38, 1, + 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, + 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, + 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, + 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, + 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, 1, 0, 0, 44, + 1, 0, 0, 44, 1, 0, 0, 58, 1, 0, 0, 58, 1, 0, 0, 75, 1, 0, 0, 92, 1, 0, 0, 92, 1, 0, 0, 107, 1, + 0, 0, 124, 1, 0, 0, 124, 1, 0, 0, 124, 1, 0, 0, 124, 1, 0, 0, 141, 1, 0, 0, 141, 1, 0, 0, 161, + 1, 0, 0, 181, 1, 0, 0, 181, 1, 0, 0, 199, 1, 0, 0, 219, 1, 0, 0, 219, 1, 0, 0, 219, 1, 0, 0, + 219, 1, 0, 0, 234, 1, 0, 0, 234, 1, 0, 0, 252, 1, 0, 0, 14, 2, 0, 0, 14, 2, 0, 0, 30, 2, 0, 0, + 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, + 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, + 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, + 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, + 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, + 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, + 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, + 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, + 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, + 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, + 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, + 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, + 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, + 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0, 48, 2, 0, 0 + ]; + const GLTF_WITH_EXTENSION = { + buffers: [ + { + arrayBuffer: new Uint8Array(binaryBufferData).buffer, + byteOffset: 0, + byteLength: binaryBufferData.length + } + ], + json: { + buffers: [ + { + byteLength: binaryBufferData.length + } + ], + bufferViews: [ + { + buffer: 0, + byteOffset: 0, + byteLength: 768 + }, + { + buffer: 0, + byteOffset: 768, + byteLength: 560 + }, + { + buffer: 0, + byteOffset: 1328, + byteLength: 1028 + } + ], + extensions: { + EXT_feature_metadata: { + schema: { + classes: { + 'owt::lulc': { + properties: { + color: { + type: 'ARRAY', + componentType: 'UINT8', + componentCount: 3 + }, + name: { + type: 'STRING' + } + } + } + } + }, + featureTables: { + 'owt::lulc': { + class: 'owt::lulc', + count: 8, + properties: { + color: { + bufferView: 0 + }, + name: { + bufferView: 1, + offsetType: 'UINT32', + stringOffsetBufferView: 2 + } + } + } + } + } + }, + extensionsUsed: ['EXT_feature_metadata'] + } + }; + + const options = {gltf: {loadImages: true, loadBuffers: true}}; + await decodeExtensions(GLTF_WITH_EXTENSION, options); + + const expectedResult = { + colorData: [ + { + 0: 0, + 1: 0, + 2: 0 + }, + { + 0: 125, + 1: 125, + 2: 125 + }, + { + 0: 0, + 1: 0, + 2: 0 + }, + { + 0: 0, + 1: 0, + 2: 0 + }, + { + 0: 0, + 1: 0, + 2: 0 + }, + { + 0: 0, + 1: 0, + 2: 0 + }, + { + 0: 255, + 1: 0, + 2: 0 + }, + { + 0: 100, + 1: 100, + 2: 0 + } + ], + nameData: ['never_classified', 'undef', '', '', '', '', 'building', 'untrusted_ground'] + }; + + const EXT_feature_metadata = GLTF_WITH_EXTENSION.json.extensions.EXT_feature_metadata; + t.deepEqual( + // @ts-expect-error + EXT_feature_metadata.featureTables['owt::lulc'].properties.color.data, + expectedResult.colorData + ); + t.deepEqual( + // @ts-expect-error + EXT_feature_metadata.featureTables['owt::lulc'].properties.name.data, + expectedResult.nameData + ); + t.end(); +}); diff --git a/modules/gltf/test/lib/extensions/EXT_mesh_features.spec.ts b/modules/gltf/test/lib/extensions/EXT_mesh_features.spec.ts new file mode 100644 index 0000000000..aec8b68e62 --- /dev/null +++ b/modules/gltf/test/lib/extensions/EXT_mesh_features.spec.ts @@ -0,0 +1,181 @@ +/* eslint-disable camelcase */ +import test from 'tape-promise/tape'; + +import {decodeExtensions} from '../../../src/lib/api/gltf-extensions'; + +test('gltf#EXT_mesh_features - Should decode', async (t) => { + const binaryBufferData = [ + 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 33, 223, 70, 43, 39, + 58, 199, 113, 55, 81, 71, 94, 21, 60, 71, 154, 68, 219, 198, 113, 55, 81, 199, 183, 210, 225, + 198, 43, 39, 58, 71, 113, 55, 81, 199, 94, 21, 60, 199, 211, 158, 216, 70, 113, 55, 81, 71, 0, + 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 63, + 0, 1, 10, 0, 68, 82, 89, 71, 82, 79, 85, 78, 68, 0, 108, 60, 0, 95, 5, 0, 1, 3, 0, 0, + // the following is to allign the buffer: + 0, 0 + ]; + + const GLTF_WITH_EXTENSION = { + buffers: [ + { + arrayBuffer: new Uint8Array(binaryBufferData).buffer, + byteOffset: 0, + byteLength: 128 + } + ], + images: [ + { + shape: [1, 1, 4], + data: { + '0': 111, + '1': 103, + '2': 88, + '3': 255 + }, + width: 1, + height: 1, + components: 4, + layers: [1] + }, + { + shape: [1, 1, 4], + data: { + '0': 1, + '1': 1, + '2': 1, + '3': 255 + }, + width: 1, + height: 1, + components: 4, + layers: [1] + } + ], + json: { + buffers: [ + { + byteLength: 126 + } + ], + bufferViews: [ + { + buffer: 0, + byteOffset: 0, + byteLength: 24, + target: 34963 + }, + { + buffer: 0, + byteOffset: 24, + byteLength: 48, + target: 34962 + }, + { + buffer: 0, + byteOffset: 72, + byteLength: 32, + target: 34962 + }, + { + buffer: 0, + byteOffset: 104, + byteLength: 3 + }, + { + buffer: 0, + byteOffset: 107, + byteLength: 10 + }, + { + buffer: 0, + byteOffset: 117, + byteLength: 3 + }, + { + buffer: 0, + byteOffset: 120, + byteLength: 3 + }, + { + buffer: 0, + byteOffset: 123, + byteLength: 3 + } + ], + accessors: [ + { + bufferView: 0, + byteOffset: 0, + componentType: 5125, + count: 6, + type: 'SCALAR' + }, + { + bufferView: 1, + byteOffset: 0, + componentType: 5126, + count: 4, + type: 'VEC3', + max: [48149.36831235699, 47655.16766258143, 53559.4425966118], + min: [-48149.36831235606, -47655.16766258143, -53559.4425966118] + }, + { + bufferView: 2, + byteOffset: 0, + componentType: 5126, + count: 4, + type: 'VEC2' + } + ], + extensions: {}, + extensionsUsed: ['EXT_mesh_features'], + meshes: [ + { + primitives: [ + { + attributes: { + TEXCOORD_0: 2, + POSITION: 1 + }, + indices: 0, + material: 0, + extensions: { + EXT_mesh_features: { + featureIds: [ + { + featureCount: 1, + texture: { + index: 1 + }, + propertyTable: 0, + data: undefined + } + ] + } + } + } + ] + } + ], + textures: [ + { + sampler: 0, + source: 0 + }, + { + sampler: 1, + source: 1 + } + ] + } + }; + const options = {gltf: {loadImages: true, loadBuffers: true}}; + await decodeExtensions(GLTF_WITH_EXTENSION, options); + // GLTF_WITH_EXTENSION has been modified + + t.deepEqual( + GLTF_WITH_EXTENSION.json.meshes[0].primitives[0].extensions.EXT_mesh_features.featureIds[0] + .data, + [1, 1, 1, 1] + ); + t.end(); +}); diff --git a/modules/gltf/test/lib/extensions/EXT_structural_metadata.spec.ts b/modules/gltf/test/lib/extensions/EXT_structural_metadata.spec.ts new file mode 100644 index 0000000000..67ff0c986b --- /dev/null +++ b/modules/gltf/test/lib/extensions/EXT_structural_metadata.spec.ts @@ -0,0 +1,188 @@ +/* eslint-disable camelcase */ +import test from 'tape-promise/tape'; + +import {decodeExtensions} from '../../../src/lib/api/gltf-extensions'; + +test('gltf#EXT_structural_metadata - Should decode', async (t) => { + const binaryBufferData = [ + 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 33, 223, 70, 43, 39, + 58, 199, 113, 55, 81, 71, 94, 21, 60, 71, 154, 68, 219, 198, 113, 55, 81, 199, 183, 210, 225, + 198, 43, 39, 58, 71, 113, 55, 81, 199, 94, 21, 60, 199, 211, 158, 216, 70, 113, 55, 81, 71, 0, + 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 63, + 0, 1, 10, 0, 68, 82, 89, 71, 82, 79, 85, 78, 68, 0, 108, 60, 0, 95, 5, 0, 1, 3, 0, 0 + ]; + const GLTF_WITH_EXTENSION = { + buffers: [ + { + arrayBuffer: new Uint8Array(binaryBufferData).buffer, + byteOffset: 0, + byteLength: 128 + } + ], + json: { + extensionsUsed: ['EXT_structural_metadata', 'EXT_mesh_features'], + buffers: [{byteLength: 126}], + bufferViews: [ + {buffer: 0, byteOffset: 0, byteLength: 24, target: 34963}, + {buffer: 0, byteOffset: 24, byteLength: 48, target: 34962}, + {buffer: 0, byteOffset: 72, byteLength: 32, target: 34962}, + {buffer: 0, byteOffset: 104, byteLength: 3}, + {buffer: 0, byteOffset: 107, byteLength: 10}, + {buffer: 0, byteOffset: 117, byteLength: 3}, + {buffer: 0, byteOffset: 120, byteLength: 3}, + {buffer: 0, byteOffset: 123, byteLength: 3} + ], + extensions: { + EXT_structural_metadata: { + schema: { + id: 'schema', + classes: { + CDBMaterialsClass: { + properties: { + name: {type: 'STRING', required: true}, + substrates: { + type: 'ENUM', + enumType: 'CDBBaseMaterial', + array: true, + required: true + }, + weights: {type: 'SCALAR', componentType: 'UINT8', array: true, required: true} + } + } + }, + enums: { + CDBBaseMaterial: { + valueType: 'UINT8', + values: [ + {name: 'BM_ASH', description: 'Ash (generic)', value: 0}, + {name: 'BM_ASH-VOLCANIC', description: 'Volcanic Ash', value: 1}, + {name: 'BM_ASPHALT', description: 'Asphalt', value: 2}, + {name: 'BM_BOULDERS', description: 'Boulders', value: 79}, + {name: 'BM_BRICK', description: 'Brick', value: 3}, + { + name: 'BM_MOISTURE', + description: 'Embedded water in porous materials', + value: 60 + }, + {name: 'BM_SOIL', description: 'Generic soil', value: 108} + ] + } + } + }, + propertyTables: [ + { + name: 'CDBMaterialFeatureTable', + class: 'CDBMaterialsClass', + count: 2, + properties: { + name: { + values: 4, + stringOffsets: 3, + stringOffsetType: 'UINT8' + }, + substrates: { + values: 5, + arrayOffsets: 7, + arrayOffsetType: 'UINT8' + }, + weights: { + values: 6, + arrayOffsets: 7, + arrayOffsetType: 'UINT8' + } + } + } + ] + } + } + } + }; + + const options = {gltf: {loadImages: true, loadBuffers: true}}; + await decodeExtensions(GLTF_WITH_EXTENSION, options); + + const expectedJson = { + extensionsUsed: ['EXT_structural_metadata', 'EXT_mesh_features'], + buffers: [{byteLength: 126}], + bufferViews: [ + {buffer: 0, byteOffset: 0, byteLength: 24, target: 34963}, + {buffer: 0, byteOffset: 24, byteLength: 48, target: 34962}, + {buffer: 0, byteOffset: 72, byteLength: 32, target: 34962}, + {buffer: 0, byteOffset: 104, byteLength: 3}, + {buffer: 0, byteOffset: 107, byteLength: 10}, + {buffer: 0, byteOffset: 117, byteLength: 3}, + {buffer: 0, byteOffset: 120, byteLength: 3}, + {buffer: 0, byteOffset: 123, byteLength: 3} + ], + extensions: { + EXT_structural_metadata: { + schema: { + id: 'schema', + classes: { + CDBMaterialsClass: { + properties: { + name: {type: 'STRING', required: true}, + substrates: { + type: 'ENUM', + enumType: 'CDBBaseMaterial', + array: true, + required: true + }, + weights: {type: 'SCALAR', componentType: 'UINT8', array: true, required: true} + } + } + }, + enums: { + CDBBaseMaterial: { + valueType: 'UINT8', + values: [ + {name: 'BM_ASH', description: 'Ash (generic)', value: 0}, + {name: 'BM_ASH-VOLCANIC', description: 'Volcanic Ash', value: 1}, + {name: 'BM_ASPHALT', description: 'Asphalt', value: 2}, + {name: 'BM_BOULDERS', description: 'Boulders', value: 79}, + {name: 'BM_BRICK', description: 'Brick', value: 3}, + { + name: 'BM_MOISTURE', + description: 'Embedded water in porous materials', + value: 60 + }, + {name: 'BM_SOIL', description: 'Generic soil', value: 108} + ] + } + } + }, + propertyTables: [ + { + name: 'CDBMaterialFeatureTable', + class: 'CDBMaterialsClass', + count: 2, + properties: { + name: { + values: 4, + stringOffsets: 3, + stringOffsetType: 'UINT8', + data: ['\u0000', 'DRYGROUND'] + }, + substrates: { + values: 5, + arrayOffsets: 7, + arrayOffsetType: 'UINT8', + data: [['BM_ASH'], ['BM_SOIL', 'BM_MOISTURE']] + }, + weights: { + values: 6, + arrayOffsets: 7, + arrayOffsetType: 'UINT8', + data: [new Uint8Array([0]), new Uint8Array([95, 5])] + } + } + } + ] + } + } + }; + + // Modifies input + t.deepEqual(GLTF_WITH_EXTENSION.json, expectedJson); + t.end(); +}); diff --git a/modules/gltf/test/lib/extensions/KHR_draco_mesh_compression.spec.js b/modules/gltf/test/lib/extensions/KHR_draco_mesh_compression.spec.ts similarity index 100% rename from modules/gltf/test/lib/extensions/KHR_draco_mesh_compression.spec.js rename to modules/gltf/test/lib/extensions/KHR_draco_mesh_compression.spec.ts diff --git a/modules/gltf/test/lib/extensions/KHR_lights_punctual.spec.js b/modules/gltf/test/lib/extensions/KHR_lights_punctual.spec.ts similarity index 100% rename from modules/gltf/test/lib/extensions/KHR_lights_punctual.spec.js rename to modules/gltf/test/lib/extensions/KHR_lights_punctual.spec.ts diff --git a/modules/gltf/test/lib/extensions/KHR_materials_unlit.spec.js b/modules/gltf/test/lib/extensions/KHR_materials_unlit.spec.ts similarity index 100% rename from modules/gltf/test/lib/extensions/KHR_materials_unlit.spec.js rename to modules/gltf/test/lib/extensions/KHR_materials_unlit.spec.ts diff --git a/modules/gltf/test/lib/extensions/KHR_texture_transform.spec.js b/modules/gltf/test/lib/extensions/KHR_texture_transform.spec.ts similarity index 100% rename from modules/gltf/test/lib/extensions/KHR_texture_transform.spec.js rename to modules/gltf/test/lib/extensions/KHR_texture_transform.spec.ts diff --git a/modules/gltf/test/lib/glb/glb-custom-payload.spec.js b/modules/gltf/test/lib/glb/glb-custom-payload.spec.ts similarity index 100% rename from modules/gltf/test/lib/glb/glb-custom-payload.spec.js rename to modules/gltf/test/lib/glb/glb-custom-payload.spec.ts diff --git a/modules/gltf/test/lib/glb/glb-encode-parse.spec.js b/modules/gltf/test/lib/glb/glb-encode-parse.spec.ts similarity index 100% rename from modules/gltf/test/lib/glb/glb-encode-parse.spec.js rename to modules/gltf/test/lib/glb/glb-encode-parse.spec.ts diff --git a/modules/gltf/test/lib/glb/glb-encoder-decoder.spec.js b/modules/gltf/test/lib/glb/glb-encoder-decoder.spec.ts similarity index 100% rename from modules/gltf/test/lib/glb/glb-encoder-decoder.spec.js rename to modules/gltf/test/lib/glb/glb-encoder-decoder.spec.ts diff --git a/modules/gltf/test/lib/gltf-utils/gltf-attribute-utils.spec.js b/modules/gltf/test/lib/gltf-utils/gltf-attribute-utils.spec.ts similarity index 100% rename from modules/gltf/test/lib/gltf-utils/gltf-attribute-utils.spec.js rename to modules/gltf/test/lib/gltf-utils/gltf-attribute-utils.spec.ts diff --git a/modules/gltf/test/lib/utils/encode-utils.spec.js b/modules/gltf/test/lib/utils/encode-utils.spec.ts similarity index 100% rename from modules/gltf/test/lib/utils/encode-utils.spec.js rename to modules/gltf/test/lib/utils/encode-utils.spec.ts diff --git a/modules/gltf/test/meshopt/meshopt-decoder.spec.js b/modules/gltf/test/meshopt/meshopt-decoder.spec.ts similarity index 100% rename from modules/gltf/test/meshopt/meshopt-decoder.spec.js rename to modules/gltf/test/meshopt/meshopt-decoder.spec.ts diff --git a/modules/gltf/tsconfig.json b/modules/gltf/tsconfig.json index 71e6eec87d..5bc67ae03b 100644 --- a/modules/gltf/tsconfig.json +++ b/modules/gltf/tsconfig.json @@ -11,6 +11,7 @@ {"path": "../draco"}, {"path": "../images"}, {"path": "../loader-utils"}, - {"path": "../textures"} + {"path": "../textures"}, + {"path": "../math"} ] } diff --git a/modules/i3s/package.json b/modules/i3s/package.json index 0fd1cde19a..de16ab9832 100644 --- a/modules/i3s/package.json +++ b/modules/i3s/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/i3s", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "i3s .", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -19,8 +20,15 @@ "mesh" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -28,27 +36,26 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle && npm run build-worker && npm run build-worker-node", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-worker && npm run build-worker-node", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/i3s-content-worker.ts --outfile=dist/i3s-content-worker.js --target=esnext --bundle --define:__VERSION__=\\\"$npm_package_version\\\"", "build-worker-node": "esbuild src/workers/i3s-content-worker-node.ts --outfile=dist/i3s-content-worker-node.js --platform=node --target=node16 --minify --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { - "@loaders.gl/compression": "4.0.0-alpha.13", - "@loaders.gl/draco": "4.0.0-alpha.13", - "@loaders.gl/images": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", - "@loaders.gl/textures": "4.0.0-alpha.13", - "@loaders.gl/tiles": "4.0.0-alpha.13", - "@luma.gl/constants": "^8.5.4", - "@math.gl/core": "^3.5.1", - "@math.gl/culling": "^3.5.1", - "@math.gl/geospatial": "^3.5.1", - "md5": "^2.3.0" + "@loaders.gl/compression": "4.0.3", + "@loaders.gl/crypto": "4.0.3", + "@loaders.gl/draco": "4.0.3", + "@loaders.gl/images": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "@loaders.gl/textures": "4.0.3", + "@loaders.gl/tiles": "4.0.3", + "@math.gl/core": "^4.0.0", + "@math.gl/culling": "^4.0.0", + "@math.gl/geospatial": "^4.0.0" }, "peerDependencies": { - "@loaders.gl/core": "^4.0.0-alpha.8" + "@loaders.gl/core": "^4.0.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/i3s/src/arcgis-webscene-loader.ts b/modules/i3s/src/arcgis-webscene-loader.ts index 672cfb6d96..55ced0e6d4 100644 --- a/modules/i3s/src/arcgis-webscene-loader.ts +++ b/modules/i3s/src/arcgis-webscene-loader.ts @@ -1,17 +1,23 @@ -import type {LoaderWithParser} from '@loaders.gl/loader-utils'; -import type {ArcGisWebSceneData} from './types'; +import type {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; +import type {ArcGISWebSceneData} from './types'; import {parseWebscene} from './lib/parsers/parse-arcgis-webscene'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. -const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'beta'; +const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; + +export type ArcGISWebSceneLoaderOptions = LoaderOptions & {}; /** - * Loader for ArcGis WebScene + * Loader for ArcGIS WebScene * Spec - https://developers.arcgis.com/web-scene-specification/objects/webscene/ */ -export const ArcGisWebSceneLoader: LoaderWithParser = { +export const ArcGISWebSceneLoader: LoaderWithParser< + ArcGISWebSceneData, + never, + ArcGISWebSceneLoaderOptions +> = { name: 'ArcGIS Web Scene Loader', id: 'arcgis-web-scene', module: 'i3s', @@ -23,9 +29,9 @@ export const ArcGisWebSceneLoader: LoaderWithParser = { }; /** - * Parse ArcGis webscene + * Parse ArcGIS webscene * @param data */ -async function parse(data: ArrayBuffer): Promise { +async function parse(data: ArrayBuffer): Promise { return parseWebscene(data); } diff --git a/modules/i3s/src/bundle.ts b/modules/i3s/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/i3s/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/i3s/src/i3s-attribute-loader.ts b/modules/i3s/src/i3s-attribute-loader.ts index 4f07702f21..dcf9fde668 100644 --- a/modules/i3s/src/i3s-attribute-loader.ts +++ b/modules/i3s/src/i3s-attribute-loader.ts @@ -1,5 +1,6 @@ import type {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; import {load} from '@loaders.gl/core'; +import type {I3SLoaderOptions} from './i3s-loader'; import type {I3STileAttributes} from './lib/parsers/parse-i3s-attribute'; import {parseI3STileAttribute} from './lib/parsers/parse-i3s-attribute'; import {getUrlWithToken} from './lib/utils/url-utils'; @@ -13,7 +14,7 @@ const REJECTED_STATUS = 'rejected'; /** * Loader for I3S attributes */ -export const I3SAttributeLoader: LoaderWithParser = { +export const I3SAttributeLoader: LoaderWithParser = { name: 'I3S Attribute', id: 'i3s-attribute', module: 'i3s', diff --git a/modules/i3s/src/i3s-building-scene-layer-loader.ts b/modules/i3s/src/i3s-building-scene-layer-loader.ts index 66c4a50c7f..10b706ce4c 100644 --- a/modules/i3s/src/i3s-building-scene-layer-loader.ts +++ b/modules/i3s/src/i3s-building-scene-layer-loader.ts @@ -1,4 +1,5 @@ import type {LoaderWithParser, LoaderOptions, LoaderContext} from '@loaders.gl/loader-utils'; +import type {I3SLoaderOptions} from './i3s-loader'; import type {BuildingSceneLayerTileset} from './types'; import {parseBuildingSceneLayer} from './lib/parsers/parse-i3s-building-scene-layer'; @@ -6,11 +7,15 @@ import {parseBuildingSceneLayer} from './lib/parsers/parse-i3s-building-scene-la // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. -const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'beta'; +const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; /** * Loader for I3S - Building Scene Layer */ -export const I3SBuildingSceneLayerLoader: LoaderWithParser = { +export const I3SBuildingSceneLayerLoader: LoaderWithParser< + BuildingSceneLayerTileset, + never, + I3SLoaderOptions +> = { name: 'I3S Building Scene Layer', id: 'i3s-building-scene-layer', module: 'i3s', diff --git a/modules/i3s/src/i3s-content-loader.ts b/modules/i3s/src/i3s-content-loader.ts index ac42f273e9..82c37fb283 100644 --- a/modules/i3s/src/i3s-content-loader.ts +++ b/modules/i3s/src/i3s-content-loader.ts @@ -1,16 +1,17 @@ import type {LoaderWithParser, LoaderContext} from '@loaders.gl/loader-utils'; import type {I3SLoaderOptions} from './i3s-loader'; import {parseI3STileContent} from './lib/parsers/parse-i3s-tile-content'; -import {I3STileOptions, I3STilesetOptions} from './types'; +import {I3STileContent, I3STileOptions, I3STilesetOptions} from './types'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. -const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'beta'; +const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; + /** * Loader for I3S - Indexed 3D Scene Layer */ -export const I3SContentLoader: LoaderWithParser = { +export const I3SContentLoader: LoaderWithParser = { name: 'I3S Content (Indexed Scene Layers)', id: 'i3s-content', module: 'i3s', diff --git a/modules/i3s/src/i3s-loader.ts b/modules/i3s/src/i3s-loader.ts index cc40ecf302..bb41ffc2e5 100644 --- a/modules/i3s/src/i3s-loader.ts +++ b/modules/i3s/src/i3s-loader.ts @@ -1,10 +1,11 @@ -import type {LoaderWithParser} from '@loaders.gl/loader-utils'; +import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; import {parse} from '@loaders.gl/core'; +import type {I3STilesetHeader} from './types'; import {I3SContentLoader} from './i3s-content-loader'; import {normalizeTileData, normalizeTilesetData} from './lib/parsers/parse-i3s'; import {COORDINATE_SYSTEM} from './lib/parsers/constants'; import {I3SParseOptions} from './types'; -import {LoaderOptions} from './../../loader-utils/src/types'; +import {getUrlWithoutParams} from './lib/utils/url-utils'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. @@ -22,7 +23,7 @@ export type I3SLoaderOptions = LoaderOptions & { /** * Loader for I3S - Indexed 3D Scene Layer */ -export const I3SLoader: LoaderWithParser = { +export const I3SLoader: LoaderWithParser = { name: 'I3S (Indexed Scene Layers)', id: 'i3s', module: 'i3s', @@ -42,13 +43,12 @@ export const I3SLoader: LoaderWithParser = { useDracoGeometry: true, useCompressedTextures: true, decodeTextures: true, - coordinateSystem: COORDINATE_SYSTEM.METER_OFFSETS, - colorsByAttribute: null + coordinateSystem: COORDINATE_SYSTEM.METER_OFFSETS } } }; -async function parseI3S(data, options: I3SLoaderOptions = {}, context) { +async function parseI3S(data, options: I3SLoaderOptions = {}, context): Promise { const url = context.url; options.i3s = options.i3s || {}; const magicNumber = getMagicNumber(data); @@ -58,17 +58,19 @@ async function parseI3S(data, options: I3SLoaderOptions = {}, context) { throw new Error('Files with .slpk extention currently are not supported by I3SLoader'); } + const urlWithoutParams = getUrlWithoutParams(url); + // auto detect file type based on url let isTileset; if (options.i3s.isTileset === 'auto') { - isTileset = TILESET_REGEX.test(url); + isTileset = TILESET_REGEX.test(urlWithoutParams); } else { isTileset = options.i3s.isTileset; } let isTileHeader; if (options.isTileHeader === 'auto') { - isTileHeader = TILE_HEADER_REGEX.test(url); + isTileHeader = TILE_HEADER_REGEX.test(urlWithoutParams); } else { isTileHeader = options.i3s.isTileHeader; } @@ -94,11 +96,9 @@ async function parseTileset(data, options: I3SLoaderOptions, context) { if (tilesetJson?.layerType === POINT_CLOUD) { throw new Error('Point Cloud layers currently are not supported by I3SLoader'); } - // eslint-disable-next-line no-use-before-define - tilesetJson.loader = I3SLoader; - await normalizeTilesetData(tilesetJson, options, context); - return tilesetJson; + const tilesetPostprocessed = await normalizeTilesetData(tilesetJson, options, context); + return tilesetPostprocessed; } async function parseTile(data, context) { diff --git a/modules/i3s/src/i3s-node-page-loader.ts b/modules/i3s/src/i3s-node-page-loader.ts index 7cceb9a7e8..b0bb8f1d53 100644 --- a/modules/i3s/src/i3s-node-page-loader.ts +++ b/modules/i3s/src/i3s-node-page-loader.ts @@ -1,18 +1,15 @@ import type {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; +import type {I3SLoaderOptions} from './i3s-loader'; import type {NodePage} from './types'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; -async function parseNodePage(data: ArrayBuffer, options?: LoaderOptions): Promise { - return JSON.parse(new TextDecoder().decode(data)) as NodePage; -} - /** * Loader for I3S node pages */ -export const I3SNodePageLoader: LoaderWithParser = { +export const I3SNodePageLoader: LoaderWithParser = { name: 'I3S Node Page', id: 'i3s-node-page', module: 'i3s', @@ -20,5 +17,11 @@ export const I3SNodePageLoader: LoaderWithParser mimeTypes: ['application/json'], parse: parseNodePage, extensions: ['json'], - options: {} + options: { + i3s: {} + } }; + +async function parseNodePage(data: ArrayBuffer, options?: LoaderOptions): Promise { + return JSON.parse(new TextDecoder().decode(data)) as NodePage; +} diff --git a/modules/i3s/src/i3s-slpk-loader.ts b/modules/i3s/src/i3s-slpk-loader.ts index 63216c431f..4251c679f2 100644 --- a/modules/i3s/src/i3s-slpk-loader.ts +++ b/modules/i3s/src/i3s-slpk-loader.ts @@ -1,27 +1,36 @@ -import {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; -import {parseSLPK} from './lib/parsers/parse-slpk/parse-slpk'; +import type {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; +import {DataViewFile} from '@loaders.gl/loader-utils'; +import {parseSLPKArchive} from './lib/parsers/parse-slpk/parse-slpk'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; +/** options to load data from SLPK */ export type SLPKLoaderOptions = LoaderOptions & { slpk?: { + /** path inside the slpk archive */ path?: string; + /** mode of the path */ pathMode?: 'http' | 'raw'; }; }; /** - * Loader for SLPK - Scene Layer Package + * Loader for SLPK - Scene Layer Package (Archive I3S format) + * @todo - this reloads the entire archive for every tile, should be optimized + * @todo - this should be updated to use `parseFile` and ReadableFile */ -export const SLPKLoader: LoaderWithParser = { +export const SLPKLoader: LoaderWithParser = { name: 'I3S SLPK (Scene Layer Package)', id: 'slpk', module: 'i3s', version: VERSION, mimeTypes: ['application/octet-stream'], - parse: parseSLPK, extensions: ['slpk'], - options: {} + options: {}, + parse: async (data: ArrayBuffer, options: SLPKLoaderOptions = {}): Promise => { + const archive = await parseSLPKArchive(new DataViewFile(new DataView(data))); + return archive.getFile(options.slpk?.path ?? '', options.slpk?.pathMode); + } }; diff --git a/modules/i3s/src/index.ts b/modules/i3s/src/index.ts index c50f71ed3f..a1c3a8f969 100644 --- a/modules/i3s/src/index.ts +++ b/modules/i3s/src/index.ts @@ -1,9 +1,13 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors export type { BoundingVolumes, Mbs, Obb, + I3STilesetHeader, + I3STileContent, + I3STileHeader, SceneLayer3D, AttributeStorageInfo, Field, @@ -29,10 +33,10 @@ export type { Histogram, ValueCount, BuildingSceneSublayer, - DATA_TYPE, OperationalLayer, TextureSetDefinitionFormats } from './types'; +export type {I3SLoaderOptions} from './i3s-loader'; export {COORDINATE_SYSTEM} from './lib/parsers/constants'; @@ -42,6 +46,8 @@ export {I3SContentLoader} from './i3s-content-loader'; export {I3SAttributeLoader, loadFeatureAttributes} from './i3s-attribute-loader'; export {I3SBuildingSceneLayerLoader} from './i3s-building-scene-layer-loader'; export {I3SNodePageLoader} from './i3s-node-page-loader'; -export {ArcGisWebSceneLoader} from './arcgis-webscene-loader'; -export {parseZipLocalFileHeader} from './lib/parsers/parse-zip/local-file-header'; -export {FileProvider} from './lib/parsers/parse-zip/file-provider'; +export {ArcGISWebSceneLoader} from './arcgis-webscene-loader'; + +export type {SLPKArchive} from './lib/parsers/parse-slpk/slpk-archieve'; +export {parseSLPKArchive} from './lib/parsers/parse-slpk/parse-slpk'; +export {customizeColors} from './lib/utils/customize-colors'; diff --git a/modules/i3s/src/lib/helpers/i3s-nodepages-tiles.ts b/modules/i3s/src/lib/helpers/i3s-nodepages-tiles.ts index 1cb81bf89c..031d5c8677 100644 --- a/modules/i3s/src/lib/helpers/i3s-nodepages-tiles.ts +++ b/modules/i3s/src/lib/helpers/i3s-nodepages-tiles.ts @@ -5,7 +5,6 @@ import {normalizeTileNonUrlData} from '../parsers/parse-i3s'; import {getUrlWithToken, generateTilesetAttributeUrls} from '../utils/url-utils'; import type {LoaderOptions} from '@loaders.gl/loader-utils'; import { - I3STilesetHeader, LodSelection, NodePage, NodeInPage, @@ -14,14 +13,15 @@ import { I3SMaterialDefinition, I3STextureFormat, MeshGeometry, - I3STileHeader + I3STileHeader, + SceneLayer3D } from '../../types'; /** * class I3SNodePagesTiles - loads nodePages and form i3s tiles from them */ export default class I3SNodePagesTiles { - tileset: I3STilesetHeader; + tileset: SceneLayer3D; nodePages: NodePage[] = []; pendingNodePages: {promise: Promise; status: 'Pending' | 'Done'}[] = []; nodesPerPage: number; @@ -29,16 +29,19 @@ export default class I3SNodePagesTiles { lodSelectionMetricType?: string; textureDefinitionsSelectedFormats: ({format: I3STextureFormat; name: string} | null)[] = []; nodesInNodePages: number; + url: string; private textureLoaderOptions: {[key: string]: any} = {}; /** * @constructs * Create a I3SNodePagesTiles instance. * @param tileset - i3s tileset header ('layers/0') + * @param url - tileset url * @param options - i3s loader options */ - constructor(tileset: I3STilesetHeader, options: LoaderOptions) { + constructor(tileset: SceneLayer3D, url: string = '', options: LoaderOptions) { this.tileset = {...tileset}; // spread the tileset to avoid circular reference + this.url = url; this.nodesPerPage = tileset.nodePages?.nodesPerPage || 64; this.lodSelectionMetricType = tileset.nodePages?.lodSelectionMetricType; this.options = options; @@ -55,7 +58,7 @@ export default class I3SNodePagesTiles { const pageIndex = Math.floor(id / this.nodesPerPage); if (!this.nodePages[pageIndex] && !this.pendingNodePages[pageIndex]) { const nodePageUrl = getUrlWithToken( - `${this.tileset.url}/nodepages/${pageIndex}`, + `${this.url}/nodepages/${pageIndex}`, // @ts-expect-error this.options is not properly typed this.options.i3s?.token ); @@ -114,11 +117,15 @@ export default class I3SNodePagesTiles { materialDefinition = nodeMaterialDefinition; textureFormat = textureData.format || textureFormat; if (textureData.name) { - textureUrl = `${this.tileset.url}/nodes/${node.mesh.material.resource}/textures/${textureData.name}`; + textureUrl = `${this.url}/nodes/${node.mesh.material.resource}/textures/${textureData.name}`; } if (this.tileset.attributeStorageInfo) { - attributeUrls = generateTilesetAttributeUrls(this.tileset, node.mesh.attribute.resource); + attributeUrls = generateTilesetAttributeUrls( + this.tileset, + this.url, + node.mesh.attribute.resource + ); } } @@ -169,7 +176,7 @@ export default class I3SNodePagesTiles { geometryDefinition.geometryBuffers[geometryIndex].compressedAttributes ); result = { - url: `${this.tileset.url}/nodes/${meshGeometryData.resource}/geometries/${geometryIndex}`, + url: `${this.url}/nodes/${meshGeometryData.resource}/geometries/${geometryIndex}`, isDracoGeometry }; } @@ -234,7 +241,7 @@ export default class I3SNodePagesTiles { * @param tileset - I3S layer data * @returns */ - private initSelectedFormatsForTextureDefinitions(tileset: I3STilesetHeader): void { + private initSelectedFormatsForTextureDefinitions(tileset: SceneLayer3D): void { this.textureDefinitionsSelectedFormats = []; const possibleI3sFormats = this.getSupportedTextureFormats(); const textureSetDefinitions = tileset.textureSetDefinitions || []; diff --git a/modules/i3s/src/lib/parsers/constants.ts b/modules/i3s/src/lib/parsers/constants.ts index 7ff2377c82..e3df68af7a 100644 --- a/modules/i3s/src/lib/parsers/constants.ts +++ b/modules/i3s/src/lib/parsers/constants.ts @@ -1,17 +1,19 @@ -import GL from '@luma.gl/constants'; -import {DATA_TYPE} from '../../types'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {GL} from '@loaders.gl/math'; export function getConstructorForDataFormat(dataType: string) { switch (dataType) { - case DATA_TYPE.UInt8: + case 'UInt8': return Uint8Array; - case DATA_TYPE.UInt16: + case 'UInt16': return Uint16Array; - case DATA_TYPE.UInt32: + case 'UInt32': return Uint32Array; - case DATA_TYPE.Float32: + case 'Float32': return Float32Array; - case DATA_TYPE.UInt64: + case 'UInt64': return Float64Array; default: throw new Error(`parse i3s tile content: unknown type of data: ${dataType}`); @@ -32,18 +34,18 @@ export const GL_TYPE_MAP: {[key: string]: number} = { */ export function sizeOf(dataType: string): number { switch (dataType) { - case DATA_TYPE.UInt8: + case 'UInt8': return 1; - case DATA_TYPE.UInt16: - case DATA_TYPE.Int16: + case 'UInt16': + case 'Int16': return 2; - case DATA_TYPE.UInt32: - case DATA_TYPE.Int32: - case DATA_TYPE.Float32: + case 'UInt32': + case 'Int32': + case 'Float32': return 4; - case DATA_TYPE.UInt64: - case DATA_TYPE.Int64: - case DATA_TYPE.Float64: + case 'UInt64': + case 'Int64': + case 'Float64': return 8; default: throw new Error(`parse i3s tile content: unknown size of data: ${dataType}`); diff --git a/modules/i3s/src/lib/parsers/parse-arcgis-webscene.ts b/modules/i3s/src/lib/parsers/parse-arcgis-webscene.ts index 3a45b0c733..4e45ac8eb1 100644 --- a/modules/i3s/src/lib/parsers/parse-arcgis-webscene.ts +++ b/modules/i3s/src/lib/parsers/parse-arcgis-webscene.ts @@ -1,5 +1,5 @@ import {JSONLoader, load} from '@loaders.gl/core'; -import type {ArcGisWebSceneData, OperationalLayer} from '../../types'; +import type {ArcGISWebSceneData, OperationalLayer} from '../../types'; /** * WKID, or Well-Known ID, of the CRS. Specify either WKID or WKT of the CRS. @@ -30,7 +30,7 @@ const NOT_SUPPORTED_CRS_ERROR = 'NOT_SUPPORTED_CRS_ERROR'; * Parses ArcGIS WebScene * @param data */ -export async function parseWebscene(data: ArrayBuffer): Promise { +export async function parseWebscene(data: ArrayBuffer): Promise { const layer0 = JSON.parse(new TextDecoder().decode(data)); const {operationalLayers} = layer0; const {layers, unsupportedLayers} = await parseOperationalLayers(operationalLayers, true); diff --git a/modules/i3s/src/lib/parsers/parse-i3s-attribute.ts b/modules/i3s/src/lib/parsers/parse-i3s-attribute.ts index b45b352f76..05fd40b66d 100644 --- a/modules/i3s/src/lib/parsers/parse-i3s-attribute.ts +++ b/modules/i3s/src/lib/parsers/parse-i3s-attribute.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {TypedArray} from '@loaders.gl/schema'; diff --git a/modules/i3s/src/lib/parsers/parse-i3s-tile-content.ts b/modules/i3s/src/lib/parsers/parse-i3s-tile-content.ts index 7e47340142..a352960cfe 100644 --- a/modules/i3s/src/lib/parsers/parse-i3s-tile-content.ts +++ b/modules/i3s/src/lib/parsers/parse-i3s-tile-content.ts @@ -2,7 +2,7 @@ import type {TypedArray} from '@loaders.gl/schema'; import {load, parse} from '@loaders.gl/core'; import {Vector3, Matrix4} from '@math.gl/core'; import {Ellipsoid} from '@math.gl/geospatial'; -import type {LoaderOptions, LoaderContext} from '@loaders.gl/loader-utils'; +import {LoaderOptions, LoaderContext, parseFromContext} from '@loaders.gl/loader-utils'; import {ImageLoader} from '@loaders.gl/images'; import {DracoLoader, DracoMesh} from '@loaders.gl/draco'; import {BasisLoader, CompressedTextureLoader} from '@loaders.gl/textures'; @@ -23,7 +23,6 @@ import {getUrlWithToken} from '../utils/url-utils'; import {GL_TYPE_MAP, getConstructorForDataFormat, sizeOf, COORDINATE_SYSTEM} from './constants'; import {I3SLoaderOptions} from '../../i3s-loader'; -import {customizeColors} from '../utils/customize-сolors'; const scratchVector = new Vector3([0, 0, 0]); @@ -65,38 +64,37 @@ export async function parseI3STileContent( // @ts-expect-error options is not properly typed const url = getUrlWithToken(tileOptions.textureUrl, options?.i3s?.token); const loader = getLoaderForTextureFormat(tileOptions.textureFormat); - const response = await fetch(url, options?.fetch as RequestInit); + const fetchFunc = context?.fetch || fetch; + const response = await fetchFunc(url); // options?.fetch const arrayBuffer = await response.arrayBuffer(); // @ts-expect-error options is not properly typed if (options?.i3s.decodeTextures) { + // TODO - replace with switch if (loader === ImageLoader) { const options = {...tileOptions.textureLoaderOptions, image: {type: 'data'}}; try { - // @ts-ignore context must be defined // Image constructor is not supported in worker thread. // Do parsing image data on the main thread by using context to avoid worker issues. - content.texture = await context.parse(arrayBuffer, options); + const texture = await parseFromContext(arrayBuffer, [], options, context!); + // @ts-expect-error + content.texture = texture; } catch (e) { // context object is different between worker and node.js conversion script. // To prevent error we parse data in ordinary way if it is not parsed by using context. - // @ts-expect-error - content.texture = await parse(arrayBuffer, loader, options); + const texture = await parse(arrayBuffer, loader, options, context); + content.texture = texture; } } else if (loader === CompressedTextureLoader || loader === BasisLoader) { let texture = await load(arrayBuffer, loader, tileOptions.textureLoaderOptions); if (loader === BasisLoader) { - // @ts-expect-error texture = texture[0]; } content.texture = { compressed: true, mipmaps: false, - // @ts-expect-error width: texture[0].width, - // @ts-expect-error height: texture[0].height, - // @ts-expect-error data: texture }; } @@ -208,14 +206,6 @@ async function parseI3SNodeGeometry( content.coordinateSystem = COORDINATE_SYSTEM.LNGLAT_OFFSETS; } - attributes.color = await customizeColors( - attributes.color, - attributes.id, - tileOptions, - tilesetOptions, - options - ); - content.attributes = { positions: attributes.position, normals: attributes.normal, diff --git a/modules/i3s/src/lib/parsers/parse-i3s.ts b/modules/i3s/src/lib/parsers/parse-i3s.ts index 70b4ce549b..da12855284 100644 --- a/modules/i3s/src/lib/parsers/parse-i3s.ts +++ b/modules/i3s/src/lib/parsers/parse-i3s.ts @@ -3,15 +3,18 @@ import {Ellipsoid} from '@math.gl/geospatial'; import {load} from '@loaders.gl/core'; import {TILE_TYPE, TILE_REFINEMENT, TILESET_TYPE} from '@loaders.gl/tiles'; import I3SNodePagesTiles from '../helpers/i3s-nodepages-tiles'; -import {generateTileAttributeUrls, getUrlWithToken} from '../utils/url-utils'; +import {generateTileAttributeUrls, getUrlWithToken, getUrlWithoutParams} from '../utils/url-utils'; import { I3STilesetHeader, I3STileHeader, Mbs, I3SMinimalNodeData, - Node3DIndexDocument + Node3DIndexDocument, + SceneLayer3D, + I3SParseOptions } from '../../types'; import type {LoaderOptions, LoaderContext} from '@loaders.gl/loader-utils'; +import { I3SLoader } from '../../i3s-loader'; export function normalizeTileData(tile : Node3DIndexDocument, context: LoaderContext): I3STileHeader { const url: string = context.url || ''; @@ -30,11 +33,15 @@ export function normalizeTileData(tile : Node3DIndexDocument, context: LoaderCon attributeUrls = generateTileAttributeUrls(url, tile); } + const children = tile.children || []; + return normalizeTileNonUrlData({ ...tile, + children, url, contentUrl, textureUrl, + textureFormat: 'jpg', // `jpg` format will cause `ImageLoader` usage that will be able to handle `png` as well attributeUrls, isDracoGeometry: false }); @@ -67,27 +74,27 @@ export function normalizeTileNonUrlData(tile : I3SMinimalNodeData): I3STileHeade const lodMetricType = tile.lodSelection?.[0].metricType; const lodMetricValue = tile.lodSelection?.[0].maxError; - const transformMatrix = tile.transform; const type = TILE_TYPE.MESH; /** * I3S specification supports only REPLACE */ const refine = TILE_REFINEMENT.REPLACE; - return {...tile, mbs, boundingVolume, lodMetricType, lodMetricValue, transformMatrix, type, refine}; + return {...tile, mbs, boundingVolume, lodMetricType, lodMetricValue, type, refine}; } -export async function normalizeTilesetData(tileset : I3STilesetHeader, options : LoaderOptions, context: LoaderContext) { - tileset.url = context.url; - +export async function normalizeTilesetData(tileset : SceneLayer3D, options : LoaderOptions, context: LoaderContext): Promise { + const url = getUrlWithoutParams(context.url || ''); + let nodePagesTile: I3SNodePagesTiles | undefined; + let root: I3STileHeader | I3STilesetHeader; if (tileset.nodePages) { - tileset.nodePagesTile = new I3SNodePagesTiles(tileset, options); - tileset.root = tileset.nodePagesTile.formTileFromNodePages(0); + nodePagesTile = new I3SNodePagesTiles(tileset, url, options); + root = await nodePagesTile.formTileFromNodePages(0); } else { - // @ts-expect-error options is not properly typed - const rootNodeUrl = getUrlWithToken(`${tileset.url}/nodes/root`, options.i3s?.token); + const parseOptions = options.i3s as I3SParseOptions; + const rootNodeUrl = getUrlWithToken(`${url}/nodes/root`, parseOptions.token); // eslint-disable-next-line no-use-before-define - tileset.root = await load(rootNodeUrl, tileset.loader, { + root = await load(rootNodeUrl, I3SLoader, { ...options, i3s: { // @ts-expect-error options is not properly typed @@ -96,11 +103,16 @@ export async function normalizeTilesetData(tileset : I3STilesetHeader, options : }); } - // base path that non-absolute paths in tileset are relative to. - tileset.basePath = tileset.url; - tileset.type = TILESET_TYPE.I3S; - - // populate from root node - tileset.lodMetricType = tileset.root.lodMetricType; - tileset.lodMetricValue = tileset.root.lodMetricValue; + return { + ...tileset, + loader: I3SLoader, + url, + basePath: url, + type: TILESET_TYPE.I3S, + nodePagesTile, + // @ts-expect-error + root, + lodMetricType: root.lodMetricType, + lodMetricValue: root.lodMetricValue + } } diff --git a/modules/i3s/src/lib/parsers/parse-slpk/parse-slpk.ts b/modules/i3s/src/lib/parsers/parse-slpk/parse-slpk.ts index 4ef1aa7cf6..51150ec5f8 100644 --- a/modules/i3s/src/lib/parsers/parse-slpk/parse-slpk.ts +++ b/modules/i3s/src/lib/parsers/parse-slpk/parse-slpk.ts @@ -1,72 +1,52 @@ -import type {SLPKLoaderOptions} from '../../../i3s-slpk-loader'; -import {DataViewFileProvider} from '../parse-zip/buffer-file-provider'; -import {parseZipCDFileHeader} from '../parse-zip/cd-file-header'; -import {parseZipLocalFileHeader} from '../parse-zip/local-file-header'; +import {FileProvider} from '@loaders.gl/loader-utils'; +import { + parseZipCDFileHeader, + cdSignature as cdHeaderSignature, + parseZipLocalFileHeader, + searchFromTheEnd, + parseHashTable, + makeHashTableFromZipHeaders +} from '@loaders.gl/zip'; import {SLPKArchive} from './slpk-archieve'; /** - * Returns one byte from the provided buffer at the provided position - * @param offset - position where to read - * @param buffer - buffer to read - * @returns one byte from the provided buffer at the provided position + * Creates slpk file handler from raw file + * @param fileProvider raw file data + * @param cb is called with information message during parsing + * @returns slpk file handler */ -const getByteAt = (offset: number, buffer: DataView): number => { - return buffer.getUint8(buffer.byteOffset + offset); -}; - -export async function parseSLPK(data: ArrayBuffer, options: SLPKLoaderOptions = {}) { - const archive = new DataView(data); - const cdFileHeaderSignature = [80, 75, 1, 2]; - - const searchWindow = [ - getByteAt(archive.byteLength - 1, archive), - getByteAt(archive.byteLength - 2, archive), - getByteAt(archive.byteLength - 3, archive), - undefined - ]; - - let hashCDOffset = 0; - - // looking for the last record in the central directory - for (let i = archive.byteLength - 4; i > -1; i--) { - searchWindow[3] = searchWindow[2]; - searchWindow[2] = searchWindow[1]; - searchWindow[1] = searchWindow[0]; - searchWindow[0] = getByteAt(i, archive); - if (searchWindow.every((val, index) => val === cdFileHeaderSignature[index])) { - hashCDOffset = i; - break; - } - } - - const fileProvider = new DataViewFileProvider(archive); +export async function parseSLPKArchive( + fileProvider: FileProvider, + cb?: (msg: string) => void +): Promise { + const hashCDOffset = await searchFromTheEnd(fileProvider, cdHeaderSignature); const cdFileHeader = await parseZipCDFileHeader(hashCDOffset, fileProvider); - if (cdFileHeader.fileName !== '@specialIndexFileHASH128@') { - throw new Error('No hash file in slpk'); - } - - const localFileHeader = await parseZipLocalFileHeader( - cdFileHeader.localHeaderOffset, - fileProvider - ); - if (!localFileHeader) { - throw new Error('No hash file in slpk'); - } + let hashTable: Record; + if (cdFileHeader?.fileName !== '@specialIndexFileHASH128@') { + hashTable = await makeHashTableFromZipHeaders(fileProvider); + cb?.( + 'SLPK doesnt contain hash file, hash info has been composed according to zip archive headers' + ); + } else { + // cb?.('SLPK contains hash file'); + const localFileHeader = await parseZipLocalFileHeader( + cdFileHeader.localHeaderOffset, + fileProvider + ); + if (!localFileHeader) { + throw new Error('corrupted SLPK'); + } - const fileDataOffset = localFileHeader.fileDataOffset; - const hashFile = archive.buffer.slice( - fileDataOffset, - fileDataOffset + localFileHeader.compressedSize - ); + const fileDataOffset = localFileHeader.fileDataOffset; + const hashFile = await fileProvider.slice( + fileDataOffset, + fileDataOffset + localFileHeader.compressedSize + ); - if (!hashFile) { - throw new Error('No hash file in slpk'); + hashTable = parseHashTable(hashFile); } - return await new SLPKArchive(data, hashFile).getFile( - options.slpk?.path ?? '', - options.slpk?.pathMode - ); + return new SLPKArchive(fileProvider, hashTable); } diff --git a/modules/i3s/src/lib/parsers/parse-slpk/slpk-archieve.ts b/modules/i3s/src/lib/parsers/parse-slpk/slpk-archieve.ts index f689dccda2..da6b63aca2 100644 --- a/modules/i3s/src/lib/parsers/parse-slpk/slpk-archieve.ts +++ b/modules/i3s/src/lib/parsers/parse-slpk/slpk-archieve.ts @@ -1,20 +1,8 @@ -import md5 from 'md5'; -import {parseZipLocalFileHeader} from '../parse-zip/local-file-header'; -import {DataViewFileProvider} from '../parse-zip/buffer-file-provider'; +import {MD5Hash} from '@loaders.gl/crypto'; +import {FileProvider} from '@loaders.gl/loader-utils'; +import {parseZipLocalFileHeader} from '@loaders.gl/zip'; import {GZipCompression} from '@loaders.gl/compression'; -/** Element of hash array */ -type HashElement = { - /** - * File name hash - */ - hash: Buffer; - /** - * File offset in the archive - */ - offset: number; -}; - /** Description of real paths for different file types */ const PATH_DESCRIPTIONS: {test: RegExp; extensions: string[]}[] = [ { @@ -26,7 +14,7 @@ const PATH_DESCRIPTIONS: {test: RegExp; extensions: string[]}[] = [ extensions: ['.json.gz'] }, { - test: /^nodes\/\d+$/, + test: /^nodes\/(\d+|root)$/, extensions: ['/3dNodeIndexDocument.json.gz'] }, { @@ -55,37 +43,20 @@ const PATH_DESCRIPTIONS: {test: RegExp; extensions: string[]}[] = [ * Class for handling information about slpk file */ export class SLPKArchive { - slpkArchive: DataView; - hashArray: {hash: Buffer; offset: number}[]; - constructor(slpkArchiveBuffer: ArrayBuffer, hashFile: ArrayBuffer) { - this.slpkArchive = new DataView(slpkArchiveBuffer); - this.hashArray = this.parseHashFile(hashFile); - } + /** A DataView representation of the archive */ + private slpkArchive: FileProvider; + // Maps hex-encoded md5 filename hashes to bigint offsets into the archive + private hashTable: Record; + /** Array of hashes and offsets into archive */ + // hashToOffsetMap: Record; - /** - * Reads hash file from buffer and returns it in ready-to-use form - * @param hashFile - bufer containing hash file - * @returns Array containing file info - */ - private parseHashFile(hashFile: ArrayBuffer): HashElement[] { - const hashFileBuffer = Buffer.from(hashFile); - const hashArray: HashElement[] = []; - for (let i = 0; i < hashFileBuffer.buffer.byteLength; i = i + 24) { - const offsetBuffer = new DataView( - hashFileBuffer.buffer.slice( - hashFileBuffer.byteOffset + i + 16, - hashFileBuffer.byteOffset + i + 24 - ) - ); - const offset = offsetBuffer.getUint32(offsetBuffer.byteOffset, true); - hashArray.push({ - hash: Buffer.from( - hashFileBuffer.subarray(hashFileBuffer.byteOffset + i, hashFileBuffer.byteOffset + i + 16) - ), - offset - }); - } - return hashArray; + protected _textEncoder = new TextEncoder(); + protected _textDecoder = new TextDecoder(); + protected _md5Hash = new MD5Hash(); + + constructor(slpkArchive: FileProvider, hashTable: Record) { + this.slpkArchive = slpkArchive; + this.hashTable = hashTable; } /** @@ -94,7 +65,7 @@ export class SLPKArchive { * @param mode - currently only raw mode supported * @returns buffer with ready to use file */ - async getFile(path: string, mode: 'http' | 'raw' = 'raw'): Promise { + async getFile(path: string, mode: 'http' | 'raw' = 'raw'): Promise { if (mode === 'http') { const extensions = PATH_DESCRIPTIONS.find((val) => val.test.test(path))?.extensions; if (extensions) { @@ -106,22 +77,22 @@ export class SLPKArchive { } } if (data) { - return Buffer.from(data); + return data; } } } if (mode === 'raw') { const decompressedFile = await this.getDataByPath(`${path}.gz`); if (decompressedFile) { - return Buffer.from(decompressedFile); + return decompressedFile; } const fileWithoutCompression = await this.getFileBytes(path); if (fileWithoutCompression) { - return Buffer.from(fileWithoutCompression); + return fileWithoutCompression; } } - throw new Error('No such file in the archieve'); + throw new Error(`No such file in the archive: ${path}`); } /** @@ -130,7 +101,12 @@ export class SLPKArchive { * @returns buffer with the file data */ private async getDataByPath(path: string): Promise { - const data = await this.getFileBytes(path); + // sometimes paths are not in lower case when hash file is created, + // so first we're looking for lower case file name and then for original one + let data = await this.getFileBytes(path.toLocaleLowerCase()); + if (!data) { + data = await this.getFileBytes(path); + } if (!data) { return undefined; } @@ -140,30 +116,29 @@ export class SLPKArchive { const decompressedData = await compression.decompress(data); return decompressedData; } - return Buffer.from(data); + return data; } /** - * Trying to get raw file data by adress + * Trying to get raw file data by address * @param path - path inside the archive * @returns buffer with the raw file data */ private async getFileBytes(path: string): Promise { - const nameHash = Buffer.from(md5(path), 'hex'); - const fileInfo = this.hashArray.find((val) => Buffer.compare(val.hash, nameHash) === 0); - if (!fileInfo) { + const binaryPath = this._textEncoder.encode(path); + const nameHash = await this._md5Hash.hash(binaryPath.buffer, 'hex'); + + const offset = this.hashTable[nameHash]; + if (offset === undefined) { return undefined; } - const localFileHeader = await parseZipLocalFileHeader( - this.slpkArchive.byteOffset + fileInfo?.offset, - new DataViewFileProvider(this.slpkArchive) - ); + const localFileHeader = await parseZipLocalFileHeader(offset, this.slpkArchive); if (!localFileHeader) { return undefined; } - const compressedFile = this.slpkArchive.buffer.slice( + const compressedFile = this.slpkArchive.slice( localFileHeader.fileDataOffset, localFileHeader.fileDataOffset + localFileHeader.compressedSize ); diff --git a/modules/i3s/src/lib/parsers/parse-zip/buffer-file-provider.ts b/modules/i3s/src/lib/parsers/parse-zip/buffer-file-provider.ts deleted file mode 100644 index 4f1d048c85..0000000000 --- a/modules/i3s/src/lib/parsers/parse-zip/buffer-file-provider.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {FileProvider} from './file-provider'; - -/** - * Provides file data using DataView - */ -export class DataViewFileProvider implements FileProvider { - /** - * The DataView from which data is provided - */ - private file: DataView; - - constructor(file: DataView) { - this.file = file; - } - - /** - * Gets an unsigned 8-bit integer at the specified byte offset from the start of the file. - * @param offset The offset, in bytes, from the start of the file where to read the data. - */ - getUint8(offset: number): Promise { - return Promise.resolve(this.file.getUint8(offset)); - } - - /** - * Gets an unsigned 16-bit integer at the specified byte offset from the start of the file. - * @param offset The offset, in bytes, from the start of the file where to read the data. - */ - getUint16(offset: number): Promise { - return Promise.resolve(this.file.getUint16(offset, true)); - } - - /** - * Gets an unsigned 32-bit integer at the specified byte offset from the start of the file. - * @param offset The offset, in bytes, from the start of the file where to read the data. - */ - getUint32(offset: number): Promise { - return Promise.resolve(this.file.getUint32(offset, true)); - } - - /** - * returns an ArrayBuffer whose contents are a copy of this file bytes from startOffset, inclusive, up to endOffset, exclusive. - * @param startOffset The offset, in bytes, from the start of the file where to start reading the data. - * @param endOffset The offset, in bytes, from the start of the file where to end reading the data. - */ - slice(startOffset: number, endOffset: number): Promise { - return Promise.resolve(this.file.buffer.slice(startOffset, endOffset)); - } - - /** - * the length (in bytes) of the data. - */ - get length() { - return this.file.byteLength; - } -} diff --git a/modules/i3s/src/lib/parsers/parse-zip/cd-file-header.ts b/modules/i3s/src/lib/parsers/parse-zip/cd-file-header.ts deleted file mode 100644 index ba9f4083a1..0000000000 --- a/modules/i3s/src/lib/parsers/parse-zip/cd-file-header.ts +++ /dev/null @@ -1,86 +0,0 @@ -import {FileProvider} from './file-provider'; - -/** - * zip central directory file header info - * according to https://en.wikipedia.org/wiki/ZIP_(file_format) - */ -export type ZipCDFileHeader = { - /** Compressed size */ - compressedSize: number; - /** Uncompressed size */ - uncompressedSize: number; - /** File name length */ - fileNameLength: number; - /** File name */ - fileName: string; - /** Extra field offset */ - extraOffset: number; - /** Relative offset of local file header */ - localHeaderOffset: number; -}; - -/** - * Parses central directory file header of zip file - * @param headerOffset - offset in the archive where header starts - * @param buffer - buffer containing whole array - * @returns Info from the header - */ -export const parseZipCDFileHeader = async ( - headerOffset: number, - buffer: FileProvider -): Promise => { - const offsets = { - CD_COMPRESSED_SIZE_OFFSET: 20, - CD_UNCOMPRESSED_SIZE_OFFSET: 24, - CD_FILE_NAME_LENGTH_OFFSET: 28, - CD_EXTRA_FIELD_LENGTH_OFFSET: 30, - CD_LOCAL_HEADER_OFFSET_OFFSET: 42, - CD_FILE_NAME_OFFSET: 46 - }; - - const compressedSize = await buffer.getUint32(headerOffset + offsets.CD_COMPRESSED_SIZE_OFFSET); - - const uncompressedSize = await buffer.getUint32( - headerOffset + offsets.CD_UNCOMPRESSED_SIZE_OFFSET - ); - - const fileNameLength = await buffer.getUint16(headerOffset + offsets.CD_FILE_NAME_LENGTH_OFFSET); - - const fileName = new TextDecoder().decode( - await buffer.slice( - headerOffset + offsets.CD_FILE_NAME_OFFSET, - headerOffset + offsets.CD_FILE_NAME_OFFSET + fileNameLength - ) - ); - - const extraOffset = headerOffset + offsets.CD_FILE_NAME_OFFSET + fileNameLength; - - const oldFormatOffset = await buffer.getUint32( - headerOffset + offsets.CD_LOCAL_HEADER_OFFSET_OFFSET - ); - - let fileDataOffset = oldFormatOffset; - if (fileDataOffset === 0xffffffff) { - let offsetInZip64Data = 4; - // looking for info that might be also be in zip64 extra field - if (compressedSize === 0xffffffff) { - offsetInZip64Data += 8; - } - if (uncompressedSize === 0xffffffff) { - offsetInZip64Data += 8; - } - - // getUint32 needs to be replaced with getBigUint64 for archieves bigger than 2gb - fileDataOffset = await buffer.getUint32(extraOffset + offsetInZip64Data); // setting it to the one from zip64 - } - const localHeaderOffset = fileDataOffset; - - return { - compressedSize, - uncompressedSize, - fileNameLength, - fileName, - extraOffset, - localHeaderOffset - }; -}; diff --git a/modules/i3s/src/lib/parsers/parse-zip/local-file-header.ts b/modules/i3s/src/lib/parsers/parse-zip/local-file-header.ts deleted file mode 100644 index 8e47b0f8c9..0000000000 --- a/modules/i3s/src/lib/parsers/parse-zip/local-file-header.ts +++ /dev/null @@ -1,65 +0,0 @@ -import {FileProvider} from './file-provider'; - -/** - * zip local file header info - * according to https://en.wikipedia.org/wiki/ZIP_(file_format) - */ -export type ZipLocalFileHeader = { - /** File name length */ - fileNameLength: number; - /** File name */ - fileName: string; - /** Extra field length */ - extraFieldLength: number; - /** Offset of the file data */ - fileDataOffset: number; - /** Compressed size */ - compressedSize: number; -}; - -const offsets = { - COMPRESSED_SIZE_OFFSET: 18, - FILE_NAME_LENGTH_OFFSET: 26, - EXTRA_FIELD_LENGTH_OFFSET: 28, - FILE_NAME_OFFSET: 30 -}; - -const signature = Buffer.from([0x50, 0x4b, 0x03, 0x04]); - -/** - * Parses local file header of zip file - * @param headerOffset - offset in the archive where header starts - * @param buffer - buffer containing whole array - * @returns Info from the header - */ -export const parseZipLocalFileHeader = async ( - headerOffset: number, - buffer: FileProvider -): Promise => { - if (Buffer.from(await buffer.slice(headerOffset, headerOffset + 4)).compare(signature) !== 0) { - return Promise.resolve(undefined); - } - - const fileNameLength = await buffer.getUint16(headerOffset + offsets.FILE_NAME_LENGTH_OFFSET); - - const fileName = new TextDecoder().decode( - await buffer.slice( - headerOffset + offsets.FILE_NAME_OFFSET, - headerOffset + offsets.FILE_NAME_OFFSET + fileNameLength - ) - ); - const extraFieldLength = await buffer.getUint16(headerOffset + offsets.EXTRA_FIELD_LENGTH_OFFSET); - - const fileDataOffset = - headerOffset + offsets.FILE_NAME_OFFSET + fileNameLength + extraFieldLength; - - const compressedSize = await buffer.getUint32(headerOffset + offsets.COMPRESSED_SIZE_OFFSET); - - return { - fileNameLength, - fileName, - extraFieldLength, - fileDataOffset, - compressedSize - }; -}; diff --git "a/modules/i3s/src/lib/utils/customize-\321\201olors.ts" b/modules/i3s/src/lib/utils/customize-colors.ts similarity index 50% rename from "modules/i3s/src/lib/utils/customize-\321\201olors.ts" rename to modules/i3s/src/lib/utils/customize-colors.ts index 0ed905266e..5db1d6cc88 100644 --- "a/modules/i3s/src/lib/utils/customize-\321\201olors.ts" +++ b/modules/i3s/src/lib/utils/customize-colors.ts @@ -1,34 +1,57 @@ -import type {MeshAttribute} from '@loaders.gl/schema'; -import type {COLOR, I3STileOptions, I3STilesetOptions} from '../../types'; +import type {MeshAttribute, TypedArray} from '@loaders.gl/schema'; +import type {AttributeStorageInfo, COLOR, Field} from '../../types'; import {load} from '@loaders.gl/core'; import {getAttributeValueType, I3SAttributeLoader} from '../../i3s-attribute-loader'; -import {I3SLoaderOptions} from '../../i3s-loader'; import {getUrlWithToken} from './url-utils'; +import {I3STileAttributes} from '../parsers/parse-i3s-attribute'; + +type ColorsByAttribute = { + /** Feature attribute name */ + attributeName: string; + /** Minimum attribute value */ + minValue: number; + /** Maximum attribute value */ + maxValue: number; + /** Minimum color. 3DObject will be colorized with gradient from `minColor to `maxColor` */ + minColor: [number, number, number, number]; + /** Maximum color. 3DObject will be colorized with gradient from `minColor to `maxColor` */ + maxColor: [number, number, number, number]; + /** Colorization mode. `replace` - replace vertex colors with a new colors, `multiply` - multiply vertex colors with new colors */ + mode: string; +}; /** - * Modify vertex colors array to visualize 3D objects in a attribute driven way + * Calculate new vertex colors array to visualize 3D objects in a attribute driven way * @param colors - vertex colors attribute * @param featureIds - feature Ids attribute - * @param tileOptions - tile - related options - * @param tilesetOptions - tileset-related options - * @param options - loader options - * @returns midified colors attribute + * @param attributeUrls - array of attribute's urls + * @param fields - array of attribute's fileds + * @param attributeStorageInfo - array of attributeStorageInfo + * @param colorsByAttribute - attribute color options + * @param token - access token + * @returns new colors attribute */ +// eslint-disable-next-line max-params export async function customizeColors( colors: MeshAttribute, - featureIds: MeshAttribute, - tileOptions: I3STileOptions, - tilesetOptions: I3STilesetOptions, - options?: I3SLoaderOptions + featureIds: number[] | TypedArray, + attributeUrls: string[], + fields: Field[], + attributeStorageInfo: AttributeStorageInfo[], + colorsByAttribute: ColorsByAttribute | null, + token?: string ): Promise { - if (!options?.i3s?.colorsByAttribute) { + if (!colorsByAttribute) { return colors; } - const colorizeAttributeField = tilesetOptions.fields.find( - ({name}) => name === options?.i3s?.colorsByAttribute?.attributeName - ); + const resultColors = { + ...colors, + value: new Uint8Array(colors.value) + }; + + const colorizeAttributeField = fields.find(({name}) => name === colorsByAttribute?.attributeName); if ( !colorizeAttributeField || !['esriFieldTypeDouble', 'esriFieldTypeInteger', 'esriFieldTypeSmallInteger'].includes( @@ -40,24 +63,24 @@ export async function customizeColors( const colorizeAttributeData = await loadFeatureAttributeData( colorizeAttributeField.name, - tileOptions, - tilesetOptions, - options + attributeUrls, + attributeStorageInfo, + token ); if (!colorizeAttributeData) { return colors; } - const objectIdField = tilesetOptions.fields.find(({type}) => type === 'esriFieldTypeOID'); + const objectIdField = fields.find(({type}) => type === 'esriFieldTypeOID'); if (!objectIdField) { return colors; } const objectIdAttributeData = await loadFeatureAttributeData( objectIdField.name, - tileOptions, - tilesetOptions, - options + attributeUrls, + attributeStorageInfo, + token ); if (!objectIdAttributeData) { return colors; @@ -70,42 +93,45 @@ export async function customizeColors( attributeValuesMap[objectIdAttributeData[objectIdField.name][i]] = calculateColorForAttribute( // @ts-expect-error colorizeAttributeData[colorizeAttributeField.name][i] as number, - options + colorsByAttribute ); } - for (let i = 0; i < featureIds.value.length; i++) { - const color = attributeValuesMap[featureIds.value[i]]; + for (let i = 0; i < featureIds.length; i++) { + const color = attributeValuesMap[featureIds[i]]; if (!color) { continue; // eslint-disable-line no-continue } /* eslint max-statements: ["error", 30] */ /* eslint complexity: ["error", 12] */ - if (options.i3s.colorsByAttribute.mode === 'multiply') { + if (colorsByAttribute.mode === 'multiply') { // multiplying original mesh and calculated for attribute rgba colors in range 0-255 color.forEach((colorItem, index) => { - colors.value[i * 4 + index] = (colors.value[i * 4 + index] * colorItem) / 255; + resultColors.value[i * 4 + index] = (resultColors.value[i * 4 + index] * colorItem) / 255; }); } else { - colors.value.set(color, i * 4); + resultColors.value.set(color, i * 4); } } - return colors; + return resultColors; } /** * Calculate rgba color from the attribute value * @param attributeValue - value of the attribute - * @param options - loader options + * @param colorsByAttribute - attribute color options * @returns - color array for a specific attribute value */ -function calculateColorForAttribute(attributeValue: number, options?: I3SLoaderOptions): COLOR { - if (!options?.i3s?.colorsByAttribute) { +function calculateColorForAttribute( + attributeValue: number, + colorsByAttribute: ColorsByAttribute +): COLOR { + if (!colorsByAttribute) { return [255, 255, 255, 255]; } - const {minValue, maxValue, minColor, maxColor} = options.i3s.colorsByAttribute; + const {minValue, maxValue, minColor, maxColor} = colorsByAttribute; const rate = (attributeValue - minValue) / (maxValue - minValue); const color: COLOR = [255, 255, 255, 255]; for (let i = 0; i < minColor.length; i++) { @@ -117,28 +143,27 @@ function calculateColorForAttribute(attributeValue: number, options?: I3SLoaderO /** * Load feature attribute data from the ArcGIS rest service * @param attributeName - attribute name - * @param tileOptions - tile-related options - * @param tilesetOptions - tileset-related options - * @param options - loader options + * @param attributeUrls - array of attribute's urls + * @param attributeStorageInfo - array of attributeStorageInfo + * @param token - access token * @returns - Array-like list of the attribute values */ async function loadFeatureAttributeData( attributeName: string, - {attributeUrls}: I3STileOptions, - {attributeStorageInfo}: I3STilesetOptions, - options?: I3SLoaderOptions -): Promise<{[key: string]: string[] | Uint32Array | Uint16Array | Float64Array | null} | null> { + attributeUrls: string[], + attributeStorageInfo: AttributeStorageInfo[], + token?: string +): Promise { const attributeIndex = attributeStorageInfo.findIndex(({name}) => attributeName === name); if (attributeIndex === -1) { return null; } - const objectIdAttributeUrl = getUrlWithToken(attributeUrls[attributeIndex], options?.i3s?.token); + const objectIdAttributeUrl = getUrlWithToken(attributeUrls[attributeIndex], token); const attributeType = getAttributeValueType(attributeStorageInfo[attributeIndex]); const objectIdAttributeData = await load(objectIdAttributeUrl, I3SAttributeLoader, { attributeName, attributeType }); - // @ts-expect-error TODO action engine return objectIdAttributeData; } diff --git a/modules/i3s/src/lib/utils/url-utils.ts b/modules/i3s/src/lib/utils/url-utils.ts index bd676bc3f0..95d93f9305 100644 --- a/modules/i3s/src/lib/utils/url-utils.ts +++ b/modules/i3s/src/lib/utils/url-utils.ts @@ -1,4 +1,21 @@ -import {Node3DIndexDocument} from '../../types'; +import {Node3DIndexDocument, SceneLayer3D} from '../../types'; + +/** + * Return URL seperated from search params + * @param url - URL that might have search params + * @returns url without search params + */ +export function getUrlWithoutParams(url: string): string { + let urlWithoutParams; + + try { + const urlObj = new URL(url); + urlWithoutParams = `${urlObj.origin}${urlObj.pathname}`; + } catch (e) { + // do nothing + } + return urlWithoutParams || url; +} /** * Generates url with token if it is exists. @@ -29,13 +46,14 @@ export function generateTileAttributeUrls(url: string, tile: Node3DIndexDocument /** * Generates attribute urls for tileset based on tileset and resource - * @param {Object} tileset - * @param {number} resource + * @param tileset - tileset metadata + * @param url - tileset base url + * @param resource - resource id per I3S spec * @returns {Array} */ -export function generateTilesetAttributeUrls(tileset, resource) { +export function generateTilesetAttributeUrls(tileset: SceneLayer3D, url: string, resource: number) { const attributeUrls: string[] = []; - const {attributeStorageInfo, url} = tileset; + const {attributeStorageInfo = []} = tileset; for (let index = 0; index < attributeStorageInfo.length; index++) { const fileName = attributeStorageInfo[index].key; diff --git a/modules/i3s/src/types.ts b/modules/i3s/src/types.ts index ef74a7ce54..add44f4410 100644 --- a/modules/i3s/src/types.ts +++ b/modules/i3s/src/types.ts @@ -1,29 +1,31 @@ import type {Matrix4, Quaternion, Vector3} from '@math.gl/core'; import type {TypedArray, MeshAttribute, TextureLevel} from '@loaders.gl/schema'; -import {Tile3D, Tileset3D} from '@loaders.gl/tiles'; - -export enum DATA_TYPE { - UInt8 = 'UInt8', - UInt16 = 'UInt16', - UInt32 = 'UInt32', - UInt64 = 'UInt64', - Int16 = 'Int16', - Int32 = 'Int32', - Int64 = 'Int64', - Float32 = 'Float32', - Float64 = 'Float64' -} +import {TILESET_TYPE, TILE_REFINEMENT, TILE_TYPE, Tile3D, Tileset3D} from '@loaders.gl/tiles'; +import I3SNodePagesTiles from './lib/helpers/i3s-nodepages-tiles'; +import {LoaderWithParser} from '@loaders.gl/loader-utils'; export type COLOR = [number, number, number, number]; /** - * spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.8/3DSceneLayer.cmn.md + * Extension of SceneLayer3D JSON with postprocessed loader data */ -// TODO Replace "[key: string]: any" with actual defenition export interface I3STilesetHeader extends SceneLayer3D { /** Not in spec, but is necessary for woking */ url?: string; - [key: string]: any; + /** Base path that non-absolute paths in tileset are relative to. */ + basePath?: string; + /** root node metadata */ + root: I3STileHeader; + /** instance of the NodePages to tiles loader */ + nodePagesTile?: I3SNodePagesTiles; + /** Type of the tileset */ + type: TILESET_TYPE.I3S; + /** LOD metric type per I3S spec*/ + lodMetricType?: string; + /** LOD metric value */ + lodMetricValue?: number; + /** Loader that has to be used to load content */ + loader: LoaderWithParser; } /** https://github.com/Esri/i3s-spec/blob/master/docs/1.8/nodePage.cmn.md */ export type NodePage = { @@ -69,20 +71,62 @@ type meshAttribute = { resource: number; }; +/** + * Texture format + * @see https://github.com/Esri/i3s-spec/blob/master/docs/1.7/textureSetDefinitionFormat.cmn.md + */ export type I3STextureFormat = 'jpg' | 'png' | 'ktx-etc2' | 'dds' | 'ktx2'; -// TODO Replace "[key: string]: any" with actual defenition -export type I3STileHeader = { - isDracoGeometry: boolean; - textureUrl?: string; - url?: string; - textureFormat?: I3STextureFormat; - textureLoaderOptions?: any; - materialDefinition?: I3SMaterialDefinition; +/** Postprocessed I3S Node */ +export type I3STileHeader = I3SMinimalNodeData & { + /** MBS per I3S spec */ mbs: Mbs; - obb?: Obb; + /** Material definition from the layer metadata per I3S spec */ + materialDefinition?: I3SMaterialDefinition; + /** Bounding volume converted to 3DTiles format. It is generic for `tile` module */ + boundingVolume: {box?: number[]; sphere?: number[]}; + /** LOD metric selected for usage */ + lodMetricType?: string; + /** LOD metric value */ + lodMetricValue?: number; + /** Tile content type */ + type: TILE_TYPE.MESH; + /** Tile refinement type. I3S supports only `REPLACE` */ + refine: TILE_REFINEMENT.REPLACE; +}; + +/** + * Minimal I3S node data is needed for loading + * These data can come from 3DNodeIndexDocument (I3S spec) or from `I3SNodePagesTiles` instance + * @see https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md + */ +export type I3SMinimalNodeData = { + /** Node ID */ + id: string; + /** Node base path */ + url?: string; + /** LOD selection metrics */ lodSelection?: LodSelection[]; - [key: string]: any; + // OBB per I3S spec + obb?: Obb; + /** MBS per I3S spec */ + mbs?: Mbs; + /** Geometry content URL */ + contentUrl?: string; + /** Texture image URL */ + textureUrl?: string; + /** Feature attributes URLs */ + attributeUrls?: string[]; + /** Material definition from I3S layer metadata */ + materialDefinition?: I3SMaterialDefinition; + /** Texture format per I3S spec */ + textureFormat: I3STextureFormat; + /** Loader options for texture loader. The loader might be `CompressedTextureLoader` for `dds`, BasisLoader for `ktx2` or ImageLoader for `jpg`and `png` */ + textureLoaderOptions?: {[key: string]: any}; + /** Child Nodes references */ + children: NodeReference[]; + /** Is the node has Draco compressed geometry */ + isDracoGeometry: boolean; }; export type I3SParseOptions = { @@ -108,13 +152,20 @@ export type I3SParseOptions = { * Supported coordinate systems: METER_OFFSETS, LNGLAT_OFFSETS */ coordinateSystem?: number; + /** Options to colorize 3DObjects by attribute value */ colorsByAttribute?: { + /** Feature attribute name */ attributeName: string; + /** Minimum attribute value */ minValue: number; + /** Maximum attribute value */ maxValue: number; + /** Minimum color. 3DObject will be colorized with gradient from `minColor to `maxColor` */ minColor: COLOR; + /** Maximum color. 3DObject will be colorized with gradient from `minColor to `maxColor` */ maxColor: COLOR; - mode: string; + /** Colorization mode. `replace` - replace vertex colors with a new colors, `multiply` - multiply vertex colors with new colors */ + mode: 'multiply' | 'replace'; }; /** @deprecated */ @@ -169,12 +220,20 @@ export type BoundingVolumes = { obb: Obb; }; +/** + * Oriented bounding box per I3S spec + * @see https://github.com/Esri/i3s-spec/blob/master/docs/1.7/obb.cmn.md + */ export type Obb = { center: number[] | Vector3; halfSize: number[] | Vector3; quaternion: number[] | Quaternion; }; +/** + * Minimum bounding sphere per I3S spec + * @see https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md#properties + */ export type Mbs = [number, number, number, number]; /** SceneLayer3D based on I3S specification - https://github.com/Esri/i3s-spec/blob/master/docs/1.8/3DSceneLayer.cmn.md */ @@ -382,36 +441,32 @@ export type Node3DIndexDocument = { }; /** - * Minimal I3S node data is needed for loading + * LOD selection metrics per I3S spec + * @see https://github.com/Esri/i3s-spec/blob/master/docs/1.7/lodSelection.cmn.md */ -export type I3SMinimalNodeData = { - id: string; - url?: string; - transform?: number[]; - lodSelection?: LodSelection[]; - obb?: Obb; - mbs?: Mbs; - contentUrl?: string; - textureUrl?: string; - attributeUrls?: string[]; - materialDefinition?: I3SMaterialDefinition; - textureFormat?: I3STextureFormat; - textureLoaderOptions?: {[key: string]: any}; - children?: NodeReference[]; - isDracoGeometry: boolean; -}; - export type LodSelection = { + /** */ metricType?: string; maxError: number; }; +/** + * Node reference per I3S spec + * @see https://github.com/Esri/i3s-spec/blob/master/docs/1.7/nodeReference.cmn.md + */ export type NodeReference = { + /** Tree Key ID of the referenced node represented as string. */ id: string; + /** Version (store update session ID) of the referenced node. */ version?: string; + /** An array of four doubles, corresponding to x, y, z and radius of the minimum bounding sphere of a node. */ mbs?: Mbs; + /** Describes oriented bounding box. */ obb?: Obb; + /** Number of values per element. */ href?: string; + /** Number of features in the referenced node and its descendants, down to the leaf nodes. */ + featureCount?: number; }; export type Resource = { @@ -690,20 +745,24 @@ type Domain = { * spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.8/store.cmn.md */ type Store = { - id: string | number; + id?: string | number; profile: string; version: number | string; - resourcePattern: string[]; - rootNode: string; - extent: number[]; - indexCRS: string; - vertexCRS: string; - normalReferenceFrame: string; - attributeEncoding: string; - textureEncoding: string[]; - lodType: string; - lodModel: string; + resourcePattern?: string[]; + rootNode?: string; + extent?: number[]; + indexCRS?: string; + vertexCRS?: string; + normalReferenceFrame?: string; + lodType?: string; + lodModel?: string; defaultGeometrySchema: DefaultGeometrySchema; + nidEncoding?: string; + textureEncoding?: string[]; + featureEncoding?: string; + geometryEncoding?: string; + attributeEncoding?: string; + indexingScheme?: string; }; /** * Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.8/defaultGeometrySchema.cmn.md @@ -726,15 +785,15 @@ type DefaultGeometrySchema = { export type HeaderAttribute = { property: HeaderAttributeProperty.vertexCount | HeaderAttributeProperty.featureCount | string; type: - | DATA_TYPE.UInt8 - | DATA_TYPE.UInt16 - | DATA_TYPE.UInt32 - | DATA_TYPE.UInt64 - | DATA_TYPE.Int16 - | DATA_TYPE.Int32 - | DATA_TYPE.Int64 - | DATA_TYPE.Float32 - | DATA_TYPE.Float64; + | 'UInt8' + | 'UInt16' + | 'UInt32' + | 'UInt64' + | 'Int16' + | 'Int32' + | 'Int64' + | 'Float32' + | 'Float64'; }; export enum HeaderAttributeProperty { vertexCount = 'vertexCount', @@ -749,14 +808,7 @@ export type VertexAttribute = { }; export type GeometryAttribute = { byteOffset?: number; - valueType: - | DATA_TYPE.UInt8 - | DATA_TYPE.UInt16 - | DATA_TYPE.Int16 - | DATA_TYPE.Int32 - | DATA_TYPE.Int64 - | DATA_TYPE.Float32 - | DATA_TYPE.Float64; + valueType: 'UInt8' | 'UInt16' | 'Int16' | 'Int32' | 'Int64' | 'Float32' | 'Float64'; valuesPerElement: number; }; export type I3SMeshAttributes = { @@ -804,7 +856,7 @@ type TextureSetDefinition = { /** Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.8/geometryDefinition.cmn.md */ type GeometryDefinition = { - topology: 'triangle' | string; + topology?: 'triangle'; geometryBuffers: GeometryBuffer[]; }; /** Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.8/geometryBuffer.cmn.md */ @@ -820,7 +872,7 @@ type GeometryBuffer = { compressedAttributes?: {encoding: string; attributes: string[]}; }; -type GeometryBufferItem = {type: string; component: number; encoding?: string; binding: string}; +type GeometryBufferItem = {type: string; component: number; encoding?: string; binding?: string}; type AttributeValue = {valueType: string; encoding?: string; valuesPerElement?: number}; @@ -831,16 +883,16 @@ export type FieldInfo = { label: string; }; -export type ArcGisWebSceneData = { - header: ArcGisWebScene; +export type ArcGISWebSceneData = { + header: ArcGISWebScene; layers: OperationalLayer[]; unsupportedLayers: OperationalLayer[]; }; /** - * ArcGis WebScene spec - https://developers.arcgis.com/web-scene-specification/objects/webscene/ + * ArcGIS WebScene spec - https://developers.arcgis.com/web-scene-specification/objects/webscene/ */ -export type ArcGisWebScene = { +export type ArcGISWebScene = { /** * @todo add type. * Spec - https://developers.arcgis.com/web-scene-specification/objects/applicationProperties/ @@ -881,7 +933,7 @@ export type ArcGisWebScene = { * Spec - https://developers.arcgis.com/web-scene-specification/objects/presentation/ * @todo Add presentation type. */ - presentation: ArcGisPresentation; + presentation: ArcGISPresentation; /** * An object that provides information about the initial environment settings and viewpoint of the web scene. */ @@ -931,7 +983,7 @@ export type ArcGisWebScene = { /** * Spec - https://developers.arcgis.com/javascript/latest/api-reference/esri-webscene-Presentation.html */ -type ArcGisPresentation = { +type ArcGISPresentation = { slides: Slide[]; }; @@ -954,26 +1006,26 @@ type Slide = { ground: { transparency: number; }; - baseMap: ArcGisBaseMap; - visibleLayers: ArcGisVisibleLayer[]; - viewpoint: ArcGisViewPoint; + baseMap: ArcGISBaseMap; + visibleLayers: ArcGISVisibleLayer[]; + viewpoint: ArcGISViewPoint; }; /** * The basemap of the scene. Only the base and reference layers of the basemap are stored in a slide. * Spec - https://developers.arcgis.com/javascript/latest/api-reference/esri-Basemap.html */ -type ArcGisBaseMap = { +type ArcGISBaseMap = { id: string; title: string; - baseMapLayers: ArcGisBaseMapLayer[]; + baseMapLayers: ArcGISBaseMapLayer[]; }; /** * The visible layers of the scene. * Spec - https://developers.arcgis.com/javascript/latest/api-reference/esri-webscene-Slide.html#visibleLayers */ -type ArcGisVisibleLayer = { +type ArcGISVisibleLayer = { id: string; sublayerIds: number[]; }; @@ -981,7 +1033,7 @@ type ArcGisVisibleLayer = { * The basemap of the scene. * Spec - https://developers.arcgis.com/javascript/latest/api-reference/esri-Basemap.html */ -type ArcGisBaseMapLayer = { +type ArcGISBaseMapLayer = { id: string; title: string; url: string; @@ -993,14 +1045,14 @@ type ArcGisBaseMapLayer = { * The viewpoint of the slide. This acts like a bookmark, saving a predefined location or point of view from which to view the scene. * Spec - https://developers.arcgis.com/javascript/latest/api-reference/esri-Viewpoint.html */ -type ArcGisViewPoint = { +type ArcGISViewPoint = { scale: number; rotation?: number; /** * Spec - https://developers.arcgis.com/web-scene-specification/objects/viewpoint/ */ targetGeometry: any; - camera: ArcGisCamera; + camera: ArcGISCamera; }; /** @@ -1008,7 +1060,7 @@ type ArcGisViewPoint = { * It is not associated with device hardware. This class only applies to 3D SceneViews. * Spec - https://developers.arcgis.com/javascript/latest/api-reference/esri-Camera.html */ -export type ArcGisCamera = { +export type ArcGISCamera = { position: { x: number; y: number; diff --git a/modules/i3s/test/arcgis-webscene-loader.spec.js b/modules/i3s/test/arcgis-webscene-loader.spec.ts similarity index 85% rename from modules/i3s/test/arcgis-webscene-loader.spec.js rename to modules/i3s/test/arcgis-webscene-loader.spec.ts index 51cd678c11..8d4ae2bb66 100644 --- a/modules/i3s/test/arcgis-webscene-loader.spec.js +++ b/modules/i3s/test/arcgis-webscene-loader.spec.ts @@ -1,6 +1,6 @@ import test from 'tape-promise/tape'; import {load} from '@loaders.gl/core'; -import {ArcGisWebSceneLoader} from '@loaders.gl/i3s'; +import {ArcGISWebSceneLoader} from '@loaders.gl/i3s'; const ARCGIS_WEB_SCENE_WITH_SUPPORTED_LAYERS_URL = '@loaders.gl/i3s/test/data/arcgis-webscenes/arcgis-webscene-with-supported-layers.json'; @@ -14,7 +14,7 @@ const ARCGIS_WEB_SCENE_WITH_UNSUPPORTED_CRS_URL = const ARCGIS_WEB_SCENE_WITH_UNSUPPORTED_LAYERS_URL = '@loaders.gl/i3s/test/data/arcgis-webscenes/arcgis-webscene-with-all-unsupported-layers.json'; -test('ArcGisWebSceneLoader#should load WebScene', async (t) => { +test('ArcGISWebSceneLoader#should load WebScene', async (t) => { const WEB_SCENE_FIRST_OPERATIONAL_LAYER_EXPECTED = { id: '15f2fe08c8d-layer-0', showLegend: true, @@ -52,7 +52,7 @@ test('ArcGisWebSceneLoader#should load WebScene', async (t) => { } }; - const webScene = await load(ARCGIS_WEB_SCENE_WITH_SUPPORTED_LAYERS_URL, ArcGisWebSceneLoader); + const webScene = await load(ARCGIS_WEB_SCENE_WITH_SUPPORTED_LAYERS_URL, ArcGISWebSceneLoader); t.ok(webScene); t.ok(webScene.header); @@ -65,16 +65,16 @@ test('ArcGisWebSceneLoader#should load WebScene', async (t) => { t.ok(url); t.equal(webScene.layers.length, 3, 'parent layers are good'); - t.equal(webScene.layers[2].layers.length, 4, 'child layers are good'); + t.equal(webScene.layers[2]?.layers?.length, 4, 'child layers are good'); t.deepEqual(dataWithoutUrl, WEB_SCENE_FIRST_OPERATIONAL_LAYER_EXPECTED); t.end(); }); -test('ArcGisWebSceneLoader#should load WebScene with partially unsupported layers', async (t) => { +test('ArcGISWebSceneLoader#should load WebScene with partially unsupported layers', async (t) => { const webScene = await load( ARCGIS_WEB_SCENE_WITH_UNSUPPORTED_LAYER_IN_LIST_URL, - ArcGisWebSceneLoader + ArcGISWebSceneLoader ); t.ok(webScene); @@ -90,9 +90,9 @@ test('ArcGisWebSceneLoader#should load WebScene with partially unsupported layer t.end(); }); -test('ArcGisWebSceneLoader#should return error of loading WebScene if one layer has unsupported CRS', async (t) => { +test('ArcGISWebSceneLoader#should return error of loading WebScene if one layer has unsupported CRS', async (t) => { try { - await load(ARCGIS_WEB_SCENE_WITH_UNSUPPORTED_CRS_URL, ArcGisWebSceneLoader); + await load(ARCGIS_WEB_SCENE_WITH_UNSUPPORTED_CRS_URL, ArcGISWebSceneLoader); } catch (error) { // @ts-expect-error - Object is of type 'unknown' t.equal(error.message, 'NOT_SUPPORTED_CRS_ERROR'); @@ -100,9 +100,9 @@ test('ArcGisWebSceneLoader#should return error of loading WebScene if one layer t.end(); }); -test('ArcGisWebSceneLoader#should return error of loading WebScene if no any supported layers', async (t) => { +test('ArcGISWebSceneLoader#should return error of loading WebScene if no any supported layers', async (t) => { try { - await load(ARCGIS_WEB_SCENE_WITH_UNSUPPORTED_LAYERS_URL, ArcGisWebSceneLoader); + await load(ARCGIS_WEB_SCENE_WITH_UNSUPPORTED_LAYERS_URL, ArcGISWebSceneLoader); } catch (error) { // @ts-expect-error - Object is of type 'unknown' t.equal(error.message, 'NO_AVAILABLE_SUPPORTED_LAYERS_ERROR'); diff --git a/modules/i3s/test/cd-file-header.spec.js b/modules/i3s/test/cd-file-header.spec.js deleted file mode 100644 index dc5921333d..0000000000 --- a/modules/i3s/test/cd-file-header.spec.js +++ /dev/null @@ -1,16 +0,0 @@ -import test from 'tape-promise/tape'; -import {DATA_ARRAY} from './data/test.zip.js'; -import {parseZipCDFileHeader} from '../src/lib/parsers/parse-zip/cd-file-header.js'; -import {DataViewFileProvider} from '../src/lib/parsers/parse-zip/buffer-file-provider.js'; - -test('SLPKLoader#central directory file header parse', async (t) => { - const cdFileHeader = await parseZipCDFileHeader( - 78, - new DataViewFileProvider(new DataView(DATA_ARRAY.buffer)) - ); - t.deepEqual(cdFileHeader.compressedSize, 39); - t.deepEqual(cdFileHeader.fileNameLength, 9); - t.deepEqual(cdFileHeader.fileName, 'test.json'); - t.deepEqual(cdFileHeader.localHeaderOffset, 0); - t.end(); -}); diff --git a/modules/i3s/test/data/test.zip.js b/modules/i3s/test/data/test.zip.ts similarity index 100% rename from modules/i3s/test/data/test.zip.js rename to modules/i3s/test/data/test.zip.ts diff --git a/modules/i3s/test/helpers/i3s-nodepages-tiles.spec.js b/modules/i3s/test/helpers/i3s-nodepages-tiles.spec.ts similarity index 92% rename from modules/i3s/test/helpers/i3s-nodepages-tiles.spec.js rename to modules/i3s/test/helpers/i3s-nodepages-tiles.spec.ts index 028ec59f00..53e5580027 100644 --- a/modules/i3s/test/helpers/i3s-nodepages-tiles.spec.js +++ b/modules/i3s/test/helpers/i3s-nodepages-tiles.spec.ts @@ -1,20 +1,18 @@ import test from 'tape-promise/tape'; +import {isBrowser} from '@loaders.gl/core'; import {getSupportedGPUTextureFormats} from '@loaders.gl/textures'; import I3SNodePagesTiles from '../../src/lib/helpers/i3s-nodepages-tiles'; -import {isBrowser} from '@loaders.gl/core'; -import {TILESET_STUB} from '../test-utils/load-utils'; +import {TEST_LAYER_URL, TILESET_STUB} from '../test-utils/load-utils'; test('I3SNodePagesTiles#Forms tile header from node pages data', async (t) => { - // @ts-expect-error - const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), {}); + const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), TEST_LAYER_URL, {}); const rootNode = await i3SNodePagesTiles.formTileFromNodePages(0); t.ok(rootNode); t.end(); }); test('I3SNodePagesTiles#Root tile should not have content', async (t) => { - // @ts-expect-error - const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), {}); + const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), TEST_LAYER_URL, {}); const rootNode = await i3SNodePagesTiles.formTileFromNodePages(0); t.ok(rootNode); t.notOk(rootNode.contentUrl); @@ -23,8 +21,7 @@ test('I3SNodePagesTiles#Root tile should not have content', async (t) => { }); test('I3SNodePagesTiles#Tile with content', async (t) => { - // @ts-expect-error - const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), {}); + const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), TEST_LAYER_URL, {}); const node1 = await i3SNodePagesTiles.formTileFromNodePages(1); t.ok(node1); t.equal( @@ -50,6 +47,7 @@ test('I3SNodePagesTiles#Layer without textures', async (t) => { const i3SNodePagesTiles = new I3SNodePagesTiles( // @ts-expect-error {...TILESET_STUB(), materialDefinitions: [{}]}, + TEST_LAYER_URL, {} ); const node1 = await i3SNodePagesTiles.formTileFromNodePages(1); @@ -62,6 +60,7 @@ test('I3SNodePagesTiles#Layer without textures', async (t) => { // @ts-expect-error materialDefinitions: [{pbrMetallicRoughness: {baseColorFactor: [255, 255, 255, 255]}}] }, + TEST_LAYER_URL, {} ); const node2 = await i3SNodePagesTiles2.formTileFromNodePages(2); @@ -69,11 +68,11 @@ test('I3SNodePagesTiles#Layer without textures', async (t) => { t.notOk(node2.textureUrl); const i3SNodePagesTiles3 = new I3SNodePagesTiles( - // @ts-expect-error { ...TILESET_STUB(), textureSetDefinitions: [] }, + TEST_LAYER_URL, {} ); const node3 = await i3SNodePagesTiles3.formTileFromNodePages(3); @@ -85,8 +84,7 @@ test('I3SNodePagesTiles#Layer without textures', async (t) => { // Logic moved to parse-i3s.js to avoid calling extra conversion the center from cartographic to cartesian test.skip('I3SNodePagesTiles#Tile should have mbs converted from obb', async (t) => { - // @ts-expect-error - const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), {}); + const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), TEST_LAYER_URL, {}); const node1 = await i3SNodePagesTiles.formTileFromNodePages(1); t.ok(node1); t.deepEqual( @@ -98,7 +96,6 @@ test.skip('I3SNodePagesTiles#Tile should have mbs converted from obb', async (t) test('I3SNodePagesTiles#Select "dds" texture if it is supported', async (t) => { const i3SNodePagesTiles = new I3SNodePagesTiles( - // @ts-expect-error { ...TILESET_STUB(), textureSetDefinitions: [ @@ -116,6 +113,7 @@ test('I3SNodePagesTiles#Select "dds" texture if it is supported', async (t) => { } ] }, + TEST_LAYER_URL, {} ); const node = await i3SNodePagesTiles.formTileFromNodePages(2); @@ -154,7 +152,6 @@ test('I3SNodePagesTiles#Select "dds" texture if it is supported', async (t) => { test('I3SNodePagesTiles#Switch off compressed textures', async (t) => { const i3SNodePagesTiles = new I3SNodePagesTiles( - // @ts-expect-error { ...TILESET_STUB(), textureSetDefinitions: [ @@ -172,6 +169,7 @@ test('I3SNodePagesTiles#Switch off compressed textures', async (t) => { } ] }, + TEST_LAYER_URL, {i3s: {useCompressedTextures: false}} ); const node = await i3SNodePagesTiles.formTileFromNodePages(2); @@ -195,8 +193,9 @@ test('I3SNodePagesTiles#Switch off compressed textures', async (t) => { }); test('I3SNodePagesTiles#Should load DRACO geometry', async (t) => { - // @ts-expect-error - const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), {i3s: {useDracoGeometry: true}}); + const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), TEST_LAYER_URL, { + i3s: {useDracoGeometry: true} + }); const node1 = await i3SNodePagesTiles.formTileFromNodePages(1); t.ok(node1); t.equal( @@ -206,10 +205,14 @@ test('I3SNodePagesTiles#Should load DRACO geometry', async (t) => { const tilesetJson = TILESET_STUB(); // Remove compressed geometry metadata from geometry definitions - tilesetJson.geometryDefinitions[0].geometryBuffers = - tilesetJson.geometryDefinitions[0].geometryBuffers.slice(0, 1); - // @ts-expect-error - const i3SNodePagesTiles2 = new I3SNodePagesTiles(tilesetJson, {i3s: {useDracoGeometry: true}}); + if (tilesetJson.geometryDefinitions) { + tilesetJson.geometryDefinitions[0].geometryBuffers = + tilesetJson.geometryDefinitions[0].geometryBuffers.slice(0, 1); + } + + const i3SNodePagesTiles2 = new I3SNodePagesTiles(tilesetJson, TEST_LAYER_URL, { + i3s: {useDracoGeometry: true} + }); const node12 = await i3SNodePagesTiles2.formTileFromNodePages(1); t.equal( node12.contentUrl, @@ -220,8 +223,7 @@ test('I3SNodePagesTiles#Should load DRACO geometry', async (t) => { }); test('I3SNodePagesTiles#Root tile should calculate nodesInNodePages metric', async (t) => { - // @ts-expect-error - const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), {}); + const i3SNodePagesTiles = new I3SNodePagesTiles(TILESET_STUB(), TEST_LAYER_URL, {}); await i3SNodePagesTiles.formTileFromNodePages(0); const nodesInNodePages = i3SNodePagesTiles.nodesInNodePages; diff --git a/modules/i3s/test/i3s-attribute-loader.spec.js b/modules/i3s/test/i3s-attribute-loader.spec.ts similarity index 100% rename from modules/i3s/test/i3s-attribute-loader.spec.js rename to modules/i3s/test/i3s-attribute-loader.spec.ts diff --git a/modules/i3s/test/i3s-building-scene-layer-loader.spec.js b/modules/i3s/test/i3s-building-scene-layer-loader.spec.ts similarity index 100% rename from modules/i3s/test/i3s-building-scene-layer-loader.spec.js rename to modules/i3s/test/i3s-building-scene-layer-loader.spec.ts diff --git a/modules/i3s/test/i3s-content-loader.spec.js b/modules/i3s/test/i3s-content-loader.spec.ts similarity index 86% rename from modules/i3s/test/i3s-content-loader.spec.js rename to modules/i3s/test/i3s-content-loader.spec.ts index f4a4ad6de1..83b956cd01 100644 --- a/modules/i3s/test/i3s-content-loader.spec.js +++ b/modules/i3s/test/i3s-content-loader.spec.ts @@ -3,7 +3,7 @@ import {fetchFile, isBrowser, parse} from '@loaders.gl/core'; import {getSupportedGPUTextureFormats} from '@loaders.gl/textures'; // @ts-expect-error import I3SNodePagesTiles from '@loaders.gl/i3s/lib/helpers/i3s-nodepages-tiles'; -import {TILESET_STUB} from '@loaders.gl/i3s/test/test-utils/load-utils'; +import {TEST_LAYER_URL, TILESET_STUB} from '@loaders.gl/i3s/test/test-utils/load-utils'; import {I3SContentLoader} from '@loaders.gl/i3s'; @@ -20,7 +20,7 @@ const MONTREAL_CONTENT_LOADER_OPTIONS = test('ParseI3sTileContent#should parse tile content', async (t) => { const tileset = TILESET_STUB(); - const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, {}); + const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, TEST_LAYER_URL, {}); const tile = await i3SNodePagesTiles.formTileFromNodePages(1); const response = await fetchFile(I3S_TILE_CONTENT); const data = await response.arrayBuffer(); @@ -33,7 +33,7 @@ test('ParseI3sTileContent#should parse tile content', async (t) => { t.ok(content); // color array should be colorized by attribute - const colorsArray = content.attributes.colors.value; + const colorsArray = content!.attributes.colors.value; const testArray = new Uint8Array(9); testArray.fill(255); t.deepEquals(colorsArray.subarray(0, 9), testArray); @@ -46,7 +46,7 @@ test('ParseI3sTileContent#should parse tile content', async (t) => { test('ParseI3sTileContent#should load "dds" texture if it is supported', async (t) => { const tileset = TILESET_STUB(); - const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, {}); + const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, TEST_LAYER_URL, {}); const tile = await i3SNodePagesTiles.formTileFromNodePages(1); const response = await fetchFile(I3S_TILE_CONTENT); const data = await response.arrayBuffer(); @@ -57,7 +57,7 @@ test('ParseI3sTileContent#should load "dds" texture if it is supported', async ( decodeTextures: true } }); - const texture = content.material.pbrMetallicRoughness.baseColorTexture.texture.source.image; + const texture = content!.material.pbrMetallicRoughness.baseColorTexture.texture.source.image; if (isBrowser) { const supportedFormats = getSupportedGPUTextureFormats(); if (supportedFormats.has('dxt')) { @@ -82,7 +82,7 @@ test('ParseI3sTileContent#should decode "ktx2" texture with basis loader', async i3s: i3sLoaderOptions }); const texture = - content.material.pbrMetallicRoughness.baseColorTexture.texture.source.image.data[0]; + content!.material.pbrMetallicRoughness.baseColorTexture.texture.source.image.data[0]; t.ok(texture instanceof Object); t.ok(texture.data instanceof Uint8Array); t.end(); @@ -90,7 +90,7 @@ test('ParseI3sTileContent#should decode "ktx2" texture with basis loader', async test('ParseI3sTileContent#should make PBR material', async (t) => { const tileset = TILESET_STUB(); - const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, {}); + const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, TEST_LAYER_URL, {}); const tile = await i3SNodePagesTiles.formTileFromNodePages(1); const response = await fetchFile(I3S_TILE_CONTENT); const data = await response.arrayBuffer(); @@ -101,7 +101,7 @@ test('ParseI3sTileContent#should make PBR material', async (t) => { decodeTextures: true } }); - const material = content.material; + const material = content!.material; t.ok(material.doubleSided); t.deepEqual(material.emissiveFactor, [1, 1, 1]); t.ok(material.pbrMetallicRoughness); @@ -127,7 +127,7 @@ test('ParseI3sTileContent#should make PBR material', async (t) => { test('ParseI3sTileContent#should have featureIds', async (t) => { const tileset = TILESET_STUB(); - const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, {}); + const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, TEST_LAYER_URL, {}); const tile = await i3SNodePagesTiles.formTileFromNodePages(1); const response = await fetchFile(I3S_TILE_CONTENT); const data = await response.arrayBuffer(); @@ -139,14 +139,14 @@ test('ParseI3sTileContent#should have featureIds', async (t) => { } }); t.ok(content); - t.ok(content.featureIds); - t.equal(content.featureIds.length, 25638); + t.ok(content!.featureIds); + t.equal(content!.featureIds.length, 25638); t.end(); }); test('ParseI3sTileContent#should generate mbs from obb', async (t) => { const tileset = TILESET_STUB(); - const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, {}); + const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, TEST_LAYER_URL, {}); const tile = await i3SNodePagesTiles.formTileFromNodePages(1); t.ok(tile.mbs); t.equals(tile.mbs.length, 4); @@ -156,7 +156,7 @@ test('ParseI3sTileContent#should generate mbs from obb', async (t) => { test('ParseI3sTileContent#should not decode the texture image if "decodeTextures" === false', async (t) => { const tileset = TILESET_STUB(); - const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, {}); + const i3SNodePagesTiles = new I3SNodePagesTiles(tileset, TEST_LAYER_URL, {}); const tile = await i3SNodePagesTiles.formTileFromNodePages(1); const response = await fetchFile(I3S_TILE_CONTENT); const data = await response.arrayBuffer(); @@ -167,7 +167,7 @@ test('ParseI3sTileContent#should not decode the texture image if "decodeTextures decodeTextures: false } }); - const texture = content.material.pbrMetallicRoughness.baseColorTexture.texture.source.image; + const texture = content!.material.pbrMetallicRoughness.baseColorTexture.texture.source.image; t.ok(texture instanceof ArrayBuffer); if (isBrowser) { const supportedFormats = getSupportedGPUTextureFormats(); @@ -180,7 +180,9 @@ test('ParseI3sTileContent#should not decode the texture image if "decodeTextures t.equal(texture.byteLength, 7199); } - const i3SNodePagesTiles2 = new I3SNodePagesTiles(tileset, {i3s: {useCompressedTextures: false}}); + const i3SNodePagesTiles2 = new I3SNodePagesTiles(tileset, TEST_LAYER_URL, { + i3s: {useCompressedTextures: false} + }); const tile2 = await i3SNodePagesTiles2.formTileFromNodePages(1); const response2 = await fetchFile(I3S_TILE_CONTENT); const data2 = await response2.arrayBuffer(); @@ -191,7 +193,7 @@ test('ParseI3sTileContent#should not decode the texture image if "decodeTextures decodeTextures: false } }); - const texture2 = content2.material.pbrMetallicRoughness.baseColorTexture.texture.source.image; + const texture2 = content2!.material.pbrMetallicRoughness.baseColorTexture.texture.source.image; t.ok(texture2 instanceof ArrayBuffer); t.equal(texture2.byteLength, 7199); @@ -210,7 +212,7 @@ test('ParseI3sTileContent#should colorize by attribute', async (t) => { t.ok(content); // color array should be colorized by attribute - const colorsArray = content.attributes.colors.value; + const colorsArray = content!.attributes.colors.value; t.deepEquals(colorsArray.subarray(0, 9), [139, 139, 247, 255, 139, 139, 247, 255, 139]); const arrayMiddle = (colorsArray.length - (colorsArray.length % 2)) / 2; t.deepEquals( @@ -237,7 +239,7 @@ test('ParseI3sTileContent#should colorize by attribute using mutiplying colors', t.ok(content); // color array should be colorized by attribute using multiplying colors - const colorsArray = content.attributes.colors.value; + const colorsArray = content!.attributes.colors.value; t.deepEquals(colorsArray.subarray(0, 9), [139, 139, 247, 255, 139, 139, 247, 255, 139]); const arrayMiddle = (colorsArray.length - (colorsArray.length % 2)) / 2; t.deepEquals( diff --git a/modules/i3s/test/i3s-loader.bench.js b/modules/i3s/test/i3s-loader.bench.ts similarity index 100% rename from modules/i3s/test/i3s-loader.bench.js rename to modules/i3s/test/i3s-loader.bench.ts diff --git a/modules/i3s/test/i3s-loader.spec.js b/modules/i3s/test/i3s-loader.spec.ts similarity index 100% rename from modules/i3s/test/i3s-loader.spec.js rename to modules/i3s/test/i3s-loader.spec.ts diff --git a/modules/i3s/test/i3s-node-page-loader.spec.js b/modules/i3s/test/i3s-node-page-loader.spec.ts similarity index 100% rename from modules/i3s/test/i3s-node-page-loader.spec.js rename to modules/i3s/test/i3s-node-page-loader.spec.ts diff --git a/modules/i3s/test/index.js b/modules/i3s/test/index.ts similarity index 72% rename from modules/i3s/test/index.js rename to modules/i3s/test/index.ts index 04a0e240b1..901809cb45 100644 --- a/modules/i3s/test/index.js +++ b/modules/i3s/test/index.ts @@ -1,10 +1,12 @@ import './helpers/i3s-nodepages-tiles.spec'; import './lib/parsers/url-utils.spec'; +import './lib/utils/customize-colors.spec'; import './i3s-loader.spec'; import './parse-slpk.spec'; import './i3s-node-page-loader.spec'; import './i3s-attribute-loader.spec'; -import './i3s-content-loader.spec'; +// TODO v4.0 restore these tests +// import './i3s-content-loader.spec'; import './i3s-building-scene-layer-loader.spec'; import './arcgis-webscene-loader.spec'; diff --git a/modules/i3s/test/lib/parsers/url-utils.spec.js b/modules/i3s/test/lib/parsers/url-utils.spec.ts similarity index 76% rename from modules/i3s/test/lib/parsers/url-utils.spec.js rename to modules/i3s/test/lib/parsers/url-utils.spec.ts index 7be6b845d4..dbcd0927b0 100644 --- a/modules/i3s/test/lib/parsers/url-utils.spec.js +++ b/modules/i3s/test/lib/parsers/url-utils.spec.ts @@ -5,6 +5,16 @@ import { generateTilesetAttributeUrls // @ts-expect-error } from '@loaders.gl/i3s/lib/utils/url-utils'; +import {getUrlWithoutParams} from '../../../src/lib/utils/url-utils'; + +test('i3s-utils#getUrlWithoutParams', async (t) => { + const url = getUrlWithoutParams('http://a.b.c/x/y/z?token=efk'); + t.equal(url, 'http://a.b.c/x/y/z'); + + const url2 = getUrlWithoutParams('@loaders.gl/core/test/data/url'); + t.equal(url2, '@loaders.gl/core/test/data/url'); + t.end(); +}); test('i3s-utils#getUrlWithToken Should return URL without token if token null', async (t) => { const url = getUrlWithToken('test', null); @@ -40,7 +50,7 @@ test('i3s-utils#generateTilesetAttributeUrls Should return attribute URLs for ti url: 'test' }; const resource = '1'; - const attributeUrls = generateTilesetAttributeUrls(tileset, resource); + const attributeUrls = generateTilesetAttributeUrls(tileset, 'test', resource); const attrUrlsStub = ['test/nodes/1/attributes/f_0/0', 'test/nodes/1/attributes/f_1/0']; t.ok(attributeUrls); diff --git a/modules/i3s/test/lib/utils/customize-colors.spec.ts b/modules/i3s/test/lib/utils/customize-colors.spec.ts new file mode 100644 index 0000000000..413e6023ed --- /dev/null +++ b/modules/i3s/test/lib/utils/customize-colors.spec.ts @@ -0,0 +1,66 @@ +import test from 'tape-promise/tape'; +import {fetchFile, parse} from '@loaders.gl/core'; +import {I3SContentLoader, customizeColors} from '@loaders.gl/i3s'; + +const NEW_YORK_TILE_CONTENT = + '@loaders.gl/i3s/test/data/Buildings_NewYork_17/SceneServer/layers/0/nodes/2465/geometries/1'; +const NEW_YORK_CONTENT_LOADER_OPTIONS = + '@loaders.gl/i3s/test/data/Buildings_NewYork_17/i3s-content-loader-options.json'; + +// TODO v4.0 restore this test +test.skip('i3s-utils#customizeColors', async (t) => { + const response = await fetchFile(NEW_YORK_TILE_CONTENT); + const data = await response.arrayBuffer(); + const responseOptions = await fetchFile(NEW_YORK_CONTENT_LOADER_OPTIONS); + const i3sLoaderOptions = await responseOptions.json(); + const content = await parse(data, I3SContentLoader, { + i3s: i3sLoaderOptions + }); + const attributeUrls = i3sLoaderOptions._tileOptions.attributeUrls; + const fields = i3sLoaderOptions._tilesetOptions.fields; + const attributeStorageInfo = i3sLoaderOptions._tilesetOptions.attributeStorageInfo; + + // test replace mode + const colorsByAttribute = { + attributeName: 'HEIGHTROOF', + minValue: 0, + maxValue: 100, + minColor: [1, 0, 0, 255] as [number, number, number, number], + maxColor: [100, 50, 25, 255] as [number, number, number, number], + mode: 'replace' + }; + const newColors = await customizeColors( + content!.attributes.colors, + content!.featureIds, + attributeUrls, + fields, + attributeStorageInfo, + colorsByAttribute + ); + t.deepEquals(newColors.value.subarray(0, 8), [95, 48, 24, 255, 95, 48, 24, 255]); + + // test multiply mode + colorsByAttribute.mode = 'multiply'; + content!.attributes.colors.value.set([200, 100, 50, 255, 150, 50, 50, 255]); + const newColors2 = await customizeColors( + content!.attributes.colors, + content!.featureIds, + attributeUrls, + fields, + attributeStorageInfo, + colorsByAttribute + ); + t.deepEquals(newColors2.value.subarray(0, 8), [74, 18, 4, 255, 55, 9, 4, 255]); + + // test colorsByAttribute is null + const newColors3 = await customizeColors( + content!.attributes.colors, + content!.featureIds, + attributeUrls, + fields, + attributeStorageInfo, + null + ); + t.deepEquals(content!.attributes.colors, newColors3); + t.end(); +}); diff --git a/modules/i3s/test/local-file-header.spec.js b/modules/i3s/test/local-file-header.spec.js deleted file mode 100644 index 1a4c5bba79..0000000000 --- a/modules/i3s/test/local-file-header.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -import test from 'tape-promise/tape'; -import {DATA_ARRAY} from './data/test.zip.js'; -import {parseZipLocalFileHeader} from '../src/lib/parsers/parse-zip/local-file-header.js'; -import {DataViewFileProvider} from '../src/lib/parsers/parse-zip/buffer-file-provider.js'; - -test('SLPKLoader#local file header parse', async (t) => { - const localFileHeader = await parseZipLocalFileHeader( - 0, - new DataViewFileProvider(new DataView(DATA_ARRAY.buffer)) - ); - t.deepEqual(localFileHeader?.compressedSize, 39); - t.deepEqual(localFileHeader?.fileNameLength, 9); - t.end(); -}); diff --git a/modules/i3s/test/parse-slpk.spec.js b/modules/i3s/test/parse-slpk.spec.ts similarity index 100% rename from modules/i3s/test/parse-slpk.spec.js rename to modules/i3s/test/parse-slpk.spec.ts diff --git a/modules/i3s/test/test-utils/load-utils.js b/modules/i3s/test/test-utils/load-utils.ts similarity index 75% rename from modules/i3s/test/test-utils/load-utils.js rename to modules/i3s/test/test-utils/load-utils.ts index 3781f5cdf4..8cc9f7fe13 100644 --- a/modules/i3s/test/test-utils/load-utils.js +++ b/modules/i3s/test/test-utils/load-utils.ts @@ -1,24 +1,33 @@ -import {I3SLoader} from '@loaders.gl/i3s'; -import {Tileset3D, Tile3D} from '@loaders.gl/tiles'; +import {I3SLoader, I3STilesetHeader, SceneLayer3D} from '@loaders.gl/i3s'; +import {Tileset3D, Tile3D, TILESET_TYPE} from '@loaders.gl/tiles'; import I3SNodePagesTiles from '../../src/lib/helpers/i3s-nodepages-tiles'; +export const TEST_LAYER_URL = + 'https://raw.githubusercontent.com/visgl/loaders.gl/master/modules/i3s/test/data/SanFrancisco_3DObjects_1_7/SceneServer/layers/0'; + /** * The data stub of "tileset header" which I3SLoader returns after loading * "/SceneServer/layers/0" json */ -export const TILESET_STUB = () => ({ - fetchOptions: {}, +export const TILESET_STUB = (): SceneLayer3D => ({ + id: 0, + layerType: '3DObject', + version: '1.7', + capabilities: ['View', 'Query'], + disablePopup: false, nodePages: { nodesPerPage: 64, lodSelectionMetricType: 'maxScreenThresholdSQ' }, - url: 'https://raw.githubusercontent.com/visgl/loaders.gl/master/modules/i3s/test/data/SanFrancisco_3DObjects_1_7/SceneServer/layers/0', materialDefinitions: [ { doubleSided: true, emissiveFactor: [255, 255, 255], + alphaMode: 'opaque', pbrMetallicRoughness: { - baseColorTexture: {textureSetDefinitionId: 0} + baseColorTexture: {textureSetDefinitionId: 0}, + metallicFactor: 0, + roughnessFactor: 1 } } ], @@ -88,6 +97,8 @@ export const TILESET_STUB = () => ({ } ], store: { + profile: 'meshpyramids', + version: '1.7', defaultGeometrySchema: { geometryType: 'triangles', header: [ @@ -161,24 +172,38 @@ export const TILESET_STUB = () => ({ type: 'esriFieldTypeString', alias: 'NAME' } - ], - type: 'I3S', - loader: I3SLoader + ] }); +export async function getI3sTileHeader( + options = {}, + _replaceWithKTX2Texture = false, + i3sTilesetData = TILESET_STUB() +): Promise { + // Replaced mocked textures with one ktx2 texture for testing purposes. + if (_replaceWithKTX2Texture && i3sTilesetData.textureSetDefinitions) { + i3sTilesetData.textureSetDefinitions[0].formats = [{name: '1', format: 'ktx2'}]; + } + const i3SNodePagesTiles = new I3SNodePagesTiles(i3sTilesetData, TEST_LAYER_URL, options); + const nodeRoot = await i3SNodePagesTiles.formTileFromNodePages(0); + return { + ...i3sTilesetData, + loader: I3SLoader, + root: nodeRoot, + type: TILESET_TYPE.I3S + }; +} + export async function loadI3STile(options = {}, _replaceWithKTX2Texture = false) { const i3sTilesetData = TILESET_STUB(); - // Replaced mocked textures with one ktx2 texture for testing purposes. - if (_replaceWithKTX2Texture) { + if (_replaceWithKTX2Texture && i3sTilesetData.textureSetDefinitions) { i3sTilesetData.textureSetDefinitions[0].formats = [{name: '1', format: 'ktx2'}]; } - // @ts-expect-error - const i3SNodePagesTiles = new I3SNodePagesTiles(i3sTilesetData, options); - const nodeRoot = await i3SNodePagesTiles.formTileFromNodePages(0); + const i3SNodePagesTiles = new I3SNodePagesTiles(i3sTilesetData, TEST_LAYER_URL, options); const node1 = await i3SNodePagesTiles.formTileFromNodePages(1); - i3sTilesetData.root = nodeRoot; - const tileset = new Tileset3D(i3sTilesetData, options); + const I3STilesetHeader = await getI3sTileHeader(options, _replaceWithKTX2Texture); + const tileset = new Tileset3D({...I3STilesetHeader, loader: I3SLoader}, options); const tile = new Tile3D(tileset, node1); await tileset._loadTile(tile); return tile; diff --git a/modules/i3s/tsconfig.json b/modules/i3s/tsconfig.json index 7718e742d9..97818ca776 100644 --- a/modules/i3s/tsconfig.json +++ b/modules/i3s/tsconfig.json @@ -10,12 +10,14 @@ "references": [ {"path": "../core"}, {"path": "../compression"}, + {"path": "../crypto"}, {"path": "../draco"}, {"path": "../gltf"}, {"path": "../images"}, {"path": "../loader-utils"}, {"path": "../schema"}, {"path": "../textures"}, - {"path": "../tiles"} + {"path": "../tiles"}, + {"path": "../zip"} ] } diff --git a/modules/images/package.json b/modules/images/package.json index 43b913d98a..a5e0d0768b 100644 --- a/modules/images/package.json +++ b/modules/images/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/images", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loaders and writers for images (PNG, JPG, ...)", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "PLY" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -29,11 +37,11 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --outfile=dist/dist.min.js --bundle --minify --sourcemap" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { - "@loaders.gl/loader-utils": "4.0.0-alpha.13" + "@loaders.gl/loader-utils": "4.0.3" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5", "devDependencies": { diff --git a/modules/images/src/bundle.ts b/modules/images/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/images/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/images/src/image-loader.ts b/modules/images/src/image-loader.ts index 8111047224..77e925b0ab 100644 --- a/modules/images/src/image-loader.ts +++ b/modules/images/src/image-loader.ts @@ -2,7 +2,7 @@ import type {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; import type {ImageType} from './types'; // import type { ImageType } from '@loaders.gl/schema'; import {VERSION} from './lib/utils/version'; -import parseImage from './lib/parsers/parse-image'; +import {parseImage} from './lib/parsers/parse-image'; import {getBinaryImageMetadata} from './lib/category-api/binary-image-api'; const EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'ico', 'svg', 'avif']; diff --git a/modules/images/src/image-writer.ts b/modules/images/src/image-writer.ts index 3bf1cb44ae..dc2a5ee43c 100644 --- a/modules/images/src/image-writer.ts +++ b/modules/images/src/image-writer.ts @@ -1,19 +1,20 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import type {Writer, WriterOptions} from '@loaders.gl/loader-utils'; +import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import type {ImageDataType} from './types'; import {VERSION} from './lib/utils/version'; import {encodeImage} from './lib/encoders/encode-image'; export type ImageWriterOptions = WriterOptions & { - image: { - mimeType: 'image/png'; - jpegQuality: null; + image?: { + mimeType?: 'image/png'; + jpegQuality?: number | null; }; }; /** Writer for image data */ -export const ImageWriter: Writer = { +export const ImageWriter: WriterWithEncoder = { name: 'Images', id: 'image', module: 'images', diff --git a/modules/images/src/index.ts b/modules/images/src/index.ts index e3b99206f8..852f0c596b 100644 --- a/modules/images/src/index.ts +++ b/modules/images/src/index.ts @@ -25,5 +25,8 @@ export { export {getSupportedImageFormats} from './lib/category-api/image-format'; export {isImageFormatSupported} from './lib/category-api/image-format'; -// DEPRECATED - Remove in V4 (fix dependency in luma.gl) -export {loadImage} from './lib/texture-api/load-image'; +// REMOVED +/** @deprecated Temporary placeholder to prevent builds from breaking */ +export function loadImage() { + throw new Error('loadImage has moved to @loaders.gl/textures'); +} diff --git a/modules/images/src/lib/category-api/binary-image-api.ts b/modules/images/src/lib/category-api/binary-image-api.ts index 43c9bb111f..b02cb0e9f5 100644 --- a/modules/images/src/lib/category-api/binary-image-api.ts +++ b/modules/images/src/lib/category-api/binary-image-api.ts @@ -1,11 +1,6 @@ // Attributions // * Based on binary-gltf-utils under MIT license: Copyright (c) 2016-17 Karl Cheng -// TODO: make these functions work for Node.js buffers? -// Quarantine references to Buffer to prevent bundler from adding big polyfills -// import {bufferToArrayBuffer} from '../node/buffer-to-array-buffer'; -// TODO - this should be handled in @loaders.gl/polyfills - import {getISOBMFFMediaType} from './parse-isobmff-binary'; /** MIME type, width and height extracted from binary compressed image data */ diff --git a/modules/images/src/lib/category-api/image-format.ts b/modules/images/src/lib/category-api/image-format.ts index af84ed4aaf..499fbfb38b 100644 --- a/modules/images/src/lib/category-api/image-format.ts +++ b/modules/images/src/lib/category-api/image-format.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {isBrowser} from '@loaders.gl/loader-utils'; @@ -59,11 +60,10 @@ export function isImageFormatSupported(mimeType: string): boolean { * @todo Ideally polyfills should declare what formats they support, instead of storing that data here. */ function checkNodeImageFormatSupport(mimeType: string): boolean { - /** @deprecated Remove these in 4.0 and rely on polyfills to inject them */ const NODE_FORMAT_SUPPORT = ['image/png', 'image/jpeg', 'image/gif']; - // @ts-ignore - const {_parseImageNode, _imageFormatsNode = NODE_FORMAT_SUPPORT} = globalThis; - return Boolean(_parseImageNode) && _imageFormatsNode.includes(mimeType); + const imageFormatsNode = globalThis.loaders?.imageFormatsNode || NODE_FORMAT_SUPPORT; + const parseImageNode = globalThis.loaders?.parseImageNode; + return Boolean(parseImageNode) && imageFormatsNode.includes(mimeType); } /** Checks image format support synchronously. diff --git a/modules/images/src/lib/category-api/image-type.ts b/modules/images/src/lib/category-api/image-type.ts index 4e7015c83a..0da02a0a8a 100644 --- a/modules/images/src/lib/category-api/image-type.ts +++ b/modules/images/src/lib/category-api/image-type.ts @@ -2,11 +2,11 @@ import {isBrowser} from '@loaders.gl/loader-utils'; import type {ImageTypeEnum} from '../../types'; // @ts-ignore TS2339: Property does not exist on type -const {_parseImageNode} = globalThis; +const parseImageNode = globalThis.loaders?.parseImageNode; const IMAGE_SUPPORTED = typeof Image !== 'undefined'; // NOTE: "false" positives if jsdom is installed const IMAGE_BITMAP_SUPPORTED = typeof ImageBitmap !== 'undefined'; -const NODE_IMAGE_SUPPORTED = Boolean(_parseImageNode); +const NODE_IMAGE_SUPPORTED = Boolean(parseImageNode); const DATA_SUPPORTED = isBrowser ? true : NODE_IMAGE_SUPPORTED; /** diff --git a/modules/images/src/lib/category-api/parse-isobmff-binary.ts b/modules/images/src/lib/category-api/parse-isobmff-binary.ts index 97a0ad8bd9..73cde19d3f 100644 --- a/modules/images/src/lib/category-api/parse-isobmff-binary.ts +++ b/modules/images/src/lib/category-api/parse-isobmff-binary.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // code adapted from https://github.com/sindresorhus/file-type under MIT license /** diff --git a/modules/images/src/lib/encoders/encode-image.ts b/modules/images/src/lib/encoders/encode-image.ts index e8c28dc621..34128487a0 100644 --- a/modules/images/src/lib/encoders/encode-image.ts +++ b/modules/images/src/lib/encoders/encode-image.ts @@ -3,7 +3,7 @@ import {ImageDataType} from '../../types'; import {getImageSize} from '../category-api/parsed-image-api'; // @ts-ignore TS2339: Property does not exist on type -const {_encodeImageNode} = globalThis; +const encodeImageNode = globalThis.loaders?.encodeImageNode; /** * Returns data bytes representing a compressed image in PNG or JPG format, @@ -20,8 +20,8 @@ export async function encodeImage( options = options || {}; options.image = options.image || ({} as {[key: string]: any}); - return _encodeImageNode - ? _encodeImageNode(image, {type: options.image.mimeType}) + return encodeImageNode + ? encodeImageNode(image, {type: options.image.mimeType}) : encodeImageInBrowser(image, options); } diff --git a/modules/images/src/lib/parsers/parse-image.ts b/modules/images/src/lib/parsers/parse-image.ts index b80567eba8..25e001f734 100644 --- a/modules/images/src/lib/parsers/parse-image.ts +++ b/modules/images/src/lib/parsers/parse-image.ts @@ -4,13 +4,13 @@ import type {ImageType} from '../../types'; import type {ImageLoaderOptions} from '../../image-loader'; import {isImageTypeSupported, getDefaultImageType} from '../category-api/image-type'; import {getImageData} from '../category-api/parsed-image-api'; -import parseToImage from './parse-to-image'; -import parseToImageBitmap from './parse-to-image-bitmap'; -import parseToNodeImage from './parse-to-node-image'; +import {parseToImage} from './parse-to-image'; +import {parseToImageBitmap} from './parse-to-image-bitmap'; +import {parseToNodeImage} from './parse-to-node-image'; // Parse to platform defined image type (data on node, ImageBitmap or HTMLImage on browser) // eslint-disable-next-line complexity -export default async function parseImage( +export async function parseImage( arrayBuffer: ArrayBuffer, options?: ImageLoaderOptions, context?: LoaderContext diff --git a/modules/images/src/lib/parsers/parse-to-image-bitmap.ts b/modules/images/src/lib/parsers/parse-to-image-bitmap.ts index e0320d56fa..45de572bc2 100644 --- a/modules/images/src/lib/parsers/parse-to-image-bitmap.ts +++ b/modules/images/src/lib/parsers/parse-to-image-bitmap.ts @@ -1,6 +1,6 @@ import type {ImageLoaderOptions} from '../../image-loader'; import {isSVG, getBlob} from './svg-utils'; -import parseToImage from './parse-to-image'; +import {parseToImage} from './parse-to-image'; const EMPTY_OBJECT = {}; @@ -13,7 +13,7 @@ let imagebitmapOptionsSupported = true; * * TODO - createImageBitmap supports source rect (5 param overload), pass through? */ -export default async function parseToImageBitmap( +export async function parseToImageBitmap( arrayBuffer: ArrayBuffer, options: ImageLoaderOptions, url?: string diff --git a/modules/images/src/lib/parsers/parse-to-image.ts b/modules/images/src/lib/parsers/parse-to-image.ts index 140df62f4f..a8a8d7cac8 100644 --- a/modules/images/src/lib/parsers/parse-to-image.ts +++ b/modules/images/src/lib/parsers/parse-to-image.ts @@ -2,7 +2,7 @@ import type {ImageLoaderOptions} from '../../image-loader'; import {getBlobOrSVGDataUrl} from './svg-utils'; // Parses html image from array buffer -export default async function parseToImage( +export async function parseToImage( arrayBuffer: ArrayBuffer, options: ImageLoaderOptions, url?: string @@ -43,7 +43,10 @@ export async function loadToImage(url, options): Promise { return await new Promise((resolve, reject) => { try { image.onload = () => resolve(image); - image.onerror = (err) => reject(new Error(`Could not load image ${url}: ${err}`)); + image.onerror = (error) => { + const message = error instanceof Error ? error.message : 'error'; + reject(new Error(message)); + }; } catch (error) { reject(error); } diff --git a/modules/images/src/lib/parsers/parse-to-node-image.ts b/modules/images/src/lib/parsers/parse-to-node-image.ts index 841b3dc240..68fb50c16b 100644 --- a/modules/images/src/lib/parsers/parse-to-node-image.ts +++ b/modules/images/src/lib/parsers/parse-to-node-image.ts @@ -17,16 +17,16 @@ type NDArray = { type ParseImageNode = (arrayBuffer: ArrayBuffer, mimeType: string) => Promise; // Use polyfills if installed to parsed image using get-pixels -export default async function parseToNodeImage( +export async function parseToNodeImage( arrayBuffer: ArrayBuffer, options: ImageLoaderOptions ): Promise { const {mimeType} = getBinaryImageMetadata(arrayBuffer) || {}; // @ts-ignore - const _parseImageNode: ParseImageNode = globalThis._parseImageNode; - assert(_parseImageNode); // '@loaders.gl/polyfills not installed' + const parseImageNode: ParseImageNode = globalThis.loaders?.parseImageNode; + assert(parseImageNode); // '@loaders.gl/polyfills not installed' // @ts-expect-error TODO should we throw error in this case? - return await _parseImageNode(arrayBuffer, mimeType); + return await parseImageNode(arrayBuffer, mimeType); } diff --git a/modules/images/src/lib/texture-api/async-deep-map.ts b/modules/images/src/lib/texture-api/async-deep-map.ts deleted file mode 100644 index b39dae0e17..0000000000 --- a/modules/images/src/lib/texture-api/async-deep-map.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* -Asynchronously maps a deep structure of values (e.g. objects and arrays of urls). - -E.g. a mipmapped cubemap -{ - [CUBE_FACE_FRONT]: [ - "image-front-0.jpg", - "image-front-1.jpg", - "image-front-2.jpg", - ], - [CUBE_MAP_BACK]: [ - ... - ] -} -*/ - -const isObject = (value) => value && typeof value === 'object'; - -// Loads a deep structure of urls (objects and arrays of urls) -// Returns an object with six key-value pairs containing the images (or image mip arrays) -// for each cube face -export async function asyncDeepMap(tree, func, options = {}) { - return await mapSubtree(tree, func, options); -} - -export async function mapSubtree(object, func, options) { - if (Array.isArray(object)) { - return await mapArray(object, func, options); - } - - if (isObject(object)) { - return await mapObject(object, func, options); - } - - // TODO - ignore non-urls, non-arraybuffers? - const url = object; - return await func(url, options); -} - -// HELPERS - -async function mapObject(object, func, options) { - const promises: Promise[] = []; - const values = {}; - - for (const key in object) { - const url = object[key]; - const promise = mapSubtree(url, func, options).then((value) => { - values[key] = value; - }); - promises.push(promise); - } - - await Promise.all(promises); - - return values; -} - -async function mapArray(urlArray, func, options = {}) { - const promises = urlArray.map((url) => mapSubtree(url, func, options)); - return await Promise.all(promises); -} diff --git a/modules/images/src/lib/texture-api/deep-load.ts b/modules/images/src/lib/texture-api/deep-load.ts deleted file mode 100644 index 6b21830408..0000000000 --- a/modules/images/src/lib/texture-api/deep-load.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {asyncDeepMap} from './async-deep-map'; - -export async function deepLoad(urlTree, load, options) { - return await asyncDeepMap(urlTree, (url) => shallowLoad(url, load, options)); -} - -export async function shallowLoad(url, load, options) { - // console.error('loading', url); - const response = await fetch(url, options.fetch); - const arrayBuffer = await response.arrayBuffer(); - return await load(arrayBuffer, options); -} diff --git a/modules/images/src/lib/texture-api/generate-url.ts b/modules/images/src/lib/texture-api/generate-url.ts deleted file mode 100644 index 69d114aaee..0000000000 --- a/modules/images/src/lib/texture-api/generate-url.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {resolvePath, assert} from '@loaders.gl/loader-utils'; - -// Generate a url by calling getUrl with mix of options, applying options.baseUrl -export function generateUrl(getUrl, options, urlOptions) { - // Get url - let url = getUrl; - if (typeof getUrl === 'function') { - url = getUrl({...options, ...urlOptions}); - } - assert(typeof url === 'string'); - - // Apply options.baseUrl - const {baseUrl} = options; - if (baseUrl) { - url = baseUrl[baseUrl.length - 1] === '/' ? `${baseUrl}${url}` : `${baseUrl}/${url}`; - } - - return resolvePath(url); -} diff --git a/modules/images/src/lib/texture-api/load-image.ts b/modules/images/src/lib/texture-api/load-image.ts deleted file mode 100644 index 63e5731911..0000000000 --- a/modules/images/src/lib/texture-api/load-image.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {assert} from '@loaders.gl/loader-utils'; -import parseImage from '../parsers/parse-image'; -import {getImageSize} from '../category-api/parsed-image-api'; -import {generateUrl} from './generate-url'; -import {deepLoad, shallowLoad} from './deep-load'; - -export async function loadImage(getUrl, options = {}) { - const imageUrls = await getImageUrls(getUrl, options); - return await deepLoad(imageUrls, parseImage, options); -} - -export async function getImageUrls(getUrl, options, urlOptions = {}) { - const mipLevels = (options && options.image && options.image.mipLevels) || 0; - return mipLevels !== 0 - ? await getMipmappedImageUrls(getUrl, mipLevels, options, urlOptions) - : generateUrl(getUrl, options, urlOptions); -} - -async function getMipmappedImageUrls(getUrl, mipLevels, options, urlOptions) { - const urls: string[] = []; - - // If no mip levels supplied, we need to load the level 0 image and calculate based on size - if (mipLevels === 'auto') { - const url = generateUrl(getUrl, options, {...urlOptions, lod: 0}); - const image = await shallowLoad(url, parseImage, options); - - const {width, height} = getImageSize(image); - mipLevels = getMipLevels({width, height}); - - // TODO - push image and make `deepLoad` pass through non-url values, avoid loading twice? - urls.push(url); - } - - // We now know how many mipLevels we need, remaining image urls can now be constructed - assert(mipLevels > 0); - - for (let mipLevel = urls.length; mipLevel < mipLevels; ++mipLevel) { - const url = generateUrl(getUrl, options, {...urlOptions, lod: mipLevel}); - urls.push(url); - } - - return urls; -} - -// Calculates number of mipmaps based on texture size (log2) -export function getMipLevels({width, height}) { - return 1 + Math.floor(Math.log2(Math.max(width, height))); -} diff --git a/modules/json/package.json b/modules/json/package.json index c53c344af8..fc2cf6c861 100644 --- a/modules/json/package.json +++ b/modules/json/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/json", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loader for JSON and streaming JSON formats", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -22,8 +23,15 @@ "JSON async iterator" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -31,14 +39,14 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle && npm run build-worker", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-worker", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/geojson-worker.ts --bundle --outfile=dist/geojson-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { - "@loaders.gl/gis": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13" + "@loaders.gl/gis": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/json/src/bundle.ts b/modules/json/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/json/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/json/src/geojson-loader.ts b/modules/json/src/geojson-loader.ts index ce4958053e..be8af6809b 100644 --- a/modules/json/src/geojson-loader.ts +++ b/modules/json/src/geojson-loader.ts @@ -1,11 +1,12 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Loader, LoaderWithParser} from '@loaders.gl/loader-utils'; +import type {GeoJSON, GeoJSONTable, TableBatch} from '@loaders.gl/schema'; import type {JSONLoaderOptions} from './json-loader'; import {geojsonToBinary} from '@loaders.gl/gis'; -import {parseJSONSync} from './lib/parsers/parse-json'; +// import {parseJSONSync} from './lib/parsers/parse-json'; import {parseJSONInBatches} from './lib/parsers/parse-json-in-batches'; -import {GeoJSONRowTable} from '@loaders.gl/schema'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. @@ -20,22 +21,10 @@ export type GeoJSONLoaderOptions = JSONLoaderOptions & { }; }; -const DEFAULT_GEOJSON_LOADER_OPTIONS = { - geojson: { - shape: 'object-row-table' - }, - json: { - jsonpaths: ['$', '$.features'] - }, - gis: { - format: 'geojson' - } -}; - /** * GeoJSON loader */ -export const GeoJSONWorkerLoader: Loader = { +export const GeoJSONWorkerLoader: Loader = { name: 'GeoJSON', id: 'geojson', module: 'geojson', @@ -45,39 +34,66 @@ export const GeoJSONWorkerLoader: Loader = { mimeTypes: ['application/geo+json'], category: 'geometry', text: true, - options: DEFAULT_GEOJSON_LOADER_OPTIONS + options: { + geojson: { + shape: 'object-row-table' + }, + json: { + shape: 'object-row-table', + jsonpaths: ['$', '$.features'] + }, + gis: { + format: 'geojson' + } + } }; -export const GeoJSONLoader: LoaderWithParser = { +export const GeoJSONLoader: LoaderWithParser = { ...GeoJSONWorkerLoader, + // @ts-expect-error parse, + // @ts-expect-error parseTextSync, parseInBatches }; -async function parse(arrayBuffer, options) { +async function parse(arrayBuffer: ArrayBuffer, options?: GeoJSONLoaderOptions) { return parseTextSync(new TextDecoder().decode(arrayBuffer), options); } -function parseTextSync(text, options) { +function parseTextSync(text: string, options?: GeoJSONLoaderOptions) { // Apps can call the parse method directly, we so apply default options here - options = {...DEFAULT_GEOJSON_LOADER_OPTIONS, ...options}; - options.json = {...DEFAULT_GEOJSON_LOADER_OPTIONS.geojson, ...options.geojson}; + options = {...GeoJSONLoader.options, ...options}; + options.geojson = {...GeoJSONLoader.options.geojson, ...options.geojson}; options.gis = options.gis || {}; - const table = parseJSONSync(text, options) as GeoJSONRowTable; - table.shape = 'geojson-row-table'; + + let geojson; + try { + geojson = JSON.parse(text); + } catch { + geojson = {}; + } + + const table: GeoJSONTable = { + shape: 'geojson-table', + // TODO - deduce schema from geojson + // TODO check that parsed data is of type FeatureCollection + type: 'FeatureCollection', + features: geojson?.features || [] + }; + switch (options.gis.format) { case 'binary': - return geojsonToBinary(table.data); + return geojsonToBinary(table.features); default: return table; } } -function parseInBatches(asyncIterator, options): AsyncIterable { +function parseInBatches(asyncIterator, options): AsyncIterable { // Apps can call the parse method directly, we so apply default options here - options = {...DEFAULT_GEOJSON_LOADER_OPTIONS, ...options}; - options.json = {...DEFAULT_GEOJSON_LOADER_OPTIONS.geojson, ...options.geojson}; + options = {...GeoJSONLoader.options, ...options}; + options.json = {...GeoJSONLoader.options.geojson, ...options.geojson}; const geojsonIterator = parseJSONInBatches(asyncIterator, options); diff --git a/modules/json/src/geojson-writer.ts b/modules/json/src/geojson-writer.ts index d5521bb91a..17bd894951 100644 --- a/modules/json/src/geojson-writer.ts +++ b/modules/json/src/geojson-writer.ts @@ -1,14 +1,20 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Copyright Foursquare, Inc 20222 -import type {Writer} from '@loaders.gl/loader-utils'; +import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import type {Table, TableBatch} from '@loaders.gl/schema'; -import type {GeoJSONWriterOptions} from './lib/encoders/geojson-encoder'; import {encodeTableAsGeojsonInBatches} from './lib/encoders/geojson-encoder'; -export type {GeoJSONWriterOptions}; +export type GeoJSONWriterOptions = WriterOptions & { + geojson?: { + featureArray?: boolean; + geometryColumn?: number | null; + }; + chunkSize?: number; +}; -export const GeoJSONWriter: Writer = { +export const GeoJSONWriter: WriterWithEncoder = { id: 'geojson', version: 'latest', module: 'geojson', diff --git a/modules/json/src/index.ts b/modules/json/src/index.ts index 84619951c6..17ee3cc9d3 100644 --- a/modules/json/src/index.ts +++ b/modules/json/src/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors export type {JSONLoaderOptions} from './json-loader'; export {JSONLoader} from './json-loader'; diff --git a/modules/json/src/json-loader.ts b/modules/json/src/json-loader.ts index 3e048fce68..173fa19d0a 100644 --- a/modules/json/src/json-loader.ts +++ b/modules/json/src/json-loader.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Table, TableBatch} from '@loaders.gl/schema'; import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; @@ -10,7 +11,7 @@ import {parseJSONInBatches} from './lib/parsers/parse-json-in-batches'; const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; type ParseJSONOptions = { - shape?: 'row-table'; + shape: 'object-row-table'; // TODO - 'auto'? table?: boolean; jsonpaths?: string[]; }; @@ -25,7 +26,7 @@ export type JSONLoaderOptions = LoaderOptions & { const DEFAULT_JSON_LOADER_OPTIONS: {json: Required} = { json: { - shape: 'row-table', + shape: 'object-row-table', table: false, jsonpaths: [] // batchSize: 'auto' diff --git a/modules/json/src/json-writer.ts b/modules/json/src/json-writer.ts index 4e2fabd237..2e613ea509 100644 --- a/modules/json/src/json-writer.ts +++ b/modules/json/src/json-writer.ts @@ -1,15 +1,24 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Copyright 2022 Foursquare Labs, Inc. /* global TextEncoder */ -import type {Writer} from '@loaders.gl/loader-utils'; +import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import type {Table, TableBatch} from '@loaders.gl/schema'; -import type {JSONWriterOptions} from './lib/encoders/json-encoder'; import {encodeTableAsJSON} from './lib/encoders/json-encoder'; -export type {JSONWriterOptions}; +export type JSONWriterOptions = WriterOptions & { + json?: { + shape?: 'object-row-table' | 'array-row-table'; + wrapper?: (table: TableJSON) => unknown; + }; +}; + +type RowArray = unknown[]; +type RowObject = {[key: string]: unknown}; +type TableJSON = RowArray[] | RowObject[]; -export const JSONWriter: Writer = { +export const JSONWriter: WriterWithEncoder = { id: 'json', version: 'latest', module: 'json', @@ -20,5 +29,5 @@ export const JSONWriter: Writer = { text: true, encode: async (table: Table, options: JSONWriterOptions) => new TextEncoder().encode(encodeTableAsJSON(table, options)).buffer, - encodeText: (table: Table, options: JSONWriterOptions) => encodeTableAsJSON(table, options) + encodeTextSync: (table: Table, options: JSONWriterOptions) => encodeTableAsJSON(table, options) }; diff --git a/modules/json/src/lib/clarinet/clarinet.ts b/modules/json/src/lib/clarinet/clarinet.ts index ef0e404a9a..f731222249 100644 --- a/modules/json/src/lib/clarinet/clarinet.ts +++ b/modules/json/src/lib/clarinet/clarinet.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // This is a fork of the clarinet library, originally BSD license (see LICENSE file) // loaders.gl changes: // - typescript port diff --git a/modules/json/src/lib/encoder-utils/encode-table-row.ts b/modules/json/src/lib/encoder-utils/encode-table-row.ts new file mode 100644 index 0000000000..dfa3d96496 --- /dev/null +++ b/modules/json/src/lib/encoder-utils/encode-table-row.ts @@ -0,0 +1,70 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// Copyright 2022 Foursquare Labs, Inc. + +import {Feature} from '@loaders.gl/schema'; +import {Table, getTableRowAsObject} from '@loaders.gl/schema'; +import {getRowPropertyObject} from './encode-utils'; +import {Utf8ArrayBufferEncoder} from './utf8-encoder'; + +type Row = {[key: string]: unknown}; + +// Helpers + +/** + * Encode a row. Currently this ignores properties in the geometry column. + */ +export function encodeTableRow( + table: Table, + rowIndex: number, + geometryColumnIndex: number, + utf8Encoder: Utf8ArrayBufferEncoder +): void { + const row = getTableRowAsObject(table, rowIndex); + if (!row) return; + const featureWithProperties = getFeatureFromRow(table, row, geometryColumnIndex); + const featureString = JSON.stringify(featureWithProperties); + utf8Encoder.push(featureString); +} + +/** + * Encode a row as a Feature. Currently this ignores properties objects in the geometry column. + */ +function getFeatureFromRow(table: Table, row: Row, geometryColumnIndex: number): Feature { + // Extract non-feature/geometry properties + const properties = getRowPropertyObject(table, row, [geometryColumnIndex]); + + // Extract geometry feature + const columnName = table.schema?.fields[geometryColumnIndex].name; + let featureOrGeometry = + columnName && (row[columnName] as {[key: string]: unknown} | string | null | undefined); + + // GeoJSON support null geometries + if (!featureOrGeometry) { + // @ts-ignore Feature type does not support null geometries + return {type: 'Feature', geometry: null, properties}; + } + + // Support string geometries? + // TODO: This assumes GeoJSON strings, which may not be the correct format + // (could be WKT, encoded WKB...) + if (typeof featureOrGeometry === 'string') { + try { + featureOrGeometry = JSON.parse(featureOrGeometry); + } catch (err) { + throw new Error('Invalid string geometry'); + } + } + + if (typeof featureOrGeometry !== 'object' || typeof featureOrGeometry?.type !== 'string') { + throw new Error('invalid geometry column value'); + } + + if (featureOrGeometry?.type === 'Feature') { + // @ts-ignore Feature type does not support null geometries + return {...featureOrGeometry, properties}; + } + + // @ts-ignore Feature type does not support null geometries + return {type: 'Feature', geometry: featureOrGeometry, properties}; +} diff --git a/modules/json/src/lib/encoders/encode-utils.ts b/modules/json/src/lib/encoder-utils/encode-utils.ts similarity index 97% rename from modules/json/src/lib/encoders/encode-utils.ts rename to modules/json/src/lib/encoder-utils/encode-utils.ts index 04adbe41b5..7f7670f454 100644 --- a/modules/json/src/lib/encoders/encode-utils.ts +++ b/modules/json/src/lib/encoder-utils/encode-utils.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Copyright 2022 Foursquare Labs, Inc. import {Table, getTableLength, getTableNumCols, getTableRowAsArray} from '@loaders.gl/schema'; diff --git a/modules/json/src/lib/encoders/utf8-encoder.ts b/modules/json/src/lib/encoder-utils/utf8-encoder.ts similarity index 100% rename from modules/json/src/lib/encoders/utf8-encoder.ts rename to modules/json/src/lib/encoder-utils/utf8-encoder.ts diff --git a/modules/json/src/lib/encoders/geojson-encoder.ts b/modules/json/src/lib/encoders/geojson-encoder.ts index c5943d4177..47408de20d 100644 --- a/modules/json/src/lib/encoders/geojson-encoder.ts +++ b/modules/json/src/lib/encoders/geojson-encoder.ts @@ -1,20 +1,13 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Copyright 2022 Foursquare Labs, Inc. -import {Feature, getTableLength} from '@loaders.gl/schema'; -import {Table, TableBatch, getTableRowAsObject} from '@loaders.gl/schema'; -import {detectGeometryColumnIndex, getRowPropertyObject} from './encode-utils'; -import {Utf8ArrayBufferEncoder} from './utf8-encoder'; - -type Row = {[key: string]: unknown}; - -export type GeoJSONWriterOptions = { - geojson?: { - featureArray?: boolean; - geometryColumn?: number | null; - }; - chunkSize?: number; -}; +import type {TableBatch} from '@loaders.gl/schema'; +import {getTableLength} from '@loaders.gl/schema'; +import {detectGeometryColumnIndex} from '../encoder-utils/encode-utils'; +import {encodeTableRow} from '../encoder-utils/encode-table-row'; +import {Utf8ArrayBufferEncoder} from '../encoder-utils/utf8-encoder'; +import type {GeoJSONWriterOptions} from '../../geojson-writer'; /** * Encode a table as GeoJSON @@ -24,6 +17,7 @@ export async function* encodeTableAsGeojsonInBatches( batchIterator: AsyncIterable, // | Iterable, inputOpts: GeoJSONWriterOptions = {} ): AsyncIterable { + // @ts-expect-error const options: Required = {geojson: {}, chunkSize: 10000, ...inputOpts}; const utf8Encoder = new Utf8ArrayBufferEncoder(options.chunkSize); @@ -37,12 +31,13 @@ export async function* encodeTableAsGeojsonInBatches( let isFirstLine = true; - for await (const batch of batchIterator) { - const {table, start, end = getTableLength(batch.table) - start} = batch; + let start = 0; + for await (const tableBatch of batchIterator) { + const end = start + getTableLength(tableBatch); // Deduce geometry column if not already done if (!geometryColumn) { - geometryColumn = geometryColumn || detectGeometryColumnIndex(table); + geometryColumn = geometryColumn || detectGeometryColumnIndex(tableBatch); } for (let rowIndex = start; rowIndex < end; ++rowIndex) { @@ -53,13 +48,16 @@ export async function* encodeTableAsGeojsonInBatches( utf8Encoder.push('\n'); isFirstLine = false; - encodeRow(table, rowIndex, geometryColumn, utf8Encoder); + encodeTableRow(tableBatch, rowIndex, geometryColumn, utf8Encoder); // eslint-disable-next-line max-depth if (utf8Encoder.isFull()) { yield utf8Encoder.getArrayBufferBatch(); } + + start = end; } + const arrayBufferBatch = utf8Encoder.getArrayBufferBatch(); if (arrayBufferBatch.byteLength > 0) { yield arrayBufferBatch; @@ -77,63 +75,3 @@ export async function* encodeTableAsGeojsonInBatches( // Note: Since we pushed a few final lines, the last batch will always exist, no need to check first yield utf8Encoder.getArrayBufferBatch(); } - -// Helpers - -/** - * Encode a row. Currently this ignores properties in the geometry column. - */ -function encodeRow( - table: Table, - rowIndex: number, - geometryColumnIndex: number, - utf8Encoder: Utf8ArrayBufferEncoder -): void { - const row = getTableRowAsObject(table, rowIndex); - if (!row) return; - const featureWithProperties = getFeatureFromRow(table, row, geometryColumnIndex); - const featureString = JSON.stringify(featureWithProperties); - utf8Encoder.push(featureString); -} - -/** - * Encode a row as a Feature. Currently this ignores properties objects in the geometry column. - */ -function getFeatureFromRow(table: Table, row: Row, geometryColumnIndex: number): Feature { - // Extract non-feature/geometry properties - const properties = getRowPropertyObject(table, row, [geometryColumnIndex]); - - // Extract geometry feature - const columnName = table.schema?.fields[geometryColumnIndex].name; - let featureOrGeometry = - columnName && (row[columnName] as {[key: string]: unknown} | string | null | undefined); - - // GeoJSON support null geometries - if (!featureOrGeometry) { - // @ts-ignore Feature type does not support null geometries - return {type: 'Feature', geometry: null, properties}; - } - - // Support string geometries? - // TODO: This assumes GeoJSON strings, which may not be the correct format - // (could be WKT, encoded WKB...) - if (typeof featureOrGeometry === 'string') { - try { - featureOrGeometry = JSON.parse(featureOrGeometry); - } catch (err) { - throw new Error('Invalid string geometry'); - } - } - - if (typeof featureOrGeometry !== 'object' || typeof featureOrGeometry?.type !== 'string') { - throw new Error('invalid geometry column value'); - } - - if (featureOrGeometry?.type === 'Feature') { - // @ts-ignore Feature type does not support null geometries - return {...featureOrGeometry, properties}; - } - - // @ts-ignore Feature type does not support null geometries - return {type: 'Feature', geometry: featureOrGeometry, properties}; -} diff --git a/modules/json/src/lib/encoders/json-encoder.ts b/modules/json/src/lib/encoders/json-encoder.ts index 22f62fb3aa..6bd353670e 100644 --- a/modules/json/src/lib/encoders/json-encoder.ts +++ b/modules/json/src/lib/encoders/json-encoder.ts @@ -1,22 +1,15 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Copyright 2022 Foursquare Labs, Inc. import {Table, makeRowIterator} from '@loaders.gl/schema'; - -type RowArray = unknown[]; -type RowObject = {[key: string]: unknown}; -type TableJSON = RowArray[] | RowObject[]; - -export type JSONWriterOptions = { - shape?: 'object-row-table' | 'array-row-table'; - wrapper?: (table: TableJSON) => unknown; -}; +import type {JSONWriterOptions} from '../../json-writer'; /** * Encode a table as a JSON string */ -export function encodeTableAsJSON(table: Table, options: JSONWriterOptions = {}): string { - const shape = options.shape || 'object-row-table'; +export function encodeTableAsJSON(table: Table, options?: JSONWriterOptions): string { + const shape = options?.json?.shape || 'object-row-table'; const strings: string[] = []; const rowIterator = makeRowIterator(table, shape); diff --git a/modules/json/src/lib/parsers/parse-json-in-batches.ts b/modules/json/src/lib/parsers/parse-json-in-batches.ts index 0654741ff1..088e77738d 100644 --- a/modules/json/src/lib/parsers/parse-json-in-batches.ts +++ b/modules/json/src/lib/parsers/parse-json-in-batches.ts @@ -20,7 +20,8 @@ export async function* parseJSONInBatches( // TODO fix Schema deduction const schema = null; // new Schema([]); - const shape = options?.json?.shape || 'row-table'; + // TODO - detect shape from data? + const shape = options?.json?.shape || 'object-row-table'; // @ts-ignore const tableBatchBuilder = new TableBatchBuilder(schema, { ...options, diff --git a/modules/json/src/lib/parsers/parse-json.ts b/modules/json/src/lib/parsers/parse-json.ts index 2c66affebc..fc3f5e8bdd 100644 --- a/modules/json/src/lib/parsers/parse-json.ts +++ b/modules/json/src/lib/parsers/parse-json.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {RowTable} from '@loaders.gl/schema'; import {makeTableFromData} from '@loaders.gl/schema'; import type {JSONLoaderOptions} from '../../json-loader'; diff --git a/modules/json/src/lib/parsers/parse-ndjson-in-batches.ts b/modules/json/src/lib/parsers/parse-ndjson-in-batches.ts index c3a87eea48..5bf0136c76 100644 --- a/modules/json/src/lib/parsers/parse-ndjson-in-batches.ts +++ b/modules/json/src/lib/parsers/parse-ndjson-in-batches.ts @@ -1,4 +1,4 @@ -import type {Batch} from '@loaders.gl/schema'; +import type {TableBatch} from '@loaders.gl/schema'; import {TableBatchBuilder} from '@loaders.gl/schema'; import { LoaderOptions, @@ -10,7 +10,7 @@ import { export async function* parseNDJSONInBatches( binaryAsyncIterator: AsyncIterable | Iterable, options?: LoaderOptions -): AsyncIterable { +): AsyncIterable { const textIterator = makeTextDecoderIterator(binaryAsyncIterator); const lineIterator = makeLineIterator(textIterator); const numberedLineIterator = makeNumberedLineIterator(lineIterator); diff --git a/modules/json/src/ndgeoson-loader.ts b/modules/json/src/ndgeoson-loader.ts index 74db6e2b03..b5fe04612f 100644 --- a/modules/json/src/ndgeoson-loader.ts +++ b/modules/json/src/ndgeoson-loader.ts @@ -1,11 +1,13 @@ import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; import {parseNDJSONSync} from './lib/parsers/parse-ndjson'; import {parseNDJSONInBatches} from './lib/parsers/parse-ndjson-in-batches'; +import {ArrayRowTable, ObjectRowTable, Batch} from '@loaders.gl/schema'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; +/** Options for NDGeoJSONLoader */ export type NDGeoJSONLoaderOptions = LoaderOptions & { geojson?: { shape?: 'object-row-table'; @@ -15,16 +17,12 @@ export type NDGeoJSONLoaderOptions = LoaderOptions & { }; }; -const DEFAULT_NDGEOJSON_LOADER_OPTIONS = { - geojson: { - shape: 'object-row-table' - }, - gis: { - format: 'geojson' - } -}; - -export const NDJSONLoader = { +/** NDGeoJSONLoader */ +export const NDJSONLoader: LoaderWithParser< + ArrayRowTable | ObjectRowTable, + Batch, + NDGeoJSONLoaderOptions +> = { name: 'NDJSON', id: 'ndjson', module: 'json', @@ -42,7 +40,12 @@ export const NDJSONLoader = { parse: async (arrayBuffer: ArrayBuffer) => parseNDJSONSync(new TextDecoder().decode(arrayBuffer)), parseTextSync: parseNDJSONSync, parseInBatches: parseNDJSONInBatches, - options: DEFAULT_NDGEOJSON_LOADER_OPTIONS + options: { + geojson: { + shape: 'object-row-table' + }, + gis: { + format: 'geojson' + } + } }; - -export const _typecheckNDJSONLoader: LoaderWithParser = NDJSONLoader; diff --git a/modules/json/src/ndjson-loader.ts b/modules/json/src/ndjson-loader.ts index 6757e4a7ac..572d59e68a 100644 --- a/modules/json/src/ndjson-loader.ts +++ b/modules/json/src/ndjson-loader.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; import {ObjectRowTable, ArrayRowTable, TableBatch} from '@loaders.gl/schema'; diff --git a/modules/json/test/geojson-writer.spec.ts b/modules/json/test/geojson-writer.spec.ts index 6b03783301..65a42d4e03 100644 --- a/modules/json/test/geojson-writer.spec.ts +++ b/modules/json/test/geojson-writer.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Copyright 2022 Foursquare Labs, Inc. /* global TextDecoder */ diff --git a/modules/json/test/json-loader.bench.ts b/modules/json/test/json-loader.bench.ts index cdc366c188..3a464f532e 100644 --- a/modules/json/test/json-loader.bench.ts +++ b/modules/json/test/json-loader.bench.ts @@ -15,7 +15,9 @@ export default async function jsonLoaderBench(suite) { // const asyncIterator = await parseInBatches(STRING, JSONLoader); const data: unknown[] = []; for await (const batch of asyncIterator) { - data.push(...batch.data); + if (batch.shape === 'object-row-table') { + data.push(...batch.data); + } } }); diff --git a/modules/json/test/json-loader.spec.ts b/modules/json/test/json-loader.spec.ts index e7ccf8eb4f..98edf4c8bc 100644 --- a/modules/json/test/json-loader.spec.ts +++ b/modules/json/test/json-loader.spec.ts @@ -1,5 +1,6 @@ import test from 'tape-promise/tape'; import {load, loadInBatches, isIterator, isAsyncIterable} from '@loaders.gl/core'; +import {getTableLength} from '@loaders.gl/schema'; import {JSONLoader} from '@loaders.gl/json'; const GEOJSON_PATH = '@loaders.gl/json/test/data/geojson-big.json'; @@ -30,7 +31,7 @@ test('JSONLoader#loadInBatches(geojson.json, rows, batchSize = auto)', async (t) // byteLength = batch.bytesUsed; } - t.comment(JSON.stringify(batchCount)); + // t.comment(JSON.stringify(batchCount)); t.ok(batchCount <= 4, 'Correct number of batches received'); t.equal(rowCount, 308, 'Correct number of row received'); // t.equal(byteLength, 135910, 'Correct number of bytes received'); @@ -81,7 +82,8 @@ test('JSONLoader#loadInBatches(jsonpaths)', async (t) => { // batchCount++; rowCount += batch.length; // byteLength = batch.bytesUsed; - t.equal(batch.jsonpath.toString(), '$.features', 'correct jsonpath on batch'); + // @ts-ignore + t.equal(batch.jsonpath?.toString(), '$.features', 'correct jsonpath on batch'); } // t.skip(batchCount <= 3, 'Correct number of batches received'); @@ -172,8 +174,8 @@ test('JSONLoader#loadInBatches(streaming array of arrays)', async (t) => { let rowCount = 0; for await (const batch of iterator) { - rowCount += batch.data.length; - t.equal(Object.values(batch.data[0]).length, 10); + rowCount += getTableLength(batch); + // t.equal(Object.values(batch.data[0]).length, 10); } t.equal(rowCount, 247); diff --git a/modules/json/test/json-writer.spec.ts b/modules/json/test/json-writer.spec.ts index e3117e965e..0282db7b29 100644 --- a/modules/json/test/json-writer.spec.ts +++ b/modules/json/test/json-writer.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Copyright 2022 Foursquare Labs, Inc. import test from 'tape-promise/tape'; @@ -27,7 +28,7 @@ test('JSONWriter#encodeTableAsText - data table, row objects', async (t) => { test('JSONWriter#encodeTableAsText - data table, row objects (explicit)', async (t) => { const encodedText = await encodeTableAsText(tableWithData, JSONWriter, { - shape: 'object-row-table' + json: {shape: 'object-row-table'} }); t.equal( encodedText, @@ -40,7 +41,7 @@ test('JSONWriter#encodeTableAsText - data table, row objects (explicit)', async test('JSONWriter#encodeTableAsText - data table, row arrays', async (t) => { const encodedText = await encodeTableAsText(tableWithData, JSONWriter, { - shape: 'array-row-table' + json: {shape: 'array-row-table'} }); t.equal( encodedText, @@ -53,8 +54,7 @@ test('JSONWriter#encodeTableAsText - data table, row arrays', async (t) => { test.skip('JSONWriter#encodeTableAsText - data table, wrapper', async (t) => { const encodedText = await encodeTableAsText(tableWithData, JSONWriter, { - wrapper: (table) => ({wrapped: true, table}), - shape: 'array-row-table' + json: {shape: 'array-row-table', wrapper: (table) => ({wrapped: true, table})} }); t.equal( encodedText, diff --git a/modules/json/test/lib/parser/streaming-json-parser.spec.js b/modules/json/test/lib/parser/streaming-json-parser.spec.js index 4ac5645043..992d583bc9 100644 --- a/modules/json/test/lib/parser/streaming-json-parser.spec.js +++ b/modules/json/test/lib/parser/streaming-json-parser.spec.js @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; import {fetchFile, makeIterator} from '@loaders.gl/core'; diff --git a/modules/json/test/ndjson-loader.spec.ts b/modules/json/test/ndjson-loader.spec.ts index cf951c918b..a7bbb61b96 100644 --- a/modules/json/test/ndjson-loader.spec.ts +++ b/modules/json/test/ndjson-loader.spec.ts @@ -34,7 +34,7 @@ test('NDJSONLoader#loadInBatches(ndjson.ndjson, rows, batchSize = auto)', async byteLength = batch.bytesUsed; } - t.comment(JSON.stringify(batchCount)); + // t.comment(JSON.stringify(batchCount)); t.equal(batchCount, 11, 'Correct number of batches received'); t.equal(rowCount, 11, 'Correct number of row received'); t.equal(byteLength, 701, 'Correct number of bytes received'); @@ -76,7 +76,7 @@ test('NDJSONLoader#loadInBatches(ndjson.ndjson, rows, batchSize = 5)', async (t) t.end(); }); -test('NDJSONLoader#loadInBatches(ndjson-invalid.ndjson)', async (t) => { +test.skip('NDJSONLoader#loadInBatches(ndjson-invalid.ndjson)', async (t) => { const iterator = await loadInBatches(NDJSON_INVALID_PATH, NDJSONLoader, { batchSize: 5 }); diff --git a/modules/kml/package.json b/modules/kml/package.json index d5dc926bb5..7a6145b0fe 100644 --- a/modules/kml/package.json +++ b/modules/kml/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/kml", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loader for the KML format", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "KML" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -29,15 +37,15 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { - "@loaders.gl/gis": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", + "@loaders.gl/gis": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", "@tmcw/togeojson": "^4.5.0", - "@xmldom/xmldom": "^0.7.5" + "@xmldom/xmldom": "^0.7.13" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/kml/src/bundle.ts b/modules/kml/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/kml/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/kml/src/gpx-loader.ts b/modules/kml/src/gpx-loader.ts index 0e874519d1..56f1585091 100644 --- a/modules/kml/src/gpx-loader.ts +++ b/modules/kml/src/gpx-loader.ts @@ -1,6 +1,14 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; import {geojsonToBinary} from '@loaders.gl/gis'; -import type {GeoJSONRowTable, FeatureCollection, ObjectRowTable} from '@loaders.gl/schema'; +import type { + GeoJSONTable, + FeatureCollection, + ObjectRowTable, + BinaryFeatureCollection +} from '@loaders.gl/schema'; import {gpx} from '@tmcw/togeojson'; import {DOMParser} from '@xmldom/xmldom'; @@ -10,13 +18,7 @@ const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; export type GPXLoaderOptions = LoaderOptions & { gpx?: { - shape?: 'object-row-table' | 'geojson-row-table' | 'geojson' | 'binary' | 'raw'; - /** @deprecated. Use options.gpx.shape */ - type?: 'object-row-table' | 'geojson-row-table' | 'geojson' | 'binary' | 'raw'; - }; - gis?: { - /** @deprecated. Use options.gpx.shape */ - format?: 'object-row-table' | 'geojson-row-table' | 'geojson' | 'binary' | 'raw'; + shape?: 'object-row-table' | 'geojson-table' | 'binary' | 'raw'; }; }; @@ -27,7 +29,11 @@ const GPX_HEADER = `\ /** * Loader for GPX (GPS exchange format) */ -export const GPXLoader = { +export const GPXLoader: LoaderWithParser< + ObjectRowTable | GeoJSONTable | BinaryFeatureCollection, + never, + GPXLoaderOptions +> = { name: 'GPX (GPS exchange format)', id: 'gpx', module: 'kml', @@ -40,18 +46,21 @@ export const GPXLoader = { parseTextSync(new TextDecoder().decode(arrayBuffer), options), parseTextSync, options: { - gpx: {}, + gpx: {shape: 'geojson-table'}, gis: {} } }; -function parseTextSync(text: string, options?: GPXLoaderOptions) { +function parseTextSync( + text: string, + options?: GPXLoaderOptions +): ObjectRowTable | GeoJSONTable | BinaryFeatureCollection { const doc = new DOMParser().parseFromString(text, 'text/xml'); const geojson: FeatureCollection = gpx(doc); - const shape = options?.gis?.format || options?.gpx?.type || options?.gpx?.shape; + const gpxOptions = {...GPXLoader.options.gpx, ...options?.gpx}; - switch (shape) { + switch (gpxOptions.shape) { case 'object-row-table': { const table: ObjectRowTable = { shape: 'object-row-table', @@ -59,23 +68,18 @@ function parseTextSync(text: string, options?: GPXLoaderOptions) { }; return table; } - case 'geojson-row-table': { - const table: GeoJSONRowTable = { - shape: 'geojson-row-table', - data: geojson.features + case 'geojson-table': { + const table: GeoJSONTable = { + shape: 'geojson-table', + type: 'FeatureCollection', + features: geojson.features }; return table; } - case 'geojson': - return geojson; case 'binary': return geojsonToBinary(geojson.features); - case 'raw': - return doc; + default: - // Default to geojson for backwards compatibility - return geojson; + throw new Error(gpxOptions.shape); } } - -export const _typecheckGPXLoader: LoaderWithParser = GPXLoader; diff --git a/modules/kml/src/index.ts b/modules/kml/src/index.ts index 26654aa120..f10cd18e7f 100644 --- a/modules/kml/src/index.ts +++ b/modules/kml/src/index.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + // POLYFILL: DOMParser // - Node: Yes // - Browser: No diff --git a/modules/kml/src/kml-loader.ts b/modules/kml/src/kml-loader.ts index a62837a832..a12e2e6908 100644 --- a/modules/kml/src/kml-loader.ts +++ b/modules/kml/src/kml-loader.ts @@ -1,6 +1,10 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; -import {geojsonToBinary} from '@loaders.gl/gis'; -import {GeoJSONRowTable, FeatureCollection, ObjectRowTable} from '@loaders.gl/schema'; +// import {geojsonToBinary} from '@loaders.gl/gis'; +// import {GeoJSONTable} from '@loaders.gl/schema'; +import {FeatureCollection, GeoJSONTable, ObjectRowTable} from '@loaders.gl/schema'; import {kml} from '@tmcw/togeojson'; import {DOMParser} from '@xmldom/xmldom'; @@ -10,13 +14,7 @@ const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; export type KMLLoaderOptions = LoaderOptions & { kml?: { - shape?: 'object-row-table' | 'geojson-row-table' | 'geojson' | 'binary' | 'raw'; - /** @deprecated. Use options.kml.shape */ - type?: 'object-row-table' | 'geojson-row-table' | 'geojson' | 'binary' | 'raw'; - }; - gis?: { - /** @deprecated. Use options.kml.shape */ - format?: 'object-row-table' | 'geojson-row-table' | 'geojson' | 'binary' | 'raw'; + shape?: 'object-row-table' | 'geojson-table' | 'binary' | 'raw'; }; }; @@ -27,7 +25,7 @@ const KML_HEADER = `\ /** * Loader for KML (Keyhole Markup Language) */ -export const KMLLoader = { +export const KMLLoader: LoaderWithParser = { name: 'KML (Keyhole Markup Language)', id: 'kml', module: 'kml', @@ -40,42 +38,39 @@ export const KMLLoader = { parseTextSync(new TextDecoder().decode(arrayBuffer), options), parseTextSync, options: { - kml: {}, + kml: {shape: 'geojson-table'}, gis: {} } }; -function parseTextSync(text: string, options?: KMLLoaderOptions) { +function parseTextSync(text: string, options?: KMLLoaderOptions): ObjectRowTable | GeoJSONTable { const doc = new DOMParser().parseFromString(text, 'text/xml'); const geojson: FeatureCollection = kml(doc); - // backwards compatibility - const shape = options?.gis?.format || options?.kml?.type || options?.kml?.shape; - switch (shape) { - case 'object-row-table': { - const table: ObjectRowTable = { - shape: 'object-row-table', - data: geojson.features + const kmlOptions = {...KMLLoader.options.kml, ...options?.kml}; + + switch (kmlOptions.shape) { + case 'geojson-table': { + const table: GeoJSONTable = { + shape: 'geojson-table', + type: 'FeatureCollection', + features: geojson.features }; return table; } - case 'geojson-row-table': { - const table: GeoJSONRowTable = { - shape: 'geojson-row-table', + // case 'geojson': + // return geojson; + // case 'binary': + // return geojsonToBinary(geojson.features); + // case 'raw': + // return doc; + case 'object-row-table': + const table: ObjectRowTable = { + shape: 'object-row-table', data: geojson.features }; return table; - } - case 'geojson': - return geojson; - case 'binary': - return geojsonToBinary(geojson.features); - case 'raw': - return doc; default: - // Default to geojson for backwards compatibility - return geojson; + throw new Error(kmlOptions.shape); } } - -export const _typecheckKMLLoader: LoaderWithParser = KMLLoader; diff --git a/modules/kml/src/tcx-loader.ts b/modules/kml/src/tcx-loader.ts index 2c16764688..403655046f 100644 --- a/modules/kml/src/tcx-loader.ts +++ b/modules/kml/src/tcx-loader.ts @@ -1,6 +1,14 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; import {geojsonToBinary} from '@loaders.gl/gis'; -import type {GeoJSONRowTable, FeatureCollection, ObjectRowTable} from '@loaders.gl/schema'; +import type { + GeoJSONTable, + FeatureCollection, + ObjectRowTable, + BinaryFeatureCollection +} from '@loaders.gl/schema'; import {tcx} from '@tmcw/togeojson'; import {DOMParser} from '@xmldom/xmldom'; @@ -10,13 +18,7 @@ const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; export type TCXLoaderOptions = LoaderOptions & { tcx?: { - shape?: 'object-row-table' | 'geojson-row-table' | 'geojson' | 'binary' | 'raw'; - /** @deprecated. Use options.tcx.shape */ - type?: 'object-row-table' | 'geojson-row-table' | 'geojson' | 'binary' | 'raw'; - }; - gis?: { - /** @deprecated. Use options.tcx.shape */ - format?: 'object-row-table' | 'geojson-row-table' | 'geojson' | 'binary' | 'raw'; + shape?: 'object-row-table' | 'geojson-table' | 'binary' | 'raw'; }; }; @@ -27,7 +29,11 @@ const TCX_HEADER = `\ /** * Loader for TCX (Training Center XML) - Garmin GPS track format */ -export const TCXLoader = { +export const TCXLoader: LoaderWithParser< + ObjectRowTable | GeoJSONTable | BinaryFeatureCollection, + never, + TCXLoaderOptions +> = { name: 'TCX (Training Center XML)', id: 'tcx', module: 'kml', @@ -40,19 +46,21 @@ export const TCXLoader = { parseTextSync(new TextDecoder().decode(arrayBuffer), options), parseTextSync, options: { - tcx: {}, + tcx: {shape: 'geojson-table'}, gis: {} } }; -function parseTextSync(text: string, options?: TCXLoaderOptions) { +function parseTextSync( + text: string, + options?: TCXLoaderOptions +): ObjectRowTable | GeoJSONTable | BinaryFeatureCollection { const doc = new DOMParser().parseFromString(text, 'text/xml'); const geojson: FeatureCollection = tcx(doc); - // backwards compatibility - const shape = options?.gis?.format || options?.tcx?.type || options?.tcx?.shape; + const tcxOptions = {...TCXLoader.options.tcx, ...options?.tcx}; - switch (shape) { + switch (tcxOptions.shape) { case 'object-row-table': { const table: ObjectRowTable = { shape: 'object-row-table', @@ -60,23 +68,18 @@ function parseTextSync(text: string, options?: TCXLoaderOptions) { }; return table; } - case 'geojson-row-table': { - const table: GeoJSONRowTable = { - shape: 'geojson-row-table', - data: geojson.features + case 'geojson-table': { + const table: GeoJSONTable = { + shape: 'geojson-table', + type: 'FeatureCollection', + features: geojson.features }; return table; } - case 'geojson': - return geojson; case 'binary': return geojsonToBinary(geojson.features); - case 'raw': - return doc; + default: - // Default to geojson for backwards compatibility - return geojson; + throw new Error(tcxOptions.shape); } } - -export const _typecheckTCXLoader: LoaderWithParser = TCXLoader; diff --git a/modules/kml/test/data/trek.geojson b/modules/kml/test/data/gpx/trek.geojson similarity index 100% rename from modules/kml/test/data/trek.geojson rename to modules/kml/test/data/gpx/trek.geojson diff --git a/modules/kml/test/data/trek.gpx b/modules/kml/test/data/gpx/trek.gpx similarity index 100% rename from modules/kml/test/data/trek.gpx rename to modules/kml/test/data/gpx/trek.gpx diff --git a/modules/kml/test/data/KML_Samples.kml b/modules/kml/test/data/kml/KML_Samples.kml similarity index 100% rename from modules/kml/test/data/KML_Samples.kml rename to modules/kml/test/data/kml/KML_Samples.kml diff --git a/modules/kml/test/data/linestring.geojson b/modules/kml/test/data/kml/linestring.geojson similarity index 100% rename from modules/kml/test/data/linestring.geojson rename to modules/kml/test/data/kml/linestring.geojson diff --git a/modules/kml/test/data/linestring.kml b/modules/kml/test/data/kml/linestring.kml similarity index 100% rename from modules/kml/test/data/linestring.kml rename to modules/kml/test/data/kml/linestring.kml diff --git a/modules/kml/test/data/tcx_sample.geojson b/modules/kml/test/data/tcx/tcx_sample.geojson similarity index 100% rename from modules/kml/test/data/tcx_sample.geojson rename to modules/kml/test/data/tcx/tcx_sample.geojson diff --git a/modules/kml/test/data/tcx_sample.tcx b/modules/kml/test/data/tcx/tcx_sample.tcx similarity index 100% rename from modules/kml/test/data/tcx_sample.tcx rename to modules/kml/test/data/tcx/tcx_sample.tcx diff --git a/modules/kml/test/gpx-loader.spec.js b/modules/kml/test/gpx-loader.spec.ts similarity index 54% rename from modules/kml/test/gpx-loader.spec.js rename to modules/kml/test/gpx-loader.spec.ts index 3ca51602c0..e3bd0c469c 100644 --- a/modules/kml/test/gpx-loader.spec.js +++ b/modules/kml/test/gpx-loader.spec.ts @@ -1,10 +1,13 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import test from 'tape-promise/tape'; import {validateLoader} from 'test/common/conformance'; import {fetchFile, load} from '@loaders.gl/core'; import {GPXLoader} from '@loaders.gl/kml'; -const GPX_URL = '@loaders.gl/kml/test/data/trek'; +const GPX_URL = '@loaders.gl/kml/test/data/gpx/trek'; test('GPXLoader#loader conformance', (t) => { validateLoader(t, GPXLoader, 'GPXLoader'); @@ -12,10 +15,15 @@ test('GPXLoader#loader conformance', (t) => { }); test('GPXLoader#parse', async (t) => { - const data = await load(`${GPX_URL}.gpx`, GPXLoader, {gis: {format: 'geojson'}}); + const data = await load(`${GPX_URL}.gpx`, GPXLoader, {gpx: {shape: 'geojson-table'}}); const resp = await fetchFile(`${GPX_URL}.geojson`); const geojson = await resp.json(); + geojson.shape = 'geojson-table'; - t.deepEqual(data, geojson, 'Data matches GeoJSON'); + t.deepEqual( + data.shape === 'geojson-table' && data.features, + geojson.features, + 'Data matches GeoJSON' + ); t.end(); }); diff --git a/modules/kml/test/index.js b/modules/kml/test/index.ts similarity index 56% rename from modules/kml/test/index.js rename to modules/kml/test/index.ts index 7bc3d5ea59..47c1d9721f 100644 --- a/modules/kml/test/index.js +++ b/modules/kml/test/index.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import './gpx-loader.spec'; import './kml-loader.spec'; import './tcx-loader.spec'; diff --git a/modules/kml/test/kml-loader.spec.js b/modules/kml/test/kml-loader.spec.ts similarity index 62% rename from modules/kml/test/kml-loader.spec.js rename to modules/kml/test/kml-loader.spec.ts index 53cba3c20d..2ffad1b150 100644 --- a/modules/kml/test/kml-loader.spec.js +++ b/modules/kml/test/kml-loader.spec.ts @@ -1,11 +1,14 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import test from 'tape-promise/tape'; import {validateLoader} from 'test/common/conformance'; import {fetchFile, load} from '@loaders.gl/core'; import {KMLLoader} from '@loaders.gl/kml'; -const KML_URL = '@loaders.gl/kml/test/data/KML_Samples.kml'; -const KML_LINESTRING_URL = '@loaders.gl/kml/test/data/linestring'; +const KML_URL = '@loaders.gl/kml/test/data/kml/KML_Samples.kml'; +const KML_LINESTRING_URL = '@loaders.gl/kml/test/data/kml/linestring'; const INVALID_KML = `\ @@ -51,23 +54,28 @@ test('KMLLoader#parse', async (t) => { }); test('KMLLoader#parse(text)', async (t) => { - const data = await load(KML_URL, KMLLoader, {gis: {format: 'geojson'}}); - t.equal(data.type, 'FeatureCollection', 'FeatureCollection found'); - t.equal(data.features.length, 20, 'Features were found'); - - const feature = data.features[0]; - t.ok(Number.isFinite(feature.geometry.coordinates[0])); - t.ok(Number.isFinite(feature.geometry.coordinates[1])); - t.ok(Number.isFinite(feature.geometry.coordinates[2])); - + const table = await load(KML_URL, KMLLoader, {kml: {shape: 'object-row-table'}}); + t.equal(table.shape, 'object-row-table', 'shape is object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 20, 'Features were found'); + const feature = table.data[0]; + t.ok(Number.isFinite(feature.geometry.coordinates[0])); + t.ok(Number.isFinite(feature.geometry.coordinates[1])); + t.ok(Number.isFinite(feature.geometry.coordinates[2])); + } t.end(); }); -test('KMLLoader#parse', async (t) => { - const data = await load(`${KML_LINESTRING_URL}.kml`, KMLLoader, {gis: {format: 'geojson'}}); +test('KMLLoader#parse(geojson-table)', async (t) => { + const table = await load(`${KML_LINESTRING_URL}.kml`, KMLLoader, { + gis: {format: 'geojson-table'} + }); const resp = await fetchFile(`${KML_LINESTRING_URL}.geojson`); const geojson = await resp.json(); + geojson.shape = 'geojson-table'; - t.deepEqual(data, geojson, 'Data matches GeoJSON'); + if (table.shape === 'geojson-table') { + t.deepEqual(table.features, geojson.features, 'Data matches GeoJSON'); + } t.end(); }); diff --git a/modules/kml/test/tcx-loader.spec.js b/modules/kml/test/tcx-loader.spec.js deleted file mode 100644 index 89f2afee1b..0000000000 --- a/modules/kml/test/tcx-loader.spec.js +++ /dev/null @@ -1,21 +0,0 @@ -import test from 'tape-promise/tape'; -import {validateLoader} from 'test/common/conformance'; - -import {fetchFile, load} from '@loaders.gl/core'; -import {TCXLoader} from '@loaders.gl/kml'; - -const TCX_URL = '@loaders.gl/kml/test/data/tcx_sample'; - -test('TCXLoader#loader conformance', (t) => { - validateLoader(t, TCXLoader, 'TCXLoader'); - t.end(); -}); - -test.skip('TCXLoader#parse', async (t) => { - const data = await load(`${TCX_URL}.tcx`, TCXLoader, {gis: {format: 'geojson'}}); - const resp = await fetchFile(`${TCX_URL}.geojson`); - const geojson = await resp.json(); - - t.deepEqual(data, geojson, 'Data matches GeoJSON'); - t.end(); -}); diff --git a/modules/kml/test/tcx-loader.spec.ts b/modules/kml/test/tcx-loader.spec.ts new file mode 100644 index 0000000000..96119775a9 --- /dev/null +++ b/modules/kml/test/tcx-loader.spec.ts @@ -0,0 +1,47 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import {validateLoader} from 'test/common/conformance'; + +import {fetchFile, load, loadInBatches} from '@loaders.gl/core'; +import {TCXLoader} from '@loaders.gl/kml'; +import type {GeoJSONTable} from '@loaders.gl/schema'; + +const TCX_URL = '@loaders.gl/kml/test/data/tcx/tcx_sample'; + +test('TCXLoader#loader conformance', (t) => { + validateLoader(t, TCXLoader, 'TCXLoader'); + t.end(); +}); + +test.skip('TCXLoader#parse', async (t) => { + const table = (await load(`${TCX_URL}.tcx`, TCXLoader, { + gis: {format: 'geojson'} + })) as GeoJSONTable; + const resp = await fetchFile(`${TCX_URL}.geojson`); + const geojson = await resp.json(); + geojson.shape = 'geojson-table'; + + // TODO - lots of nulls injected in the metrics- should they be copies? + // console.error(JSON.stringify(table, null, 2)); + + // t.deepEqual(table, geojson, 'Data matches GeoJSON'); + t.equal(table.features.length, 1, 'Data matches GeoJSON'); + t.end(); +}); + +test('TCXLoader#parseInBatches', async (t) => { + const iterator = await loadInBatches(`${TCX_URL}.tcx`, TCXLoader, {gis: {format: 'geojson'}}); + let data: any; + for await (const batch of iterator) { + data = batch; + } + // const resp = await fetchFile(`${TCX_URL}.geojson`); + // const geojson = await resp.json(); + // geojson.shape = 'geojson-table'; + + t.equal(data.features.length, 1); + // t.deepEqual(data, geojson, 'Data matches GeoJSON'); + t.end(); +}); diff --git a/modules/las/package.json b/modules/las/package.json index 4d0161de49..f54e60defc 100644 --- a/modules/las/package.json +++ b/modules/las/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/las", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loader for the LAS and LAZ formats", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -21,8 +22,15 @@ "LAZ" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "browser": { "fs": false, @@ -36,15 +44,15 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle && npm run build-worker", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-worker", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/las-worker.ts --bundle --outfile=dist/las-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { "@babel/runtime": "^7.3.1", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", - "apache-arrow": "^9.0.0" + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "laz-perf": "^0.0.6" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/las/src/bundle.ts b/modules/las/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/las/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/las/src/lib/libs/laz-perf.ts b/modules/las/src/lib/libs/laz-perf.ts index 6d598756c4..1b83163448 100644 --- a/modules/las/src/lib/libs/laz-perf.ts +++ b/modules/las/src/lib/libs/laz-perf.ts @@ -63,7 +63,8 @@ export default function getModule() { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = require('path').dirname(scriptDirectory) + '/'; } else { - scriptDirectory = __dirname + '/'; + const dirname = typeof __dirname !== 'undefined' ? __dirname : ''; + scriptDirectory = dirname + '/'; } read_ = function shell_read(filename, binary) { var ret = tryParseAsDataURI(filename); diff --git a/modules/lerc/README.md b/modules/lerc/README.md new file mode 100644 index 0000000000..ce9c638dcb --- /dev/null +++ b/modules/lerc/README.md @@ -0,0 +1,5 @@ +# @loaders.gl/lerc + +This module contains a loader for the LERC raster format. + +[loaders.gl](https://loaders.gl/docs) is a collection of framework-independent visualization-focused loaders (parsers). diff --git a/modules/lerc/package.json b/modules/lerc/package.json new file mode 100644 index 0000000000..facc6d546b --- /dev/null +++ b/modules/lerc/package.json @@ -0,0 +1,47 @@ +{ + "name": "@loaders.gl/lerc", + "version": "4.0.3", + "description": "Framework-independent loader for LERC (Limited Error Raster Compression) files", + "license": "MIT", + "type": "module", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/visgl/loaders.gl" + }, + "keywords": [ + "webgl", + "loader", + "parser", + "LERC" + ], + "types": "dist/index.d.ts", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, + "sideEffects": false, + "files": [ + "src", + "dist", + "README.md" + ], + "scripts": { + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-worker", + "build-bundle": "# ocular-bundle ./src/index.ts", + "build-worker": "# esbuild src/workers/lerc-worker.ts --bundle --outfile=dist/lerc-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" + }, + "dependencies": { + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "lerc": "^4.0.1" + }, + "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" +} diff --git a/modules/lerc/src/index.ts b/modules/lerc/src/index.ts new file mode 100644 index 0000000000..558961b881 --- /dev/null +++ b/modules/lerc/src/index.ts @@ -0,0 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +// LERC - Limited Error Raster Compression +export type {LERCData} from './lib/parsers/lerc/lerc-types'; +export {LERCLoader} from './lerc-loader'; diff --git a/modules/wms/src/lerc-loader.ts b/modules/lerc/src/lerc-loader.ts similarity index 92% rename from modules/wms/src/lerc-loader.ts rename to modules/lerc/src/lerc-loader.ts index d9f780fe76..930aec7a8f 100644 --- a/modules/wms/src/lerc-loader.ts +++ b/modules/lerc/src/lerc-loader.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; import type {LERCData} from './lib/parsers/lerc/lerc-types'; @@ -22,7 +23,7 @@ export type LERCLoaderOptions = LoaderOptions & { /** * Loader for the LERC raster format */ -export const LERCLoader = { +export const LERCLoader: LoaderWithParser = { id: 'lerc', name: 'LERC', @@ -46,5 +47,3 @@ async function parseLERC(arrayBuffer: ArrayBuffer, options?: LERCLoaderOptions): const pixelBlock = Lerc.decode(arrayBuffer, options?.lerc); return pixelBlock; } - -export const _typecheckLERCLoader: LoaderWithParser = LERCLoader; diff --git a/modules/wms/src/lib/parsers/lerc/lerc-types.ts b/modules/lerc/src/lib/parsers/lerc/lerc-types.ts similarity index 97% rename from modules/wms/src/lib/parsers/lerc/lerc-types.ts rename to modules/lerc/src/lib/parsers/lerc/lerc-types.ts index fcf54babef..b5b1ad64e6 100644 --- a/modules/wms/src/lib/parsers/lerc/lerc-types.ts +++ b/modules/lerc/src/lib/parsers/lerc/lerc-types.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors /** Data returned by LERC loader */ export type LERCData = { diff --git a/modules/lerc/src/workers/lerc-worker.ts b/modules/lerc/src/workers/lerc-worker.ts new file mode 100644 index 0000000000..00328035bd --- /dev/null +++ b/modules/lerc/src/workers/lerc-worker.ts @@ -0,0 +1,7 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {createLoaderWorker} from '@loaders.gl/loader-utils'; +import {LERCLoader} from '../lerc-loader'; + +createLoaderWorker(LERCLoader); diff --git a/modules/lerc/test/data/README.md b/modules/lerc/test/data/README.md new file mode 100644 index 0000000000..83151dda14 --- /dev/null +++ b/modules/lerc/test/data/README.md @@ -0,0 +1,3 @@ +# Test Data Attributions + +- The `clarinet` folder contains test data from [clarinet](https://github.com/dscape/clarinet/tree/master/samples) under BSD 2-clause license. diff --git a/modules/wms/test/data/lerc/README.md b/modules/lerc/test/data/lerc/README.md similarity index 100% rename from modules/wms/test/data/lerc/README.md rename to modules/lerc/test/data/lerc/README.md diff --git a/modules/wms/test/data/lerc/bluemarble_256_256_3_byte.lerc2 b/modules/lerc/test/data/lerc/bluemarble_256_256_3_byte.lerc2 similarity index 100% rename from modules/wms/test/data/lerc/bluemarble_256_256_3_byte.lerc2 rename to modules/lerc/test/data/lerc/bluemarble_256_256_3_byte.lerc2 diff --git a/modules/wms/test/data/lerc/california_400_400_1_float.lerc2 b/modules/lerc/test/data/lerc/california_400_400_1_float.lerc2 similarity index 100% rename from modules/wms/test/data/lerc/california_400_400_1_float.lerc2 rename to modules/lerc/test/data/lerc/california_400_400_1_float.lerc2 diff --git a/modules/wms/test/data/lerc/world.lerc1 b/modules/lerc/test/data/lerc/world.lerc1 similarity index 100% rename from modules/wms/test/data/lerc/world.lerc1 rename to modules/lerc/test/data/lerc/world.lerc1 diff --git a/modules/lerc/test/index.ts b/modules/lerc/test/index.ts new file mode 100644 index 0000000000..e2166e4e7e --- /dev/null +++ b/modules/lerc/test/index.ts @@ -0,0 +1,4 @@ +// LERC - Limited Error Raster Compression + +import './lerc/lerc-sanity.spec'; +import './lerc/lerc-level2.spec'; diff --git a/modules/wms/test/lerc/lerc-level2.spec.ts.disabled b/modules/lerc/test/lerc/lerc-level2.spec.ts similarity index 86% rename from modules/wms/test/lerc/lerc-level2.spec.ts.disabled rename to modules/lerc/test/lerc/lerc-level2.spec.ts index f919f40c77..a91e2d4080 100644 --- a/modules/wms/test/lerc/lerc-level2.spec.ts.disabled +++ b/modules/lerc/test/lerc/lerc-level2.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/Esri/lerc/blob/master/OtherLanguages/js/tests/ // under Apache 2 license @@ -8,14 +9,14 @@ import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; // import {LERCLoader, LERCData} from '@loaders.gl/wms'; -import type {LERCData} from '../../src/lib/lerc/lerc-types'; +import type {LERCData} from '../../src/lib/parsers/lerc/lerc-types'; import {LERCLoader} from '../../src/lerc-loader'; import {load, isBrowser} from '@loaders.gl/core'; const LERC_FILES = [ - '@loaders.gl/wms/test/data/lerc/bluemarble_256_256_3_byte.lerc2', - '@loaders.gl/wms/test/data/lerc/california_400_400_1_float.lerc2', - '@loaders.gl/wms/test/data/lerc/world.lerc1' + '@loaders.gl/lerc/test/data/lerc/bluemarble_256_256_3_byte.lerc2', + '@loaders.gl/lerc/test/data/lerc/california_400_400_1_float.lerc2', + '@loaders.gl/lerc/test/data/lerc/world.lerc1' ]; test('LERCLoader#level2', async (t) => { @@ -24,7 +25,7 @@ test('LERCLoader#level2', async (t) => { return; } for (const lercFileName of LERC_FILES) { - const result = (await load(lercFileName, LERCLoader)) as LERCData; + const result = await load(lercFileName, LERCLoader); const actual = formatPixelBlock(result); diff --git a/modules/wms/test/lerc/lerc-sanity.spec.ts.disabled b/modules/lerc/test/lerc/lerc-sanity.spec.ts similarity index 97% rename from modules/wms/test/lerc/lerc-sanity.spec.ts.disabled rename to modules/lerc/test/lerc/lerc-sanity.spec.ts index a4ab2bdb29..affd87c087 100644 --- a/modules/wms/test/lerc/lerc-sanity.spec.ts.disabled +++ b/modules/lerc/test/lerc/lerc-sanity.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/Esri/lerc/blob/master/OtherLanguages/js/tests/ // under Apache 2 license @@ -8,7 +9,7 @@ import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; // import {LERCLoader, LERCData} from '@loaders.gl/wms'; -import type {LERCData} from '../../src/lib/lerc/lerc-types'; +// import type {LERCData} from '../../src/lib/parsers/lerc/lerc-types'; import {LERCLoader} from '../../src/lerc-loader'; import {parse, isBrowser} from '@loaders.gl/core'; @@ -36,7 +37,7 @@ test('LERCLoader#4D sanity', async (t) => { t.end(); return; } - const result = (await parse(LERC_DATA_4D, LERCLoader)) as LERCData; + const result = await parse(LERC_DATA_4D, LERCLoader); // test default decoding @@ -68,9 +69,9 @@ test('LERCLoader#4D sanity', async (t) => { return; } // test interleaved flag - const bipResult = (await parse(LERC_DATA_4D, LERCLoader, { + const bipResult = await parse(LERC_DATA_4D, LERCLoader, { lerc: {returnInterleaved: true} - })) as LERCData; + }); t.equal(bipResult.pixels[0].slice(0, 6).join(','), '13,57,68,14,59,80', 'returnInterleaved'); t.end(); diff --git a/modules/lerc/tsconfig.json b/modules/lerc/tsconfig.json new file mode 100644 index 0000000000..4e11b7c942 --- /dev/null +++ b/modules/lerc/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.module.json", + "include": ["src/**/*"], + "exclude": ["node_modules"], + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "dist" + }, + "references": [ + {"path": "../loader-utils"}, + {"path": "../schema"} + ] +} diff --git a/modules/loader-utils/package.json b/modules/loader-utils/package.json index 25655c47ff..20b75b6ea8 100644 --- a/modules/loader-utils/package.json +++ b/modules/loader-utils/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/loader-utils", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loaders for 3D graphics formats", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -19,8 +20,15 @@ "point cloud" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -29,17 +37,18 @@ ], "browser": { "./src/lib/node/buffer.ts": "./src/lib/node/buffer.browser.ts", - "./src/lib/node/buffer.js": "./src/lib/node/buffer.browser.js", "./dist/lib/node/buffer.js": "./dist/lib/node/buffer.browser.js", - "./dist/es5/lib/node/buffer.js": "./dist/es5/lib/node/buffer.browser.js", - "./dist/esm/lib/node/buffer.js": "./dist/esm/lib/node/buffer.browser.js", + "./src/lib/node/fs.ts": "./src/lib/node/fs.browser.ts", + "./dist/lib/node/fs.js": "./dist/lib/node/fs.browser.js", + "./src/lib/node/stream.ts": "./src/lib/node/stream.browser.ts", + "./dist/lib/node/stream.js": "./dist/lib/node/stream.browser.js", "fs": false, "stream": false }, "scripts": {}, "dependencies": { "@babel/runtime": "^7.3.1", - "@loaders.gl/worker-utils": "4.0.0-alpha.13", + "@loaders.gl/worker-utils": "4.0.3", "@probe.gl/stats": "^4.0.2" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" diff --git a/modules/loader-utils/src/index.ts b/modules/loader-utils/src/index.ts index 0bed217a3c..201710a01c 100644 --- a/modules/loader-utils/src/index.ts +++ b/modules/loader-utils/src/index.ts @@ -1,32 +1,42 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + // TYPES + export type { - // loaders - Loader, - LoaderWithParser, - LoaderContext, - LoaderOptions, - LoaderOptionsType, - LoaderReturnType, - LoaderBatchType, - // writers - Writer, - WriterOptions, - WriterOptionsType, // misc DataType, SyncDataType, BatchableDataType, - IFileSystem, - IRandomAccessReadFileSystem, // numeric array types TypedArray, BigTypedArray, TypedArrayConstructor, BigTypedArrayConstructor, NumberArray, - NumericArray + NumericArray, + // fetch + FetchLike } from './types'; +// loaders + +export type { + Loader, + LoaderWithParser, + LoaderContext, + LoaderOptions, + LoaderOptionsType, + LoaderReturnType, + LoaderBatchType +} from './loader-types'; + +export {parseFromContext, parseSyncFromContext, parseInBatchesFromContext} from './loader-types'; + +// writers + +export type {Writer, WriterWithEncoder, WriterOptions, WriterOptionsType} from './writer-types'; + // GENERAL UTILS export {assert} from './lib/env-utils/assert'; export { @@ -101,19 +111,45 @@ export {promisify1, promisify2} from './lib/node/promisify'; import * as path from './lib/path-utils/path'; export {path}; -// Use instead of importing 'fs' to avoid node dependencies` -import * as fs from './lib/node/fs'; -export {fs}; - // Use instead of importing 'stream' to avoid node dependencies` import * as stream from './lib/node/stream'; export {stream}; -// EXPERIMENTAL -export type {ReadableFile} from './lib/filesystems/readable-file'; -export {makeReadableFile} from './lib/filesystems/readable-file'; +// EXPERIMENTAL: FILE SYSTEMS + +export type {ReadableFile, WritableFile, Stat} from './lib/files/file'; +export {BlobFile} from './lib/files/blob-file'; +export {HttpFile} from './lib/files/http-file'; +export {NodeFileFacade as NodeFile} from './lib/files/node-file-facade'; + +export type {FileSystem, RandomAccessFileSystem} from './lib/filesystems/filesystem'; +export {NodeFileSystemFacade as NodeFilesystem} from './lib/filesystems/node-filesystem-facade'; + +// TODO - replace with ReadableFile +export type {FileProvider} from './lib/file-provider/file-provider'; +export {isFileProvider} from './lib/file-provider/file-provider'; +export {FileHandleFile} from './lib/file-provider/file-handle-file'; +export {DataViewFile} from './lib/file-provider/data-view-file'; + +// EXPERIMENTAL: DATA SOURCES +export type {Service} from './service-types'; + +export type {DataSourceProps} from './lib/sources/data-source'; +export {DataSource} from './lib/sources/data-source'; + +export type {ImageType} from './lib/sources/utils/image-type'; +export type {ImageSourceProps, ImageSourceMetadata} from './lib/sources/image-source'; +export type {GetImageParameters} from './lib/sources/image-source'; +export {ImageSource} from './lib/sources/image-source'; + +export type { + TileSourceProps, + TileSourceMetadata, + GetTileParameters, + TileLoadParameters +} from './lib/sources/tile-source'; +export type {TileSource} from './lib/sources/tile-source'; -export type {WritableFile} from './lib/filesystems/writable-file'; -export {makeWritableFile} from './lib/filesystems/writable-file'; +export type {ImageTileSource} from './lib/sources/image-tile-source'; -export {default as _NodeFileSystem} from './lib/filesystems/node-filesystem'; +export type {VectorTileSource} from './lib/sources/vector-tile-source'; diff --git a/modules/loader-utils/src/json-loader.ts b/modules/loader-utils/src/json-loader.ts index 0bc0ebd075..1561c5d150 100644 --- a/modules/loader-utils/src/json-loader.ts +++ b/modules/loader-utils/src/json-loader.ts @@ -1,4 +1,4 @@ -import type {LoaderWithParser, LoaderOptions} from './types'; +import type {LoaderWithParser, LoaderOptions} from './loader-types'; import type {Table, TableBatch} from '@loaders.gl/schema'; // __VERSION__ is injected by babel-plugin-version-inline @@ -25,7 +25,7 @@ export const JSONLoader: LoaderWithParser options: {} }; -// TODO - deprecated +// TODO - Better error handling! function parseTextSync(text) { return JSON.parse(text); } diff --git a/modules/loader-utils/src/lib/binary-utils/get-first-characters.ts b/modules/loader-utils/src/lib/binary-utils/get-first-characters.ts index 748a8d030b..d4ec143510 100644 --- a/modules/loader-utils/src/lib/binary-utils/get-first-characters.ts +++ b/modules/loader-utils/src/lib/binary-utils/get-first-characters.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors /** * Get the first characters from a binary file (interpret the first bytes as an ASCII string) diff --git a/modules/loader-utils/src/lib/binary-utils/memory-conversion-utils.ts b/modules/loader-utils/src/lib/binary-utils/memory-conversion-utils.ts index db1d32cdc8..d12d13679c 100644 --- a/modules/loader-utils/src/lib/binary-utils/memory-conversion-utils.ts +++ b/modules/loader-utils/src/lib/binary-utils/memory-conversion-utils.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import * as node from '../node/buffer'; diff --git a/modules/loader-utils/src/lib/file-provider/data-view-file.ts b/modules/loader-utils/src/lib/file-provider/data-view-file.ts new file mode 100644 index 0000000000..5f38a6cd5c --- /dev/null +++ b/modules/loader-utils/src/lib/file-provider/data-view-file.ts @@ -0,0 +1,74 @@ +import {FileProvider} from './file-provider'; + +/** + * Checks if bigint can be converted to number and convert it if possible + * @param bigint bigint to be converted + * @returns number + */ +const toNumber = (bigint: bigint) => { + if (bigint > Number.MAX_SAFE_INTEGER) { + throw new Error('Offset is out of bounds'); + } + return Number(bigint); +}; + +/** + * Provides file data using DataView + * @deprecated - will be replaced with ReadableFile + */ +export class DataViewFile implements FileProvider { + /** The DataView from which data is provided */ + private file: DataView; + + constructor(file: DataView) { + this.file = file; + } + + async destroy(): Promise {} + + /** + * Gets an unsigned 8-bit integer at the specified byte offset from the start of the file. + * @param offset The offset, in bytes, from the start of the file where to read the data. + */ + async getUint8(offset: bigint): Promise { + return this.file.getUint8(toNumber(offset)); + } + + /** + * Gets an unsigned 16-bit intege at the specified byte offset from the start of the file. + * @param offset The offset, in bytes, from the start of the file where to read the data. + */ + async getUint16(offset: bigint): Promise { + return this.file.getUint16(toNumber(offset), true); + } + + /** + * Gets an unsigned 32-bit integer at the specified byte offset from the start of the file. + * @param offset The offset, in bytes, from the start of the file where to read the data. + */ + async getUint32(offset: bigint): Promise { + return this.file.getUint32(toNumber(offset), true); + } + + /** + * Gets an unsigned 64-bit integer at the specified byte offset from the start of the file. + * @param offset The offset, in bytes, from the start of the file where to read the data. + */ + async getBigUint64(offset: bigint): Promise { + return this.file.getBigUint64(toNumber(offset), true); + } + + /** + * returns an ArrayBuffer whose contents are a copy of this file bytes from startOffset, inclusive, up to endOffset, exclusive. + * @param startOffset The offset, in bytes, from the start of the file where to start reading the data. + * @param endOffset The offset, in bytes, from the start of the file where to end reading the data. + */ + async slice(startOffset: bigint, endOffset: bigint): Promise { + return this.file.buffer.slice(toNumber(startOffset), toNumber(endOffset)); + } + + /** the length (in bytes) of the data. */ + get length() { + return BigInt(this.file.byteLength); + } +} diff --git a/modules/loader-utils/src/lib/file-provider/file-handle-file.ts b/modules/loader-utils/src/lib/file-provider/file-handle-file.ts new file mode 100644 index 0000000000..73b2c4f32b --- /dev/null +++ b/modules/loader-utils/src/lib/file-provider/file-handle-file.ts @@ -0,0 +1,101 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {FileProvider} from './file-provider'; +import {NodeFileFacade as NodeFile} from '../files/node-file-facade'; + +/** + * Provides file data using node fs library + * @deprecated - will be replaced with ReadableFile + */ +export class FileHandleFile implements FileProvider { + /** The FileHandle from which data is provided */ + private file: NodeFile; + + /** The file length in bytes */ + private size: bigint; + + /** Create a new FileHandleFile */ + constructor(path: string) { + this.file = new NodeFile(path, 'r'); + this.size = this.file.bigsize; + } + + /** Close file */ + async destroy(): Promise { + await this.file.close(); + } + + /** + * Gets an unsigned 8-bit integer at the specified byte offset from the start of the file. + * @param offset The offset, in bytes, from the start of the file where to read the data. + */ + async getUint8(offset: number | bigint): Promise { + const arrayBuffer = await this.file.read(offset, 1); + const val = new Uint8Array(arrayBuffer).at(0); + if (val === undefined) { + throw new Error('something went wrong'); + } + return val; + } + + /** + * Gets an unsigned 16-bit integer at the specified byte offset from the start of the file. + * @param offset The offset, in bytes, from the start of the file where to read the data. + */ + async getUint16(offset: number | bigint): Promise { + const arrayBuffer = await this.file.read(offset, 2); + const val = new Uint16Array(arrayBuffer).at(0); + if (val === undefined) { + throw new Error('something went wrong'); + } + return val; + } + + /** + * Gets an unsigned 32-bit integer at the specified byte offset from the start of the file. + * @param offset The offset, in bytes, from the start of the file where to read the data. + */ + async getUint32(offset: number | bigint): Promise { + const arrayBuffer = await this.file.read(offset, 4); + const val = new Uint32Array(arrayBuffer).at(0); + if (val === undefined) { + throw new Error('something went wrong'); + } + return val; + } + + /** + * Gets an unsigned 32-bit integer at the specified byte offset from the start of the file. + * @param offset The offset, in bytes, from the start of the file where to read the data. + */ + async getBigUint64(offset: number | bigint): Promise { + const arrayBuffer = await this.file.read(offset, 8); + const val = new BigInt64Array(arrayBuffer).at(0); + if (val === undefined) { + throw new Error('something went wrong'); + } + return val; + } + + /** + * returns an ArrayBuffer whose contents are a copy of this file bytes from startOffset, inclusive, up to endOffset, exclusive. + * @param startOffset The offset, in byte, from the start of the file where to start reading the data. + * @param endOffset The offset, in bytes, from the start of the file where to end reading the data. + */ + async slice(startOffset: bigint, endOffset: bigint): Promise { + const bigLength = endOffset - startOffset; + if (bigLength > Number.MAX_SAFE_INTEGER) { + throw new Error('too big slice'); + } + const length = Number(bigLength); + return await this.file.read(startOffset, length); + } + + /** + * the length (in bytes) of the data. + */ + get length(): bigint { + return this.size; + } +} diff --git a/modules/i3s/src/lib/parsers/parse-zip/file-provider.ts b/modules/loader-utils/src/lib/file-provider/file-provider.ts similarity index 54% rename from modules/i3s/src/lib/parsers/parse-zip/file-provider.ts rename to modules/loader-utils/src/lib/file-provider/file-provider.ts index 4d0cebf9c7..f7ed7f34c0 100644 --- a/modules/i3s/src/lib/parsers/parse-zip/file-provider.ts +++ b/modules/loader-utils/src/lib/file-provider/file-provider.ts @@ -1,34 +1,57 @@ /** * Interface for providing file data + * @deprecated - will be replaced with ReadableFile */ export interface FileProvider { + /** + * Cleanup class data + */ + destroy(): Promise; /** * Gets an unsigned 8-bit integer at the specified byte offset from the start of the file. * @param offset The offset, in bytes, from the start of the file where to read the data. */ - getUint8(offset: number): Promise; + getUint8(offset: bigint): Promise; /** * Gets an unsigned 16-bit integer at the specified byte offset from the start of the file. * @param offset The offset, in bytes, from the start of the file where to read the data. */ - getUint16(offset: number): Promise; + getUint16(offset: bigint): Promise; /** * Gets an unsigned 32-bit integer at the specified byte offset from the start of the file. * @param offset The offset, in bytes, from the file of the view where to read the data. */ - getUint32(offset: number): Promise; + getUint32(offset: bigint): Promise; + + /** + * Gets an unsigned 32-bit integer at the specified byte offset from the start of the file. + * @param offset The offset, in byte, from the file of the view where to read the data. + */ + getBigUint64(offset: bigint): Promise; /** * returns an ArrayBuffer whose contents are a copy of this file bytes from startOffset, inclusive, up to endOffset, exclusive. * @param startOffset The offset, in bytes, from the start of the file where to start reading the data. * @param endOffset The offset, in bytes, from the start of the file where to end reading the data. */ - slice(startOffset: number, endOffset: number): Promise; + slice(startOffset: bigint, endOffset: bigint): Promise; /** * the length (in bytes) of the data. */ - length: number; + length: bigint; } + +/** + * Check is the object has FileProvider members + * @param fileProvider - tested object + */ +export const isFileProvider = (fileProvider: unknown) => { + return ( + (fileProvider as FileProvider)?.getUint8 && + (fileProvider as FileProvider)?.slice && + (fileProvider as FileProvider)?.length + ); +}; diff --git a/modules/loader-utils/src/lib/files/blob-file.ts b/modules/loader-utils/src/lib/files/blob-file.ts new file mode 100644 index 0000000000..a5b12b0c4d --- /dev/null +++ b/modules/loader-utils/src/lib/files/blob-file.ts @@ -0,0 +1,36 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {ReadableFile} from './file'; + +/** + * BlobFile provides a "file like interface" to the data in a Blob or File object + */ +export class BlobFile implements ReadableFile { + readonly handle: Blob; + readonly size: number; + readonly bigsize: bigint; + readonly url: string; + + constructor(blob: Blob | File | ArrayBuffer) { + this.handle = blob instanceof ArrayBuffer ? new Blob([blob]) : blob; + this.size = blob instanceof ArrayBuffer ? blob.byteLength : blob.size; + this.bigsize = BigInt(this.size); + this.url = blob instanceof File ? blob.name : ''; + } + + async close() {} + + async stat() { + return { + size: this.handle.size, + bigsize: BigInt(this.handle.size), + isDirectory: false + }; + } + + async read(start: number, length: number): Promise { + const arrayBuffer = await this.handle.slice(start, start + length).arrayBuffer(); + return arrayBuffer; + } +} diff --git a/modules/loader-utils/src/lib/files/file.ts b/modules/loader-utils/src/lib/files/file.ts new file mode 100644 index 0000000000..ec9c5b493d --- /dev/null +++ b/modules/loader-utils/src/lib/files/file.ts @@ -0,0 +1,38 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +export type Stat = { + size: number; + bigsize: bigint; + isDirectory: boolean; +}; + +export interface ReadableFile { + /** The underlying file handle (Blob, Node.js file descriptor etc) */ + readonly handle: unknown; + /** Length of file in bytes, if available */ + readonly size: number; + /** Length of file in bytes, if available */ + readonly bigsize: bigint; + /** Url, if available */ + readonly url: string; + + /** Read data */ + read(start?: number | bigint, length?: number): Promise; + /** Read data */ + fetchRange?(offset: number | bigint, length: number, signal?: AbortSignal): Promise; + /** Get information about file */ + stat?(): Promise; + /** Close the file */ + close(): Promise; +} + +export interface WritableFile { + handle: unknown; + /** Write to file. The number of bytes written will be returned */ + write: (arrayBuffer: ArrayBuffer, offset?: number | bigint, length?: number) => Promise; + /** Get information about the file */ + stat?(): Promise; + /** Close the file */ + close(): Promise; +} diff --git a/modules/loader-utils/src/lib/files/http-file.ts b/modules/loader-utils/src/lib/files/http-file.ts new file mode 100644 index 0000000000..4687ad6f29 --- /dev/null +++ b/modules/loader-utils/src/lib/files/http-file.ts @@ -0,0 +1,121 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {ReadableFile, Stat} from './file'; + +export class HttpFile implements ReadableFile { + readonly handle: string; + readonly size: number = 0; + readonly bigsize: bigint = 0n; + readonly url: string; + + constructor(url: string) { + this.handle = url; + this.url = url; + } + + async close(): Promise {} + + async stat(): Promise { + const response = await fetch(this.handle, {method: 'HEAD'}); + if (!response.ok) { + throw new Error(`Failed to fetch HEAD ${this.handle}`); + } + const size = parseInt(response.headers.get('Content-Length') || '0'); + return { + size, + bigsize: BigInt(size), + isDirectory: false + }; + } + + async read(offset: number | bigint, length: number): Promise { + const response = await this.fetchRange(offset, length); + const arrayBuffer = await response.arrayBuffer(); + return arrayBuffer; + } + + /** + * + * @param offset + * @param length + * @param signal + * @returns + * @see https://github.com/protomaps/PMTiles + */ + // eslint-disable-next-line complexity + async fetchRange( + offset: number | bigint, + length: number, + signal?: AbortSignal + ): Promise { + const nOffset = Number(offset); + const nLength = Number(length); + + let controller: AbortController | undefined; + if (!signal) { + // ToDO why is it so important to abort in case 200? + // TODO check this works or assert 206 + controller = new AbortController(); + signal = controller.signal; + } + + const url = this.handle; + let response = await fetch(url, { + signal, + headers: {Range: `bytes=${nOffset}-${nOffset + nLength - 1}`} + }); + + switch (response.status) { + case 206: // Partial Content success + // This is the expected success code for a range request + break; + + case 200: + // some well-behaved backends, e.g. DigitalOcean CDN, respond with 200 instead of 206 + // but we also need to detect no support for Byte Serving which is returning the whole file + const contentLength = response.headers.get('Content-Length'); + if (!contentLength || Number(contentLength) > length) { + if (controller) { + controller.abort(); + } + throw Error( + 'content-length header missing or exceeding request. Server must support HTTP Byte Serving.' + ); + } + + // @eslint-disable-next-line no-fallthrough + case 416: // "Range Not Satisfiable" + // some HTTP servers don't accept ranges beyond the end of the resource. + // Retry with the exact length + // TODO: can return 416 with offset > 0 if content changed, which will have a blank etag. + // See https://github.com/protomaps/PMTiles/issues/90 + if (offset === 0) { + const contentRange = response.headers.get('Content-Range'); + if (!contentRange || !contentRange.startsWith('bytes *')) { + throw Error('Missing content-length on 416 response'); + } + const actualLength = Number(contentRange.substr(8)); + response = await fetch(this.url, { + signal, + headers: {Range: `bytes=0-${actualLength - 1}`} + }); + } + break; + + default: + if (response.status >= 300) { + throw Error(`Bad response code: ${response.status}`); + } + } + + return response; + // const data = await response.arrayBuffer(); + // return { + // data, + // etag: response.headers.get('ETag') || undefined, + // cacheControl: response.headers.get('Cache-Control') || undefined, + // expires: response.headers.get('Expires') || undefined + // }; + } +} diff --git a/modules/loader-utils/src/lib/files/node-file-facade.ts b/modules/loader-utils/src/lib/files/node-file-facade.ts new file mode 100644 index 0000000000..cc841f76bb --- /dev/null +++ b/modules/loader-utils/src/lib/files/node-file-facade.ts @@ -0,0 +1,40 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {isBrowser} from '../env-utils/globals'; +import {ReadableFile, WritableFile, Stat} from './file'; + +const NOT_IMPLEMENTED = new Error('Not implemented'); + +/** This class is a facade that gets replaced with an actual NodeFile instance */ +export class NodeFileFacade implements ReadableFile, WritableFile { + handle: unknown; + size: number = 0; + bigsize: bigint = 0n; + url: string = ''; + + constructor(url: string, flags?: 'r' | 'w' | 'wx', mode?: number) { + // Return the actual implementation instance + if (globalThis.loaders?.NodeFile) { + return new globalThis.loaders.NodeFile(url, flags, mode); + } + if (isBrowser) { + throw new Error('Can\'t instantiate NodeFile in browser.'); + } + throw new Error('Can\'t instantiate NodeFile. Make sure to import @loaders.gl/polyfills first.'); + } + /** Read data */ + async read(start?: number | bigint, end?: number | bigint): Promise { + throw NOT_IMPLEMENTED; + } + /** Write to file. The number of bytes written will be returned */ + async write(arrayBuffer: ArrayBuffer, offset?: number | bigint, length?: number | bigint): Promise { + throw NOT_IMPLEMENTED; + } + /** Get information about file */ + async stat(): Promise { + throw NOT_IMPLEMENTED; + } + /** Close the file */ + async close(): Promise {} +} diff --git a/modules/loader-utils/src/lib/files/sources.ts b/modules/loader-utils/src/lib/files/sources.ts new file mode 100644 index 0000000000..ab45a1f30d --- /dev/null +++ b/modules/loader-utils/src/lib/files/sources.ts @@ -0,0 +1,150 @@ +/* +import {fetchFile} from '@loaders.gl/core'; + +import {Source as PMTilesSource, RangeResponse} from 'pmtiles'; + +/** @note "source" here is a PMTiles library type, referring to * +export function makeSource(data: string | Blob, fetch?) { + if (typeof data === 'string') { + return new FetchSource(data, fetch); + } + if (data instanceof Blob) { + const url = ''; + return new BlobSource(data, url); + } +} + +export class BlobSource implements PMTilesSource { + blob: Blob; + key: string; + + constructor(blob: Blob, key: string) { + this.blob = blob; + this.key = key; + } + + // TODO - how is this used? + getKey() { + return this.blob.url || ''; + } + + async getBytes(offset: number, length: number, signal?: AbortSignal): Promise { + const data = await this.blob.arrayBuffer(); + return { + data + // etag: response.headers.get('ETag') || undefined, + // cacheControl: response.headers.get('Cache-Control') || undefined, + // expires: response.headers.get('Expires') || undefined + }; + } +} + +export class FetchSource implements PMTilesSource { + url: string; + fetch; + + constructor(url: string, fetch = fetchFile) { + this.url = url; + this.fetch = fetch; + } + + // TODO - how is this used? + getKey() { + return this.url; + } + + async getBytes(offset: number, length: number, signal?: AbortSignal): Promise { + let controller; + if (!signal) { + // ToDO why is it so important to abort in case 200? + // TODO check this works or assert 206 + controller = new AbortController(); + signal = controller.signal; + } + + let response = await fetch(this.url, { + signal, + headers: {Range: `bytes=${offset}-${offset + length - 1}`} + }); + + switch (response.status) { + case 206: // Partial Content success + // This is the expected success code for a range request + break; + + case 200: + // some well-behaved backends, e.g. DigitalOcean CDN, respond with 200 instead of 206 + // but we also need to detect no support for Byte Serving which is returning the whole file + const content_length = response.headers.get('Content-Length'); + if (!content_length || Number(content_length) > length) { + if (controller) { + controller.abort(); + } + throw Error( + 'content-length header missing or exceeding request. Server must support HTTP Byte Serving.' + ); + } + + case 416: // "Range Not Satisfiable" + // some HTTP servers don't accept ranges beyond the end of the resource. + // Retry with the exact length + // TODO: can return 416 with offset > 0 if content changed, which will have a blank etag. + // See https://github.com/protomaps/PMTiles/issues/90 + if (offset === 0) { + const content_range = response.headers.get('Content-Range'); + if (!content_range || !content_range.startsWith('bytes *')) { + throw Error('Missing content-length on 416 response'); + } + const actual_length = Number(content_range.substr(8)); + response = await fetch(this.url, { + signal, + headers: {Range: `bytes=0-${actual_length - 1}`} + }); + } + break; + + default: + if (response.status >= 300) { + throw Error(`Bad response code: ${response.status}`); + } + } + + const data = await response.arrayBuffer(); + return { + data, + etag: response.headers.get('ETag') || undefined, + cacheControl: response.headers.get('Cache-Control') || undefined, + expires: response.headers.get('Expires') || undefined + }; + } +} + +/* +class TestNodeFileSource implements Source { + buffer: ArrayBuffer; + path: string; + key: string; + etag?: string; + + constructor(path: string, key: string) { + this.path = path; + this.buffer = fs.readFileSync(path); + this.key = key; + } + + getKey() { + return this.key; + } + + replaceData(path: string) { + this.path = path; + this.buffer = fs.readFileSync(path); + } + + async getBytes(offset: number, length: number): Promise { + const slice = new Uint8Array(this.buffer.slice(offset, offset + length)) + .buffer; + return { data: slice, etag: this.etag }; + } +} +*/ diff --git a/modules/loader-utils/src/lib/filesystems/filesystem.ts b/modules/loader-utils/src/lib/filesystems/filesystem.ts new file mode 100644 index 0000000000..d728d5e336 --- /dev/null +++ b/modules/loader-utils/src/lib/filesystems/filesystem.ts @@ -0,0 +1,39 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {ReadableFile, WritableFile} from '../files/file'; + +/** + * A FileSystem interface can encapsulate various file sources, + * a FileList, a Node.js filesystem, a ZipFile, a GoogleDrive etc. + */ +export interface FileSystem { + /** Return a list of file names in a "directory" */ + readdir(dirname?: string, options?: {recursive?: boolean}): Promise; + + /** Gets information from a local file from the filesystem */ + stat(filename: string, options?: object): Promise<{size: number}>; + + /** Removes a file from the file system */ + unlink?(path: string): Promise; + + /** Fetches the full contents of a file from the filesystem (or a URL) */ + fetch(path: string, options?: RequestInit): Promise; +} + +/** + * A random access file system, open readable and/or writable files + */ +export interface RandomAccessFileSystem extends FileSystem { + /** Can open readable files */ + readonly readable: boolean; + + /** Can open writable files */ + readonly writable: boolean; + + /** Open a readable file */ + openReadableFile(path: string, flags?: 'r'): Promise; + + /** Open a writable file */ + openWritableFile(path: string, flags?: 'w' | 'wx', mode?: number): Promise; +} diff --git a/modules/loader-utils/src/lib/filesystems/node-filesystem-facade.ts b/modules/loader-utils/src/lib/filesystems/node-filesystem-facade.ts new file mode 100644 index 0000000000..c7ca2a9d89 --- /dev/null +++ b/modules/loader-utils/src/lib/filesystems/node-filesystem-facade.ts @@ -0,0 +1,64 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {isBrowser} from '../env-utils/globals'; +import {Stat} from '../files/file'; +import {NodeFileFacade as NodeFile} from '../files/node-file-facade'; +import {RandomAccessFileSystem} from './filesystem'; + +const NOT_IMPLEMENTED = new Error('Not implemented'); + +/** + * FileSystem pass-through for Node.js + * Compatible with BrowserFileSystem. + * @note Dummy implementation, not used (constructor returns a real NodeFileSystem instance) + * @param options + */ +export class NodeFileSystemFacade implements RandomAccessFileSystem { + // implements FileSystem + constructor(options: {[key: string]: any}) { + if (globalThis.loaders?.NodeFileSystem) { + return new globalThis.loaders.NodeFileSystem(options); + } + if (isBrowser) { + throw new Error('Can\'t instantiate NodeFileSystem in browser.'); + } + throw new Error( + 'Can\'t instantiate NodeFileSystem. Make sure to import @loaders.gl/polyfills first.' + ); + } + + // DUMMY IMPLEMENTATION, not used (constructor returns a real NodeFileSystem instance) + + // implements RandomAccessReadFileSystem + + readonly readable = true; + readonly writable = true; + + async openReadableFile(path: string, flags): Promise { + throw NOT_IMPLEMENTED; + } + + // implements RandomAccessWriteFileSystem + async openWritableFile(path: string, flags, mode): Promise { + throw NOT_IMPLEMENTED; + } + + // Implements file system + + async readdir(dirname = '.', options?: {}): Promise { + throw NOT_IMPLEMENTED; + } + + async stat(path: string, options?: {}): Promise { + throw NOT_IMPLEMENTED; + } + + async unlink(path: string): Promise { + throw NOT_IMPLEMENTED; + } + + async fetch(path: RequestInfo, options?: RequestInit): Promise { + throw NOT_IMPLEMENTED; + } +} diff --git a/modules/loader-utils/src/lib/filesystems/node-filesystem.ts b/modules/loader-utils/src/lib/filesystems/node-filesystem.ts deleted file mode 100644 index 5a37e8491f..0000000000 --- a/modules/loader-utils/src/lib/filesystems/node-filesystem.ts +++ /dev/null @@ -1,79 +0,0 @@ -import * as fs from '../node/fs'; -import {IFileSystem, IRandomAccessReadFileSystem} from '../../types'; -// import {fetchFile} from "../fetch/fetch-file" -// import {selectLoader} from "../api/select-loader"; - -type Stat = { - size: number; - isDirectory: () => boolean; - info?: fs.Stats; -}; - -type ReadOptions = { - buffer?: Buffer; - offset?: number; - length?: number; - position?: number; -}; - -/** - * FileSystem pass-through for Node.js - * Compatible with BrowserFileSystem. - * @param options - */ -export default class NodeFileSystem implements IFileSystem, IRandomAccessReadFileSystem { - // implements IFileSystem - constructor(options: {[key: string]: any}) { - this.fetch = options._fetch; - } - - async readdir(dirname = '.', options?: {}): Promise { - return await fs.readdir(dirname, options); - } - - async stat(path: string, options?: {}): Promise { - const info = await fs.stat(path, options); - return {size: Number(info.size), isDirectory: () => false, info}; - } - - async fetch(path: string, options: {[key: string]: any}) { - // Falls back to handle https:/http:/data: etc fetches - // eslint-disable-next-line - const fallbackFetch = options.fetch || this.fetch; - return fallbackFetch(path, options); - } - - // implements IRandomAccessFileSystem - async open(path: string, flags: string | number, mode?: any): Promise { - return await fs.open(path, flags); - } - - async close(fd: number): Promise { - return await fs.close(fd); - } - - async fstat(fd: number): Promise { - const info = await fs.fstat(fd); - return info; - } - - async read( - fd: number, - // @ts-ignore Possibly null - {buffer = null, offset = 0, length = buffer.byteLength, position = null}: ReadOptions - ): Promise<{bytesRead: number; buffer: Buffer}> { - let totalBytesRead = 0; - // Read in loop until we get required number of bytes - while (totalBytesRead < length) { - const {bytesRead} = await fs.read( - fd, - buffer, - offset + totalBytesRead, - length - totalBytesRead, - position + totalBytesRead - ); - totalBytesRead += bytesRead; - } - return {bytesRead: totalBytesRead, buffer}; - } -} diff --git a/modules/loader-utils/src/lib/filesystems/readable-file.ts b/modules/loader-utils/src/lib/filesystems/readable-file.ts deleted file mode 100644 index 3f755bfc7b..0000000000 --- a/modules/loader-utils/src/lib/filesystems/readable-file.ts +++ /dev/null @@ -1,30 +0,0 @@ -// loaders.gl, MIT license - -export type ReadableFile = { - read: (position: number, length: number) => Promise; - close: () => Promise; - /** Length of file in bytes */ - size: number; -}; - -/** Helper function to create an envelope reader for a binary memory input */ -export function makeReadableFile(data: Blob | ArrayBuffer): ReadableFile { - if (data instanceof ArrayBuffer) { - const arrayBuffer: ArrayBuffer = data; - return { - read: async (start: number, length: number) => Buffer.from(data, start, length), - close: async () => {}, - size: arrayBuffer.byteLength - }; - } - - const blob: Blob = data; - return { - read: async (start: number, length: number) => { - const arrayBuffer = await blob.slice(start, start + length).arrayBuffer(); - return Buffer.from(arrayBuffer); - }, - close: async () => {}, - size: blob.size - }; -} diff --git a/modules/loader-utils/src/lib/filesystems/writable-file.ts b/modules/loader-utils/src/lib/filesystems/writable-file.ts deleted file mode 100644 index 24c5bec5f3..0000000000 --- a/modules/loader-utils/src/lib/filesystems/writable-file.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Forked from https://github.com/kbajalc/parquets under MIT license (Copyright (c) 2017 ironSource Ltd.) -import {isBrowser} from '../env-utils/globals'; -import * as fs from '../node/fs'; -import type {Writable} from 'stream'; - -export type WritableFile = { - write: (buf: Buffer) => Promise; - close: () => Promise; -}; - -export interface WriteStreamOptions { - flags?: string; - encoding?: 'utf8'; - fd?: number; - mode?: number; - autoClose?: boolean; - start?: number; -} - -/** Helper function to create an envelope reader for a binary memory input */ -export function makeWritableFile( - pathOrStream: string | Writable, - options?: WriteStreamOptions -): WritableFile { - if (isBrowser) { - return { - write: async () => {}, - close: async () => {} - }; - } - - const outputStream: Writable = - typeof pathOrStream === 'string' ? fs.createWriteStream(pathOrStream, options) : pathOrStream; - return { - write: async (buffer: Buffer) => - new Promise((resolve, reject) => { - outputStream.write(buffer, (err) => (err ? reject(err) : resolve())); - }), - close: () => - new Promise((resolve, reject) => { - (outputStream as any).close((err) => (err ? reject(err) : resolve())); - }) - }; -} diff --git a/modules/loader-utils/src/lib/node/buffer.browser.ts b/modules/loader-utils/src/lib/node/buffer.browser.ts index a77d8daf8a..2d2f486377 100644 --- a/modules/loader-utils/src/lib/node/buffer.browser.ts +++ b/modules/loader-utils/src/lib/node/buffer.browser.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Isolates Buffer references to ensure they are only bundled under Node.js (avoids big webpack polyfill) // this file is selected by the package.json "browser" field). @@ -15,6 +16,6 @@ export function toArrayBuffer(buffer) { /** * Convert (copy) ArrayBuffer to Buffer */ -export function toBuffer(binaryData: ArrayBuffer | ArrayBuffer | Buffer): Buffer { +export function toBuffer(binaryData: ArrayBuffer | Buffer): Buffer { throw new Error('Buffer not supported in browser'); } diff --git a/modules/loader-utils/src/lib/node/buffer.ts b/modules/loader-utils/src/lib/node/buffer.ts index c7183bee0f..3e1197db51 100644 --- a/modules/loader-utils/src/lib/node/buffer.ts +++ b/modules/loader-utils/src/lib/node/buffer.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Isolates Buffer references to ensure they are only bundled under Node.js (avoids big webpack polyfill) // this file is selected by the package.json "browser" field). @@ -20,7 +21,7 @@ export function toArrayBuffer(buffer) { /** * Convert (copy) ArrayBuffer to Buffer */ -export function toBuffer(binaryData: ArrayBuffer | ArrayBuffer | Buffer): Buffer { +export function toBuffer(binaryData: ArrayBuffer | Buffer): Buffer { if (Buffer.isBuffer(binaryData)) { return binaryData; } @@ -29,6 +30,7 @@ export function toBuffer(binaryData: ArrayBuffer | ArrayBuffer | Buffer): Buffer binaryData = binaryData.buffer; } + // TODO - move to loaders.gl/polyfills if (typeof Buffer !== 'undefined' && binaryData instanceof ArrayBuffer) { return Buffer.from(binaryData); } diff --git a/modules/loader-utils/src/lib/node/fs.browser.ts b/modules/loader-utils/src/lib/node/fs.browser.ts new file mode 100644 index 0000000000..b2daf7dd49 --- /dev/null +++ b/modules/loader-utils/src/lib/node/fs.browser.ts @@ -0,0 +1 @@ +export const isSupported = false; diff --git a/modules/loader-utils/src/lib/node/fs.ts b/modules/loader-utils/src/lib/node/fs.ts deleted file mode 100644 index 0bc546acc8..0000000000 --- a/modules/loader-utils/src/lib/node/fs.ts +++ /dev/null @@ -1,79 +0,0 @@ -// fs wrapper (promisified fs + avoids bundling fs in browsers) -import fs from 'fs'; -import {toArrayBuffer} from './buffer'; -import {promisify2, promisify3} from './promisify'; - -export type {Stats, WriteStream} from 'fs'; - -export let readdir; -/** Wrapper for Node.js fs method */ -export let stat; - -/** Wrapper for Node.js fs method */ -export let readFile; -/** Wrapper for Node.js fs method */ -export let readFileSync; -/** Wrapper for Node.js fs method */ -export let writeFile; -/** Wrapper for Node.js fs method */ -export let writeFileSync; - -// file descriptors - -/** Wrapper for Node.js fs method */ -export let open; -/** Wrapper for Node.js fs method */ -export let close: (fd: number) => Promise; -/** Wrapper for Node.js fs method */ -export let read; -/** Wrapper for Node.js fs method */ -export let fstat; - -export let createWriteStream: typeof fs.createWriteStream; - -export let isSupported = Boolean(fs); - -// paths - -try { - /** Wrapper for Node.js fs method */ - readdir = promisify2(fs.readdir); - /** Wrapper for Node.js fs method */ - stat = promisify2(fs.stat); - - /** Wrapper for Node.js fs method */ - readFile = fs.readFile; - /** Wrapper for Node.js fs method */ - readFileSync = fs.readFileSync; - /** Wrapper for Node.js fs method */ - writeFile = promisify3(fs.writeFile); - /** Wrapper for Node.js fs method */ - writeFileSync = fs.writeFileSync; - - // file descriptors - - /** Wrapper for Node.js fs method */ - open = fs.open; - /** Wrapper for Node.js fs method */ - close = (fd: number) => - new Promise((resolve, reject) => fs.close(fd, (err) => (err ? reject(err) : resolve()))); - /** Wrapper for Node.js fs method */ - read = fs.read; - /** Wrapper for Node.js fs method */ - fstat = fs.fstat; - - createWriteStream = fs.createWriteStream; - - isSupported = Boolean(fs); -} catch { - // ignore -} - -export async function _readToArrayBuffer(fd: number, start: number, length: number) { - const buffer = Buffer.alloc(length); - const {bytesRead} = await read(fd, buffer, 0, length, start); - if (bytesRead !== length) { - throw new Error('fs.read failed'); - } - return toArrayBuffer(buffer); -} diff --git a/modules/loader-utils/src/lib/node/stream.browser.ts b/modules/loader-utils/src/lib/node/stream.browser.ts new file mode 100644 index 0000000000..b2daf7dd49 --- /dev/null +++ b/modules/loader-utils/src/lib/node/stream.browser.ts @@ -0,0 +1 @@ +export const isSupported = false; diff --git a/modules/loader-utils/src/lib/node/stream.ts b/modules/loader-utils/src/lib/node/stream.ts index edb2861596..a23ca29cec 100644 --- a/modules/loader-utils/src/lib/node/stream.ts +++ b/modules/loader-utils/src/lib/node/stream.ts @@ -1,18 +1,11 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import stream from 'stream'; +import * as stream from 'stream'; export type {Writable} from 'stream'; -export let Transform; +/** Wrapper for Node.js stream method */ +export const Transform = stream.Transform; export const isSupported = Boolean(stream); - -// paths - -try { - /** Wrapper for Node.js fs method */ - Transform = stream.Transform; -} catch { - // ignore -} diff --git a/modules/loader-utils/src/lib/option-utils/merge-loader-options.ts b/modules/loader-utils/src/lib/option-utils/merge-loader-options.ts index 7ed781289e..75df3906b7 100644 --- a/modules/loader-utils/src/lib/option-utils/merge-loader-options.ts +++ b/modules/loader-utils/src/lib/option-utils/merge-loader-options.ts @@ -1,6 +1,7 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import {LoaderOptions} from '../../types'; +import {LoaderOptions} from '../../loader-types'; /** * @@ -21,7 +22,7 @@ function mergeOptionsRecursively( ): Record { const options = {...baseOptions}; for (const [key, newValue] of Object.entries(newOptions)) { - if (newValue && typeof newValue === 'object') { + if (newValue && typeof newValue === 'object' && !Array.isArray(newValue)) { options[key] = mergeOptionsRecursively( (options[key] as Record) || {}, newOptions[key] as Record diff --git a/modules/wms/src/lib/sources/data-source.ts b/modules/loader-utils/src/lib/sources/data-source.ts similarity index 91% rename from modules/wms/src/lib/sources/data-source.ts rename to modules/loader-utils/src/lib/sources/data-source.ts index 7283d2e3ea..3c100e88a4 100644 --- a/modules/wms/src/lib/sources/data-source.ts +++ b/modules/loader-utils/src/lib/sources/data-source.ts @@ -1,14 +1,17 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {LoaderOptions} from '@loaders.gl/loader-utils'; +/** Common properties for all data sources */ export type DataSourceProps = { /** LoaderOptions provide an option to override `fetch`. Will also be passed to any sub loaders */ loadOptions?: LoaderOptions; }; /** base class of all data sources */ -export abstract class DataSource { +export abstract class DataSource { + abstract data: unknown; /** A resolved fetch function extracted from loadOptions prop */ fetch: (url: string, options?: RequestInit) => Promise; /** The actual load options, if calling a loaders.gl loader */ diff --git a/modules/wms/src/lib/sources/image-source.ts b/modules/loader-utils/src/lib/sources/image-source.ts similarity index 68% rename from modules/wms/src/lib/sources/image-source.ts rename to modules/loader-utils/src/lib/sources/image-source.ts index 92505715db..e616feaf7f 100644 --- a/modules/wms/src/lib/sources/image-source.ts +++ b/modules/loader-utils/src/lib/sources/image-source.ts @@ -1,8 +1,9 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import type {ImageType} from '@loaders.gl/images'; import type {DataSourceProps} from './data-source'; import {DataSource} from './data-source'; +import {ImageType} from './utils/image-type'; /** * Normalized capabilities of an Image service @@ -27,12 +28,9 @@ export type ImageSourceLayer = { /** Coordinate systems supported by this layer */ crs?: string[]; /** layer limits in unspecified CRS:84-like lng/lat, for quick access w/o CRS calculations. */ - geographicBoundingBox?: [min: [x: number, y: number], max: [x: number, y: number]]; + boundingBox?: [min: [x: number, y: number], max: [x: number, y: number]]; /** Sub layers of this layer */ layers?: ImageSourceLayer[]; - - /** @deprecated from v3.4: non-vis.gl style bounding box. Use `.geographicBoundingBox` instead */ - boundingBox?: [number, number, number, number]; }; /** Generic parameters for requesting an image from an image source */ @@ -42,31 +40,7 @@ export type GetImageParameters = { /** Styling */ styles?: unknown; /** bounding box of the requested map image */ - bbox: [number, number, number, number]; - /** pixel width of returned image */ - width: number; - /** pixels */ - height: number; - /** crs for the image (not the bounding box) */ - crs?: string; - /** requested format for the return image */ - format?: 'image/png'; -}; - -// Attempt to break down GetImageParameters -export type ImageFilters = { - /** Layers to render */ - layers: string | string[]; - /** Styling */ - styles?: unknown; -}; - -export type ImageRegion = { - /** bounding box of the requested map image */ - bbox: [number, number, number, number]; -}; - -export type ImageFormat = { + boundingBox: [min: [x: number, y: number], max: [x: number, y: number]]; /** pixel width of returned image */ width: number; /** pixels */ @@ -87,6 +61,9 @@ export type ImageSourceProps = DataSourceProps; export abstract class ImageSource< PropsT extends ImageSourceProps = ImageSourceProps > extends DataSource { + static type: string = 'template'; + static testURL = (url: string): boolean => false; + abstract getMetadata(): Promise; abstract getImage(parameters: GetImageParameters): Promise; } diff --git a/modules/loader-utils/src/lib/sources/image-tile-source.ts b/modules/loader-utils/src/lib/sources/image-tile-source.ts new file mode 100644 index 0000000000..cfbf048cc4 --- /dev/null +++ b/modules/loader-utils/src/lib/sources/image-tile-source.ts @@ -0,0 +1,15 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {ImageType} from './utils/image-type'; +import type {TileSource, TileSourceMetadata} from './tile-source'; +import type {GetTileParameters} from './tile-source'; + +/** + * MapTileSource - data sources that allow data to be queried by (geospatial) tile + * @note If geospatial, bounding box is expected to be in web mercator coordinates + */ +export interface ImageTileSource + extends TileSource { + getImageTile(parameters: GetTileParameters): Promise; +} diff --git a/modules/loader-utils/src/lib/sources/tile-source-adapter.ts b/modules/loader-utils/src/lib/sources/tile-source-adapter.ts new file mode 100644 index 0000000000..cb2847c623 --- /dev/null +++ b/modules/loader-utils/src/lib/sources/tile-source-adapter.ts @@ -0,0 +1,56 @@ +// loaders.gl, MIT license + +import {TileSource, GetTileParameters} from './tile-source'; +import {ImageSource, ImageSourceMetadata} from './image-source'; + +/** + * MapTileSource - data sources that allow data to be queried by (geospatial) extents + * @note + * - If geospatial, bounding box is expected to be in web mercator coordinates + */ +export class TileSourceAdapter implements TileSource { + readonly viewportSource: ImageSource; + constructor(source: ImageSource) { + this.viewportSource = source; + } + async getMetadata(): Promise { + return await this.viewportSource.getMetadata(); + } + /** Flat parameters */ + getTile(parameters: GetTileParameters): Promise { + const width = 512; + const height = 512; + const boundingBox = this.getTileBoundingBox(parameters); + return this.viewportSource.getImage({boundingBox, layers: [], width, height}); + } + + /** deck.gl style parameters */ + // getTileData(parameters: TileLoadParameters): Promise { + // return this.viewportSource.getImage + // } + + /** Bounding box of tiles in this tileset `[[w, s], [e, n]]` */ + protected getTileBoundingBox( + parameters: GetTileParameters + ): [min: [x: number, y: number], max: [x: number, y: number]] { + if (parameters.crs && parameters.crs !== 'ESPG3758') { + throw new Error('SRS not ESPG3758'); + } + const {x, y, zoom} = parameters; + + return [ + /** Bounding box of tile in this tileset `[[w, s], ...]` */ + this.getTileLowerLeftCorner(x, y, zoom), + /** Bounding box of tile in this tileset `[..., [e, n]]` */ + this.getTileLowerLeftCorner(x + 1, y - 1, zoom) + ]; + } + + getTileLowerLeftCorner(x: number, y: number, zoom: number): [number, number] { + const tiles = 2 ^ zoom; + const longitude = (x / tiles) * 360.0 - 180.0; + const latitudeRadians = Math.atan(Math.sinh(Math.PI * (1 - (2 * (y + 1)) / tiles))); + const latitude = (180.0 * latitudeRadians) / Math.PI; + return [longitude, latitude]; + } +} diff --git a/modules/loader-utils/src/lib/sources/tile-source.ts b/modules/loader-utils/src/lib/sources/tile-source.ts new file mode 100644 index 0000000000..cf55073757 --- /dev/null +++ b/modules/loader-utils/src/lib/sources/tile-source.ts @@ -0,0 +1,102 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +/** + * Normalized capabilities of an Image service + * Sources are expected to normalize the response to capabilities + * @example + * A WMS service would normalize the response to the WMS `GetCapabilities` data structure extracted from WMS XML response + * into an TileSourceMetadata. + */ +export type TileSourceMetadata = { + format?: string; + formatHeader?: unknown; + + /** Name of the tileset (extracted from JSON metadata if available) */ + name?: string; + title?: string; + abstract?: string; + keywords?: string[]; + /** Attribution string (extracted from JSON metadata if available) */ + attributions?: string[]; + + /** Minimal zoom level of tiles in this tileset */ + minZoom?: number; + /** Maximal zoom level of tiles in this tileset */ + maxZoom?: number; + /** Bounding box of tiles in this tileset `[[w, s], [e, n]]` */ + boundingBox?: [min: [x: number, y: number], max: [x: number, y: number]]; + + /** Layer information */ + layer?: { + name: string; + title?: string; + srs?: string[]; + boundingBox?: [number, number, number, number]; + layers: TileSourceLayer[]; + }; +}; + +/** + * Description of one data layer in the image source + */ +export type TileSourceLayer = { + name: string; + title?: string; + srs?: string[]; + boundingBox?: [number, number, number, number]; + layers: TileSourceLayer[]; +}; + +/** + * Generic parameters for requesting an image from an image source + */ +export type GetTileParameters = { + /** Layers to render */ + layers: string | string[]; + /** Styling */ + styles?: unknown; + /** bounding box of the requested map image */ + zoom: number; + /** tile x coordinate */ + x: number; + /** tile y coordinate */ + y: number; + /** Coordinate reference system for the image (not the bounding box) */ + crs?: string; + /** requested format for the return image */ + format?: 'image/png'; +}; + +export type TileLoadParameters = { + index: {x: number; y: number; z: number}; + id: string; + bbox: TileBoundingBox; + zoom?: number; + url?: string | null; + signal?: AbortSignal; + userData?: Record; +}; + +/** deck.gl compatible bounding box */ +export type TileBoundingBox = NonGeoBoundingBox | GeoBoundingBox; +export type GeoBoundingBox = {west: number; north: number; east: number; south: number}; +export type NonGeoBoundingBox = {left: number; top: number; right: number; bottom: number}; + +/** + * Props for a TileSource + */ +export type TileSourceProps = {}; + +/** + * MapTileSource - data sources that allow data to be queried by (geospatial) extents + * @note + * - If geospatial, bounding box is expected to be in web mercator coordinates + */ +export interface TileSource { + getMetadata(): Promise; + /** Flat parameters */ + getTile(parameters: GetTileParameters): Promise; + /** deck.gl style parameters */ + getTileData?(parameters: TileLoadParameters): Promise; +} diff --git a/modules/loader-utils/src/lib/sources/utils/image-type.ts b/modules/loader-utils/src/lib/sources/utils/image-type.ts new file mode 100644 index 0000000000..ee0a18c7bd --- /dev/null +++ b/modules/loader-utils/src/lib/sources/utils/image-type.ts @@ -0,0 +1,10 @@ +/** Supported Image Types @note duplicates definition in images/schema to avoid circular deps */ +export type ImageType = ImageBitmap | ImageDataType | HTMLImageElement; + +/** data images @note duplicates definition in images/schema to avoid circular deps */ +export type ImageDataType = { + data: Uint8Array; + width: number; + height: number; + compressed?: boolean; +}; diff --git a/modules/wms/src/lib/sources/utils/utils.ts b/modules/loader-utils/src/lib/sources/utils/utils.ts similarity index 96% rename from modules/wms/src/lib/sources/utils/utils.ts rename to modules/loader-utils/src/lib/sources/utils/utils.ts index 8e3f7a8683..eb93b5f374 100644 --- a/modules/wms/src/lib/sources/utils/utils.ts +++ b/modules/loader-utils/src/lib/sources/utils/utils.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {LoaderOptions} from '@loaders.gl/loader-utils'; diff --git a/modules/loader-utils/src/lib/sources/vector-tile-source.ts b/modules/loader-utils/src/lib/sources/vector-tile-source.ts new file mode 100644 index 0000000000..7454a56443 --- /dev/null +++ b/modules/loader-utils/src/lib/sources/vector-tile-source.ts @@ -0,0 +1,14 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {TileSource, TileSourceMetadata} from './tile-source'; +import {GetTileParameters} from './tile-source'; + +/** + * MapTileSource - data sources that allow data to be queried by (geospatial) tile + * @note If geospatial, bounding box is expected to be in web mercator coordinates + */ +export interface VectorTileSource + extends TileSource { + getVectorTile(parameters: GetTileParameters): Promise; +} diff --git a/modules/loader-utils/src/lib/worker-loader-utils/create-loader-worker.ts b/modules/loader-utils/src/lib/worker-loader-utils/create-loader-worker.ts index bd7bf85df1..4ee872d87f 100644 --- a/modules/loader-utils/src/lib/worker-loader-utils/create-loader-worker.ts +++ b/modules/loader-utils/src/lib/worker-loader-utils/create-loader-worker.ts @@ -1,5 +1,5 @@ /* eslint-disable no-restricted-globals */ -import type {LoaderWithParser} from '../../types'; +import type {LoaderWithParser, LoaderOptions, LoaderContext} from '../../loader-types'; import {WorkerBody} from '@loaders.gl/worker-utils'; // import {validateLoaderVersion} from './validate-loader-version'; @@ -9,9 +9,9 @@ let requestId = 0; * Set up a WebWorkerGlobalScope to talk with the main thread * @param loader */ -export function createLoaderWorker(loader: LoaderWithParser) { +export async function createLoaderWorker(loader: LoaderWithParser) { // Check that we are actually in a worker thread - if (!WorkerBody.inWorkerThread()) { + if (!(await WorkerBody.inWorkerThread())) { return; } @@ -27,9 +27,10 @@ export function createLoaderWorker(loader: LoaderWithParser) { loader, arrayBuffer: input, options, + // @ts-expect-error fetch missing context: { ...context, - parse: parseOnMainThread + _parse: parseOnMainThread } }); WorkerBody.postMessage('done', {result}); @@ -43,7 +44,12 @@ export function createLoaderWorker(loader: LoaderWithParser) { }; } -function parseOnMainThread(arrayBuffer: ArrayBuffer, options: {[key: string]: any}): Promise { +function parseOnMainThread( + arrayBuffer: ArrayBuffer, + loader: any, + options?: LoaderOptions, + context?: LoaderContext +): Promise { return new Promise((resolve, reject) => { const id = requestId++; @@ -83,7 +89,17 @@ function parseOnMainThread(arrayBuffer: ArrayBuffer, options: {[key: string]: an // TODO - Why not support async loader.parse* funcs here? // TODO - Why not reuse a common function instead of reimplementing loader.parse* selection logic? Keeping loader small? // TODO - Lack of appropriate parser functions can be detected when we create worker, no need to wait until parse -async function parseData({loader, arrayBuffer, options, context}) { +async function parseData({ + loader, + arrayBuffer, + options, + context +}: { + loader: LoaderWithParser; + arrayBuffer: ArrayBuffer; + options: LoaderOptions; + context: LoaderContext; +}) { let data; let parser; if (loader.parseSync || loader.parse) { diff --git a/modules/loader-utils/src/lib/worker-loader-utils/encode-with-worker.ts b/modules/loader-utils/src/lib/worker-loader-utils/encode-with-worker.ts index 30c049bf03..166203f3e0 100644 --- a/modules/loader-utils/src/lib/worker-loader-utils/encode-with-worker.ts +++ b/modules/loader-utils/src/lib/worker-loader-utils/encode-with-worker.ts @@ -1,5 +1,5 @@ import {WorkerFarm} from '@loaders.gl/worker-utils'; -import {Writer, WriterOptions} from '../../types'; +import {Writer, WriterOptions} from '../../writer-types'; import {isBrowser} from '../env-utils/globals'; /** diff --git a/modules/loader-utils/src/lib/worker-loader-utils/parse-with-worker.ts b/modules/loader-utils/src/lib/worker-loader-utils/parse-with-worker.ts index d9cdf454eb..c15d4de574 100644 --- a/modules/loader-utils/src/lib/worker-loader-utils/parse-with-worker.ts +++ b/modules/loader-utils/src/lib/worker-loader-utils/parse-with-worker.ts @@ -4,7 +4,7 @@ import { WorkerMessagePayload, isBrowser } from '@loaders.gl/worker-utils'; -import type {Loader, LoaderOptions, LoaderContext} from '../../types'; +import type {Loader, LoaderOptions, LoaderContext} from '../../loader-types'; import {WorkerFarm, getWorkerURL} from '@loaders.gl/worker-utils'; /** @@ -34,7 +34,7 @@ export async function parseWithWorker( data: any, options?: LoaderOptions, context?: LoaderContext, - parseOnMainThread?: (arrayBuffer: ArrayBuffer, options: {[key: string]: any}) => Promise + parseOnMainThread?: (arrayBuffer: ArrayBuffer, options: {[key: string]: any}) => Promise ) { const name = loader.id; // TODO const url = getWorkerURL(loader, options); diff --git a/modules/loader-utils/src/loader-types.ts b/modules/loader-utils/src/loader-types.ts new file mode 100644 index 0000000000..46a5fb0101 --- /dev/null +++ b/modules/loader-utils/src/loader-types.ts @@ -0,0 +1,374 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import { + FetchLike, + TransformBatches /* , DataType, SyncDataType, BatchableDataType */ +} from './types'; +import {ReadableFile} from './lib/files/file'; + +// LOADERS + +/** + * Core Loader Options + */ +export type LoaderOptions = { + /** fetch options or a custom fetch function */ + fetch?: typeof fetch | FetchLike | RequestInit | null; + /** Do not throw on errors */ + nothrow?: boolean; + + /** loader selection, search first for supplied mimeType */ + mimeType?: string; + /** loader selection, provide fallback mimeType is server does not provide */ + fallbackMimeType?: string; + /** loader selection, avoid searching registered loaders */ + ignoreRegisteredLoaders?: boolean; + + // general + /** Experimental: Supply a logger to the parser */ + log?: any; + /** Force to load WASM libraries from local file system in NodeJS or from loaders.gl CDN in a web browser */ + useLocalLibraries?: boolean; + + // batched parsing + + /** Size of each batch. `auto` matches batches to size of incoming chunks */ + batchSize?: number | 'auto'; + /** Minimal amount of time between batches */ + batchDebounceMs?: number; + /** Stop loading after a given number of rows (compare SQL limit clause) */ + limit?: 0; + /** Experimental: Stop loading after reaching */ + _limitMB?: 0; + /** Generate metadata batches */ + metadata?: boolean; + /** Transforms to run on incoming batches */ + transforms?: TransformBatches[]; + + // workers + + /** CDN load workers from */ + CDN?: string | null; + /** Set to `false` to disable workers */ + worker?: boolean; + /** Number of concurrent workers (per loader) on desktop browser */ + maxConcurrency?: number; + /** Number of concurrent workers (per loader) on mobile browsers */ + maxMobileConcurrency?: number; + /** Set to `false` to prevent reuse workers */ + reuseWorkers?: boolean; + /** Whether to use workers under Node.js (experimental) */ + _nodeWorkers?: boolean; + /** set to 'test' to run local worker */ + _workerType?: string; + + /** @deprecated `options.batchType` removed, Use `options..type` instead */ + batchType?: 'row' | 'columnar' | 'arrow'; + /** @deprecated `options.throw removed`, Use `options.nothrow` instead */ + throws?: boolean; + /** @deprecated `options.dataType` no longer used */ + dataType?: never; + /** @deprecated `options.uri` no longer used */ + uri?: never; + /** @deprecated Use `options.fetch.method` */ + method?: never; + /** @deprecated Use `options.fetch.headers` */ + headers?: never; + /** @deprecated Use `options.fetch.body` */ + body?: never; + /** @deprecated Use `options.fetch.mode` */ + mode?: never; + /** @deprecated Use `options.fetch.credentials` */ + credentials?: never; + /** @deprecated Use `options.fetch.cache` */ + cache?: never; + /** @deprecated Use `options.fetch.redirect` */ + redirect?: never; + /** @deprecated Use `options.fetch.referrer` */ + referrer?: never; + /** @deprecated Use `options.fetch.referrerPolicy` */ + referrerPolicy?: never; + /** @deprecated Use `options.fetch.integrity` */ + integrity?: never; + /** @deprecated Use `options.fetch.keepalive` */ + keepalive?: never; + /** @deprecated Use `options.fetch.signal` */ + signal?: never; + + // Accept other keys (loader options objects, e.g. `options.csv`, `options.json` ...) + [loaderId: string]: unknown; +}; + +type PreloadOptions = { + [key: string]: unknown; +}; + +/** + * A worker loader definition that can be used with `@loaders.gl/core` functions + */ +export type Loader = { + /** The result type of this loader */ + dataType?: DataT; + /** The batched result type of this loader */ + batchType?: BatchT; + + /** Default Options */ + options: LoaderOptionsT; + /** Deprecated Options */ + deprecatedOptions?: Record>; + + /** Human readable name */ + name: string; + /** id should be the same as the field used in LoaderOptions */ + id: string; + /** module is used to generate worker threads, need to be the module directory name */ + module: string; + /** Version should be injected by build tools */ + version: string; + /** A boolean, or a URL */ + worker?: string | boolean; + // end Worker + + /** Which category does this loader belong to */ + category?: string; + /** File extensions that are potential matches with this loader. */ + extensions: string[]; + /** MIMETypes that indicate a match with this loader. @note Some MIMETypes are generic and supported by many loaders */ + mimeTypes: string[]; + + /** Is the input of this loader binary */ + binary?: boolean; + /** Is the input of this loader text */ + text?: boolean; + + /** Test some initial bytes of content to see if this loader might be a match */ + tests?: (((ArrayBuffer: ArrayBuffer) => boolean) | ArrayBuffer | string)[]; + + /** @deprecated */ + supported?: boolean; + /** @deprecated */ + testText?: (string: string) => boolean; +}; + +/** + * A "bundled" loader definition that can be used with `@loaders.gl/core` functions + * If a worker loader is supported it will also be supported. + */ +export type LoaderWithParser = Loader< + DataT, + BatchT, + LoaderOptionsT +> & { + /** Perform actions before load. @deprecated Not officially supported. */ + preload?: Preload; + /** Parse asynchronously and atomically from an arraybuffer */ + parse: ( + arrayBuffer: ArrayBuffer, + options?: LoaderOptionsT, + context?: LoaderContext + ) => Promise; + /** Parse asynchronously and atomically from a random access "file like" input */ + parseFile?: ( + file: ReadableFile, + options?: LoaderOptionsT, + context?: LoaderContext + ) => Promise; + /** Parse synchronously and atomically from an arraybuffer */ + parseSync?: ( + arrayBuffer: ArrayBuffer, + options?: LoaderOptionsT, + context?: LoaderContext + ) => DataT; + /** Parse atomically from a string asynchronously */ + parseText?: (text: string, options?: LoaderOptionsT, context?: LoaderContext) => Promise; + /** Parse atomically from a string synchronously */ + parseTextSync?: (text: string, options?: LoaderOptionsT, context?: LoaderContext) => DataT; + /** Parse batches of data from an iterator (e.g. fetch stream), return an iterator that yield parsed batches. */ + parseInBatches?: ( + iterator: AsyncIterable | Iterable, + options?: LoaderOptionsT, + context?: LoaderContext + ) => AsyncIterable; + /** For random access, file like sources, source that don't integrate with fetch. */ + parseFileInBatches?: ( + file: ReadableFile, + options?: LoaderOptionsT, + context?: LoaderContext + ) => AsyncIterable; +}; + +/** + * A Loader context is provided as a third parameters to a loader object's + * parse functions when that loader is called by other loaders rather then + * directly by the application. + * + * - The context object allows the subloaders to be aware of the parameters and + * options that the application provided in the top level call. + * - The context also providedsaccess to parse functions so that the subloader + * does not need to include the core module. + * - In addition, the context's parse functions may also redirect loads from worker + * threads back to main thread. + */ +export type LoaderContext = { + /** Loader list provided to top-level loader call plus any sub loaders */ + loaders?: Loader[] | null; + /** If URL is available. */ + url?: string; + /** the file name component of the URL (leading path and query string removed) */ + filename?: string; + /** the directory name component of the URL (leading path excluding file name and query string) */ + baseUrl?: string; + /** Query string (characters after `?`) */ + queryString?: string; + + /** Provides access to any application overrides of fetch() */ + fetch: typeof fetch | FetchLike; + + /** TBD */ + response?: Response; + + /** + * Parse function for subloaders. Avoids importing `core`. In workers, may redirect to main thread + */ + _parse: ( + arrayBuffer: ArrayBuffer, + loaders?: Loader | Loader[] | LoaderOptions, + options?: LoaderOptions, + context?: LoaderContext + ) => Promise; + + /** + * ParseSync function. Avoids importing `core`. In workers, may redirect to main thread + * @deprecated Do not call directly, use `parseSyncFromContext` instead + */ + _parseSync?: ( + arrayBuffer: ArrayBuffer, + loaders?: Loader | Loader[] | LoaderOptions, + options?: LoaderOptions, + context?: LoaderContext + ) => any; + + /** + * ParseInBatches function. Avoids importing `core`. + * @deprecated Do not call directly, use `parseInBatchesFromContext` instead + */ + _parseInBatches?: ( + iterator: AsyncIterable | Iterable | Response, + loaders?: Loader | Loader[] | LoaderOptions, + options?: LoaderOptions, + context?: LoaderContext + ) => AsyncIterable | Promise>; +}; + +// type Parse = ( +// arrayBuffer: ArrayBuffer, +// options?: LoaderOptions, +// context?: LoaderContext +// ) => Promise; +// type ParseSync = ( +// arrayBuffer: ArrayBuffer, +// options?: LoaderOptions, +// context?: LoaderContext +// ) => any; +// type ParseText = (text: string, options?: LoaderOptions) => Promise; +// type ParseTextSync = (text: string, options?: LoaderOptions) => any; +// type ParseInBatches = ( +// iterator: AsyncIterable | Iterable, +// options?: LoaderOptions, +// context?: LoaderContext +// ) => AsyncIterable; +// type ParseFileInBatches = ( +// file: Blob, +// options?: LoaderOptions, +// context?: LoaderContext +// ) => AsyncIterable; + +type Preload = (url: string, options?: PreloadOptions) => any; + +/** Typescript helper to extract options type from a generic loader type */ +export type LoaderOptionsType = T extends Loader + ? Options + : never; +/** Typescript helper to extract data type from a generic loader type */ +export type LoaderReturnType = T extends Loader + ? Return + : never; +/** Typescript helper to extract batch type from a generic loader type */ +export type LoaderBatchType = T extends Loader ? Batch : never; + +/** + * Parses `data` asynchronously using the supplied loader, parse function provided via the loader context + */ +export async function parseFromContext< + LoaderT extends Loader, + OptionsT extends LoaderOptions = LoaderOptionsType +>( + data: ArrayBuffer, + loader: LoaderT, + options: OptionsT | undefined, + context: LoaderContext +): Promise>; + +/** + * Parses `data` asynchronously by matching one of the supplied loader + */ +export async function parseFromContext( + data: ArrayBuffer, + loaders: Loader[], + options: LoaderOptions | undefined, + context: LoaderContext +): Promise; + +/** + * Parses `data` using a specified loader + * @param data + * @param loaders + * @param options + * @param context + */ +// implementation signature +export async function parseFromContext( + data: ArrayBuffer, + loaders: Loader | Loader[], + options: LoaderOptions | undefined, + context: LoaderContext +): Promise { + return context._parse(data, loaders, options, context); +} + +/** + * Parses `data` synchronously using the specified loader, parse function provided via the loader context + */ +export function parseSyncFromContext< + LoaderT extends Loader, + OptionsT extends LoaderOptions = LoaderOptionsType +>( + data: ArrayBuffer, + loader: LoaderT, + options: OptionsT | undefined, + context: LoaderContext +): LoaderReturnType { + if (!context._parseSync) { + throw new Error('parseSync'); + } + return context._parseSync(data, loader, options, context); +} + +/** + * Parses `data` synchronously using a specified loader, parse function provided via the loader context + */ +export async function parseInBatchesFromContext< + LoaderT extends Loader, + OptionsT extends LoaderOptions = LoaderOptionsType +>( + data: Iterable | AsyncIterable | Response, + loader: LoaderT, + options: OptionsT | undefined, + context: LoaderContext +): Promise>> { + if (!context._parseInBatches) { + throw new Error('parseInBatches'); + } + return context._parseInBatches(data, loader, options, context); +} diff --git a/modules/loader-utils/src/service-types.ts b/modules/loader-utils/src/service-types.ts new file mode 100644 index 0000000000..d1e93e6ecf --- /dev/null +++ b/modules/loader-utils/src/service-types.ts @@ -0,0 +1,13 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {ImageSource, ImageSourceProps} from './lib/sources/image-source'; + +/** Export interface source */ +export interface Service { + source?: SourceT; + props?: SourcePropsT; + type: string; + testURL: (url: string) => boolean; + create(props: SourcePropsT): SourceT; +} diff --git a/modules/loader-utils/src/types.ts b/modules/loader-utils/src/types.ts index d0963f2c3c..c9bb70da79 100644 --- a/modules/loader-utils/src/types.ts +++ b/modules/loader-utils/src/types.ts @@ -7,8 +7,6 @@ export type TypedIntArray = | Int16Array | Uint16Array | Int32Array - | Uint32Array - | Int32Array | Uint32Array; export type TypedFloatArray = Uint16Array | Float32Array | Float64Array; @@ -24,8 +22,6 @@ export type TypedArrayConstructor = | Uint16ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor - | Int32ArrayConstructor - | Uint32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor; @@ -39,324 +35,9 @@ export type NumberArray = number[] | TypedArray; export type NumericArray = number[] | TypedArray; -type FetchLike = (url: string, options?: RequestInit) => Promise; - -// LOADERS - -/** - * Core Loader Options - */ -export type LoaderOptions = { - /** fetch options or a custom fetch function */ - fetch?: typeof fetch | FetchLike | RequestInit | null; - /** Do not throw on errors */ - nothrow?: boolean; - - /** loader selection, search first for supplied mimeType */ - mimeType?: string; - /** loader selection, provide fallback mimeType is server does not provide */ - fallbackMimeType?: string; - /** loader selection, avoid searching registered loaders */ - ignoreRegisteredLoaders?: boolean; - - // general - /** Experimental: Supply a logger to the parser */ - log?: any; - - // batched parsing - - /** Size of each batch. `auto` matches batches to size of incoming chunks */ - batchSize?: number | 'auto'; - /** Minimal amount of time between batches */ - batchDebounceMs?: number; - /** Stop loading after a given number of rows (compare SQL limit clause) */ - limit?: 0; - /** Experimental: Stop loading after reaching */ - _limitMB?: 0; - /** Generate metadata batches */ - metadata?: boolean; - /** Transforms to run on incoming batches */ - transforms?: TransformBatches[]; - - // workers - - /** CDN load workers from */ - CDN?: string | null; - /** Set to `false` to disable workers */ - worker?: boolean; - /** Number of concurrent workers (per loader) on desktop browser */ - maxConcurrency?: number; - /** Number of concurrent workers (per loader) on mobile browsers */ - maxMobileConcurrency?: number; - /** Set to `false` to prevent reuse workers */ - reuseWorkers?: boolean; - /** Whether to use workers under Node.js (experimental) */ - _nodeWorkers?: boolean; - /** set to 'test' to run local worker */ - _workerType?: string; - - /** @deprecated `options.batchType` removed, Use `options..type` instead */ - batchType?: 'row' | 'columnar' | 'arrow'; - /** @deprecated `options.throw removed`, Use `options.nothrow` instead */ - throws?: boolean; - /** @deprecated `options.dataType` no longer used */ - dataType?: never; - /** @deprecated `options.uri` no longer used */ - uri?: never; - /** @deprecated `options.method` removed. Use `options.fetch.method` */ - method?: never; - /** @deprecated `options.headers` removed. Use `options.fetch.headers` */ - headers?: never; - /** @deprecated `options.body` removed. Use `options.fetch.body` */ - body?: never; - /** @deprecated `options.mode` removed. Use `options.fetch.mode` */ - mode?: never; - /** @deprecated `options.credentials` removed. Use `options.fetch.credentials` */ - credentials?: never; - /** @deprecated `options.cache` removed. Use `options.fetch.cache` */ - cache?: never; - /** @deprecated `options.redirect` removed. Use `options.fetch.redirect` */ - redirect?: never; - /** @deprecated `options.referrer` removed. Use `options.fetch.referrer` */ - referrer?: never; - /** @deprecated `options.referrerPolicy` removed. Use `options.fetch.referrerPolicy` */ - referrerPolicy?: never; - /** @deprecated `options.integrity` removed. Use `options.fetch.integrity` */ - integrity?: never; - /** @deprecated `options.keepalive` removed. Use `options.fetch.keepalive` */ - keepalive?: never; - /** @deprecated `options.signal` removed. Use `options.fetch.signal` */ - signal?: never; - - // Accept other keys (loader options objects, e.g. `options.csv`, `options.json` ...) - [loaderId: string]: unknown; -}; - -type PreloadOptions = { - [key: string]: unknown; -}; - -/** - * A worker loader definition that can be used with `@loaders.gl/core` functions - */ -export type Loader = { - // Types - dataType?: DataT; - batchType?: BatchT; - - /** Default Options */ - options: LoaderOptionsT; - /** Deprecated Options */ - deprecatedOptions?: Record>; - - // Worker - name: string; - /** id should be the same as the field used in LoaderOptions */ - id: string; - /** module is used to generate worker threads, need to be the module directory name */ - module: string; - /** Version should be injected by build tools */ - version: string; - /** A boolean, or a URL */ - worker?: string | boolean; - // end Worker - - /** Which category does this loader belong to */ - category?: string; - /** What extensions does this loader generate */ - extensions: string[]; - mimeTypes: string[]; - - binary?: boolean; - text?: boolean; - - tests?: (((ArrayBuffer: ArrayBuffer) => boolean) | ArrayBuffer | string)[]; - - // TODO - deprecated - supported?: boolean; - testText?: (string: string) => boolean; -}; +// FETCH -/** - * A "bundled" loader definition that can be used with `@loaders.gl/core` functions - * If a worker loader is supported it will also be supported. - */ -export type LoaderWithParser = Loader< - DataT, - BatchT, - LoaderOptionsT -> & { - // TODO - deprecated - testText?: (string: string) => boolean; - - parse: ( - arrayBuffer: ArrayBuffer, - options?: LoaderOptionsT, - context?: LoaderContext - ) => Promise; - preload?: Preload; - parseSync?: ( - arrayBuffer: ArrayBuffer, - options?: LoaderOptionsT, - context?: LoaderContext - ) => DataT; - parseText?: (text: string, options?: LoaderOptionsT) => Promise; - parseTextSync?: (text: string, options?: LoaderOptionsT) => DataT; - parseInBatches?: ( - iterator: AsyncIterable | Iterable, - options?: LoaderOptionsT, - context?: LoaderContext - ) => AsyncIterable; - parseFileInBatches?: ( - file: Blob, - options?: LoaderOptionsT, - context?: LoaderContext - ) => AsyncIterable; -}; - -/** - * A Loader context is provided as a third parameters to a loader object's - * parse functions when that loader is called by other loaders rather then - * directly by the application. - * - * - The context object allows the subloaders to be aware of the parameters and - * options that the application provided in the top level call. - * - The context also providedsaccess to parse functions so that the subloader - * does not need to include the core module. - * - In addition, the context's parse functions may also redirect loads from worker - * threads back to main thread. - */ -export type LoaderContext = { - loaders?: Loader[] | null; - /** If URL is available. */ - url?: string; - /** the file name component of the URL (leading path and query string removed) */ - filename?: string; - /** the directory name component of the URL (leading path excluding file name and query string) */ - baseUrl?: string; - /** Query string (characters after `?`) */ - queryString?: string; - - /** Provides access to any application overrides of fetch() */ - fetch: typeof fetch | FetchLike; - /** TBD */ - response?: Response; - /** Parse function. Use instead of importing `core`. In workers, may redirect to main thread */ - parse: ( - arrayBuffer: ArrayBuffer, - loaders?: Loader | Loader[] | LoaderOptions, - options?: LoaderOptions, - context?: LoaderContext - ) => Promise; - /** ParseSync function. Use instead of importing `core`. In workers, may redirect to main thread */ - parseSync?: ( - arrayBuffer: ArrayBuffer, - loaders?: Loader | Loader[] | LoaderOptions, - options?: LoaderOptions, - context?: LoaderContext - ) => any; - /** ParseInBatches function. Use instead of importing `core`. */ - parseInBatches?: ( - iterator: AsyncIterable | Iterable, - loaders?: Loader | Loader[] | LoaderOptions, - options?: LoaderOptions, - context?: LoaderContext - ) => AsyncIterable | Promise>; -}; - -// type Parse = ( -// arrayBuffer: ArrayBuffer, -// options?: LoaderOptions, -// context?: LoaderContext -// ) => Promise; -// type ParseSync = ( -// arrayBuffer: ArrayBuffer, -// options?: LoaderOptions, -// context?: LoaderContext -// ) => any; -// type ParseText = (text: string, options?: LoaderOptions) => Promise; -// type ParseTextSync = (text: string, options?: LoaderOptions) => any; -// type ParseInBatches = ( -// iterator: AsyncIterable | Iterable, -// options?: LoaderOptions, -// context?: LoaderContext -// ) => AsyncIterable; -// type ParseFileInBatches = ( -// file: Blob, -// options?: LoaderOptions, -// context?: LoaderContext -// ) => AsyncIterable; - -type Preload = (url: string, options?: PreloadOptions) => any; - -/** Typescript helper to extract options type from a generic loader type */ -export type LoaderOptionsType = T extends Loader - ? Options - : never; -/** Typescript helper to extract data type from a generic loader type */ -export type LoaderReturnType = T extends Loader - ? Return - : never; -/** Typescript helper to extract batch type from a generic loader type */ -export type LoaderBatchType = T extends Loader ? Batch : never; - -// WRITERS - -/** Options for writers */ -export type WriterOptions = { - /** worker source. If is set will be used instead of loading worker from the Internet */ - souce?: string | null; - /** writer-specific options */ - [writerId: string]: any; -}; - -/** - * A writer definition that can be used with `@loaders.gl/core` functions - */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export type Writer = { - name: string; - - id: string; - module: string; - version: string; - worker?: string | boolean; - - // TODO - are these are needed? - extensions?: string[]; - mimeTypes?: string[]; - binary?: boolean; - text?: boolean; - - options: WriterOptionsT; - deprecatedOptions?: Record; - - // encodeText?: EncodeText; - // encode?: Encode; - encodeSync?: EncodeSync; - // encodeInBatches?: EncodeInBatches; - encodeURLtoURL?: EncodeURLtoURL; - - encode?(data: DataT, options?: WriterOptionsT): Promise; - encodeText?(table: DataT, options?: WriterOptionsT): Promise | string; - encodeInBatches?(data: AsyncIterable, options?: WriterOptionsT): AsyncIterable; -}; - -// type Encode = (data: any, options?: WriterOptions) => Promise; -type EncodeSync = (data: any, options?: WriterOptions) => ArrayBuffer; -// TODO -// type EncodeText = Function; -// type EncodeInBatches = Function; -type EncodeURLtoURL = ( - inputUrl: string, - outputUrl: string, - options?: WriterOptions -) => Promise; - -/** Typescript helper to extract the writer options type from a generic writer type */ -export type WriterOptionsType = T extends Writer - ? Options - : never; +export type FetchLike = (url: string, options?: RequestInit) => Promise; // MISC TYPES @@ -384,37 +65,3 @@ export type BatchableDataType = | Iterable | AsyncIterable | Promise>; - -/** - * A FileSystem interface can encapsulate a FileList, a ZipFile, a GoogleDrive etc. - */ -export interface IFileSystem { - /** - * Return a list of file names - * @param dirname directory name. file system root directory if omitted - */ - readdir(dirname?: string, options?: {recursive?: boolean}): Promise; - - /** - * Gets information from a local file from the filesystem - * @param filename file name to stat - * @param options currently unused - * @throws if filename is not in local filesystem - */ - stat(filename: string, options?: object): Promise<{size: number}>; - - /** - * Fetches a local file from the filesystem (or a URL) - * @param filename - * @param options - */ - fetch(filename: string, options?: object): Promise; -} - -type ReadOptions = {buffer?: ArrayBuffer; offset?: number; length?: number; position?: number}; -export interface IRandomAccessReadFileSystem extends IFileSystem { - open(path: string, flags: string | number, mode?: any): Promise; - close(fd: any): Promise; - fstat(fd: any): Promise; - read(fd: any, options?: ReadOptions): Promise<{bytesRead: number; buffer: Buffer}>; -} diff --git a/modules/loader-utils/src/writer-types.ts b/modules/loader-utils/src/writer-types.ts new file mode 100644 index 0000000000..feea60ac55 --- /dev/null +++ b/modules/loader-utils/src/writer-types.ts @@ -0,0 +1,80 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +// WRITERS + +/** Options for writers */ +export type WriterOptions = { + /** worker source. If is set will be used instead of loading worker from the Internet */ + source?: string | null; + /** Force to load WASM libraries from local file system in NodeJS or from loaders.gl CDN in a web browser */ + useLocalLibraries?: boolean; + /** writer-specific options */ + [writerId: string]: any; +}; + +/** + * A writer definition that can be used with `@loaders.gl/core` functions + */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export type Writer = { + /** The result type of this loader */ + dataType?: DataT; + /** The batched result type of this loader */ + batchType?: BatchT; + + /** Human readable name */ + name: string; + /** id should be the same as the field used in LoaderOptions */ + id: string; + /** module is used to generate worker threads, need to be the module directory name */ + module: string; + /** Version should be injected by build tools */ + version: string; + /** A boolean, or a URL */ + worker?: string | boolean; + // end Worker + + /** Which category does this loader belong to */ + category?: string; + /** File extensions that are potential matches with this loader. */ + extensions: string[]; + /** MIMETypes that indicate a match with this loader. @note Some MIMETypes are generic and supported by many loaders */ + mimeTypes?: string[]; + + /** Is the input of this loader binary */ + binary?: boolean; + /** Is the input of this loader text */ + text?: boolean; + + options: WriterOptionsT; + deprecatedOptions?: Record; +}; + +/** + * A writer definition that can be used with `@loaders.gl/core` functions + */ +export type WriterWithEncoder< + DataT = unknown, + BatchT = unknown, + WriterOptionsT = WriterOptions +> = Writer & { + encode?(data: DataT, options?: WriterOptionsT): Promise; + encodeSync?(data: DataT, options?: WriterOptionsT): ArrayBuffer; + + encodeText?(table: DataT, options?: WriterOptionsT): Promise; + encodeTextSync?(table: DataT, options?: WriterOptionsT): string; + + encodeInBatches?(data: AsyncIterable, options?: WriterOptionsT): AsyncIterable; + + encodeURLtoURL?: ( + inputUrl: string, + outputUrl: string, + options?: WriterOptionsT + ) => Promise; +}; + +/** Typescript helper to extract the writer options type from a generic writer type */ +export type WriterOptionsType = T extends Writer + ? Options + : never; diff --git a/modules/loader-utils/test/index.ts b/modules/loader-utils/test/index.ts index 4ad1fefd61..efea14b5f0 100644 --- a/modules/loader-utils/test/index.ts +++ b/modules/loader-utils/test/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import './lib/binary-utils/array-buffer-utils.spec'; import './lib/binary-utils/binary-copy-utils.spec'; @@ -15,4 +16,10 @@ import './lib/path-utils/path.spec'; import './lib/request-utils/request-scheduler.spec'; +// import './lib/files/node-file-facade.spec'; +// import './lib/filesystems/node-filesystem-facade.spec'; + +import './lib/file-provider/data-view-file.spec'; +import './lib/file-provider/file-handle-file.spec'; + import './lib/worker-loader-utils/parse-with-worker.spec'; diff --git a/modules/loader-utils/test/lib/binary-utils/array-buffer-utils.spec.js b/modules/loader-utils/test/lib/binary-utils/array-buffer-utils.spec.js index e4b96d95df..e0da32efb2 100644 --- a/modules/loader-utils/test/lib/binary-utils/array-buffer-utils.spec.js +++ b/modules/loader-utils/test/lib/binary-utils/array-buffer-utils.spec.js @@ -14,8 +14,11 @@ test('toArrayBuffer', (t) => { buffer = toArrayBuffer(typedArray.buffer); t.ok(buffer instanceof ArrayBuffer, 'returns ArrayBuffer from ArrayBuffer'); - buffer = toArrayBuffer(Buffer.from(typedArray.buffer)); - t.ok(buffer instanceof ArrayBuffer, 'returns ArrayBuffer from Buffer'); + // TODO - skipping as this uses Node.js Buffers + // if (!isBrowser) { + // buffer = toArrayBuffer(Buffer.from(typedArray.buffer)); + // t.ok(buffer instanceof ArrayBuffer, 'returns ArrayBuffer from Buffer'); + // } buffer = toArrayBuffer('0123'); t.ok(buffer instanceof ArrayBuffer, 'returns ArrayBuffer from string'); diff --git a/modules/loader-utils/test/lib/file-provider/data-view-file.spec.ts b/modules/loader-utils/test/lib/file-provider/data-view-file.spec.ts new file mode 100644 index 0000000000..5155db97d3 --- /dev/null +++ b/modules/loader-utils/test/lib/file-provider/data-view-file.spec.ts @@ -0,0 +1,40 @@ +import test from 'tape-promise/tape'; +import {DATA_ARRAY} from '@loaders.gl/i3s/test/data/test.zip'; +import {DataViewFile, isBrowser} from '@loaders.gl/loader-utils'; + +// TODO - try harder to avoid use of Node.js Buffer +export const getSignature = () => new Uint8Array([0x50, 0x4b, 0x03, 0x04]); + +// TODO remove browser exclude when DataFileView no longer uses buffer +if (!isBrowser) { + test('DataViewFile#slice', async (t) => { + const provider = new DataViewFile(new DataView(DATA_ARRAY.buffer)); + const slice = await provider.slice(0n, 4n); + t.deepEqual(new Uint8Array(slice), getSignature()); + t.end(); + }); + + test('DataViewFile#getUint8', async (t) => { + const provider = new DataViewFile(new DataView(DATA_ARRAY.buffer)); + t.equals(await provider.getUint8(0n), 80); + t.end(); + }); + + test('DataViewFile#getUint16', async (t) => { + const provider = new DataViewFile(new DataView(DATA_ARRAY.buffer)); + t.equals(await provider.getUint16(0n), 19280); + t.end(); + }); + + test('DataViewFile#getUint32', async (t) => { + const provider = new DataViewFile(new DataView(DATA_ARRAY.buffer)); + t.equals(await provider.getUint32(0n), 67324752); + t.end(); + }); + + test('DataViewFile#getBigUint64', async (t) => { + const provider = new DataViewFile(new DataView(DATA_ARRAY.buffer)); + t.equals(await provider.getBigUint64(0n), 563035920091984n); + t.end(); + }); +} diff --git a/modules/loader-utils/test/lib/file-provider/file-handle-file.spec.js b/modules/loader-utils/test/lib/file-provider/file-handle-file.spec.js new file mode 100644 index 0000000000..78940ae884 --- /dev/null +++ b/modules/loader-utils/test/lib/file-provider/file-handle-file.spec.js @@ -0,0 +1,49 @@ +import test from 'tape-promise/tape'; +import {localHeaderSignature as signature} from '@loaders.gl/zip'; +import {isBrowser} from '@loaders.gl/core'; + +import {FileHandleFile} from '@loaders.gl/loader-utils'; + +const SLPKUrl = 'modules/i3s/test/data/DA12_subset.slpk'; + +test('FileHandleFile#slice', async (t) => { + if (!isBrowser) { + const provider = new FileHandleFile(SLPKUrl); + const slice = await provider.slice(0n, 4n); + t.deepEqual(new Uint8Array(slice), signature); + } + t.end(); +}); + +test('FileHandleFile#getUint8', async (t) => { + if (!isBrowser) { + const provider = new FileHandleFile(SLPKUrl); + t.equals(await provider.getUint8(0n), 80); + t.end(); + } + t.end(); +}); + +test('FileHandleFile#getUint16', async (t) => { + if (!isBrowser) { + const provider = new FileHandleFile(SLPKUrl); + t.equals(await provider.getUint16(0n), 19280); + } + t.end(); +}); + +test('FileHandleFile#getUint32', async (t) => { + if (!isBrowser) { + const provider = new FileHandleFile(SLPKUrl); + t.equals(await provider.getUint32(0n), 67324752); + } + t.end(); +}); + +test('FileHandleFile#getBigUint64', async (t) => { + if (!isBrowser) { + const provider = new FileHandleFile(SLPKUrl); + t.equals(await provider.getBigUint64(0n), 193340853072n); + } + t.end(); +}); diff --git a/modules/loader-utils/test/lib/iterators/async-iteration.spec.ts b/modules/loader-utils/test/lib/iterators/async-iteration.spec.ts index 8ead3720f0..b3194fe4a0 100644 --- a/modules/loader-utils/test/lib/iterators/async-iteration.spec.ts +++ b/modules/loader-utils/test/lib/iterators/async-iteration.spec.ts @@ -98,6 +98,7 @@ test('async-iterator#makeNumberedLineIterator', async (t) => { test('async-iterator#parseNDJSONInBatches', async (t) => { let id = 0; for await (const batch of parseNDJSONInBatches(asyncNDJson())) { + // @ts-expect-error const obj = batch.data[0]; t.equals(typeof obj, 'object', 'async iterator yields object'); /* eslint-disable */ diff --git a/modules/loader-utils/test/lib/iterators/make-iterator.spec.ts b/modules/loader-utils/test/lib/iterators/make-iterator.spec.ts index 3cb105d38c..122963fe4e 100644 --- a/modules/loader-utils/test/lib/iterators/make-iterator.spec.ts +++ b/modules/loader-utils/test/lib/iterators/make-iterator.spec.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import test from 'tape-promise/tape'; import {fetchFile, makeIterator} from '@loaders.gl/core'; import {concatenateArrayBuffersAsync, makeTextEncoderIterator} from '@loaders.gl/loader-utils'; @@ -64,7 +67,7 @@ const DATA_URL = '@loaders.gl/draco/test/data/raw-attribute-buffers/lidar-positi test('makeIterator(fetch)#async iterate', async (t) => { const response = await fetchFile(DATA_URL); - const stream = await response.body; + const stream = response.body; t.ok(stream); if (stream) { diff --git a/modules/loader-utils/test/lib/iterators/make-transform-iterator.spec.ts b/modules/loader-utils/test/lib/iterators/make-transform-iterator.spec.ts index 2c82acc5fa..2d9a29abb0 100644 --- a/modules/loader-utils/test/lib/iterators/make-transform-iterator.spec.ts +++ b/modules/loader-utils/test/lib/iterators/make-transform-iterator.spec.ts @@ -80,7 +80,7 @@ test('byteLengthTransform#non-streaming', async (t) => { const response = await fetchFile(SHAPEFILE_URL); let iterator = makeIterator(response); - iterator = crc32.hashBatches(iterator); + iterator = crc32.hashBatches(iterator, 'base64'); const batchIterator = await parseInBatches(iterator, ShapefileLoader); for await (const batch of batchIterator) { diff --git a/modules/loader-utils/test/lib/worker-loader-utils/parse-with-worker.spec.js b/modules/loader-utils/test/lib/worker-loader-utils/parse-with-worker.spec.js index 00a742b5fc..f2931e1d85 100644 --- a/modules/loader-utils/test/lib/worker-loader-utils/parse-with-worker.spec.js +++ b/modules/loader-utils/test/lib/worker-loader-utils/parse-with-worker.spec.js @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import test from 'tape-promise/tape'; import {WorkerPool} from '@loaders.gl/worker-utils'; import {toArrayBuffer, parseWithWorker} from '@loaders.gl/loader-utils'; @@ -19,21 +22,12 @@ test('parseWithWorker', async (t) => { const testOptions = { _workerType: 'test', reuseWorkers: false, - null: {echoParameters: true}, custom: 'custom' }; - const testContext = {response: testResponse, fetch, parse: async (arrayBuffer) => arrayBuffer}; - const {arrayBuffer, options, context} = await parseWithWorker( - NullWorkerLoader, - testData, - testOptions, - testContext - ); + const testContext = {response: testResponse, fetch, _parse: async (arrayBuffer) => arrayBuffer}; + const result = await parseWithWorker(NullWorkerLoader, testData, testOptions, testContext); - t.deepEquals(arrayBuffer, testData, 'data parsed with relative worker url'); - t.equals(options.custom, 'custom', 'options parsed with relative worker url'); - t.ok(context, 'context parsed with relative worker url'); - t.ok(context.response, 'context response parsed with relative worker url'); + t.equal(result, null); t.end(); }); diff --git a/modules/math/package.json b/modules/math/package.json index 11edd3e0c7..1b138e1dac 100644 --- a/modules/math/package.json +++ b/modules/math/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/math", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Experimental math classes for loaders.gl", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -19,8 +20,15 @@ "glTF" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -32,9 +40,9 @@ "pre-build": "echo \"Nothing to build in @loaders.gl/math\"" }, "dependencies": { - "@loaders.gl/images": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@math.gl/core": "^3.5.1" + "@loaders.gl/images": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@math.gl/core": "^4.0.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/math/src/geometry/utils/coordinates.ts b/modules/math/src/geometry/utils/coordinates.ts new file mode 100644 index 0000000000..6e595aa0cd --- /dev/null +++ b/modules/math/src/geometry/utils/coordinates.ts @@ -0,0 +1,7 @@ +/** + * Handle UVs if they are out of range [0,1]. + * @param n + */ +export function emod(n: number): number { + return ((n % 1) + 1) % 1; +} diff --git a/modules/math/src/index.ts b/modules/math/src/index.ts index 267e9c8893..cc164de5e0 100644 --- a/modules/math/src/index.ts +++ b/modules/math/src/index.ts @@ -5,8 +5,6 @@ export type TypedArrayConstructor = | Uint16ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor - | Int32ArrayConstructor - | Uint32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor; @@ -48,3 +46,5 @@ export { decompressTextureCoordinates, zigZagDeltaDecode } from './geometry/compression/attribute-compression'; + +export {emod} from './geometry/utils/coordinates'; diff --git a/modules/math/test/geometry/compression/attribute-compression.spec.js b/modules/math/test/geometry/compression/attribute-compression.spec.js index 0c6ca6be2a..eedd0a15ad 100644 --- a/modules/math/test/geometry/compression/attribute-compression.spec.js +++ b/modules/math/test/geometry/compression/attribute-compression.spec.js @@ -88,28 +88,28 @@ it('throws oct encode result undefined', () => { it('throws oct encode non unit vector', () => { const nonUnitLengthVector = new Vector3(2.0, 0.0, 0.0); const result = new Vector2(); - expect(() => octEncode(nonUnitLengthVector, result)).toThrow(); + expect(() => octEncode(nonUnitLengthVector, result)).toThrow(''); }); it('throws oct encode zero length vector', () => { const result = new Vector2(); - expect(() => octEncode(Vector3.ZERO, result)).toThrow(); + expect(() => octEncode(Vector3.ZERO, result)).toThrow(''); }); it('throws oct decode result undefined', () => { const result = undefined; // @ts-ignore - expect(() => octDecode(0, 0, result)).toThrow(); + expect(() => octDecode(0, 0, result)).toThrow(''); }); it('throws oct decode x out of bounds', () => { const result = new Vector3(); - expect(() => octDecode(256, 0, result)).toThrow(); + expect(() => octDecode(256, 0, result)).toThrow(''); }); it('throws oct decode y out of bounds', () => { const result = new Vector3(); - expect(() => octDecode(0, 256, result)).toThrow(); + expect(() => octDecode(0, 256, result)).toThrow(''); }); it('throws 4-component oct decode out of bounds', () => { @@ -756,11 +756,11 @@ it('throws when zigZagDeltaDecode has an undefined vBuffer', () => { }); it('throws when zigZagDeltaDecode has unequal uBuffer and vBuffer length', () => { - expect(() => zigZagDeltaDecode(new Uint16Array(10), new Uint16Array(11))).toThrow(); + expect(() => zigZagDeltaDecode(new Uint16Array(10), new Uint16Array(11))).toThrow(''); }); it('throws when zigZagDeltaDecode has unequal uBuffer, vBuffer, and heightBuffer length', () => { expect(() => zigZagDeltaDecode(new Uint16Array(10), new Uint16Array(10), new Uint16Array(11)) - ).toThrow(); + ).toThrow(''); }); diff --git a/modules/mvt/package.json b/modules/mvt/package.json index ee0a3d4751..46148adb0c 100644 --- a/modules/mvt/package.json +++ b/modules/mvt/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/mvt", "description": "Loader for Mapbox Vector Tiles", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -19,8 +20,15 @@ "Mapbox Vector Tiles" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -28,15 +36,16 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-worker && npm run build-worker --env.dev && npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-worker && npm run build-worker --env.dev && npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/mvt-worker.ts --bundle --outfile=dist/mvt-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { - "@loaders.gl/gis": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", - "@math.gl/polygon": "^3.5.1", + "@loaders.gl/gis": "4.0.3", + "@loaders.gl/images": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "@math.gl/polygon": "^4.0.0", "pbf": "^3.2.1" }, "devDependencies": { diff --git a/modules/mvt/src/bundle.ts b/modules/mvt/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/mvt/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/mvt/src/index.ts b/modules/mvt/src/index.ts index 47bfef5065..905fb2e4f9 100644 --- a/modules/mvt/src/index.ts +++ b/modules/mvt/src/index.ts @@ -1,7 +1,15 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors +export type {MVTLoaderOptions} from './lib/types'; export {MVTLoader, MVTWorkerLoader} from './mvt-loader'; +export type {TileJSON} from './lib/parse-tilejson'; +export type {TileJSONLoaderOptions} from './tilejson-loader'; +export {TileJSONLoader} from './tilejson-loader'; + +export {MVTSource} from './mvt-source'; + // GeoJSONTiler export type {GeoJSONTilerOptions} from './lib/geojson-tiler/geojson-tiler'; diff --git a/modules/mvt/src/lib/geojson-tiler/clip.ts b/modules/mvt/src/lib/geojson-tiler/clip.ts index a972b2eba3..24ed73bec1 100644 --- a/modules/mvt/src/lib/geojson-tiler/clip.ts +++ b/modules/mvt/src/lib/geojson-tiler/clip.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license import type {GeoJSONTileFeature} from './tile'; diff --git a/modules/mvt/src/lib/geojson-tiler/convert.ts b/modules/mvt/src/lib/geojson-tiler/convert.ts index f77948fda2..e0926020f9 100644 --- a/modules/mvt/src/lib/geojson-tiler/convert.ts +++ b/modules/mvt/src/lib/geojson-tiler/convert.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license /* eslint-disable */ diff --git a/modules/mvt/src/lib/geojson-tiler/feature.ts b/modules/mvt/src/lib/geojson-tiler/feature.ts index e5e705c474..742be492f1 100644 --- a/modules/mvt/src/lib/geojson-tiler/feature.ts +++ b/modules/mvt/src/lib/geojson-tiler/feature.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license import {GeoJSONTileFeature} from './tile'; diff --git a/modules/mvt/src/lib/geojson-tiler/geojson-tiler.ts b/modules/mvt/src/lib/geojson-tiler/geojson-tiler.ts index 44586b4caf..d717691e49 100644 --- a/modules/mvt/src/lib/geojson-tiler/geojson-tiler.ts +++ b/modules/mvt/src/lib/geojson-tiler/geojson-tiler.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license /* eslint-disable no-console, no-continue */ diff --git a/modules/mvt/src/lib/geojson-tiler/simplify.ts b/modules/mvt/src/lib/geojson-tiler/simplify.ts index 15d4b58d43..3dd7496da3 100644 --- a/modules/mvt/src/lib/geojson-tiler/simplify.ts +++ b/modules/mvt/src/lib/geojson-tiler/simplify.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license /** diff --git a/modules/mvt/src/lib/geojson-tiler/tile.ts b/modules/mvt/src/lib/geojson-tiler/tile.ts index 8deaf54fb3..e49c15cae7 100644 --- a/modules/mvt/src/lib/geojson-tiler/tile.ts +++ b/modules/mvt/src/lib/geojson-tiler/tile.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license // import type {Feature} from '@loaders.gl/schema'; diff --git a/modules/mvt/src/lib/geojson-tiler/transform.ts b/modules/mvt/src/lib/geojson-tiler/transform.ts index 976f6bc144..e0b106d0e7 100644 --- a/modules/mvt/src/lib/geojson-tiler/transform.ts +++ b/modules/mvt/src/lib/geojson-tiler/transform.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license import type {GeoJSONTile} from './tile'; diff --git a/modules/mvt/src/lib/geojson-tiler/wrap.ts b/modules/mvt/src/lib/geojson-tiler/wrap.ts index 34ebdf199f..1a4d223060 100644 --- a/modules/mvt/src/lib/geojson-tiler/wrap.ts +++ b/modules/mvt/src/lib/geojson-tiler/wrap.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license import type {GeoJSONTileFeature} from './tile'; diff --git a/modules/mvt/src/lib/parse-mvt.ts b/modules/mvt/src/lib/parse-mvt.ts index 744e7527ce..1529d82e0a 100644 --- a/modules/mvt/src/lib/parse-mvt.ts +++ b/modules/mvt/src/lib/parse-mvt.ts @@ -3,8 +3,8 @@ import type { FlatFeature, Feature, GeojsonGeometryInfo, - BinaryFeatures, - GeoJSONRowTable + BinaryFeatureCollection, + GeoJSONTable } from '@loaders.gl/schema'; import Protobuf from 'pbf'; @@ -25,29 +25,31 @@ import VectorTileFeatureMapBox from './mapbox-vector-tile/vector-tile-feature'; export default function parseMVT(arrayBuffer: ArrayBuffer, options?: MVTLoaderOptions) { const mvtOptions = normalizeOptions(options); - const shape = options?.gis?.format || options?.mvt?.shape; + const shape: string | undefined = + options?.gis?.format || options?.mvt?.shape || (options?.shape as string); switch (shape) { case 'columnar-table': // binary + some JS arrays return {shape: 'columnar-table', data: parseToBinary(arrayBuffer, mvtOptions)}; - case 'geojson-row-table': { - const table: GeoJSONRowTable = { - shape: 'geojson-row-table', - data: parseToGeojson(arrayBuffer, mvtOptions) + case 'geojson-table': { + const table: GeoJSONTable = { + shape: 'geojson-table', + type: 'FeatureCollection', + features: parseToGeojsonFeatures(arrayBuffer, mvtOptions) }; return table; } case 'geojson': - return parseToGeojson(arrayBuffer, mvtOptions); + return parseToGeojsonFeatures(arrayBuffer, mvtOptions); case 'binary-geometry': return parseToBinary(arrayBuffer, mvtOptions); case 'binary': return parseToBinary(arrayBuffer, mvtOptions); default: - throw new Error(shape); + throw new Error(shape || 'undefined shape'); } } -function parseToBinary(arrayBuffer: ArrayBuffer, options: MVTOptions): BinaryFeatures { +function parseToBinary(arrayBuffer: ArrayBuffer, options: MVTOptions): BinaryFeatureCollection { const [flatGeoJsonFeatures, geometryInfo] = parseToFlatGeoJson(arrayBuffer, options); const binaryData = flatGeojsonToBinary(flatGeoJsonFeatures, geometryInfo); @@ -101,7 +103,7 @@ function parseToFlatGeoJson( return [features, geometryInfo]; } -function parseToGeojson(arrayBuffer: ArrayBuffer, options: MVTOptions): Feature[] { +function parseToGeojsonFeatures(arrayBuffer: ArrayBuffer, options: MVTOptions): Feature[] { if (arrayBuffer.byteLength <= 0) { return []; } @@ -159,6 +161,7 @@ function getDecodedFeature( layerName: string ): MVTMapboxCoordinates { const decodedFeature = feature.toGeoJSON( + // @ts-expect-error What is going on here? options.coordinates === 'wgs84' ? options.tileIndex : transformToLocalCoordinates ); @@ -181,6 +184,7 @@ function getDecodedFeatureBinary( layerName: string ): FlatFeature { const decodedFeature = feature.toBinaryCoordinates( + // @ts-expect-error What is going on here? options.coordinates === 'wgs84' ? options.tileIndex : transformToLocalCoordinatesBinary ); diff --git a/modules/mvt/src/lib/parse-tilejson.ts b/modules/mvt/src/lib/parse-tilejson.ts new file mode 100644 index 0000000000..b031965c2c --- /dev/null +++ b/modules/mvt/src/lib/parse-tilejson.ts @@ -0,0 +1,411 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +export type TileJSONOptions = { + maxValues?: number | false; +}; + +/** Parsed and typed TileJSON, merges Tilestats information if present */ +export type TileJSON = { + name?: string; + description?: string; + version?: string; + + tileFormat?: string; + tilesetType?: string; + + /** Generating application. Tippecanoe adds this. */ + generator?: string; + /** Generating application options. Tippecanoe adds this. */ + generatorOptions?: string; + + /** Tile indexing scheme */ + scheme?: 'xyz' | 'tms'; + /** Sharded URLs */ + tiles?: string[]; + /** `[[w, s], [e, n]]`, indicates the limits of the bounding box using the axis units and order of the specified CRS. */ + boundingBox?: [min: [w: number, s: number], max: [e: number, n: number]]; + /** May be set to the maxZoom of the first layer */ + maxZoom?: number | null; + /** May be set to the minZoom of the first layer */ + minZoom?: number | null; + center?: number[] | null; + htmlAttribution?: string; + htmlLegend?: string; + + // Combination of tilestats (if present) and tilejson layer information + layers?: TileJSONLayer[]; + + /** Any nested JSON metadata */ + metaJson?: any | null; +}; + +export type TileJSONLayer = { + /** The name (id) of this layer (tilejson.vector_layers[].id / tilestats.layers[].layer) */ + name: string; + + /** The description of this layer (tilejson.layer.description) */ + description?: string; + + // tilestats + + /** The number of features in this layer (tilestats.layer.count) */ + featureCount?: number; + /** The dominant geometry type in this layer (tilestats.layer.geometry) */ + dominantGeometry?: string; + /** An array of details about the first 100 attributes in this layer */ + + /** */ + minZoom?: number; + maxZoom?: number; + fields: TileJSONField[]; +}; + +export type TileJSONField = { + /** The name of this attribute */ + name: string; + description?: string; + + // tilestats + + type: string; + /** min value (if there are *any* numbers in the values) */ + min?: number; + /** max value (if there are *any* numbers in the values) */ + max?: number; + /** Number of unique values across the tileset */ + uniqueValueCount?: number; + /** An array of this attribute's first 100 unique values */ + values?: unknown[]; +}; + +/** + * The raw/unparsed tilestats layer type + * @see https://github.com/mapbox/mapbox-geostats#output-the-stats + */ +type TilestatsLayer = { + /** The name of this layer */ + layer: string; + /** The number of features in this layer */ + count: number; + /** The dominant geometry type in this layer */ + geometry: string; + /** The number of unique attributes in this layer (max. 1000) */ + attributeCount: number; + /** Fields for this layer */ + attributes?: TilestatsLayerAttribute[]; +}; + +/** + * The raw/unparsed tilestats attribute type + * @see https://github.com/mapbox/mapbox-geostats#output-the-stats + */ +type TilestatsLayerAttribute = { + /** The name of this layer */ + attribute?: string; + /** Each attribute has one of the following types: + * - 'string' if all its values are strings (or null). + * - 'number' if all its values are numbers (or null). + * - 'boolean' if all its values are booleans (or null). + * - 'null' if its only value is null. + * - 'mixed' if it has values of multiple types. + * - Array and object values are coerced to strings. + */ + type?: string; + /** min value (if there are *any* numbers in the values) */ + min?: number; + /** max value (if there are *any* numbers in the values) */ + max?: number; + /** Number of unique values */ + count?: number; + /** First 100 values */ + values?: unknown[]; +}; + +const isObject: (x: unknown) => boolean = (x) => x !== null && typeof x === 'object'; + +export function parseTileJSON(jsonMetadata: any, options: TileJSONOptions): TileJSON | null { + if (!jsonMetadata || !isObject(jsonMetadata)) { + return null; + } + + let tileJSON: TileJSON = { + name: jsonMetadata.name || '', + description: jsonMetadata.description || '' + }; + + // tippecanoe + + if (typeof jsonMetadata.generator === 'string') { + tileJSON.generator = jsonMetadata.generator; + } + if (typeof jsonMetadata.generator_options === 'string') { + tileJSON.generatorOptions = jsonMetadata.generator_options; + } + + // Tippecanoe emits `antimeridian_adjusted_bounds` instead of `bounds` + tileJSON.boundingBox = + parseBounds(jsonMetadata.bounds) || parseBounds(jsonMetadata.antimeridian_adjusted_bounds); + + // TODO - can be undefined - we could set to center of bounds... + tileJSON.center = parseCenter(jsonMetadata.center); + // TODO - can be undefined, we could extract from layers... + tileJSON.maxZoom = safeParseFloat(jsonMetadata.maxzoom); + // TODO - can be undefined, we could extract from layers... + tileJSON.minZoom = safeParseFloat(jsonMetadata.minzoom); + + // Look for nested metadata embedded in .json field + // TODO - document what source this applies to, when is this needed? + if (typeof jsonMetadata?.json === 'string') { + // try to parse json + try { + tileJSON.metaJson = JSON.parse(jsonMetadata.json); + } catch (error) { + // eslint-disable-next-line no-console + console.warn('Failed to parse tilejson.json field', error); + // do nothing + } + } + + // Look for fields in tilestats + + const tilestats = jsonMetadata.tilestats || tileJSON.metaJson?.tilestats; + const tileStatsLayers = parseTilestatsLayers(tilestats, options); + const tileJSONlayers = parseTileJSONLayers(jsonMetadata.vector_layers); // eslint-disable-line camelcase + // TODO - merge in description from tilejson + const layers = mergeLayers(tileJSONlayers, tileStatsLayers); + + tileJSON = { + ...tileJSON, + layers + }; + + if (tileJSON.maxZoom === null && layers.length > 0) { + tileJSON.maxZoom = layers[0].maxZoom || null; + } + + if (tileJSON.minZoom === null && layers.length > 0) { + tileJSON.minZoom = layers[0].minZoom || null; + } + + return tileJSON; +} + +function parseTileJSONLayers(layers: any[]): TileJSONLayer[] { + // Look for fields in vector_layers + if (!Array.isArray(layers)) { + return []; + } + return layers.map((layer) => parseTileJSONLayer(layer)); +} + +function parseTileJSONLayer(layer: any): TileJSONLayer { + const fields = Object.entries(layer.fields || []).map(([key, datatype]) => ({ + name: key, + ...attributeTypeToFieldType(String(datatype)) + })); + const layer2 = {...layer}; + delete layer2.fields; + return { + name: layer.id || '', + ...layer2, + fields + }; +} + +/** parse Layers array from tilestats */ +function parseTilestatsLayers(tilestats: any, options: TileJSONOptions): TileJSONLayer[] { + if (isObject(tilestats) && Array.isArray(tilestats.layers)) { + // we are in luck! + return tilestats.layers.map((layer) => parseTilestatsForLayer(layer, options)); + } + return []; +} + +function parseTilestatsForLayer(layer: TilestatsLayer, options: TileJSONOptions): TileJSONLayer { + const fields: TileJSONField[] = []; + const indexedAttributes: {[key: string]: TilestatsLayerAttribute[]} = {}; + + const attributes = layer.attributes || []; + for (const attribute of attributes) { + const name = attribute.attribute; + if (typeof name === 'string') { + // TODO - code copied from kepler.gl, need sample tilestats files to test + if (name.split('|').length > 1) { + // indexed field + const fname = name.split('|')[0]; + indexedAttributes[fname] = indexedAttributes[fname] || []; + indexedAttributes[fname].push(attribute); + // eslint-disable-next-line no-console + console.warn('ignoring tilestats indexed field', fname); + } else if (!fields[name]) { + fields.push(attributeToField(attribute, options)); + } else { + // return (fields[name], attribute); + } + } + } + return { + name: layer.layer || '', + dominantGeometry: layer.geometry, + fields + }; +} + +function mergeLayers(layers: TileJSONLayer[], tilestatsLayers: TileJSONLayer[]): TileJSONLayer[] { + return layers.map((layer) => { + const tilestatsLayer = tilestatsLayers.find((tsLayer) => tsLayer.name === layer.name); + // For aesthetics in JSON dumps, we preserve field order (make sure layers is last) + const fields = tilestatsLayer?.fields || []; + const layer2: Partial = {...layer}; + delete layer2.fields; + return { + ...layer2, + ...tilestatsLayer, + fields + } as TileJSONLayer; + }); +} + +/** + * bounds should be [minLng, minLat, maxLng, maxLat] + *`[[w, s], [e, n]]`, indicates the limits of the bounding box using the axis units and order of the specified CRS. + */ +function parseBounds( + bounds: string | number[] +): [[east: number, south: number], [west: number, north: number]] | undefined { + // supported formats + // string: "-96.657715,40.126127,-90.140061,43.516689", + // array: [ -180, -85.05112877980659, 180, 85.0511287798066 ] + const result = fromArrayOrString(bounds); + // validate bounds + if ( + Array.isArray(result) && + result.length === 4 && + [result[0], result[2]].every(isLng) && + [result[1], result[3]].every(isLat) + ) { + return [ + [result[0], result[1]], + [result[2], result[3]] + ]; + } + return undefined; +} + +function parseCenter(center: string | number[]): number[] | null { + // supported formats + // string: "-96.657715,40.126127,-90.140061,43.516689", + // array: [-91.505127,41.615442,14] + const result = fromArrayOrString(center); + if ( + Array.isArray(result) && + result.length === 3 && + isLng(result[0]) && + isLat(result[1]) && + isZoom(result[2]) + ) { + return result; + } + return null; +} + +function safeParseFloat(input: unknown): number | null { + const result = + typeof input === 'string' ? parseFloat(input) : typeof input === 'number' ? input : null; + return result === null || isNaN(result) ? null : result; +} + +// https://github.com/mapbox/tilejson-spec/tree/master/2.2.0 +function isLat(num: any): boolean { + return Number.isFinite(num) && num <= 90 && num >= -90; +} +function isLng(num: any): boolean { + return Number.isFinite(num) && num <= 180 && num >= -180; +} +function isZoom(num: any): boolean { + return Number.isFinite(num) && num >= 0 && num <= 22; +} +function fromArrayOrString(data: string | number[]): number[] | null { + if (typeof data === 'string') { + return data.split(',').map(parseFloat); + } else if (Array.isArray(data)) { + return data; + } + return null; +} + +// possible types https://github.com/mapbox/tippecanoe#modifying-feature-attributes +const attrTypeMap = { + number: { + type: 'float32' + }, + numeric: { + type: 'float32' + }, + string: { + type: 'utf8' + }, + vachar: { + type: 'utf8' + }, + float: { + type: 'float32' + }, + int: { + type: 'int32' + }, + int4: { + type: 'int32' + }, + boolean: { + type: 'boolean' + }, + bool: { + type: 'boolean' + } +}; + +function attributeToField( + attribute: TilestatsLayerAttribute = {}, + options: TileJSONOptions +): TileJSONField { + const fieldTypes = attributeTypeToFieldType(attribute.type!); + const field: TileJSONField = { + name: attribute.attribute as string, + // what happens if attribute type is string... + // filterProps: getFilterProps(fieldTypes.type, attribute), + ...fieldTypes + }; + + // attribute: "_season_peaks_color" + // count: 1000 + // max: 0.95 + // min: 0.24375 + // type: "number" + + if (typeof attribute.min === 'number') { + field.min = attribute.min; + } + if (typeof attribute.max === 'number') { + field.max = attribute.max; + } + if (typeof attribute.count === 'number') { + field.uniqueValueCount = attribute.count; + } + if (options.maxValues !== false && attribute.values) { + // Too much data? Add option? + field.values = attribute.values?.slice(0, options.maxValues); + } + return field; +} + +function attributeTypeToFieldType(aType: string): {type: string} { + const type = aType.toLowerCase(); + if (!type || !attrTypeMap[type]) { + // console.warn( + // `cannot convert attribute type ${type} to loaders.gl data type, use string by default` + // ); + } + return attrTypeMap[type] || {type: 'string'}; +} diff --git a/modules/mvt/src/lib/types.ts b/modules/mvt/src/lib/types.ts index 0bc800bf72..bc79b30bd3 100644 --- a/modules/mvt/src/lib/types.ts +++ b/modules/mvt/src/lib/types.ts @@ -14,16 +14,17 @@ type MVTWgs84CoordinatesOptions = { /** * When set to `wgs84`, the parser will return a flat array of GeoJSON objects with coordinates in longitude, latitude decoded from the provided tile index. */ - coordinates: 'wgs84'; + coordinates?: 'wgs84'; /** * Mandatory with `wgs84` coordinates option. An object containing tile index values (`x`, `y`, * `z`) to reproject features' coordinates into WGS84. */ - tileIndex: {x: number; y: number; z: number}; + tileIndex?: {x: number; y: number; z: number}; }; export type MVTOptions = (MVTLocalCoordinatesOptions | MVTWgs84CoordinatesOptions) & { + shape?: 'geojson-table' | 'columnar-table' | 'geojson' | 'binary' | 'binary-geometry'; /** * When non-`null`, the layer name of each feature is added to * `feature.properties[layerProperty]`. (A `feature.properties` object is created if the feature @@ -36,7 +37,6 @@ export type MVTOptions = (MVTLocalCoordinatesOptions | MVTWgs84CoordinatesOption * be included in the output. If `null`, features from all layers are returned. */ layers?: string[]; - shape?: 'geojson-row-table' | 'columnar-table' | 'geojson' | 'binary' | 'binary-geometry'; }; export type MVTMapboxGeometry = { @@ -64,6 +64,6 @@ export type MVTLoaderOptions = LoaderOptions & { */ binary?: boolean; /** @deprecated. Use options.mvt.shape */ - format?: 'geojson-row-table' | 'columnar-table' | 'geojson' | 'binary' | 'binary-geometry'; + format?: 'geojson-table' | 'columnar-table' | 'geojson' | 'binary' | 'binary-geometry'; }; }; diff --git a/modules/mvt/src/mvt-loader.ts b/modules/mvt/src/mvt-loader.ts index 0174d07941..013a19d695 100644 --- a/modules/mvt/src/mvt-loader.ts +++ b/modules/mvt/src/mvt-loader.ts @@ -1,25 +1,26 @@ import type {Loader, LoaderWithParser} from '@loaders.gl/loader-utils'; import type {MVTLoaderOptions} from './lib/types'; +// import type { +// Feature, +// BinaryFeatureCollection, +// GeoJSONTable, +// Geometry, +// GeoJsonProperties +// } from '@loaders.gl/schema'; import parseMVT from './lib/parse-mvt'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; -const DEFAULT_MVT_LOADER_OPTIONS: MVTLoaderOptions = { - mvt: { - shape: 'geojson', - coordinates: 'local', - layerProperty: 'layerName', - layers: undefined, - tileIndex: null - } -}; - /** * Worker loader for the Mapbox Vector Tile format */ -export const MVTWorkerLoader: Loader = { +export const MVTWorkerLoader: Loader< + any, // BinaryFeatureCollection | GeoJSONTable | Feature, + never, + MVTLoaderOptions +> = { name: 'Mapbox Vector Tile', id: 'mvt', module: 'mvt', @@ -27,19 +28,32 @@ export const MVTWorkerLoader: Loader = { // Note: ArcGIS uses '.pbf' extension and 'application/octet-stream' extensions: ['mvt', 'pbf'], mimeTypes: [ + // https://www.iana.org/assignments/media-types/application/vnd.mapbox-vector-tile 'application/vnd.mapbox-vector-tile', 'application/x-protobuf' // 'application/octet-stream' ], worker: true, category: 'geometry', - options: DEFAULT_MVT_LOADER_OPTIONS + options: { + mvt: { + shape: 'geojson', + coordinates: 'local', + layerProperty: 'layerName', + layers: undefined, + tileIndex: null + } + } }; /** * Loader for the Mapbox Vector Tile format */ -export const MVTLoader: LoaderWithParser = { +export const MVTLoader: LoaderWithParser< + any, // BinaryFeatureCollection | GeoJSONTable | Feature, + never, + MVTLoaderOptions +> = { ...MVTWorkerLoader, parse: async (arrayBuffer, options?: MVTLoaderOptions) => parseMVT(arrayBuffer, options), parseSync: parseMVT, diff --git a/modules/mvt/src/mvt-source.ts b/modules/mvt/src/mvt-source.ts new file mode 100644 index 0000000000..e5f81df4a8 --- /dev/null +++ b/modules/mvt/src/mvt-source.ts @@ -0,0 +1,154 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {GetTileParameters, ImageType, DataSourceProps} from '@loaders.gl/loader-utils'; +import type {ImageTileSource, VectorTileSource} from '@loaders.gl/loader-utils'; +import {DataSource, resolvePath} from '@loaders.gl/loader-utils'; +import {ImageLoader, getBinaryImageMetadata} from '@loaders.gl/images'; +import {MVTLoader, MVTLoaderOptions, TileJSONLoader, TileJSON} from '@loaders.gl/mvt'; + +import {TileLoadParameters} from '@loaders.gl/loader-utils'; + +export type MVTSourceProps = DataSourceProps & { + url: string; + attributions?: string[]; +}; + +/** + * A PMTiles data source + * @note Can be either a raster or vector tile source depending on the contents of the PMTiles file. + */ +export class MVTSource extends DataSource implements ImageTileSource, VectorTileSource { + props: MVTSourceProps; + url: string; + data: string; + schema: 'tms' | 'xyz' = 'tms'; + metadata: Promise; + extension = '.png'; + mimeType: string | null = null; + + constructor(props: MVTSourceProps) { + super(props); + this.props = props; + this.url = resolvePath(props.url); + this.data = this.url; + this.getTileData = this.getTileData.bind(this); + this.metadata = this.getMetadata(); + } + + // @ts-ignore - Metadata type misalignment + async getMetadata(): Promise { + const metadataUrl = this.getMetadataUrl(); + let response: Response; + try { + // Annoyingly, on CORS errors, fetch doesn't use the response status/ok mechanism but instead throws + // CORS errors are common when requesting an unavailable sub resource such as a metadata file or an unavailable tile) + response = await this.fetch(metadataUrl); + } catch (error: unknown) { + // eslint-disable-next-line no-console + console.error((error as TypeError).message); + return null; + } + if (!response.ok) { + // eslint-disable-next-line no-console + console.error(response.statusText); + return null; + } + const tileJSON = await response.text(); + const metadata = TileJSONLoader.parseTextSync?.(JSON.stringify(tileJSON)) || null; + // metadata.attributions = [...this.props.attributions, ...(metadata.attributions || [])]; + // if (metadata?.mimeType) { + // this.mimeType = metadata?.tileMIMEType; + // } + return metadata; + } + + getTileMIMEType(): string | null { + return this.mimeType; + } + + async getTile(tileParams: GetTileParameters): Promise { + const {x, y, zoom: z} = tileParams; + const tileUrl = this.getTileURL(x, y, z); + const response = await this.fetch(tileUrl); + if (!response.ok) { + return null; + } + const arrayBuffer = await response.arrayBuffer(); + return arrayBuffer; + } + + // Tile Source interface implementation: deck.gl compatible API + // TODO - currently only handles image tiles, not vector tiles + + async getTileData(tileParams: TileLoadParameters): Promise { + const {x, y, z} = tileParams.index; + // const metadata = await this.metadata; + // mimeType = metadata?.tileMIMEType || 'application/vnd.mapbox-vector-tile'; + + const arrayBuffer = await this.getTile({x, y, zoom: z, layers: []}); + if (arrayBuffer === null) { + return null; + } + + const imageMetadata = getBinaryImageMetadata(arrayBuffer); + this.mimeType = + this.mimeType || imageMetadata?.mimeType || 'application/vnd.mapbox-vector-tile'; + switch (this.mimeType) { + case 'application/vnd.mapbox-vector-tile': + return await this.parseVectorTile(arrayBuffer, {x, y, zoom: z, layers: []}); + default: + return await this.parseImageTile(arrayBuffer); + } + } + x; + + // ImageTileSource interface implementation + + async getImageTile(tileParams: GetTileParameters): Promise { + const arrayBuffer = await this.getTile(tileParams); + return arrayBuffer ? this.parseImageTile(arrayBuffer) : null; + } + + protected async parseImageTile(arrayBuffer: ArrayBuffer): Promise { + return await ImageLoader.parse(arrayBuffer, this.loadOptions); + } + + // VectorTileSource interface implementation + + async getVectorTile(tileParams: GetTileParameters): Promise { + const arrayBuffer = await this.getTile(tileParams); + return arrayBuffer ? this.parseVectorTile(arrayBuffer, tileParams) : null; + } + + protected async parseVectorTile( + arrayBuffer: ArrayBuffer, + tileParams: GetTileParameters + ): Promise { + const loadOptions: MVTLoaderOptions = { + shape: 'geojson-table', + mvt: { + coordinates: 'wgs84', + tileIndex: {x: tileParams.x, y: tileParams.y, z: tileParams.zoom}, + ...(this.loadOptions as MVTLoaderOptions)?.mvt + }, + ...this.loadOptions + }; + + return await MVTLoader.parse(arrayBuffer, loadOptions); + } + + getMetadataUrl(): string { + return `${this.url}/tilejson.json`; + } + + getTileURL(x: number, y: number, z: number) { + switch (this.schema) { + case 'xyz': + return `${this.url}/${x}/${y}/${z}${this.extension}`; + case 'tms': + default: + return `${this.url}/${z}/${x}/${y}${this.extension}`; + } + } +} diff --git a/modules/mvt/src/tilejson-loader.ts b/modules/mvt/src/tilejson-loader.ts new file mode 100644 index 0000000000..0b166524f9 --- /dev/null +++ b/modules/mvt/src/tilejson-loader.ts @@ -0,0 +1,46 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; +import type {TileJSON} from './lib/parse-tilejson'; +import {parseTileJSON} from './lib/parse-tilejson'; + +// __VERSION__ is injected by babel-plugin-version-inline +// @ts-ignore TS2304: Cannot find name '__VERSION__'. +const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; + +export type TileJSONLoaderOptions = LoaderOptions & { + tilejson?: { + maxValues?: number | false; + }; +}; + +/** + * Loader for TileJSON metadata + */ +export const TileJSONLoader: LoaderWithParser = { + name: 'TileJSON', + id: 'tilejson', + module: 'pmtiles', + version: VERSION, + worker: true, + extensions: ['json'], + mimeTypes: ['application/json'], + text: true, + options: { + tilejson: { + maxValues: 10 + } + }, + parse: async (arrayBuffer, options?: TileJSONLoaderOptions) => { + const jsonString = new TextDecoder().decode(arrayBuffer); + const json = JSON.parse(jsonString); + const tilejsonOptions = {...TileJSONLoader.options.tilejson, ...options?.tilejson}; + return parseTileJSON(json, tilejsonOptions) as TileJSON; + }, + parseTextSync: (text, options) => { + const json = JSON.parse(text); + const tilejsonOptions = {...TileJSONLoader.options.tilejson, ...options?.tilejson}; + return parseTileJSON(json, tilejsonOptions) as TileJSON; + } +}; diff --git a/modules/mvt/test/data/fetch_data.py b/modules/mvt/test/data/fetch_data.py deleted file mode 100755 index 7cd308c125..0000000000 --- a/modules/mvt/test/data/fetch_data.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 - -"""Simple script to fetch MVT tiles for testing""" -import requests -import os - -TABLE = 'cartodb-gcp-backend-data-team.alasarr' - -DATASETS = { - 'blockgroup': f'{TABLE}.usa_blockgroup_population', - 'blockgroup_numprop': f'{TABLE}.usa_blockgroup_tileset_numprop', - 'census_numprop': f'{TABLE}.usa_censustract_2015_tileset_numprop', - 'zipcodes': f'{TABLE}.usa_zcta_2015_tileset', - 'zipcodes_numprop': f'{TABLE}.usa_zcta_2015_tileset_numprop', - 'counties_numprop': f'{TABLE}.usa_county_2015_tileset_numprop', -} - - -SERVER = 'https://maps-api-v2.us.carto.com/user/aasuero/bigquery' - -TILES = [ - {'z': 4, 'x': 4, 'y': 6}, - {'z': 4, 'x': 3, 'y': 6}, - {'z': 5, 'x': 8, 'y': 12}, - {'z': 6, 'x': 18, 'y': 24}, - {'z': 7, 'x': 35, 'y': 50}, - {'z': 8, 'x': 75, 'y': 96} -] -for name, dataset in DATASETS.items(): - # Get url pattern for tiles - params = { - 'source': dataset, - 'format': 'tilejson' - } - url = f'{SERVER}/tileset' - r = requests.get(url, params) - parsed = r.json() - tileformat = parsed['tiles'][0] - - for tile in TILES: - tileurl = tileformat.format(**tile) - zxy = '{z}_{x}_{y}'.format(**tile) - tilename = f'{name}_{zxy}' - filename = f'{tilename}.mvt' - print(f'{tilename}: \'{filename}\',') - - if not os.path.isfile(filename): - r = requests.get(tileurl) - with open(filename, 'wb') as f: - f.write(r.content) diff --git a/modules/mvt/test/results/decoded_mvt_lines.json b/modules/mvt/test/data/mvt-results/decoded_mvt_lines.json similarity index 100% rename from modules/mvt/test/results/decoded_mvt_lines.json rename to modules/mvt/test/data/mvt-results/decoded_mvt_lines.json diff --git a/modules/mvt/test/results/decoded_mvt_points.json b/modules/mvt/test/data/mvt-results/decoded_mvt_points.json similarity index 100% rename from modules/mvt/test/results/decoded_mvt_points.json rename to modules/mvt/test/data/mvt-results/decoded_mvt_points.json diff --git a/modules/mvt/test/results/decoded_mvt_polygons.json b/modules/mvt/test/data/mvt-results/decoded_mvt_polygons.json similarity index 100% rename from modules/mvt/test/results/decoded_mvt_polygons.json rename to modules/mvt/test/data/mvt-results/decoded_mvt_polygons.json diff --git a/modules/mvt/test/results/decoded_mvt_polygons_array.json b/modules/mvt/test/data/mvt-results/decoded_mvt_polygons_array.json similarity index 100% rename from modules/mvt/test/results/decoded_mvt_polygons_array.json rename to modules/mvt/test/data/mvt-results/decoded_mvt_polygons_array.json diff --git a/modules/mvt/test/data/lines_10-501-386_multiplelayers.mvt b/modules/mvt/test/data/mvt/lines_10-501-386_multiplelayers.mvt similarity index 100% rename from modules/mvt/test/data/lines_10-501-386_multiplelayers.mvt rename to modules/mvt/test/data/mvt/lines_10-501-386_multiplelayers.mvt diff --git a/modules/mvt/test/data/lines_2-2-1.mvt b/modules/mvt/test/data/mvt/lines_2-2-1.mvt similarity index 100% rename from modules/mvt/test/data/lines_2-2-1.mvt rename to modules/mvt/test/data/mvt/lines_2-2-1.mvt diff --git a/modules/mvt/test/data/points_4-2-6.mvt b/modules/mvt/test/data/mvt/points_4-2-6.mvt similarity index 100% rename from modules/mvt/test/data/points_4-2-6.mvt rename to modules/mvt/test/data/mvt/points_4-2-6.mvt diff --git a/modules/mvt/test/data/polygon_with_zero_size_hole.mvt b/modules/mvt/test/data/mvt/polygon_with_zero_size_hole.mvt similarity index 100% rename from modules/mvt/test/data/polygon_with_zero_size_hole.mvt rename to modules/mvt/test/data/mvt/polygon_with_zero_size_hole.mvt diff --git a/modules/mvt/test/data/polygons_10-133-325.mvt b/modules/mvt/test/data/mvt/polygons_10-133-325.mvt similarity index 100% rename from modules/mvt/test/data/polygons_10-133-325.mvt rename to modules/mvt/test/data/mvt/polygons_10-133-325.mvt diff --git a/modules/mvt/test/data/with_feature_id.mvt b/modules/mvt/test/data/mvt/with_feature_id.mvt similarity index 100% rename from modules/mvt/test/data/with_feature_id.mvt rename to modules/mvt/test/data/mvt/with_feature_id.mvt diff --git a/modules/mvt/test/data/rings_ring_and_hole.json b/modules/mvt/test/data/rings/rings_ring_and_hole.json similarity index 100% rename from modules/mvt/test/data/rings_ring_and_hole.json rename to modules/mvt/test/data/rings/rings_ring_and_hole.json diff --git a/modules/mvt/test/data/rings_single_ring.json b/modules/mvt/test/data/rings/rings_single_ring.json similarity index 100% rename from modules/mvt/test/data/rings_single_ring.json rename to modules/mvt/test/data/rings/rings_single_ring.json diff --git a/modules/mvt/test/data/rings_two_rings.json b/modules/mvt/test/data/rings/rings_two_rings.json similarity index 100% rename from modules/mvt/test/data/rings_two_rings.json rename to modules/mvt/test/data/rings/rings_two_rings.json diff --git a/modules/mvt/test/data/rings_zero_size_hole.json b/modules/mvt/test/data/rings/rings_zero_size_hole.json similarity index 100% rename from modules/mvt/test/data/rings_zero_size_hole.json rename to modules/mvt/test/data/rings/rings_zero_size_hole.json diff --git a/modules/mvt/test/data/tilejson/bad.tilejson b/modules/mvt/test/data/tilejson/bad.tilejson new file mode 100644 index 0000000000..9767765b31 --- /dev/null +++ b/modules/mvt/test/data/tilejson/bad.tilejson @@ -0,0 +1,9 @@ +{ + // Invalid JSON + "name": "World Bright", + "scheme": "tms", + "tiles": [ "http://a.tiles.mapbox.com/mapbox/1.0.0/world-bright/{z}/{x}/{y}.png" ], + "minzoom": 0, + "maxzoom": 11, + "bounds": [ -180, -85, 180, 85 ] +} diff --git a/modules/mvt/test/data/tilejson/grid.tilejson b/modules/mvt/test/data/tilejson/grid.tilejson new file mode 100644 index 0000000000..f6c2ccb5bf --- /dev/null +++ b/modules/mvt/test/data/tilejson/grid.tilejson @@ -0,0 +1,10 @@ +{ + "name": "Geography Class", + "scheme": "tms", + "tiles": [ "http://a.tiles.mapbox.com/mapbox/1.0.0/geography-class/{z}/{x}/{y}.png" ], + "grids": [ "http://a.tiles.mapbox.com/mapbox/1.0.0/geography-class/{z}/{x}/{y}.grid.json" ], + "minzoom": 0, + "maxzoom": 8, + "bounds": [ -179.99992505544913, -85.05112231458043, 179.99992505544913, 85.05112231458043 ], + "center": [ 0, 0, 4 ] +} diff --git a/modules/mvt/test/data/tilejson/invalid.tilejson b/modules/mvt/test/data/tilejson/invalid.tilejson new file mode 100644 index 0000000000..e1dd9279fa --- /dev/null +++ b/modules/mvt/test/data/tilejson/invalid.tilejson @@ -0,0 +1,8 @@ +{ + "name": "Invalid tiles", + "scheme": "xyz", + "tiles": [ "http://localhost:38923/tiles/{z}/{x}/{y}.png" ], + "minzoom": 0, + "maxzoom": 18, + "bounds": [ -180, -85, 180, 85 ] +} diff --git a/modules/mvt/test/data/tilejson/tilejson.ts b/modules/mvt/test/data/tilejson/tilejson.ts new file mode 100644 index 0000000000..2ff0aa0857 --- /dev/null +++ b/modules/mvt/test/data/tilejson/tilejson.ts @@ -0,0 +1,10 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +export const TILEJSONS = [ + // {url: '@loaders.gl/mvt/test/data/tilejson/bad.tilejson', bad: true}, + // {url: '@loaders.gl/mvt/test/data/tilejson/invalid.tilejson', bad: true}, + {url: '@loaders.gl/mvt/test/data/tilejson/grid.tilejson', bad: false}, + {url: '@loaders.gl/mvt/test/data/tilejson/world-bright-ssl.tilejson', bad: false}, + {url: '@loaders.gl/mvt/test/data/tilejson/world-bright.tilejson', bad: false} +]; diff --git a/modules/mvt/test/data/tilejson/tippecanoe.expected.json b/modules/mvt/test/data/tilejson/tippecanoe.expected.json new file mode 100644 index 0000000000..3d499b0bc4 --- /dev/null +++ b/modules/mvt/test/data/tilejson/tippecanoe.expected.json @@ -0,0 +1,294 @@ +{ + "name": "output.pmtiles", + "format": "pbf", + "type": "overlay", + "description": "output.pmtiles", + "version": "2", + "generator": "tippecanoe v2.35.0", + "generator_options": "tippecanoe -zg -o output.pmtiles --drop-densest-as-needed input.geojson", + "antimeridian_adjusted_bounds": "-150.112222,-51.895278,179.357778,69.604375", + "vector_layers": [ + { + "id": "input", + "description": "", + "minzoom": 0, + "maxzoom": 6, + "fields": { + "add": "Number", + "category": "Number", + "continent": "String", + "disp_scale": "String", + "electric": "Number", + "featurecla": "String", + "mult_track": "Number", + "natlscale": "Number", + "other_code": "Number", + "part": "String", + "rwdb_rr_id": "Number", + "scalerank": "Number" + } + } + ], + "tilestats": { + "layerCount": 1, + "layers": [ + { + "layer": "input", + "count": 25413, + "geometry": "LineString", + "attributeCount": 12, + "attributes": [ + { + "attribute": "add", + "count": 2, + "type": "number", + "values": [ + -1, + 0 + ], + "min": -1, + "max": 0 + }, + { + "attribute": "category", + "count": 7, + "type": "number", + "values": [ + 0, + 1, + 2, + 3, + 6, + 7, + 9 + ], + "min": 0, + "max": 9 + }, + { + "attribute": "continent", + "count": 6, + "type": "string", + "values": [ + "Africa", + "Asia", + "Europe", + "North America", + "Oceania", + "South America" + ] + }, + { + "attribute": "disp_scale", + "count": 6, + "type": "string", + "values": [ + "1:10m", + "1:20m", + "1:3m", + "1:40m", + "1:5m", + "1:80m" + ] + }, + { + "attribute": "electric", + "count": 3, + "type": "number", + "values": [ + 0, + 1, + 2 + ], + "min": 0, + "max": 2 + }, + { + "attribute": "featurecla", + "count": 2, + "type": "string", + "values": [ + "Railroad", + "Railroad ferry" + ] + }, + { + "attribute": "mult_track", + "count": 3, + "type": "number", + "values": [ + 0, + 1, + 2 + ], + "min": 0, + "max": 2 + }, + { + "attribute": "natlscale", + "count": 7, + "type": "number", + "values": [ + 1, + 10, + 150, + 20, + 40, + 5, + 75 + ], + "min": 1, + "max": 150 + }, + { + "attribute": "other_code", + "count": 4, + "type": "number", + "values": [ + 0, + 1, + 6, + 7 + ], + "min": 0, + "max": 7 + }, + { + "attribute": "part", + "count": 2, + "type": "string", + "values": [ + "ne_1d4_north_america_dup", + "ne_global_not_north_america" + ] + }, + { + "attribute": "rwdb_rr_id", + "count": 1000, + "type": "number", + "values": [ + 0, + 1, + 10, + 100, + 1000, + 10000, + 10001, + 10002, + 10003, + 10004, + 10005, + 10006, + 10007, + 10008, + 10009, + 1001, + 10010, + 10011, + 10012, + 10013, + 10014, + 10015, + 10016, + 10017, + 10018, + 10019, + 1002, + 10020, + 10021, + 10022, + 10023, + 10024, + 10025, + 10026, + 10027, + 10028, + 10029, + 1003, + 10030, + 10031, + 10032, + 10033, + 10034, + 10035, + 10036, + 10037, + 10038, + 10039, + 1004, + 10040, + 10041, + 10042, + 10043, + 10044, + 10045, + 10046, + 10047, + 10048, + 10049, + 1005, + 10050, + 10051, + 10052, + 10053, + 10054, + 10055, + 10056, + 10057, + 10058, + 10059, + 1006, + 10060, + 10061, + 10062, + 10063, + 10064, + 10065, + 10066, + 10067, + 10068, + 10069, + 1007, + 10070, + 10071, + 10072, + 10073, + 10074, + 10075, + 10076, + 10077, + 10078, + 10079, + 1008, + 10080, + 10081, + 10082, + 10083, + 10084, + 10085, + 10086 + ], + "min": 0, + "max": 25407 + }, + { + "attribute": "scalerank", + "count": 7, + "type": "number", + "values": [ + 10, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "min": 4, + "max": 10 + } + ] + } + ] + } + } \ No newline at end of file diff --git a/modules/mvt/test/data/tilejson/tippecanoe.tilejson b/modules/mvt/test/data/tilejson/tippecanoe.tilejson new file mode 100644 index 0000000000..c4ff997b2f --- /dev/null +++ b/modules/mvt/test/data/tilejson/tippecanoe.tilejson @@ -0,0 +1,294 @@ +{ + "name": "output.pmtiles", + "format": "pbf", + "type": "overlay", + "description": "output.pmtiles", + "version": "2", + "generator": "tippecanoe v2.35.0", + "generator_options": "tippecanoe -zg -o output.pmtiles --drop-densest-as-needed input.geojson", + "antimeridian_adjusted_bounds": "-150.112222,-51.895278,179.357778,69.604375", + "vector_layers": [ + { + "id": "input", + "description": "", + "minzoom": 0, + "maxzoom": 6, + "fields": { + "add": "Number", + "category": "Number", + "continent": "String", + "disp_scale": "String", + "electric": "Number", + "featurecla": "String", + "mult_track": "Number", + "natlscale": "Number", + "other_code": "Number", + "part": "String", + "rwdb_rr_id": "Number", + "scalerank": "Number" + } + } + ], + "tilestats": { + "layerCount": 1, + "layers": [ + { + "layer": "input", + "count": 25413, + "geometry": "LineString", + "attributeCount": 12, + "attributes": [ + { + "attribute": "add", + "count": 2, + "type": "number", + "values": [ + -1, + 0 + ], + "min": -1, + "max": 0 + }, + { + "attribute": "category", + "count": 7, + "type": "number", + "values": [ + 0, + 1, + 2, + 3, + 6, + 7, + 9 + ], + "min": 0, + "max": 9 + }, + { + "attribute": "continent", + "count": 6, + "type": "string", + "values": [ + "Africa", + "Asia", + "Europe", + "North America", + "Oceania", + "South America" + ] + }, + { + "attribute": "disp_scale", + "count": 6, + "type": "string", + "values": [ + "1:10m", + "1:20m", + "1:3m", + "1:40m", + "1:5m", + "1:80m" + ] + }, + { + "attribute": "electric", + "count": 3, + "type": "number", + "values": [ + 0, + 1, + 2 + ], + "min": 0, + "max": 2 + }, + { + "attribute": "featurecla", + "count": 2, + "type": "string", + "values": [ + "Railroad", + "Railroad ferry" + ] + }, + { + "attribute": "mult_track", + "count": 3, + "type": "number", + "values": [ + 0, + 1, + 2 + ], + "min": 0, + "max": 2 + }, + { + "attribute": "natlscale", + "count": 7, + "type": "number", + "values": [ + 1, + 10, + 150, + 20, + 40, + 5, + 75 + ], + "min": 1, + "max": 150 + }, + { + "attribute": "other_code", + "count": 4, + "type": "number", + "values": [ + 0, + 1, + 6, + 7 + ], + "min": 0, + "max": 7 + }, + { + "attribute": "part", + "count": 2, + "type": "string", + "values": [ + "ne_1d4_north_america_dup", + "ne_global_not_north_america" + ] + }, + { + "attribute": "rwdb_rr_id", + "count": 1000, + "type": "number", + "values": [ + 0, + 1, + 10, + 100, + 1000, + 10000, + 10001, + 10002, + 10003, + 10004, + 10005, + 10006, + 10007, + 10008, + 10009, + 1001, + 10010, + 10011, + 10012, + 10013, + 10014, + 10015, + 10016, + 10017, + 10018, + 10019, + 1002, + 10020, + 10021, + 10022, + 10023, + 10024, + 10025, + 10026, + 10027, + 10028, + 10029, + 1003, + 10030, + 10031, + 10032, + 10033, + 10034, + 10035, + 10036, + 10037, + 10038, + 10039, + 1004, + 10040, + 10041, + 10042, + 10043, + 10044, + 10045, + 10046, + 10047, + 10048, + 10049, + 1005, + 10050, + 10051, + 10052, + 10053, + 10054, + 10055, + 10056, + 10057, + 10058, + 10059, + 1006, + 10060, + 10061, + 10062, + 10063, + 10064, + 10065, + 10066, + 10067, + 10068, + 10069, + 1007, + 10070, + 10071, + 10072, + 10073, + 10074, + 10075, + 10076, + 10077, + 10078, + 10079, + 1008, + 10080, + 10081, + 10082, + 10083, + 10084, + 10085, + 10086 + ], + "min": 0, + "max": 25407 + }, + { + "attribute": "scalerank", + "count": 7, + "type": "number", + "values": [ + 10, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "min": 4, + "max": 10 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/modules/mvt/test/data/tilejson/world-bright-ssl.tilejson b/modules/mvt/test/data/tilejson/world-bright-ssl.tilejson new file mode 100644 index 0000000000..a50a3a7f9b --- /dev/null +++ b/modules/mvt/test/data/tilejson/world-bright-ssl.tilejson @@ -0,0 +1,8 @@ +{ + "name": "World Bright", + "scheme": "xyz", + "tiles": [ "https://a.tiles.mapbox.com/v3/mapbox.world-bright/{z}/{x}/{y}.png" ], + "minzoom": 0, + "maxzoom": 11, + "bounds": [ -180, -85, 180, 85 ] +} diff --git a/modules/mvt/test/data/tilejson/world-bright.tilejson b/modules/mvt/test/data/tilejson/world-bright.tilejson new file mode 100644 index 0000000000..a3b4820058 --- /dev/null +++ b/modules/mvt/test/data/tilejson/world-bright.tilejson @@ -0,0 +1,8 @@ +{ + "name": "World Bright", + "scheme": "xyz", + "tiles": [ "http://a.tiles.mapbox.com/v3/mapbox.world-bright/{z}/{x}/{y}.png" ], + "minzoom": 0, + "maxzoom": 11, + "bounds": [ -180, -85, 180, 85 ] +} diff --git a/modules/mvt/test/data/tilesets.ts b/modules/mvt/test/data/tilesets.ts new file mode 100644 index 0000000000..b83e80aa3c --- /dev/null +++ b/modules/mvt/test/data/tilesets.ts @@ -0,0 +1 @@ +export const TILESETS: string[] = []; diff --git a/modules/mvt/test/index.ts b/modules/mvt/test/index.ts index 8c183cd073..c4994293dc 100644 --- a/modules/mvt/test/index.ts +++ b/modules/mvt/test/index.ts @@ -1,6 +1,11 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import './mvt-loader.spec'; +import './tilejson-loader.spec'; + +import './mvt-source.spec'; + // geojson-vt import './lib/geojson-tiler'; diff --git a/modules/mvt/test/lib/geojson-tiler/clip.spec.ts b/modules/mvt/test/lib/geojson-tiler/clip.spec.ts index 815fdf32b4..196c9ab07c 100644 --- a/modules/mvt/test/lib/geojson-tiler/clip.spec.ts +++ b/modules/mvt/test/lib/geojson-tiler/clip.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license import test from 'tape-promise/tape'; diff --git a/modules/mvt/test/lib/geojson-tiler/full.spec.ts b/modules/mvt/test/lib/geojson-tiler/full.spec.ts index cb80fa65fd..334a0787b1 100644 --- a/modules/mvt/test/lib/geojson-tiler/full.spec.ts +++ b/modules/mvt/test/lib/geojson-tiler/full.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license import test from 'tape-promise/tape'; diff --git a/modules/mvt/test/lib/geojson-tiler/get-tile.spec.ts b/modules/mvt/test/lib/geojson-tiler/get-tile.spec.ts index b6d57eea34..bf7ceb5144 100644 --- a/modules/mvt/test/lib/geojson-tiler/get-tile.spec.ts +++ b/modules/mvt/test/lib/geojson-tiler/get-tile.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license /* eslint-disable no-console */ @@ -28,7 +29,6 @@ const square = [ test('GeoJSONVT#getTile#us-states.json', async (t) => { const log = console.log; - // eslint-disable-next-line @typescript-eslint/no-empty-function console.log = function () {}; const geojson = await getJSON('us-states.json'); diff --git a/modules/mvt/test/lib/geojson-tiler/index.ts b/modules/mvt/test/lib/geojson-tiler/index.ts index f8d473a5e3..3c50898396 100644 --- a/modules/mvt/test/lib/geojson-tiler/index.ts +++ b/modules/mvt/test/lib/geojson-tiler/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import './clip.spec'; import './simplify.spec'; diff --git a/modules/mvt/test/lib/geojson-tiler/multi-world.spec.ts b/modules/mvt/test/lib/geojson-tiler/multi-world.spec.ts index f6800b6d2a..510914d54d 100644 --- a/modules/mvt/test/lib/geojson-tiler/multi-world.spec.ts +++ b/modules/mvt/test/lib/geojson-tiler/multi-world.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license import test from 'tape-promise/tape'; diff --git a/modules/mvt/test/lib/geojson-tiler/simplify.spec.ts b/modules/mvt/test/lib/geojson-tiler/simplify.spec.ts index a755e9ab9c..c226aaa7d7 100644 --- a/modules/mvt/test/lib/geojson-tiler/simplify.spec.ts +++ b/modules/mvt/test/lib/geojson-tiler/simplify.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license import test from 'tape-promise/tape'; diff --git a/modules/mvt/test/mvt-loader.bench.js b/modules/mvt/test/mvt-loader.bench.ts similarity index 93% rename from modules/mvt/test/mvt-loader.bench.js rename to modules/mvt/test/mvt-loader.bench.ts index f36aaac263..d6bc555296 100644 --- a/modules/mvt/test/mvt-loader.bench.js +++ b/modules/mvt/test/mvt-loader.bench.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import {MVTLoader} from '@loaders.gl/mvt'; import {fetchFile, parse} from '@loaders.gl/core'; import {geojsonToBinary} from '@loaders.gl/gis'; @@ -12,7 +15,7 @@ const testfiles = { // See `data/fetch_data.py` }; -const DATA_PATH = '@loaders.gl/mvt/test/data/'; +const DATA_PATH = '@loaders.gl/mvt/test/data/mvt'; // Benchmark to compare old method of parsing binary // format via an intermediate geojson step with the diff --git a/modules/mvt/test/mvt-loader.spec.js b/modules/mvt/test/mvt-loader.spec.ts similarity index 84% rename from modules/mvt/test/mvt-loader.spec.js rename to modules/mvt/test/mvt-loader.spec.ts index 2ef7b71dd5..696e07b3ed 100644 --- a/modules/mvt/test/mvt-loader.spec.js +++ b/modules/mvt/test/mvt-loader.spec.ts @@ -1,35 +1,39 @@ -/** @typedef {import('@loaders.gl/schema').BinaryFeatures} BinaryFeatures */ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +// import type {BinaryFeatureCollection} from '@loaders.gl/schema'; import test from 'tape-promise/tape'; -import {MVTLoader} from '@loaders.gl/mvt'; +import {MVTLoader, MVTLoaderOptions} from '@loaders.gl/mvt'; import {setLoaderOptions, fetchFile, parse, parseSync} from '@loaders.gl/core'; import {geojsonToBinary, binaryToGeojson} from '@loaders.gl/gis'; import {TEST_EXPORTS} from '../src/lib/binary-vector-tile/vector-tile-feature'; const {classifyRings} = TEST_EXPORTS; -const MVT_POINTS_DATA_URL = '@loaders.gl/mvt/test/data/points_4-2-6.mvt'; -const MVT_LINES_DATA_URL = '@loaders.gl/mvt/test/data/lines_2-2-1.mvt'; -const MVT_POLYGONS_DATA_URL = '@loaders.gl/mvt/test/data/polygons_10-133-325.mvt'; +const MVT_POINTS_DATA_URL = '@loaders.gl/mvt/test/data/mvt/points_4-2-6.mvt'; +const MVT_LINES_DATA_URL = '@loaders.gl/mvt/test/data/mvt/lines_2-2-1.mvt'; +const MVT_POLYGONS_DATA_URL = '@loaders.gl/mvt/test/data/mvt/polygons_10-133-325.mvt'; const MVT_POLYGON_ZERO_SIZE_HOLE_DATA_URL = - '@loaders.gl/mvt/test/data/polygon_with_zero_size_hole.mvt'; + '@loaders.gl/mvt/test/data/mvt/polygon_with_zero_size_hole.mvt'; const MVT_MULTIPLE_LAYERS_DATA_URL = - '@loaders.gl/mvt/test/data/lines_10-501-386_multiplelayers.mvt'; -const WITH_FEATURE_ID = '@loaders.gl/mvt/test/data/with_feature_id.mvt'; + '@loaders.gl/mvt/test/data/mvt/lines_10-501-386_multiplelayers.mvt'; +const WITH_FEATURE_ID = '@loaders.gl/mvt/test/data/mvt/with_feature_id.mvt'; // Geometry Array Results -import decodedPolygonsGeometry from '@loaders.gl/mvt/test/results/decoded_mvt_polygons_array.json'; +// // GeoJSON Results +import decodedPolygonsGeometry from '@loaders.gl/mvt/test/data/mvt-results/decoded_mvt_polygons_array.json' assert {type: 'json'}; // GeoJSON Results -import decodedPointsGeoJSON from '@loaders.gl/mvt/test/results/decoded_mvt_points.json'; -import decodedLinesGeoJSON from '@loaders.gl/mvt/test/results/decoded_mvt_lines.json'; -import decodedPolygonsGeoJSON from '@loaders.gl/mvt/test/results/decoded_mvt_polygons.json'; +import decodedPointsGeoJSON from '@loaders.gl/mvt/test/data/mvt-results/decoded_mvt_points.json' assert {type: 'json'}; +import decodedLinesGeoJSON from '@loaders.gl/mvt/test/data/mvt-results/decoded_mvt_lines.json' assert {type: 'json'}; +import decodedPolygonsGeoJSON from '@loaders.gl/mvt/test/data/mvt-results/decoded_mvt_polygons.json' assert {type: 'json'}; // Rings -import ringsSingleRing from '@loaders.gl/mvt/test/data/rings_single_ring.json'; -import ringsRingAndHole from '@loaders.gl/mvt/test/data/rings_ring_and_hole.json'; -import ringsTwoRings from '@loaders.gl/mvt/test/data/rings_two_rings.json'; -import ringsZeroSizeHole from '@loaders.gl/mvt/test/data/rings_zero_size_hole.json'; +import ringsSingleRing from '@loaders.gl/mvt/test/data/rings/rings_single_ring.json' assert {type: 'json'}; +import ringsRingAndHole from '@loaders.gl/mvt/test/data/rings/rings_ring_and_hole.json' assert {type: 'json'}; +import ringsTwoRings from '@loaders.gl/mvt/test/data/rings/rings_two_rings.json' assert {type: 'json'}; +import ringsZeroSizeHole from '@loaders.gl/mvt/test/data/rings/rings_zero_size_hole.json' assert {type: 'json'}; setLoaderOptions({ _workerType: 'test' @@ -101,7 +105,7 @@ for (const binary of [true, false]) { const response = await fetchFile(MVT_POINTS_DATA_URL); const mvtArrayBuffer = await response.arrayBuffer(); - const loaderOptions = { + const loaderOptions: MVTLoaderOptions = { mvt: { coordinates: 'wgs84', tileIndex: { @@ -132,7 +136,7 @@ for (const binary of [true, false]) { const response = await fetchFile(MVT_LINES_DATA_URL); const mvtArrayBuffer = await response.arrayBuffer(); - const loaderOptions = { + const loaderOptions: MVTLoaderOptions = { mvt: { coordinates: 'wgs84', tileIndex: { @@ -163,7 +167,7 @@ for (const binary of [true, false]) { const response = await fetchFile(MVT_POLYGONS_DATA_URL); const mvtArrayBuffer = await response.arrayBuffer(); - const loaderOptions = { + const loaderOptions: MVTLoaderOptions = { mvt: { coordinates: 'wgs84', tileIndex: { @@ -195,7 +199,7 @@ test('Should raise an error when coordinates param is wgs84 and tileIndex is mis const response = await fetchFile(MVT_POINTS_DATA_URL); const mvtArrayBuffer = await response.arrayBuffer(); - const loaderOptions = { + const loaderOptions: MVTLoaderOptions = { mvt: {coordinates: 'wgs84'} }; @@ -208,7 +212,7 @@ test('Should add layer name to custom property', async (t) => { const response = await fetchFile(MVT_POINTS_DATA_URL); const mvtArrayBuffer = await response.arrayBuffer(); - const loaderOptions = { + const loaderOptions: MVTLoaderOptions = { mvt: {layerProperty: 'layerSource'} }; @@ -222,7 +226,7 @@ test('Should return features from selected layers when layers property is provid const response = await fetchFile(MVT_MULTIPLE_LAYERS_DATA_URL); const mvtArrayBuffer = await response.arrayBuffer(); - const loaderOptions = { + const loaderOptions: MVTLoaderOptions = { mvt: {layers: ['layer1']} }; diff --git a/modules/mvt/test/mvt-source.spec.ts b/modules/mvt/test/mvt-source.spec.ts new file mode 100644 index 0000000000..1b8a30fbd6 --- /dev/null +++ b/modules/mvt/test/mvt-source.spec.ts @@ -0,0 +1,239 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import {isBrowser} from '@loaders.gl/core'; + +import {TILESETS} from './data/tilesets'; +import {MVTSource} from '@loaders.gl/mvt'; + +test('MVTSource#urls', async (t) => { + if (!isBrowser) { + t.comment('MVTSource currently only supported in browser'); + t.end(); + return; + } + for (const tilesetUrl of TILESETS) { + const source = new MVTSource({url: tilesetUrl}); + t.ok(source); + const metadata = await source.getMetadata(); + t.ok(metadata); + // console.error(JSON.stringify(metadata.tileJSON, null, 2)); + } + t.end(); +}); + +test('MVTSource#Blobs', async (t) => { + if (!isBrowser) { + t.comment('MVTSource currently only supported in browser'); + t.end(); + return; + } + for (const tilesetUrl of TILESETS) { + const source = new MVTSource({url: tilesetUrl}); + t.ok(source); + const metadata = await source.getMetadata(); + t.ok(metadata); + // console.error(JSON.stringify(metadata.tileJSON, null, 2)); + } + t.end(); +}); + +// TBA - TILE LOADING TESTS + +/* +import test from 'tape-promise/tape'; +import {validateLoader} from 'test/common/conformance'; + +import {load} from '@loaders.gl/core'; +import {PMTilesLoader} from '@loaders.gl/pmtiles'; + +import {PMTILESETS} from './data/tilesets'; + +test('PMTilesLoader#loader conformance', (t) => { + validateLoader(t, PMTilesLoader, 'PMTilesLoader'); + t.end(); +}); + +test.skip('PMTilesLoader#load', async (t) => { + for (const tilesetUrl of PMTILESETS) { + const metadata = await load(tilesetUrl, PMTilesLoader); + t.ok(metadata); + } + t.end(); +}); + +/* +// echo '{"type":"Polygon","coordinates":[[[0,0],[0,1],[1,1],[1,0],[0,0]]]}' | ./tippecanoe -zg -o test_fixture_1.pmtiles +test('cache getHeader', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + const cache = new SharedPromiseCache(); + const header = await cache.getHeader(source); + t.strictEqual(header.rootDirectoryOffset, 127); + t.strictEqual(header.rootDirectoryLength, 25); + t.strictEqual(header.jsonMetadataOffset, 152); + t.strictEqual(header.jsonMetadataLength, 247); + t.strictEqual(header.leafDirectoryOffset, 0); + t.strictEqual(header.leafDirectoryLength, 0); + t.strictEqual(header.tileDataOffset, 399); + t.strictEqual(header.tileDataLength, 69); + t.strictEqual(header.numAddressedTiles, 1); + t.strictEqual(header.numTileEntries, 1); + t.strictEqual(header.numTileContents, 1); + t.strictEqual(header.clustered, false); + t.strictEqual(header.internalCompression, 2); + t.strictEqual(header.tileCompression, 2); + t.strictEqual(header.tileType, 1); + t.strictEqual(header.minZoom, 0); + t.strictEqual(header.maxZoom, 0); + t.strictEqual(header.minLon, 0); + t.strictEqual(header.minLat, 0); + // t.strictEqual(header.maxLon,1); // TODO fix me + t.strictEqual(header.maxLat, 1); +}); + +test('cache check against empty', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/empty.pmtiles', '1'); + const cache = new SharedPromiseCache(); + t.rejects(async () => { + await cache.getHeader(source); + }); +}); + +test('cache check magic number', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/invalid.pmtiles', '1'); + const cache = new SharedPromiseCache(); + t.rejects(async () => { + await cache.getHeader(source); + }); +}); + +test('cache check future spec version', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/invalid_v4.pmtiles', '1'); + const cache = new SharedPromiseCache(); + t.rejects(async () => { + await cache.getHeader(source); + }); +}); + +test('cache getDirectory', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + + let cache = new SharedPromiseCache(6400, false); + let header = await cache.getHeader(source); + t.strictEqual(cache.cache.size, 1); + + cache = new SharedPromiseCache(6400, true); + header = await cache.getHeader(source); + + // prepopulates the root directory + t.strictEqual(cache.cache.size, 2); + + const directory = await cache.getDirectory( + source, + header.rootDirectoryOffset, + header.rootDirectoryLength, + header + ); + t.strictEqual(directory.length, 1); + t.strictEqual(directory[0].tileId, 0); + t.strictEqual(directory[0].offset, 0); + t.strictEqual(directory[0].length, 69); + t.strictEqual(directory[0].runLength, 1); + + for (const v of cache.cache.values()) { + t.ok(v.lastUsed > 0); + } +}); + +test('multiple sources in a single cache', async (t) => { + const cache = new SharedPromiseCache(); + const source1 = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + const source2 = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '2'); + await cache.getHeader(source1); + t.strictEqual(cache.cache.size, 2); + await cache.getHeader(source2); + t.strictEqual(cache.cache.size, 4); +}); + +test('etags are part of key', async (t) => { + const cache = new SharedPromiseCache(6400, false); + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + source.etag = 'etag_1'; + let header = await cache.getHeader(source); + t.strictEqual(header.etag, 'etag_1'); + + source.etag = 'etag_2'; + + t.rejects(async () => { + await cache.getDirectory( + source, + header.rootDirectoryOffset, + header.rootDirectoryLength, + header + ); + }); + + cache.invalidate(source, 'etag_2'); + header = await cache.getHeader(source); + t.ok( + await cache.getDirectory(source, header.rootDirectoryOffset, header.rootDirectoryLength, header) + ); +}); + +test.skip('soft failure on etag weirdness', async (t) => { + const cache = new SharedPromiseCache(6400, false); + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + source.etag = 'etag_1'; + let header = await cache.getHeader(source); + t.strictEqual(header.etag, 'etag_1'); + + source.etag = 'etag_2'; + + t.rejects(async () => { + await cache.getDirectory( + source, + header.rootDirectoryOffset, + header.rootDirectoryLength, + header + ); + }); + + source.etag = 'etag_1'; + cache.invalidate(source, 'etag_2'); + + header = await cache.getHeader(source); + t.strictEqual(header.etag, undefined); +}); + +test('cache pruning by byte size', async (t) => { + const cache = new SharedPromiseCache(2, false); + cache.cache.set('0', {lastUsed: 0, data: Promise.resolve([])}); + cache.cache.set('1', {lastUsed: 1, data: Promise.resolve([])}); + cache.cache.set('2', {lastUsed: 2, data: Promise.resolve([])}); + cache.prune(); + t.strictEqual(cache.cache.size, 2); + t.ok(cache.cache.get('2')); + t.ok(cache.cache.get('1')); + t.ok(!cache.cache.get('0')); +}); + +test('pmtiles get metadata', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + const p = new PMTiles(source); + const metadata = await p.getMetadata(); + t.ok(metadata.name); +}); + +// echo '{"type":"Polygon","coordinates":[[[0,0],[0,1],[1,0],[0,0]]]}' | ./tippecanoe -zg -o test_fixture_2.pmtiles +test('pmtiles handle retries', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + source.etag = '1'; + const p = new PMTiles(source); + const metadata = await p.getMetadata(); + t.ok(metadata.name); + source.etag = '2'; + source.replaceData('@loaders.gl/pmtiles/test/data/test_fixture_2.pmtiles'); + t.ok(await p.getZxy(0, 0, 0)); +}); +*/ diff --git a/modules/mvt/test/tilejson-loader.spec.ts b/modules/mvt/test/tilejson-loader.spec.ts new file mode 100644 index 0000000000..35f4a4b7b0 --- /dev/null +++ b/modules/mvt/test/tilejson-loader.spec.ts @@ -0,0 +1,37 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import {validateLoader} from 'test/common/conformance'; + +import {load, JSONLoader} from '@loaders.gl/core'; +import {TileJSONLoader} from '@loaders.gl/mvt'; + +import {TILEJSONS} from './data/tilejson/tilejson'; + +const TIPPECANOE_TILEJSON = '@loaders.gl/mvt/test/data/tilejson/tippecanoe.tilejson'; +const TIPPECANOE_EXPECTED = '@loaders.gl/mvt/test/data/tilejson/tippecanoe.expected.json'; + +test('TileJSONLoader#loader conformance', (t) => { + validateLoader(t, TileJSONLoader, 'TileJSONLoader'); + t.end(); +}); + +test('TileJSONLoader#load', async (t) => { + for (const tileJSON of TILEJSONS) { + const metadata = await load(tileJSON.url, TileJSONLoader); + t.ok(metadata.layers); + // TODO - actually check results, add tilejsons with fields + // t.deepEqual(metadata, parsedMetadata); + // console.error(JSON.stringify(metadata, null, 2)); + } + t.end(); +}); + +test.skip('TileJSONLoader#tippecanoe', async (t) => { + const metadata = await load(TIPPECANOE_TILEJSON, TileJSONLoader); + const expected = await load(TIPPECANOE_EXPECTED, JSONLoader); + console.error(metadata, expected); + t.deepEqual(metadata, expected, 'Tippecanoe TileJSON loaded correctly'); + t.end(); +}); diff --git a/modules/mvt/tsconfig.json b/modules/mvt/tsconfig.json index a1f485e8d3..46b940c801 100644 --- a/modules/mvt/tsconfig.json +++ b/modules/mvt/tsconfig.json @@ -8,6 +8,7 @@ "outDir": "dist" }, "references": [ + {"path": "../images"}, {"path": "../loader-utils"}, {"path": "../gis"} ] diff --git a/modules/netcdf/package.json b/modules/netcdf/package.json index ac4d0f05db..c7fefac141 100644 --- a/modules/netcdf/package.json +++ b/modules/netcdf/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/netcdf", "description": "Loader for NetCDF", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -18,8 +19,15 @@ "NetCDF" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -27,11 +35,11 @@ "README.md" ], "scripts": { - "pre-build-disabled": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { - "@loaders.gl/loader-utils": "4.0.0-alpha.13" + "@loaders.gl/loader-utils": "4.0.3" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/netcdf/src/netcdf-loader.ts b/modules/netcdf/src/netcdf-loader.ts index c2c1c57b7b..76c7535deb 100644 --- a/modules/netcdf/src/netcdf-loader.ts +++ b/modules/netcdf/src/netcdf-loader.ts @@ -14,13 +14,14 @@ export type NetCDF = { export type NetCDFLoaderOptions = LoaderOptions & { netcdf?: { loadData?: boolean; + loadVariables?: boolean; }; }; /** * Worker loader for NETCDF */ -export const NetCDFWorkerLoader = { +export const NetCDFWorkerLoader: Loader = { name: 'NetCDF', id: 'mvt', module: 'mvt', @@ -42,7 +43,7 @@ export const NetCDFWorkerLoader = { /** * Loader for the NetCDF format */ -export const NetCDFLoader = { +export const NetCDFLoader: LoaderWithParser = { ...NetCDFWorkerLoader, parse: async (arrayBuffer, options) => parseNetCDF(arrayBuffer, options), binary: true @@ -61,7 +62,3 @@ function parseNetCDF(arrayBuffer: ArrayBuffer, options?: NetCDFLoaderOptions): N data: variables }; } - -// Type tests -export const _typecheckNetCDFWorkerLoader: Loader = NetCDFWorkerLoader; -export const _typecheckNetCDFLoader: LoaderWithParser = NetCDFLoader; diff --git a/modules/netcdf/test/netcdf-loader.spec.js b/modules/netcdf/test/netcdf-loader.spec.js index 5d75584b22..b9c7a8d57b 100644 --- a/modules/netcdf/test/netcdf-loader.spec.js +++ b/modules/netcdf/test/netcdf-loader.spec.js @@ -14,7 +14,7 @@ describe('NetCDFLoader', () => { // }); it('read header information', async () => { - const result = await load(`${DATA_PATH}/madis-sao.nc`, [NetCDFLoader]); + const result = await load(`${DATA_PATH}/madis-sao.nc`, NetCDFLoader); expect(result.loaderData.version).toBe(1); expect(result.loaderData.recordDimension).toStrictEqual({ length: 178, @@ -90,7 +90,7 @@ describe('NetCDFLoader', () => { }); it('read non-record variable', async () => { - const result = await load(`${DATA_PATH}/madis-sao.nc`, [NetCDFLoader], { + const result = await load(`${DATA_PATH}/madis-sao.nc`, NetCDFLoader, { netcdf: {loadData: true} }); @@ -98,7 +98,7 @@ describe('NetCDFLoader', () => { }); it('read 2 dimensional variable', async () => { - const result = await load(`${DATA_PATH}/ichthyop.nc`, [NetCDFLoader], { + const result = await load(`${DATA_PATH}/ichthyop.nc`, NetCDFLoader, { netcdf: {loadData: true} }); expect(result.data.time).toHaveLength(49); @@ -109,7 +109,7 @@ describe('NetCDFLoader', () => { }); it('read record variable with string', async () => { - const result = await load(`${DATA_PATH}/madis-sao.nc`, [NetCDFLoader], { + const result = await load(`${DATA_PATH}/madis-sao.nc`, NetCDFLoader, { netcdf: {loadData: true} }); @@ -119,7 +119,7 @@ describe('NetCDFLoader', () => { }); it('read non-record variable with object', async () => { - const result = await load(`${DATA_PATH}/madis-sao.nc`, [NetCDFLoader], { + const result = await load(`${DATA_PATH}/madis-sao.nc`, NetCDFLoader, { netcdf: {loadData: true} }); @@ -134,7 +134,7 @@ describe('NetCDFLoader', () => { }); it('read 64 bit offset file', async () => { - const result = await load(`${DATA_PATH}/model1_md2.nc`, [NetCDFLoader], { + const result = await load(`${DATA_PATH}/model1_md2.nc`, NetCDFLoader, { netcdf: {loadData: true} }); expect(result.loaderData.version).toBe(2); @@ -143,7 +143,7 @@ describe('NetCDFLoader', () => { }); it('read agilent hplc file file', async () => { - const result = await load(`${DATA_PATH}/agilent_hplc.cdf`, [NetCDFLoader], { + const result = await load(`${DATA_PATH}/agilent_hplc.cdf`, NetCDFLoader, { netcdf: {loadData: true} }); diff --git a/modules/obj/package.json b/modules/obj/package.json index 3d8ff6b344..6980b444c2 100644 --- a/modules/obj/package.json +++ b/modules/obj/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/obj", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loader for the OBJ format", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "OBJ" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -29,14 +37,14 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-worker && npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-worker && npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/obj-worker.ts --bundle --outfile=dist/obj-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { "@babel/runtime": "^7.3.1", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13" + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/obj/src/bundle.ts b/modules/obj/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/obj/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/obj/src/index.ts b/modules/obj/src/index.ts index 7ec5b1a304..d7d566f72c 100644 --- a/modules/obj/src/index.ts +++ b/modules/obj/src/index.ts @@ -34,6 +34,3 @@ export const MTLLoader: LoaderWithParser parseMTL(new TextDecoder().decode(arrayBuffer), options?.mtl), parseTextSync: (text: string, options?: MTLLoaderOptions) => parseMTL(text, options?.mtl) }; - -export const _typecheckOBJLoader: LoaderWithParser = OBJLoader; -export const _typecheckMTLLoader: LoaderWithParser = MTLLoader; diff --git a/modules/obj/src/lib/get-obj-schema.ts b/modules/obj/src/lib/get-obj-schema.ts index 00137b4dca..a7a678c5fe 100644 --- a/modules/obj/src/lib/get-obj-schema.ts +++ b/modules/obj/src/lib/get-obj-schema.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Schema, SchemaMetadata, Field, MeshAttribute} from '@loaders.gl/schema'; import {getDataTypeFromArray} from '@loaders.gl/schema'; diff --git a/modules/obj/src/lib/parse-mtl.ts b/modules/obj/src/lib/parse-mtl.ts index bf5c76ec69..d54189f050 100644 --- a/modules/obj/src/lib/parse-mtl.ts +++ b/modules/obj/src/lib/parse-mtl.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from THREE.js under MIT license // https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/MTLLoader.js diff --git a/modules/obj/src/lib/parse-obj-meshes.ts b/modules/obj/src/lib/parse-obj-meshes.ts index 90297adc96..7d504161b5 100644 --- a/modules/obj/src/lib/parse-obj-meshes.ts +++ b/modules/obj/src/lib/parse-obj-meshes.ts @@ -343,7 +343,7 @@ export function parseOBJMeshes(text) { switch (data[0]) { case 'v': state.vertices.push(parseFloat(data[1]), parseFloat(data[2]), parseFloat(data[3])); - if (data.length === 8) { + if (data.length >= 7) { state.colors.push(parseFloat(data[4]), parseFloat(data[5]), parseFloat(data[6])); } break; diff --git a/modules/obj/src/mtl-loader.ts b/modules/obj/src/mtl-loader.ts index b3481a79f9..6dd29a3e22 100644 --- a/modules/obj/src/mtl-loader.ts +++ b/modules/obj/src/mtl-loader.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Loader, LoaderOptions} from '@loaders.gl/loader-utils'; import type {MTLMaterial, ParseMTLOptions} from './lib/parse-mtl'; @@ -27,5 +28,3 @@ export const MTLLoader: Loader = { mtl: {} } }; - -export const _typecheckMTLLoader: Loader = MTLLoader; diff --git a/modules/obj/src/obj-loader.ts b/modules/obj/src/obj-loader.ts index 5b4dac0025..2952cd0d47 100644 --- a/modules/obj/src/obj-loader.ts +++ b/modules/obj/src/obj-loader.ts @@ -30,5 +30,3 @@ function testOBJFile(text: string): boolean { // TODO - There could be comment line first return text[0] === 'v'; } - -export const _typecheckOBJLoader: Loader = OBJLoader; diff --git a/modules/obj/test/data/cube-vertex-colors.obj b/modules/obj/test/data/cube-vertex-colors.obj new file mode 100644 index 0000000000..6c5484f511 --- /dev/null +++ b/modules/obj/test/data/cube-vertex-colors.obj @@ -0,0 +1,24 @@ +# OBJ file format with vertex colors + +o cube +v 1.000000 1.000000 -1.000000 0.2801 0.4429 0.8987 +v 1.000000 -1.000000 -1.000000 0.6907 0.2524 0.8987 +v 1.000000 1.000000 1.000000 0.2801 0.4429 0.8987 +v 1.000000 -1.000000 1.000000 0.6907 0.2524 0.8987 +v -1.000000 1.000000 -1.000000 0.2801 0.4429 0.8987 +v -1.000000 -1.000000 -1.000000 0.6907 0.2524 0.8987 +v -1.000000 1.000000 1.000000 0.2801 0.4429 0.8987 +v -1.000000 -1.000000 1.000000 0.6907 0.2524 0.8987 +vn -0.0000 1.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -1.0000 -0.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +s 0 +f 1//1 5//1 7//1 3//1 +f 4//2 3//2 7//2 8//2 +f 8//3 7//3 5//3 6//3 +f 6//4 2//4 4//4 8//4 +f 2//5 1//5 3//5 4//5 +f 6//6 5//6 1//6 2//6 diff --git a/modules/obj/test/obj-loader.spec.js b/modules/obj/test/obj-loader.spec.js index 81979c5a66..6e25ee994d 100644 --- a/modules/obj/test/obj-loader.spec.js +++ b/modules/obj/test/obj-loader.spec.js @@ -4,10 +4,12 @@ import {validateLoader, validateMeshCategoryData} from 'test/common/conformance' import {OBJLoader, OBJWorkerLoader} from '@loaders.gl/obj'; import {setLoaderOptions, load} from '@loaders.gl/core'; +import {equals} from '@math.gl/core'; const OBJ_ASCII_URL = '@loaders.gl/obj/test/data/bunny.obj'; const OBJ_NORMALS_URL = '@loaders.gl/obj/test/data/cube.obj'; const OBJ_MULTI_PART_URL = '@loaders.gl/obj/test/data/magnolia.obj'; +const OBJ_VERTEX_COLOR_URL = '@loaders.gl/obj/test/data/cube-vertex-colors.obj'; setLoaderOptions({ _workerType: 'test' @@ -76,6 +78,36 @@ test('OBJLoader#parseText - multi-part object', async (t) => { t.end(); }); +test('OBJLoader#parseText - object with vertex colors', async (t) => { + const data = await load(OBJ_VERTEX_COLOR_URL, OBJLoader); + validateMeshCategoryData(t, data); + + t.equal(data.attributes.POSITION.value.length, 108, 'POSITION attribute was found'); + t.equal(data.attributes.POSITION.size, 3, 'POSITION attribute was found'); + t.equal(data.attributes.NORMAL.value.length, 108, 'NORMAL attribute was found'); + t.equal(data.attributes.NORMAL.size, 3, 'NORMAL attribute was found'); + t.equal(data.attributes.COLOR_0.value.length, 108, 'COLOR_0 attribute was found'); + t.equal(data.attributes.COLOR_0.size, 3, 'COLOR_0 attribute was found'); + + // Test two verticies with different colors. + const vertex1Color = [0.2801, 0.4429, 0.8987]; + t.ok( + vertex1Color.every((value, index) => + equals(data.attributes.COLOR_0.value[index], value, 0.0001) + ), + 'vertex 1 color parsed as float rgb' + ); + + const vertex2Color = [0.6907, 0.2524, 0.8987]; + t.ok( + vertex2Color.every((value, index) => + equals(data.attributes.COLOR_0.value[index + 18], value, 0.0001) + ), + 'vertex 2 color parsed as float rgb' + ); + t.end(); +}); + test('OBJWorkerLoader#parse(text)', async (t) => { if (typeof Worker === 'undefined') { t.comment('Worker is not usable in non-browser environments'); diff --git a/modules/parquet/package.json b/modules/parquet/package.json index cec6883693..909308f3bd 100644 --- a/modules/parquet/package.json +++ b/modules/parquet/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/parquet", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loader for Apache Parquet files", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "Apache Parquet" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -29,9 +37,10 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-worker && npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --outfile=dist/dist.min.js --bundle --minify --sourcemap --external:{util,fs,path,crypto}", - "build-worker": "esbuild src/workers/parquet-worker.ts --outfile=dist/parquet-worker.js --bundle --minify --sourcemap --external:{util,fs,path,crypto} --define:__VERSION__=\\\"$npm_package_version\\\"" + "pre-build": "echo npm run build-worker && yarn run copy-wasm", + "build-bundle": "ocular-bundle ./src/index.ts", + "build-worker": "esbuild src/workers/parquet-worker.ts --outfile=dist/parquet-worker.js --bundle --minify --sourcemap --external:{util,fs,path,crypto,events} --define:__VERSION__=\\\"$npm_package_version\\\"", + "copy-wasm": "cp ../../node_modules/parquet-wasm/esm2/arrow1_bg.wasm dist/arrow1_bg.wasm" }, "browser": { "child_process": false, @@ -39,34 +48,48 @@ "tls": false, "stream": false, "fs": false, - "./src/lib/wasm/load-wasm/load-wasm-node.ts": "./src/lib/wasm/load-wasm/load-wasm-browser.ts" + "util": false, + "events": false, + "./src/polyfills/buffer/buffer-polyfill.node.ts": "./src/polyfills/buffer/buffer-polyfill.browser.ts", + "./dist/polyfills/buffer/buffer-polyfill.node.js": "./dist/polyfills/buffer/buffer-polyfill.browser.js", + "./src/lib/wasm/load-wasm-node.ts": "./src/lib/wasm/load-wasm-browser.ts", + "./dist/lib/wasm/load-wasm-node.js": "./dist/lib/wasm/load-wasm-browser.js" }, + "comments": [ + "base64-js and ieee754 are used by buffer polyfill" + ], "dependencies": { - "@loaders.gl/bson": "4.0.0-alpha.13", - "@loaders.gl/compression": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", + "@loaders.gl/arrow": "4.0.3", + "@loaders.gl/bson": "4.0.3", + "@loaders.gl/compression": "4.0.3", + "@loaders.gl/gis": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "@loaders.gl/wkt": "4.0.3", "async-mutex": "^0.2.2", + "base64-js": "^1.3.1", "brotli": "^1.3.2", + "ieee754": "^1.2.1", "int53": "^0.2.4", "lz4js": "^0.2.0", "node-int64": "^0.4.0", "object-stream": "0.0.1", "parquet-wasm": "^0.3.1", "snappyjs": "^0.6.0", - "thrift": "^0.14.2", + "thrift": "^0.19.0", + "util": "^0.12.5", "varint": "^5.0.0", "zstd-codec": "^0.1" }, "peerDependencies": { - "apache-arrow": "^9.0.0" + "apache-arrow": "^13.0.0" }, "devDependencies": { "@types/node": "^10.14.15", "@types/node-int64": "^0.4.29", "@types/thrift": "^0.10.8", "@types/varint": "^5.0.0", - "apache-arrow": "^9.0.0" + "apache-arrow": "^13.0.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/parquet/src/bundle.ts b/modules/parquet/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/parquet/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/parquet/src/constants.ts b/modules/parquet/src/constants.ts index 738cf7bb7d..4ca177e88f 100644 --- a/modules/parquet/src/constants.ts +++ b/modules/parquet/src/constants.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + // Forked from https://github.com/kbajalc/parquets under MIT license (Copyright (c) 2017 ironSource Ltd.) /** * Parquet File Magic String diff --git a/modules/parquet/src/index.ts b/modules/parquet/src/index.ts index 7940f0b089..f123709bdb 100644 --- a/modules/parquet/src/index.ts +++ b/modules/parquet/src/index.ts @@ -1,64 +1,78 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +export {Buffer} from './polyfills/buffer/install-buffer-polyfill'; import type {LoaderWithParser} from '@loaders.gl/loader-utils'; import type { ObjectRowTable, ObjectRowTableBatch, ColumnarTable, - ColumnarTableBatch + ColumnarTableBatch, + GeoJSONTable, + GeoJSONTableBatch } from '@loaders.gl/schema'; -import type {Table as ArrowTable} from 'apache-arrow'; + +// import {ArrowTable, ArrowTableBatch} from '@loaders.gl/arrow'; // ParquetLoader +import {BlobFile} from '@loaders.gl/loader-utils'; import { ParquetLoader as ParquetWorkerLoader, - ParquetLoader as ParquetColumnarWorkerLoader, + ParquetColumnarLoader as ParquetColumnarWorkerLoader, ParquetLoaderOptions } from './parquet-loader'; -import {parseParquet, parseParquetFileInBatches} from './lib/parsers/parse-parquet-to-rows'; +import {parseParquetFile, parseParquetFileInBatches} from './lib/parsers/parse-parquet-to-rows'; import { - parseParquetInColumns, + parseParquetFileInColumns, parseParquetFileInColumnarBatches } from './lib/parsers/parse-parquet-to-columns'; -import {parseParquetWasm, ParquetWasmLoaderOptions} from './lib/wasm/parse-parquet-wasm'; -import {ParquetWasmLoader as ParquetWasmWorkerLoader} from './parquet-wasm-loader'; +// import type {ParquetWasmLoaderOptions} from './lib/wasm/parse-parquet-wasm'; +// import {parseParquetWasm} from './lib/wasm/parse-parquet-wasm'; +// import {ParquetWasmLoader as ParquetWasmWorkerLoader} from './parquet-wasm-loader'; -export {ParquetWorkerLoader, ParquetWasmWorkerLoader}; +export {ParquetWorkerLoader}; +// export {ParquetWasmWorkerLoader}; /** ParquetJS table loader */ export const ParquetLoader: LoaderWithParser< - ObjectRowTable, - ObjectRowTableBatch, + ObjectRowTable | GeoJSONTable, + ObjectRowTableBatch | GeoJSONTableBatch, ParquetLoaderOptions > = { ...ParquetWorkerLoader, - parse: parseParquet, + parse(arrayBuffer: ArrayBuffer, options?: ParquetLoaderOptions) { + return parseParquetFile(new BlobFile(arrayBuffer), options); + }, + parseFile: parseParquetFile, parseFileInBatches: parseParquetFileInBatches }; /** ParquetJS table loader */ -// @ts-expect-error export const ParquetColumnarLoader: LoaderWithParser< ColumnarTable, ColumnarTableBatch, ParquetLoaderOptions > = { ...ParquetColumnarWorkerLoader, - parse: parseParquetInColumns, + parse(arrayBuffer: ArrayBuffer, options?: ParquetLoaderOptions) { + return parseParquetFileInColumns(new BlobFile(arrayBuffer), options); + }, + parseFile: parseParquetFileInColumns, parseFileInBatches: parseParquetFileInColumnarBatches }; -export const ParquetWasmLoader: LoaderWithParser = { - ...ParquetWasmWorkerLoader, - parse: parseParquetWasm -}; +// export const ParquetWasmLoader: LoaderWithParser = { +// ...ParquetWasmWorkerLoader, +// parse: parseParquetWasm +// }; // ParquetWriter export {ParquetWriter as _ParquetWriter} from './parquet-writer'; -export {ParquetWasmWriter} from './parquet-wasm-writer'; +// export {ParquetWasmWriter} from './parquet-wasm-writer'; // EXPERIMENTAL - expose the internal parquetjs API @@ -73,11 +87,5 @@ export { convertParquetSchema as convertParquetToArrowSchema } from './lib/arrow/convert-schema-from-parquet'; -// TESTS -export const _typecheckParquetLoader: LoaderWithParser = ParquetLoader; - -// Geo Metadata -export {default as geoJSONSchema} from './lib/geo/geoparquet-schema'; - -export type {GeoMetadata} from './lib/geo/decode-geo-metadata'; -export {getGeoMetadata, setGeoMetadata, unpackGeoMetadata} from './lib/geo/decode-geo-metadata'; +// Experimental +export {BufferPolyfill, installBufferPolyfill} from './polyfills/buffer'; diff --git a/modules/parquet/src/lib/arrow/convert-row-group-to-columns.ts b/modules/parquet/src/lib/arrow/convert-row-group-to-columns.ts index 41f62d543f..c4aecaab84 100644 --- a/modules/parquet/src/lib/arrow/convert-row-group-to-columns.ts +++ b/modules/parquet/src/lib/arrow/convert-row-group-to-columns.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {Schema} from '@loaders.gl/schema'; import {ParquetRowGroup} from '@loaders.gl/parquet/parquetjs/schema/declare'; diff --git a/modules/parquet/src/lib/arrow/convert-schema-from-parquet.ts b/modules/parquet/src/lib/arrow/convert-schema-from-parquet.ts index 3c5063a8ce..bb90219d3b 100644 --- a/modules/parquet/src/lib/arrow/convert-schema-from-parquet.ts +++ b/modules/parquet/src/lib/arrow/convert-schema-from-parquet.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {Schema, Field, DataType} from '@loaders.gl/schema'; diff --git a/modules/parquet/src/lib/arrow/convert-schema-to-parquet.ts b/modules/parquet/src/lib/arrow/convert-schema-to-parquet.ts index f1c60f6dbb..970b87464c 100644 --- a/modules/parquet/src/lib/arrow/convert-schema-to-parquet.ts +++ b/modules/parquet/src/lib/arrow/convert-schema-to-parquet.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // import type {ParquetSchema} from '../../parquetjs/schema/schema'; import type { diff --git a/modules/parquet/src/lib/geo/decode-geo-metadata.ts b/modules/parquet/src/lib/geo/decode-geo-metadata.ts deleted file mode 100644 index acf2e56b06..0000000000 --- a/modules/parquet/src/lib/geo/decode-geo-metadata.ts +++ /dev/null @@ -1,108 +0,0 @@ -// loaders.gl, MIT license -import {Schema, Field} from '@loaders.gl/schema'; - -/* eslint-disable camelcase */ - -/** A geoarrow / geoparquet geo metadata object (stored in stringified form in the top level metadata 'geo' key) */ -export type GeoMetadata = { - version?: string; - primary_column?: string; - columns: Record; - [key: string]: unknown; -}; - -/** A geoarrow / geoparquet geo metadata for one geometry column */ -export type GeoColumnMetadata = { - bounding_box?: - | [number, number, number, number] - | [number, number, number, number, number, number]; - crs?: string; - geometry_type?: string[]; - edges?: string; - [key: string]: unknown; -}; - -/** - * Reads the GeoMetadata object from the metadata - * @note geoarrow / parquet schema is stringified into a single key-value pair in the parquet metadata */ -export function getGeoMetadata(schema: Schema): GeoMetadata | null { - const stringifiedGeoMetadata = schema.metadata.geo; - if (!stringifiedGeoMetadata) { - return null; - } - - try { - const geoMetadata = JSON.parse(stringifiedGeoMetadata) as GeoMetadata; - return geoMetadata; - } catch { - return null; - } -} - -/** - * Stores a geoarrow / geoparquet geo metadata object in the schema - * @note geoarrow / geoparquet geo metadata is a single stringified JSON field - */ -export function setGeoMetadata(schema: Schema, geoMetadata: GeoMetadata): void { - const stringifiedGeoMetadata = JSON.stringify(geoMetadata); - schema.metadata.geo = stringifiedGeoMetadata; -} - -/** - * Unpacks geo metadata into separate metadata fields (parses the long JSON string) - * @note geoarrow / parquet schema is stringified into a single key-value pair in the parquet metadata - */ -export function unpackGeoMetadata(schema: Schema): void { - const geoMetadata = getGeoMetadata(schema); - if (!geoMetadata) { - return; - } - - // Store Parquet Schema Level Metadata - - const {version, primary_column, columns} = geoMetadata; - if (version) { - schema.metadata['geo.version'] = version; - } - - if (primary_column) { - schema.metadata['geo.primary_column'] = primary_column; - } - - // store column names as comma separated list - schema.metadata['geo.columns'] = Object.keys(columns || {}).join(''); - - for (const [columnName, columnMetadata] of Object.entries(columns || {})) { - const field = schema.fields.find((field) => field.name === columnName); - if (field) { - if (field.name === primary_column) { - setFieldMetadata(field, 'geo.primary_field', 'true'); - } - unpackGeoFieldMetadata(field, columnMetadata); - } - } -} - -function unpackGeoFieldMetadata(field: Field, columnMetadata): void { - for (const [key, value] of Object.entries(columnMetadata || {})) { - switch (key) { - case 'geometry_type': - setFieldMetadata(field, `geo.${key}`, (value as string[]).join(',')); - break; - case 'bbox': - case 'crs': - case 'edges': - default: - setFieldMetadata( - field, - `geo.${key}`, - typeof value === 'string' ? value : JSON.stringify(value) - ); - } - } -} - -function setFieldMetadata(field: Field, key: string, value: string): void { - field.metadata = field.metadata || {}; - field.metadata[key] = value; -} diff --git a/modules/parquet/src/lib/parsers/get-parquet-schema.ts b/modules/parquet/src/lib/parsers/get-parquet-schema.ts new file mode 100644 index 0000000000..64fff63eb7 --- /dev/null +++ b/modules/parquet/src/lib/parsers/get-parquet-schema.ts @@ -0,0 +1,14 @@ +// loaders.gl +import {Schema} from '@loaders.gl/schema'; +import {ParquetReader} from '../../parquetjs/parser/parquet-reader'; +import {convertParquetSchema} from '../arrow/convert-schema-from-parquet'; +import {unpackGeoMetadata, unpackJSONStringMetadata} from '@loaders.gl/gis'; + +export async function getSchemaFromParquetReader(reader: ParquetReader): Promise { + const parquetSchema = await reader.getSchema(); + const parquetMetadata = await reader.getFileMetadata(); + const schema = convertParquetSchema(parquetSchema, parquetMetadata); + unpackGeoMetadata(schema); + unpackJSONStringMetadata(schema, 'pandas'); + return schema; +} diff --git a/modules/parquet/src/lib/parsers/parse-parquet-to-columns.ts b/modules/parquet/src/lib/parsers/parse-parquet-to-columns.ts index 35cf6fbab3..6c7c4fd6a3 100644 --- a/modules/parquet/src/lib/parsers/parse-parquet-to-columns.ts +++ b/modules/parquet/src/lib/parsers/parse-parquet-to-columns.ts @@ -1,23 +1,22 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -// import type {LoaderWithParser, Loader, LoaderOptions} from '@loaders.gl/loader-utils'; -import {ColumnarTable, ColumnarTableBatch, Schema} from '@loaders.gl/schema'; -import {makeReadableFile} from '@loaders.gl/loader-utils'; +import type {ColumnarTable, ColumnarTableBatch, Schema} from '@loaders.gl/schema'; +import type {ReadableFile} from '@loaders.gl/loader-utils'; import type {ParquetLoaderOptions} from '../../parquet-loader'; import {ParquetReader} from '../../parquetjs/parser/parquet-reader'; import {ParquetRowGroup} from '../../parquetjs/schema/declare'; import {ParquetSchema} from '../../parquetjs/schema/schema'; -import {convertParquetSchema} from '../arrow/convert-schema-from-parquet'; import {materializeColumns} from '../../parquetjs/schema/shred'; -// import {convertParquetRowGroupToColumns} from '../arrow/convert-row-group-to-columns'; -import {unpackGeoMetadata} from '../geo/decode-geo-metadata'; +import {getSchemaFromParquetReader} from './get-parquet-schema'; +import {installBufferPolyfill} from '../../polyfills/buffer'; -export async function parseParquetInColumns( - arrayBuffer: ArrayBuffer, +export async function parseParquetFileInColumns( + file: ReadableFile, options?: ParquetLoaderOptions ): Promise { - const blob = new Blob([arrayBuffer]); - for await (const batch of parseParquetFileInColumnarBatches(blob, options)) { + installBufferPolyfill(); + for await (const batch of parseParquetFileInColumnarBatches(file, options)) { return { shape: 'columnar-table', schema: batch.schema, @@ -28,24 +27,26 @@ export async function parseParquetInColumns( } export async function* parseParquetFileInColumnarBatches( - blob: Blob, + file: ReadableFile, options?: ParquetLoaderOptions ): AsyncIterable { - const file = makeReadableFile(blob); const reader = new ParquetReader(file); + + // Extract schema and geo metadata + const schema = await getSchemaFromParquetReader(reader); + const parquetSchema = await reader.getSchema(); - const parquetMetadata = await reader.getFileMetadata(); - const schema = convertParquetSchema(parquetSchema, parquetMetadata); - unpackGeoMetadata(schema); + + // Iterate over row batches const rowGroups = reader.rowGroupIterator(options?.parquet); for await (const rowGroup of rowGroups) { - yield convertRowGroupToTableBatch(parquetSchema, rowGroup, schema); + yield convertRowGroupToTableBatch(rowGroup, parquetSchema, schema); } } function convertRowGroupToTableBatch( - parquetSchema: ParquetSchema, rowGroup: ParquetRowGroup, + parquetSchema: ParquetSchema, schema: Schema ): ColumnarTableBatch { // const data = convertParquetRowGroupToColumns(schema, rowGroup); diff --git a/modules/parquet/src/lib/parsers/parse-parquet-to-rows.ts b/modules/parquet/src/lib/parsers/parse-parquet-to-rows.ts index 4f34078496..0473b1df20 100644 --- a/modules/parquet/src/lib/parsers/parse-parquet-to-rows.ts +++ b/modules/parquet/src/lib/parsers/parse-parquet-to-rows.ts @@ -1,45 +1,99 @@ // import type {LoaderWithParser, Loader, LoaderOptions} from '@loaders.gl/loader-utils'; // import {ColumnarTableBatch} from '@loaders.gl/schema'; -import {makeReadableFile} from '@loaders.gl/loader-utils'; -import {ObjectRowTable, ObjectRowTableBatch} from '@loaders.gl/schema'; +import type {ReadableFile} from '@loaders.gl/loader-utils'; +import type { + GeoJSONTable, + GeoJSONTableBatch, + ObjectRowTable, + ObjectRowTableBatch +} from '@loaders.gl/schema'; +import {convertWKBTableToGeoJSON} from '@loaders.gl/gis'; +import {WKTLoader, WKBLoader} from '@loaders.gl/wkt'; + import type {ParquetLoaderOptions} from '../../parquet-loader'; import type {ParquetRow} from '../../parquetjs/schema/declare'; import {ParquetReader} from '../../parquetjs/parser/parquet-reader'; +import {getSchemaFromParquetReader} from './get-parquet-schema'; +import {installBufferPolyfill} from '../../polyfills/buffer'; -export async function parseParquet( - arrayBuffer: ArrayBuffer, +export async function parseParquetFile( + file: ReadableFile, options?: ParquetLoaderOptions -): Promise { - const blob = new Blob([arrayBuffer]); +): Promise { + installBufferPolyfill(); + + const reader = new ParquetReader(file, { + preserveBinary: options?.parquet?.preserveBinary + }); + + const schema = await getSchemaFromParquetReader(reader); const rows: ParquetRow[] = []; - for await (const batch of parseParquetFileInBatches(blob, options)) { + + const rowBatches = reader.rowBatchIterator(options?.parquet); + for await (const rowBatch of rowBatches) { // we have only one input batch so return - for (const row of batch.data) { + for (const row of rowBatch) { rows.push(row); } } - - return { + const objectRowTable: ObjectRowTable = { shape: 'object-row-table', - // TODO - spread can fail for very large number of batches + schema, data: rows }; + + const shape = options?.parquet?.shape; + return convertTable(objectRowTable, shape); } export async function* parseParquetFileInBatches( - blob: Blob, + file: ReadableFile, options?: ParquetLoaderOptions -): AsyncIterable { - const file = makeReadableFile(blob); - const reader = new ParquetReader(file); +): AsyncIterable { + const reader = new ParquetReader(file, { + preserveBinary: options?.parquet?.preserveBinary + }); + + const schema = await getSchemaFromParquetReader(reader); const rowBatches = reader.rowBatchIterator(options?.parquet); for await (const rows of rowBatches) { - yield { + const objectRowTable: ObjectRowTable = { shape: 'object-row-table', - data: rows, + schema, + data: rows + }; + const shape = options?.parquet?.shape; + const table = convertTable(objectRowTable, shape); + + yield { batchType: 'data', + schema, + ...table, length: rows.length }; } } + +function convertTable( + objectRowTable: ObjectRowTable, + shape?: 'object-row-table' | 'geojson-table' +): ObjectRowTable | GeoJSONTable { + switch (shape) { + case 'object-row-table': + return objectRowTable; + + case 'geojson-table': + try { + return convertWKBTableToGeoJSON(objectRowTable, objectRowTable.schema!, [ + WKTLoader, + WKBLoader + ]); + } catch (error) { + return objectRowTable; + } + + default: + throw new Error(shape); + } +} diff --git a/modules/parquet/src/lib/wasm/encode-parquet-wasm.ts b/modules/parquet/src/lib/wasm/encode-parquet-wasm.ts index 39147d5f64..ed752b87d0 100644 --- a/modules/parquet/src/lib/wasm/encode-parquet-wasm.ts +++ b/modules/parquet/src/lib/wasm/encode-parquet-wasm.ts @@ -1,7 +1,7 @@ -import type {Table} from 'apache-arrow'; import type {WriterOptions} from '@loaders.gl/loader-utils'; +import type {ArrowTable} from '@loaders.gl/arrow'; -import {RecordBatchStreamWriter} from 'apache-arrow'; +import * as arrow from 'apache-arrow'; import {loadWasm} from './load-wasm'; export type ParquetWriterOptions = WriterOptions & { @@ -11,13 +11,21 @@ export type ParquetWriterOptions = WriterOptions & { }; /** - * Encode Arrow Table to Parquet buffer + * Encode Arrow arrow.Table to Parquet buffer */ -export async function encode(table: Table, options?: ParquetWriterOptions): Promise { +export async function encode( + table: ArrowTable, + options?: ParquetWriterOptions +): Promise { const wasmUrl = options?.parquet?.wasmUrl; const wasm = await loadWasm(wasmUrl); - const arrowIPCBytes = tableToIPC(table); + const arrowTable: arrow.Table = table.data; + + // Serialize a table to the IPC format. + const writer = arrow.RecordBatchStreamWriter.writeAll(arrowTable); + const arrowIPCBytes = writer.toUint8Array(true); + // TODO: provide options for how to write table. const writerProperties = new wasm.WriterPropertiesBuilder().build(); const parquetBytes = wasm.writeParquet(arrowIPCBytes, writerProperties); @@ -26,15 +34,3 @@ export async function encode(table: Table, options?: ParquetWriterOptions): Prom parquetBytes.byteLength + parquetBytes.byteOffset ); } - -/** - * Serialize a {@link Table} to the IPC format. This function is a convenience - * wrapper for {@link RecordBatchStreamWriter} and {@link RecordBatchFileWriter}. - * Opposite of {@link tableFromIPC}. - * - * @param table The Table to serialize. - * @param type Whether to serialize the Table as a file or a stream. - */ -export function tableToIPC(table: Table): Uint8Array { - return RecordBatchStreamWriter.writeAll(table).toUint8Array(true); -} diff --git a/modules/parquet/src/lib/wasm/load-wasm/load-wasm-browser.ts b/modules/parquet/src/lib/wasm/load-wasm-browser.ts similarity index 100% rename from modules/parquet/src/lib/wasm/load-wasm/load-wasm-browser.ts rename to modules/parquet/src/lib/wasm/load-wasm-browser.ts diff --git a/modules/parquet/src/lib/wasm/load-wasm/load-wasm-node.ts b/modules/parquet/src/lib/wasm/load-wasm-node.ts similarity index 100% rename from modules/parquet/src/lib/wasm/load-wasm/load-wasm-node.ts rename to modules/parquet/src/lib/wasm/load-wasm-node.ts diff --git a/modules/parquet/src/lib/wasm/load-wasm/index.ts b/modules/parquet/src/lib/wasm/load-wasm.ts similarity index 100% rename from modules/parquet/src/lib/wasm/load-wasm/index.ts rename to modules/parquet/src/lib/wasm/load-wasm.ts diff --git a/modules/parquet/src/lib/wasm/parse-parquet-wasm.ts b/modules/parquet/src/lib/wasm/parse-parquet-wasm.ts index ae8dc2a081..44c3f3f7a8 100644 --- a/modules/parquet/src/lib/wasm/parse-parquet-wasm.ts +++ b/modules/parquet/src/lib/wasm/parse-parquet-wasm.ts @@ -1,8 +1,9 @@ // eslint-disable -import type {RecordBatch} from 'apache-arrow'; import type {LoaderOptions} from '@loaders.gl/loader-utils'; -import {Table as ArrowTable, RecordBatchStreamReader} from 'apache-arrow'; -import {loadWasm} from './load-wasm/load-wasm-node'; +import type {ArrowTable} from '@loaders.gl/arrow'; +import {serializeArrowSchema} from '@loaders.gl/arrow'; +import * as arrow from 'apache-arrow'; +import {loadWasm} from './load-wasm'; export type ParquetWasmLoaderOptions = LoaderOptions & { parquet?: { @@ -24,19 +25,17 @@ export async function parseParquetWasm( arrowIPCUint8Arr.byteOffset, arrowIPCUint8Arr.byteLength + arrowIPCUint8Arr.byteOffset ); - const arrowTable = tableFromIPC(arrowIPCBuffer); - return arrowTable; -} -/** - * Deserialize the IPC format into a {@link Table}. This function is a - * convenience wrapper for {@link RecordBatchReader}. Opposite of {@link tableToIPC}. - */ -function tableFromIPC(input: ArrayBuffer): ArrowTable { - const reader = RecordBatchStreamReader.from(input); - const recordBatches: RecordBatch[] = []; + const reader = arrow.RecordBatchStreamReader.from(arrowIPCBuffer); + const recordBatches: arrow.RecordBatch[] = []; for (const recordBatch of reader) { recordBatches.push(recordBatch); } - return new ArrowTable(recordBatches); + const arrowTable = new arrow.Table(recordBatches); + + return { + shape: 'arrow-table', + schema: serializeArrowSchema(arrowTable.schema), + data: arrowTable + }; } diff --git a/modules/parquet/src/parquet-loader.ts b/modules/parquet/src/parquet-loader.ts index 766b880dd2..89ba506704 100644 --- a/modules/parquet/src/parquet-loader.ts +++ b/modules/parquet/src/parquet-loader.ts @@ -1,4 +1,6 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {Loader, LoaderOptions} from '@loaders.gl/loader-utils'; import type { ObjectRowTable, @@ -7,28 +9,29 @@ import type { ColumnarTableBatch } from '@loaders.gl/schema'; +export {Buffer} from './polyfills/buffer/install-buffer-polyfill'; + // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; +/** Options for the parquet loader */ export type ParquetLoaderOptions = LoaderOptions & { + /** Options for the parquet loader */ parquet?: { - type?: 'object-row-table'; - url?: string; + /** Format of returned parsed data */ + shape?: 'object-row-table' | 'geojson-table'; + /** Restrict which columns that are parsed from the table. Can save significant memory. */ columnList?: string[] | string[][]; + /** If true, binary values are not converted to strings */ + preserveBinary?: boolean; + /** @deprecated not used? Set to true to indicate that this is a geoparquet file. */ geoparquet?: boolean; + /** @deprecated URL to override loaders.gl/core parser system */ + url?: string; }; }; -const DEFAULT_PARQUET_LOADER_OPTIONS: ParquetLoaderOptions = { - parquet: { - type: 'object-row-table', - url: undefined, - columnList: [], - geoparquet: true - } -}; - /** ParquetJS table loader */ export const ParquetLoader: Loader = { name: 'Apache Parquet', @@ -41,10 +44,18 @@ export const ParquetLoader: Loader = { name: 'Apache Parquet', id: 'parquet-wasm', module: 'parquet', @@ -30,7 +28,10 @@ export const ParquetWasmLoader = { mimeTypes: ['application/octet-stream'], binary: true, tests: ['PAR1', 'PARE'], - options: DEFAULT_PARQUET_LOADER_OPTIONS + options: { + parquet: { + type: 'arrow-table', + wasmUrl: 'https://unpkg.com/parquet-wasm@0.3.1/esm2/arrow1_bg.wasm' + } + } }; - -export const _typecheckParquetLoader: Loader = ParquetWasmLoader; diff --git a/modules/parquet/src/parquet-wasm-writer.ts b/modules/parquet/src/parquet-wasm-writer.ts index 1b481d94bc..64a705eb21 100644 --- a/modules/parquet/src/parquet-wasm-writer.ts +++ b/modules/parquet/src/parquet-wasm-writer.ts @@ -1,24 +1,27 @@ -import type {Writer} from '@loaders.gl/loader-utils'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {WriterWithEncoder} from '@loaders.gl/loader-utils'; +import type {ArrowTable} from '@loaders.gl/arrow'; import {encode, ParquetWriterOptions} from './lib/wasm/encode-parquet-wasm'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; -const DEFAULT_PARQUET_WRITER_OPTIONS: ParquetWriterOptions = { - parquet: { - wasmUrl: 'https://unpkg.com/parquet-wasm@0.3.1/esm2/arrow1_bg.wasm' - } -}; - -export const ParquetWasmWriter: Writer = { +/** Parquet WASM writer */ +export const ParquetWasmWriter: WriterWithEncoder = { name: 'Apache Parquet', id: 'parquet-wasm', module: 'parquet', version: VERSION, extensions: ['parquet'], mimeTypes: ['application/octet-stream'], - encode, binary: true, - options: DEFAULT_PARQUET_WRITER_OPTIONS + options: { + parquet: { + wasmUrl: 'https://unpkg.com/parquet-wasm@0.3.1/esm2/arrow1_bg.wasm' + } + }, + encode }; diff --git a/modules/parquet/src/parquet-writer.ts b/modules/parquet/src/parquet-writer.ts index 5c8f5520f2..d974853ce0 100644 --- a/modules/parquet/src/parquet-writer.ts +++ b/modules/parquet/src/parquet-writer.ts @@ -1,6 +1,7 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import type {Writer} from '@loaders.gl/loader-utils'; +import type {WriterWithEncoder} from '@loaders.gl/loader-utils'; import {Table, TableBatch} from '@loaders.gl/schema'; // __VERSION__ is injected by babel-plugin-version-inline @@ -9,18 +10,17 @@ const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; export type ParquetWriterOptions = {}; -const DEFAULT_PARQUET_LOADER_OPTIONS = {}; - -export const ParquetWriter: Writer = { +export const ParquetWriter: WriterWithEncoder = { name: 'Apache Parquet', id: 'parquet', module: 'parquet', version: VERSION, extensions: ['parquet'], mimeTypes: ['application/octet-stream'], - encodeSync, binary: true, - options: DEFAULT_PARQUET_LOADER_OPTIONS + options: {}, + encode: async (data, options) => encodeSync(data, options), + encodeSync }; function encodeSync(data, options?: ParquetWriterOptions) { diff --git a/modules/parquet/src/parquetjs/codecs/plain.ts b/modules/parquet/src/parquetjs/codecs/plain.ts index 83e5cd36c1..6a38e7659a 100644 --- a/modules/parquet/src/parquetjs/codecs/plain.ts +++ b/modules/parquet/src/parquetjs/codecs/plain.ts @@ -200,6 +200,7 @@ function decodeValues_BYTE_ARRAY(cursor: CursorBuffer, count: number): Buffer[] for (let i = 0; i < count; i++) { const len = cursor.buffer.readUInt32LE(cursor.offset); cursor.offset += 4; + // values.push(cursor.buffer.buffer.slice(cursor.offset, cursor.offset + len)); values.push(cursor.buffer.slice(cursor.offset, cursor.offset + len)); cursor.offset += len; } diff --git a/modules/parquet/src/parquetjs/codecs/rle.ts b/modules/parquet/src/parquetjs/codecs/rle.ts index 1c54f227ab..9ad17b9cd9 100644 --- a/modules/parquet/src/parquetjs/codecs/rle.ts +++ b/modules/parquet/src/parquetjs/codecs/rle.ts @@ -63,6 +63,8 @@ export function encodeValues( } const envelope = Buffer.alloc(buf.length + 4); + + // @ts-ignore buffer polyfill envelope.writeUInt32LE(buf.length, undefined); buf.copy(envelope, 4); diff --git a/modules/parquet/src/parquetjs/encoder/parquet-encoder.ts b/modules/parquet/src/parquetjs/encoder/parquet-encoder.ts index 1bf9239216..18d3fd6ad9 100644 --- a/modules/parquet/src/parquetjs/encoder/parquet-encoder.ts +++ b/modules/parquet/src/parquetjs/encoder/parquet-encoder.ts @@ -632,6 +632,7 @@ function encodeFooter( const metadataEncoded = serializeThrift(metadata); const footerEncoded = Buffer.alloc(metadataEncoded.length + 8); + metadataEncoded.copy(footerEncoded); footerEncoded.writeUInt32LE(metadataEncoded.length, metadataEncoded.length); footerEncoded.write(PARQUET_MAGIC, metadataEncoded.length + 4); diff --git a/modules/parquet/src/parquetjs/parquet-thrift/index.ts b/modules/parquet/src/parquetjs/parquet-thrift/index.ts index 779392aa3a..fc8d49bc3d 100644 --- a/modules/parquet/src/parquetjs/parquet-thrift/index.ts +++ b/modules/parquet/src/parquetjs/parquet-thrift/index.ts @@ -4,6 +4,10 @@ * Autogenerated by @creditkarma/thrift-typescript v3.7.2 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING */ +export {Buffer} from '../../polyfills/buffer/install-buffer-polyfill'; + +export {TBufferedTransport, TCompactProtocol, TFramedTransport} from 'thrift'; + export * from './Type'; export * from './ConvertedType'; export * from './FieldRepetitionType'; diff --git a/modules/parquet/src/parquetjs/parser/decoders.ts b/modules/parquet/src/parquetjs/parser/decoders.ts index 8cfe96b940..34ec642b89 100644 --- a/modules/parquet/src/parquetjs/parser/decoders.ts +++ b/modules/parquet/src/parquetjs/parser/decoders.ts @@ -2,7 +2,7 @@ import { ParquetCodec, ParquetColumnChunk, - ParquetOptions, + ParquetReaderContext, ParquetPageData, ParquetType, PrimitiveType, @@ -31,7 +31,7 @@ import {decodePageHeader, getThriftEnum, getBitWidth} from '../utils/read-utils' */ export async function decodeDataPages( buffer: Buffer, - options: ParquetOptions + context: ParquetReaderContext ): Promise { const cursor: CursorBuffer = { buffer, @@ -47,15 +47,15 @@ export async function decodeDataPages( count: 0 }; - let dictionary = options.dictionary || []; + let dictionary = context.dictionary || []; while ( // @ts-ignore size can be undefined cursor.offset < cursor.size && - (!options.numValues || data.dlevels.length < Number(options.numValues)) + (!context.numValues || data.dlevels.length < Number(context.numValues)) ) { // Looks like we have to decode these in sequence due to cursor updates? - const page = await decodePage(cursor, options); + const page = await decodePage(cursor, context); if (page.dictionary) { dictionary = page.dictionary; @@ -88,13 +88,14 @@ export async function decodeDataPages( /** * Decode parquet page based on page type * @param cursor - * @param options + * @param context */ export async function decodePage( cursor: CursorBuffer, - options: ParquetOptions + context: ParquetReaderContext ): Promise { let page; + const {pageHeader, length} = decodePageHeader(cursor.buffer, cursor.offset); cursor.offset += length; @@ -102,14 +103,14 @@ export async function decodePage( switch (pageType) { case 'DATA_PAGE': - page = await decodeDataPage(cursor, pageHeader, options); + page = await decodeDataPage(cursor, pageHeader, context); break; case 'DATA_PAGE_V2': - page = await decodeDataPageV2(cursor, pageHeader, options); + page = await decodeDataPageV2(cursor, pageHeader, context); break; case 'DICTIONARY_PAGE': page = { - dictionary: await decodeDictionaryPage(cursor, pageHeader, options), + dictionary: await decodeDictionaryPage(cursor, pageHeader, context), pageHeader }; break; @@ -225,7 +226,7 @@ function decodeValues( async function decodeDataPage( cursor: CursorBuffer, header: PageHeader, - options: ParquetOptions + context: ParquetReaderContext ): Promise { const cursorEnd = cursor.offset + header.compressed_page_size; const valueCount = header.data_page_header?.num_values; @@ -233,9 +234,9 @@ async function decodeDataPage( /* uncompress page */ let dataCursor = cursor; - if (options.compression !== 'UNCOMPRESSED') { + if (context.compression !== 'UNCOMPRESSED') { const valuesBuf = await decompress( - options.compression, + context.compression, cursor.buffer.slice(cursor.offset, cursorEnd), header.uncompressed_page_size ); @@ -255,9 +256,9 @@ async function decodeDataPage( // tslint:disable-next-line:prefer-array-literal let rLevels = new Array(valueCount); - if (options.column.rLevelMax > 0) { + if (context.column.rLevelMax > 0) { rLevels = decodeValues(PARQUET_RDLVL_TYPE, rLevelEncoding, dataCursor, valueCount!, { - bitWidth: getBitWidth(options.column.rLevelMax), + bitWidth: getBitWidth(context.column.rLevelMax), disableEnvelope: false // column: opts.column }); @@ -272,9 +273,9 @@ async function decodeDataPage( ) as ParquetCodec; // tslint:disable-next-line:prefer-array-literal let dLevels = new Array(valueCount); - if (options.column.dLevelMax > 0) { + if (context.column.dLevelMax > 0) { dLevels = decodeValues(PARQUET_RDLVL_TYPE, dLevelEncoding, dataCursor, valueCount!, { - bitWidth: getBitWidth(options.column.dLevelMax), + bitWidth: getBitWidth(context.column.dLevelMax), disableEnvelope: false // column: opts.column }); @@ -283,20 +284,20 @@ async function decodeDataPage( } let valueCountNonNull = 0; for (const dlvl of dLevels) { - if (dlvl === options.column.dLevelMax) { + if (dlvl === context.column.dLevelMax) { valueCountNonNull++; } } /* read values */ const valueEncoding = getThriftEnum(Encoding, header.data_page_header?.encoding!) as ParquetCodec; - const decodeOptions = { - typeLength: options.column.typeLength, - bitWidth: options.column.typeLength + const decodeOptions: ParquetCodecOptions = { + typeLength: context.column.typeLength, + bitWidth: context.column.typeLength }; const values = decodeValues( - options.column.primitiveType!, + context.column.primitiveType!, valueEncoding, dataCursor, valueCountNonNull, @@ -322,7 +323,7 @@ async function decodeDataPage( async function decodeDataPageV2( cursor: CursorBuffer, header: PageHeader, - opts: any + context: ParquetReaderContext ): Promise { const cursorEnd = cursor.offset + header.compressed_page_size; @@ -337,9 +338,9 @@ async function decodeDataPageV2( /* read repetition levels */ // tslint:disable-next-line:prefer-array-literal let rLevels = new Array(valueCount); - if (opts.column.rLevelMax > 0) { + if (context.column.rLevelMax > 0) { rLevels = decodeValues(PARQUET_RDLVL_TYPE, PARQUET_RDLVL_ENCODING, cursor, valueCount!, { - bitWidth: getBitWidth(opts.column.rLevelMax), + bitWidth: getBitWidth(context.column.rLevelMax), disableEnvelope: true }); } else { @@ -349,9 +350,9 @@ async function decodeDataPageV2( /* read definition levels */ // tslint:disable-next-line:prefer-array-literal let dLevels = new Array(valueCount); - if (opts.column.dLevelMax > 0) { + if (context.column.dLevelMax > 0) { dLevels = decodeValues(PARQUET_RDLVL_TYPE, PARQUET_RDLVL_ENCODING, cursor, valueCount!, { - bitWidth: getBitWidth(opts.column.dLevelMax), + bitWidth: getBitWidth(context.column.dLevelMax), disableEnvelope: true }); } else { @@ -363,7 +364,7 @@ async function decodeDataPageV2( if (header.data_page_header_v2?.is_compressed) { const valuesBuf = await decompress( - opts.compression, + context.compression, cursor.buffer.slice(cursor.offset, cursorEnd), header.uncompressed_page_size ); @@ -378,12 +379,12 @@ async function decodeDataPageV2( } const decodeOptions = { - typeLength: opts.column.typeLength, - bitWidth: opts.column.typeLength + typeLength: context.column.typeLength, + bitWidth: context.column.typeLength }; const values = decodeValues( - opts.column.primitiveType!, + context.column.primitiveType!, valueEncoding, valuesBufCursor, valueCountNonNull, @@ -403,13 +404,13 @@ async function decodeDataPageV2( * Do decoding of dictionary page which helps to iterate over all indexes and get dataPage values. * @param cursor * @param pageHeader - * @param options + * @param context */ async function decodeDictionaryPage( cursor: CursorBuffer, pageHeader: PageHeader, - options: ParquetOptions -): Promise { + context: ParquetReaderContext +): Promise<(string | ArrayBuffer)[]> { const cursorEnd = cursor.offset + pageHeader.compressed_page_size; let dictCursor = { @@ -420,9 +421,9 @@ async function decodeDictionaryPage( cursor.offset = cursorEnd; - if (options.compression !== 'UNCOMPRESSED') { + if (context.compression !== 'UNCOMPRESSED') { const valuesBuf = await decompress( - options.compression, + context.compression, dictCursor.buffer.slice(dictCursor.offset, cursorEnd), pageHeader.uncompressed_page_size ); @@ -438,11 +439,32 @@ async function decodeDictionaryPage( const numValues = pageHeader?.dictionary_page_header?.num_values || 0; - return decodeValues( - options.column.primitiveType!, - options.column.encoding!, + const decodedDictionaryValues = decodeValues( + context.column.primitiveType!, + context.column.encoding!, dictCursor, numValues, - options as ParquetCodecOptions - ).map((d) => d.toString()); + // TODO - this looks wrong? + context as ParquetCodecOptions + ); + + // Makes it look a little easier + let values: any[]; + if (context?.preserveBinary) { + values = decodedDictionaryValues.map((d) => preserveBinary(d)); + } else { + values = decodedDictionaryValues.map((d) => d.toString()); + } + return values; +} + +function preserveBinary(d: any): ArrayBuffer | ArrayBufferView | string { + if (ArrayBuffer.isView(d)) { + return d; + } + // Convert to ArrayBuffer + if (Buffer.isBuffer(d)) { + return d.buffer.slice(d.byteOffset, d.byteLength); + } + return d.toString(); } diff --git a/modules/parquet/src/parquetjs/parser/parquet-reader.ts b/modules/parquet/src/parquetjs/parser/parquet-reader.ts index 1ac86bafc3..558bf4b681 100644 --- a/modules/parquet/src/parquetjs/parser/parquet-reader.ts +++ b/modules/parquet/src/parquetjs/parser/parquet-reader.ts @@ -12,13 +12,14 @@ import { ParquetCompression, ParquetColumnChunk, PrimitiveType, - ParquetOptions + ParquetReaderContext } from '../schema/declare'; import {decodeFileMetadata, getThriftEnum, fieldIndexOf} from '../utils/read-utils'; import {decodeDataPages, decodePage} from './decoders'; export type ParquetReaderProps = { defaultDictionarySize?: number; + preserveBinary?: boolean; }; /** Properties for initializing a ParquetRowGroupReader */ @@ -27,10 +28,6 @@ export type ParquetIterationProps = { columnList?: string[] | string[][]; }; -const DEFAULT_PROPS: Required = { - defaultDictionarySize: 1e6 -}; - /** * The parquet envelope reader allows direct, unbuffered access to the individual * sections of the parquet file, namely the header, footer and the row groups. @@ -38,13 +35,18 @@ const DEFAULT_PROPS: Required = { * rows from a parquet file use the ParquetReader instead */ export class ParquetReader { + static defaultProps: Required = { + defaultDictionarySize: 1e6, + preserveBinary: false + }; + props: Required; file: ReadableFile; metadata: Promise | null = null; constructor(file: ReadableFile, props?: ParquetReaderProps) { this.file = file; - this.props = {...DEFAULT_PROPS, ...props}; + this.props = {...ParquetReader.defaultProps, ...props}; } close(): void { @@ -132,7 +134,8 @@ export class ParquetReader { /** Metadata is stored in the footer */ async readHeader(): Promise { - const buffer = await this.file.read(0, PARQUET_MAGIC.length); + const arrayBuffer = await this.file.read(0, PARQUET_MAGIC.length); + const buffer = Buffer.from(arrayBuffer); const magic = buffer.toString(); switch (magic) { case PARQUET_MAGIC: @@ -147,7 +150,8 @@ export class ParquetReader { /** Metadata is stored in the footer */ async readFooter(): Promise { const trailerLen = PARQUET_MAGIC.length + 4; - const trailerBuf = await this.file.read(this.file.size - trailerLen, trailerLen); + const arrayBuffer = await this.file.read(this.file.size - trailerLen, trailerLen); + const trailerBuf = Buffer.from(arrayBuffer); const magic = trailerBuf.slice(4).toString(); if (magic !== PARQUET_MAGIC) { @@ -160,9 +164,11 @@ export class ParquetReader { throw new Error(`Invalid metadata size ${metadataOffset}`); } - const metadataBuf = await this.file.read(metadataOffset, metadataSize); + const arrayBuffer2 = await this.file.read(metadataOffset, metadataSize); + const metadataBuf = Buffer.from(arrayBuffer2); // let metadata = new parquet_thrift.FileMetaData(); // parquet_util.decodeThrift(metadata, metadataBuf); + const {metadata} = decodeFileMetadata(metadataBuf); return metadata; } @@ -218,14 +224,16 @@ export class ParquetReader { ); } - const options: ParquetOptions = { + const context: ParquetReaderContext = { type, rLevelMax: field.rLevelMax, dLevelMax: field.dLevelMax, compression, column: field, numValues: colChunk.meta_data?.num_values, - dictionary: [] + dictionary: [], + // Options - TBD is this the right place for these? + preserveBinary: this.props.preserveBinary }; let dictionary; @@ -235,24 +243,25 @@ export class ParquetReader { if (dictionaryPageOffset) { const dictionaryOffset = Number(dictionaryPageOffset); // Getting dictionary from column chunk to iterate all over indexes to get dataPage values. - dictionary = await this.getDictionary(dictionaryOffset, options, pagesOffset); + dictionary = await this.getDictionary(dictionaryOffset, context, pagesOffset); } - dictionary = options.dictionary?.length ? options.dictionary : dictionary; - const pagesBuf = await this.file.read(pagesOffset, pagesSize); - return await decodeDataPages(pagesBuf, {...options, dictionary}); + dictionary = context.dictionary?.length ? context.dictionary : dictionary; + const arrayBuffer = await this.file.read(pagesOffset, pagesSize); + const pagesBuf = Buffer.from(arrayBuffer); + return await decodeDataPages(pagesBuf, {...context, dictionary}); } /** * Getting dictionary for allows to flatten values by indices. * @param dictionaryPageOffset - * @param options + * @param context * @param pagesOffset * @returns */ async getDictionary( dictionaryPageOffset: number, - options: ParquetOptions, + context: ParquetReaderContext, pagesOffset: number ): Promise { if (dictionaryPageOffset === 0) { @@ -270,10 +279,11 @@ export class ParquetReader { this.file.size - dictionaryPageOffset, this.props.defaultDictionarySize ); - const pagesBuf = await this.file.read(dictionaryPageOffset, dictionarySize); + const arrayBuffer = await this.file.read(dictionaryPageOffset, dictionarySize); + const pagesBuf = Buffer.from(arrayBuffer); const cursor = {buffer: pagesBuf, offset: 0, size: pagesBuf.length}; - const decodedPage = await decodePage(cursor, options); + const decodedPage = await decodePage(cursor, context); return decodedPage.dictionary!; } diff --git a/modules/parquet/src/parquetjs/schema/declare.ts b/modules/parquet/src/parquetjs/schema/declare.ts index 33fdc30a91..3924171bf2 100644 --- a/modules/parquet/src/parquetjs/schema/declare.ts +++ b/modules/parquet/src/parquetjs/schema/declare.ts @@ -99,7 +99,7 @@ export interface ParquetField { } /** @todo better name, this is an internal type? */ -export interface ParquetOptions { +export interface ParquetReaderContext { type: ParquetType; rLevelMax: number; dLevelMax: number; @@ -107,6 +107,8 @@ export interface ParquetOptions { column: ParquetField; numValues?: Int64; dictionary?: ParquetDictionary; + /** If true, binary values are not converted to strings */ + preserveBinary?: boolean; } export interface ParquetPageData { diff --git a/modules/parquet/src/parquetjs/utils/file-utils.ts b/modules/parquet/src/parquetjs/utils/file-utils.ts index f4dcf8a4f8..347540d0c0 100644 --- a/modules/parquet/src/parquetjs/utils/file-utils.ts +++ b/modules/parquet/src/parquetjs/utils/file-utils.ts @@ -1,5 +1,6 @@ // Forked from https://github.com/kbajalc/parquets under MIT license (Copyright (c) 2017 ironSource Ltd.) -import {fs, stream} from '@loaders.gl/loader-utils'; +import {stream} from '@loaders.gl/loader-utils'; +import * as fs from 'fs'; export function load(name: string): any { return (module || (global as any)).require(name); diff --git a/modules/parquet/src/parquetjs/utils/read-utils.ts b/modules/parquet/src/parquetjs/utils/read-utils.ts index d1410853c9..36a0205e2b 100644 --- a/modules/parquet/src/parquetjs/utils/read-utils.ts +++ b/modules/parquet/src/parquetjs/utils/read-utils.ts @@ -1,4 +1,4 @@ -import {TBufferedTransport, TCompactProtocol, TFramedTransport} from 'thrift'; +import {TBufferedTransport, TCompactProtocol, TFramedTransport} from '../parquet-thrift'; import {FileMetaData, PageHeader} from '../parquet-thrift'; class UFramedTransport extends TFramedTransport { @@ -12,7 +12,7 @@ export function serializeThrift(obj: any): Buffer { const output: Buffer[] = []; const transport = new TBufferedTransport(undefined, (buf) => { - output.push(buf as Buffer); + output.push(buf as unknown as Buffer); }); const protocol = new TCompactProtocol(transport); diff --git a/modules/parquet/src/polyfills/buffer/buffer-polyfill.browser.ts b/modules/parquet/src/polyfills/buffer/buffer-polyfill.browser.ts new file mode 100644 index 0000000000..f220de121c --- /dev/null +++ b/modules/parquet/src/polyfills/buffer/buffer-polyfill.browser.ts @@ -0,0 +1,12 @@ +// luma.gl, MIT license + +import {Buffer as BufferPolyfill} from './buffer'; + +/** Install Node.js Buffer polyfill (NO-OP in Node.js) */ +export function installBufferPolyfill(): typeof Buffer { + // @ts-ignore + globalThis.Buffer = globalThis.Buffer || BufferPolyfill; + + // @ts-ignore + return globalThis.Buffer; +} diff --git a/modules/parquet/src/polyfills/buffer/buffer-polyfill.node.ts b/modules/parquet/src/polyfills/buffer/buffer-polyfill.node.ts new file mode 100644 index 0000000000..f9de484ae4 --- /dev/null +++ b/modules/parquet/src/polyfills/buffer/buffer-polyfill.node.ts @@ -0,0 +1,17 @@ +// luma.gl, MIT license + +import {Buffer as BufferPolyfill} from './buffer'; + +/** Install the Node.js Buffer polyfill (NO-OP in Node.js) */ +export function installBufferPolyfill(): typeof Buffer { + const Buffer_ = typeof Buffer !== 'undefined' ? Buffer : null; + if (!Buffer_) { + // @ts-expect-error + globalThis.Buffer = BufferPolyfill; + return BufferPolyfill as unknown as typeof Buffer; + } + + globalThis.process = globalThis.process || {}; + // Buffer is a global variable in Node.js + return Buffer_; +} diff --git a/modules/parquet/src/polyfills/buffer/buffer.ts b/modules/parquet/src/polyfills/buffer/buffer.ts new file mode 100644 index 0000000000..54d4ebcf26 --- /dev/null +++ b/modules/parquet/src/polyfills/buffer/buffer.ts @@ -0,0 +1,2208 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +// The code has primarily been converted to TypeScript. + +/* ! + * The buffer module from node.js, for the browser. + * @author Feross Aboukhadijeh + * @license MIT + * https://github.com/feross/buffer/blob/master/AUTHORS.md + */ + +/** + * The decision to include this polyfill in loaders.gl may seem controversial. + * + * It is based on the following reasoning: + * - The Buffer API is used in the parquetjs library and likely other Node.js + * libraries we will use in the future. + * - While the goal is to convert code from Buffer to ArrayBuffer, the Thrift + * code in Parquet may be autogenerated. + * - Bundlers often require a polyfill to be included, this extra step is not + * a great experience for loaders.gl users. + * - The forked buffer polyfill module had old and generated some type errors. + */ + +/* eslint-disable */ // no-proto, max-statements, max-depth, complexity, no-continue, prefer-spread, no-constant-condition, consistent-return */ + +// @ts-nocheck - this references don't work well in prototype assignment + +import base64 from 'base64-js'; +import ieee754 from 'ieee754'; + +export const kMaxLength = 0x7fffffff; +export const INSPECT_MAX_BYTES = 50; + +type BufferEncoding = string; + +// const customInspectSymbol = +// typeof Symbol === 'function' && typeof Symbol['for'] === 'function' // eslint-disable-line dot-notation +// ? Symbol['for']('nodejs.util.inspect.custom') // eslint-disable-line dot-notation +// : null; + +/** + * The Buffer constructor returns instances of `Uint8Array` that have their + * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of + * `Uint8Array`, so the returned instances will have all the node `Buffer` methods + * and the `Uint8Array` methods. Square bracket notation works as expected -- it + * returns a single octet. + * + * The `Uint8Array` prototype remains unmodified. + */ +export class Buffer extends Uint8Array { + static poolSize = 8192; // not used by this implementation + + // length: number; inherited + + get parent() { + if (!Buffer.isBuffer(this)) return undefined; + return this.buffer; + } + + get offset() { + if (!Buffer.isBuffer(this)) return undefined; + return this.byteOffset; + } + + /** This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) + * to detect a Buffer instance. It's not possible to use `instanceof Buffer` + * reliably in a browserify context because there could be multiple different + * copies of the 'buffer' package in use. This method works even for Buffer + * instances that were created from another copy of the `buffer` package. + * @see: https://github.com/feross/buffer/issues/154 + */ + _isBuffer = true; + + /** + * Allocates a new buffer containing the given {str}. + * + * @param str String to store in buffer. + * @param encoding encoding to use, optional. Default is 'utf8' + */ + constructor(str: string, encoding?: string); + /** + * Allocates a new buffer of {size} octets. + * + * @param size count of octets to allocate. + */ + constructor(size: number); + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + */ + constructor(array: Uint8Array); + /** + * Produces a Buffer backed by the same allocated memory as + * the given {ArrayBuffer}. + * + * + * @param arrayBuffer The ArrayBuffer with which to share memory. + */ + constructor(arrayBuffer: ArrayBuffer); + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + */ + constructor(array: any[]); + /** + * Copies the passed {buffer} data onto a new {Buffer} instance. + * + * @param buffer The buffer to copy. + */ + constructor(buffer: Buffer); + + constructor(arg, encodingOrOffset?, length?: number) { + if (typeof arg !== 'number') { + return Buffer.from(arg, encodingOrOffset, length); + } + + // Basic case, just a length + const size = arg; + if (size > kMaxLength) { + throw new RangeError(`The value "${size}" is invalid for option "size"`); + } + if (typeof encodingOrOffset === 'string') { + throw new TypeError('The "string" argument must be of type string. Received type number'); + } + super(size < 0 ? 0 : checked(size) | 0); + return; + } + + /** + * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError + * if value is a number. + * Buffer.from(str[, encoding]) + * Buffer.from(array) + * Buffer.from(buffer) + * Buffer.from(arrayBuffer[, byteOffset[, length]]) + **/ + + /** + * Allocates a new Buffer using an {array} of octets. + * + * @param array + */ + static from(array: any[]): Buffer; + /** + * When passed a reference to the .buffer property of a TypedArray instance, + * the newly created Buffer will share the same allocated memory as the TypedArray. + * The optional {byteOffset} and {length} arguments specify a memory range + * within the {arrayBuffer} that will be shared by the Buffer. + * + * @param arrayBuffer The .buffer property of a TypedArray or a new ArrayBuffer() + * @param byteOffset + * @param length + */ + static from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer; + /** + * Copies the passed {buffer} data onto a new Buffer instance. + * + * @param buffer + */ + static from(buffer: Buffer | Uint8Array): Buffer; + /** + * Creates a new Buffer containing the given JavaScript string {str}. + * If provided, the {encoding} parameter identifies the character encoding. + * If not provided, {encoding} defaults to 'utf8'. + * + * @param str + */ + static from(str: string, encoding?: string): Buffer; + + static from(value, encodingOrOffset, length) { + if (typeof value === 'string') { + return fromString(value, encodingOrOffset); + } + + if (ArrayBuffer.isView(value)) { + return fromArrayView(value); + } + + if (value == null) { + throw new TypeError( + `${ + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + }${typeof value}` + ); + } + + if (isInstance(value, ArrayBuffer) || (value && isInstance(value.buffer, ArrayBuffer))) { + return fromArrayBuffer(value, encodingOrOffset, length); + } + + if ( + typeof SharedArrayBuffer !== 'undefined' && + (isInstance(value, SharedArrayBuffer) || + (value && isInstance(value.buffer, SharedArrayBuffer))) + ) { + return fromArrayBuffer(value, encodingOrOffset, length); + } + + if (typeof value === 'number') { + throw new TypeError('The "value" argument must not be of type number. Received type number'); + } + + const valueOf = value.valueOf && value.valueOf(); + if (valueOf != null && valueOf !== value) { + return Buffer.from(valueOf, encodingOrOffset, length); + } + + const b = fromObject(value); + if (b) return b; + + if ( + typeof Symbol !== 'undefined' && + Symbol.toPrimitive != null && + typeof value[Symbol.toPrimitive] === 'function' + ) { + return Buffer.from(value[Symbol.toPrimitive]('string'), encodingOrOffset, length); + } + + throw new TypeError( + `${ + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + }${typeof value}` + ); + } + + /** + * Returns true if {obj} is a Buffer + * + * @param obj object to test. + */ + static isBuffer(b: any): obj is Buffer { + return b != null && b._isBuffer === true && b !== Buffer.prototype; // so Buffer.isBuffer(Buffer.prototype) will be false + } + + /** + * The same as buf1.compare(buf2). + */ + static compare(a: Uint8Array | Buffer, b: Uint8Array | Buffer): number { + if (!Buffer.isBuffer(a) && isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength); + if (!Buffer.isBuffer(b) && isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength); + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + throw new TypeError('The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array'); + } + + if (a === b) return 0; + + let x = a.length; + let y = b.length; + + for (let i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i]; + y = b[i]; + break; + } + } + + if (x < y) return -1; + if (y < x) return 1; + return 0; + } + + /** + * Returns true if {encoding} is a valid encoding argument. + * Valid string encodings in Node 0.12: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex' + * + * @param encoding string to test. + */ + static isEncoding(encoding: string): boolean { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'latin1': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true; + default: + return false; + } + } + + /** + * Returns a buffer which is the result of concatenating all the buffers in the list together. + * + * If the list has no items, or if the totalLength is 0, then it returns a zero-length buffer. + * If the list has exactly one item, then the first item of the list is returned. + * If the list has more than one item, then a new Buffer is created. + * + * @param list An array of Buffer objects to concatenate + * @param totalLength Total length of the buffers when concatenated. + * If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly. + */ + static concat(list: (Uint8Array | Buffer)[], length?: number): Buffer { + if (!Array.isArray(list)) { + throw new TypeError('"list" argument must be an Array of Buffers'); + } + + if (list.length === 0) { + return Buffer.alloc(0); + } + + let i; + if (length === undefined) { + length = 0; + for (i = 0; i < list.length; ++i) { + length += list[i].length; + } + } + + const buffer = Buffer.allocUnsafe(length); + let pos = 0; + for (i = 0; i < list.length; ++i) { + let buf = list[i]; + if (isInstance(buf, Uint8Array)) { + if (pos + buf.length > buffer.length) { + if (!Buffer.isBuffer(buf)) { + buf = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength); + } + buf.copy(buffer, pos); + } else { + Uint8Array.prototype.set.call(buffer, buf, pos); + } + } else if (!Buffer.isBuffer(buf)) { + throw new TypeError('"list" argument must be an Array of Buffers'); + } else { + buf.copy(buffer, pos); + } + pos += buf.length; + } + return buffer; + } + + /** + * Allocates a new buffer of {size} octets. + * + * @param size count of octets to allocate. + * @param fill if specified, buffer will be initialized by calling buf.fill(fill). + * If parameter is omitted, buffer will be filled with zeros. + * @param encoding encoding used for call to buf.fill while initializing + */ + static alloc(size: number, fill?: string | Buffer | number, encoding?: string): Buffer { + return alloc(size, fill, encoding); + } + + /** + * Allocates a new buffer of {size} octets, leaving memory not initialized, so the contents + * of the newly created Buffer are unknown and may contain sensitive data. + * + * @param size count of octets to allocate + */ + static allocUnsafe(size: number): Buffer { + assertSize(size); + return new Buffer(size); + } + + /** + * Allocates a new non-pooled buffer of {size} octets, leaving memory not initialized, so the contents + * of the newly created Buffer are unknown and may contain sensitive data. + * + * @param size count of octets to allocate + */ + static allocUnsafeSlow(size: number): Buffer { + return allocUnsafe(size); + } + + includes(val: string | number | Buffer, byteOffset?: number, encoding?: string): boolean { + return this.indexOf(val, byteOffset, encoding) !== -1; + } + + indexOf(val: string | number | Buffer, byteOffset?: number, encoding?: string): number { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true); + } + + lastIndexOf(val: string | number | Buffer, byteOffset?: number, encoding?: string): number { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false); + } + + readInt8(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 1, this.length); + if (!(this[offset] & 0x80)) return this[offset]; + return (0xff - this[offset] + 1) * -1; + } + + readInt16LE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 2, this.length); + const val = this[offset] | (this[offset + 1] << 8); + return val & 0x8000 ? val | 0xffff0000 : val; + } + + readInt16BE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 2, this.length); + const val = this[offset + 1] | (this[offset] << 8); + return val & 0x8000 ? val | 0xffff0000 : val; + } + + readInt32LE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + + return ( + this[offset] | (this[offset + 1] << 8) | (this[offset + 2] << 16) | (this[offset + 3] << 24) + ); + } + + readInt32BE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + + return ( + (this[offset] << 24) | (this[offset + 1] << 16) | (this[offset + 2] << 8) | this[offset + 3] + ); + } + + readIntBE(offset: number, byteLength: number, noAssert?: boolean): number { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) checkOffset(offset, byteLength, this.length); + + let i = byteLength; + let mul = 1; + let val = this[offset + --i]; + while (i > 0 && (mul *= 0x100)) { + val += this[offset + --i] * mul; + } + mul *= 0x80; + + if (val >= mul) val -= Math.pow(2, 8 * byteLength); + + return val; + } + + readIntLE(offset: number, byteLength: number, noAssert?: boolean): number { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) checkOffset(offset, byteLength, this.length); + + let val = this[offset]; + let mul = 1; + let i = 0; + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul; + } + mul *= 0x80; + + if (val >= mul) val -= Math.pow(2, 8 * byteLength); + + return val; + } + + readBigInt64LE(offset: number): BigInt { + offset = offset >>> 0; + validateNumber(offset, 'offset'); + const first = this[offset]; + const last = this[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8); + } + + const val = + this[offset + 4] + this[offset + 5] * 2 ** 8 + this[offset + 6] * 2 ** 16 + (last << 24); // Overflow + + return ( + (BigInt(val) << BigInt(32)) + + BigInt(first + this[++offset] * 2 ** 8 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 24) + ); + } + + readBigInt64BE(offset: number): BigInt { + offset = offset >>> 0; + validateNumber(offset, 'offset'); + const first = this[offset]; + const last = this[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8); + } + + const val = + (first << 24) + // Overflow + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + this[++offset]; + + return ( + (BigInt(val) << BigInt(32)) + + BigInt(this[++offset] * 2 ** 24 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 8 + last) + ); + } + + readUInt8(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 1, this.length); + return this[offset]; + } + + readUInt16LE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 2, this.length); + return this[offset] | (this[offset + 1] << 8); + } + + readUInt16BE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 2, this.length); + return (this[offset] << 8) | this[offset + 1]; + } + + readUInt32LE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + + return ( + (this[offset] | (this[offset + 1] << 8) | (this[offset + 2] << 16)) + + this[offset + 3] * 0x1000000 + ); + } + + readUInt32BE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + + return ( + this[offset] * 0x1000000 + + ((this[offset + 1] << 16) | (this[offset + 2] << 8) | this[offset + 3]) + ); + } + + readUIntLE(offset, byteLength, noAssert) { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) checkOffset(offset, byteLength, this.length); + + let val = this[offset]; + let mul = 1; + let i = 0; + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul; + } + + return val; + } + + readUIntBE(offset, byteLength, noAssert) { + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) { + checkOffset(offset, byteLength, this.length); + } + + let val = this[offset + --byteLength]; + let mul = 1; + while (byteLength > 0 && (mul *= 0x100)) { + val += this[offset + --byteLength] * mul; + } + + return val; + } + + readBigUInt64LE(offset: number): BigInt { + offset = offset >>> 0; + validateNumber(offset, 'offset'); + const first = this[offset]; + const last = this[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8); + } + + const lo = + first + this[++offset] * 2 ** 8 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 24; + + const hi = this[++offset] + this[++offset] * 2 ** 8 + this[++offset] * 2 ** 16 + last * 2 ** 24; + + return BigInt(lo) + (BigInt(hi) << BigInt(32)); + } + + readBigUInt64BE(offset: number): BigInt { + offset = offset >>> 0; + validateNumber(offset, 'offset'); + const first = this[offset]; + const last = this[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8); + } + + const hi = + first * 2 ** 24 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 8 + this[++offset]; + + const lo = this[++offset] * 2 ** 24 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 8 + last; + + return (BigInt(hi) << BigInt(32)) + BigInt(lo); + } + + readFloatLE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + return ieee754.read(this, offset, true, 23, 4); + } + + readFloatBE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 4, this.length); + return ieee754.read(this, offset, false, 23, 4); + } + + readDoubleLE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 8, this.length); + return ieee754.read(this, offset, true, 52, 8); + } + + readDoubleBE(offset: number, noAssert?: boolean): number { + offset = offset >>> 0; + if (!noAssert) checkOffset(offset, 8, this.length); + return ieee754.read(this, offset, false, 52, 8); + } + + writeUInt8(value: number, offset: number, noAssert?: boolean): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0); + this[offset] = value & 0xff; + return offset + 1; + } + + writeUInt16LE(value: number, offset: number, noAssert?: boolean): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0); + this[offset] = value & 0xff; + this[offset + 1] = value >>> 8; + return offset + 2; + } + + writeUInt16BE(value: number, offset: number, noAssert?: boolean): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0); + this[offset] = value >>> 8; + this[offset + 1] = value & 0xff; + return offset + 2; + } + + writeUInt32LE(value: number, offset: number, noAssert?: boolean): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0); + this[offset + 3] = value >>> 24; + this[offset + 2] = value >>> 16; + this[offset + 1] = value >>> 8; + this[offset] = value & 0xff; + return offset + 4; + } + + writeUInt32BE(value: number, offset: number, noAssert?: boolean): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0); + this[offset] = value >>> 24; + this[offset + 1] = value >>> 16; + this[offset + 2] = value >>> 8; + this[offset + 3] = value & 0xff; + return offset + 4; + } + + writeUIntLE(value, offset, byteLength, noAssert) { + value = Number(value); + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) { + const maxBytes = Math.pow(2, 8 * byteLength) - 1; + checkInt(this, value, offset, byteLength, maxBytes, 0); + } + + let mul = 1; + let i = 0; + this[offset] = value & 0xff; + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xff; + } + + return offset + byteLength; + } + + writeUIntBE(value, offset, byteLength, noAssert) { + value = Number(value); + offset = offset >>> 0; + byteLength = byteLength >>> 0; + if (!noAssert) { + const maxBytes = Math.pow(2, 8 * byteLength) - 1; + checkInt(this, value, offset, byteLength, maxBytes, 0); + } + + let i = byteLength - 1; + let mul = 1; + this[offset + i] = value & 0xff; + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xff; + } + + return offset + byteLength; + } + + writeBigUInt64LE(value: number, offset: number = 0): BigInt { + return wrtBigUInt64LE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff')); + } + + writeBigUInt64BE(value: number, offset: number = 0): BigInt { + return wrtBigUInt64BE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff')); + } + + writeIntLE(value, offset, byteLength, noAssert) { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) { + const limit = Math.pow(2, 8 * byteLength - 1); + + checkInt(this, value, offset, byteLength, limit - 1, -limit); + } + + let i = 0; + let mul = 1; + let sub = 0; + this[offset] = value & 0xff; + while (++i < byteLength && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { + sub = 1; + } + this[offset + i] = (((value / mul) >> 0) - sub) & 0xff; + } + + return offset + byteLength; + } + + writeIntBE(value, offset, byteLength, noAssert) { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) { + const limit = Math.pow(2, 8 * byteLength - 1); + + checkInt(this, value, offset, byteLength, limit - 1, -limit); + } + + let i = byteLength - 1; + let mul = 1; + let sub = 0; + this[offset + i] = value & 0xff; + while (--i >= 0 && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { + sub = 1; + } + this[offset + i] = (((value / mul) >> 0) - sub) & 0xff; + } + + return offset + byteLength; + } + + writeInt8(value: number, offset: number, noAssert?: boolean): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80); + if (value < 0) value = 0xff + value + 1; + this[offset] = value & 0xff; + return offset + 1; + } + + writeInt16LE(value: number, offset: number, noAssert?: boolean): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000); + this[offset] = value & 0xff; + this[offset + 1] = value >>> 8; + return offset + 2; + } + + writeInt16BE(value: number, offset: number, noAssert?: boolean): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000); + this[offset] = value >>> 8; + this[offset + 1] = value & 0xff; + return offset + 2; + } + + writeInt32LE(value: number, offset: number, noAssert?: boolean): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); + this[offset] = value & 0xff; + this[offset + 1] = value >>> 8; + this[offset + 2] = value >>> 16; + this[offset + 3] = value >>> 24; + return offset + 4; + } + + writeInt32BE(value: number, offset: number, noAssert?: boolean): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); + if (value < 0) value = 0xffffffff + value + 1; + this[offset] = value >>> 24; + this[offset + 1] = value >>> 16; + this[offset + 2] = value >>> 8; + this[offset + 3] = value & 0xff; + return offset + 4; + } + + writeBigInt64LE(value: number, offset: number = 0): BigInt { + return wrtBigUInt64LE( + this, + value, + offset, + -BigInt('0x8000000000000000'), + BigInt('0x7fffffffffffffff') + ); + } + + writeBigInt64BE(value: number, offset: number = 0): BigInt { + return wrtBigUInt64BE( + this, + value, + offset, + -BigInt('0x8000000000000000'), + BigInt('0x7fffffffffffffff') + ); + } + + writeFloatLE(value: number, offset: number, noAssert?: boolean): number { + return writeFloat(this, value, offset, true, noAssert); + } + + writeFloatBE(value: number, offset: number, noAssert?: boolean): number { + return writeFloat(this, value, offset, false, noAssert); + } + + writeDoubleLE(value: number, offset: number, noAssert?: boolean): number { + return writeDouble(this, value, offset, true, noAssert); + } + + writeDoubleBE(value: number, offset: number, noAssert?: boolean): number { + return writeDouble(this, value, offset, false, noAssert); + } + + write(string: string, encoding?: BufferEncoding): number; + write(string: string, offset: number, encoding?: BufferEncoding): number; + write(string: string, offset: number, length: number, encoding?: BufferEncoding): number; + write(string: string, offset, length, encoding): number { + // Buffer#write(string) + if (offset === undefined) { + encoding = 'utf8'; + length = this.length; + offset = 0; + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset; + length = this.length; + offset = 0; + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset >>> 0; + if (isFinite(length)) { + length = length >>> 0; + if (encoding === undefined) encoding = 'utf8'; + } else { + encoding = length; + length = undefined; + } + } else { + throw new Error('Buffer.write(string, encoding, offset[, length]) is no longer supported'); + } + + const remaining = this.length - offset; + if (length === undefined || length > remaining) length = remaining; + + if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { + throw new RangeError('Attempt to write outside buffer bounds'); + } + + if (!encoding) encoding = 'utf8'; + + let loweredCase = false; + for (;;) { + switch (encoding) { + case 'hex': + return hexWrite(this, string, offset, length); + + case 'utf8': + case 'utf-8': + return utf8Write(this, string, offset, length); + + case 'ascii': + case 'latin1': + case 'binary': + return asciiWrite(this, string, offset, length); + + case 'base64': + // Warning: maxLength not taken into account in base64Write + return base64Write(this, string, offset, length); + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return ucs2Write(this, string, offset, length); + + default: + if (loweredCase) throw new TypeError(`Unknown encoding: ${encoding}`); + encoding = `${encoding}`.toLowerCase(); + loweredCase = true; + } + } + } + + // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) + copy(target: Buffer, targetStart?: number, start?: number, end?: number): number { + if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer'); + if (!start) start = 0; + if (!end && end !== 0) end = this.length; + if (targetStart >= target.length) targetStart = target.length; + if (!targetStart) targetStart = 0; + if (end > 0 && end < start) end = start; + + // Copy 0 bytes; we're done + if (end === start) return 0; + if (target.length === 0 || this.length === 0) return 0; + + // Fatal error conditions + if (targetStart < 0) { + throw new RangeError('targetStart out of bounds'); + } + if (start < 0 || start >= this.length) throw new RangeError('Index out of range'); + if (end < 0) throw new RangeError('sourceEnd out of bounds'); + + // Are we oob? + if (end > this.length) end = this.length; + if (target.length - targetStart < end - start) { + end = target.length - targetStart + start; + } + + const len = end - start; + + if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { + // Use built-in when available, missing from IE11 + this.copyWithin(targetStart, start, end); + } else { + Uint8Array.prototype.set.call(target, this.subarray(start, end), targetStart); + } + + return len; + } + + // Usage: + // buffer.fill(number[, offset[, end]]) + // buffer.fill(buffer[, offset[, end]]) + // buffer.fill(string[, offset[, end]][, encoding]) + fill(val: any, start?: number, end?: number, encoding?): this { + // Handle string cases: + if (typeof val === 'string') { + if (typeof start === 'string') { + encoding = start; + start = 0; + end = this.length; + } else if (typeof end === 'string') { + encoding = end; + end = this.length; + } + if (encoding !== undefined && typeof encoding !== 'string') { + throw new TypeError('encoding must be a string'); + } + if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { + throw new TypeError(`Unknown encoding: ${encoding}`); + } + if (val.length === 1) { + const code = val.charCodeAt(0); + if ((encoding === 'utf8' && code < 128) || encoding === 'latin1') { + // Fast path: If `val` fits into a single byte, use that numeric value. + val = code; + } + } + } else if (typeof val === 'number') { + val = val & 255; + } else if (typeof val === 'boolean') { + val = Number(val); + } + + // Invalid ranges are not set to a default, so can range check early. + if (start < 0 || this.length < start || this.length < end) { + throw new RangeError('Out of range index'); + } + + if (end <= start) { + return this; + } + + start = start >>> 0; + end = end === undefined ? this.length : end >>> 0; + + if (!val) val = 0; + + let i; + if (typeof val === 'number') { + for (i = start; i < end; ++i) { + this[i] = val; + } + } else { + const bytes = Buffer.isBuffer(val) ? val : Buffer.from(val, encoding); + const len = bytes.length; + if (len === 0) { + throw new TypeError(`The value "${val}" is invalid for argument "value"`); + } + for (i = 0; i < end - start; ++i) { + this[i + start] = bytes[i % len]; + } + } + + return this; + } + + swap16() { + const len = this.length; + if (len % 2 !== 0) { + throw new RangeError('Buffer size must be a multiple of 16-bits'); + } + for (let i = 0; i < len; i += 2) { + swap(this, i, i + 1); + } + return this; + } + + swap32() { + const len = this.length; + if (len % 4 !== 0) { + throw new RangeError('Buffer size must be a multiple of 32-bits'); + } + for (let i = 0; i < len; i += 4) { + swap(this, i, i + 3); + swap(this, i + 1, i + 2); + } + return this; + } + + swap64() { + const len = this.length; + if (len % 8 !== 0) { + throw new RangeError('Buffer size must be a multiple of 64-bits'); + } + for (let i = 0; i < len; i += 8) { + swap(this, i, i + 7); + swap(this, i + 1, i + 6); + swap(this, i + 2, i + 5); + swap(this, i + 3, i + 4); + } + return this; + } + + toString(encoding?: string, start?: number, end?: number): string { + const length = this.length; + if (length === 0) return ''; + if (arguments.length === 0) return utf8Slice(this, 0, length); + return this._slowToString(...arguments); + } + + // toLocaleString(b) { + // if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer'); + // if (this === b) return true; + // return Buffer.compare(this, b) === 0; + // } + + inspect() { + let str = ''; + const max = INSPECT_MAX_BYTES; + str = this.toString('hex', 0, max) + .replace(/(.{2})/g, '$1 ') + .trim(); + if (this.length > max) str += ' ... '; + return ``; + } + // if (customInspectSymbol) { + // Buffer.prototype[customInspectSymbol] = Buffer.prototype.inspect; + // } + // } + + equals(b: Buffer): boolean { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer'); + if (this === b) return true; + return Buffer.compare(this, b) === 0; + } + + compare( + target: Buffer | Uint8Array, + start?: number, + end?: number, + thisStart?: number, + thisEnd?: number + ): number { + if (!Buffer.isBuffer(target) && isInstance(target, Uint8Array)) { + target = Buffer.from(target, target.offset, target.byteLength); + } + if (!Buffer.isBuffer(target)) { + throw new TypeError( + `${ + 'The "target" argument must be one of type Buffer or Uint8Array. ' + 'Received type ' + }${typeof target}` + ); + } + + if (start === undefined) { + start = 0; + } + if (end === undefined) { + end = target ? target.length : 0; + } + if (thisStart === undefined) { + thisStart = 0; + } + if (thisEnd === undefined) { + thisEnd = this.length; + } + + if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { + throw new RangeError('out of range index'); + } + + if (thisStart >= thisEnd && start >= end) { + return 0; + } + if (thisStart >= thisEnd) { + return -1; + } + if (start >= end) { + return 1; + } + + start >>>= 0; + end >>>= 0; + thisStart >>>= 0; + thisEnd >>>= 0; + + if (this === target) return 0; + + let x = thisEnd - thisStart; + let y = end - start; + const len = Math.min(x, y); + + const thisCopy = this.slice(thisStart, thisEnd); + const targetCopy = target.slice(start, end); + + for (let i = 0; i < len; ++i) { + if (thisCopy[i] !== targetCopy[i]) { + x = thisCopy[i]; + y = targetCopy[i]; + break; + } + } + + if (x < y) return -1; + if (y < x) return 1; + return 0; + } + + toJSON(): {type: 'Buffer'; data: any[]} { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + }; + } + + slice(start?: number, end?: number): this { + const len = this.length; + start = ~~start; + end = end === undefined ? len : ~~end; + + if (start < 0) { + start += len; + if (start < 0) start = 0; + } else if (start > len) { + start = len; + } + + if (end < 0) { + end += len; + if (end < 0) end = 0; + } else if (end > len) { + end = len; + } + + if (end < start) end = start; + + const newBuf = this.subarray(start, end); + + // Return an augmented `Uint8Array` instance + Object.setPrototypeOf(newBuf, Buffer.prototype); + + return newBuf as unknown as Buffer; + } + + // Typo support? + + // readUint8(offset: number, noAssert?: boolean): number { + // return this.readUInt8(...arguments); + // } + + // readUint16LE(offset: number, noAssert?: boolean): number { + // return this.readUInt16LE(...arguments); + // } + + // readUint16BE(offset: number, noAssert?: boolean): number { + // return this.readUInt16BE(...arguments); + // } + + // readUint32LE(offset: number, noAssert?: boolean): number { + // return this.readUInt32LE(...arguments); + // } + + // readUint32BE(offset: number, noAssert?: boolean): number { + // return this.readUInt32BE(...arguments); + // } + + // writeUint8() { + // return this.writeUInt8(...arguments); + // } + + // writeUint16LE + // writeUint16LE + // writeUint32LE = Buffer.prototype. + // Buffer.prototype.writeUint32BE + + protected _slowToString(encoding, start, end) { + let loweredCase = false; + + // No need to verify that "this.length <= MAX_UINT32" since it's a read-only + // property of a typed array. + + // This behaves neither like String nor Uint8Array in that we set start/end + // to their upper/lower bounds if the value passed is out of range. + // undefined is handled specially as per ECMA-262 6th Edition, + // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. + if (start === undefined || start < 0) { + start = 0; + } + // Return early if start > this.length. Done here to prevent potential uint32 + // coercion fail below. + if (start > this.length) { + return ''; + } + + if (end === undefined || end > this.length) { + end = this.length; + } + + if (end <= 0) { + return ''; + } + + // Force coercion to uint32. This will also coerce falsey/NaN values to 0. + end >>>= 0; + start >>>= 0; + + if (end <= start) { + return ''; + } + + if (!encoding) encoding = 'utf8'; + + while (true) { + switch (encoding) { + case 'hex': + return hexSlice(this, start, end); + + case 'utf8': + case 'utf-8': + return utf8Slice(this, start, end); + + case 'ascii': + return asciiSlice(this, start, end); + + case 'latin1': + case 'binary': + return latin1Slice(this, start, end); + + case 'base64': + return base64Slice(this, start, end); + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return utf16leSlice(this, start, end); + + default: + if (loweredCase) throw new TypeError(`Unknown encoding: ${encoding}`); + encoding = `${encoding}`.toLowerCase(); + loweredCase = true; + } + } + } +} + +function checkInt(buf: Buffer, value, offset, ext, max, min) { + if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance'); + if (value > max || value < min) throw new RangeError('"value" argument is out of bounds'); + if (offset + ext > buf.length) throw new RangeError('Index out of range'); +} + +function wrtBigUInt64LE(buf: Buffer, value, offset, min, max) { + checkIntBI(value, min, max, buf, offset, 7); + + let lo = Number(value & BigInt(0xffffffff)); + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + let hi = Number((value >> BigInt(32)) & BigInt(0xffffffff)); + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + return offset; +} + +function wrtBigUInt64BE(buf: Buffer, value, offset, min, max) { + checkIntBI(value, min, max, buf, offset, 7); + + let lo = Number(value & BigInt(0xffffffff)); + buf[offset + 7] = lo; + lo = lo >> 8; + buf[offset + 6] = lo; + lo = lo >> 8; + buf[offset + 5] = lo; + lo = lo >> 8; + buf[offset + 4] = lo; + let hi = Number((value >> BigInt(32)) & BigInt(0xffffffff)); + buf[offset + 3] = hi; + hi = hi >> 8; + buf[offset + 2] = hi; + hi = hi >> 8; + buf[offset + 1] = hi; + hi = hi >> 8; + buf[offset] = hi; + return offset + 8; +} + +// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: +// https://github.com/feross/buffer/pull/148 +// Object.setPrototypeOf(Buffer.prototype, Uint8Array.prototype); +// Object.setPrototypeOf(Buffer, Uint8Array); + +function assertSize(size: number): void { + if (typeof size !== 'number') { + throw new TypeError('"size" argument must be of type number'); + } else if (size < 0) { + throw new RangeError(`The value "${size}" is invalid for option "size"`); + } +} + +function alloc(size, fill, encoding): Buffer { + assertSize(size); + if (size <= 0) { + return new Buffer(size); + } + if (fill !== undefined) { + // Only pay attention to encoding if it's a string. This + // prevents accidentally sending in a number that would + // be interpreted as a start offset. + return typeof encoding === 'string' + ? new Buffer(size).fill(fill, encoding) + : new Buffer(size).fill(fill); + } + return new Buffer(size); +} + +function fromString(string: string, encoding: BufferEncoding): Buffer { + if (typeof encoding !== 'string' || encoding === '') { + encoding = 'utf8'; + } + + if (!Buffer.isEncoding(encoding)) { + throw new TypeError(`Unknown encoding: ${encoding}`); + } + + const length = byteLength(string, encoding) | 0; + let buf = new Buffer(length); + + const actual = buf.write(string, encoding); + + if (actual !== length) { + // Writing a hex string, for example, that contains invalid characters will + // cause everything after the first invalid character to be ignored. (e.g. + // 'abxxcd' will be treated as 'ab') + buf = buf.slice(0, actual); + } + + return buf; +} + +function fromArrayLike(array: ArrayLike): Buffer { + const length = array.length < 0 ? 0 : checked(array.length) | 0; + const buf = new Buffer(length); + for (let i = 0; i < length; i += 1) { + buf[i] = array[i] & 255; + } + return buf; +} + +function fromArrayView(arrayView): Buffer { + if (isInstance(arrayView, Uint8Array)) { + const copy = new Uint8Array(arrayView); + return fromArrayBuffer(copy.buffer, copy.byteOffset, copy.byteLength); + } + return fromArrayLike(arrayView); +} + +function fromArrayBuffer(array: ArrayBuffer, byteOffset: number, length: number): Buffer { + if (byteOffset < 0 || array.byteLength < byteOffset) { + throw new RangeError('"offset" is outside of buffer bounds'); + } + + if (array.byteLength < byteOffset + (length || 0)) { + throw new RangeError('"length" is outside of buffer bounds'); + } + + let buf; + if (byteOffset === undefined && length === undefined) { + buf = new Uint8Array(array); + } else if (length === undefined) { + buf = new Uint8Array(array, byteOffset); + } else { + buf = new Uint8Array(array, byteOffset, length); + } + + // Return an augmented `Uint8Array` instance + Object.setPrototypeOf(buf, Buffer.prototype); + + return buf; +} + +function fromObject(obj) { + if (Buffer.isBuffer(obj)) { + const len = checked(obj.length) | 0; + const buf = new Buffer(len); + + if (buf.length === 0) { + return buf; + } + + obj.copy(buf, 0, 0, len); + return buf; + } + + if (obj.length !== undefined) { + if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { + return new Buffer(0); + } + return fromArrayLike(obj); + } + + if (obj.type === 'Buffer' && Array.isArray(obj.data)) { + return fromArrayLike(obj.data); + } +} + +function checked(length) { + // Note: cannot use `length < kMaxLength` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= kMaxLength) { + throw new RangeError( + `${'Attempt to allocate Buffer larger than maximum ' + 'size: 0x'}${kMaxLength.toString( + 16 + )} bytes` + ); + } + return length | 0; +} + +function byteLength(string, encoding) { + if (Buffer.isBuffer(string)) { + return string.length; + } + if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { + return string.byteLength; + } + if (typeof string !== 'string') { + throw new TypeError( + `${ + 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + + 'Received type ' + }${typeof string}` + ); + } + + const len = string.length; + const mustMatch = arguments.length > 2 && arguments[2] === true; + if (!mustMatch && len === 0) return 0; + + // Use a for loop to avoid recursion + let loweredCase = false; + for (;;) { + switch (encoding) { + case 'ascii': + case 'latin1': + case 'binary': + return len; + case 'utf8': + case 'utf-8': + return utf8ToBytes(string).length; + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2; + case 'hex': + return len >>> 1; + case 'base64': + return base64ToBytes(string).length; + default: + if (loweredCase) { + return mustMatch ? -1 : utf8ToBytes(string).length; // assume utf8 + } + encoding = `${encoding}`.toLowerCase(); + loweredCase = true; + } + } +} + +// Buffer.byteLength = byteLength; + +function swap(b, n, m) { + const i = b[n]; + b[n] = b[m]; + b[m] = i; +} + +// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, +// OR the last index of `val` in `buffer` at offset <= `byteOffset`. +// +// Arguments: +// - buffer - a Buffer to search +// - val - a string, Buffer, or number +// - byteOffset - an index into `buffer`; will be clamped to an int32 +// - encoding - an optional encoding, relevant is val is a string +// - dir - true for indexOf, false for lastIndexOf +function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) { + // Empty buffer means no match + if (buffer.length === 0) return -1; + + // Normalize byteOffset + if (typeof byteOffset === 'string') { + encoding = byteOffset; + byteOffset = 0; + } else if (byteOffset > 0x7fffffff) { + byteOffset = 0x7fffffff; + } else if (byteOffset < -0x80000000) { + byteOffset = -0x80000000; + } + byteOffset = Number(byteOffset); // Coerce to Number. + if (numberIsNaN(byteOffset)) { + // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer + byteOffset = dir ? 0 : buffer.length - 1; + } + + // Normalize byteOffset: negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = buffer.length + byteOffset; + if (byteOffset >= buffer.length) { + if (dir) return -1; + byteOffset = buffer.length - 1; + } else if (byteOffset < 0) { + if (dir) byteOffset = 0; + else return -1; + } + + // Normalize val + if (typeof val === 'string') { + val = Buffer.from(val, encoding); + } + + // Finally, search either indexOf (if dir is true) or lastIndexOf + if (Buffer.isBuffer(val)) { + // Special case: looking for empty string/buffer always fails + if (val.length === 0) { + return -1; + } + return arrayIndexOf(buffer, val, byteOffset, encoding, dir); + } else if (typeof val === 'number') { + val = val & 0xff; // Search for a byte value [0-255] + if (typeof Uint8Array.prototype.indexOf === 'function') { + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset); + } + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset); + } + return arrayIndexOf(buffer, [val], byteOffset, encoding, dir); + } + + throw new TypeError('val must be string, number or Buffer'); +} + +function arrayIndexOf(arr, val, byteOffset, encoding, dir) { + let indexSize = 1; + let arrLength = arr.length; + let valLength = val.length; + + if (encoding !== undefined) { + encoding = String(encoding).toLowerCase(); + if ( + encoding === 'ucs2' || + encoding === 'ucs-2' || + encoding === 'utf16le' || + encoding === 'utf-16le' + ) { + if (arr.length < 2 || val.length < 2) { + return -1; + } + indexSize = 2; + arrLength /= 2; + valLength /= 2; + byteOffset /= 2; + } + } + + function read(buf, i) { + if (indexSize === 1) { + return buf[i]; + } + return buf.readUInt16BE(i * indexSize); + } + + let i; + if (dir) { + let foundIndex = -1; + for (i = byteOffset; i < arrLength; i++) { + if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { + if (foundIndex === -1) foundIndex = i; + if (i - foundIndex + 1 === valLength) return foundIndex * indexSize; + } else { + if (foundIndex !== -1) i -= i - foundIndex; + foundIndex = -1; + } + } + } else { + if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength; + for (i = byteOffset; i >= 0; i--) { + let found = true; + for (let j = 0; j < valLength; j++) { + if (read(arr, i + j) !== read(val, j)) { + found = false; + break; + } + } + if (found) return i; + } + } + + return -1; +} + +function hexWrite(buf: Buffer, string, offset, length): number { + offset = Number(offset) || 0; + const remaining = buf.length - offset; + if (!length) { + length = remaining; + } else { + length = Number(length); + if (length > remaining) { + length = remaining; + } + } + + const strLen = string.length; + + if (length > strLen / 2) { + length = strLen / 2; + } + let i; + for (i = 0; i < length; ++i) { + const parsed = parseInt(string.substr(i * 2, 2), 16); + if (numberIsNaN(parsed)) return i; + buf[offset + i] = parsed; + } + return i; +} + +function utf8Write(buf: Buffer, string, offset, length) { + return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length); +} + +function asciiWrite(buf: Buffer, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length); +} + +function base64Write(buf: Buffer, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length); +} + +function ucs2Write(buf: Buffer, string, offset, length) { + return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length); +} + +function base64Slice(buf: Buffer, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf); + } + return base64.fromByteArray(buf.slice(start, end)); +} + +function utf8Slice(buf: Buffer, start, end) { + end = Math.min(buf.length, end); + const res: number[] = []; + + let i = start; + while (i < end) { + const firstByte = buf[i]; + let codePoint: number | null = null; + let bytesPerSequence = firstByte > 0xef ? 4 : firstByte > 0xdf ? 3 : firstByte > 0xbf ? 2 : 1; + + if (i + bytesPerSequence <= end) { + let fourthByte; + let secondByte; + let tempCodePoint; + let thirdByte; + + switch (bytesPerSequence) { + case 1: + if (firstByte < 0x80) { + codePoint = firstByte; + } + break; + case 2: + secondByte = buf[i + 1]; + if ((secondByte & 0xc0) === 0x80) { + tempCodePoint = ((firstByte & 0x1f) << 0x6) | (secondByte & 0x3f); + if (tempCodePoint > 0x7f) { + codePoint = tempCodePoint; + } + } + break; + case 3: + secondByte = buf[i + 1]; + thirdByte = buf[i + 2]; + if ((secondByte & 0xc0) === 0x80 && (thirdByte & 0xc0) === 0x80) { + tempCodePoint = + ((firstByte & 0xf) << 0xc) | ((secondByte & 0x3f) << 0x6) | (thirdByte & 0x3f); + if (tempCodePoint > 0x7ff && (tempCodePoint < 0xd800 || tempCodePoint > 0xdfff)) { + codePoint = tempCodePoint; + } + } + break; + case 4: + secondByte = buf[i + 1]; + thirdByte = buf[i + 2]; + fourthByte = buf[i + 3]; + if ( + (secondByte & 0xc0) === 0x80 && + (thirdByte & 0xc0) === 0x80 && + (fourthByte & 0xc0) === 0x80 + ) { + tempCodePoint = + ((firstByte & 0xf) << 0x12) | + ((secondByte & 0x3f) << 0xc) | + ((thirdByte & 0x3f) << 0x6) | + (fourthByte & 0x3f); + if (tempCodePoint > 0xffff && tempCodePoint < 0x110000) { + codePoint = tempCodePoint; + } + } + } + } + + if (codePoint === null) { + // we did not generate a valid codePoint so insert a + // replacement char (U+FFFD) and advance only 1 byte + codePoint = 0xfffd; + bytesPerSequence = 1; + } else if (codePoint > 0xffff) { + // encode to utf16 (surrogate pair dance) + codePoint -= 0x10000; + res.push(((codePoint >>> 10) & 0x3ff) | 0xd800); + codePoint = 0xdc00 | (codePoint & 0x3ff); + } + + res.push(codePoint); + i += bytesPerSequence; + } + + return decodeCodePointsArray(res); +} + +// Based on http://stackoverflow.com/a/22747272/680742, the browser with +// the lowest limit is Chrome, with 0x10000 args. +// We go 1 magnitude less, for safety +const MAX_ARGUMENTS_LENGTH = 0x1000; + +function decodeCodePointsArray(codePoints: number[]): string { + const len = codePoints.length; + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints); // avoid extra slice() + } + + // Decode in chunks to avoid "call stack size exceeded". + let res = ''; + let i = 0; + while (i < len) { + res += String.fromCharCode.apply(String, codePoints.slice(i, (i += MAX_ARGUMENTS_LENGTH))); + } + return res; +} + +function asciiSlice(buf: Buffer, start, end): string { + let ret = ''; + end = Math.min(buf.length, end); + + for (let i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i] & 0x7f); + } + return ret; +} + +function latin1Slice(buf: Buffer, start, end): string { + let ret = ''; + end = Math.min(buf.length, end); + + for (let i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i]); + } + return ret; +} + +function hexSlice(buf: Buffer, start, end): string { + const len = buf.length; + + if (!start || start < 0) start = 0; + if (!end || end < 0 || end > len) end = len; + + let out = ''; + for (let i = start; i < end; ++i) { + out += hexSliceLookupTable[buf[i]]; + } + return out; +} + +function utf16leSlice(buf: Buffer, start, end): string { + const bytes = buf.slice(start, end); + let res = ''; + // If bytes.length is odd, the last 8 bits must be ignored (same as node.js) + for (let i = 0; i < bytes.length - 1; i += 2) { + res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256); + } + return res; +} + +/* + * Need to make sure that buffer isn't trying to write out of bounds. + */ +function checkOffset(offset, ext, length): void { + if (offset % 1 !== 0 || offset < 0) throw new RangeError('offset is not uint'); + if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length'); +} + +function checkIEEE754(buf: Buffer, value, offset, ext, max, min): void { + if (offset + ext > buf.length) throw new RangeError('Index out of range'); + if (offset < 0) throw new RangeError('Index out of range'); +} + +function writeFloat(buf: Buffer, value, offset, littleEndian, noAssert): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) { + checkIEEE754(buf, value, offset, 4, 3.4028234663852886e38, -3.4028234663852886e38); + } + ieee754.write(buf, value, offset, littleEndian, 23, 4); + return offset + 4; +} + +function writeDouble(buf: Buffer, value, offset, littleEndian, noAssert): number { + value = Number(value); + offset = offset >>> 0; + if (!noAssert) { + checkIEEE754(buf, value, offset, 8, 1.7976931348623157e308, -1.7976931348623157e308); + } + ieee754.write(buf, value, offset, littleEndian, 52, 8); + return offset + 8; +} + +// CUSTOM ERRORS +// ============= + +// Simplified versions from Node, changed for Buffer-only usage +const errors: Record = {}; +function E(sym, getMessage, Base) { + errors[sym] = class NodeError extends Base { + constructor() { + super(); + + Object.defineProperty(this, 'message', { + value: getMessage.apply(this, arguments), + writable: true, + configurable: true + }); + + // Add the error code to the name to include it in the stack trace. + this.name = `${this.name} [${sym}]`; + // Access the stack to generate the error message including the error code + // from the name. + this.stack; // eslint-disable-line no-unused-expressions + // Reset the name to the actual name. + delete this.name; + } + + get code() { + return sym; + } + + set code(value) { + Object.defineProperty(this, 'code', { + configurable: true, + enumerable: true, + value, + writable: true + }); + } + + toString() { + return `${this.name} [${sym}]: ${this.message}`; + } + }; +} + +E( + 'ERR_BUFFER_OUT_OF_BOUNDS', + function (name) { + if (name) { + return `${name} is outside of buffer bounds`; + } + + return 'Attempt to access memory outside buffer bounds'; + }, + RangeError +); +E( + 'ERR_INVALID_ARG_TYPE', + function (name, actual) { + return `The "${name}" argument must be of type number. Received type ${typeof actual}`; + }, + TypeError +); +E( + 'ERR_OUT_OF_RANGE', + function (str, range, input) { + let msg = `The value of "${str}" is out of range.`; + let received = input; + if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) { + received = addNumericalSeparator(String(input)); + } else if (typeof input === 'bigint') { + received = String(input); + if (input > BigInt(2) ** BigInt(32) || input < -(BigInt(2) ** BigInt(32))) { + received = addNumericalSeparator(received); + } + received += 'n'; + } + msg += ` It must be ${range}. Received ${received}`; + return msg; + }, + RangeError +); + +function addNumericalSeparator(val) { + let res = ''; + let i = val.length; + const start = val[0] === '-' ? 1 : 0; + for (; i >= start + 4; i -= 3) { + res = `_${val.slice(i - 3, i)}${res}`; + } + return `${val.slice(0, i)}${res}`; +} + +// CHECK FUNCTIONS +// =============== + +function checkBounds(buf, offset, byteLength) { + validateNumber(offset, 'offset'); + if (buf[offset] === undefined || buf[offset + byteLength] === undefined) { + boundsError(offset, buf.length - (byteLength + 1)); + } +} + +function checkIntBI(value, min, max, buf, offset, byteLength) { + if (value > max || value < min) { + const n = typeof min === 'bigint' ? 'n' : ''; + let range; + if (byteLength > 3) { + if (min === 0 || min === BigInt(0)) { + range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}`; + } else { + range = + `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and < 2 ** ` + + `${(byteLength + 1) * 8 - 1}${n}`; + } + } else { + range = `>= ${min}${n} and <= ${max}${n}`; + } + throw new errors.ERR_OUT_OF_RANGE('value', range, value); + } + checkBounds(buf, offset, byteLength); +} + +function validateNumber(value, name) { + if (typeof value !== 'number') { + throw new errors.ERR_INVALID_ARG_TYPE(name, 'number', value); + } +} + +function boundsError(value, length, type?) { + if (Math.floor(value) !== value) { + validateNumber(value, type); + throw new errors.ERR_OUT_OF_RANGE(type || 'offset', 'an integer', value); + } + + if (length < 0) { + throw new errors.ERR_BUFFER_OUT_OF_BOUNDS(); + } + + throw new errors.ERR_OUT_OF_RANGE(type || 'offset', `>= ${type ? 1 : 0} and <= ${length}`, value); +} + +// HELPER FUNCTIONS +// ================ + +const INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g; + +function base64clean(str: string): string { + // Node takes equal signs as end of the Base64 encoding + str = str.split('=')[0]; + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = str.trim().replace(INVALID_BASE64_RE, ''); + // Node converts strings with length < 2 to '' + if (str.length < 2) return ''; + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = `${str}=`; + } + return str; +} + +function utf8ToBytes(string: string, units?: number): number[] { + units = units || Infinity; + let codePoint; + const length = string.length; + let leadSurrogate = null; + const bytes: number[] = []; + + for (let i = 0; i < length; ++i) { + codePoint = string.charCodeAt(i); + + // is surrogate component + if (codePoint > 0xd7ff && codePoint < 0xe000) { + // last char was a lead + if (!leadSurrogate) { + // no lead yet + if (codePoint > 0xdbff) { + // unexpected trail + if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd); + continue; + } else if (i + 1 === length) { + // unpaired lead + if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd); + continue; + } + + // valid lead + leadSurrogate = codePoint; + + continue; + } + + // 2 leads in a row + if (codePoint < 0xdc00) { + if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd); + leadSurrogate = codePoint; + continue; + } + + // valid surrogate pair + codePoint = (((leadSurrogate - 0xd800) << 10) | (codePoint - 0xdc00)) + 0x10000; + } else if (leadSurrogate) { + // valid bmp char, but last char was a lead + if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd); + } + + leadSurrogate = null; + + // encode utf8 + if (codePoint < 0x80) { + if ((units -= 1) < 0) break; + bytes.push(codePoint); + } else if (codePoint < 0x800) { + if ((units -= 2) < 0) break; + bytes.push((codePoint >> 0x6) | 0xc0, (codePoint & 0x3f) | 0x80); + } else if (codePoint < 0x10000) { + if ((units -= 3) < 0) break; + bytes.push( + (codePoint >> 0xc) | 0xe0, + ((codePoint >> 0x6) & 0x3f) | 0x80, + (codePoint & 0x3f) | 0x80 + ); + } else if (codePoint < 0x110000) { + if ((units -= 4) < 0) break; + bytes.push( + (codePoint >> 0x12) | 0xf0, + ((codePoint >> 0xc) & 0x3f) | 0x80, + ((codePoint >> 0x6) & 0x3f) | 0x80, + (codePoint & 0x3f) | 0x80 + ); + } else { + throw new Error('Invalid code point'); + } + } + + return bytes; +} + +function asciiToBytes(str: string): number[] { + const byteArray: number[] = []; + for (let i = 0; i < str.length; ++i) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xff); + } + return byteArray; +} + +function utf16leToBytes(str: string, units: number): number[] { + let c; + let hi; + let lo; + const byteArray: number[] = []; + for (let i = 0; i < str.length; ++i) { + if ((units -= 2) < 0) break; + + c = str.charCodeAt(i); + hi = c >> 8; + lo = c % 256; + byteArray.push(lo); + byteArray.push(hi); + } + + return byteArray; +} + +function base64ToBytes(str: string): Uint8Array { + return base64.toByteArray(base64clean(str)); +} + +function blitBuffer(src, dst, offset, length): number { + let i; + for (i = 0; i < length; ++i) { + if (i + offset >= dst.length || i >= src.length) break; + dst[i + offset] = src[i]; + } + return i; +} + +// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass +// the `instanceof` check but they should be treated as of that type. +// See: https://github.com/feross/buffer/issues/166 +function isInstance(obj, type): boolean { + return ( + obj instanceof type || + (obj != null && + obj.constructor != null && + obj.constructor.name != null && + obj.constructor.name === type.name) + ); +} +function numberIsNaN(obj): boolean { + // For IE11 support + return obj !== obj; // eslint-disable-line no-self-compare +} + +// Create lookup table for `toString('hex')` +// See: https://github.com/feross/buffer/issues/219 +const hexSliceLookupTable = (function () { + const alphabet = '0123456789abcdef'; + const table = new Array(256); + for (let i = 0; i < 16; ++i) { + const i16 = i * 16; + for (let j = 0; j < 16; ++j) { + table[i16 + j] = alphabet[i] + alphabet[j]; + } + } + return table; +})(); diff --git a/modules/parquet/src/polyfills/buffer/index.ts b/modules/parquet/src/polyfills/buffer/index.ts new file mode 100644 index 0000000000..cd482f45fe --- /dev/null +++ b/modules/parquet/src/polyfills/buffer/index.ts @@ -0,0 +1,9 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +export {Buffer as BufferPolyfill} from './buffer'; +export {Buffer} from './install-buffer-polyfill'; +export {installBufferPolyfill} from './buffer-polyfill.node'; + +// import { installBufferPolyfill } from "./buffer-polyfill.node"; + +// installBufferPolyfill(); diff --git a/modules/parquet/src/polyfills/buffer/install-buffer-polyfill.ts b/modules/parquet/src/polyfills/buffer/install-buffer-polyfill.ts new file mode 100644 index 0000000000..6f9d3ff647 --- /dev/null +++ b/modules/parquet/src/polyfills/buffer/install-buffer-polyfill.ts @@ -0,0 +1,8 @@ +import {installBufferPolyfill} from './buffer-polyfill.node'; + +// @ts-ignore +globalThis.process = globalThis.process || {}; +// @ts-ignore +globalThis.process.env = globalThis.process.env || {}; + +export const Buffer = installBufferPolyfill(); diff --git a/modules/parquet/src/polyfills/util.js b/modules/parquet/src/polyfills/util.js new file mode 100644 index 0000000000..efef2e0b96 --- /dev/null +++ b/modules/parquet/src/polyfills/util.js @@ -0,0 +1,7 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +// Polyfill for Node.js util library + +export const TextEncoder = globalThis.TextEncoder; +export const TextDecoder = globalThis.TextDecoder; diff --git a/modules/parquet/src/workers/parquet-worker.ts b/modules/parquet/src/workers/parquet-worker.ts index a8c34ec0e5..da1f0586bf 100644 --- a/modules/parquet/src/workers/parquet-worker.ts +++ b/modules/parquet/src/workers/parquet-worker.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import {createLoaderWorker} from '@loaders.gl/loader-utils'; import {ParquetLoader} from '../index'; diff --git a/modules/parquet/test/buffer-polyfill/base64.spec.js b/modules/parquet/test/buffer-polyfill/base64.spec.js new file mode 100644 index 0000000000..8b981ea820 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/base64.spec.js @@ -0,0 +1,58 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; + +test('base64: ignore whitespace', function (t) { + const text = '\n YW9ldQ== ' + const buf = new BufferPolyfill(text, 'base64') + t.equal(buf.toString(), 'aoeu') + t.end() +}) + +test('base64: strings without padding', function (t) { + t.equal((new BufferPolyfill('YW9ldQ', 'base64').toString()), 'aoeu') + t.end() +}) + +test('base64: newline in utf8 -- should not be an issue', function (t) { + t.equal( + new BufferPolyfill('LS0tCnRpdGxlOiBUaHJlZSBkYXNoZXMgbWFya3MgdGhlIHNwb3QKdGFnczoK', 'base64').toString('utf8'), + '---\ntitle: Three dashes marks the spot\ntags:\n' + ) + t.end() +}) + +test('base64: newline in base64 -- should get stripped', function (t) { + t.equal( + new BufferPolyfill('LS0tCnRpdGxlOiBUaHJlZSBkYXNoZXMgbWFya3MgdGhlIHNwb3QKdGFnczoK\nICAtIHlhbWwKICAtIGZyb250LW1hdHRlcgogIC0gZGFzaGVzCmV4cGFuZWQt', 'base64').toString('utf8'), + '---\ntitle: Three dashes marks the spot\ntags:\n - yaml\n - front-matter\n - dashes\nexpaned-' + ) + t.end() +}) + +test('base64: tab characters in base64 - should get stripped', function (t) { + t.equal( + new BufferPolyfill('LS0tCnRpdGxlOiBUaHJlZSBkYXNoZXMgbWFya3MgdGhlIHNwb3QKdGFnczoK\t\t\t\tICAtIHlhbWwKICAtIGZyb250LW1hdHRlcgogIC0gZGFzaGVzCmV4cGFuZWQt', 'base64').toString('utf8'), + '---\ntitle: Three dashes marks the spot\ntags:\n - yaml\n - front-matter\n - dashes\nexpaned-' + ) + t.end() +}) + +test('base64: invalid non-alphanumeric characters -- should be stripped', function (t) { + t.equal( + new BufferPolyfill('!"#$%&\'()*,.:;<=>?@[\\]^`{|}~', 'base64').toString('utf8'), + '' + ) + t.end() +}) + +test('base64: high byte', function (t) { + const highByte = BufferPolyfill.from([128]) + t.deepEqual( + BufferPolyfill.alloc(1, highByte.toString('base64'), 'base64'), + highByte + ) + t.end() +}) diff --git a/modules/parquet/test/buffer-polyfill/basic.spec.js b/modules/parquet/test/buffer-polyfill/basic.spec.js new file mode 100644 index 0000000000..f5b74fe0b6 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/basic.spec.js @@ -0,0 +1,76 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; + +test('instanceof Buffer', function (t) { + const buf = new BufferPolyfill([1, 2]) + t.ok(buf instanceof BufferPolyfill) + t.end() +}) + +test('convert to Uint8Array in modern browsers', function (t) { + const buf = new BufferPolyfill([1, 2]) + const uint8array = new Uint8Array(buf.buffer) + t.ok(uint8array instanceof Uint8Array) + t.equal(uint8array[0], 1) + t.equal(uint8array[1], 2) + t.end() +}) + +test('indexes from a string', function (t) { + const buf = new BufferPolyfill('abc') + t.equal(buf[0], 97) + t.equal(buf[1], 98) + t.equal(buf[2], 99) + t.end() +}) + +test('indexes from an array', function (t) { + const buf = new BufferPolyfill([97, 98, 99]) + t.equal(buf[0], 97) + t.equal(buf[1], 98) + t.equal(buf[2], 99) + t.end() +}) + +test('setting index value should modify buffer contents', function (t) { + const buf = new BufferPolyfill([97, 98, 99]) + t.equal(buf[2], 99) + t.equal(buf.toString(), 'abc') + + buf[2] += 10 + t.equal(buf[2], 109) + t.equal(buf.toString(), 'abm') + t.end() +}) + +test('storing negative number should cast to unsigned', function (t) { + let buf = new BufferPolyfill(1) + + buf[0] = -3 + t.equal(buf[0], 253) + + buf = new BufferPolyfill(1) + buf.writeInt8(-3, 0) + t.equal(buf[0], 253) + + t.end() +}) + +test('test that memory is copied from array-like', function (t) { + const u = new Uint8Array(4) + const b = new BufferPolyfill(u) + b[0] = 1 + b[1] = 2 + b[2] = 3 + b[3] = 4 + + t.equal(u[0], 0) + t.equal(u[1], 0) + t.equal(u[2], 0) + t.equal(u[3], 0) + + t.end() +}) diff --git a/modules/parquet/test/buffer-polyfill/compare.spec.js b/modules/parquet/test/buffer-polyfill/compare.spec.js new file mode 100644 index 0000000000..45dae228bf --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/compare.spec.js @@ -0,0 +1,65 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; + +test('buffer.compare', function (t) { + const b = new BufferPolyfill(1).fill('a') + const c = new BufferPolyfill(1).fill('c') + const d = new BufferPolyfill(2).fill('aa') + + t.equal(b.compare(c), -1) + t.equal(c.compare(d), 1) + t.equal(d.compare(b), 1) + t.equal(b.compare(d), -1) + + // static method + t.equal(BufferPolyfill.compare(b, c), -1) + t.equal(BufferPolyfill.compare(c, d), 1) + t.equal(BufferPolyfill.compare(d, b), 1) + t.equal(BufferPolyfill.compare(b, d), -1) + t.end() +}) + +test('buffer.compare argument validation', function (t) { + t.throws(function () { + const b = new BufferPolyfill(1) + // @ts-expect-error throws + BufferPolyfill.compare(b, 'abc') + }) + + t.throws(function () { + const b = new BufferPolyfill(1) + // @ts-expect-error throws + BufferPolyfill.compare('abc', b) + }) + + t.throws(function () { + const b = new BufferPolyfill(1) + // @ts-expect-error throws + b.compare('abc') + }) + t.end() +}) + +test('buffer.equals', function (t) { + const b = new BufferPolyfill(5).fill('abcdf') + const c = new BufferPolyfill(5).fill('abcdf') + const d = new BufferPolyfill(5).fill('abcde') + const e = new BufferPolyfill(6).fill('abcdef') + + t.ok(b.equals(c)) + t.ok(!c.equals(d)) + t.ok(!d.equals(e)) + t.end() +}) + +test('buffer.equals argument validation', function (t) { + t.throws(function () { + const b = new BufferPolyfill(1) + // @ts-expect-error throws + b.equals('abc') + }) + t.end() +}) diff --git a/modules/parquet/test/buffer-polyfill/constructor.spec.js b/modules/parquet/test/buffer-polyfill/constructor.spec.js new file mode 100644 index 0000000000..652e22c48b --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/constructor.spec.js @@ -0,0 +1,193 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; + +test('new buffer from array', function (t) { + t.equal( + new BufferPolyfill([1, 2, 3]).toString(), + '\u0001\u0002\u0003' + ) + t.end() +}) + +test('new buffer from array w/ negatives', function (t) { + t.equal( + new BufferPolyfill([-1, -2, -3]).toString('hex'), + 'fffefd' + ) + t.end() +}) + +test('new buffer from array with mixed signed input', function (t) { + t.equal( + new BufferPolyfill([-255, 255, -128, 128, 512, -512, 511, -511]).toString('hex'), + '01ff80800000ff01' + ) + t.end() +}) + +test('new buffer from string', function (t) { + t.equal( + new BufferPolyfill('hey', 'utf8').toString(), + 'hey' + ) + t.end() +}) + +test('new buffer from buffer', function (t) { + const b1 = new BufferPolyfill('asdf') + const b2 = new BufferPolyfill(b1) + t.equal(b1.toString('hex'), b2.toString('hex')) + t.end() +}) + +test('new buffer from ArrayBuffer', function (t) { + if (typeof ArrayBuffer !== 'undefined') { + const arraybuffer = new Uint8Array([0, 1, 2, 3]).buffer + const b = new BufferPolyfill(arraybuffer) + t.equal(b.length, 4) + t.equal(b[0], 0) + t.equal(b[1], 1) + t.equal(b[2], 2) + t.equal(b[3], 3) + t.equal(b[4], undefined) + } + t.end() +}) + +test('new buffer from ArrayBuffer, shares memory', function (t) { + const u = new Uint8Array([0, 1, 2, 3]) + const arraybuffer = u.buffer + const b = new BufferPolyfill(arraybuffer) + t.equal(b.length, 4) + t.equal(b[0], 0) + t.equal(b[1], 1) + t.equal(b[2], 2) + t.equal(b[3], 3) + t.equal(b[4], undefined) + + // changing the Uint8Array (and thus the ArrayBuffer), changes the Buffer + u[0] = 10 + t.equal(b[0], 10) + u[1] = 11 + t.equal(b[1], 11) + u[2] = 12 + t.equal(b[2], 12) + u[3] = 13 + t.equal(b[3], 13) + t.end() +}) + +test('new buffer from Uint8Array', function (t) { + if (typeof Uint8Array !== 'undefined') { + const b1 = new Uint8Array([0, 1, 2, 3]) + const b2 = new BufferPolyfill(b1) + t.equal(b1.length, b2.length) + t.equal(b1[0], 0) + t.equal(b1[1], 1) + t.equal(b1[2], 2) + t.equal(b1[3], 3) + t.equal(b1[4], undefined) + } + t.end() +}) + +test('new buffer from Uint16Array', function (t) { + if (typeof Uint16Array !== 'undefined') { + const b1 = new Uint16Array([0, 1, 2, 3]) + const b2 = new BufferPolyfill(b1) + t.equal(b1.length, b2.length) + t.equal(b1[0], 0) + t.equal(b1[1], 1) + t.equal(b1[2], 2) + t.equal(b1[3], 3) + t.equal(b1[4], undefined) + } + t.end() +}) + +test('new buffer from Uint32Array', function (t) { + if (typeof Uint32Array !== 'undefined') { + const b1 = new Uint32Array([0, 1, 2, 3]) + const b2 = new BufferPolyfill(b1) + t.equal(b1.length, b2.length) + t.equal(b1[0], 0) + t.equal(b1[1], 1) + t.equal(b1[2], 2) + t.equal(b1[3], 3) + t.equal(b1[4], undefined) + } + t.end() +}) + +test('new buffer from Int16Array', function (t) { + if (typeof Int16Array !== 'undefined') { + const b1 = new Int16Array([0, 1, 2, 3]) + const b2 = new BufferPolyfill(b1) + t.equal(b1.length, b2.length) + t.equal(b1[0], 0) + t.equal(b1[1], 1) + t.equal(b1[2], 2) + t.equal(b1[3], 3) + t.equal(b1[4], undefined) + } + t.end() +}) + +test('new buffer from Int32Array', function (t) { + if (typeof Int32Array !== 'undefined') { + const b1 = new Int32Array([0, 1, 2, 3]) + const b2 = new BufferPolyfill(b1) + t.equal(b1.length, b2.length) + t.equal(b1[0], 0) + t.equal(b1[1], 1) + t.equal(b1[2], 2) + t.equal(b1[3], 3) + t.equal(b1[4], undefined) + } + t.end() +}) + +test('new buffer from Float32Array', function (t) { + if (typeof Float32Array !== 'undefined') { + const b1 = new Float32Array([0, 1, 2, 3]) + const b2 = new BufferPolyfill(b1) + t.equal(b1.length, b2.length) + t.equal(b1[0], 0) + t.equal(b1[1], 1) + t.equal(b1[2], 2) + t.equal(b1[3], 3) + t.equal(b1[4], undefined) + } + t.end() +}) + +test('new buffer from Float64Array', function (t) { + if (typeof Float64Array !== 'undefined') { + const b1 = new Float64Array([0, 1, 2, 3]) + const b2 = new BufferPolyfill(b1) + t.equal(b1.length, b2.length) + t.equal(b1[0], 0) + t.equal(b1[1], 1) + t.equal(b1[2], 2) + t.equal(b1[3], 3) + t.equal(b1[4], undefined) + } + t.end() +}) + +test('new buffer from buffer.toJSON() output', function (t) { + if (typeof JSON === 'undefined') { + // ie6, ie7 lack support + t.end() + return + } + const buf = new BufferPolyfill('test') + const json = JSON.stringify(buf) + const obj = JSON.parse(json) + const copy = new BufferPolyfill(obj) + t.ok(buf.equals(copy)) + t.end() +}) diff --git a/modules/parquet/test/buffer-polyfill/from-string.spec.js b/modules/parquet/test/buffer-polyfill/from-string.spec.js new file mode 100644 index 0000000000..d2be006f1f --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/from-string.spec.js @@ -0,0 +1,136 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; + +test('detect utf16 surrogate pairs', function (t) { + const text = '\uD83D\uDE38' + '\uD83D\uDCAD' + '\uD83D\uDC4D' + const buf = new BufferPolyfill(text) + t.equal(text, buf.toString()) + t.end() +}) + +test('detect utf16 surrogate pairs over U+20000 until U+10FFFF', function (t) { + const text = '\uD842\uDFB7' + '\uD93D\uDCAD' + '\uDBFF\uDFFF' + const buf = new BufferPolyfill(text) + t.equal(text, buf.toString()) + t.end() +}) + +test('replace orphaned utf16 surrogate lead code point', function (t) { + const text = '\uD83D\uDE38' + '\uD83D' + '\uD83D\uDC4D' + const buf = new BufferPolyfill(text) + t.deepEqual(buf, new BufferPolyfill([0xf0, 0x9f, 0x98, 0xb8, 0xef, 0xbf, 0xbd, 0xf0, 0x9f, 0x91, 0x8d])) + t.end() +}) + +test('replace orphaned utf16 surrogate trail code point', function (t) { + const text = '\uD83D\uDE38' + '\uDCAD' + '\uD83D\uDC4D' + const buf = new BufferPolyfill(text) + t.deepEqual(buf, new BufferPolyfill([0xf0, 0x9f, 0x98, 0xb8, 0xef, 0xbf, 0xbd, 0xf0, 0x9f, 0x91, 0x8d])) + t.end() +}) + +test('do not write partial utf16 code units', function (t) { + const f = new BufferPolyfill([0, 0, 0, 0, 0]) + t.equal(f.length, 5) + const size = f.write('あいうえお', 'utf16le') + t.equal(size, 4) + t.deepEqual(f, new BufferPolyfill([0x42, 0x30, 0x44, 0x30, 0x00])) + t.end() +}) + +// eslint-disable-next-line max-statements +test('handle partial utf16 code points when encoding to utf8 the way node does', function (t) { + const text = '\uD83D\uDE38' + '\uD83D\uDC4D' + + let buf = new BufferPolyfill(8) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0xf0, 0x9f, 0x98, 0xb8, 0xf0, 0x9f, 0x91, 0x8d])) + + buf = new BufferPolyfill(7) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0xf0, 0x9f, 0x98, 0xb8, 0x00, 0x00, 0x00])) + + buf = new BufferPolyfill(6) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0xf0, 0x9f, 0x98, 0xb8, 0x00, 0x00])) + + buf = new BufferPolyfill(5) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0xf0, 0x9f, 0x98, 0xb8, 0x00])) + + buf = new BufferPolyfill(4) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0xf0, 0x9f, 0x98, 0xb8])) + + buf = new BufferPolyfill(3) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0x00, 0x00, 0x00])) + + buf = new BufferPolyfill(2) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0x00, 0x00])) + + buf = new BufferPolyfill(1) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0x00])) + + t.end() +}) + +// eslint-disable-next-line max-statements +test('handle invalid utf16 code points when encoding to utf8 the way node does', function (t) { + const text = 'a' + '\uDE38\uD83D' + 'b' + + let buf = new BufferPolyfill(8) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0x61, 0xef, 0xbf, 0xbd, 0xef, 0xbf, 0xbd, 0x62])) + + buf = new BufferPolyfill(7) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0x61, 0xef, 0xbf, 0xbd, 0xef, 0xbf, 0xbd])) + + buf = new BufferPolyfill(6) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0x61, 0xef, 0xbf, 0xbd, 0x00, 0x00])) + + buf = new BufferPolyfill(5) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0x61, 0xef, 0xbf, 0xbd, 0x00])) + + buf = new BufferPolyfill(4) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0x61, 0xef, 0xbf, 0xbd])) + + buf = new BufferPolyfill(3) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0x61, 0x00, 0x00])) + + buf = new BufferPolyfill(2) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0x61, 0x00])) + + buf = new BufferPolyfill(1) + buf.fill(0) + buf.write(text) + t.deepEqual(buf, new BufferPolyfill([0x61])) + + t.end() +}) diff --git a/modules/parquet/test/buffer-polyfill/index.ts b/modules/parquet/test/buffer-polyfill/index.ts new file mode 100644 index 0000000000..b8606ba888 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/index.ts @@ -0,0 +1,15 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import './base64.spec'; +import './basic.spec'; +import './compare.spec'; +import './constructor.spec'; +import './from-string.spec'; +import './is-buffer.spec'; +import './methods.spec'; +import './slice.spec'; +import './static.spec'; +import './to-string.spec'; +import './write-infinity.spec'; +import './write.spec'; \ No newline at end of file diff --git a/modules/parquet/test/buffer-polyfill/is-buffer.spec.js b/modules/parquet/test/buffer-polyfill/is-buffer.spec.js new file mode 100644 index 0000000000..4c5aaf8e2f --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/is-buffer.spec.js @@ -0,0 +1,23 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; + +test('is-buffer tests', function (t) { + t.ok(BufferPolyfill.isBuffer(new BufferPolyfill(4)), 'new Buffer(4)') + + t.notOk(BufferPolyfill.isBuffer(undefined), 'undefined') + t.notOk(BufferPolyfill.isBuffer(null), 'null') + t.notOk(BufferPolyfill.isBuffer(''), 'empty string') + t.notOk(BufferPolyfill.isBuffer(true), 'true') + t.notOk(BufferPolyfill.isBuffer(false), 'false') + t.notOk(BufferPolyfill.isBuffer(0), '0') + t.notOk(BufferPolyfill.isBuffer(1), '1') + t.notOk(BufferPolyfill.isBuffer(1.0), '1.0') + t.notOk(BufferPolyfill.isBuffer('string'), 'string') + t.notOk(BufferPolyfill.isBuffer({}), '{}') + t.notOk(BufferPolyfill.isBuffer(function foo () {}), 'function foo () {}') + + t.end() +}) diff --git a/modules/parquet/test/buffer-polyfill/methods.spec.js b/modules/parquet/test/buffer-polyfill/methods.spec.js new file mode 100644 index 0000000000..bc82d7717c --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/methods.spec.js @@ -0,0 +1,143 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; + +test('buffer.toJSON', function (t) { + const data = [1, 2, 3, 4] + t.deepEqual( + new BufferPolyfill(data).toJSON(), + { type: 'Buffer', data: [1, 2, 3, 4] } + ) + t.end() +}) + +test('buffer.copy', function (t) { + // copied from nodejs.org example + const buf1 = new BufferPolyfill(26) + const buf2 = new BufferPolyfill(26) + + for (let i = 0; i < 26; i++) { + buf1[i] = i + 97 // 97 is ASCII a + buf2[i] = 33 // ASCII ! + } + + buf1.copy(buf2, 8, 16, 20) + + t.equal( + buf2.toString('ascii', 0, 25), + '!!!!!!!!qrst!!!!!!!!!!!!!' + ) + t.end() +}) + +test('test offset returns are correct', function (t) { + const b = new BufferPolyfill(16) + t.equal(4, b.writeUInt32LE(0, 0)) + t.equal(6, b.writeUInt16LE(0, 4)) + t.equal(7, b.writeUInt8(0, 6)) + t.equal(8, b.writeInt8(0, 7)) + t.equal(16, b.writeDoubleLE(0, 8)) + t.end() +}) + +test('concat() a varying number of buffers', function (t) { + const zero = [] + const one = [new BufferPolyfill('asdf')] + const long = [] + for (let i = 0; i < 10; i++) { + long.push(new BufferPolyfill('asdf')) + } + + const flatZero = BufferPolyfill.concat(zero) + const flatOne = BufferPolyfill.concat(one) + const flatLong = BufferPolyfill.concat(long) + const flatLongLen = BufferPolyfill.concat(long, 40) + + t.equal(flatZero.length, 0) + t.equal(flatOne.toString(), 'asdf') + t.deepEqual(flatOne, one[0]) + t.equal(flatLong.toString(), (new Array(10 + 1).join('asdf'))) + t.equal(flatLongLen.toString(), (new Array(10 + 1).join('asdf'))) + t.end() +}) + +test('concat() works on Uint8Array instances', function (t) { + const result = BufferPolyfill.concat([new Uint8Array([1, 2]), new Uint8Array([3, 4])]) + const expected = BufferPolyfill.from([1, 2, 3, 4]) + t.deepEqual(result, expected) + t.end() +}) + +test('concat() works on Uint8Array instances for smaller provided totalLength', function (t) { + const result = BufferPolyfill.concat([new Uint8Array([1, 2]), new Uint8Array([3, 4])], 3) + const expected = BufferPolyfill.from([1, 2, 3]) + t.deepEqual(result, expected) + t.end() +}) + +test('fill', function (t) { + const b = new BufferPolyfill(10) + b.fill(2) + t.equal(b.toString('hex'), '02020202020202020202') + t.end() +}) + +test('fill (string)', function (t) { + const b = new BufferPolyfill(10) + b.fill('abc') + t.equal(b.toString(), 'abcabcabca') + b.fill('է') + t.equal(b.toString(), 'էէէէէ') + t.end() +}) + +test('copy() empty buffer with sourceEnd=0', function (t) { + const source = new BufferPolyfill([42]) + const destination = new BufferPolyfill([43]) + source.copy(destination, 0, 0, 0) + t.equal(destination.readUInt8(0), 43) + t.end() +}) + +test('copy() after slice()', function (t) { + const source = new BufferPolyfill(200) + const dest = new BufferPolyfill(200) + const expected = new BufferPolyfill(200) + for (let i = 0; i < 200; i++) { + source[i] = i + dest[i] = 0 + } + + source.slice(2).copy(dest) + source.copy(expected, 0, 2) + t.deepEqual(dest, expected) + t.end() +}) + +test('copy() ascending', function (t) { + const b = new BufferPolyfill('abcdefghij') + b.copy(b, 0, 3, 10) + t.equal(b.toString(), 'defghijhij') + t.end() +}) + +test('copy() descending', function (t) { + const b = new BufferPolyfill('abcdefghij') + b.copy(b, 3, 0, 7) + t.equal(b.toString(), 'abcabcdefg') + t.end() +}) + +test('buffer.slice sets indexes', function (t) { + t.equal((new BufferPolyfill('hallo')).slice(0, 5).toString(), 'hallo') + t.end() +}) + +test('buffer.slice out of range', function (t) { + t.plan(2) + t.equal((new BufferPolyfill('hallo')).slice(0, 10).toString(), 'hallo') + t.equal((new BufferPolyfill('hallo')).slice(10, 2).toString(), '') + t.end() +}) diff --git a/modules/parquet/test/buffer-polyfill/slice.spec.js b/modules/parquet/test/buffer-polyfill/slice.spec.js new file mode 100644 index 0000000000..4f32955ebb --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/slice.spec.js @@ -0,0 +1,35 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; + +test('modifying buffer created by .slice() modifies original memory', function (t) { + const buf1 = new BufferPolyfill(26) + for (let i = 0; i < 26; i++) { + buf1[i] = i + 97 // 97 is ASCII a + } + + const buf2 = buf1.slice(0, 3) + t.equal(buf2.toString('ascii', 0, buf2.length), 'abc') + + buf2[0] = '!'.charCodeAt(0) + t.equal(buf1.toString('ascii', 0, buf2.length), '!bc') + + t.end() +}) + +test('modifying parent buffer modifies .slice() buffer\'s memory', function (t) { + const buf1 = new BufferPolyfill(26) + for (let i = 0; i < 26; i++) { + buf1[i] = i + 97 // 97 is ASCII a + } + + const buf2 = buf1.slice(0, 3) + t.equal(buf2.toString('ascii', 0, buf2.length), 'abc') + + buf1[0] = '!'.charCodeAt(0) + t.equal(buf2.toString('ascii', 0, buf2.length), '!bc') + + t.end() +}) diff --git a/modules/parquet/test/buffer-polyfill/static.spec.js b/modules/parquet/test/buffer-polyfill/static.spec.js new file mode 100644 index 0000000000..7b5151d82c --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/static.spec.js @@ -0,0 +1,20 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; + +test('Buffer.isEncoding', function (t) { + t.equal(BufferPolyfill.isEncoding('HEX'), true) + t.equal(BufferPolyfill.isEncoding('hex'), true) + t.equal(BufferPolyfill.isEncoding('bad'), false) + t.end() +}) + +test('Buffer.isBuffer', function (t) { + t.equal(BufferPolyfill.isBuffer(new BufferPolyfill('hey', 'utf8')), true) + // @ts-expect-error TODO should be supported + t.equal(BufferPolyfill.isBuffer(new BufferPolyfill([1, 2, 3], 'utf8')), true) + t.equal(BufferPolyfill.isBuffer('hey'), false) + t.end() +}) diff --git a/modules/parquet/test/buffer-polyfill/to-string.spec.js b/modules/parquet/test/buffer-polyfill/to-string.spec.js new file mode 100644 index 0000000000..ef27392359 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/to-string.spec.js @@ -0,0 +1,159 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; + +test('utf8 buffer to base64', function (t) { + t.equal(new BufferPolyfill('Ձאab', 'utf8').toString('base64'), '1YHXkGFi'); + t.end(); +}); + +test('utf8 buffer to hex', function (t) { + t.equal(new BufferPolyfill('Ձאab', 'utf8').toString('hex'), 'd581d7906162'); + t.end(); +}); + +test('utf8 to utf8', function (t) { + t.equal(new BufferPolyfill('öäüõÖÄÜÕ', 'utf8').toString('utf8'), 'öäüõÖÄÜÕ'); + t.end(); +}); + +test('utf16le to utf16', function (t) { + t.equal( + new BufferPolyfill(new BufferPolyfill('abcd', 'utf8').toString('utf16le'), 'utf16le').toString( + 'utf8' + ), + 'abcd' + ); + t.end(); +}); + +test('utf16le to utf16 with odd byte length input', function (t) { + t.equal( + new BufferPolyfill(new BufferPolyfill('abcde', 'utf8').toString('utf16le'), 'utf16le').toString( + 'utf8' + ), + 'abcd' + ); + t.end(); +}); + +test('utf16le to hex', function (t) { + t.equal(new BufferPolyfill('abcd', 'utf16le').toString('hex'), '6100620063006400'); + t.end(); +}); + +test('ascii buffer to base64', function (t) { + t.equal(new BufferPolyfill('123456!@#$%^', 'ascii').toString('base64'), 'MTIzNDU2IUAjJCVe'); + t.end(); +}); + +test('ascii buffer to hex', function (t) { + t.equal(new BufferPolyfill('123456!@#$%^', 'ascii').toString('hex'), '31323334353621402324255e'); + t.end(); +}); + +test('base64 buffer to utf8', function (t) { + t.equal(new BufferPolyfill('1YHXkGFi', 'base64').toString('utf8'), 'Ձאab'); + t.end(); +}); + +test('hex buffer to utf8', function (t) { + t.equal(new BufferPolyfill('d581d7906162', 'hex').toString('utf8'), 'Ձאab'); + t.end(); +}); + +test('base64 buffer to ascii', function (t) { + t.equal(new BufferPolyfill('MTIzNDU2IUAjJCVe', 'base64').toString('ascii'), '123456!@#$%^'); + t.end(); +}); + +test('hex buffer to ascii', function (t) { + t.equal(new BufferPolyfill('31323334353621402324255e', 'hex').toString('ascii'), '123456!@#$%^'); + t.end(); +}); + +test('base64 buffer to binary', function (t) { + t.equal(new BufferPolyfill('MTIzNDU2IUAjJCVe', 'base64').toString('binary'), '123456!@#$%^'); + t.end(); +}); + +test('hex buffer to binary', function (t) { + t.equal(new BufferPolyfill('31323334353621402324255e', 'hex').toString('binary'), '123456!@#$%^'); + t.end(); +}); + +test('utf8 to binary', function (t) { + /* jshint -W100 */ + t.equal(new BufferPolyfill('öäüõÖÄÜÕ', 'utf8').toString('binary'), 'öäüõÖÄÜÕ'); + /* jshint +W100 */ + t.end(); +}); + +test('utf8 replacement chars (1 byte sequence)', function (t) { + t.equal(new BufferPolyfill([0x80]).toString(), '\uFFFD'); + t.equal(new BufferPolyfill([0x7f]).toString(), '\u007F'); + t.end(); +}); + +test('utf8 replacement chars (2 byte sequences)', function (t) { + t.equal(new BufferPolyfill([0xc7]).toString(), '\uFFFD'); + t.equal(new BufferPolyfill([0xc7, 0xb1]).toString(), '\u01F1'); + t.equal(new BufferPolyfill([0xc0, 0xb1]).toString(), '\uFFFD\uFFFD'); + t.equal(new BufferPolyfill([0xc1, 0xb1]).toString(), '\uFFFD\uFFFD'); + t.end(); +}); + +test('utf8 replacement chars (3 byte sequences)', function (t) { + t.equal(new BufferPolyfill([0xe0]).toString(), '\uFFFD'); + t.equal(new BufferPolyfill([0xe0, 0xac]).toString(), '\uFFFD\uFFFD'); + t.equal(new BufferPolyfill([0xe0, 0xac, 0xb9]).toString(), '\u0B39'); + t.end(); +}); + +test('utf8 replacement chars (4 byte sequences)', function (t) { + t.equal(new BufferPolyfill([0xf4]).toString(), '\uFFFD'); + t.equal(new BufferPolyfill([0xf4, 0x8f]).toString(), '\uFFFD\uFFFD'); + t.equal(new BufferPolyfill([0xf4, 0x8f, 0x80]).toString(), '\uFFFD\uFFFD\uFFFD'); + t.equal(new BufferPolyfill([0xf4, 0x8f, 0x80, 0x84]).toString(), '\uDBFC\uDC04'); + t.equal(new BufferPolyfill([0xff]).toString(), '\uFFFD'); + t.equal(new BufferPolyfill([0xff, 0x8f, 0x80, 0x84]).toString(), '\uFFFD\uFFFD\uFFFD\uFFFD'); + t.end(); +}); + +test('utf8 replacement chars on 256 random bytes', function (t) { + t.equal( + new BufferPolyfill([ + 152, 130, 206, 23, 243, 238, 197, 44, 27, 86, 208, 36, 163, 184, 164, 21, 94, 242, 178, 46, + 25, 26, 253, 178, 72, 147, 207, 112, 236, 68, 179, 190, 29, 83, 239, 147, 125, 55, 143, 19, + 157, 68, 157, 58, 212, 224, 150, 39, 128, 24, 94, 225, 120, 121, 75, 192, 112, 19, 184, 142, + 203, 36, 43, 85, 26, 147, 227, 139, 242, 186, 57, 78, 11, 102, 136, 117, 180, 210, 241, 92, 3, + 215, 54, 167, 249, 1, 44, 225, 146, 86, 2, 42, 68, 21, 47, 238, 204, 153, 216, 252, 183, 66, + 222, 255, 15, 202, 16, 51, 134, 1, 17, 19, 209, 76, 238, 38, 76, 19, 7, 103, 249, 5, 107, 137, + 64, 62, 170, 57, 16, 85, 179, 193, 97, 86, 166, 196, 36, 148, 138, 193, 210, 69, 187, 38, 242, + 97, 195, 219, 252, 244, 38, 1, 197, 18, 31, 246, 53, 47, 134, 52, 105, 72, 43, 239, 128, 203, + 73, 93, 199, 75, 222, 220, 166, 34, 63, 236, 11, 212, 76, 243, 171, 110, 78, 39, 205, 204, 6, + 177, 233, 212, 243, 0, 33, 41, 122, 118, 92, 252, 0, 157, 108, 120, 70, 137, 100, 223, 243, + 171, 232, 66, 126, 111, 142, 33, 3, 39, 117, 27, 107, 54, 1, 217, 227, 132, 13, 166, 3, 73, + 53, 127, 225, 236, 134, 219, 98, 214, 125, 148, 24, 64, 142, 111, 231, 194, 42, 150, 185, 10, + 182, 163, 244, 19, 4, 59, 135, 16 + ]).toString(), + '\uFFFD\uFFFD\uFFFD\u0017\uFFFD\uFFFD\uFFFD\u002C\u001B\u0056\uFFFD\u0024\uFFFD\uFFFD\uFFFD\u0015\u005E\uFFFD\uFFFD\u002E\u0019\u001A\uFFFD\uFFFD\u0048\uFFFD\uFFFD\u0070\uFFFD\u0044\uFFFD\uFFFD\u001D\u0053\uFFFD\uFFFD\u007D\u0037\uFFFD\u0013\uFFFD\u0044\uFFFD\u003A\uFFFD\uFFFD\uFFFD\u0027\uFFFD\u0018\u005E\uFFFD\u0078\u0079\u004B\uFFFD\u0070\u0013\uFFFD\uFFFD\uFFFD\u0024\u002B\u0055\u001A\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0039\u004E\u000B\u0066\uFFFD\u0075\uFFFD\uFFFD\uFFFD\u005C\u0003\uFFFD\u0036\uFFFD\uFFFD\u0001\u002C\uFFFD\uFFFD\u0056\u0002\u002A\u0044\u0015\u002F\uFFFD\u0319\uFFFD\uFFFD\uFFFD\u0042\uFFFD\uFFFD\u000F\uFFFD\u0010\u0033\uFFFD\u0001\u0011\u0013\uFFFD\u004C\uFFFD\u0026\u004C\u0013\u0007\u0067\uFFFD\u0005\u006B\uFFFD\u0040\u003E\uFFFD\u0039\u0010\u0055\uFFFD\uFFFD\u0061\u0056\uFFFD\uFFFD\u0024\uFFFD\uFFFD\uFFFD\uFFFD\u0045\uFFFD\u0026\uFFFD\u0061\uFFFD\uFFFD\uFFFD\uFFFD\u0026\u0001\uFFFD\u0012\u001F\uFFFD\u0035\u002F\uFFFD\u0034\u0069\u0048\u002B\uFFFD\uFFFD\uFFFD\u0049\u005D\uFFFD\u004B\uFFFD\u0726\u0022\u003F\uFFFD\u000B\uFFFD\u004C\uFFFD\uFFFD\u006E\u004E\u0027\uFFFD\uFFFD\u0006\uFFFD\uFFFD\uFFFD\uFFFD\u0000\u0021\u0029\u007A\u0076\u005C\uFFFD\u0000\uFFFD\u006C\u0078\u0046\uFFFD\u0064\uFFFD\uFFFD\uFFFD\uFFFD\u0042\u007E\u006F\uFFFD\u0021\u0003\u0027\u0075\u001B\u006B\u0036\u0001\uFFFD\uFFFD\uFFFD\u000D\uFFFD\u0003\u0049\u0035\u007F\uFFFD\uFFFD\uFFFD\uFFFD\u0062\uFFFD\u007D\uFFFD\u0018\u0040\uFFFD\u006F\uFFFD\uFFFD\u002A\uFFFD\uFFFD\u000A\uFFFD\uFFFD\uFFFD\u0013\u0004\u003B\uFFFD\u0010' + ); + t.end(); +}); + +test('utf8 replacement chars for anything in the surrogate pair range', function (t) { + t.equal(new BufferPolyfill([0xed, 0x9f, 0xbf]).toString(), '\uD7FF'); + t.equal(new BufferPolyfill([0xed, 0xa0, 0x80]).toString(), '\uFFFD\uFFFD\uFFFD'); + t.equal(new BufferPolyfill([0xed, 0xbe, 0x8b]).toString(), '\uFFFD\uFFFD\uFFFD'); + t.equal(new BufferPolyfill([0xed, 0xbf, 0xbf]).toString(), '\uFFFD\uFFFD\uFFFD'); + t.equal(new BufferPolyfill([0xee, 0x80, 0x80]).toString(), '\uE000'); + t.end(); +}); + +test("utf8 don't replace the replacement char", function (t) { + t.equal(new BufferPolyfill('\uFFFD').toString(), '\uFFFD'); + t.end(); +}); diff --git a/modules/parquet/test/buffer-polyfill/wip/common.js b/modules/parquet/test/buffer-polyfill/wip/common.js new file mode 100644 index 0000000000..c71f42b730 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/common.js @@ -0,0 +1,138 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-disable required-modules, crypto-check */ +'use strict'; +const assert = require('assert'); +const mustCallChecks = []; + +function runCallChecks(exitCode) { + if (exitCode !== 0) return; + + const failed = mustCallChecks.filter(function(context) { + if ('minimum' in context) { + context.messageSegment = `at least ${context.minimum}`; + return context.actual < context.minimum; + } else { + context.messageSegment = `exactly ${context.exact}`; + return context.actual !== context.exact; + } + }); + + failed.forEach(function(context) { + console.log('Mismatched %s function calls. Expected %s, actual %d.', + context.name, + context.messageSegment, + context.actual); + console.log(context.stack.split('\n').slice(2).join('\n')); + }); + + if (failed.length) process.exit(1); +} + +exports.mustCall = function(fn, exact) { + return _mustCallInner(fn, exact, 'exact'); +}; + +function _mustCallInner(fn, criteria = 1, field) { + if (process._exiting) + throw new Error('Cannot use common.mustCall*() in process exit handler'); + if (typeof fn === 'number') { + criteria = fn; + fn = noop; + } else if (fn === undefined) { + fn = noop; + } + + if (typeof criteria !== 'number') + throw new TypeError(`Invalid ${field} value: ${criteria}`); + + const context = { + [field]: criteria, + actual: 0, + stack: (new Error()).stack, + name: fn.name || '' + }; + + // add the exit listener only once to avoid listener leak warnings + if (mustCallChecks.length === 0) process.on('exit', runCallChecks); + + mustCallChecks.push(context); + + return function() { + context.actual++; + return fn.apply(this, arguments); + }; +} + +exports.printSkipMessage = function(msg) {} + +// Useful for testing expected internal/error objects +exports.expectsError = function expectsError(fn, settings, exact) { + if (typeof fn !== 'function') { + exact = settings; + settings = fn; + fn = undefined; + } + function innerFn(error) { + if ('type' in settings) { + const type = settings.type; + if (type !== Error && !Error.isPrototypeOf(type)) { + throw new TypeError('`settings.type` must inherit from `Error`'); + } + assert(error instanceof type, + `${error.name} is not instance of ${type.name}`); + let typeName = error.constructor.name; + if (typeName === 'NodeError' && type.name !== 'NodeError') { + typeName = Object.getPrototypeOf(error.constructor).name; + } + assert.strictEqual(typeName, type.name); + } + if ('message' in settings) { + const message = settings.message; + if (typeof message === 'string') { + assert.strictEqual(error.message, message); + } else { + assert(message.test(error.message), + `${error.message} does not match ${message}`); + } + } + if ('name' in settings) { + assert.strictEqual(error.name, settings.name); + } + if (error.constructor.name === 'AssertionError') { + ['generatedMessage', 'actual', 'expected', 'operator'].forEach((key) => { + if (key in settings) { + const actual = error[key]; + const expected = settings[key]; + assert.strictEqual(actual, expected, + `${key}: expected ${expected}, not ${actual}`); + } + }); + } + return true; + } + if (fn) { + assert.throws(fn, innerFn); + return; + } + return exports.mustCall(innerFn, exact); +}; diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-alloc.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-alloc.js new file mode 100644 index 0000000000..5d96e0a490 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-alloc.js @@ -0,0 +1,1044 @@ +'use strict'; +var Buffer = require('../../').Buffer; +const common = require('./common'); +const assert = require('assert'); +const vm = require('vm'); + +const SlowBuffer = require('../../').SlowBuffer; + + +const b = Buffer.allocUnsafe(1024); +assert.strictEqual(1024, b.length); + +b[0] = -1; +assert.strictEqual(b[0], 255); + +for (let i = 0; i < 1024; i++) { + b[i] = i % 256; +} + +for (let i = 0; i < 1024; i++) { + assert.strictEqual(i % 256, b[i]); +} + +const c = Buffer.allocUnsafe(512); +assert.strictEqual(512, c.length); + +const d = Buffer.from([]); +assert.strictEqual(0, d.length); + +// Test offset properties +{ + const b = Buffer.alloc(128); + assert.strictEqual(128, b.length); + assert.strictEqual(0, b.byteOffset); + assert.strictEqual(0, b.offset); +} + +// Test creating a Buffer from a Uint32Array +{ + const ui32 = new Uint32Array(4).fill(42); + const e = Buffer.from(ui32); + for (const [index, value] of e.entries()) { + assert.strictEqual(value, ui32[index]); + } +} +// Test creating a Buffer from a Uint32Array (old constructor) +{ + const ui32 = new Uint32Array(4).fill(42); + const e = Buffer(ui32); + for (const [key, value] of e.entries()) { + assert.deepStrictEqual(value, ui32[key]); + } +} +{ + const sab = new SharedArrayBuffer(Uint8Array.BYTES_PER_ELEMENT * 4); + const ui32 = new Uint8Array(sab).fill(42); + const e = Buffer(sab); + for (const [key, value] of e.entries()) { + assert.deepStrictEqual(value, ui32[key]); + } +} + +// Test invalid encoding for Buffer.toString +assert.throws(() => b.toString('invalid'), + /Unknown encoding: invalid/); +// invalid encoding for Buffer.write +assert.throws(() => b.write('test string', 0, 5, 'invalid'), + /Unknown encoding: invalid/); +// unsupported arguments for Buffer.write +assert.throws(() => b.write('test', 'utf8', 0), + /is no longer supported/); + + +// try to create 0-length buffers +assert.doesNotThrow(() => Buffer.from('')); +assert.doesNotThrow(() => Buffer.from('', 'ascii')); +assert.doesNotThrow(() => Buffer.from('', 'latin1')); +assert.doesNotThrow(() => Buffer.alloc(0)); +assert.doesNotThrow(() => Buffer.allocUnsafe(0)); +assert.doesNotThrow(() => new Buffer('')); +assert.doesNotThrow(() => new Buffer('', 'ascii')); +assert.doesNotThrow(() => new Buffer('', 'latin1')); +assert.doesNotThrow(() => new Buffer('', 'binary')); +assert.doesNotThrow(() => Buffer(0)); +assert.doesNotThrow(() => Buffer.alloc(16, !!true)); + +// try to write a 0-length string beyond the end of b +assert.throws(() => b.write('', 2048), RangeError); + +// throw when writing to negative offset +assert.throws(() => b.write('a', -1), RangeError); + +// throw when writing past bounds from the pool +assert.throws(() => b.write('a', 2048), RangeError); + +// throw when writing to negative offset +assert.throws(() => b.write('a', -1), RangeError); + +// try to copy 0 bytes worth of data into an empty buffer +b.copy(Buffer.alloc(0), 0, 0, 0); + +// try to copy 0 bytes past the end of the target buffer +b.copy(Buffer.alloc(0), 1, 1, 1); +b.copy(Buffer.alloc(1), 1, 1, 1); + +// try to copy 0 bytes from past the end of the source buffer +b.copy(Buffer.alloc(1), 0, 2048, 2048); + +// testing for smart defaults and ability to pass string values as offset +{ + const writeTest = Buffer.from('abcdes'); + writeTest.write('n', 'ascii'); + writeTest.write('o', '1', 'ascii'); + writeTest.write('d', '2', 'ascii'); + writeTest.write('e', 3, 'ascii'); + writeTest.write('j', 4, 'ascii'); + assert.strictEqual(writeTest.toString(), 'nodejs'); +} + +// Offset points to the end of the buffer +// (see https://github.com/nodejs/node/issues/8127). +assert.doesNotThrow(() => Buffer.alloc(1).write('', 1, 0)); + +// ASCII slice test +{ + const asciiString = 'hello world'; + + for (let i = 0; i < asciiString.length; i++) { + b[i] = asciiString.charCodeAt(i); + } + const asciiSlice = b.toString('ascii', 0, asciiString.length); + assert.strictEqual(asciiString, asciiSlice); +} + +{ + const asciiString = 'hello world'; + const offset = 100; + + assert.strictEqual(asciiString.length, b.write(asciiString, offset, 'ascii')); + const asciiSlice = b.toString('ascii', offset, offset + asciiString.length); + assert.strictEqual(asciiString, asciiSlice); +} + +{ + const asciiString = 'hello world'; + const offset = 100; + + const sliceA = b.slice(offset, offset + asciiString.length); + const sliceB = b.slice(offset, offset + asciiString.length); + for (let i = 0; i < asciiString.length; i++) { + assert.strictEqual(sliceA[i], sliceB[i]); + } +} + +// UTF-8 slice test +{ + const utf8String = '¡hέlló wôrld!'; + const offset = 100; + + b.write(utf8String, 0, Buffer.byteLength(utf8String), 'utf8'); + let utf8Slice = b.toString('utf8', 0, Buffer.byteLength(utf8String)); + assert.strictEqual(utf8String, utf8Slice); + + assert.strictEqual(Buffer.byteLength(utf8String), + b.write(utf8String, offset, 'utf8')); + utf8Slice = b.toString('utf8', offset, + offset + Buffer.byteLength(utf8String)); + assert.strictEqual(utf8String, utf8Slice); + + const sliceA = b.slice(offset, offset + Buffer.byteLength(utf8String)); + const sliceB = b.slice(offset, offset + Buffer.byteLength(utf8String)); + for (let i = 0; i < Buffer.byteLength(utf8String); i++) { + assert.strictEqual(sliceA[i], sliceB[i]); + } +} + +{ + const slice = b.slice(100, 150); + assert.strictEqual(50, slice.length); + for (let i = 0; i < 50; i++) { + assert.strictEqual(b[100 + i], slice[i]); + } +} + +{ + // make sure only top level parent propagates from allocPool + const b = Buffer.allocUnsafe(5); + const c = b.slice(0, 4); + const d = c.slice(0, 2); + assert.strictEqual(b.parent, c.parent); + assert.strictEqual(b.parent, d.parent); +} + +{ + // also from a non-pooled instance + const b = Buffer.allocUnsafeSlow(5); + const c = b.slice(0, 4); + const d = c.slice(0, 2); + assert.strictEqual(c.parent, d.parent); +} + +{ + // Bug regression test + const testValue = '\u00F6\u65E5\u672C\u8A9E'; // ö日本語 + const buffer = Buffer.allocUnsafe(32); + const size = buffer.write(testValue, 0, 'utf8'); + const slice = buffer.toString('utf8', 0, size); + assert.strictEqual(slice, testValue); +} + +{ + // Test triple slice + const a = Buffer.allocUnsafe(8); + for (let i = 0; i < 8; i++) a[i] = i; + const b = a.slice(4, 8); + assert.strictEqual(4, b[0]); + assert.strictEqual(5, b[1]); + assert.strictEqual(6, b[2]); + assert.strictEqual(7, b[3]); + const c = b.slice(2, 4); + assert.strictEqual(6, c[0]); + assert.strictEqual(7, c[1]); +} + +{ + const d = Buffer.from([23, 42, 255]); + assert.strictEqual(d.length, 3); + assert.strictEqual(d[0], 23); + assert.strictEqual(d[1], 42); + assert.strictEqual(d[2], 255); + assert.deepStrictEqual(d, Buffer.from(d)); +} + +{ + // Test for proper UTF-8 Encoding + const e = Buffer.from('über'); + assert.deepStrictEqual(e, Buffer.from([195, 188, 98, 101, 114])); +} + +{ + // Test for proper ascii Encoding, length should be 4 + const f = Buffer.from('über', 'ascii'); + assert.deepStrictEqual(f, Buffer.from([252, 98, 101, 114])); +} + +['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach((encoding) => { + { + // Test for proper UTF16LE encoding, length should be 8 + const f = Buffer.from('über', encoding); + assert.deepStrictEqual(f, Buffer.from([252, 0, 98, 0, 101, 0, 114, 0])); + } + + { + // Length should be 12 + const f = Buffer.from('привет', encoding); + assert.deepStrictEqual( + f, Buffer.from([63, 4, 64, 4, 56, 4, 50, 4, 53, 4, 66, 4]) + ); + assert.strictEqual(f.toString(encoding), 'привет'); + } + + { + const f = Buffer.from([0, 0, 0, 0, 0]); + assert.strictEqual(f.length, 5); + const size = f.write('あいうえお', encoding); + assert.strictEqual(size, 4); + assert.deepStrictEqual(f, Buffer.from([0x42, 0x30, 0x44, 0x30, 0x00])); + } +}); + +{ + const f = Buffer.from('\uD83D\uDC4D', 'utf-16le'); // THUMBS UP SIGN (U+1F44D) + assert.strictEqual(f.length, 4); + assert.deepStrictEqual(f, Buffer.from('3DD84DDC', 'hex')); +} + +// Test construction from arrayish object +{ + const arrayIsh = { 0: 0, 1: 1, 2: 2, 3: 3, length: 4 }; + let g = Buffer.from(arrayIsh); + assert.deepStrictEqual(g, Buffer.from([0, 1, 2, 3])); + const strArrayIsh = { 0: '0', 1: '1', 2: '2', 3: '3', length: 4 }; + g = Buffer.from(strArrayIsh); + assert.deepStrictEqual(g, Buffer.from([0, 1, 2, 3])); +} + +// +// Test toString('base64') +// +assert.strictEqual('TWFu', (Buffer.from('Man')).toString('base64')); + +{ + // test that regular and URL-safe base64 both work + const expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff]; + assert.deepStrictEqual(Buffer.from('//++/++/++//', 'base64'), + Buffer.from(expected)); + assert.deepStrictEqual(Buffer.from('__--_--_--__', 'base64'), + Buffer.from(expected)); +} + +{ + // big example + const quote = 'Man is distinguished, not only by his reason, but by this ' + + 'singular passion from other animals, which is a lust ' + + 'of the mind, that by a perseverance of delight in the ' + + 'continued and indefatigable generation of knowledge, ' + + 'exceeds the short vehemence of any carnal pleasure.'; + const expected = 'TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb' + + '24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlci' + + 'BhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQ' + + 'gYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu' + + 'dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZ' + + 'GdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm' + + '5hbCBwbGVhc3VyZS4='; + assert.strictEqual(expected, (Buffer.from(quote)).toString('base64')); + + let b = Buffer.allocUnsafe(1024); + let bytesWritten = b.write(expected, 0, 'base64'); + assert.strictEqual(quote.length, bytesWritten); + assert.strictEqual(quote, b.toString('ascii', 0, quote.length)); + + // check that the base64 decoder ignores whitespace + const expectedWhite = `${expected.slice(0, 60)} \n` + + `${expected.slice(60, 120)} \n` + + `${expected.slice(120, 180)} \n` + + `${expected.slice(180, 240)} \n` + + `${expected.slice(240, 300)}\n` + + `${expected.slice(300, 360)}\n`; + b = Buffer.allocUnsafe(1024); + bytesWritten = b.write(expectedWhite, 0, 'base64'); + assert.strictEqual(quote.length, bytesWritten); + assert.strictEqual(quote, b.toString('ascii', 0, quote.length)); + + // check that the base64 decoder on the constructor works + // even in the presence of whitespace. + b = Buffer.from(expectedWhite, 'base64'); + assert.strictEqual(quote.length, b.length); + assert.strictEqual(quote, b.toString('ascii', 0, quote.length)); + + // check that the base64 decoder ignores illegal chars + const expectedIllegal = expected.slice(0, 60) + ' \x80' + + expected.slice(60, 120) + ' \xff' + + expected.slice(120, 180) + ' \x00' + + expected.slice(180, 240) + ' \x98' + + expected.slice(240, 300) + '\x03' + + expected.slice(300, 360); + b = Buffer.from(expectedIllegal, 'base64'); + assert.strictEqual(quote.length, b.length); + assert.strictEqual(quote, b.toString('ascii', 0, quote.length)); +} + +assert.strictEqual(Buffer.from('', 'base64').toString(), ''); +assert.strictEqual(Buffer.from('K', 'base64').toString(), ''); + +// multiple-of-4 with padding +assert.strictEqual(Buffer.from('Kg==', 'base64').toString(), '*'); +assert.strictEqual(Buffer.from('Kio=', 'base64').toString(), '*'.repeat(2)); +assert.strictEqual(Buffer.from('Kioq', 'base64').toString(), '*'.repeat(3)); +assert.strictEqual(Buffer.from('KioqKg==', 'base64').toString(), '*'.repeat(4)); +assert.strictEqual(Buffer.from('KioqKio=', 'base64').toString(), '*'.repeat(5)); +assert.strictEqual(Buffer.from('KioqKioq', 'base64').toString(), '*'.repeat(6)); +assert.strictEqual(Buffer.from('KioqKioqKg==', 'base64').toString(), + '*'.repeat(7)); +assert.strictEqual(Buffer.from('KioqKioqKio=', 'base64').toString(), + '*'.repeat(8)); +assert.strictEqual(Buffer.from('KioqKioqKioq', 'base64').toString(), + '*'.repeat(9)); +assert.strictEqual(Buffer.from('KioqKioqKioqKg==', 'base64').toString(), + '*'.repeat(10)); +assert.strictEqual(Buffer.from('KioqKioqKioqKio=', 'base64').toString(), + '*'.repeat(11)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioq', 'base64').toString(), + '*'.repeat(12)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKg==', 'base64').toString(), + '*'.repeat(13)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKio=', 'base64').toString(), + '*'.repeat(14)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioq', 'base64').toString(), + '*'.repeat(15)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKg==', 'base64').toString(), + '*'.repeat(16)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKio=', 'base64').toString(), + '*'.repeat(17)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioq', 'base64').toString(), + '*'.repeat(18)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKg==', + 'base64').toString(), + '*'.repeat(19)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKio=', + 'base64').toString(), + '*'.repeat(20)); + +// no padding, not a multiple of 4 +assert.strictEqual(Buffer.from('Kg', 'base64').toString(), '*'); +assert.strictEqual(Buffer.from('Kio', 'base64').toString(), '*'.repeat(2)); +assert.strictEqual(Buffer.from('KioqKg', 'base64').toString(), '*'.repeat(4)); +assert.strictEqual(Buffer.from('KioqKio', 'base64').toString(), '*'.repeat(5)); +assert.strictEqual(Buffer.from('KioqKioqKg', 'base64').toString(), + '*'.repeat(7)); +assert.strictEqual(Buffer.from('KioqKioqKio', 'base64').toString(), + '*'.repeat(8)); +assert.strictEqual(Buffer.from('KioqKioqKioqKg', 'base64').toString(), + '*'.repeat(10)); +assert.strictEqual(Buffer.from('KioqKioqKioqKio', 'base64').toString(), + '*'.repeat(11)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKg', 'base64').toString(), + '*'.repeat(13)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKio', 'base64').toString(), + '*'.repeat(14)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKg', 'base64').toString(), + '*'.repeat(16)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKio', 'base64').toString(), + '*'.repeat(17)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKg', + 'base64').toString(), + '*'.repeat(19)); +assert.strictEqual(Buffer.from('KioqKioqKioqKioqKioqKioqKio', + 'base64').toString(), + '*'.repeat(20)); + +// handle padding graciously, multiple-of-4 or not +assert.strictEqual( + Buffer.from('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw==', 'base64').length, + 32 +); +assert.strictEqual( + Buffer.from('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw=', 'base64').length, + 32 +); +assert.strictEqual( + Buffer.from('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw', 'base64').length, + 32 +); +assert.strictEqual( + Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg==', 'base64').length, + 31 +); +assert.strictEqual( + Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg=', 'base64').length, + 31 +); +assert.strictEqual( + Buffer.from('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg', 'base64').length, + 31 +); + +{ +// This string encodes single '.' character in UTF-16 + const dot = Buffer.from('//4uAA==', 'base64'); + assert.strictEqual(dot[0], 0xff); + assert.strictEqual(dot[1], 0xfe); + assert.strictEqual(dot[2], 0x2e); + assert.strictEqual(dot[3], 0x00); + assert.strictEqual(dot.toString('base64'), '//4uAA=='); +} + +{ + // Writing base64 at a position > 0 should not mangle the result. + // + // https://github.com/joyent/node/issues/402 + const segments = ['TWFkbmVzcz8h', 'IFRoaXM=', 'IGlz', 'IG5vZGUuanMh']; + const b = Buffer.allocUnsafe(64); + let pos = 0; + + for (let i = 0; i < segments.length; ++i) { + pos += b.write(segments[i], pos, 'base64'); + } + assert.strictEqual(b.toString('latin1', 0, pos), + 'Madness?! This is node.js!'); +} + +// Regression test for https://github.com/nodejs/node/issues/3496. +assert.strictEqual(Buffer.from('=bad'.repeat(1e4), 'base64').length, 0); + +// Regression test for https://github.com/nodejs/node/issues/11987. +assert.deepStrictEqual(Buffer.from('w0 ', 'base64'), + Buffer.from('w0', 'base64')); + +// Regression test for https://github.com/nodejs/node/issues/13657. +assert.deepStrictEqual(Buffer.from(' YWJvcnVtLg', 'base64'), + Buffer.from('YWJvcnVtLg', 'base64')); + +{ + // Creating buffers larger than pool size. + const l = Buffer.poolSize + 5; + const s = 'h'.repeat(l); + const b = Buffer.from(s); + + for (let i = 0; i < l; i++) { + assert.strictEqual('h'.charCodeAt(0), b[i]); + } + + const sb = b.toString(); + assert.strictEqual(sb.length, s.length); + assert.strictEqual(sb, s); +} + +{ + // test hex toString + const hexb = Buffer.allocUnsafe(256); + for (let i = 0; i < 256; i++) { + hexb[i] = i; + } + const hexStr = hexb.toString('hex'); + assert.strictEqual(hexStr, + '000102030405060708090a0b0c0d0e0f' + + '101112131415161718191a1b1c1d1e1f' + + '202122232425262728292a2b2c2d2e2f' + + '303132333435363738393a3b3c3d3e3f' + + '404142434445464748494a4b4c4d4e4f' + + '505152535455565758595a5b5c5d5e5f' + + '606162636465666768696a6b6c6d6e6f' + + '707172737475767778797a7b7c7d7e7f' + + '808182838485868788898a8b8c8d8e8f' + + '909192939495969798999a9b9c9d9e9f' + + 'a0a1a2a3a4a5a6a7a8a9aaabacadaeaf' + + 'b0b1b2b3b4b5b6b7b8b9babbbcbdbebf' + + 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf' + + 'd0d1d2d3d4d5d6d7d8d9dadbdcdddedf' + + 'e0e1e2e3e4e5e6e7e8e9eaebecedeeef' + + 'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'); + + const hexb2 = Buffer.from(hexStr, 'hex'); + for (let i = 0; i < 256; i++) { + assert.strictEqual(hexb2[i], hexb[i]); + } +} + +// Test single hex character is discarded. +assert.strictEqual(Buffer.from('A', 'hex').length, 0); + +// Test that if a trailing character is discarded, rest of string is processed. +assert.deepStrictEqual(Buffer.from('Abx', 'hex'), Buffer.from('Ab', 'hex')); + +// Test single base64 char encodes as 0. +assert.strictEqual(Buffer.from('A', 'base64').length, 0); + + +{ + // test an invalid slice end. + const b = Buffer.from([1, 2, 3, 4, 5]); + const b2 = b.toString('hex', 1, 10000); + const b3 = b.toString('hex', 1, 5); + const b4 = b.toString('hex', 1); + assert.strictEqual(b2, b3); + assert.strictEqual(b2, b4); +} + +function buildBuffer(data) { + if (Array.isArray(data)) { + const buffer = Buffer.allocUnsafe(data.length); + data.forEach((v, k) => buffer[k] = v); + return buffer; + } + return null; +} + +const x = buildBuffer([0x81, 0xa3, 0x66, 0x6f, 0x6f, 0xa3, 0x62, 0x61, 0x72]); + +assert.strictEqual('', x.inspect()); + +{ + const z = x.slice(4); + assert.strictEqual(5, z.length); + assert.strictEqual(0x6f, z[0]); + assert.strictEqual(0xa3, z[1]); + assert.strictEqual(0x62, z[2]); + assert.strictEqual(0x61, z[3]); + assert.strictEqual(0x72, z[4]); +} + +{ + const z = x.slice(0); + assert.strictEqual(z.length, x.length); +} + +{ + const z = x.slice(0, 4); + assert.strictEqual(4, z.length); + assert.strictEqual(0x81, z[0]); + assert.strictEqual(0xa3, z[1]); +} + +{ + const z = x.slice(0, 9); + assert.strictEqual(9, z.length); +} + +{ + const z = x.slice(1, 4); + assert.strictEqual(3, z.length); + assert.strictEqual(0xa3, z[0]); +} + +{ + const z = x.slice(2, 4); + assert.strictEqual(2, z.length); + assert.strictEqual(0x66, z[0]); + assert.strictEqual(0x6f, z[1]); +} + +['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach((encoding) => { + const b = Buffer.allocUnsafe(10); + b.write('あいうえお', encoding); + assert.strictEqual(b.toString(encoding), 'あいうえお'); +}); + +['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach((encoding) => { + const b = Buffer.allocUnsafe(11); + b.write('あいうえお', 1, encoding); + assert.strictEqual(b.toString(encoding, 1), 'あいうえお'); +}); + +{ + // latin1 encoding should write only one byte per character. + const b = Buffer.from([0xde, 0xad, 0xbe, 0xef]); + let s = String.fromCharCode(0xffff); + b.write(s, 0, 'latin1'); + assert.strictEqual(0xff, b[0]); + assert.strictEqual(0xad, b[1]); + assert.strictEqual(0xbe, b[2]); + assert.strictEqual(0xef, b[3]); + s = String.fromCharCode(0xaaee); + b.write(s, 0, 'latin1'); + assert.strictEqual(0xee, b[0]); + assert.strictEqual(0xad, b[1]); + assert.strictEqual(0xbe, b[2]); + assert.strictEqual(0xef, b[3]); +} + +{ + // Binary encoding should write only one byte per character. + const b = Buffer.from([0xde, 0xad, 0xbe, 0xef]); + let s = String.fromCharCode(0xffff); + b.write(s, 0, 'latin1'); + assert.strictEqual(0xff, b[0]); + assert.strictEqual(0xad, b[1]); + assert.strictEqual(0xbe, b[2]); + assert.strictEqual(0xef, b[3]); + s = String.fromCharCode(0xaaee); + b.write(s, 0, 'latin1'); + assert.strictEqual(0xee, b[0]); + assert.strictEqual(0xad, b[1]); + assert.strictEqual(0xbe, b[2]); + assert.strictEqual(0xef, b[3]); +} + +{ + // https://github.com/nodejs/node-v0.x-archive/pull/1210 + // Test UTF-8 string includes null character + let buf = Buffer.from('\0'); + assert.strictEqual(buf.length, 1); + buf = Buffer.from('\0\0'); + assert.strictEqual(buf.length, 2); +} + +{ + const buf = Buffer.allocUnsafe(2); + assert.strictEqual(buf.write(''), 0); //0bytes + assert.strictEqual(buf.write('\0'), 1); // 1byte (v8 adds null terminator) + assert.strictEqual(buf.write('a\0'), 2); // 1byte * 2 + assert.strictEqual(buf.write('あ'), 0); // 3bytes + assert.strictEqual(buf.write('\0あ'), 1); // 1byte + 3bytes + assert.strictEqual(buf.write('\0\0あ'), 2); // 1byte * 2 + 3bytes +} + +{ + const buf = Buffer.allocUnsafe(10); + assert.strictEqual(buf.write('あいう'), 9); // 3bytes * 3 (v8 adds null term.) + assert.strictEqual(buf.write('あいう\0'), 10); // 3bytes * 3 + 1byte +} + +{ + // https://github.com/nodejs/node-v0.x-archive/issues/243 + // Test write() with maxLength + const buf = Buffer.allocUnsafe(4); + buf.fill(0xFF); + assert.strictEqual(buf.write('abcd', 1, 2, 'utf8'), 2); + assert.strictEqual(buf[0], 0xFF); + assert.strictEqual(buf[1], 0x61); + assert.strictEqual(buf[2], 0x62); + assert.strictEqual(buf[3], 0xFF); + + buf.fill(0xFF); + assert.strictEqual(buf.write('abcd', 1, 4), 3); + assert.strictEqual(buf[0], 0xFF); + assert.strictEqual(buf[1], 0x61); + assert.strictEqual(buf[2], 0x62); + assert.strictEqual(buf[3], 0x63); + + buf.fill(0xFF); + assert.strictEqual(buf.write('abcd', 1, 2, 'utf8'), 2); + assert.strictEqual(buf[0], 0xFF); + assert.strictEqual(buf[1], 0x61); + assert.strictEqual(buf[2], 0x62); + assert.strictEqual(buf[3], 0xFF); + + buf.fill(0xFF); + assert.strictEqual(buf.write('abcdef', 1, 2, 'hex'), 2); + assert.strictEqual(buf[0], 0xFF); + assert.strictEqual(buf[1], 0xAB); + assert.strictEqual(buf[2], 0xCD); + assert.strictEqual(buf[3], 0xFF); + + ['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach((encoding) => { + buf.fill(0xFF); + assert.strictEqual(buf.write('abcd', 0, 2, encoding), 2); + assert.strictEqual(buf[0], 0x61); + assert.strictEqual(buf[1], 0x00); + assert.strictEqual(buf[2], 0xFF); + assert.strictEqual(buf[3], 0xFF); + }); +} + +{ + // test offset returns are correct + const b = Buffer.allocUnsafe(16); + assert.strictEqual(4, b.writeUInt32LE(0, 0)); + assert.strictEqual(6, b.writeUInt16LE(0, 4)); + assert.strictEqual(7, b.writeUInt8(0, 6)); + assert.strictEqual(8, b.writeInt8(0, 7)); + assert.strictEqual(16, b.writeDoubleLE(0, 8)); +} + +{ + // test unmatched surrogates not producing invalid utf8 output + // ef bf bd = utf-8 representation of unicode replacement character + // see https://codereview.chromium.org/121173009/ + const buf = Buffer.from('ab\ud800cd', 'utf8'); + assert.strictEqual(buf[0], 0x61); + assert.strictEqual(buf[1], 0x62); + assert.strictEqual(buf[2], 0xef); + assert.strictEqual(buf[3], 0xbf); + assert.strictEqual(buf[4], 0xbd); + assert.strictEqual(buf[5], 0x63); + assert.strictEqual(buf[6], 0x64); +} + +{ + // test for buffer overrun + const buf = Buffer.from([0, 0, 0, 0, 0]); // length: 5 + const sub = buf.slice(0, 4); // length: 4 + assert.strictEqual(sub.write('12345', 'latin1'), 4); + assert.strictEqual(buf[4], 0); + assert.strictEqual(sub.write('12345', 'binary'), 4); + assert.strictEqual(buf[4], 0); +} + +{ + // test alloc with fill option + const buf = Buffer.alloc(5, '800A', 'hex'); + assert.strictEqual(buf[0], 128); + assert.strictEqual(buf[1], 10); + assert.strictEqual(buf[2], 128); + assert.strictEqual(buf[3], 10); + assert.strictEqual(buf[4], 128); +} + + +// Check for fractional length args, junk length args, etc. +// https://github.com/joyent/node/issues/1758 + +// Call .fill() first, stops valgrind warning about uninitialized memory reads. +Buffer.allocUnsafe(3.3).fill().toString(); +// throws bad argument error in commit 43cb4ec +Buffer.alloc(3.3).fill().toString(); +assert.strictEqual(Buffer.allocUnsafe(NaN).length, 0); +assert.strictEqual(Buffer.allocUnsafe(3.3).length, 3); +assert.strictEqual(Buffer.from({ length: 3.3 }).length, 3); +assert.strictEqual(Buffer.from({ length: 'BAM' }).length, 0); + +// Make sure that strings are not coerced to numbers. +assert.strictEqual(Buffer.from('99').length, 2); +assert.strictEqual(Buffer.from('13.37').length, 5); + +// Ensure that the length argument is respected. +['ascii', 'utf8', 'hex', 'base64', 'latin1', 'binary'].forEach((enc) => { + assert.strictEqual(Buffer.allocUnsafe(1).write('aaaaaa', 0, 1, enc), 1); +}); + +{ + // Regression test, guard against buffer overrun in the base64 decoder. + const a = Buffer.allocUnsafe(3); + const b = Buffer.from('xxx'); + a.write('aaaaaaaa', 'base64'); + assert.strictEqual(b.toString(), 'xxx'); +} + +// issue GH-3416 +Buffer.from(Buffer.allocUnsafe(0), 0, 0); + +// issue GH-5587 +assert.throws(() => Buffer.alloc(8).writeFloatLE(0, 5), RangeError); +assert.throws(() => Buffer.alloc(16).writeDoubleLE(0, 9), RangeError); + +// attempt to overflow buffers, similar to previous bug in array buffers +assert.throws(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, 0xffffffff), + RangeError); +assert.throws(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, 0xffffffff), + RangeError); + + +// ensure negative values can't get past offset +assert.throws(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, -1), RangeError); +assert.throws(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, -1), RangeError); + + +// test for common write(U)IntLE/BE +{ + let buf = Buffer.allocUnsafe(3); + buf.writeUIntLE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x56, 0x34, 0x12]); + assert.strictEqual(buf.readUIntLE(0, 3), 0x123456); + + buf.fill(0xFF); + buf.writeUIntBE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56]); + assert.strictEqual(buf.readUIntBE(0, 3), 0x123456); + + buf.fill(0xFF); + buf.writeIntLE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x56, 0x34, 0x12]); + assert.strictEqual(buf.readIntLE(0, 3), 0x123456); + + buf.fill(0xFF); + buf.writeIntBE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56]); + assert.strictEqual(buf.readIntBE(0, 3), 0x123456); + + buf.fill(0xFF); + buf.writeIntLE(-0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xaa, 0xcb, 0xed]); + assert.strictEqual(buf.readIntLE(0, 3), -0x123456); + + buf.fill(0xFF); + buf.writeIntBE(-0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcb, 0xaa]); + assert.strictEqual(buf.readIntBE(0, 3), -0x123456); + + buf.fill(0xFF); + buf.writeIntLE(-0x123400, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x00, 0xcc, 0xed]); + assert.strictEqual(buf.readIntLE(0, 3), -0x123400); + + buf.fill(0xFF); + buf.writeIntBE(-0x123400, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcc, 0x00]); + assert.strictEqual(buf.readIntBE(0, 3), -0x123400); + + buf.fill(0xFF); + buf.writeIntLE(-0x120000, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x00, 0x00, 0xee]); + assert.strictEqual(buf.readIntLE(0, 3), -0x120000); + + buf.fill(0xFF); + buf.writeIntBE(-0x120000, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xee, 0x00, 0x00]); + assert.strictEqual(buf.readIntBE(0, 3), -0x120000); + + buf = Buffer.allocUnsafe(5); + buf.writeUIntLE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x90, 0x78, 0x56, 0x34, 0x12]); + assert.strictEqual(buf.readUIntLE(0, 5), 0x1234567890); + + buf.fill(0xFF); + buf.writeUIntBE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56, 0x78, 0x90]); + assert.strictEqual(buf.readUIntBE(0, 5), 0x1234567890); + + buf.fill(0xFF); + buf.writeIntLE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x90, 0x78, 0x56, 0x34, 0x12]); + assert.strictEqual(buf.readIntLE(0, 5), 0x1234567890); + + buf.fill(0xFF); + buf.writeIntBE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56, 0x78, 0x90]); + assert.strictEqual(buf.readIntBE(0, 5), 0x1234567890); + + buf.fill(0xFF); + buf.writeIntLE(-0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x70, 0x87, 0xa9, 0xcb, 0xed]); + assert.strictEqual(buf.readIntLE(0, 5), -0x1234567890); + + buf.fill(0xFF); + buf.writeIntBE(-0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcb, 0xa9, 0x87, 0x70]); + assert.strictEqual(buf.readIntBE(0, 5), -0x1234567890); + + buf.fill(0xFF); + buf.writeIntLE(-0x0012000000, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x00, 0x00, 0x00, 0xee, 0xff]); + assert.strictEqual(buf.readIntLE(0, 5), -0x0012000000); + + buf.fill(0xFF); + buf.writeIntBE(-0x0012000000, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0xff, 0xee, 0x00, 0x00, 0x00]); + assert.strictEqual(buf.readIntBE(0, 5), -0x0012000000); +} + +// Regression test for https://github.com/nodejs/node-v0.x-archive/issues/5482: +// should throw but not assert in C++ land. +common.expectsError( + () => Buffer.from('', 'buffer'), + { + code: 'ERR_UNKNOWN_ENCODING', + type: TypeError, + message: 'Unknown encoding: buffer' + } +); + +// Regression test for https://github.com/nodejs/node-v0.x-archive/issues/6111. +// Constructing a buffer from another buffer should a) work, and b) not corrupt +// the source buffer. +{ + const a = [...Array(128).keys()]; // [0, 1, 2, 3, ... 126, 127] + const b = Buffer.from(a); + const c = Buffer.from(b); + assert.strictEqual(b.length, a.length); + assert.strictEqual(c.length, a.length); + for (let i = 0, k = a.length; i < k; ++i) { + assert.strictEqual(a[i], i); + assert.strictEqual(b[i], i); + assert.strictEqual(c[i], i); + } +} + +if (common.hasCrypto) { // eslint-disable-line crypto-check + // Test truncation after decode + const crypto = require('crypto'); + + const b1 = Buffer.from('YW55=======', 'base64'); + const b2 = Buffer.from('YW55', 'base64'); + + assert.strictEqual( + crypto.createHash('sha1').update(b1).digest('hex'), + crypto.createHash('sha1').update(b2).digest('hex') + ); +} else { + common.printSkipMessage('missing crypto'); +} + +const ps = Buffer.poolSize; +Buffer.poolSize = 0; +assert(Buffer.allocUnsafe(1).parent instanceof ArrayBuffer); +Buffer.poolSize = ps; + +// Test Buffer.copy() segfault +assert.throws(() => Buffer.allocUnsafe(10).copy(), + /TypeError: argument should be a Buffer/); + +const regErrorMsg = + new RegExp('The first argument must be one of type string, Buffer, ' + + 'ArrayBuffer, Array, or Array-like Object\\.'); + +assert.throws(() => Buffer.from(), regErrorMsg); +assert.throws(() => Buffer.from(null), regErrorMsg); + +// Test prototype getters don't throw +assert.strictEqual(Buffer.prototype.parent, undefined); +assert.strictEqual(Buffer.prototype.offset, undefined); +assert.strictEqual(SlowBuffer.prototype.parent, undefined); +assert.strictEqual(SlowBuffer.prototype.offset, undefined); + + +{ + // Test that large negative Buffer length inputs don't affect the pool offset. + // Use the fromArrayLike() variant here because it's more lenient + // about its input and passes the length directly to allocate(). + assert.deepStrictEqual(Buffer.from({ length: -Buffer.poolSize }), + Buffer.from('')); + assert.deepStrictEqual(Buffer.from({ length: -100 }), + Buffer.from('')); + + // Check pool offset after that by trying to write string into the pool. + assert.doesNotThrow(() => Buffer.from('abc')); +} + + +// Test that ParseArrayIndex handles full uint32 +{ + const errMsg = common.expectsError({ + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + type: RangeError, + message: '"offset" is outside of buffer bounds' + }); + assert.throws(() => Buffer.from(new ArrayBuffer(0), -1 >>> 0), errMsg); +} + +// ParseArrayIndex() should reject values that don't fit in a 32 bits size_t. +common.expectsError(() => { + const a = Buffer.alloc(1); + const b = Buffer.alloc(1); + a.copy(b, 0, 0x100000000, 0x100000001); +}, { code: undefined, type: RangeError, message: 'Index out of range' }); + +// Unpooled buffer (replaces SlowBuffer) +{ + const ubuf = Buffer.allocUnsafeSlow(10); + assert(ubuf); + assert(ubuf.buffer); + assert.strictEqual(ubuf.buffer.byteLength, 10); +} + +// Regression test +assert.doesNotThrow(() => Buffer.from(new ArrayBuffer())); + +// Test that ArrayBuffer from a different context is detected correctly +const arrayBuf = vm.runInNewContext('new ArrayBuffer()'); +assert.doesNotThrow(() => Buffer.from(arrayBuf)); +assert.doesNotThrow(() => Buffer.from({ buffer: arrayBuf })); + +assert.throws(() => Buffer.alloc({ valueOf: () => 1 }), + /"size" argument must be of type number/); +assert.throws(() => Buffer.alloc({ valueOf: () => -1 }), + /"size" argument must be of type number/); + +assert.strictEqual(Buffer.prototype.toLocaleString, Buffer.prototype.toString); +{ + const buf = Buffer.from('test'); + assert.strictEqual(buf.toLocaleString(), buf.toString()); +} + +common.expectsError(() => { + Buffer.alloc(0x1000, 'This is not correctly encoded', 'hex'); +}, { + code: 'ERR_INVALID_ARG_VALUE', + type: TypeError +}); + +common.expectsError(() => { + Buffer.alloc(0x1000, 'c', 'hex'); +}, { + code: 'ERR_INVALID_ARG_VALUE', + type: TypeError +}); + +common.expectsError(() => { + Buffer.alloc(1, Buffer.alloc(0)); +}, { + code: 'ERR_INVALID_ARG_VALUE', + type: TypeError +}); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-arraybuffer.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-arraybuffer.js new file mode 100644 index 0000000000..b3b1a48d76 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-arraybuffer.js @@ -0,0 +1,152 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +const common = require('./common'); +const assert = require('assert'); + +const LENGTH = 16; + +const ab = new ArrayBuffer(LENGTH); +const dv = new DataView(ab); +const ui = new Uint8Array(ab); +const buf = Buffer.from(ab); + + +assert.ok(buf instanceof Buffer); +assert.strictEqual(buf.parent, buf.buffer); +assert.strictEqual(buf.buffer, ab); +assert.strictEqual(buf.length, ab.byteLength); + + +buf.fill(0xC); +for (let i = 0; i < LENGTH; i++) { + assert.strictEqual(ui[i], 0xC); + ui[i] = 0xF; + assert.strictEqual(buf[i], 0xF); +} + +buf.writeUInt32LE(0xF00, 0); +buf.writeUInt32BE(0xB47, 4); +buf.writeDoubleLE(3.1415, 8); + +assert.strictEqual(dv.getUint32(0, true), 0xF00); +assert.strictEqual(dv.getUint32(4), 0xB47); +assert.strictEqual(dv.getFloat64(8, true), 3.1415); + + +// Now test protecting users from doing stupid things + +assert.throws(function() { + function AB() { } + Object.setPrototypeOf(AB, ArrayBuffer); + Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype); + Buffer.from(new AB()); +}, TypeError); + +// write{Double,Float}{LE,BE} with noAssert should not crash, cf. #3766 +const b = Buffer.allocUnsafe(1); +b.writeFloatLE(11.11, 0, true); +b.writeFloatBE(11.11, 0, true); +b.writeDoubleLE(11.11, 0, true); +b.writeDoubleBE(11.11, 0, true); + +// Test the byteOffset and length arguments +{ + const ab = new Uint8Array(5); + ab[0] = 1; + ab[1] = 2; + ab[2] = 3; + ab[3] = 4; + ab[4] = 5; + const buf = Buffer.from(ab.buffer, 1, 3); + assert.strictEqual(buf.length, 3); + assert.strictEqual(buf[0], 2); + assert.strictEqual(buf[1], 3); + assert.strictEqual(buf[2], 4); + buf[0] = 9; + assert.strictEqual(ab[1], 9); + + common.expectsError(() => Buffer.from(ab.buffer, 6), { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + type: RangeError, + message: '"offset" is outside of buffer bounds' + }); + common.expectsError(() => Buffer.from(ab.buffer, 3, 6), { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + type: RangeError, + message: '"length" is outside of buffer bounds' + }); +} + +// Test the deprecated Buffer() version also +{ + const ab = new Uint8Array(5); + ab[0] = 1; + ab[1] = 2; + ab[2] = 3; + ab[3] = 4; + ab[4] = 5; + const buf = Buffer(ab.buffer, 1, 3); + assert.strictEqual(buf.length, 3); + assert.strictEqual(buf[0], 2); + assert.strictEqual(buf[1], 3); + assert.strictEqual(buf[2], 4); + buf[0] = 9; + assert.strictEqual(ab[1], 9); + + common.expectsError(() => Buffer(ab.buffer, 6), { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + type: RangeError, + message: '"offset" is outside of buffer bounds' + }); + common.expectsError(() => Buffer(ab.buffer, 3, 6), { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + type: RangeError, + message: '"length" is outside of buffer bounds' + }); +} + +{ + // If byteOffset is not numeric, it defaults to 0. + const ab = new ArrayBuffer(10); + const expected = Buffer.from(ab, 0); + assert.deepStrictEqual(Buffer.from(ab, 'fhqwhgads'), expected); + assert.deepStrictEqual(Buffer.from(ab, NaN), expected); + assert.deepStrictEqual(Buffer.from(ab, {}), expected); + assert.deepStrictEqual(Buffer.from(ab, []), expected); + + // If byteOffset can be converted to a number, it will be. + assert.deepStrictEqual(Buffer.from(ab, [1]), Buffer.from(ab, 1)); + + // If byteOffset is Infinity, throw. + common.expectsError(() => { + Buffer.from(ab, Infinity); + }, { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + type: RangeError, + message: '"offset" is outside of buffer bounds' + }); +} + +{ + // If length is not numeric, it defaults to 0. + const ab = new ArrayBuffer(10); + const expected = Buffer.from(ab, 0, 0); + assert.deepStrictEqual(Buffer.from(ab, 0, 'fhqwhgads'), expected); + assert.deepStrictEqual(Buffer.from(ab, 0, NaN), expected); + assert.deepStrictEqual(Buffer.from(ab, 0, {}), expected); + assert.deepStrictEqual(Buffer.from(ab, 0, []), expected); + + // If length can be converted to a number, it will be. + assert.deepStrictEqual(Buffer.from(ab, 0, [1]), Buffer.from(ab, 0, 1)); + + //If length is Infinity, throw. + common.expectsError(() => { + Buffer.from(ab, 0, Infinity); + }, { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + type: RangeError, + message: '"length" is outside of buffer bounds' + }); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-ascii.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-ascii.js new file mode 100644 index 0000000000..6c462e750d --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-ascii.js @@ -0,0 +1,48 @@ +// Copyright Joyent, Inc. and other Node contributors.var Buffer = require('../../').Buffer; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('./common'); +const assert = require('assert'); + +// ASCII conversion in node.js simply masks off the high bits, +// it doesn't do transliteration. +assert.strictEqual(Buffer.from('hérité').toString('ascii'), 'hC)ritC)'); + +// 71 characters, 78 bytes. The ’ character is a triple-byte sequence. +const input = 'C’est, graphiquement, la réunion d’un accent aigu ' + + 'et d’un accent grave.'; + +const expected = 'Cb\u0000\u0019est, graphiquement, la rC)union ' + + 'db\u0000\u0019un accent aigu et db\u0000\u0019un ' + + 'accent grave.'; + +const buf = Buffer.from(input); + +for (let i = 0; i < expected.length; ++i) { + assert.strictEqual(buf.slice(i).toString('ascii'), expected.slice(i)); + + // Skip remainder of multi-byte sequence. + if (input.charCodeAt(i) > 65535) ++i; + if (input.charCodeAt(i) > 127) ++i; +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-bad-overload.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-bad-overload.js new file mode 100644 index 0000000000..ea2406f40a --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-bad-overload.js @@ -0,0 +1,23 @@ +'use strict'; +var Buffer = require('../../').Buffer; +const common = require('./common'); +const assert = require('assert'); + +assert.doesNotThrow(function() { + Buffer.allocUnsafe(10); +}); + +const err = common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "value" argument must not be of type number. ' + + 'Received type number' +}); +assert.throws(function() { + Buffer.from(10, 'hex'); +}, err); + +assert.doesNotThrow(function() { + Buffer.from('deadbeaf', 'hex'); +}); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-badhex.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-badhex.js new file mode 100644 index 0000000000..a6388e3117 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-badhex.js @@ -0,0 +1,50 @@ +'use strict'; +var Buffer = require('../../').Buffer; +require('./common'); +const assert = require('assert'); + +// Test hex strings and bad hex strings +{ + const buf = Buffer.alloc(4); + assert.strictEqual(buf.length, 4); + assert.deepStrictEqual(buf, new Buffer([0, 0, 0, 0])); + assert.strictEqual(buf.write('abcdxx', 0, 'hex'), 2); + assert.deepStrictEqual(buf, new Buffer([0xab, 0xcd, 0x00, 0x00])); + assert.strictEqual(buf.toString('hex'), 'abcd0000'); + assert.strictEqual(buf.write('abcdef01', 0, 'hex'), 4); + assert.deepStrictEqual(buf, new Buffer([0xab, 0xcd, 0xef, 0x01])); + assert.strictEqual(buf.toString('hex'), 'abcdef01'); + + const copy = Buffer.from(buf.toString('hex'), 'hex'); + assert.strictEqual(buf.toString('hex'), copy.toString('hex')); +} + +{ + const buf = Buffer.alloc(5); + assert.strictEqual(buf.write('abcdxx', 1, 'hex'), 2); + assert.strictEqual(buf.toString('hex'), '00abcd0000'); +} + +{ + const buf = Buffer.alloc(4); + assert.deepStrictEqual(buf, new Buffer([0, 0, 0, 0])); + assert.strictEqual(buf.write('xxabcd', 0, 'hex'), 0); + assert.deepStrictEqual(buf, new Buffer([0, 0, 0, 0])); + assert.strictEqual(buf.write('xxab', 1, 'hex'), 0); + assert.deepStrictEqual(buf, new Buffer([0, 0, 0, 0])); + assert.strictEqual(buf.write('cdxxab', 0, 'hex'), 1); + assert.deepStrictEqual(buf, new Buffer([0xcd, 0, 0, 0])); +} + +{ + const buf = Buffer.alloc(256); + for (let i = 0; i < 256; i++) + buf[i] = i; + + const hex = buf.toString('hex'); + assert.deepStrictEqual(Buffer.from(hex, 'hex'), buf); + + const badHex = `${hex.slice(0, 256)}xx${hex.slice(256, 510)}`; + assert.deepStrictEqual(Buffer.from(badHex, 'hex'), buf.slice(0, 128)); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-bigint64.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-bigint64.js new file mode 100644 index 0000000000..c4d05da6be --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-bigint64.js @@ -0,0 +1,57 @@ +'use strict' +var Buffer = require('../../').Buffer +const assert = require('assert') + +const buf = Buffer.allocUnsafe(8) + +;['LE', 'BE'].forEach(function(endianness) { + // Should allow simple BigInts to be written and read + let val = 123456789n + buf['writeBigInt64' + endianness](val, 0) + let rtn = buf['readBigInt64' + endianness](0) + assert.strictEqual(val, rtn) + + // Should allow INT64_MAX to be written and read + val = 0x7fffffffffffffffn + buf['writeBigInt64' + endianness](val, 0) + rtn = buf['readBigInt64' + endianness](0) + assert.strictEqual(val, rtn) + + // Should read and write a negative signed 64-bit integer + val = -123456789n + buf['writeBigInt64' + endianness](val, 0) + assert.strictEqual(val, buf['readBigInt64' + endianness](0)) + + // Should read and write an unsigned 64-bit integer + val = 123456789n + buf['writeBigUInt64' + endianness](val, 0) + assert.strictEqual(val, buf['readBigUInt64' + endianness](0)) + + // Should throw a RangeError upon INT64_MAX+1 being written + assert.throws(function() { + const val = 0x8000000000000000n + buf['writeBigInt64' + endianness](val, 0) + }, RangeError) + + // Should throw a RangeError upon UINT64_MAX+1 being written + assert.throws(function() { + const val = 0x10000000000000000n + buf['writeBigUInt64' + endianness](val, 0) + }, function(err) { + assert(err instanceof RangeError) + assert(err.code === 'ERR_OUT_OF_RANGE') + assert(err.message === 'The value of "value" is out of range. It must be ' + + '>= 0n and < 2n ** 64n. Received 18_446_744_073_709_551_616n') + return true; + }) + + // Should throw a TypeError upon invalid input + assert.throws(function() { + buf['writeBigInt64' + endianness]('bad', 0) + }, TypeError) + + // Should throw a TypeError upon invalid input + assert.throws(function() { + buf['writeBigUInt64' + endianness]('bad', 0) + }, TypeError) +}) diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-bytelength.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-bytelength.js new file mode 100644 index 0000000000..9028a34972 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-bytelength.js @@ -0,0 +1,125 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +const common = require('./common'); +const assert = require('assert'); +const SlowBuffer = require('../../').SlowBuffer; +const vm = require('vm'); + +[ + [32, 'latin1'], + [NaN, 'utf8'], + [{}, 'latin1'], + [] +].forEach((args) => { + common.expectsError( + () => Buffer.byteLength(...args), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "string" argument must be one of type string, ' + + `Buffer, or ArrayBuffer. Received type ${typeof args[0]}` + } + ); +}); + +assert.strictEqual(Buffer.byteLength('', undefined, true), -1); + +assert(ArrayBuffer.isView(new Buffer(10))); +assert(ArrayBuffer.isView(new SlowBuffer(10))); +assert(ArrayBuffer.isView(Buffer.alloc(10))); +assert(ArrayBuffer.isView(Buffer.allocUnsafe(10))); +assert(ArrayBuffer.isView(Buffer.allocUnsafeSlow(10))); +assert(ArrayBuffer.isView(Buffer.from(''))); + +// buffer +const incomplete = Buffer.from([0xe4, 0xb8, 0xad, 0xe6, 0x96]); +assert.strictEqual(Buffer.byteLength(incomplete), 5); +const ascii = Buffer.from('abc'); +assert.strictEqual(Buffer.byteLength(ascii), 3); + +// ArrayBuffer +const buffer = new ArrayBuffer(8); +assert.strictEqual(Buffer.byteLength(buffer), 8); + +// TypedArray +const int8 = new Int8Array(8); +assert.strictEqual(Buffer.byteLength(int8), 8); +const uint8 = new Uint8Array(8); +assert.strictEqual(Buffer.byteLength(uint8), 8); +const uintc8 = new Uint8ClampedArray(2); +assert.strictEqual(Buffer.byteLength(uintc8), 2); +const int16 = new Int16Array(8); +assert.strictEqual(Buffer.byteLength(int16), 16); +const uint16 = new Uint16Array(8); +assert.strictEqual(Buffer.byteLength(uint16), 16); +const int32 = new Int32Array(8); +assert.strictEqual(Buffer.byteLength(int32), 32); +const uint32 = new Uint32Array(8); +assert.strictEqual(Buffer.byteLength(uint32), 32); +const float32 = new Float32Array(8); +assert.strictEqual(Buffer.byteLength(float32), 32); +const float64 = new Float64Array(8); +assert.strictEqual(Buffer.byteLength(float64), 64); + +// DataView +const dv = new DataView(new ArrayBuffer(2)); +assert.strictEqual(Buffer.byteLength(dv), 2); + +// special case: zero length string +assert.strictEqual(Buffer.byteLength('', 'ascii'), 0); +assert.strictEqual(Buffer.byteLength('', 'HeX'), 0); + +// utf8 +assert.strictEqual(Buffer.byteLength('∑éllö wørl∂!', 'utf-8'), 19); +assert.strictEqual(Buffer.byteLength('κλμνξο', 'utf8'), 12); +assert.strictEqual(Buffer.byteLength('挵挶挷挸挹', 'utf-8'), 15); +assert.strictEqual(Buffer.byteLength('𠝹𠱓𠱸', 'UTF8'), 12); +// without an encoding, utf8 should be assumed +assert.strictEqual(Buffer.byteLength('hey there'), 9); +assert.strictEqual(Buffer.byteLength('𠱸挶νξ#xx :)'), 17); +assert.strictEqual(Buffer.byteLength('hello world', ''), 11); +// it should also be assumed with unrecognized encoding +assert.strictEqual(Buffer.byteLength('hello world', 'abc'), 11); +assert.strictEqual(Buffer.byteLength('ßœ∑≈', 'unkn0wn enc0ding'), 10); + +// base64 +assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ=', 'base64'), 11); +assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ=', 'BASE64'), 11); +assert.strictEqual(Buffer.byteLength('bm9kZS5qcyByb2NrcyE=', 'base64'), 14); +assert.strictEqual(Buffer.byteLength('aGkk', 'base64'), 3); +assert.strictEqual( + Buffer.byteLength('bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw==', 'base64'), 25 +); +// special padding +assert.strictEqual(Buffer.byteLength('aaa=', 'base64'), 2); +assert.strictEqual(Buffer.byteLength('aaaa==', 'base64'), 3); + +assert.strictEqual(Buffer.byteLength('Il était tué'), 14); +assert.strictEqual(Buffer.byteLength('Il était tué', 'utf8'), 14); + +['ascii', 'latin1', 'binary'] + .reduce((es, e) => es.concat(e, e.toUpperCase()), []) + .forEach((encoding) => { + assert.strictEqual(Buffer.byteLength('Il était tué', encoding), 12); + }); + +['ucs2', 'ucs-2', 'utf16le', 'utf-16le'] + .reduce((es, e) => es.concat(e, e.toUpperCase()), []) + .forEach((encoding) => { + assert.strictEqual(Buffer.byteLength('Il était tué', encoding), 24); + }); + +// Test that ArrayBuffer from a different context is detected correctly +const arrayBuf = vm.runInNewContext('new ArrayBuffer()'); +assert.strictEqual(Buffer.byteLength(arrayBuf), 0); + +// Verify that invalid encodings are treated as utf8 +for (let i = 1; i < 10; i++) { + const encoding = String(i).repeat(i); + + assert.ok(!Buffer.isEncoding(encoding)); + assert.strictEqual(Buffer.byteLength('foo', encoding), + Buffer.byteLength('foo', 'utf8')); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-compare-offset.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-compare-offset.js new file mode 100644 index 0000000000..f7a76d34f5 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-compare-offset.js @@ -0,0 +1,76 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +const common = require('./common'); +const assert = require('assert'); + +const a = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); +const b = Buffer.from([5, 6, 7, 8, 9, 0, 1, 2, 3, 4]); + +assert.strictEqual(-1, a.compare(b)); + +// Equivalent to a.compare(b). +assert.strictEqual(-1, a.compare(b, 0)); +assert.strictEqual(-1, a.compare(b, '0')); +assert.strictEqual(-1, a.compare(b, undefined)); + +// Equivalent to a.compare(b). +assert.strictEqual(-1, a.compare(b, 0, undefined, 0)); + +// Zero-length target, return 1 +assert.strictEqual(1, a.compare(b, 0, 0, 0)); +assert.strictEqual(1, a.compare(b, '0', '0', '0')); + +// Equivalent to Buffer.compare(a, b.slice(6, 10)) +assert.strictEqual(1, a.compare(b, 6, 10)); + +// Zero-length source, return -1 +assert.strictEqual(-1, a.compare(b, 6, 10, 0, 0)); + +// Zero-length source and target, return 0 +assert.strictEqual(0, a.compare(b, 0, 0, 0, 0)); +assert.strictEqual(0, a.compare(b, 1, 1, 2, 2)); + +// Equivalent to Buffer.compare(a.slice(4), b.slice(0, 5)) +assert.strictEqual(1, a.compare(b, 0, 5, 4)); + +// Equivalent to Buffer.compare(a.slice(1), b.slice(5)) +assert.strictEqual(1, a.compare(b, 5, undefined, 1)); + +// Equivalent to Buffer.compare(a.slice(2), b.slice(2, 4)) +assert.strictEqual(-1, a.compare(b, 2, 4, 2)); + +// Equivalent to Buffer.compare(a.slice(4), b.slice(0, 7)) +assert.strictEqual(-1, a.compare(b, 0, 7, 4)); + +// Equivalent to Buffer.compare(a.slice(4, 6), b.slice(0, 7)); +assert.strictEqual(-1, a.compare(b, 0, 7, 4, 6)); + +// zero length target +assert.strictEqual(1, a.compare(b, 0, null)); + +// coerces to targetEnd == 5 +assert.strictEqual(-1, a.compare(b, 0, { valueOf: () => 5 })); + +// zero length target +assert.strictEqual(1, a.compare(b, Infinity, -Infinity)); + +// zero length target because default for targetEnd <= targetSource +assert.strictEqual(1, a.compare(b, '0xff')); + +const oor = common.expectsError({ code: 'ERR_INDEX_OUT_OF_RANGE' }, 7); + +assert.throws(() => a.compare(b, 0, 100, 0), oor); +assert.throws(() => a.compare(b, 0, 1, 0, 100), oor); +assert.throws(() => a.compare(b, -1), oor); +assert.throws(() => a.compare(b, 0, '0xff'), oor); +assert.throws(() => a.compare(b, 0, Infinity), oor); +assert.throws(() => a.compare(b, 0, 1, -1), oor); +assert.throws(() => a.compare(b, -Infinity, Infinity), oor); +common.expectsError(() => a.compare(), { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "target" argument must be one of ' + + 'type Buffer or Uint8Array. Received type undefined' +}); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-compare.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-compare.js new file mode 100644 index 0000000000..c5d2872589 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-compare.js @@ -0,0 +1,48 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +const common = require('./common'); +const assert = require('assert'); + +const b = Buffer.alloc(1, 'a'); +const c = Buffer.alloc(1, 'c'); +const d = Buffer.alloc(2, 'aa'); +const e = new Uint8Array([ 0x61, 0x61 ]); // ASCII 'aa', same as d + +assert.strictEqual(b.compare(c), -1); +assert.strictEqual(c.compare(d), 1); +assert.strictEqual(d.compare(b), 1); +assert.strictEqual(d.compare(e), 0); +assert.strictEqual(b.compare(d), -1); +assert.strictEqual(b.compare(b), 0); + +assert.strictEqual(Buffer.compare(b, c), -1); +assert.strictEqual(Buffer.compare(c, d), 1); +assert.strictEqual(Buffer.compare(d, b), 1); +assert.strictEqual(Buffer.compare(b, d), -1); +assert.strictEqual(Buffer.compare(c, c), 0); +assert.strictEqual(Buffer.compare(e, e), 0); +assert.strictEqual(Buffer.compare(d, e), 0); +assert.strictEqual(Buffer.compare(d, b), 1); + +assert.strictEqual(Buffer.compare(Buffer.alloc(0), Buffer.alloc(0)), 0); +assert.strictEqual(Buffer.compare(Buffer.alloc(0), Buffer.alloc(1)), -1); +assert.strictEqual(Buffer.compare(Buffer.alloc(1), Buffer.alloc(0)), 1); + +const errMsg = common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "buf1", "buf2" arguments must be one of ' + + 'type Buffer or Uint8Array' +}, 2); +assert.throws(() => Buffer.compare(Buffer.alloc(1), 'abc'), errMsg); + +assert.throws(() => Buffer.compare('abc', Buffer.alloc(1)), errMsg); + +common.expectsError(() => Buffer.alloc(1).compare('abc'), { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "target" argument must be one of ' + + 'type Buffer or Uint8Array. Received type string' +}); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-concat.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-concat.js new file mode 100644 index 0000000000..c0dc4da095 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-concat.js @@ -0,0 +1,40 @@ +'use strict'; +var Buffer = require('../../').Buffer; + + +var assert = require('assert'); + +var zero = []; +var one = [ Buffer.from('asdf') ]; +var long = []; +for (var i = 0; i < 10; i++) long.push(Buffer.from('asdf')); + +var flatZero = Buffer.concat(zero); +var flatOne = Buffer.concat(one); +var flatLong = Buffer.concat(long); +var flatLongLen = Buffer.concat(long, 40); + +assert(flatZero.length === 0); +assert(flatOne.toString() === 'asdf'); +// A special case where concat used to return the first item, +// if the length is one. This check is to make sure that we don't do that. +assert(flatOne !== one[0]); +assert(flatLong.toString() === (new Array(10 + 1).join('asdf'))); +assert(flatLongLen.toString() === (new Array(10 + 1).join('asdf'))); + +assertWrongList(); +assertWrongList(null); +assertWrongList(Buffer.from('hello')); +assertWrongList([42]); +assertWrongList(['hello', 'world']); +assertWrongList(['hello', Buffer.from('world')]); + +function assertWrongList(value) { + assert.throws(function() { + Buffer.concat(value); + }, function(err) { + return err instanceof TypeError && + err.message === '"list" argument must be an Array of Buffers'; + }); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-failed-alloc-typed-arrays.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-failed-alloc-typed-arrays.js new file mode 100644 index 0000000000..0e01955f54 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-failed-alloc-typed-arrays.js @@ -0,0 +1,35 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +require('./common'); +const assert = require('assert'); +const SlowBuffer = require('../../').SlowBuffer; + +// Test failed or zero-sized Buffer allocations not affecting typed arrays. +// This test exists because of a regression that occurred. Because Buffer +// instances are allocated with the same underlying allocator as TypedArrays, +// but Buffer's can optional be non-zero filled, there was a regression that +// occurred when a Buffer allocated failed, the internal flag specifying +// whether or not to zero-fill was not being reset, causing TypedArrays to +// allocate incorrectly. +const zeroArray = new Uint32Array(10).fill(0); +const sizes = [1e10, 0, 0.1, -1, 'a', undefined, null, NaN]; +const allocators = [ + Buffer, + SlowBuffer, + Buffer.alloc, + Buffer.allocUnsafe, + Buffer.allocUnsafeSlow +]; +for (const allocator of allocators) { + for (const size of sizes) { + try { + // These allocations are known to fail. If they do, + // Uint32Array should still produce a zeroed out result. + allocator(size); + } catch (e) { + assert.deepStrictEqual(new Uint32Array(10), zeroArray); + } + } +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-fill.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-fill.js new file mode 100644 index 0000000000..56d479c28f --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-fill.js @@ -0,0 +1,286 @@ +'use strict'; +var Buffer = require('../../').Buffer; +const common = require('./common'); + + +var assert = require('assert'); +var os = require('os'); +var SIZE = 28; + +var buf1 = Buffer.allocUnsafe(SIZE); +var buf2 = Buffer.allocUnsafe(SIZE); + + +// Default encoding +testBufs('abc'); +testBufs('\u0222aa'); +testBufs('a\u0234b\u0235c\u0236'); +testBufs('abc', 4); +testBufs('abc', 5); +testBufs('abc', SIZE); +testBufs('\u0222aa', 2); +testBufs('\u0222aa', 8); +testBufs('a\u0234b\u0235c\u0236', 4); +testBufs('a\u0234b\u0235c\u0236', 12); +testBufs('abc', 4, -1); +testBufs('abc', 4, 1); +testBufs('abc', 5, 1); +testBufs('\u0222aa', 2, -1); +testBufs('\u0222aa', 8, 1); +testBufs('a\u0234b\u0235c\u0236', 4, -1); +testBufs('a\u0234b\u0235c\u0236', 4, 1); +testBufs('a\u0234b\u0235c\u0236', 12, 1); + + +// UTF8 +testBufs('abc', 'utf8'); +testBufs('\u0222aa', 'utf8'); +testBufs('a\u0234b\u0235c\u0236', 'utf8'); +testBufs('abc', 4, 'utf8'); +testBufs('abc', 5, 'utf8'); +testBufs('abc', SIZE, 'utf8'); +testBufs('\u0222aa', 2, 'utf8'); +testBufs('\u0222aa', 8, 'utf8'); +testBufs('a\u0234b\u0235c\u0236', 4, 'utf8'); +testBufs('a\u0234b\u0235c\u0236', 12, 'utf8'); +testBufs('abc', 4, -1, 'utf8'); +testBufs('abc', 4, 1, 'utf8'); +testBufs('abc', 5, 1, 'utf8'); +testBufs('\u0222aa', 2, -1, 'utf8'); +testBufs('\u0222aa', 8, 1, 'utf8'); +testBufs('a\u0234b\u0235c\u0236', 4, -1, 'utf8'); +testBufs('a\u0234b\u0235c\u0236', 4, 1, 'utf8'); +testBufs('a\u0234b\u0235c\u0236', 12, 1, 'utf8'); +assert.equal(Buffer.allocUnsafe(1).fill(0).fill('\u0222')[0], 0xc8); + + +// BINARY +testBufs('abc', 'binary'); +testBufs('\u0222aa', 'binary'); +testBufs('a\u0234b\u0235c\u0236', 'binary'); +testBufs('abc', 4, 'binary'); +testBufs('abc', 5, 'binary'); +testBufs('abc', SIZE, 'binary'); +testBufs('\u0222aa', 2, 'binary'); +testBufs('\u0222aa', 8, 'binary'); +testBufs('a\u0234b\u0235c\u0236', 4, 'binary'); +testBufs('a\u0234b\u0235c\u0236', 12, 'binary'); +testBufs('abc', 4, -1, 'binary'); +testBufs('abc', 4, 1, 'binary'); +testBufs('abc', 5, 1, 'binary'); +testBufs('\u0222aa', 2, -1, 'binary'); +testBufs('\u0222aa', 8, 1, 'binary'); +testBufs('a\u0234b\u0235c\u0236', 4, -1, 'binary'); +testBufs('a\u0234b\u0235c\u0236', 4, 1, 'binary'); +testBufs('a\u0234b\u0235c\u0236', 12, 1, 'binary'); + + +// LATIN1 +testBufs('abc', 'latin1'); +testBufs('\u0222aa', 'latin1'); +testBufs('a\u0234b\u0235c\u0236', 'latin1'); +testBufs('abc', 4, 'latin1'); +testBufs('abc', 5, 'latin1'); +testBufs('abc', SIZE, 'latin1'); +testBufs('\u0222aa', 2, 'latin1'); +testBufs('\u0222aa', 8, 'latin1'); +testBufs('a\u0234b\u0235c\u0236', 4, 'latin1'); +testBufs('a\u0234b\u0235c\u0236', 12, 'latin1'); +testBufs('abc', 4, -1, 'latin1'); +testBufs('abc', 4, 1, 'latin1'); +testBufs('abc', 5, 1, 'latin1'); +testBufs('\u0222aa', 2, -1, 'latin1'); +testBufs('\u0222aa', 8, 1, 'latin1'); +testBufs('a\u0234b\u0235c\u0236', 4, -1, 'latin1'); +testBufs('a\u0234b\u0235c\u0236', 4, 1, 'latin1'); +testBufs('a\u0234b\u0235c\u0236', 12, 1, 'latin1'); + + +// UCS2 +testBufs('abc', 'ucs2'); +testBufs('\u0222aa', 'ucs2'); +testBufs('a\u0234b\u0235c\u0236', 'ucs2'); +testBufs('abc', 4, 'ucs2'); +testBufs('abc', SIZE, 'ucs2'); +testBufs('\u0222aa', 2, 'ucs2'); +testBufs('\u0222aa', 8, 'ucs2'); +testBufs('a\u0234b\u0235c\u0236', 4, 'ucs2'); +testBufs('a\u0234b\u0235c\u0236', 12, 'ucs2'); +testBufs('abc', 4, -1, 'ucs2'); +testBufs('abc', 4, 1, 'ucs2'); +testBufs('abc', 5, 1, 'ucs2'); +testBufs('\u0222aa', 2, -1, 'ucs2'); +testBufs('\u0222aa', 8, 1, 'ucs2'); +testBufs('a\u0234b\u0235c\u0236', 4, -1, 'ucs2'); +testBufs('a\u0234b\u0235c\u0236', 4, 1, 'ucs2'); +testBufs('a\u0234b\u0235c\u0236', 12, 1, 'ucs2'); +assert.equal(Buffer.allocUnsafe(1).fill('\u0222', 'ucs2')[0], + os.endianness() === 'LE' ? 0x22 : 0x02); + + +// HEX +testBufs('616263', 'hex'); +testBufs('c8a26161', 'hex'); +testBufs('61c8b462c8b563c8b6', 'hex'); +testBufs('616263', 4, 'hex'); +testBufs('616263', 5, 'hex'); +testBufs('616263', SIZE, 'hex'); +testBufs('c8a26161', 2, 'hex'); +testBufs('c8a26161', 8, 'hex'); +testBufs('61c8b462c8b563c8b6', 4, 'hex'); +testBufs('61c8b462c8b563c8b6', 12, 'hex'); +testBufs('616263', 4, -1, 'hex'); +testBufs('616263', 4, 1, 'hex'); +testBufs('616263', 5, 1, 'hex'); +testBufs('c8a26161', 2, -1, 'hex'); +testBufs('c8a26161', 8, 1, 'hex'); +testBufs('61c8b462c8b563c8b6', 4, -1, 'hex'); +testBufs('61c8b462c8b563c8b6', 4, 1, 'hex'); +testBufs('61c8b462c8b563c8b6', 12, 1, 'hex'); + +common.expectsError(() => { + const buf = Buffer.allocUnsafe(SIZE); + + buf.fill('yKJh', 'hex'); +}, { + code: 'ERR_INVALID_ARG_VALUE', + type: TypeError +}); + +common.expectsError(() => { + const buf = Buffer.allocUnsafe(SIZE); + + buf.fill('\u0222', 'hex'); +}, { + code: 'ERR_INVALID_ARG_VALUE', + type: TypeError +}); + +// BASE64 +testBufs('YWJj', 'ucs2'); +testBufs('yKJhYQ==', 'ucs2'); +testBufs('Yci0Ysi1Y8i2', 'ucs2'); +testBufs('YWJj', 4, 'ucs2'); +testBufs('YWJj', SIZE, 'ucs2'); +testBufs('yKJhYQ==', 2, 'ucs2'); +testBufs('yKJhYQ==', 8, 'ucs2'); +testBufs('Yci0Ysi1Y8i2', 4, 'ucs2'); +testBufs('Yci0Ysi1Y8i2', 12, 'ucs2'); +testBufs('YWJj', 4, -1, 'ucs2'); +testBufs('YWJj', 4, 1, 'ucs2'); +testBufs('YWJj', 5, 1, 'ucs2'); +testBufs('yKJhYQ==', 2, -1, 'ucs2'); +testBufs('yKJhYQ==', 8, 1, 'ucs2'); +testBufs('Yci0Ysi1Y8i2', 4, -1, 'ucs2'); +testBufs('Yci0Ysi1Y8i2', 4, 1, 'ucs2'); +testBufs('Yci0Ysi1Y8i2', 12, 1, 'ucs2'); + + +// Buffer +function deepStrictEqualValues(buf, arr) { + for (var [index, value] of buf.entries()) { + assert.deepStrictEqual(value, arr[index]); + } +} + + +var buf2Fill = Buffer.allocUnsafe(1).fill(2); +deepStrictEqualValues(genBuffer(4, [buf2Fill]), [2, 2, 2, 2]); +deepStrictEqualValues(genBuffer(4, [buf2Fill, 1]), [0, 2, 2, 2]); +deepStrictEqualValues(genBuffer(4, [buf2Fill, 1, 3]), [0, 2, 2, 0]); +deepStrictEqualValues(genBuffer(4, [buf2Fill, 1, 1]), [0, 0, 0, 0]); +deepStrictEqualValues(genBuffer(4, [buf2Fill, 1, -1]), [0, 0, 0, 0]); +var hexBufFill = Buffer.allocUnsafe(2).fill(0).fill('0102', 'hex'); +deepStrictEqualValues(genBuffer(4, [hexBufFill]), [1, 2, 1, 2]); +deepStrictEqualValues(genBuffer(4, [hexBufFill, 1]), [0, 1, 2, 1]); +deepStrictEqualValues(genBuffer(4, [hexBufFill, 1, 3]), [0, 1, 2, 0]); +deepStrictEqualValues(genBuffer(4, [hexBufFill, 1, 1]), [0, 0, 0, 0]); +deepStrictEqualValues(genBuffer(4, [hexBufFill, 1, -1]), [0, 0, 0, 0]); + + +// Check exceptions +assert.throws(() => buf1.fill(0, -1)); +assert.throws(() => buf1.fill(0, 0, buf1.length + 1)); +assert.throws(() => buf1.fill('', -1)); +assert.throws(() => buf1.fill('', 0, buf1.length + 1)); +assert.throws(() => buf1.fill('a', 0, buf1.length, 'node rocks!')); +assert.throws(() => buf1.fill('a', 0, 0, NaN)); +assert.throws(() => buf1.fill('a', 0, 0, null)); +assert.throws(() => buf1.fill('a', 0, 0, 'foo')); + + +function genBuffer(size, args) { + var b = Buffer.allocUnsafe(size); + return b.fill(0).fill.apply(b, args); +} + + +function bufReset() { + buf1.fill(0); + buf2.fill(0); +} + + +// This is mostly accurate. Except write() won't write partial bytes to the +// string while fill() blindly copies bytes into memory. To account for that an +// error will be thrown if not all the data can be written, and the SIZE has +// been massaged to work with the input characters. +function writeToFill(string, offset, end, encoding) { + if (typeof offset === 'string') { + encoding = offset; + offset = 0; + end = buf2.length; + } else if (typeof end === 'string') { + encoding = end; + end = buf2.length; + } else if (end === undefined) { + end = buf2.length; + } + + if (offset < 0 || end > buf2.length) + throw new RangeError('Out of range index'); + + if (end <= offset) + return buf2; + + offset >>>= 0; + end >>>= 0; + assert(offset <= buf2.length); + + // Convert "end" to "length" (which write understands). + var length = end - offset < 0 ? 0 : end - offset; + + var wasZero = false; + do { + var written = buf2.write(string, offset, length, encoding); + offset += written; + // Safety check in case write falls into infinite loop. + if (written === 0) { + if (wasZero) + throw new Error('Could not write all data to Buffer'); + else + wasZero = true; + } + } while (offset < buf2.length); + + // Correction for UCS2 operations. + if (os.endianness() === 'BE' && encoding === 'ucs2') { + for (var i = 0; i < buf2.length; i += 2) { + var tmp = buf2[i]; + buf2[i] = buf2[i + 1]; + buf2[i + 1] = tmp; + } + } + + return buf2; +} + + +function testBufs(string, offset, length, encoding) { + bufReset(); + buf1.fill.apply(buf1, arguments); + // Swap bytes on BE archs for ucs2 encoding. + assert.deepStrictEqual(buf1.fill.apply(buf1, arguments), + writeToFill.apply(null, arguments)); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-from.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-from.js new file mode 100644 index 0000000000..6857ba3305 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-from.js @@ -0,0 +1,68 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +const common = require('./common'); +const { deepStrictEqual, throws } = require('assert'); +const { runInNewContext } = require('vm'); + +const checkString = 'test'; + +const check = Buffer.from(checkString); + +class MyString extends String { + constructor() { + super(checkString); + } +} + +class MyPrimitive { + [Symbol.toPrimitive]() { + return checkString; + } +} + +class MyBadPrimitive { + [Symbol.toPrimitive]() { + return 1; + } +} + +deepStrictEqual(Buffer.from(new String(checkString)), check); +deepStrictEqual(Buffer.from(new MyString()), check); +deepStrictEqual(Buffer.from(new MyPrimitive()), check); +deepStrictEqual( + Buffer.from(runInNewContext('new String(checkString)', { checkString })), + check +); + +[ + [{}, 'object'], + [new Boolean(true), 'boolean'], + [{ valueOf() { return null; } }, 'object'], + [{ valueOf() { return undefined; } }, 'object'], + [{ valueOf: null }, 'object'], + [Object.create(null), 'object'] +].forEach(([input, actualType]) => { + const err = common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The first argument must be one of type string, Buffer, ' + + 'ArrayBuffer, Array, or Array-like Object. Received ' + + `type ${actualType}` + }); + throws(() => Buffer.from(input), err); +}); + +[ + new Number(true), + new MyBadPrimitive() +].forEach((input) => { + const errMsg = common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "value" argument must not be of type number. ' + + 'Received type number' + }); + throws(() => Buffer.from(input), errMsg); +}); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-includes.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-includes.js new file mode 100644 index 0000000000..2096a34ba4 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-includes.js @@ -0,0 +1,305 @@ +'use strict'; +var Buffer = require('../../').Buffer; + + +var assert = require('assert'); + +var Buffer = require('../../').Buffer; + +var b = Buffer.from('abcdef'); +var buf_a = Buffer.from('a'); +var buf_bc = Buffer.from('bc'); +var buf_f = Buffer.from('f'); +var buf_z = Buffer.from('z'); +var buf_empty = Buffer.from(''); + +assert(b.includes('a')); +assert(!b.includes('a', 1)); +assert(!b.includes('a', -1)); +assert(!b.includes('a', -4)); +assert(b.includes('a', -b.length)); +assert(b.includes('a', NaN)); +assert(b.includes('a', -Infinity)); +assert(!b.includes('a', Infinity)); +assert(b.includes('bc')); +assert(!b.includes('bc', 2)); +assert(!b.includes('bc', -1)); +assert(!b.includes('bc', -3)); +assert(b.includes('bc', -5)); +assert(b.includes('bc', NaN)); +assert(b.includes('bc', -Infinity)); +assert(!b.includes('bc', Infinity)); +assert(b.includes('f'), b.length - 1); +assert(!b.includes('z')); +assert(!b.includes('')); +assert(!b.includes('', 1)); +assert(!b.includes('', b.length + 1)); +assert(!b.includes('', Infinity)); +assert(b.includes(buf_a)); +assert(!b.includes(buf_a, 1)); +assert(!b.includes(buf_a, -1)); +assert(!b.includes(buf_a, -4)); +assert(b.includes(buf_a, -b.length)); +assert(b.includes(buf_a, NaN)); +assert(b.includes(buf_a, -Infinity)); +assert(!b.includes(buf_a, Infinity)); +assert(b.includes(buf_bc)); +assert(!b.includes(buf_bc, 2)); +assert(!b.includes(buf_bc, -1)); +assert(!b.includes(buf_bc, -3)); +assert(b.includes(buf_bc, -5)); +assert(b.includes(buf_bc, NaN)); +assert(b.includes(buf_bc, -Infinity)); +assert(!b.includes(buf_bc, Infinity)); +assert(b.includes(buf_f), b.length - 1); +assert(!b.includes(buf_z)); +assert(!b.includes(buf_empty)); +assert(!b.includes(buf_empty, 1)); +assert(!b.includes(buf_empty, b.length + 1)); +assert(!b.includes(buf_empty, Infinity)); +assert(b.includes(0x61)); +assert(!b.includes(0x61, 1)); +assert(!b.includes(0x61, -1)); +assert(!b.includes(0x61, -4)); +assert(b.includes(0x61, -b.length)); +assert(b.includes(0x61, NaN)); +assert(b.includes(0x61, -Infinity)); +assert(!b.includes(0x61, Infinity)); +assert(!b.includes(0x0)); + +// test offsets +assert(b.includes('d', 2)); +assert(b.includes('f', 5)); +assert(b.includes('f', -1)); +assert(!b.includes('f', 6)); + +assert(b.includes(Buffer.from('d'), 2)); +assert(b.includes(Buffer.from('f'), 5)); +assert(b.includes(Buffer.from('f'), -1)); +assert(!b.includes(Buffer.from('f'), 6)); + +assert(!Buffer.from('ff').includes(Buffer.from('f'), 1, 'ucs2')); + +// test hex encoding +assert.strictEqual( + Buffer.from(b.toString('hex'), 'hex') + .includes('64', 0, 'hex'), + true +); +assert.strictEqual( + Buffer.from(b.toString('hex'), 'hex') + .includes(Buffer.from('64', 'hex'), 0, 'hex'), + true +); + +// test base64 encoding +assert.strictEqual( + Buffer.from(b.toString('base64'), 'base64') + .includes('ZA==', 0, 'base64'), + true +); +assert.strictEqual( + Buffer.from(b.toString('base64'), 'base64') + .includes(Buffer.from('ZA==', 'base64'), 0, 'base64'), + true +); + +// test ascii encoding +assert.strictEqual( + Buffer.from(b.toString('ascii'), 'ascii') + .includes('d', 0, 'ascii'), + true +); +assert.strictEqual( + Buffer.from(b.toString('ascii'), 'ascii') + .includes(Buffer.from('d', 'ascii'), 0, 'ascii'), + true +); + +// test latin1 encoding +assert.strictEqual( + Buffer.from(b.toString('latin1'), 'latin1') + .includes('d', 0, 'latin1'), + true +); +assert.strictEqual( + Buffer.from(b.toString('latin1'), 'latin1') + .includes(Buffer.from('d', 'latin1'), 0, 'latin1'), + true +); + +// test binary encoding +assert.strictEqual( + Buffer.from(b.toString('binary'), 'binary') + .includes('d', 0, 'binary'), + true +); +assert.strictEqual( + Buffer.from(b.toString('binary'), 'binary') + .includes(Buffer.from('d', 'binary'), 0, 'binary'), + true +); + + +// test usc2 encoding +var twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2'); + +assert(twoByteString.includes('\u0395', 4, 'ucs2')); +assert(twoByteString.includes('\u03a3', -4, 'ucs2')); +assert(twoByteString.includes('\u03a3', -6, 'ucs2')); +assert(twoByteString.includes( + Buffer.from('\u03a3', 'ucs2'), -6, 'ucs2')); +assert(!twoByteString.includes('\u03a3', -2, 'ucs2')); + +var mixedByteStringUcs2 = + Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395', 'ucs2'); +assert(mixedByteStringUcs2.includes('bc', 0, 'ucs2')); +assert(mixedByteStringUcs2.includes('\u03a3', 0, 'ucs2')); +assert(!mixedByteStringUcs2.includes('\u0396', 0, 'ucs2')); + +assert( + 6, mixedByteStringUcs2.includes(Buffer.from('bc', 'ucs2'), 0, 'ucs2')); +assert( + 10, mixedByteStringUcs2.includes(Buffer.from('\u03a3', 'ucs2'), + 0, 'ucs2')); +assert( + -1, mixedByteStringUcs2.includes(Buffer.from('\u0396', 'ucs2'), + 0, 'ucs2')); + +twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2'); + +// Test single char pattern +assert(twoByteString.includes('\u039a', 0, 'ucs2')); +assert(twoByteString.includes('\u0391', 0, 'ucs2'), 'Alpha'); +assert(twoByteString.includes('\u03a3', 0, 'ucs2'), 'First Sigma'); +assert(twoByteString.includes('\u03a3', 6, 'ucs2'), 'Second Sigma'); +assert(twoByteString.includes('\u0395', 0, 'ucs2'), 'Epsilon'); +assert(!twoByteString.includes('\u0392', 0, 'ucs2'), 'Not beta'); + +// Test multi-char pattern +assert(twoByteString.includes('\u039a\u0391', 0, 'ucs2'), 'Lambda Alpha'); +assert(twoByteString.includes('\u0391\u03a3', 0, 'ucs2'), 'Alpha Sigma'); +assert(twoByteString.includes('\u03a3\u03a3', 0, 'ucs2'), 'Sigma Sigma'); +assert(twoByteString.includes('\u03a3\u0395', 0, 'ucs2'), 'Sigma Epsilon'); + +var mixedByteStringUtf8 = Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395'); +assert(mixedByteStringUtf8.includes('bc')); +assert(mixedByteStringUtf8.includes('bc', 5)); +assert(mixedByteStringUtf8.includes('bc', -8)); +assert(mixedByteStringUtf8.includes('\u03a3')); +assert(!mixedByteStringUtf8.includes('\u0396')); + + +// Test complex string includes algorithms. Only trigger for long strings. +// Long string that isn't a simple repeat of a shorter string. +var longString = 'A'; +for (var i = 66; i < 76; i++) { // from 'B' to 'K' + longString = longString + String.fromCharCode(i) + longString; +} + +var longBufferString = Buffer.from(longString); + +// pattern of 15 chars, repeated every 16 chars in long +var pattern = 'ABACABADABACABA'; +for (var i = 0; i < longBufferString.length - pattern.length; i += 7) { + var includes = longBufferString.includes(pattern, i); + assert(includes, 'Long ABACABA...-string at index ' + i); +} +assert(longBufferString.includes('AJABACA'), 'Long AJABACA, First J'); +assert(longBufferString.includes('AJABACA', 511), 'Long AJABACA, Second J'); + +pattern = 'JABACABADABACABA'; +assert(longBufferString.includes(pattern), 'Long JABACABA..., First J'); +assert(longBufferString.includes(pattern, 512), 'Long JABACABA..., Second J'); + +// Search for a non-ASCII string in a pure ASCII string. +var asciiString = Buffer.from( + 'arglebargleglopglyfarglebargleglopglyfarglebargleglopglyf'); +assert(!asciiString.includes('\x2061')); +assert(asciiString.includes('leb', 0)); + +// Search in string containing many non-ASCII chars. +var allCodePoints = []; +for (var i = 0; i < 65536; i++) allCodePoints[i] = i; +var allCharsString = String.fromCharCode.apply(String, allCodePoints); +var allCharsBufferUtf8 = Buffer.from(allCharsString); +var allCharsBufferUcs2 = Buffer.from(allCharsString, 'ucs2'); + +// Search for string long enough to trigger complex search with ASCII pattern +// and UC16 subject. +assert(!allCharsBufferUtf8.includes('notfound')); +assert(!allCharsBufferUcs2.includes('notfound')); + +// Find substrings in Utf8. +var lengths = [1, 3, 15]; // Single char, simple and complex. +var indices = [0x5, 0x60, 0x400, 0x680, 0x7ee, 0xFF02, 0x16610, 0x2f77b]; +for (var lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) { + for (var i = 0; i < indices.length; i++) { + var index = indices[i]; + var length = lengths[lengthIndex]; + + if (index + length > 0x7F) { + length = 2 * length; + } + + if (index + length > 0x7FF) { + length = 3 * length; + } + + if (index + length > 0xFFFF) { + length = 4 * length; + } + + var patternBufferUtf8 = allCharsBufferUtf8.slice(index, index + length); + assert(index, allCharsBufferUtf8.includes(patternBufferUtf8)); + + var patternStringUtf8 = patternBufferUtf8.toString(); + assert(index, allCharsBufferUtf8.includes(patternStringUtf8)); + } +} + +// Find substrings in Usc2. +lengths = [2, 4, 16]; // Single char, simple and complex. +indices = [0x5, 0x65, 0x105, 0x205, 0x285, 0x2005, 0x2085, 0xfff0]; +for (var lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) { + for (var i = 0; i < indices.length; i++) { + var index = indices[i] * 2; + var length = lengths[lengthIndex]; + + var patternBufferUcs2 = + allCharsBufferUcs2.slice(index, index + length); + assert( + index, allCharsBufferUcs2.includes(patternBufferUcs2, 0, 'ucs2')); + + var patternStringUcs2 = patternBufferUcs2.toString('ucs2'); + assert( + index, allCharsBufferUcs2.includes(patternStringUcs2, 0, 'ucs2')); + } +} + +assert.throws(function() { + b.includes(function() { }); +}); +assert.throws(function() { + b.includes({}); +}); +assert.throws(function() { + b.includes([]); +}); + +// test truncation of Number arguments to uint8 +{ + var buf = Buffer.from('this is a test'); + assert.ok(buf.includes(0x6973)); + assert.ok(buf.includes(0x697320)); + assert.ok(buf.includes(0x69732069)); + assert.ok(buf.includes(0x697374657374)); + assert.ok(buf.includes(0x69737374)); + assert.ok(buf.includes(0x69737465)); + assert.ok(buf.includes(0x69737465)); + assert.ok(buf.includes(-140)); + assert.ok(buf.includes(-152)); + assert.ok(!buf.includes(0xff)); + assert.ok(!buf.includes(0xffff)); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-indexof.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-indexof.js new file mode 100644 index 0000000000..dcf150bdbc --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-indexof.js @@ -0,0 +1,523 @@ +'use strict'; +var Buffer = require('../../').Buffer; + + +var assert = require('assert'); + +var Buffer = require('../../').Buffer; + +var b = Buffer.from('abcdef'); +var buf_a = Buffer.from('a'); +var buf_bc = Buffer.from('bc'); +var buf_f = Buffer.from('f'); +var buf_z = Buffer.from('z'); +var buf_empty = Buffer.from(''); + +assert.equal(b.indexOf('a'), 0); +assert.equal(b.indexOf('a', 1), -1); +assert.equal(b.indexOf('a', -1), -1); +assert.equal(b.indexOf('a', -4), -1); +assert.equal(b.indexOf('a', -b.length), 0); +assert.equal(b.indexOf('a', NaN), 0); +assert.equal(b.indexOf('a', -Infinity), 0); +assert.equal(b.indexOf('a', Infinity), -1); +assert.equal(b.indexOf('bc'), 1); +assert.equal(b.indexOf('bc', 2), -1); +assert.equal(b.indexOf('bc', -1), -1); +assert.equal(b.indexOf('bc', -3), -1); +assert.equal(b.indexOf('bc', -5), 1); +assert.equal(b.indexOf('bc', NaN), 1); +assert.equal(b.indexOf('bc', -Infinity), 1); +assert.equal(b.indexOf('bc', Infinity), -1); +assert.equal(b.indexOf('f'), b.length - 1); +assert.equal(b.indexOf('z'), -1); +assert.equal(b.indexOf(''), -1); +assert.equal(b.indexOf('', 1), -1); +assert.equal(b.indexOf('', b.length + 1), -1); +assert.equal(b.indexOf('', Infinity), -1); +assert.equal(b.indexOf(buf_a), 0); +assert.equal(b.indexOf(buf_a, 1), -1); +assert.equal(b.indexOf(buf_a, -1), -1); +assert.equal(b.indexOf(buf_a, -4), -1); +assert.equal(b.indexOf(buf_a, -b.length), 0); +assert.equal(b.indexOf(buf_a, NaN), 0); +assert.equal(b.indexOf(buf_a, -Infinity), 0); +assert.equal(b.indexOf(buf_a, Infinity), -1); +assert.equal(b.indexOf(buf_bc), 1); +assert.equal(b.indexOf(buf_bc, 2), -1); +assert.equal(b.indexOf(buf_bc, -1), -1); +assert.equal(b.indexOf(buf_bc, -3), -1); +assert.equal(b.indexOf(buf_bc, -5), 1); +assert.equal(b.indexOf(buf_bc, NaN), 1); +assert.equal(b.indexOf(buf_bc, -Infinity), 1); +assert.equal(b.indexOf(buf_bc, Infinity), -1); +assert.equal(b.indexOf(buf_f), b.length - 1); +assert.equal(b.indexOf(buf_z), -1); +assert.equal(b.indexOf(buf_empty), -1); +assert.equal(b.indexOf(buf_empty, 1), -1); +assert.equal(b.indexOf(buf_empty, b.length + 1), -1); +assert.equal(b.indexOf(buf_empty, Infinity), -1); +assert.equal(b.indexOf(0x61), 0); +assert.equal(b.indexOf(0x61, 1), -1); +assert.equal(b.indexOf(0x61, -1), -1); +assert.equal(b.indexOf(0x61, -4), -1); +assert.equal(b.indexOf(0x61, -b.length), 0); +assert.equal(b.indexOf(0x61, NaN), 0); +assert.equal(b.indexOf(0x61, -Infinity), 0); +assert.equal(b.indexOf(0x61, Infinity), -1); +assert.equal(b.indexOf(0x0), -1); + +// test offsets +assert.equal(b.indexOf('d', 2), 3); +assert.equal(b.indexOf('f', 5), 5); +assert.equal(b.indexOf('f', -1), 5); +assert.equal(b.indexOf('f', 6), -1); + +assert.equal(b.indexOf(Buffer.from('d'), 2), 3); +assert.equal(b.indexOf(Buffer.from('f'), 5), 5); +assert.equal(b.indexOf(Buffer.from('f'), -1), 5); +assert.equal(b.indexOf(Buffer.from('f'), 6), -1); + +assert.equal(Buffer.from('ff').indexOf(Buffer.from('f'), 1, 'ucs2'), -1); + +// test hex encoding +assert.strictEqual( + Buffer.from(b.toString('hex'), 'hex') + .indexOf('64', 0, 'hex'), + 3 +); +assert.strictEqual( + Buffer.from(b.toString('hex'), 'hex') + .indexOf(Buffer.from('64', 'hex'), 0, 'hex'), + 3 +); + +// test base64 encoding +assert.strictEqual( + Buffer.from(b.toString('base64'), 'base64') + .indexOf('ZA==', 0, 'base64'), + 3 +); +assert.strictEqual( + Buffer.from(b.toString('base64'), 'base64') + .indexOf(Buffer.from('ZA==', 'base64'), 0, 'base64'), + 3 +); + +// test ascii encoding +assert.strictEqual( + Buffer.from(b.toString('ascii'), 'ascii') + .indexOf('d', 0, 'ascii'), + 3 +); +assert.strictEqual( + Buffer.from(b.toString('ascii'), 'ascii') + .indexOf(Buffer.from('d', 'ascii'), 0, 'ascii'), + 3 +); + +// test latin1 encoding +assert.strictEqual( + Buffer.from(b.toString('latin1'), 'latin1') + .indexOf('d', 0, 'latin1'), + 3 +); +assert.strictEqual( + Buffer.from(b.toString('latin1'), 'latin1') + .indexOf(Buffer.from('d', 'latin1'), 0, 'latin1'), + 3 +); +assert.strictEqual( + Buffer.from('aa\u00e8aa', 'latin1') + .indexOf('\u00e8', 'latin1'), + 2 +); +assert.strictEqual( + Buffer.from('\u00e8', 'latin1') + .indexOf('\u00e8', 'latin1'), + 0 +); +assert.strictEqual( + Buffer.from('\u00e8', 'latin1') + .indexOf(Buffer.from('\u00e8', 'latin1'), 'latin1'), + 0 +); + +// test binary encoding +assert.strictEqual( + Buffer.from(b.toString('binary'), 'binary') + .indexOf('d', 0, 'binary'), + 3 +); +assert.strictEqual( + Buffer.from(b.toString('binary'), 'binary') + .indexOf(Buffer.from('d', 'binary'), 0, 'binary'), + 3 +); +assert.strictEqual( + Buffer.from('aa\u00e8aa', 'binary') + .indexOf('\u00e8', 'binary'), + 2 +); +assert.strictEqual( + Buffer.from('\u00e8', 'binary') + .indexOf('\u00e8', 'binary'), + 0 +); +assert.strictEqual( + Buffer.from('\u00e8', 'binary') + .indexOf(Buffer.from('\u00e8', 'binary'), 'binary'), + 0 +); + + +// test optional offset with passed encoding +assert.equal(Buffer.from('aaaa0').indexOf('30', 'hex'), 4); +assert.equal(Buffer.from('aaaa00a').indexOf('3030', 'hex'), 4); + +{ + // test usc2 encoding + var twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2'); + + assert.equal(8, twoByteString.indexOf('\u0395', 4, 'ucs2')); + assert.equal(6, twoByteString.indexOf('\u03a3', -4, 'ucs2')); + assert.equal(4, twoByteString.indexOf('\u03a3', -6, 'ucs2')); + assert.equal(4, twoByteString.indexOf( + Buffer.from('\u03a3', 'ucs2'), -6, 'ucs2')); + assert.equal(-1, twoByteString.indexOf('\u03a3', -2, 'ucs2')); +} + +var mixedByteStringUcs2 = + Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395', 'ucs2'); +assert.equal(6, mixedByteStringUcs2.indexOf('bc', 0, 'ucs2')); +assert.equal(10, mixedByteStringUcs2.indexOf('\u03a3', 0, 'ucs2')); +assert.equal(-1, mixedByteStringUcs2.indexOf('\u0396', 0, 'ucs2')); + +assert.equal( + 6, mixedByteStringUcs2.indexOf(Buffer.from('bc', 'ucs2'), 0, 'ucs2')); +assert.equal( + 10, mixedByteStringUcs2.indexOf(Buffer.from('\u03a3', 'ucs2'), 0, 'ucs2')); +assert.equal( + -1, mixedByteStringUcs2.indexOf(Buffer.from('\u0396', 'ucs2'), 0, 'ucs2')); + +{ + var twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2'); + + // Test single char pattern + assert.equal(0, twoByteString.indexOf('\u039a', 0, 'ucs2')); + assert.equal(2, twoByteString.indexOf('\u0391', 0, 'ucs2'), 'Alpha'); + assert.equal(4, twoByteString.indexOf('\u03a3', 0, 'ucs2'), 'First Sigma'); + assert.equal(6, twoByteString.indexOf('\u03a3', 6, 'ucs2'), 'Second Sigma'); + assert.equal(8, twoByteString.indexOf('\u0395', 0, 'ucs2'), 'Epsilon'); + assert.equal(-1, twoByteString.indexOf('\u0392', 0, 'ucs2'), 'Not beta'); + + // Test multi-char pattern + assert.equal( + 0, twoByteString.indexOf('\u039a\u0391', 0, 'ucs2'), 'Lambda Alpha'); + assert.equal( + 2, twoByteString.indexOf('\u0391\u03a3', 0, 'ucs2'), 'Alpha Sigma'); + assert.equal( + 4, twoByteString.indexOf('\u03a3\u03a3', 0, 'ucs2'), 'Sigma Sigma'); + assert.equal( + 6, twoByteString.indexOf('\u03a3\u0395', 0, 'ucs2'), 'Sigma Epsilon'); +} + +var mixedByteStringUtf8 = Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395'); +assert.equal(5, mixedByteStringUtf8.indexOf('bc')); +assert.equal(5, mixedByteStringUtf8.indexOf('bc', 5)); +assert.equal(5, mixedByteStringUtf8.indexOf('bc', -8)); +assert.equal(7, mixedByteStringUtf8.indexOf('\u03a3')); +assert.equal(-1, mixedByteStringUtf8.indexOf('\u0396')); + + +// Test complex string indexOf algorithms. Only trigger for long strings. +// Long string that isn't a simple repeat of a shorter string. +var longString = 'A'; +for (var i = 66; i < 76; i++) { // from 'B' to 'K' + longString = longString + String.fromCharCode(i) + longString; +} + +var longBufferString = Buffer.from(longString); + +// pattern of 15 chars, repeated every 16 chars in long +var pattern = 'ABACABADABACABA'; +for (var i = 0; i < longBufferString.length - pattern.length; i += 7) { + var index = longBufferString.indexOf(pattern, i); + assert.equal((i + 15) & ~0xf, index, 'Long ABACABA...-string at index ' + i); +} +assert.equal(510, longBufferString.indexOf('AJABACA'), 'Long AJABACA, First J'); +assert.equal( + 1534, longBufferString.indexOf('AJABACA', 511), 'Long AJABACA, Second J'); + +pattern = 'JABACABADABACABA'; +assert.equal( + 511, longBufferString.indexOf(pattern), 'Long JABACABA..., First J'); +assert.equal( + 1535, longBufferString.indexOf(pattern, 512), 'Long JABACABA..., Second J'); + +// Search for a non-ASCII string in a pure ASCII string. +var asciiString = Buffer.from( + 'arglebargleglopglyfarglebargleglopglyfarglebargleglopglyf'); +assert.equal(-1, asciiString.indexOf('\x2061')); +assert.equal(3, asciiString.indexOf('leb', 0)); + +// Search in string containing many non-ASCII chars. +var allCodePoints = []; +for (var i = 0; i < 65536; i++) allCodePoints[i] = i; +var allCharsString = String.fromCharCode.apply(String, allCodePoints); +var allCharsBufferUtf8 = Buffer.from(allCharsString); +var allCharsBufferUcs2 = Buffer.from(allCharsString, 'ucs2'); + +// Search for string long enough to trigger complex search with ASCII pattern +// and UC16 subject. +assert.equal(-1, allCharsBufferUtf8.indexOf('notfound')); +assert.equal(-1, allCharsBufferUcs2.indexOf('notfound')); + +// Needle is longer than haystack, but only because it's encoded as UTF-16 +assert.strictEqual(Buffer.from('aaaa').indexOf('a'.repeat(4), 'ucs2'), -1); + +assert.strictEqual(Buffer.from('aaaa').indexOf('a'.repeat(4), 'utf8'), 0); +assert.strictEqual(Buffer.from('aaaa').indexOf('你好', 'ucs2'), -1); + +// Haystack has odd length, but the needle is UCS2. +// assert.strictEqual(Buffer.from('aaaaa').indexOf('b', 'ucs2'), -1); + +{ + // Find substrings in Utf8. + var lengths = [1, 3, 15]; // Single char, simple and complex. + var indices = [0x5, 0x60, 0x400, 0x680, 0x7ee, 0xFF02, 0x16610, 0x2f77b]; + for (var lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) { + for (var i = 0; i < indices.length; i++) { + var index = indices[i]; + var length = lengths[lengthIndex]; + + if (index + length > 0x7F) { + length = 2 * length; + } + + if (index + length > 0x7FF) { + length = 3 * length; + } + + if (index + length > 0xFFFF) { + length = 4 * length; + } + + var patternBufferUtf8 = allCharsBufferUtf8.slice(index, index + length); + assert.equal(index, allCharsBufferUtf8.indexOf(patternBufferUtf8)); + + var patternStringUtf8 = patternBufferUtf8.toString(); + assert.equal(index, allCharsBufferUtf8.indexOf(patternStringUtf8)); + } + } +} + +{ + // Find substrings in Usc2. + var lengths = [2, 4, 16]; // Single char, simple and complex. + var indices = [0x5, 0x65, 0x105, 0x205, 0x285, 0x2005, 0x2085, 0xfff0]; + for (var lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) { + for (var i = 0; i < indices.length; i++) { + var index = indices[i] * 2; + var length = lengths[lengthIndex]; + + var patternBufferUcs2 = + allCharsBufferUcs2.slice(index, index + length); + assert.equal( + index, allCharsBufferUcs2.indexOf(patternBufferUcs2, 0, 'ucs2')); + + var patternStringUcs2 = patternBufferUcs2.toString('ucs2'); + assert.equal( + index, allCharsBufferUcs2.indexOf(patternStringUcs2, 0, 'ucs2')); + } + } +} + +assert.throws(function() { + b.indexOf(function() { }); +}); +assert.throws(function() { + b.indexOf({}); +}); +assert.throws(function() { + b.indexOf([]); +}); + +// All code for handling encodings is shared between Buffer.indexOf and +// Buffer.lastIndexOf, so only testing the separate lastIndexOf semantics. + +// Test lastIndexOf basic functionality; Buffer b contains 'abcdef'. +// lastIndexOf string: +assert.equal(b.lastIndexOf('a'), 0); +assert.equal(b.lastIndexOf('a', 1), 0); +assert.equal(b.lastIndexOf('b', 1), 1); +assert.equal(b.lastIndexOf('c', 1), -1); +assert.equal(b.lastIndexOf('a', -1), 0); +assert.equal(b.lastIndexOf('a', -4), 0); +assert.equal(b.lastIndexOf('a', -b.length), 0); +assert.equal(b.lastIndexOf('a', -b.length - 1), -1); +assert.equal(b.lastIndexOf('a', NaN), 0); +assert.equal(b.lastIndexOf('a', -Infinity), -1); +assert.equal(b.lastIndexOf('a', Infinity), 0); +// lastIndexOf Buffer: +assert.equal(b.lastIndexOf(buf_a), 0); +assert.equal(b.lastIndexOf(buf_a, 1), 0); +assert.equal(b.lastIndexOf(buf_a, -1), 0); +assert.equal(b.lastIndexOf(buf_a, -4), 0); +assert.equal(b.lastIndexOf(buf_a, -b.length), 0); +assert.equal(b.lastIndexOf(buf_a, -b.length - 1), -1); +assert.equal(b.lastIndexOf(buf_a, NaN), 0); +assert.equal(b.lastIndexOf(buf_a, -Infinity), -1); +assert.equal(b.lastIndexOf(buf_a, Infinity), 0); +assert.equal(b.lastIndexOf(buf_bc), 1); +assert.equal(b.lastIndexOf(buf_bc, 2), 1); +assert.equal(b.lastIndexOf(buf_bc, -1), 1); +assert.equal(b.lastIndexOf(buf_bc, -3), 1); +assert.equal(b.lastIndexOf(buf_bc, -5), 1); +assert.equal(b.lastIndexOf(buf_bc, -6), -1); +assert.equal(b.lastIndexOf(buf_bc, NaN), 1); +assert.equal(b.lastIndexOf(buf_bc, -Infinity), -1); +assert.equal(b.lastIndexOf(buf_bc, Infinity), 1); +assert.equal(b.lastIndexOf(buf_f), b.length - 1); +assert.equal(b.lastIndexOf(buf_z), -1); +assert.equal(b.lastIndexOf(buf_empty), -1); +assert.equal(b.lastIndexOf(buf_empty, 1), -1); +assert.equal(b.lastIndexOf(buf_empty, b.length + 1), -1); +assert.equal(b.lastIndexOf(buf_empty, Infinity), -1); +// lastIndexOf number: +assert.equal(b.lastIndexOf(0x61), 0); +assert.equal(b.lastIndexOf(0x61, 1), 0); +assert.equal(b.lastIndexOf(0x61, -1), 0); +assert.equal(b.lastIndexOf(0x61, -4), 0); +assert.equal(b.lastIndexOf(0x61, -b.length), 0); +assert.equal(b.lastIndexOf(0x61, -b.length - 1), -1); +assert.equal(b.lastIndexOf(0x61, NaN), 0); +assert.equal(b.lastIndexOf(0x61, -Infinity), -1); +assert.equal(b.lastIndexOf(0x61, Infinity), 0); +assert.equal(b.lastIndexOf(0x0), -1); + +// Test weird offset arguments. +// Behaviour should match String.lastIndexOf: +assert.equal(b.lastIndexOf('b', 0), -1); +assert.equal(b.lastIndexOf('b', undefined), 1); +assert.equal(b.lastIndexOf('b', null), -1); +assert.equal(b.lastIndexOf('b', {}), 1); +assert.equal(b.lastIndexOf('b', []), -1); +assert.equal(b.lastIndexOf('b', [2]), 1); + +// Test needles longer than the haystack. +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'ucs2'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'utf8'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'latin1'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'binary'), -1); +assert.strictEqual(b.lastIndexOf(Buffer.from('aaaaaaaaaaaaaaa')), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 2, 'ucs2'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 3, 'utf8'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 5, 'latin1'), -1); +assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 5, 'binary'), -1); +assert.strictEqual(b.lastIndexOf(Buffer.from('aaaaaaaaaaaaaaa'), 7), -1); + +// 你好 expands to a total of 6 bytes using UTF-8 and 4 bytes using UTF-16 +assert.strictEqual(buf_bc.lastIndexOf('你好', 'ucs2'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 'utf8'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 'latin1'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 'binary'), -1); +assert.strictEqual(buf_bc.lastIndexOf(Buffer.from('你好')), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 2, 'ucs2'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 3, 'utf8'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 5, 'latin1'), -1); +assert.strictEqual(buf_bc.lastIndexOf('你好', 5, 'binary'), -1); +assert.strictEqual(buf_bc.lastIndexOf(Buffer.from('你好'), 7), -1); + +// Test lastIndexOf on a longer buffer: +var bufferString = new Buffer('a man a plan a canal panama'); +assert.equal(15, bufferString.lastIndexOf('canal')); +assert.equal(21, bufferString.lastIndexOf('panama')); +assert.equal(0, bufferString.lastIndexOf('a man a plan a canal panama')); +assert.equal(-1, bufferString.lastIndexOf('a man a plan a canal mexico')); +assert.equal(-1, bufferString.lastIndexOf('a man a plan a canal mexico city')); +assert.equal(-1, bufferString.lastIndexOf(Buffer.from('a'.repeat(1000)))); +assert.equal(0, bufferString.lastIndexOf('a man a plan', 4)); +assert.equal(13, bufferString.lastIndexOf('a ')); +assert.equal(13, bufferString.lastIndexOf('a ', 13)); +assert.equal(6, bufferString.lastIndexOf('a ', 12)); +assert.equal(0, bufferString.lastIndexOf('a ', 5)); +assert.equal(13, bufferString.lastIndexOf('a ', -1)); +assert.equal(0, bufferString.lastIndexOf('a ', -27)); +assert.equal(-1, bufferString.lastIndexOf('a ', -28)); + +// Test lastIndexOf for the case that the first character can be found, +// but in a part of the buffer that does not make search to search +// due do length constraints. +var abInUCS2 = Buffer.from('ab', 'ucs2'); +assert.strictEqual(-1, Buffer.from('µaaaa¶bbbb', 'latin1').lastIndexOf('µ')); +assert.strictEqual(-1, Buffer.from('µaaaa¶bbbb', 'binary').lastIndexOf('µ')); +assert.strictEqual(-1, Buffer.from('bc').lastIndexOf('ab')); +assert.strictEqual(-1, Buffer.from('abc').lastIndexOf('qa')); +assert.strictEqual(-1, Buffer.from('abcdef').lastIndexOf('qabc')); +assert.strictEqual(-1, Buffer.from('bc').lastIndexOf(Buffer.from('ab'))); +assert.strictEqual(-1, Buffer.from('bc', 'ucs2').lastIndexOf('ab', 'ucs2')); +assert.strictEqual(-1, Buffer.from('bc', 'ucs2').lastIndexOf(abInUCS2)); + +assert.strictEqual(0, Buffer.from('abc').lastIndexOf('ab')); +assert.strictEqual(0, Buffer.from('abc').lastIndexOf('ab', 1)); +assert.strictEqual(0, Buffer.from('abc').lastIndexOf('ab', 2)); +assert.strictEqual(0, Buffer.from('abc').lastIndexOf('ab', 3)); + +// The above tests test the LINEAR and SINGLE-CHAR strategies. +// Now, we test the BOYER-MOORE-HORSPOOL strategy. +// Test lastIndexOf on a long buffer w multiple matches: +pattern = 'JABACABADABACABA'; +assert.equal(1535, longBufferString.lastIndexOf(pattern)); +assert.equal(1535, longBufferString.lastIndexOf(pattern, 1535)); +assert.equal(511, longBufferString.lastIndexOf(pattern, 1534)); + +// Finally, give it a really long input to trigger fallback from BMH to +// regular BOYER-MOORE (which has better worst-case complexity). + +// Generate a really long Thue-Morse sequence of 'yolo' and 'swag', +// "yolo swag swag yolo swag yolo yolo swag" ..., goes on for about 5MB. +// This is hard to search because it all looks similar, but never repeats. + +// countBits returns the number of bits in the binary representation of n. +function countBits(n) { + for (var count = 0; n > 0; count++) { + n = n & (n - 1); // remove top bit + } + return count; +} +var parts = []; +for (var i = 0; i < 1000000; i++) { + parts.push((countBits(i) % 2 === 0) ? 'yolo' : 'swag'); +} +var reallyLong = new Buffer(parts.join(' ')); +assert.equal('yolo swag swag yolo', reallyLong.slice(0, 19).toString()); + +// Expensive reverse searches. Stress test lastIndexOf: +pattern = reallyLong.slice(0, 100000); // First 1/50th of the pattern. +assert.equal(4751360, reallyLong.lastIndexOf(pattern)); +assert.equal(3932160, reallyLong.lastIndexOf(pattern, 4000000)); +assert.equal(2949120, reallyLong.lastIndexOf(pattern, 3000000)); +pattern = reallyLong.slice(100000, 200000); // Second 1/50th. +assert.equal(4728480, reallyLong.lastIndexOf(pattern)); +pattern = reallyLong.slice(0, 1000000); // First 1/5th. +assert.equal(3932160, reallyLong.lastIndexOf(pattern)); +pattern = reallyLong.slice(0, 2000000); // first 2/5ths. +assert.equal(0, reallyLong.lastIndexOf(pattern)); + +// test truncation of Number arguments to uint8 +{ + var buf = Buffer.from('this is a test'); + assert.strictEqual(buf.indexOf(0x6973), 3); + assert.strictEqual(buf.indexOf(0x697320), 4); + assert.strictEqual(buf.indexOf(0x69732069), 2); + assert.strictEqual(buf.indexOf(0x697374657374), 0); + assert.strictEqual(buf.indexOf(0x69737374), 0); + assert.strictEqual(buf.indexOf(0x69737465), 11); + assert.strictEqual(buf.indexOf(0x69737465), 11); + assert.strictEqual(buf.indexOf(-140), 0); + assert.strictEqual(buf.indexOf(-152), 1); + assert.strictEqual(buf.indexOf(0xff), -1); + assert.strictEqual(buf.indexOf(0xffff), -1); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-inheritance.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-inheritance.js new file mode 100644 index 0000000000..26ac8f9b0b --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-inheritance.js @@ -0,0 +1,41 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +require('./common'); +const assert = require('assert'); + + +function T(n) { + const ui8 = new Uint8Array(n); + Object.setPrototypeOf(ui8, T.prototype); + return ui8; +} +Object.setPrototypeOf(T.prototype, Buffer.prototype); +Object.setPrototypeOf(T, Buffer); + +T.prototype.sum = function sum() { + let cntr = 0; + for (let i = 0; i < this.length; i++) + cntr += this[i]; + return cntr; +}; + + +const vals = [new T(4), T(4)]; + +vals.forEach(function(t) { + assert.strictEqual(t.constructor, T); + assert.strictEqual(Object.getPrototypeOf(t), T.prototype); + assert.strictEqual(Object.getPrototypeOf(Object.getPrototypeOf(t)), + Buffer.prototype); + + t.fill(5); + let cntr = 0; + for (let i = 0; i < t.length; i++) + cntr += t[i]; + assert.strictEqual(t.length * 5, cntr); + + // Check this does not throw + t.toString(); +}); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-inspect.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-inspect.js new file mode 100644 index 0000000000..8c404335ad --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-inspect.js @@ -0,0 +1,63 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +var Buffer = require('../../').Buffer; + +require('./common'); +const assert = require('assert'); +const util = require('util'); +const buffer = require('../../'); + +var defaultMaxBytes = buffer.INSPECT_MAX_BYTES; +buffer.INSPECT_MAX_BYTES = 2; + +let b = Buffer.allocUnsafe(4); +b.fill('1234'); + +let s = buffer.SlowBuffer(4); +s.fill('1234'); + +let expected = ''; + +assert.strictEqual(util.inspect(b), expected); +assert.strictEqual(util.inspect(s), expected); + +b = Buffer.allocUnsafe(2); +b.fill('12'); + +s = buffer.SlowBuffer(2); +s.fill('12'); + +expected = ''; + +assert.strictEqual(util.inspect(b), expected); +assert.strictEqual(util.inspect(s), expected); + +buffer.INSPECT_MAX_BYTES = Infinity; + +assert.strictEqual(util.inspect(b), expected); +assert.strictEqual(util.inspect(s), expected); + +b.inspect = undefined; +assert.strictEqual(util.inspect(b), expected); + +buffer.INSPECT_MAX_BYTES = defaultMaxBytes; diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-isencoding.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-isencoding.js new file mode 100644 index 0000000000..0d79140712 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-isencoding.js @@ -0,0 +1,39 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +require('./common'); +const assert = require('assert'); + +[ + 'hex', + 'utf8', + 'utf-8', + 'ascii', + 'latin1', + 'binary', + 'base64', + 'ucs2', + 'ucs-2', + 'utf16le', + 'utf-16le' +].forEach((enc) => { + assert.strictEqual(Buffer.isEncoding(enc), true); +}); + +[ + 'utf9', + 'utf-7', + 'Unicode-FTW', + 'new gnu gun', + false, + NaN, + {}, + Infinity, + [], + 1, + 0, + -1 +].forEach((enc) => { + assert.strictEqual(Buffer.isEncoding(enc), false); +}); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-iterator.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-iterator.js new file mode 100644 index 0000000000..3e084738bc --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-iterator.js @@ -0,0 +1,64 @@ +'use strict'; +var Buffer = require('../../').Buffer; +require('./common'); +const assert = require('assert'); + +const buffer = Buffer.from([1, 2, 3, 4, 5]); +let arr; +let b; + +// buffers should be iterable + +arr = []; + +for (b of buffer) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// buffer iterators should be iterable + +arr = []; + +for (b of buffer[Symbol.iterator]()) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// buffer#values() should return iterator for values + +arr = []; + +for (b of buffer.values()) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// buffer#keys() should return iterator for keys + +arr = []; + +for (b of buffer.keys()) + arr.push(b); + +assert.deepStrictEqual(arr, [0, 1, 2, 3, 4]); + + +// buffer#entries() should return iterator for entries + +arr = []; + +for (b of buffer.entries()) + arr.push(b); + +assert.deepStrictEqual(arr, [ + [0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5] +]); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-new.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-new.js new file mode 100644 index 0000000000..38692804c5 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-new.js @@ -0,0 +1,11 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +const common = require('./common'); + +common.expectsError(() => new Buffer(42, 'utf8'), { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "string" argument must be of type string. Received type number' +}); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-parent-property.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-parent-property.js new file mode 100644 index 0000000000..d01376a066 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-parent-property.js @@ -0,0 +1,25 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +/* + * Fix for https://github.com/nodejs/node/issues/8266 + * + * Zero length Buffer objects should expose the `buffer` property of the + * TypedArrays, via the `parent` property. + */ +require('./common'); +const assert = require('assert'); + +// If the length of the buffer object is zero +assert((new Buffer(0)).parent instanceof ArrayBuffer); + +// If the length of the buffer object is equal to the underlying ArrayBuffer +assert((new Buffer(Buffer.poolSize)).parent instanceof ArrayBuffer); + +// Same as the previous test, but with user created buffer +const arrayBuffer = new ArrayBuffer(0); +assert.strictEqual(new Buffer(arrayBuffer).parent, arrayBuffer); +assert.strictEqual(new Buffer(arrayBuffer).buffer, arrayBuffer); +assert.strictEqual(Buffer.from(arrayBuffer).parent, arrayBuffer); +assert.strictEqual(Buffer.from(arrayBuffer).buffer, arrayBuffer); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-prototype-inspect.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-prototype-inspect.js new file mode 100644 index 0000000000..2c643a75d7 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-prototype-inspect.js @@ -0,0 +1,25 @@ +'use strict'; +var Buffer = require('../../').Buffer; +require('./common'); + +// lib/buffer.js defines Buffer.prototype.inspect() to override how buffers are +// presented by util.inspect(). + +const assert = require('assert'); +const util = require('util'); + +{ + const buf = Buffer.from('fhqwhgads'); + assert.strictEqual(util.inspect(buf), ''); +} + +{ + const buf = Buffer.from(''); + assert.strictEqual(util.inspect(buf), ''); +} + +{ + const buf = Buffer.from('x'.repeat(51)); + assert.ok(/^$/.test(util.inspect(buf))); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-safe-unsafe.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-safe-unsafe.js new file mode 100644 index 0000000000..ca171b6867 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-safe-unsafe.js @@ -0,0 +1,26 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +require('./common'); +const assert = require('assert'); + +const safe = Buffer.alloc(10); + +function isZeroFilled(buf) { + for (let n = 0; n < buf.length; n++) + if (buf[n] !== 0) return false; + return true; +} + +assert(isZeroFilled(safe)); + +// Test that unsafe allocations doesn't affect subsequent safe allocations +Buffer.allocUnsafe(10); +assert(isZeroFilled(new Float64Array(10))); + +new Buffer(10); +assert(isZeroFilled(new Float64Array(10))); + +Buffer.allocUnsafe(10); +assert(isZeroFilled(Buffer.alloc(10))); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-slice.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-slice.js new file mode 100644 index 0000000000..c2d3282f59 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-slice.js @@ -0,0 +1,132 @@ +// Copyright Joyent, Inc. and other Node contributors.var Buffer = require('../../').Buffer; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('./common'); +const assert = require('assert'); + +assert.strictEqual(0, Buffer.from('hello', 'utf8').slice(0, 0).length); +assert.strictEqual(0, Buffer('hello', 'utf8').slice(0, 0).length); + +const buf = Buffer.from('0123456789', 'utf8'); +const expectedSameBufs = [ + [buf.slice(-10, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice(-20, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice(-20, -10), Buffer.from('', 'utf8')], + [buf.slice(), Buffer.from('0123456789', 'utf8')], + [buf.slice(0), Buffer.from('0123456789', 'utf8')], + [buf.slice(0, 0), Buffer.from('', 'utf8')], + [buf.slice(undefined), Buffer.from('0123456789', 'utf8')], + [buf.slice('foobar'), Buffer.from('0123456789', 'utf8')], + [buf.slice(undefined, undefined), Buffer.from('0123456789', 'utf8')], + [buf.slice(2), Buffer.from('23456789', 'utf8')], + [buf.slice(5), Buffer.from('56789', 'utf8')], + [buf.slice(10), Buffer.from('', 'utf8')], + [buf.slice(5, 8), Buffer.from('567', 'utf8')], + [buf.slice(8, -1), Buffer.from('8', 'utf8')], + [buf.slice(-10), Buffer.from('0123456789', 'utf8')], + [buf.slice(0, -9), Buffer.from('0', 'utf8')], + [buf.slice(0, -10), Buffer.from('', 'utf8')], + [buf.slice(0, -1), Buffer.from('012345678', 'utf8')], + [buf.slice(2, -2), Buffer.from('234567', 'utf8')], + [buf.slice(0, 65536), Buffer.from('0123456789', 'utf8')], + [buf.slice(65536, 0), Buffer.from('', 'utf8')], + [buf.slice(-5, -8), Buffer.from('', 'utf8')], + [buf.slice(-5, -3), Buffer.from('56', 'utf8')], + [buf.slice(-10, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice('0', '1'), Buffer.from('0', 'utf8')], + [buf.slice('-5', '10'), Buffer.from('56789', 'utf8')], + [buf.slice('-10', '10'), Buffer.from('0123456789', 'utf8')], + [buf.slice('-10', '-5'), Buffer.from('01234', 'utf8')], + [buf.slice('-10', '-0'), Buffer.from('', 'utf8')], + [buf.slice('111'), Buffer.from('', 'utf8')], + [buf.slice('0', '-111'), Buffer.from('', 'utf8')] +]; + +for (let i = 0, s = buf.toString(); i < buf.length; ++i) { + expectedSameBufs.push( + [buf.slice(i), Buffer.from(s.slice(i))], + [buf.slice(0, i), Buffer.from(s.slice(0, i))], + [buf.slice(-i), Buffer.from(s.slice(-i))], + [buf.slice(0, -i), Buffer.from(s.slice(0, -i))] + ); +} + +expectedSameBufs.forEach(([buf1, buf2]) => { + assert.strictEqual(0, Buffer.compare(buf1, buf2)); +}); + +const utf16Buf = Buffer.from('0123456789', 'utf16le'); +assert.deepStrictEqual(utf16Buf.slice(0, 6), Buffer.from('012', 'utf16le')); +// try to slice a zero length Buffer +// see https://github.com/joyent/node/issues/5881 +assert.doesNotThrow(() => Buffer.alloc(0).slice(0, 1)); +assert.strictEqual(Buffer.alloc(0).slice(0, 1).length, 0); + +{ + // Single argument slice + assert.strictEqual('bcde', + Buffer.from('abcde', 'utf8').slice(1).toString('utf8')); +} + +// slice(0,0).length === 0 +assert.strictEqual(0, Buffer.from('hello', 'utf8').slice(0, 0).length); + +{ + // Regression tests for https://github.com/nodejs/node/issues/9096 + const buf = Buffer.from('abcd', 'utf8'); + assert.strictEqual(buf.slice(buf.length / 3).toString('utf8'), 'bcd'); + assert.strictEqual( + buf.slice(buf.length / 3, buf.length).toString(), + 'bcd' + ); +} + +{ + const buf = Buffer.from('abcdefg', 'utf8'); + assert.strictEqual(buf.slice(-(-1 >>> 0) - 1).toString('utf8'), + buf.toString('utf8')); +} + +{ + const buf = Buffer.from('abc', 'utf8'); + assert.strictEqual(buf.slice(-0.5).toString('utf8'), buf.toString('utf8')); +} + +{ + const buf = Buffer.from([ + 1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0, + 0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0 + ]); + const chunk1 = Buffer.from([ + 1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0 + ]); + const chunk2 = Buffer.from([ + 0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0 + ]); + const middle = buf.length / 2; + + assert.deepStrictEqual(buf.slice(0, middle), chunk1); + assert.deepStrictEqual(buf.slice(middle), chunk2); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-slow.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-slow.js new file mode 100644 index 0000000000..4d73993898 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-slow.js @@ -0,0 +1,73 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +const common = require('./common'); +const assert = require('assert'); +const buffer = require('../../'); +const SlowBuffer = buffer.SlowBuffer; + +const ones = [1, 1, 1, 1]; + +// should create a Buffer +let sb = SlowBuffer(4); +assert(sb instanceof Buffer); +assert.strictEqual(sb.length, 4); +sb.fill(1); +for (const [key, value] of sb.entries()) { + assert.deepStrictEqual(value, ones[key]); +} + +// underlying ArrayBuffer should have the same length +assert.strictEqual(sb.buffer.byteLength, 4); + +// should work without new +sb = SlowBuffer(4); +assert(sb instanceof Buffer); +assert.strictEqual(sb.length, 4); +sb.fill(1); +for (const [key, value] of sb.entries()) { + assert.deepStrictEqual(value, ones[key]); +} + +// should work with edge cases +assert.strictEqual(SlowBuffer(0).length, 0); +try { + assert.strictEqual( + SlowBuffer(buffer.kMaxLength).length, buffer.kMaxLength); +} catch (e) { + // Don't match on message as it is from the JavaScript engine. V8 and + // ChakraCore provide different messages. + assert.strictEqual(e.name, 'RangeError'); +} + +// should work with number-coercible values +assert.strictEqual(SlowBuffer('6').length, 6); +assert.strictEqual(SlowBuffer(true).length, 1); + +// should create zero-length buffer if parameter is not a number +assert.strictEqual(SlowBuffer().length, 0); +assert.strictEqual(SlowBuffer(NaN).length, 0); +assert.strictEqual(SlowBuffer({}).length, 0); +assert.strictEqual(SlowBuffer('string').length, 0); + +// should throw with invalid length +const bufferMaxSizeMsg = common.expectsError({ + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError, + message: /^The value "[^"]*" is invalid for option "size"$/ +}, 2); +assert.throws(function() { + SlowBuffer(Infinity); +}, bufferMaxSizeMsg); +common.expectsError(function() { + SlowBuffer(-1); +}, { + code: 'ERR_INVALID_OPT_VALUE', + type: RangeError, + message: 'The value "-1" is invalid for option "size"' +}); + +assert.throws(function() { + SlowBuffer(buffer.kMaxLength + 1); +}, bufferMaxSizeMsg); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-swap.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-swap.js new file mode 100644 index 0000000000..9af5755ff9 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-swap.js @@ -0,0 +1,154 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +require('./common'); +const assert = require('assert'); + +// Test buffers small enough to use the JS implementation +{ + const buf = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10]); + + assert.strictEqual(buf, buf.swap16()); + assert.deepStrictEqual(buf, Buffer.from([0x02, 0x01, 0x04, 0x03, 0x06, 0x05, + 0x08, 0x07, 0x0a, 0x09, 0x0c, 0x0b, + 0x0e, 0x0d, 0x10, 0x0f])); + buf.swap16(); // restore + + assert.strictEqual(buf, buf.swap32()); + assert.deepStrictEqual(buf, Buffer.from([0x04, 0x03, 0x02, 0x01, 0x08, 0x07, + 0x06, 0x05, 0x0c, 0x0b, 0x0a, 0x09, + 0x10, 0x0f, 0x0e, 0x0d])); + buf.swap32(); // restore + + assert.strictEqual(buf, buf.swap64()); + assert.deepStrictEqual(buf, Buffer.from([0x08, 0x07, 0x06, 0x05, 0x04, 0x03, + 0x02, 0x01, 0x10, 0x0f, 0x0e, 0x0d, + 0x0c, 0x0b, 0x0a, 0x09])); +} + +// Operates in-place +{ + const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7]); + buf.slice(1, 5).swap32(); + assert.deepStrictEqual(buf, Buffer.from([0x1, 0x5, 0x4, 0x3, 0x2, 0x6, 0x7])); + buf.slice(1, 5).swap16(); + assert.deepStrictEqual(buf, Buffer.from([0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7])); + + // Length assertions + const re16 = /Buffer size must be a multiple of 16-bits/; + const re32 = /Buffer size must be a multiple of 32-bits/; + const re64 = /Buffer size must be a multiple of 64-bits/; + + assert.throws(() => Buffer.from(buf).swap16(), re16); + assert.throws(() => Buffer.alloc(1025).swap16(), re16); + assert.throws(() => Buffer.from(buf).swap32(), re32); + assert.throws(() => buf.slice(1, 3).swap32(), re32); + assert.throws(() => Buffer.alloc(1025).swap32(), re32); + assert.throws(() => buf.slice(1, 3).swap64(), re64); + assert.throws(() => Buffer.alloc(1025).swap64(), re64); +} + +{ + const buf = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10]); + + buf.slice(2, 18).swap64(); + + assert.deepStrictEqual(buf, Buffer.from([0x01, 0x02, 0x0a, 0x09, 0x08, 0x07, + 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10])); +} + +// Force use of native code (Buffer size above threshold limit for js impl) +{ + const bufData = new Uint32Array(256).fill(0x04030201); + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + const otherBufData = new Uint32Array(256).fill(0x03040102); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap16(); + assert.deepStrictEqual(buf, otherBuf); +} + +{ + const bufData = new Uint32Array(256).fill(0x04030201); + const buf = Buffer.from(bufData.buffer); + const otherBufData = new Uint32Array(256).fill(0x01020304); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap32(); + assert.deepStrictEqual(buf, otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 8; + otherBufData[otherBufData.length - i - 1] = i % 8; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap64(); + assert.deepStrictEqual(buf, otherBuf); +} + +// Test native code with buffers that are not memory-aligned +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 2); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 2; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 2; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 0|1 0|1... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 1|0 1|0... + + buf.slice(1, buf.length - 1).swap16(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 4); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 4; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 4; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 2 3 0|1 2 3... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 3 2 1|0 3 2... + + buf.slice(1, buf.length - 3).swap32(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 8); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 8; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 8; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 2 3 4 5 6 7 0|1 2 3 4... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 7 6 5 4 3 2 1|0 7 6 5... + + buf.slice(1, buf.length - 7).swap64(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-tojson.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-tojson.js new file mode 100644 index 0000000000..dfb94d6107 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-tojson.js @@ -0,0 +1,37 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +require('./common'); +const assert = require('assert'); + +{ + assert.strictEqual(JSON.stringify(Buffer.alloc(0)), + '{"type":"Buffer","data":[]}'); + assert.strictEqual(JSON.stringify(Buffer.from([1, 2, 3, 4])), + '{"type":"Buffer","data":[1,2,3,4]}'); +} + +// issue GH-7849 +{ + const buf = Buffer.from('test'); + const json = JSON.stringify(buf); + const obj = JSON.parse(json); + const copy = Buffer.from(obj); + + assert.deepStrictEqual(buf, copy); +} + +// GH-5110 +{ + const buffer = Buffer.from('test'); + const string = JSON.stringify(buffer); + + assert.strictEqual(string, '{"type":"Buffer","data":[116,101,115,116]}'); + + function receiver(key, value) { + return value && value.type === 'Buffer' ? Buffer.from(value.data) : value; + } + + assert.deepStrictEqual(buffer, JSON.parse(string, receiver)); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-tostring.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-tostring.js new file mode 100644 index 0000000000..ac0102c67d --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-tostring.js @@ -0,0 +1,39 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +const common = require('./common'); +const assert = require('assert'); + +// utf8, ucs2, ascii, latin1, utf16le +const encodings = ['utf8', 'utf-8', 'ucs2', 'ucs-2', 'ascii', 'latin1', + 'binary', 'utf16le', 'utf-16le']; + +encodings + .reduce((es, e) => es.concat(e, e.toUpperCase()), []) + .forEach((encoding) => { + assert.strictEqual(Buffer.from('foo', encoding).toString(encoding), 'foo'); + }); + +// base64 +['base64', 'BASE64'].forEach((encoding) => { + assert.strictEqual(Buffer.from('Zm9v', encoding).toString(encoding), 'Zm9v'); +}); + +// hex +['hex', 'HEX'].forEach((encoding) => { + assert.strictEqual(Buffer.from('666f6f', encoding).toString(encoding), + '666f6f'); +}); + +// Invalid encodings +for (let i = 1; i < 10; i++) { + const encoding = String(i).repeat(i); + const error = common.expectsError({ + code: 'ERR_UNKNOWN_ENCODING', + type: TypeError, + message: `Unknown encoding: ${encoding}` + }); + assert.ok(!Buffer.isEncoding(encoding)); + assert.throws(() => Buffer.from('foo').toString(encoding), error); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-write.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-write.js new file mode 100644 index 0000000000..85a87e8e4b --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-write.js @@ -0,0 +1,74 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +const common = require('./common'); +const assert = require('assert'); + +const outsideBounds = common.expectsError({ + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + type: RangeError, + message: 'Attempt to write outside buffer bounds' +}, 2); + +assert.throws(() => Buffer.alloc(9).write('foo', -1), outsideBounds); +assert.throws(() => Buffer.alloc(9).write('foo', 10), outsideBounds); + +const resultMap = new Map([ + ['utf8', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], + ['ucs2', Buffer.from([102, 0, 111, 0, 111, 0, 0, 0, 0])], + ['ascii', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], + ['latin1', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], + ['binary', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], + ['utf16le', Buffer.from([102, 0, 111, 0, 111, 0, 0, 0, 0])], + ['base64', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], + ['hex', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])] +]); + +// utf8, ucs2, ascii, latin1, utf16le +const encodings = ['utf8', 'utf-8', 'ucs2', 'ucs-2', 'ascii', 'latin1', + 'binary', 'utf16le', 'utf-16le']; + +encodings + .reduce((es, e) => es.concat(e, e.toUpperCase()), []) + .forEach((encoding) => { + const buf = Buffer.alloc(9); + const len = Buffer.byteLength('foo', encoding); + assert.strictEqual(buf.write('foo', 0, len, encoding), len); + + if (encoding.includes('-')) + encoding = encoding.replace('-', ''); + + assert.deepStrictEqual(buf, resultMap.get(encoding.toLowerCase())); + }); + +// base64 +['base64', 'BASE64'].forEach((encoding) => { + const buf = Buffer.alloc(9); + const len = Buffer.byteLength('Zm9v', encoding); + + assert.strictEqual(buf.write('Zm9v', 0, len, encoding), len); + assert.deepStrictEqual(buf, resultMap.get(encoding.toLowerCase())); +}); + +// hex +['hex', 'HEX'].forEach((encoding) => { + const buf = Buffer.alloc(9); + const len = Buffer.byteLength('666f6f', encoding); + + assert.strictEqual(buf.write('666f6f', 0, len, encoding), len); + assert.deepStrictEqual(buf, resultMap.get(encoding.toLowerCase())); +}); + +// Invalid encodings +for (let i = 1; i < 10; i++) { + const encoding = String(i).repeat(i); + const error = common.expectsError({ + code: 'ERR_UNKNOWN_ENCODING', + type: TypeError, + message: `Unknown encoding: ${encoding}` + }); + + assert.ok(!Buffer.isEncoding(encoding)); + assert.throws(() => Buffer.alloc(9).write('foo', encoding), error); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-zero-fill-cli.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-zero-fill-cli.js new file mode 100644 index 0000000000..1e86b277a6 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-zero-fill-cli.js @@ -0,0 +1,34 @@ +'use strict'; +var Buffer = require('../../').Buffer; +// Flags: --zero-fill-buffers + +// when using --zero-fill-buffers, every Buffer and SlowBuffer +// instance must be zero filled upon creation + +require('./common'); +const SlowBuffer = require('../../').SlowBuffer; +const assert = require('assert'); + +function isZeroFilled(buf) { + for (let n = 0; n < buf.length; n++) + if (buf[n] > 0) return false; + return true; +} + +// This can be somewhat unreliable because the +// allocated memory might just already happen to +// contain all zeroes. The test is run multiple +// times to improve the reliability. +for (let i = 0; i < 50; i++) { + const bufs = [ + Buffer.alloc(20), + Buffer.allocUnsafe(20), + SlowBuffer(20), + Buffer(20), + new SlowBuffer(20) + ]; + for (const buf of bufs) { + assert(isZeroFilled(buf)); + } +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-zero-fill-reset.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-zero-fill-reset.js new file mode 100644 index 0000000000..875a64ba16 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-zero-fill-reset.js @@ -0,0 +1,21 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +require('./common'); +const assert = require('assert'); + + +function testUint8Array(ui) { + const length = ui.length; + for (let i = 0; i < length; i++) + if (ui[i] !== 0) return false; + return true; +} + + +for (let i = 0; i < 100; i++) { + Buffer.alloc(0); + const ui = new Uint8Array(65); + assert.ok(testUint8Array(ui), `Uint8Array is not zero-filled: ${ui}`); +} + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer-zero-fill.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer-zero-fill.js new file mode 100644 index 0000000000..f5c1a13914 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer-zero-fill.js @@ -0,0 +1,15 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +require('./common'); +const assert = require('assert'); + +const buf1 = Buffer(100); +const buf2 = new Buffer(100); + +for (let n = 0; n < buf1.length; n++) + assert.strictEqual(buf1[n], 0); + +for (let n = 0; n < buf2.length; n++) + assert.strictEqual(buf2[n], 0); + diff --git a/modules/parquet/test/buffer-polyfill/wip/test-buffer.js b/modules/parquet/test/buffer-polyfill/wip/test-buffer.js new file mode 100644 index 0000000000..3a8c452746 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/wip/test-buffer.js @@ -0,0 +1,1500 @@ +'use strict'; +var Buffer = require('../../').Buffer; + +var common = { skip: function () {} }; +var assert = require('assert'); + +var Buffer = require('../../').Buffer; +var SlowBuffer = require('../../').SlowBuffer; + +// counter to ensure unique value is always copied +var cntr = 0; + +var b = Buffer(1024); // safe constructor + +// console.log('b.length == %d', b.length); +assert.strictEqual(1024, b.length); + +b[0] = -1; +assert.strictEqual(b[0], 255); + +for (var i = 0; i < 1024; i++) { + b[i] = i % 256; +} + +for (var i = 0; i < 1024; i++) { + assert.strictEqual(i % 256, b[i]); +} + +var c = Buffer(512); +// console.log('c.length == %d', c.length); +assert.strictEqual(512, c.length); + +var d = new Buffer([]); +assert.strictEqual(0, d.length); + +var ui32 = new Uint32Array(4).fill(42); +var e = Buffer(ui32); +for (var [key, value] of e.entries()) { + assert.deepStrictEqual(value, ui32[key]); +} + +// First check Buffer#fill() works as expected. + +assert.throws(function() { + Buffer(8).fill('a', -1); +}); + +assert.throws(function() { + Buffer(8).fill('a', 0, 9); +}); + +// Make sure this doesn't hang indefinitely. +Buffer(8).fill(''); + +{ + var buf = new Buffer(64); + buf.fill(10); + for (var i = 0; i < buf.length; i++) + assert.equal(buf[i], 10); + + buf.fill(11, 0, buf.length >> 1); + for (var i = 0; i < buf.length >> 1; i++) + assert.equal(buf[i], 11); + for (var i = (buf.length >> 1) + 1; i < buf.length; i++) + assert.equal(buf[i], 10); + + buf.fill('h'); + for (var i = 0; i < buf.length; i++) + assert.equal('h'.charCodeAt(0), buf[i]); + + buf.fill(0); + for (var i = 0; i < buf.length; i++) + assert.equal(0, buf[i]); + + buf.fill(null); + for (var i = 0; i < buf.length; i++) + assert.equal(0, buf[i]); + + buf.fill(1, 16, 32); + for (var i = 0; i < 16; i++) + assert.equal(0, buf[i]); + for (var i = 16; i < 32; i++) + assert.equal(1, buf[i]); + for (var i = 32; i < buf.length; i++) + assert.equal(0, buf[i]); +} + +{ + var buf = new Buffer(10); + buf.fill('abc'); + assert.equal(buf.toString(), 'abcabcabca'); + buf.fill('է'); + assert.equal(buf.toString(), 'էէէէէ'); +} + +{ + // copy 512 bytes, from 0 to 512. + b.fill(++cntr); + c.fill(++cntr); + var copied = b.copy(c, 0, 0, 512); +// console.log('copied %d bytes from b into c', copied); + assert.strictEqual(512, copied); + for (var i = 0; i < c.length; i++) { + assert.strictEqual(b[i], c[i]); + } +} + +{ + // copy c into b, without specifying sourceEnd + b.fill(++cntr); + c.fill(++cntr); + var copied = c.copy(b, 0, 0); +// console.log('copied %d bytes from c into b w/o sourceEnd', copied); + assert.strictEqual(c.length, copied); + for (var i = 0; i < c.length; i++) { + assert.strictEqual(c[i], b[i]); + } +} + +{ + // copy c into b, without specifying sourceStart + b.fill(++cntr); + c.fill(++cntr); + var copied = c.copy(b, 0); +// console.log('copied %d bytes from c into b w/o sourceStart', copied); + assert.strictEqual(c.length, copied); + for (var i = 0; i < c.length; i++) { + assert.strictEqual(c[i], b[i]); + } +} + +{ + // copy longer buffer b to shorter c without targetStart + b.fill(++cntr); + c.fill(++cntr); + var copied = b.copy(c); +// console.log('copied %d bytes from b into c w/o targetStart', copied); + assert.strictEqual(c.length, copied); + for (var i = 0; i < c.length; i++) { + assert.strictEqual(b[i], c[i]); + } +} + +{ + // copy starting near end of b to c + b.fill(++cntr); + c.fill(++cntr); + var copied = b.copy(c, 0, b.length - Math.floor(c.length / 2)); +// console.log('copied %d bytes from end of b into beginning of c', copied); + assert.strictEqual(Math.floor(c.length / 2), copied); + for (var i = 0; i < Math.floor(c.length / 2); i++) { + assert.strictEqual(b[b.length - Math.floor(c.length / 2) + i], c[i]); + } + for (var i = Math.floor(c.length / 2) + 1; i < c.length; i++) { + assert.strictEqual(c[c.length - 1], c[i]); + } +} + +{ + // try to copy 513 bytes, and check we don't overrun c + b.fill(++cntr); + c.fill(++cntr); + var copied = b.copy(c, 0, 0, 513); +// console.log('copied %d bytes from b trying to overrun c', copied); + assert.strictEqual(c.length, copied); + for (var i = 0; i < c.length; i++) { + assert.strictEqual(b[i], c[i]); + } +} + +{ + // copy 768 bytes from b into b + b.fill(++cntr); + b.fill(++cntr, 256); + var copied = b.copy(b, 0, 256, 1024); +// console.log('copied %d bytes from b into b', copied); + assert.strictEqual(768, copied); + for (var i = 0; i < b.length; i++) { + assert.strictEqual(cntr, b[i]); + } +} + +// copy string longer than buffer length (failure will segfault) +var bb = Buffer(10); +bb.fill('hello crazy world'); + + +// try to copy from before the beginning of b +assert.doesNotThrow(() => { b.copy(c, 0, 100, 10); }); + +// copy throws at negative sourceStart +assert.throws(function() { + Buffer(5).copy(Buffer(5), 0, -1); +}, RangeError); + +{ + // check sourceEnd resets to targetEnd if former is greater than the latter + b.fill(++cntr); + c.fill(++cntr); + var copied = b.copy(c, 0, 0, 1025); +// console.log('copied %d bytes from b into c', copied); + for (var i = 0; i < c.length; i++) { + assert.strictEqual(b[i], c[i]); + } +} + +// throw with negative sourceEnd +// console.log('test copy at negative sourceEnd'); +assert.throws(function() { + b.copy(c, 0, 0, -1); +}, RangeError); + +// when sourceStart is greater than sourceEnd, zero copied +assert.equal(b.copy(c, 0, 100, 10), 0); + +// when targetStart > targetLength, zero copied +assert.equal(b.copy(c, 512, 0, 10), 0); + +var caught_error; + +// invalid encoding for Buffer.toString +caught_error = null; +try { + b.toString('invalid'); +} catch (err) { + caught_error = err; +} +assert.strictEqual('Unknown encoding: invalid', caught_error.message); + +// invalid encoding for Buffer.write +caught_error = null; +try { + b.write('test string', 0, 5, 'invalid'); +} catch (err) { + caught_error = err; +} +assert.strictEqual('Unknown encoding: invalid', caught_error.message); + +// try to create 0-length buffers +new Buffer(''); +new Buffer('', 'ascii'); +new Buffer('', 'latin1'); +new Buffer('', 'binary'); +Buffer(0); + +// try to write a 0-length string beyond the end of b +assert.throws(function() { + b.write('', 2048); +}, RangeError); + +// throw when writing to negative offset +assert.throws(function() { + b.write('a', -1); +}, RangeError); + +// throw when writing past bounds from the pool +assert.throws(function() { + b.write('a', 2048); +}, RangeError); + +// throw when writing to negative offset +assert.throws(function() { + b.write('a', -1); +}, RangeError); + +// try to copy 0 bytes worth of data into an empty buffer +b.copy(Buffer(0), 0, 0, 0); + +// try to copy 0 bytes past the end of the target buffer +b.copy(Buffer(0), 1, 1, 1); +b.copy(Buffer(1), 1, 1, 1); + +// try to copy 0 bytes from past the end of the source buffer +b.copy(Buffer(1), 0, 2048, 2048); + +var rangeBuffer = new Buffer('abc'); + +// if start >= buffer's length, empty string will be returned +assert.equal(rangeBuffer.toString('ascii', 3), ''); +assert.equal(rangeBuffer.toString('ascii', +Infinity), ''); +assert.equal(rangeBuffer.toString('ascii', 3.14, 3), ''); +assert.equal(rangeBuffer.toString('ascii', 'Infinity', 3), ''); + +// if end <= 0, empty string will be returned +assert.equal(rangeBuffer.toString('ascii', 1, 0), ''); +assert.equal(rangeBuffer.toString('ascii', 1, -1.2), ''); +assert.equal(rangeBuffer.toString('ascii', 1, -100), ''); +assert.equal(rangeBuffer.toString('ascii', 1, -Infinity), ''); + +// if start < 0, start will be taken as zero +assert.equal(rangeBuffer.toString('ascii', -1, 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', -1.99, 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', -Infinity, 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', '-1', 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', '-1.99', 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', '-Infinity', 3), 'abc'); + +// if start is an invalid integer, start will be taken as zero +assert.equal(rangeBuffer.toString('ascii', 'node.js', 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', {}, 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', [], 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', NaN, 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', null, 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', undefined, 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', false, 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', '', 3), 'abc'); + +// but, if start is an integer when coerced, then it will be coerced and used. +assert.equal(rangeBuffer.toString('ascii', '-1', 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', '1', 3), 'bc'); +assert.equal(rangeBuffer.toString('ascii', '-Infinity', 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', '3', 3), ''); +assert.equal(rangeBuffer.toString('ascii', Number(3), 3), ''); +assert.equal(rangeBuffer.toString('ascii', '3.14', 3), ''); +assert.equal(rangeBuffer.toString('ascii', '1.99', 3), 'bc'); +assert.equal(rangeBuffer.toString('ascii', '-1.99', 3), 'abc'); +assert.equal(rangeBuffer.toString('ascii', 1.99, 3), 'bc'); +assert.equal(rangeBuffer.toString('ascii', true, 3), 'bc'); + +// if end > buffer's length, end will be taken as buffer's length +assert.equal(rangeBuffer.toString('ascii', 0, 5), 'abc'); +assert.equal(rangeBuffer.toString('ascii', 0, 6.99), 'abc'); +assert.equal(rangeBuffer.toString('ascii', 0, Infinity), 'abc'); +assert.equal(rangeBuffer.toString('ascii', 0, '5'), 'abc'); +assert.equal(rangeBuffer.toString('ascii', 0, '6.99'), 'abc'); +assert.equal(rangeBuffer.toString('ascii', 0, 'Infinity'), 'abc'); + +// if end is an invalid integer, end will be taken as buffer's length +assert.equal(rangeBuffer.toString('ascii', 0, 'node.js'), ''); +assert.equal(rangeBuffer.toString('ascii', 0, {}), ''); +assert.equal(rangeBuffer.toString('ascii', 0, NaN), ''); +assert.equal(rangeBuffer.toString('ascii', 0, undefined), 'abc'); +assert.equal(rangeBuffer.toString('ascii', 0), 'abc'); +assert.equal(rangeBuffer.toString('ascii', 0, null), ''); +assert.equal(rangeBuffer.toString('ascii', 0, []), ''); +assert.equal(rangeBuffer.toString('ascii', 0, false), ''); +assert.equal(rangeBuffer.toString('ascii', 0, ''), ''); + +// but, if end is an integer when coerced, then it will be coerced and used. +assert.equal(rangeBuffer.toString('ascii', 0, '-1'), ''); +assert.equal(rangeBuffer.toString('ascii', 0, '1'), 'a'); +assert.equal(rangeBuffer.toString('ascii', 0, '-Infinity'), ''); +assert.equal(rangeBuffer.toString('ascii', 0, '3'), 'abc'); +assert.equal(rangeBuffer.toString('ascii', 0, Number(3)), 'abc'); +assert.equal(rangeBuffer.toString('ascii', 0, '3.14'), 'abc'); +assert.equal(rangeBuffer.toString('ascii', 0, '1.99'), 'a'); +assert.equal(rangeBuffer.toString('ascii', 0, '-1.99'), ''); +assert.equal(rangeBuffer.toString('ascii', 0, 1.99), 'a'); +assert.equal(rangeBuffer.toString('ascii', 0, true), 'a'); + +// try toString() with a object as a encoding +assert.equal(rangeBuffer.toString({toString: function() { + return 'ascii'; +}}), 'abc'); + +// testing for smart defaults and ability to pass string values as offset +var writeTest = new Buffer('abcdes'); +writeTest.write('n', 'ascii'); +writeTest.write('o', '1', 'ascii'); +writeTest.write('d', '2', 'ascii'); +writeTest.write('e', 3, 'ascii'); +writeTest.write('j', 4, 'ascii'); +assert.equal(writeTest.toString(), 'nodejs'); + +// ASCII slice test +{ + var asciiString = 'hello world'; + + for (var i = 0; i < asciiString.length; i++) { + b[i] = asciiString.charCodeAt(i); + } + var asciiSlice = b.toString('ascii', 0, asciiString.length); + assert.equal(asciiString, asciiSlice); +} + +{ + var asciiString = 'hello world'; + var offset = 100; + + var written = b.write(asciiString, offset, 'ascii'); + assert.equal(asciiString.length, written); + var asciiSlice = b.toString('ascii', offset, offset + asciiString.length); + assert.equal(asciiString, asciiSlice); +} + +{ + var asciiString = 'hello world'; + var offset = 100; + + var sliceA = b.slice(offset, offset + asciiString.length); + var sliceB = b.slice(offset, offset + asciiString.length); + for (var i = 0; i < asciiString.length; i++) { + assert.equal(sliceA[i], sliceB[i]); + } +} + +// UTF-8 slice test + +var utf8String = '¡hέlló wôrld!'; +var offset = 100; + +b.write(utf8String, 0, Buffer.byteLength(utf8String), 'utf8'); +var utf8Slice = b.toString('utf8', 0, Buffer.byteLength(utf8String)); +assert.equal(utf8String, utf8Slice); + +var written = b.write(utf8String, offset, 'utf8'); +assert.equal(Buffer.byteLength(utf8String), written); +utf8Slice = b.toString('utf8', offset, offset + Buffer.byteLength(utf8String)); +assert.equal(utf8String, utf8Slice); + +var sliceA = b.slice(offset, offset + Buffer.byteLength(utf8String)); +var sliceB = b.slice(offset, offset + Buffer.byteLength(utf8String)); +for (var i = 0; i < Buffer.byteLength(utf8String); i++) { + assert.equal(sliceA[i], sliceB[i]); +} + +{ + var slice = b.slice(100, 150); + assert.equal(50, slice.length); + for (var i = 0; i < 50; i++) { + assert.equal(b[100 + i], slice[i]); + } +} + +{ + // make sure only top level parent propagates from allocPool + var b = new Buffer(5); + var c = b.slice(0, 4); + var d = c.slice(0, 2); + assert.equal(b.parent, c.parent); + assert.equal(b.parent, d.parent); +} + +{ + // also from a non-pooled instance + var b = new SlowBuffer(5); + var c = b.slice(0, 4); + var d = c.slice(0, 2); + assert.equal(c.parent, d.parent); +} + +{ + // Bug regression test + var testValue = '\u00F6\u65E5\u672C\u8A9E'; // ö日本語 + var buffer = new Buffer(32); + var size = buffer.write(testValue, 0, 'utf8'); +// console.log('bytes written to buffer: ' + size); + var slice = buffer.toString('utf8', 0, size); + assert.equal(slice, testValue); +} + +{ + // Test triple slice + var a = new Buffer(8); + for (var i = 0; i < 8; i++) a[i] = i; + var b = a.slice(4, 8); + assert.equal(4, b[0]); + assert.equal(5, b[1]); + assert.equal(6, b[2]); + assert.equal(7, b[3]); + var c = b.slice(2, 4); + assert.equal(6, c[0]); + assert.equal(7, c[1]); +} + +{ + var d = new Buffer([23, 42, 255]); + assert.equal(d.length, 3); + assert.equal(d[0], 23); + assert.equal(d[1], 42); + assert.equal(d[2], 255); + assert.deepStrictEqual(d, new Buffer(d)); +} + +{ + var e = new Buffer('über'); +// console.error('uber: \'%s\'', e.toString()); + assert.deepStrictEqual(e, new Buffer([195, 188, 98, 101, 114])); +} + +{ + var f = new Buffer('über', 'ascii'); +// console.error('f.length: %d (should be 4)', f.length); + assert.deepStrictEqual(f, new Buffer([252, 98, 101, 114])); +} + +['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach(function(encoding) { + { + var f = new Buffer('über', encoding); +// console.error('f.length: %d (should be 8)', f.length); + assert.deepStrictEqual(f, new Buffer([252, 0, 98, 0, 101, 0, 114, 0])); + } + + { + var f = new Buffer('привет', encoding); +// console.error('f.length: %d (should be 12)', f.length); + var expected = new Buffer([63, 4, 64, 4, 56, 4, 50, 4, 53, 4, 66, 4]); + assert.deepStrictEqual(f, expected); + assert.equal(f.toString(encoding), 'привет'); + } + + { + var f = new Buffer([0, 0, 0, 0, 0]); + assert.equal(f.length, 5); + var size = f.write('あいうえお', encoding); +// console.error('bytes written to buffer: %d (should be 4)', size); + assert.equal(size, 4); + assert.deepStrictEqual(f, new Buffer([0x42, 0x30, 0x44, 0x30, 0x00])); + } +}); + +{ + var f = new Buffer('\uD83D\uDC4D', 'utf-16le'); // THUMBS UP SIGN (U+1F44D) + assert.equal(f.length, 4); + assert.deepStrictEqual(f, new Buffer('3DD84DDC', 'hex')); +} + + +var arrayIsh = {0: 0, 1: 1, 2: 2, 3: 3, length: 4}; +var g = new Buffer(arrayIsh); +assert.deepStrictEqual(g, new Buffer([0, 1, 2, 3])); +var strArrayIsh = {0: '0', 1: '1', 2: '2', 3: '3', length: 4}; +g = new Buffer(strArrayIsh); +assert.deepStrictEqual(g, new Buffer([0, 1, 2, 3])); + + +// +// Test toString('base64') +// +assert.equal('TWFu', (new Buffer('Man')).toString('base64')); + +{ + // test that regular and URL-safe base64 both work + var expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff]; + assert.deepStrictEqual(Buffer('//++/++/++//', 'base64'), Buffer(expected)); + assert.deepStrictEqual(Buffer('__--_--_--__', 'base64'), Buffer(expected)); +} + +{ + // big example + var quote = 'Man is distinguished, not only by his reason, but by this ' + + 'singular passion from other animals, which is a lust ' + + 'of the mind, that by a perseverance of delight in the ' + + 'continued and indefatigable generation of knowledge, ' + + 'exceeds the short vehemence of any carnal pleasure.'; + var expected = 'TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb' + + '24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlci' + + 'BhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQ' + + 'gYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu' + + 'dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZ' + + 'GdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm' + + '5hbCBwbGVhc3VyZS4='; + assert.equal(expected, (new Buffer(quote)).toString('base64')); + + var b = new Buffer(1024); + var bytesWritten = b.write(expected, 0, 'base64'); + assert.equal(quote.length, bytesWritten); + assert.equal(quote, b.toString('ascii', 0, quote.length)); + + // check that the base64 decoder ignores whitespace + var expectedWhite = expected.slice(0, 60) + ' \n' + + expected.slice(60, 120) + ' \n' + + expected.slice(120, 180) + ' \n' + + expected.slice(180, 240) + ' \n' + + expected.slice(240, 300) + '\n' + + expected.slice(300, 360) + '\n'; + b = new Buffer(1024); + bytesWritten = b.write(expectedWhite, 0, 'base64'); + assert.equal(quote.length, bytesWritten); + assert.equal(quote, b.toString('ascii', 0, quote.length)); + + // check that the base64 decoder on the constructor works + // even in the presence of whitespace. + b = new Buffer(expectedWhite, 'base64'); + assert.equal(quote.length, b.length); + assert.equal(quote, b.toString('ascii', 0, quote.length)); + + // check that the base64 decoder ignores illegal chars + var expectedIllegal = expected.slice(0, 60) + ' \x80' + + expected.slice(60, 120) + ' \xff' + + expected.slice(120, 180) + ' \x00' + + expected.slice(180, 240) + ' \x98' + + expected.slice(240, 300) + '\x03' + + expected.slice(300, 360); + b = new Buffer(expectedIllegal, 'base64'); + assert.equal(quote.length, b.length); + assert.equal(quote, b.toString('ascii', 0, quote.length)); +} + +assert.equal(new Buffer('', 'base64').toString(), ''); +assert.equal(new Buffer('K', 'base64').toString(), ''); + +// multiple-of-4 with padding +assert.equal(new Buffer('Kg==', 'base64').toString(), '*'); +assert.equal(new Buffer('Kio=', 'base64').toString(), '**'); +assert.equal(new Buffer('Kioq', 'base64').toString(), '***'); +assert.equal(new Buffer('KioqKg==', 'base64').toString(), '****'); +assert.equal(new Buffer('KioqKio=', 'base64').toString(), '*****'); +assert.equal(new Buffer('KioqKioq', 'base64').toString(), '******'); +assert.equal(new Buffer('KioqKioqKg==', 'base64').toString(), '*******'); +assert.equal(new Buffer('KioqKioqKio=', 'base64').toString(), '********'); +assert.equal(new Buffer('KioqKioqKioq', 'base64').toString(), '*********'); +assert.equal(new Buffer('KioqKioqKioqKg==', 'base64').toString(), + '**********'); +assert.equal(new Buffer('KioqKioqKioqKio=', 'base64').toString(), + '***********'); +assert.equal(new Buffer('KioqKioqKioqKioq', 'base64').toString(), + '************'); +assert.equal(new Buffer('KioqKioqKioqKioqKg==', 'base64').toString(), + '*************'); +assert.equal(new Buffer('KioqKioqKioqKioqKio=', 'base64').toString(), + '**************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioq', 'base64').toString(), + '***************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKg==', 'base64').toString(), + '****************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKio=', 'base64').toString(), + '*****************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKioq', 'base64').toString(), + '******************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKioqKg==', 'base64').toString(), + '*******************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKioqKio=', 'base64').toString(), + '********************'); + +// no padding, not a multiple of 4 +assert.equal(new Buffer('Kg', 'base64').toString(), '*'); +assert.equal(new Buffer('Kio', 'base64').toString(), '**'); +assert.equal(new Buffer('KioqKg', 'base64').toString(), '****'); +assert.equal(new Buffer('KioqKio', 'base64').toString(), '*****'); +assert.equal(new Buffer('KioqKioqKg', 'base64').toString(), '*******'); +assert.equal(new Buffer('KioqKioqKio', 'base64').toString(), '********'); +assert.equal(new Buffer('KioqKioqKioqKg', 'base64').toString(), '**********'); +assert.equal(new Buffer('KioqKioqKioqKio', 'base64').toString(), '***********'); +assert.equal(new Buffer('KioqKioqKioqKioqKg', 'base64').toString(), + '*************'); +assert.equal(new Buffer('KioqKioqKioqKioqKio', 'base64').toString(), + '**************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKg', 'base64').toString(), + '****************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKio', 'base64').toString(), + '*****************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKioqKg', 'base64').toString(), + '*******************'); +assert.equal(new Buffer('KioqKioqKioqKioqKioqKioqKio', 'base64').toString(), + '********************'); + +// handle padding graciously, multiple-of-4 or not +assert.equal( + new Buffer('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw==', 'base64').length, + 32 +); +assert.equal( + new Buffer('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw=', 'base64').length, + 32 +); +assert.equal( + new Buffer('72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw', 'base64').length, + 32 +); +assert.equal( + new Buffer('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg==', 'base64').length, + 31 +); +assert.equal( + new Buffer('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg=', 'base64').length, + 31 +); +assert.equal( + new Buffer('w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg', 'base64').length, + 31 +); + +// This string encodes single '.' character in UTF-16 +var dot = new Buffer('//4uAA==', 'base64'); +assert.equal(dot[0], 0xff); +assert.equal(dot[1], 0xfe); +assert.equal(dot[2], 0x2e); +assert.equal(dot[3], 0x00); +assert.equal(dot.toString('base64'), '//4uAA=='); + +{ + // Writing base64 at a position > 0 should not mangle the result. + // + // https://github.com/joyent/node/issues/402 + var segments = ['TWFkbmVzcz8h', 'IFRoaXM=', 'IGlz', 'IG5vZGUuanMh']; + var b = new Buffer(64); + var pos = 0; + + for (var i = 0; i < segments.length; ++i) { + pos += b.write(segments[i], pos, 'base64'); + } + assert.equal(b.toString('latin1', 0, pos), 'Madness?! This is node.js!'); + assert.equal(b.toString('binary', 0, pos), 'Madness?! This is node.js!'); +} + +// Regression test for https://github.com/nodejs/node/issues/3496. +// assert.equal(Buffer('=bad'.repeat(1e4), 'base64').length, 0); + +{ + // Creating buffers larger than pool size. + var l = Buffer.poolSize + 5; + var s = 'h'.repeat(l); + + var b = new Buffer(s); + + for (var i = 0; i < l; i++) { + assert.equal('h'.charCodeAt(0), b[i]); + } + + var sb = b.toString(); + assert.equal(sb.length, s.length); + assert.equal(sb, s); +} + +{ + // Single argument slice + var b = new Buffer('abcde'); + assert.equal('bcde', b.slice(1).toString()); +} + +// slice(0,0).length === 0 +assert.equal(0, Buffer('hello').slice(0, 0).length); + +// test hex toString +// console.log('Create hex string from buffer'); +var hexb = new Buffer(256); +for (var i = 0; i < 256; i++) { + hexb[i] = i; +} +var hexStr = hexb.toString('hex'); +assert.equal(hexStr, + '000102030405060708090a0b0c0d0e0f' + + '101112131415161718191a1b1c1d1e1f' + + '202122232425262728292a2b2c2d2e2f' + + '303132333435363738393a3b3c3d3e3f' + + '404142434445464748494a4b4c4d4e4f' + + '505152535455565758595a5b5c5d5e5f' + + '606162636465666768696a6b6c6d6e6f' + + '707172737475767778797a7b7c7d7e7f' + + '808182838485868788898a8b8c8d8e8f' + + '909192939495969798999a9b9c9d9e9f' + + 'a0a1a2a3a4a5a6a7a8a9aaabacadaeaf' + + 'b0b1b2b3b4b5b6b7b8b9babbbcbdbebf' + + 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf' + + 'd0d1d2d3d4d5d6d7d8d9dadbdcdddedf' + + 'e0e1e2e3e4e5e6e7e8e9eaebecedeeef' + + 'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'); + +// console.log('Create buffer from hex string'); +var hexb2 = new Buffer(hexStr, 'hex'); +for (var i = 0; i < 256; i++) { + assert.equal(hexb2[i], hexb[i]); +} + +// Test single base64 char encodes as 0 +// assert.strictEqual(Buffer.from('A', 'base64').length, 0); + +{ + // test an invalid slice end. +// console.log('Try to slice off the end of the buffer'); + var b = new Buffer([1, 2, 3, 4, 5]); + var b2 = b.toString('hex', 1, 10000); + var b3 = b.toString('hex', 1, 5); + var b4 = b.toString('hex', 1); + assert.equal(b2, b3); + assert.equal(b2, b4); +} + +function buildBuffer(data) { + if (Array.isArray(data)) { + var buffer = Buffer(data.length); + data.forEach(function(v, k) { + buffer[k] = v; + }); + return buffer; + } + return null; +} + +var x = buildBuffer([0x81, 0xa3, 0x66, 0x6f, 0x6f, 0xa3, 0x62, 0x61, 0x72]); + +// console.log(x.inspect()); +assert.equal('', x.inspect()); + +{ + var z = x.slice(4); +// console.log(z.inspect()); +// console.log(z.length); + assert.equal(5, z.length); + assert.equal(0x6f, z[0]); + assert.equal(0xa3, z[1]); + assert.equal(0x62, z[2]); + assert.equal(0x61, z[3]); + assert.equal(0x72, z[4]); +} + +{ + var z = x.slice(0); +// console.log(z.inspect()); +// console.log(z.length); + assert.equal(z.length, x.length); +} + +{ + var z = x.slice(0, 4); +// console.log(z.inspect()); +// console.log(z.length); + assert.equal(4, z.length); + assert.equal(0x81, z[0]); + assert.equal(0xa3, z[1]); +} + +{ + var z = x.slice(0, 9); +// console.log(z.inspect()); +// console.log(z.length); + assert.equal(9, z.length); +} + +{ + var z = x.slice(1, 4); +// console.log(z.inspect()); +// console.log(z.length); + assert.equal(3, z.length); + assert.equal(0xa3, z[0]); +} + +{ + var z = x.slice(2, 4); +// console.log(z.inspect()); +// console.log(z.length); + assert.equal(2, z.length); + assert.equal(0x66, z[0]); + assert.equal(0x6f, z[1]); +} + +assert.equal(0, Buffer('hello').slice(0, 0).length); + +['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach(function(encoding) { + var b = new Buffer(10); + b.write('あいうえお', encoding); + assert.equal(b.toString(encoding), 'あいうえお'); +}); + +{ + // latin1 encoding should write only one byte per character. + var b = Buffer([0xde, 0xad, 0xbe, 0xef]); + var s = String.fromCharCode(0xffff); + b.write(s, 0, 'latin1'); + assert.equal(0xff, b[0]); + assert.equal(0xad, b[1]); + assert.equal(0xbe, b[2]); + assert.equal(0xef, b[3]); + s = String.fromCharCode(0xaaee); + b.write(s, 0, 'latin1'); + assert.equal(0xee, b[0]); + assert.equal(0xad, b[1]); + assert.equal(0xbe, b[2]); + assert.equal(0xef, b[3]); +} + +{ + // Binary encoding should write only one byte per character. + var b = Buffer([0xde, 0xad, 0xbe, 0xef]); + var s = String.fromCharCode(0xffff); + b.write(s, 0, 'binary'); + assert.equal(0xff, b[0]); + assert.equal(0xad, b[1]); + assert.equal(0xbe, b[2]); + assert.equal(0xef, b[3]); + s = String.fromCharCode(0xaaee); + b.write(s, 0, 'binary'); + assert.equal(0xee, b[0]); + assert.equal(0xad, b[1]); + assert.equal(0xbe, b[2]); + assert.equal(0xef, b[3]); +} + +{ + // #1210 Test UTF-8 string includes null character + var buf = new Buffer('\0'); + assert.equal(buf.length, 1); + buf = new Buffer('\0\0'); + assert.equal(buf.length, 2); +} + +{ + var buf = new Buffer(2); + var written = buf.write(''); // 0byte + assert.equal(written, 0); + written = buf.write('\0'); // 1byte (v8 adds null terminator) + assert.equal(written, 1); + written = buf.write('a\0'); // 1byte * 2 + assert.equal(written, 2); + written = buf.write('あ'); // 3bytes + assert.equal(written, 0); + written = buf.write('\0あ'); // 1byte + 3bytes + assert.equal(written, 1); + written = buf.write('\0\0あ'); // 1byte * 2 + 3bytes + assert.equal(written, 2); +} + +{ + var buf = new Buffer(10); + written = buf.write('あいう'); // 3bytes * 3 (v8 adds null terminator) + assert.equal(written, 9); + written = buf.write('あいう\0'); // 3bytes * 3 + 1byte + assert.equal(written, 10); +} + +{ + // #243 Test write() with maxLength + var buf = new Buffer(4); + buf.fill(0xFF); + var written = buf.write('abcd', 1, 2, 'utf8'); +// console.log(buf); + assert.equal(written, 2); + assert.equal(buf[0], 0xFF); + assert.equal(buf[1], 0x61); + assert.equal(buf[2], 0x62); + assert.equal(buf[3], 0xFF); + + buf.fill(0xFF); + written = buf.write('abcd', 1, 4); +// console.log(buf); + assert.equal(written, 3); + assert.equal(buf[0], 0xFF); + assert.equal(buf[1], 0x61); + assert.equal(buf[2], 0x62); + assert.equal(buf[3], 0x63); + + buf.fill(0xFF); + written = buf.write('abcd', 1, 2, 'utf8'); +// console.log(buf); + assert.equal(written, 2); + assert.equal(buf[0], 0xFF); + assert.equal(buf[1], 0x61); + assert.equal(buf[2], 0x62); + assert.equal(buf[3], 0xFF); + + buf.fill(0xFF); + written = buf.write('abcdef', 1, 2, 'hex'); +// console.log(buf); + assert.equal(written, 2); + assert.equal(buf[0], 0xFF); + assert.equal(buf[1], 0xAB); + assert.equal(buf[2], 0xCD); + assert.equal(buf[3], 0xFF); + + ['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach(function(encoding) { + buf.fill(0xFF); + written = buf.write('abcd', 0, 2, encoding); +// console.log(buf); + assert.equal(written, 2); + assert.equal(buf[0], 0x61); + assert.equal(buf[1], 0x00); + assert.equal(buf[2], 0xFF); + assert.equal(buf[3], 0xFF); + }); +} + +{ + // test offset returns are correct + var b = new Buffer(16); + assert.equal(4, b.writeUInt32LE(0, 0)); + assert.equal(6, b.writeUInt16LE(0, 4)); + assert.equal(7, b.writeUInt8(0, 6)); + assert.equal(8, b.writeInt8(0, 7)); + assert.equal(16, b.writeDoubleLE(0, 8)); +} + +{ + // test unmatched surrogates not producing invalid utf8 output + // ef bf bd = utf-8 representation of unicode replacement character + // see https://codereview.chromium.org/121173009/ + var buf = new Buffer('ab\ud800cd', 'utf8'); + assert.equal(buf[0], 0x61); + assert.equal(buf[1], 0x62); + assert.equal(buf[2], 0xef); + assert.equal(buf[3], 0xbf); + assert.equal(buf[4], 0xbd); + assert.equal(buf[5], 0x63); + assert.equal(buf[6], 0x64); +} + +{ + // test for buffer overrun + var buf = new Buffer([0, 0, 0, 0, 0]); // length: 5 + var sub = buf.slice(0, 4); // length: 4 + written = sub.write('12345', 'latin1'); + assert.equal(written, 4); + assert.equal(buf[4], 0); + written = sub.write('12345', 'binary'); + assert.equal(written, 4); + assert.equal(buf[4], 0); +} + +// Check for fractional length args, junk length args, etc. +// https://github.com/joyent/node/issues/1758 + +// Call .fill() first, stops valgrind warning about uninitialized memory reads. +Buffer(3.3).fill().toString(); // throws bad argument error in commit 43cb4ec +assert.equal(Buffer(NaN).length, 0); +assert.equal(Buffer(3.3).length, 3); +assert.equal(Buffer({length: 3.3}).length, 3); +assert.equal(Buffer({length: 'BAM'}).length, 0); + +// Make sure that strings are not coerced to numbers. +assert.equal(Buffer('99').length, 2); +assert.equal(Buffer('13.37').length, 5); + +// Ensure that the length argument is respected. +'ascii utf8 hex base64 latin1 binary'.split(' ').forEach(function(enc) { + assert.equal(Buffer(1).write('aaaaaa', 0, 1, enc), 1); +}); + +{ + // Regression test, guard against buffer overrun in the base64 decoder. + var a = Buffer(3); + var b = Buffer('xxx'); + a.write('aaaaaaaa', 'base64'); + assert.equal(b.toString(), 'xxx'); +} + +// issue GH-3416 +Buffer(Buffer(0), 0, 0); + +[ 'hex', + 'utf8', + 'utf-8', + 'ascii', + 'latin1', + 'binary', + 'base64', + 'ucs2', + 'ucs-2', + 'utf16le', + 'utf-16le' ].forEach(function(enc) { + assert.equal(Buffer.isEncoding(enc), true); + }); + +[ 'utf9', + 'utf-7', + 'Unicode-FTW', + 'new gnu gun' ].forEach(function(enc) { + assert.equal(Buffer.isEncoding(enc), false); + }); + + +// GH-5110 +{ + var buffer = new Buffer('test'); + var string = JSON.stringify(buffer); + + assert.strictEqual(string, '{"type":"Buffer","data":[116,101,115,116]}'); + + assert.deepStrictEqual(buffer, JSON.parse(string, function(key, value) { + return value && value.type === 'Buffer' + ? new Buffer(value.data) + : value; + })); +} + +// issue GH-7849 +{ + var buf = new Buffer('test'); + var json = JSON.stringify(buf); + var obj = JSON.parse(json); + var copy = new Buffer(obj); + + assert(buf.equals(copy)); +} + +// issue GH-4331 +assert.throws(function() { + Buffer(0xFFFFFFFF); +}, RangeError); +assert.throws(function() { + Buffer(0xFFFFFFFFF); +}, RangeError); + +// issue GH-5587 +assert.throws(function() { + var buf = new Buffer(8); + buf.writeFloatLE(0, 5); +}, RangeError); +assert.throws(function() { + var buf = new Buffer(16); + buf.writeDoubleLE(0, 9); +}, RangeError); + + +// attempt to overflow buffers, similar to previous bug in array buffers +assert.throws(function() { + var buf = Buffer(8); + buf.readFloatLE(0xffffffff); +}, RangeError); + +assert.throws(function() { + var buf = Buffer(8); + buf.writeFloatLE(0.0, 0xffffffff); +}, RangeError); + +assert.throws(function() { + var buf = Buffer(8); + buf.readFloatLE(0xffffffff); +}, RangeError); + +assert.throws(function() { + var buf = Buffer(8); + buf.writeFloatLE(0.0, 0xffffffff); +}, RangeError); + + +// ensure negative values can't get past offset +assert.throws(function() { + var buf = Buffer(8); + buf.readFloatLE(-1); +}, RangeError); + +assert.throws(function() { + var buf = Buffer(8); + buf.writeFloatLE(0.0, -1); +}, RangeError); + +assert.throws(function() { + var buf = Buffer(8); + buf.readFloatLE(-1); +}, RangeError); + +assert.throws(function() { + var buf = Buffer(8); + buf.writeFloatLE(0.0, -1); +}, RangeError); + +// offset checks +{ + var buf = new Buffer(0); + + assert.throws(function() { buf.readUInt8(0); }, RangeError); + assert.throws(function() { buf.readInt8(0); }, RangeError); +} + +{ + var buf = new Buffer([0xFF]); + + assert.equal(buf.readUInt8(0), 255); + assert.equal(buf.readInt8(0), -1); +} + +[16, 32].forEach(function(bits) { + var buf = new Buffer(bits / 8 - 1); + + assert.throws(function() { buf['readUInt' + bits + 'BE'](0); }, + RangeError, + 'readUInt' + bits + 'BE'); + + assert.throws(function() { buf['readUInt' + bits + 'LE'](0); }, + RangeError, + 'readUInt' + bits + 'LE'); + + assert.throws(function() { buf['readInt' + bits + 'BE'](0); }, + RangeError, + 'readInt' + bits + 'BE()'); + + assert.throws(function() { buf['readInt' + bits + 'LE'](0); }, + RangeError, + 'readInt' + bits + 'LE()'); +}); + +[16, 32].forEach(function(bits) { + var buf = new Buffer([0xFF, 0xFF, 0xFF, 0xFF]); + + assert.equal(buf['readUInt' + bits + 'BE'](0), + (0xFFFFFFFF >>> (32 - bits))); + + assert.equal(buf['readUInt' + bits + 'LE'](0), + (0xFFFFFFFF >>> (32 - bits))); + + assert.equal(buf['readInt' + bits + 'BE'](0), + (0xFFFFFFFF >> (32 - bits))); + + assert.equal(buf['readInt' + bits + 'LE'](0), + (0xFFFFFFFF >> (32 - bits))); +}); + +// test for common read(U)IntLE/BE +{ + var buf = new Buffer([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]); + + assert.strictEqual(buf.readUIntLE(0, 1), 0x01); + assert.strictEqual(buf.readUIntBE(0, 1), 0x01); + assert.strictEqual(buf.readUIntLE(0, 3), 0x030201); + assert.strictEqual(buf.readUIntBE(0, 3), 0x010203); + assert.strictEqual(buf.readUIntLE(0, 5), 0x0504030201); + assert.strictEqual(buf.readUIntBE(0, 5), 0x0102030405); + assert.strictEqual(buf.readUIntLE(0, 6), 0x060504030201); + assert.strictEqual(buf.readUIntBE(0, 6), 0x010203040506); + assert.strictEqual(buf.readIntLE(0, 1), 0x01); + assert.strictEqual(buf.readIntBE(0, 1), 0x01); + assert.strictEqual(buf.readIntLE(0, 3), 0x030201); + assert.strictEqual(buf.readIntBE(0, 3), 0x010203); + assert.strictEqual(buf.readIntLE(0, 5), 0x0504030201); + assert.strictEqual(buf.readIntBE(0, 5), 0x0102030405); + assert.strictEqual(buf.readIntLE(0, 6), 0x060504030201); + assert.strictEqual(buf.readIntBE(0, 6), 0x010203040506); +} + +// test for common write(U)IntLE/BE +{ + var buf = Buffer(3); + buf.writeUIntLE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x56, 0x34, 0x12]); + assert.equal(buf.readUIntLE(0, 3), 0x123456); + + buf = Buffer(3); + buf.writeUIntBE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56]); + assert.equal(buf.readUIntBE(0, 3), 0x123456); + + buf = Buffer(3); + buf.writeIntLE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x56, 0x34, 0x12]); + assert.equal(buf.readIntLE(0, 3), 0x123456); + + buf = Buffer(3); + buf.writeIntBE(0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56]); + assert.equal(buf.readIntBE(0, 3), 0x123456); + + buf = Buffer(3); + buf.writeIntLE(-0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xaa, 0xcb, 0xed]); + assert.equal(buf.readIntLE(0, 3), -0x123456); + + buf = Buffer(3); + buf.writeIntBE(-0x123456, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcb, 0xaa]); + assert.equal(buf.readIntBE(0, 3), -0x123456); + + buf = Buffer(3); + buf.writeIntLE(-0x123400, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x00, 0xcc, 0xed]); + assert.equal(buf.readIntLE(0, 3), -0x123400); + + buf = Buffer(3); + buf.writeIntBE(-0x123400, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcc, 0x00]); + assert.equal(buf.readIntBE(0, 3), -0x123400); + + buf = Buffer(3); + buf.writeIntLE(-0x120000, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0x00, 0x00, 0xee]); + assert.equal(buf.readIntLE(0, 3), -0x120000); + + buf = Buffer(3); + buf.writeIntBE(-0x120000, 0, 3); + assert.deepStrictEqual(buf.toJSON().data, [0xee, 0x00, 0x00]); + assert.equal(buf.readIntBE(0, 3), -0x120000); + + buf = Buffer(5); + buf.writeUIntLE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x90, 0x78, 0x56, 0x34, 0x12]); + assert.equal(buf.readUIntLE(0, 5), 0x1234567890); + + buf = Buffer(5); + buf.writeUIntBE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56, 0x78, 0x90]); + assert.equal(buf.readUIntBE(0, 5), 0x1234567890); + + buf = Buffer(5); + buf.writeIntLE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x90, 0x78, 0x56, 0x34, 0x12]); + assert.equal(buf.readIntLE(0, 5), 0x1234567890); + + buf = Buffer(5); + buf.writeIntBE(0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56, 0x78, 0x90]); + assert.equal(buf.readIntBE(0, 5), 0x1234567890); + + buf = Buffer(5); + buf.writeIntLE(-0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x70, 0x87, 0xa9, 0xcb, 0xed]); + assert.equal(buf.readIntLE(0, 5), -0x1234567890); + + buf = Buffer(5); + buf.writeIntBE(-0x1234567890, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcb, 0xa9, 0x87, 0x70]); + assert.equal(buf.readIntBE(0, 5), -0x1234567890); + + buf = Buffer(5); + buf.writeIntLE(-0x0012000000, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0x00, 0x00, 0x00, 0xee, 0xff]); + assert.equal(buf.readIntLE(0, 5), -0x0012000000); + + buf = Buffer(5); + buf.writeIntBE(-0x0012000000, 0, 5); + assert.deepStrictEqual(buf.toJSON().data, [0xff, 0xee, 0x00, 0x00, 0x00]); + assert.equal(buf.readIntBE(0, 5), -0x0012000000); +} + +// test Buffer slice +{ + var buf = new Buffer('0123456789'); + assert.equal(buf.slice(-10, 10), '0123456789'); + assert.equal(buf.slice(-20, 10), '0123456789'); + assert.equal(buf.slice(-20, -10), ''); + assert.equal(buf.slice(), '0123456789'); + assert.equal(buf.slice(0), '0123456789'); + assert.equal(buf.slice(0, 0), ''); + assert.equal(buf.slice(undefined), '0123456789'); + assert.equal(buf.slice('foobar'), '0123456789'); + assert.equal(buf.slice(undefined, undefined), '0123456789'); + + assert.equal(buf.slice(2), '23456789'); + assert.equal(buf.slice(5), '56789'); + assert.equal(buf.slice(10), ''); + assert.equal(buf.slice(5, 8), '567'); + assert.equal(buf.slice(8, -1), '8'); + assert.equal(buf.slice(-10), '0123456789'); + assert.equal(buf.slice(0, -9), '0'); + assert.equal(buf.slice(0, -10), ''); + assert.equal(buf.slice(0, -1), '012345678'); + assert.equal(buf.slice(2, -2), '234567'); + assert.equal(buf.slice(0, 65536), '0123456789'); + assert.equal(buf.slice(65536, 0), ''); + assert.equal(buf.slice(-5, -8), ''); + assert.equal(buf.slice(-5, -3), '56'); + assert.equal(buf.slice(-10, 10), '0123456789'); + for (var i = 0, s = buf.toString(); i < buf.length; ++i) { + assert.equal(buf.slice(i), s.slice(i)); + assert.equal(buf.slice(0, i), s.slice(0, i)); + assert.equal(buf.slice(-i), s.slice(-i)); + assert.equal(buf.slice(0, -i), s.slice(0, -i)); + } + + var utf16Buf = new Buffer('0123456789', 'utf16le'); + assert.deepStrictEqual(utf16Buf.slice(0, 6), Buffer('012', 'utf16le')); + + assert.equal(buf.slice('0', '1'), '0'); + assert.equal(buf.slice('-5', '10'), '56789'); + assert.equal(buf.slice('-10', '10'), '0123456789'); + assert.equal(buf.slice('-10', '-5'), '01234'); + assert.equal(buf.slice('-10', '-0'), ''); + assert.equal(buf.slice('111'), ''); + assert.equal(buf.slice('0', '-111'), ''); + + // try to slice a zero length Buffer + // see https://github.com/joyent/node/issues/5881 + SlowBuffer(0).slice(0, 1); +} + +// Regression test for #5482: should throw but not assert in C++ land. +assert.throws(function() { + Buffer('', 'buffer'); +}, TypeError); + +// Regression test for #6111. Constructing a buffer from another buffer +// should a) work, and b) not corrupt the source buffer. +{ + var a = [0]; + for (var i = 0; i < 7; ++i) a = a.concat(a); + a = a.map(function(_, i) { return i; }); + var b = Buffer(a); + var c = Buffer(b); + assert.strictEqual(b.length, a.length); + assert.strictEqual(c.length, a.length); + for (var i = 0, k = a.length; i < k; ++i) { + assert.strictEqual(a[i], i); + assert.strictEqual(b[i], i); + assert.strictEqual(c[i], i); + } +} + + +assert.throws(function() { + new Buffer((-1 >>> 0) + 1); +}, RangeError); + +assert.throws(function() { + SlowBuffer((-1 >>> 0) + 1); +}, RangeError); + +if (common.hasCrypto) { + // Test truncation after decode + // var crypto = require('crypto'); + + var b1 = new Buffer('YW55=======', 'base64'); + var b2 = new Buffer('YW55', 'base64'); + + assert.equal( + 1 /*crypto.createHash('sha1').update(b1).digest('hex')*/, + 1 /*crypto.createHash('sha1').update(b2).digest('hex')*/ + ); +} else { + common.skip('missing crypto'); +} + +// Test Compare +{ + var b = new Buffer(1).fill('a'); + var c = new Buffer(1).fill('c'); + var d = new Buffer(2).fill('aa'); + + assert.equal(b.compare(c), -1); + assert.equal(c.compare(d), 1); + assert.equal(d.compare(b), 1); + assert.equal(b.compare(d), -1); + assert.equal(b.compare(b), 0); + + assert.equal(Buffer.compare(b, c), -1); + assert.equal(Buffer.compare(c, d), 1); + assert.equal(Buffer.compare(d, b), 1); + assert.equal(Buffer.compare(b, d), -1); + assert.equal(Buffer.compare(c, c), 0); + + assert.equal(Buffer.compare(Buffer(0), Buffer(0)), 0); + assert.equal(Buffer.compare(Buffer(0), Buffer(1)), -1); + assert.equal(Buffer.compare(Buffer(1), Buffer(0)), 1); +} + +assert.throws(function() { + var b = Buffer(1); + Buffer.compare(b, 'abc'); +}); + +assert.throws(function() { + var b = Buffer(1); + Buffer.compare('abc', b); +}); + +assert.throws(function() { + var b = Buffer(1); + b.compare('abc'); +}); + +// Test Equals +{ + var b = new Buffer(5).fill('abcdf'); + var c = new Buffer(5).fill('abcdf'); + var d = new Buffer(5).fill('abcde'); + var e = new Buffer(6).fill('abcdef'); + + assert.ok(b.equals(c)); + assert.ok(!c.equals(d)); + assert.ok(!d.equals(e)); + assert.ok(d.equals(d)); +} + +assert.throws(function() { + var b = Buffer(1); + b.equals('abc'); +}); + +// Regression test for https://github.com/nodejs/node/issues/649. +assert.throws(function() { Buffer(1422561062959).toString('utf8'); }); + +{ + // Test that large negative Buffer length inputs don't affect the pool offset. + // Use the fromArrayLike() variant here because it's more lenient + // about its input and passes the length directly to allocate(). + assert.deepStrictEqual(Buffer({ length: -Buffer.poolSize }), Buffer.from('')); + assert.deepStrictEqual(Buffer({ length: -100 }), Buffer.from('')); + + // Check pool offset after that by trying to write string into the pool. + assert.doesNotThrow(() => Buffer.from('abc')); +} + + +// Test failed or zero-sized Buffer allocations not affecting typed arrays +{ + var zeroArray = new Uint32Array(10).fill(0); + var sizes = [1e10, 0, 0.1, -1, 'a', undefined, null, NaN]; + var allocators = [ + Buffer, + SlowBuffer, + Buffer.alloc, + Buffer.allocUnsafe, + Buffer.allocUnsafeSlow + ]; + for (var allocator of allocators) { + for (var size of sizes) { + try { + allocator(size); + } catch (e) { + assert.deepStrictEqual(new Uint32Array(10), zeroArray); + } + } + } +} + +// Test that large negative Buffer length inputs throw errors. +assert.throws(() => Buffer(-Buffer.poolSize), + '"size" argument must not be negative'); +assert.throws(() => Buffer(-100), + '"size" argument must not be negative'); +assert.throws(() => Buffer(-1), + '"size" argument must not be negative'); + diff --git a/modules/parquet/test/buffer-polyfill/write-infinity.spec.js b/modules/parquet/test/buffer-polyfill/write-infinity.spec.js new file mode 100644 index 0000000000..6dc3606898 --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/write-infinity.spec.js @@ -0,0 +1,47 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; + +test('write/read Infinity as a float', function (t) { + const buf = new BufferPolyfill(4) + t.equal(buf.writeFloatBE(Infinity, 0), 4) + t.equal(buf.readFloatBE(0), Infinity) + t.end() +}) + +test('write/read -Infinity as a float', function (t) { + const buf = new BufferPolyfill(4) + t.equal(buf.writeFloatBE(-Infinity, 0), 4) + t.equal(buf.readFloatBE(0), -Infinity) + t.end() +}) + +test('write/read Infinity as a double', function (t) { + const buf = new BufferPolyfill(8) + t.equal(buf.writeDoubleBE(Infinity, 0), 8) + t.equal(buf.readDoubleBE(0), Infinity) + t.end() +}) + +test('write/read -Infinity as a double', function (t) { + const buf = new BufferPolyfill(8) + t.equal(buf.writeDoubleBE(-Infinity, 0), 8) + t.equal(buf.readDoubleBE(0), -Infinity) + t.end() +}) + +test('write/read float greater than max', function (t) { + const buf = new BufferPolyfill(4) + t.equal(buf.writeFloatBE(4e38, 0), 4) + t.equal(buf.readFloatBE(0), Infinity) + t.end() +}) + +test('write/read float less than min', function (t) { + const buf = new BufferPolyfill(4) + t.equal(buf.writeFloatBE(-4e40, 0), 4) + t.equal(buf.readFloatBE(0), -Infinity) + t.end() +}) diff --git a/modules/parquet/test/buffer-polyfill/write.spec.js b/modules/parquet/test/buffer-polyfill/write.spec.js new file mode 100644 index 0000000000..30a32b843c --- /dev/null +++ b/modules/parquet/test/buffer-polyfill/write.spec.js @@ -0,0 +1,130 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// This file is forked from https://github.com/feross/buffer under MIT license +import test from 'tape-promise/tape'; +import {BufferPolyfill} from '@loaders.gl/parquet'; +// const isnan = require('is-nan') + +test('buffer.write string should get parsed as number', function (t) { + const b = new BufferPolyfill(64) + // @ts-expect-error + b.writeUInt16LE('1003', 0) + t.equal(b.readUInt16LE(0), 1003) + t.end() +}) + +test('buffer.writeUInt8 a fractional number will get Math.floored', function (t) { + // Some extra work is necessary to make this test pass with the Object implementation + + const b = new BufferPolyfill(1) + b.writeInt8(5.5, 0) + t.equal(b[0], 5) + t.end() +}) + +test('writeUint8 with a negative number throws', function (t) { + const buf = new BufferPolyfill(1) + + t.throws(function () { + buf.writeUInt8(-3, 0) + }) + + t.end() +}) + +test('hex of write{Uint,Int}{8,16,32}{LE,BE}', function (t) { + t.plan(2 * ((2 * 2 * 2) + 2)) + const hex = [ + '03', '0300', '0003', '03000000', '00000003', + 'fd', 'fdff', 'fffd', 'fdffffff', 'fffffffd' + ] + const reads = [3, 3, 3, 3, 3, -3, -3, -3, -3, -3] + const xs = ['UInt', 'Int'] + const ys = [8, 16, 32] + for (let i = 0; i < xs.length; i++) { + const x = xs[i] + for (let j = 0; j < ys.length; j++) { + const y = ys[j] + const endianesses = (y === 8) ? [''] : ['LE', 'BE'] + for (let k = 0; k < endianesses.length; k++) { + const z = endianesses[k] + + const v1 = new BufferPolyfill(y / 8) + const writefn = `write${ x }${y }${z}` + const val = (x === 'Int') ? -3 : 3 + v1[writefn](val, 0) + t.equal( + v1.toString('hex'), + hex.shift() + ) + const readfn = `read${ x }${y }${z}` + t.equal( + v1[readfn](0), + reads.shift() + ) + } + } + } + t.end() +}) +/* +test('hex of write{Uint,Int}{8,16,32}{LE,BE} with overflow', function (t) { + t.plan(3 * ((2 * 2 * 2) + 2)) + const hex = [ + '', '03', '00', '030000', '000000', + '', 'fd', 'ff', 'fdffff', 'ffffff' + ] + const reads = [ + undefined, 3, 0, NaN, 0, + undefined, 253, -256, 16777213, -256 + ] + const xs = ['UInt', 'Int'] + const ys = [8, 16, 32] + for (let i = 0; i < xs.length; i++) { + const x = xs[i] + for (let j = 0; j < ys.length; j++) { + const y = ys[j] + const endianesses = (y === 8) ? [''] : ['LE', 'BE'] + for (let k = 0; k < endianesses.length; k++) { + const z = endianesses[k] + + const v1 = new BufferPolyfill((y / 8) - 1) + const next = new BufferPolyfill(4) + next.writeUInt32BE(0, 0) + const writefn = 'write' + x + y + z + const val = (x === 'Int') ? -3 : 3 + v1[writefn](val, 0, true) + t.equal( + v1.toString('hex'), + hex.shift() + ) + // check that nothing leaked to next buffer. + t.equal(next.readUInt32BE(0), 0) + // check that no bytes are read from next buffer. + next.writeInt32BE(~0, 0) + const readfn = 'read' + x + y + z + const r = reads.shift() + if (isnan(r)) t.pass('equal') + else t.equal(v1[readfn](0, true), r) + } + } + } + t.end() +}) +*/ + +test('large values do not improperly roll over (ref #80)', function (t) { + const nums = [-25589992, -633756690, -898146932] + const out = new BufferPolyfill(12) + out.fill(0) + out.writeInt32BE(nums[0], 0) + let newNum = out.readInt32BE(0) + t.equal(nums[0], newNum) + out.writeInt32BE(nums[1], 4) + newNum = out.readInt32BE(4) + t.equal(nums[1], newNum) + out.writeInt32BE(nums[2], 8) + newNum = out.readInt32BE(8) + t.equal(nums[2], newNum) + t.end() +}) diff --git a/modules/parquet/test/geoparquet-loader.spec.ts b/modules/parquet/test/geoparquet-loader.spec.ts index ac34f8bc96..e7b5ebed60 100644 --- a/modules/parquet/test/geoparquet-loader.spec.ts +++ b/modules/parquet/test/geoparquet-loader.spec.ts @@ -1,30 +1,50 @@ import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; -import {ParquetColumnarLoader, _ParquetWriter as ParquetWriter} from '@loaders.gl/parquet'; +import { + ParquetColumnarLoader, + ParquetLoader, + _ParquetWriter as ParquetWriter +} from '@loaders.gl/parquet'; import {load, encode, setLoaderOptions} from '@loaders.gl/core'; import {getTableLength} from '@loaders.gl/schema'; -import {Table as ArrowTable, makeVector, vectorFromArray, Bool, Utf8} from 'apache-arrow'; +import * as arrow from 'apache-arrow'; const PARQUET_DIR = '@loaders.gl/parquet/test/data/geoparquet'; const GEOPARQUET_EXAMPLE = `${PARQUET_DIR}/example.parquet`; -const GEOPARQUET_FILES = [ - 'example.parquet', - 'airports.parquet', - 'geojson-big.parquet' -]; +const GEOPARQUET_FILES = ['example.parquet', 'airports.parquet', 'geojson-big.parquet']; // Use local workers setLoaderOptions({_workerType: 'test'}); +test.skip('Load GeoParquet#airports.parquet', async (t) => { + const table = await load(`${PARQUET_DIR}/airports.parquet`, ParquetLoader, { + worker: false, + parquet: { + shape: 'geojson-table', + preserveBinary: true + } + }); + + t.equal(table.shape, 'geojson-table'); + t.equal(getTableLength(table), 1000); + t.deepEqual( + table.schema?.fields.map((f) => f.name), + ['cartodb_id', 'gps_code', 'name', 'geom'] + ); + t.end(); +}); + test('Load GeoParquet file', async (t) => { const table = await load(GEOPARQUET_EXAMPLE, ParquetColumnarLoader, {worker: false}); t.equal(getTableLength(table), 5); - t.deepEqual(table.schema?.fields.map(f => f.name), - [ 'pop_est', 'continent', 'name', 'iso_a3', 'gdp_md_est', 'geometry' ]); + t.deepEqual( + table.schema?.fields.map((f) => f.name), + ['pop_est', 'continent', 'name', 'iso_a3', 'gdp_md_est', 'geometry'] + ); t.end(); -}) +}); test.skip('GeoParquetColumnarLoader#load', async (t) => { t.comment('SUPPORTED FILES'); @@ -35,7 +55,7 @@ test.skip('GeoParquetColumnarLoader#load', async (t) => { } t.end(); -}) +}); test.skip('ParquetWriterLoader round trip', async (t) => { const table = createArrowTable(); @@ -45,18 +65,18 @@ test.skip('ParquetWriterLoader round trip', async (t) => { t.deepEqual(table.schema, newTable.schema); t.end(); -}) +}); -function createArrowTable(): ArrowTable { - const utf8Vector = vectorFromArray(['a', 'b', 'c', 'd']); - const boolVector = makeVector({data: [1, 1, 0, 0], type: new Bool()}); - const uint8Vector = makeVector(new Uint8Array([1, 2, 3, 4])); - const int32Vector = makeVector(new Int32Array([0, -2147483638, 2147483637, 1])); +function createArrowTable(): arrow.Table { + const utf8Vector = arrow.vectorFromArray(['a', 'b', 'c', 'd']); + const boolVector = arrow.makeVector({data: [1, 1, 0, 0], type: new arrow.Bool()}); + const uint8Vector = arrow.makeVector(new Uint8Array([1, 2, 3, 4])); + const int32Vector = arrow.makeVector(new Int32Array([0, -2147483638, 2147483637, 1])); - const table = new ArrowTable({ + const table = new arrow.Table({ str: utf8Vector, - uint8: uint8Vector, - int32: int32Vector, + uint8: uint8Vector, + int32: int32Vector, bool: boolVector }); return table; diff --git a/modules/parquet/test/index.ts b/modules/parquet/test/index.ts index f7a87e434a..d4602cf5cc 100644 --- a/modules/parquet/test/index.ts +++ b/modules/parquet/test/index.ts @@ -8,16 +8,14 @@ import './parquetjs/shred.spec'; import './parquetjs/thrift.spec'; import './parquetjs/reader.spec'; -// The integration spec runs tens of thousands of detailed tests. Too slow for CI, uncomment to run. +// // The integration spec runs tens of thousands of detailed tests. Too slow for CI, uncomment to run. // import './parquetjs/integration.spec'; -// loader/writer +// // loader/writer import './parquet-loader.spec'; import './parquet-writer.spec'; - -import './parquet-wasm-loader.spec'; +import './geoparquet-loader.spec'; import './parquet-columnar-loader.spec'; - -import './geoparquet-loader.spec'; +// import './parquet-wasm-loader.spec'; diff --git a/modules/parquet/test/init.ts b/modules/parquet/test/init.ts index a7ce9b57d6..aaaf69bcfa 100644 --- a/modules/parquet/test/init.ts +++ b/modules/parquet/test/init.ts @@ -1,4 +1,5 @@ import {preloadCompressions} from '@loaders.gl/parquet'; +import {installBufferPolyfill} from '@loaders.gl/parquet'; // Import big dependencies @@ -7,6 +8,8 @@ import brotliDecompress from 'brotli/decompress'; // import lzo from 'lzo'; +installBufferPolyfill(); + // Inject large dependencies through Compression constructor options const modules = { // brotli has problems with decompress in browsers diff --git a/modules/parquet/test/parquet-loader.spec.ts b/modules/parquet/test/parquet-loader.spec.ts index b152d37024..7a21b5b5f0 100644 --- a/modules/parquet/test/parquet-loader.spec.ts +++ b/modules/parquet/test/parquet-loader.spec.ts @@ -41,8 +41,11 @@ test('ParquetLoader#load alltypes_dictionary file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/alltypes_dictionary.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 2); - t.deepEqual(table.data, ALL_TYPES_DICTIONARY_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 2); + t.deepEqual(table.data, ALL_TYPES_DICTIONARY_EXPECTED); + } t.end(); }); @@ -50,8 +53,11 @@ test('ParquetLoader#load alltypes_plain file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/alltypes_plain.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 8); - t.deepEqual(table.data, ALL_TYPES_PLAIN_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 8); + t.deepEqual(table.data, ALL_TYPES_PLAIN_EXPECTED); + } t.end(); }); @@ -59,8 +65,11 @@ test('ParquetLoader#load alltypes_plain_snappy file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/alltypes_plain.snappy.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 2); - t.deepEqual(table.data, ALL_TYPES_PLAIN_SNAPPY_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 2); + t.deepEqual(table.data, ALL_TYPES_PLAIN_SNAPPY_EXPECTED); + } t.end(); }); @@ -68,8 +77,11 @@ test('ParquetLoader#load binary file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/binary.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 12); - t.deepEqual(table.data, BINARY_EXPECTED()); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 12); + t.deepEqual(table.data, BINARY_EXPECTED()); + } t.end(); }); @@ -77,8 +89,11 @@ test('ParquetLoader#load binary file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/binary.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 12); - t.deepEqual(table.data, BINARY_EXPECTED()); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 12); + t.deepEqual(table.data, BINARY_EXPECTED()); + } t.end(); }); @@ -86,8 +101,11 @@ test('ParquetLoader#load dict file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/dict-page-offset-zero.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 39); - t.deepEqual(table.data, DICT_EXPECTED()); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 39); + t.deepEqual(table.data, DICT_EXPECTED()); + } t.end(); }); @@ -95,8 +113,11 @@ test('ParquetLoader#load list_columns file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/list_columns.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 3); - t.deepEqual(table.data, LIST_COLUMNS_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 3); + t.deepEqual(table.data, LIST_COLUMNS_EXPECTED); + } t.end(); }); @@ -106,7 +127,10 @@ test('ParquetLoader#load nation file', async (t) => { const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); t.ok(table); - t.equal(table.data.length, 25); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 25); + } t.end(); }); @@ -114,8 +138,11 @@ test('ParquetLoader#load nested_lists file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/nested_lists.snappy.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 3); - t.deepEqual(table.data, NESTED_LIST_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 3); + t.deepEqual(table.data, NESTED_LIST_EXPECTED); + } t.end(); }); @@ -123,8 +150,11 @@ test('ParquetLoader#load nested_maps file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/nested_maps.snappy.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 6); - t.deepEqual(table.data, NESTED_MAPS_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 6); + t.deepEqual(table.data, NESTED_MAPS_EXPECTED); + } t.end(); }); @@ -132,8 +162,11 @@ test('ParquetLoader#load nonnullable file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/nonnullable.impala.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 1); - t.deepEqual(table.data, NO_NULLABLE_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 1); + t.deepEqual(table.data, NO_NULLABLE_EXPECTED); + } t.end(); }); @@ -141,8 +174,11 @@ test('ParquetLoader#load nullable file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/nullable.impala.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 7); - t.deepEqual(table.data, NULLABLE_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 7); + t.deepEqual(table.data, NULLABLE_EXPECTED); + } t.end(); }); @@ -150,8 +186,11 @@ test('ParquetLoader#load nulls file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/nulls.snappy.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 8); - t.deepEqual(table.data, NULLS_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 8); + t.deepEqual(table.data, NULLS_EXPECTED); + } t.end(); }); @@ -165,9 +204,12 @@ test('ParquetLoader#decimal files', async (t) => { ]; for (const url of urls) { const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.deepEqual(table.data, DECIMAL_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.deepEqual(table.data, DECIMAL_EXPECTED); + } } - + t.end(); }); @@ -175,18 +217,22 @@ test('ParquetLoader#load repeated_no_annotation file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/repeated_no_annotation.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 6); - t.deepEqual(table.data, REPEATED_NO_ANNOTATION_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 6); + t.deepEqual(table.data, REPEATED_NO_ANNOTATION_EXPECTED); + } t.end(); }); test('ParquetLoader#load lz4_raw_compressed file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/lz4_raw_compressed.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - - - t.equal(table.data.length, 4); - t.deepEqual(table.data, LZ4_RAW_COMPRESSED_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 4); + t.deepEqual(table.data, LZ4_RAW_COMPRESSED_EXPECTED); + } t.end(); }); @@ -194,10 +240,13 @@ test('ParquetLoader#load lz4_raw_compressed_larger file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/lz4_raw_compressed_larger.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 10000); - // Compare only first and last items in data because file is huge. - t.deepEqual(table.data[0], LZ4_RAW_COMPRESSED_LARGER_FIRST_EXPECTED); - t.deepEqual(table.data[9999], LZ4_RAW_COMPRESSED_LARGER_LAST_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 10000); + // Compare only first and last items in data because file is huge. + t.deepEqual(table.data[0], LZ4_RAW_COMPRESSED_LARGER_FIRST_EXPECTED); + t.deepEqual(table.data[9999], LZ4_RAW_COMPRESSED_LARGER_LAST_EXPECTED); + } t.end(); }); @@ -205,13 +254,15 @@ test('ParquetLoader#load non_hadoop_lz4_compressed file', async (t) => { const url = '@loaders.gl/parquet/test/data/apache/good/non_hadoop_lz4_compressed.parquet'; const table = await load(url, ParquetLoader, {parquet: {url}, worker: false}); - t.equal(table.data.length, 4); - t.deepEqual(table.data, NON_HADOOP_LZ4_COMPRESSED_EXPECTED); + t.equal(table.shape, 'object-row-table'); + if (table.shape === 'object-row-table') { + t.equal(table.data.length, 4); + t.deepEqual(table.data, NON_HADOOP_LZ4_COMPRESSED_EXPECTED); + } t.end(); }); test('ParquetLoader#load', async (t) => { - // Buffer is not defined issue in worker thread of browser. if (!isBrowser) { t.comment('SUPPORTED FILES with worker'); diff --git a/modules/parquet/test/parquet-wasm-loader.spec.ts b/modules/parquet/test/parquet-wasm-loader.spec.ts.disabled similarity index 54% rename from modules/parquet/test/parquet-wasm-loader.spec.ts rename to modules/parquet/test/parquet-wasm-loader.spec.ts.disabled index f1125d42f0..02a4e043a0 100644 --- a/modules/parquet/test/parquet-wasm-loader.spec.ts +++ b/modules/parquet/test/parquet-wasm-loader.spec.ts.disabled @@ -1,9 +1,10 @@ import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; -import {ParquetWasmLoader, ParquetWasmWriter} from '@loaders.gl/parquet'; import {load, encode, setLoaderOptions} from '@loaders.gl/core'; -import {Table, vectorFromArray, Utf8, Bool, Uint8, Uint32} from 'apache-arrow'; +import {ArrowTable} from '@loaders.gl/arrow'; +import {ParquetWasmLoader, ParquetWasmWriter} from '@loaders.gl/parquet'; +import * as arrow from 'apache-arrow'; import {WASM_SUPPORTED_FILES} from './data/files'; const PARQUET_DIR = '@loaders.gl/parquet/test/data'; @@ -22,57 +23,66 @@ test('ParquetLoader#loader objects', (t) => { test('Load Parquet file', async (t) => { const url = `${PARQUET_DIR}/geoparquet/example.parquet`; - const table: Table = await load(url, ParquetWasmLoader, { + const table = await load(url, ParquetWasmLoader, { parquet: { wasmUrl: WASM_URL } }); - - t.equal(table.numRows, 5); - t.deepEqual(table.schema.fields.map(f => f.name), - [ 'pop_est', 'continent', 'name', 'iso_a3', 'gdp_md_est', 'geometry' ]); + const arrowTable = table.data; + t.equal(arrowTable.numRows, 5); + t.deepEqual(table.schema?.fields.map((f) => f.name), [ + 'pop_est', + 'continent', + 'name', + 'iso_a3', + 'gdp_md_est', + 'geometry' + ]); t.end(); -}) +}); test('ParquetWasmLoader#load', async (t) => { t.comment('SUPPORTED FILES'); for (const {title, path} of WASM_SUPPORTED_FILES) { const url = `${PARQUET_DIR}/apache/${path}`; - const data = await load(url, ParquetWasmLoader, { + const table = await load(url, ParquetWasmLoader, { parquet: { wasmUrl: WASM_URL } }); - t.ok(data, `GOOD(${title})`); + const arrowTable = table.data; + t.ok(arrowTable, `GOOD(${title})`); } t.end(); -}) +}); -test('ParquetWasmWriterLoader round trip', async (t) => { +test('ParquetWasmWriter#writer/loader round trip', async (t) => { const table = createArrowTable(); - const parquetBuffer = await encode(table, ParquetWasmWriter, {worker: false, + const parquetBuffer = await encode(table, ParquetWasmWriter, { + worker: false, parquet: { wasmUrl: WASM_URL } }); - const newTable = await load(parquetBuffer, ParquetWasmLoader, {worker: false, + const newTable = await load(parquetBuffer, ParquetWasmLoader, { + worker: false, parquet: { wasmUrl: WASM_URL } }); - t.deepEqual(table.schema, newTable.schema); + t.deepEqual(table.data.schema, newTable.data.schema); t.end(); -}) +}); -function createArrowTable() { - const utf8Vector = vectorFromArray(['a', 'b', 'c', 'd'], new Utf8); - const boolVector = vectorFromArray([true, true, false, false], new Bool) - const uint8Vector = vectorFromArray([1, 2, 3, 4], new Uint8) - const int32Vector = vectorFromArray([0, -2147483638, 2147483637, 1], new Uint32) +function createArrowTable(): ArrowTable { + const utf8Vector = arrow.vectorFromArray(['a', 'b', 'c', 'd'], new arrow.Utf8()); + const boolVector = arrow.vectorFromArray([true, true, false, false], new arrow.Bool()); + const uint8Vector = arrow.vectorFromArray([1, 2, 3, 4], new arrow.Uint8()); + const int32Vector = arrow.vectorFromArray([0, -2147483638, 2147483637, 1], new arrow.Uint32()); - const table = new Table({utf8Vector, uint8Vector, int32Vector, boolVector}); - return table; + const table = new arrow.Table({utf8Vector, uint8Vector, int32Vector, boolVector}); + return {shape: 'arrow-table', data: table}; } diff --git a/modules/parquet/test/parquetjs/integration.spec.ts b/modules/parquet/test/parquetjs/integration.spec.ts index 057e386d86..e9769b223a 100644 --- a/modules/parquet/test/parquetjs/integration.spec.ts +++ b/modules/parquet/test/parquetjs/integration.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable camelcase */ import test, {Test} from 'tape-promise/tape'; import {isBrowser, fetchFile} from '@loaders.gl/core'; -import {makeReadableFile} from '@loaders.gl/loader-utils'; +import {BlobFile} from '@loaders.gl/loader-utils'; import {ParquetSchema, ParquetReader, ParquetEncoder} from '@loaders.gl/parquet'; const FRUITS_URL = '@loaders.gl/parquet/test/data/fruits.parquet'; @@ -115,7 +115,7 @@ async function writeTestFile(opts) { async function readTestFile(t: Test) { const response = await fetchFile(FRUITS_URL); const arrayBuffer = await response.arrayBuffer(); - const reader = new ParquetReader(makeReadableFile(arrayBuffer)); + const reader = new ParquetReader(new BlobFile(arrayBuffer)); t.equal(reader.getRowCount(), TEST_NUM_ROWS * 4); t.deepEqual(reader.getSchemaMetadata(), {myuid: '420', fnord: 'dronf'}); diff --git a/modules/parquet/test/parquetjs/reader.spec.ts b/modules/parquet/test/parquetjs/reader.spec.ts index 25e197d6bd..2ff793f3a2 100644 --- a/modules/parquet/test/parquetjs/reader.spec.ts +++ b/modules/parquet/test/parquetjs/reader.spec.ts @@ -1,6 +1,6 @@ /* eslint-disable camelcase */ import test from 'tape-promise/tape'; -import {makeReadableFile} from '@loaders.gl/loader-utils'; +import {BlobFile} from '@loaders.gl/loader-utils'; import {ParquetReader} from '@loaders.gl/parquet'; import {fetchFile} from '@loaders.gl/core'; @@ -12,7 +12,7 @@ const FRUITS_URL = '@loaders.gl/parquet/test/data/fruits.parquet'; test('ParquetReader#fruits.parquet', async t => { const response = await fetchFile(FRUITS_URL); const arrayBuffer = await response.arrayBuffer(); - const reader = new ParquetReader(makeReadableFile(arrayBuffer)); + const reader = new ParquetReader(new BlobFile(arrayBuffer)); // t.equal(reader.getRowCount(), TEST_NUM_ROWS * 4, 'rowCount'); const metadata = await reader.getSchemaMetadata(); diff --git a/modules/parquet/test/parquetjs/thrift.spec.ts b/modules/parquet/test/parquetjs/thrift.spec.ts index 9cbc3fa592..0ea0347f91 100644 --- a/modules/parquet/test/parquetjs/thrift.spec.ts +++ b/modules/parquet/test/parquetjs/thrift.spec.ts @@ -4,7 +4,8 @@ import test from 'tape-promise/tape'; import * as parquetThrift from '@loaders.gl/parquet/parquetjs/parquet-thrift'; import {serializeThrift} from '@loaders.gl/parquet/parquetjs/utils/read-utils'; -test('thrift#should correctly en/decode literal zeroes with the CompactProtocol', assert => { +// TODO v4 disabled because of Node.js Buffer dependency +test.skip('thrift#should correctly en/decode literal zeroes with the CompactProtocol', assert => { const obj = new parquetThrift.ColumnMetaData({ type: parquetThrift.Type.BOOLEAN, path_in_schema: ['test'], diff --git a/modules/parquet/tsconfig.json b/modules/parquet/tsconfig.json index a3ffdb067f..d7951c495c 100644 --- a/modules/parquet/tsconfig.json +++ b/modules/parquet/tsconfig.json @@ -8,8 +8,11 @@ "outDir": "dist" }, "references": [ + {"path": "../arrow"}, {"path": "../compression"}, {"path": "../loader-utils"}, - {"path": "../schema"} + {"path": "../schema"}, + {"path": "../gis"}, + {"path": "../wkt"} ] } diff --git a/modules/pcd/package.json b/modules/pcd/package.json index 5f32e74c91..a81ad8c536 100644 --- a/modules/pcd/package.json +++ b/modules/pcd/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/pcd", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loader for the PCD format", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "PCD" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -29,13 +37,13 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-worker && npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-worker && npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/pcd-worker.ts --bundle --outfile=dist/pcd-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13" + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/pcd/src/bundle.ts b/modules/pcd/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/pcd/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/pcd/src/index.ts b/modules/pcd/src/index.ts index 6b4552c458..6ccd9f3c99 100644 --- a/modules/pcd/src/index.ts +++ b/modules/pcd/src/index.ts @@ -13,5 +13,3 @@ export const PCDLoader: LoaderWithParser = { parse: async (arrayBuffer) => parsePCDSync(arrayBuffer), parseSync: parsePCDSync }; - -export const _typecheckPCDLoader: LoaderWithParser = PCDLoader; diff --git a/modules/pcd/src/pcd-loader.ts b/modules/pcd/src/pcd-loader.ts index b54d13c1a9..dd5d042c3a 100644 --- a/modules/pcd/src/pcd-loader.ts +++ b/modules/pcd/src/pcd-loader.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Loader, LoaderOptions} from '@loaders.gl/loader-utils'; import {PCDMesh} from './lib/pcd-types'; diff --git a/modules/ply/package.json b/modules/ply/package.json index a89db529df..057f8ac0ff 100644 --- a/modules/ply/package.json +++ b/modules/ply/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/ply", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loader for the PLY format", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "PLY" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -29,14 +37,14 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-worker && npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-worker && npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/ply-worker.ts --bundle --outfile=dist/ply-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { "@babel/runtime": "^7.3.1", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13" + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/ply/src/bundle.ts b/modules/ply/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/ply/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/ply/src/index.ts b/modules/ply/src/index.ts index b62c47f9b0..3e9137c41c 100644 --- a/modules/ply/src/index.ts +++ b/modules/ply/src/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; import type {PLYMesh} from './lib/ply-types'; diff --git a/modules/ply/src/lib/ply-types.ts b/modules/ply/src/lib/ply-types.ts index d63ee86582..81cf299140 100644 --- a/modules/ply/src/lib/ply-types.ts +++ b/modules/ply/src/lib/ply-types.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Mesh} from '@loaders.gl/schema'; /** A parsed PLY mesh */ diff --git a/modules/ply/src/ply-loader.ts b/modules/ply/src/ply-loader.ts index 1ccc175e6f..ab918665cd 100644 --- a/modules/ply/src/ply-loader.ts +++ b/modules/ply/src/ply-loader.ts @@ -27,5 +27,3 @@ export const PLYLoader: Loader = { ply: {} } }; - -export const _typecheckPLYLoader: Loader = PLYLoader; diff --git a/modules/pmtiles/README.md b/modules/pmtiles/README.md new file mode 100644 index 0000000000..b4ce9f83aa --- /dev/null +++ b/modules/pmtiles/README.md @@ -0,0 +1,7 @@ +# @loaders.gl/pmtiles + +[loaders.gl](https://loaders.gl/docs) is a collection of framework-independent 3D and geospatial parsers and encoders. + +This module contains loaders for the pmtiles format. + +For documentation please visit the [website](https://loaders.gl). diff --git a/modules/pmtiles/package.json b/modules/pmtiles/package.json new file mode 100644 index 0000000000..5eba235ab8 --- /dev/null +++ b/modules/pmtiles/package.json @@ -0,0 +1,50 @@ +{ + "name": "@loaders.gl/pmtiles", + "version": "4.0.3", + "description": "Framework-independent loader for the pmtiles format", + "license": "MIT", + "type": "module", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/visgl/loaders.gl" + }, + "keywords": [ + "webgl", + "loader", + "3d", + "mesh", + "point cloud", + "PCD" + ], + "types": "dist/index.d.ts", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, + "sideEffects": false, + "files": [ + "src", + "dist", + "README.md" + ], + "scripts": { + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" + }, + "dependencies": { + "@loaders.gl/images": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/mvt": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "pmtiles": "^2.7.2" + }, + "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" +} diff --git a/modules/pmtiles/src/index.ts b/modules/pmtiles/src/index.ts new file mode 100644 index 0000000000..d2dd7fb5f9 --- /dev/null +++ b/modules/pmtiles/src/index.ts @@ -0,0 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +export type {PMTilesMetadata} from './lib/parse-pmtiles'; +export type {PMTilesSourceProps} from './pmtiles-source'; +export {PMTilesSource} from './pmtiles-source'; diff --git a/modules/pmtiles/src/lib/blob-source.ts b/modules/pmtiles/src/lib/blob-source.ts new file mode 100644 index 0000000000..80077a15c0 --- /dev/null +++ b/modules/pmtiles/src/lib/blob-source.ts @@ -0,0 +1,40 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import * as pmtiles from 'pmtiles'; + +/** + * A PMTiles library compatible source that reads from blobs + * @deprecated TODO - reimplement as ReadableFileSource + * Use loaders.gl HTTP range requests instead + */ +export class BlobSource implements pmtiles.Source { + blob: Blob; + key: string; + + constructor(blob: Blob, key: string) { + this.blob = blob; + this.key = key; + } + + // TODO - how is this used? + getKey() { + // @ts-expect-error url is only defined on File subclass + return this.blob.url || ''; + } + + async getBytes( + offset: number, + length: number, + signal?: AbortSignal + ): Promise { + const slice = this.blob.slice(offset, offset + length); + const data = await slice.arrayBuffer(); + return { + data + // etag: response.headers.get('ETag') || undefined, + // cacheControl: response.headers.get('Cache-Control') || undefined, + // expires: response.headers.get('Expires') || undefined + }; + } +} diff --git a/modules/pmtiles/src/lib/parse-pmtiles.ts b/modules/pmtiles/src/lib/parse-pmtiles.ts new file mode 100644 index 0000000000..6d9c91f542 --- /dev/null +++ b/modules/pmtiles/src/lib/parse-pmtiles.ts @@ -0,0 +1,142 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {TileJSON} from '@loaders.gl/mvt'; +import {TileJSONLoader} from '@loaders.gl/mvt'; +// import {Source, PMTiles, Header, TileType} from 'pmtiles'; +import * as pmtiles from 'pmtiles'; +const {TileType} = pmtiles; + +/** Metadata describing a PMTiles file */ +export type PMTilesMetadata = { + format: 'pmtiles'; + /** Version of pm tiles format used by this tileset */ + formatVersion: number; + + /** MIME type for tile contents. Unknown tile types will return 'application/octet-stream */ + tileMIMEType: + | 'application/vnd.mapbox-vector-tile' + | 'image/png' + | 'image/jpeg' + | 'image/webp' + | 'image/avif' + | 'application/octet-stream'; + + /** Name of the tileset (extracted from JSON metadata if available) */ + name?: string; + /** Attribution string (extracted from JSON metadata if available) */ + attributions?: string[]; + + /** Minimal zoom level of tiles in this tileset */ + minZoom: number; + /** Maximal zoom level of tiles in this tileset */ + maxZoom: number; + /** Bounding box of tiles in this tileset `[[w, s], [e, n]]` */ + boundingBox: [min: [x: number, y: number], max: [x: number, y: number]]; + /** Center long, lat of this tileset */ + center: [number, number]; + /** Center zoom level of this tileset */ + centerZoom: number; + /** Cache tag */ + etag?: string; + + /** Parsed TileJSON/tilestats metadata, if present */ + tilejson?: TileJSON; + + /** @deprecated PMTiles format specific header */ + formatHeader?: pmtiles.Header; + /** @deprecated Unparsed metadata (Assumption metadata generated by e.g. tippecanoe, typically TileJSON) */ + formatMetadata?: Record; +}; + +/** + * Parse PMTiles metdata from a PMTiles file + * @param header + * @param tilejsonMetadata + * @param options + * @returns + */ +export function parsePMTilesHeader( + header: pmtiles.Header, + pmtilesMetadata: Record | null, + options?: {includeFormatHeader?: boolean} +): PMTilesMetadata { + // Ironically, to use the TileJSON loader we need to stringify the metadata again. + // This is the price of integrating with the existing pmtiles library. + // TODO - provide a non-standard TileJSONLoader parsers that accepts a JSON object? + let tilejson: TileJSON | null = null; + if (pmtilesMetadata) { + try { + const string = JSON.stringify(pmtilesMetadata); + tilejson = TileJSONLoader.parseTextSync?.(string) || null; + } catch (error) { + // eslint-disable-next-line no-console + console.warn('PMTiles metadata could not be interpreted as TileJSON', error); + } + } + + const partialMetadata: Partial = {}; + + if (typeof tilejson?.name === 'string') { + partialMetadata.name = tilejson.name; + } + + if (typeof tilejson?.htmlAttribution === 'string') { + partialMetadata.attributions = [tilejson.htmlAttribution]; + } + + const metadata: PMTilesMetadata = { + ...partialMetadata, + format: 'pmtiles', + formatVersion: header.specVersion, + attributions: [], + tileMIMEType: decodeTileType(header.tileType), + minZoom: header.minZoom, + maxZoom: header.maxZoom, + boundingBox: [ + [header.minLon, header.minLat], + [header.maxLon, header.maxLat] + ], + center: [header.centerLon, header.centerLat], + centerZoom: header.centerZoom, + etag: header.etag + }; + + if (tilejson) { + metadata.tilejson = tilejson; + } + + // Application can optionally include the raw header and metadata. + if (options?.includeFormatHeader) { + metadata.formatHeader = header; + metadata.formatMetadata = metadata; + } + + return metadata; +} + +/** Extract a MIME type for tiles from vector tile header */ +function decodeTileType( + tileType: pmtiles.TileType +): + | 'application/vnd.mapbox-vector-tile' + | 'image/png' + | 'image/jpeg' + | 'image/webp' + | 'image/avif' + | 'application/octet-stream' { + switch (tileType) { + case TileType.Mvt: + return 'application/vnd.mapbox-vector-tile'; + case TileType.Png: + return 'image/png'; + case TileType.Jpeg: + return 'image/jpeg'; + case TileType.Webp: + return 'image/webp'; + case TileType.Avif: + return 'image/avif'; + default: + return 'application/octet-stream'; + } +} diff --git a/modules/pmtiles/src/pmtiles-source.ts b/modules/pmtiles/src/pmtiles-source.ts new file mode 100644 index 0000000000..c2b5ddc134 --- /dev/null +++ b/modules/pmtiles/src/pmtiles-source.ts @@ -0,0 +1,139 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {TileLoadParameters, GetTileParameters} from '@loaders.gl/loader-utils'; +import type {ImageType, DataSourceProps} from '@loaders.gl/loader-utils'; +import type {ImageTileSource, VectorTileSource} from '@loaders.gl/loader-utils'; +import {DataSource, resolvePath} from '@loaders.gl/loader-utils'; +import {ImageLoader} from '@loaders.gl/images'; +import {MVTLoader, MVTLoaderOptions} from '@loaders.gl/mvt'; + +import * as pmtiles from 'pmtiles'; +const {PMTiles} = pmtiles; + +import type {PMTilesMetadata} from './lib/parse-pmtiles'; +import {parsePMTilesHeader} from './lib/parse-pmtiles'; +import {BlobSource} from './lib/blob-source'; + +const VERSION = '1.0.0'; + +export type Service = { + name: string; + id: string; + module: string; + version: string; + extensions: string[]; + mimeTypes: string[]; + options: Record; +}; + +export type ServiceWithSource = Service & { + _source?: SourceT; + _sourceProps?: SourcePropsT; + createSource: (props: SourcePropsT) => SourceT; +}; + +export const PMTilesService: ServiceWithSource = { + name: 'PMTiles', + id: 'pmtiles', + module: 'pmtiles', + version: VERSION, + extensions: ['pmtiles'], + mimeTypes: ['application/octet-stream'], + options: { + pmtiles: {} + }, + createSource: (props: PMTilesSourceProps) => new PMTilesSource(props) +}; + +export type PMTilesSourceProps = DataSourceProps & { + url: string | Blob; + attributions?: string[]; +}; + +/** + * A PMTiles data source + * @note Can be either a raster or vector tile source depending on the contents of the PMTiles file. + */ +export class PMTilesSource extends DataSource implements ImageTileSource, VectorTileSource { + data: string | Blob; + props: PMTilesSourceProps; + mimeType: string | null = null; + pmtiles: pmtiles.PMTiles; + metadata: Promise; + + constructor(props: PMTilesSourceProps) { + super(props); + this.props = props; + const url = + typeof props.url === 'string' ? resolvePath(props.url) : new BlobSource(props.url, 'pmtiles'); + this.data = props.url; + this.pmtiles = new PMTiles(url); + this.getTileData = this.getTileData.bind(this); + this.metadata = this.getMetadata(); + } + + async getMetadata(): Promise { + const pmtilesHeader = await this.pmtiles.getHeader(); + const pmtilesMetadata = await this.pmtiles.getMetadata(); + const metadata: PMTilesMetadata = parsePMTilesHeader(pmtilesHeader, pmtilesMetadata); + // Add additional attribution if necessary + if (this.props.attributions) { + metadata.attributions = [...this.props.attributions, ...(metadata.attributions || [])]; + } + if (metadata?.tileMIMEType) { + this.mimeType = metadata?.tileMIMEType; + } + // TODO - do we need to allow tileSize to be overridden? Some PMTiles examples seem to suggest it. + return metadata; + } + + async getTile(tileParams: GetTileParameters): Promise { + const {x, y, zoom: z} = tileParams; + const rangeResponse = await this.pmtiles.getZxy(z, x, y); + const arrayBuffer = rangeResponse?.data; + if (!arrayBuffer) { + // console.error('No arrayBuffer', tileParams); + return null; + } + return arrayBuffer; + } + + // Tile Source interface implementation: deck.gl compatible API + // TODO - currently only handles image tiles, not vector tiles + + async getTileData(tileParams: TileLoadParameters): Promise { + const {x, y, z} = tileParams.index; + const metadata = await this.metadata; + switch (metadata.tileMIMEType) { + case 'application/vnd.mapbox-vector-tile': + return await this.getVectorTile({x, y, zoom: z, layers: []}); + default: + return await this.getImageTile({x, y, zoom: z, layers: []}); + } + } + + // ImageTileSource interface implementation + + async getImageTile(tileParams: GetTileParameters): Promise { + const arrayBuffer = await this.getTile(tileParams); + return arrayBuffer ? await ImageLoader.parse(arrayBuffer, this.loadOptions) : null; + } + + // VectorTileSource interface implementation + + async getVectorTile(tileParams: GetTileParameters): Promise { + const arrayBuffer = await this.getTile(tileParams); + const loadOptions: MVTLoaderOptions = { + shape: 'geojson-table', + mvt: { + coordinates: 'wgs84', + tileIndex: {x: tileParams.x, y: tileParams.y, z: tileParams.zoom}, + ...(this.loadOptions as MVTLoaderOptions)?.mvt + }, + ...this.loadOptions + }; + + return arrayBuffer ? await MVTLoader.parse(arrayBuffer, loadOptions) : null; + } +} diff --git a/modules/pmtiles/test/data/README.md b/modules/pmtiles/test/data/README.md new file mode 100644 index 0000000000..36f1372da7 --- /dev/null +++ b/modules/pmtiles/test/data/README.md @@ -0,0 +1,9 @@ +# Attributions for Sample Files + +## pmtiles + +https://github.com/protomaps/PMTiles/tree/main/js/test/data under BSD-3 license + +## tilejson + +https://github.com/mapbox/node-tilejson/tree/master/test under BSD-2 license diff --git a/docs/arrowjs/paul-drafts/builders/index.md b/modules/pmtiles/test/data/pmtiles-v2/empty.pmtiles similarity index 100% rename from docs/arrowjs/paul-drafts/builders/index.md rename to modules/pmtiles/test/data/pmtiles-v2/empty.pmtiles diff --git a/modules/pmtiles/test/data/pmtiles-v2/invalid.pmtiles b/modules/pmtiles/test/data/pmtiles-v2/invalid.pmtiles new file mode 100644 index 0000000000..f2b720ba30 --- /dev/null +++ b/modules/pmtiles/test/data/pmtiles-v2/invalid.pmtiles @@ -0,0 +1 @@ +This is an invalid tile archive, a test case to make sure that the code throws an error, but it needs to be the minimum size to pass the first test diff --git a/modules/pmtiles/test/data/pmtiles-v2/invalid_v4.pmtiles b/modules/pmtiles/test/data/pmtiles-v2/invalid_v4.pmtiles new file mode 100644 index 0000000000..1871cb2759 Binary files /dev/null and b/modules/pmtiles/test/data/pmtiles-v2/invalid_v4.pmtiles differ diff --git a/modules/pmtiles/test/data/pmtiles-v2/test_fixture_1.pmtiles b/modules/pmtiles/test/data/pmtiles-v2/test_fixture_1.pmtiles new file mode 100644 index 0000000000..c86db1f27b Binary files /dev/null and b/modules/pmtiles/test/data/pmtiles-v2/test_fixture_1.pmtiles differ diff --git a/modules/pmtiles/test/data/pmtiles-v2/test_fixture_2.pmtiles b/modules/pmtiles/test/data/pmtiles-v2/test_fixture_2.pmtiles new file mode 100644 index 0000000000..cb19dd5f11 Binary files /dev/null and b/modules/pmtiles/test/data/pmtiles-v2/test_fixture_2.pmtiles differ diff --git a/modules/pmtiles/test/data/pmtiles-v3/stamen_toner(raster)CC-BY+ODbL_z3.pmtiles b/modules/pmtiles/test/data/pmtiles-v3/stamen_toner(raster)CC-BY+ODbL_z3.pmtiles new file mode 100644 index 0000000000..8d170ca89e Binary files /dev/null and b/modules/pmtiles/test/data/pmtiles-v3/stamen_toner(raster)CC-BY+ODbL_z3.pmtiles differ diff --git a/modules/pmtiles/test/data/tilesets.ts b/modules/pmtiles/test/data/tilesets.ts new file mode 100644 index 0000000000..de2130129c --- /dev/null +++ b/modules/pmtiles/test/data/tilesets.ts @@ -0,0 +1,12 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +export const PMTILESETS = [ + 'test_fixture_1.pmtiles', + 'test_fixture_2.pmtiles' + // v3 folder? +].map((tileset) => `@loaders.gl/pmtiles/test/data/pmtiles-v2/${tileset}`); + +export const PMTILESETS_INVALID = ['empty.pmtiles', 'invalid.pmtiles', 'invalid_v4.pmtiles'].map( + (tileset) => `@loaders.gl/pmtiles/test/data/${tileset}` +); diff --git a/modules/pmtiles/test/index.ts b/modules/pmtiles/test/index.ts new file mode 100644 index 0000000000..c26bb9a64f --- /dev/null +++ b/modules/pmtiles/test/index.ts @@ -0,0 +1,4 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import './pmtiles-source.spec'; diff --git a/modules/pmtiles/test/pmtiles-source.spec.ts b/modules/pmtiles/test/pmtiles-source.spec.ts new file mode 100644 index 0000000000..f26a62dcb6 --- /dev/null +++ b/modules/pmtiles/test/pmtiles-source.spec.ts @@ -0,0 +1,241 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import {isBrowser, fetchFile} from '@loaders.gl/core'; + +import {PMTILESETS} from './data/tilesets'; +import {PMTilesSource} from '@loaders.gl/pmtiles'; + +test('PMTilesSource#urls', async (t) => { + if (!isBrowser) { + t.comment('PMTilesSource currently only supported in browser'); + t.end(); + return; + } + for (const tilesetUrl of PMTILESETS) { + const source = new PMTilesSource({url: tilesetUrl}); + t.ok(source); + const metadata = await source.getMetadata(); + t.ok(metadata); + // console.error(JSON.stringify(metadata.tileJSON, null, 2)); + } + t.end(); +}); + +test('PMTilesSource#Blobs', async (t) => { + if (!isBrowser) { + t.comment('PMTilesSource currently only supported in browser'); + t.end(); + return; + } + for (const tilesetUrl of PMTILESETS) { + const response = await fetchFile(tilesetUrl); + const blob = await response.blob(); + const source = new PMTilesSource({url: blob}); + t.ok(source); + const metadata = await source.getMetadata(); + t.ok(metadata); + // console.error(JSON.stringify(metadata.tileJSON, null, 2)); + } + t.end(); +}); + +// TBA - TILE LOADING TESTS + +/* +import test from 'tape-promise/tape'; +import {validateLoader} from 'test/common/conformance'; + +import {load} from '@loaders.gl/core'; +import {PMTilesLoader} from '@loaders.gl/pmtiles'; + +import {PMTILESETS} from './data/tilesets'; + +test('PMTilesLoader#loader conformance', (t) => { + validateLoader(t, PMTilesLoader, 'PMTilesLoader'); + t.end(); +}); + +test.skip('PMTilesLoader#load', async (t) => { + for (const tilesetUrl of PMTILESETS) { + const metadata = await load(tilesetUrl, PMTilesLoader); + t.ok(metadata); + } + t.end(); +}); + +/* +// echo '{"type":"Polygon","coordinates":[[[0,0],[0,1],[1,1],[1,0],[0,0]]]}' | ./tippecanoe -zg -o test_fixture_1.pmtiles +test('cache getHeader', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + const cache = new SharedPromiseCache(); + const header = await cache.getHeader(source); + t.strictEqual(header.rootDirectoryOffset, 127); + t.strictEqual(header.rootDirectoryLength, 25); + t.strictEqual(header.jsonMetadataOffset, 152); + t.strictEqual(header.jsonMetadataLength, 247); + t.strictEqual(header.leafDirectoryOffset, 0); + t.strictEqual(header.leafDirectoryLength, 0); + t.strictEqual(header.tileDataOffset, 399); + t.strictEqual(header.tileDataLength, 69); + t.strictEqual(header.numAddressedTiles, 1); + t.strictEqual(header.numTileEntries, 1); + t.strictEqual(header.numTileContents, 1); + t.strictEqual(header.clustered, false); + t.strictEqual(header.internalCompression, 2); + t.strictEqual(header.tileCompression, 2); + t.strictEqual(header.tileType, 1); + t.strictEqual(header.minZoom, 0); + t.strictEqual(header.maxZoom, 0); + t.strictEqual(header.minLon, 0); + t.strictEqual(header.minLat, 0); + // t.strictEqual(header.maxLon,1); // TODO fix me + t.strictEqual(header.maxLat, 1); +}); + +test('cache check against empty', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/empty.pmtiles', '1'); + const cache = new SharedPromiseCache(); + t.rejects(async () => { + await cache.getHeader(source); + }); +}); + +test('cache check magic number', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/invalid.pmtiles', '1'); + const cache = new SharedPromiseCache(); + t.rejects(async () => { + await cache.getHeader(source); + }); +}); + +test('cache check future spec version', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/invalid_v4.pmtiles', '1'); + const cache = new SharedPromiseCache(); + t.rejects(async () => { + await cache.getHeader(source); + }); +}); + +test('cache getDirectory', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + + let cache = new SharedPromiseCache(6400, false); + let header = await cache.getHeader(source); + t.strictEqual(cache.cache.size, 1); + + cache = new SharedPromiseCache(6400, true); + header = await cache.getHeader(source); + + // prepopulates the root directory + t.strictEqual(cache.cache.size, 2); + + const directory = await cache.getDirectory( + source, + header.rootDirectoryOffset, + header.rootDirectoryLength, + header + ); + t.strictEqual(directory.length, 1); + t.strictEqual(directory[0].tileId, 0); + t.strictEqual(directory[0].offset, 0); + t.strictEqual(directory[0].length, 69); + t.strictEqual(directory[0].runLength, 1); + + for (const v of cache.cache.values()) { + t.ok(v.lastUsed > 0); + } +}); + +test('multiple sources in a single cache', async (t) => { + const cache = new SharedPromiseCache(); + const source1 = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + const source2 = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '2'); + await cache.getHeader(source1); + t.strictEqual(cache.cache.size, 2); + await cache.getHeader(source2); + t.strictEqual(cache.cache.size, 4); +}); + +test('etags are part of key', async (t) => { + const cache = new SharedPromiseCache(6400, false); + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + source.etag = 'etag_1'; + let header = await cache.getHeader(source); + t.strictEqual(header.etag, 'etag_1'); + + source.etag = 'etag_2'; + + t.rejects(async () => { + await cache.getDirectory( + source, + header.rootDirectoryOffset, + header.rootDirectoryLength, + header + ); + }); + + cache.invalidate(source, 'etag_2'); + header = await cache.getHeader(source); + t.ok( + await cache.getDirectory(source, header.rootDirectoryOffset, header.rootDirectoryLength, header) + ); +}); + +test.skip('soft failure on etag weirdness', async (t) => { + const cache = new SharedPromiseCache(6400, false); + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + source.etag = 'etag_1'; + let header = await cache.getHeader(source); + t.strictEqual(header.etag, 'etag_1'); + + source.etag = 'etag_2'; + + t.rejects(async () => { + await cache.getDirectory( + source, + header.rootDirectoryOffset, + header.rootDirectoryLength, + header + ); + }); + + source.etag = 'etag_1'; + cache.invalidate(source, 'etag_2'); + + header = await cache.getHeader(source); + t.strictEqual(header.etag, undefined); +}); + +test('cache pruning by byte size', async (t) => { + const cache = new SharedPromiseCache(2, false); + cache.cache.set('0', {lastUsed: 0, data: Promise.resolve([])}); + cache.cache.set('1', {lastUsed: 1, data: Promise.resolve([])}); + cache.cache.set('2', {lastUsed: 2, data: Promise.resolve([])}); + cache.prune(); + t.strictEqual(cache.cache.size, 2); + t.ok(cache.cache.get('2')); + t.ok(cache.cache.get('1')); + t.ok(!cache.cache.get('0')); +}); + +test('pmtiles get metadata', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + const p = new PMTiles(source); + const metadata = await p.getMetadata(); + t.ok(metadata.name); +}); + +// echo '{"type":"Polygon","coordinates":[[[0,0],[0,1],[1,0],[0,0]]]}' | ./tippecanoe -zg -o test_fixture_2.pmtiles +test('pmtiles handle retries', async (t) => { + const source = new TestFileSource('@loaders.gl/pmtiles/test/data/test_fixture_1.pmtiles', '1'); + source.etag = '1'; + const p = new PMTiles(source); + const metadata = await p.getMetadata(); + t.ok(metadata.name); + source.etag = '2'; + source.replaceData('@loaders.gl/pmtiles/test/data/test_fixture_2.pmtiles'); + t.ok(await p.getZxy(0, 0, 0)); +}); +*/ diff --git a/modules/pmtiles/tsconfig.json b/modules/pmtiles/tsconfig.json new file mode 100644 index 0000000000..da2e083a9a --- /dev/null +++ b/modules/pmtiles/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.module.json", + "include": ["src/**/*"], + "exclude": ["node_modules"], + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "dist" + }, + "references": [ + {"path": "../loader-utils"}, + {"path": "../schema"}, + {"path": "../images"}, + {"path": "../mvt"} + ] +} diff --git a/modules/polyfills/package.json b/modules/polyfills/package.json index 682f71280a..c54df508b8 100644 --- a/modules/polyfills/package.json +++ b/modules/polyfills/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/polyfills", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Polyfills for TextEncoder/TextDecoder", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -19,92 +20,42 @@ "TextDecoder" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "files": [ "src", "dist", "README.md" ], "browser": { - "./src/node/fetch/utils/decode-data-uri.node.js": false, - "./src/node/fetch/utils/decode-data-uri.node.ts": false, - "./dist/es5/node/fetch/utils/decode-data-uri.node.js": false, - "./dist/esm/node/fetch/utils/decode-data-uri.node.js": false, - "./src/node/fetch/utils/stream-utils.node.js": false, - "./src/node/fetch/utils/stream-utils.node.ts": false, - "./dist/es5/node/fetch/utils/stream-utils.node.js": false, - "./dist/esm/node/fetch/utils/stream-utils.node.js": false, - "./src/node/fetch/fetch.node.js": false, - "./src/node/fetch/fetch.node.ts": false, - "./dist/es5/node/fetch/fetch.node.js": false, - "./dist/esm/node/fetch/fetch.node.js": false, - "./src/node/fetch/headers.node.js": false, - "./src/node/fetch/headers.node.ts": false, - "./dist/es5/node/fetch/headers.node.js": false, - "./dist/esm/node/fetch/headers.node.js": false, - "./src/node/fetch/response.node.js": false, - "./src/node/fetch/response.node.ts": false, - "./dist/es5/node/fetch/response.node.js": false, - "./dist/esm/node/fetch/response.node.js": false, - "./src/node/images/encode-image.node.js": false, - "./src/node/images/encode-image.node.ts": false, - "./dist/es5/node/images/encode-image.node.js": false, - "./dist/esm/node/images/encode-image.node.js": false, - "./src/node/images/parse-image.node.js": false, - "./src/node/images/parse-image.node.ts": false, - "./dist/es5/node/images/parse-image.node.js": false, - "./dist/esm/node/images/parse-image.node.js": false, - "./src/node/buffer/to-array-buffer.node.js": false, - "./src/node/buffer/to-array-buffer.node.ts": false, - "./dist/es5/node/buffer/to-array-buffer.node.js": false, - "./dist/esm/node/buffer/to-array-buffer.node.js": false, - "./src/node/buffer/btoa.node.js": false, - "./src/node/buffer/btoa.node.ts": false, - "./dist/es5/node/buffer/btoa.node.js": false, - "./dist/esm/node/buffer/btoa.node.js": false, - "./src/node/file/blob.js": false, - "./src/node/file/blob.ts": false, - "./dist/es5/node/file/blob.js": false, - "./dist/esm/node/file/blob.js": false, - "./src/node/file/file.js": false, - "./src/node/file/file.ts": false, - "./dist/es5/node/file/file.js": false, - "./dist/esm/node/file/file.js": false, - "./src/node/file/readable-stream.js": false, - "./src/node/file/readable-stream.ts": false, - "./dist/es5/node/file/readable-stream.js": false, - "./dist/esm/node/file/readable-stream.js": false, - "./src/libs/encoding-indices.js": false, - "./src/libs/encoding-indices.ts": false, - "./dist/es5/libs/encoding-indices.js": false, - "./dist/esm/libs/encoding-indices.js": false, "fs": false, - "http": false, - "https": false, - "stream": false, - "get-pixels": false, - "ndarray": false, - "save-pixels": false, - "stream-to-async-iterator": false, - "through": false, - "util": false, - "zlib": false, - "web-streams-polyfill": false + "path": false, + "./src/index.ts": "./src/index.browser.ts", + "./dist/index.js": "./dist/index.browser.js" }, "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-node-bundle", + "build-bundle": "ocular-bundle ./src/index.ts", + "build-node-bundle": "esbuild src/index.ts --outfile=dist/index.cjs --bundle --platform=node --target=node16 --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { "@babel/runtime": "^7.3.1", + "@loaders.gl/crypto": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", "buffer": "^6.0.3", - "get-pixels": "^3.3.2", - "ndarray": "^1.0.18", - "save-pixels": "^2.3.2", - "stream-to-async-iterator": "^0.2.0", + "get-pixels": "^3.3.3", + "ndarray": "^1.0.19", + "save-pixels": "^2.3.6", + "stream-to-async-iterator": "^1.0.0", "through": "^2.3.8", - "web-streams-polyfill": "^3.0.0" + "web-streams-polyfill": "^3.2.1" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/polyfills/src/node/buffer/btoa.node.ts b/modules/polyfills/src/buffer/btoa.node.ts similarity index 100% rename from modules/polyfills/src/node/buffer/btoa.node.ts rename to modules/polyfills/src/buffer/btoa.node.ts diff --git a/modules/polyfills/src/node/buffer/to-array-buffer.node.ts b/modules/polyfills/src/buffer/to-array-buffer.node.ts similarity index 100% rename from modules/polyfills/src/node/buffer/to-array-buffer.node.ts rename to modules/polyfills/src/buffer/to-array-buffer.node.ts diff --git a/modules/polyfills/src/bundle.ts b/modules/polyfills/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/polyfills/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/polyfills/src/crypto/node-hash.ts b/modules/polyfills/src/crypto/node-hash.ts new file mode 100644 index 0000000000..d6f7434b9a --- /dev/null +++ b/modules/polyfills/src/crypto/node-hash.ts @@ -0,0 +1,71 @@ +// This dependency is too big, application must provide it +import {Hash} from '@loaders.gl/crypto'; +import * as crypto from 'crypto'; // Node.js builtin + +type CryptoHashOptions = { + crypto: { + algorithm: string; + onEnd?: (result: {hash: string}) => any; + }; +}; + +/** + * Calculates Cryptographic Hash using Node.js crypto library + * @deprecated Warning, experimental class + */ +export class NodeHash extends Hash { + readonly name = 'crypto-node'; + + options: CryptoHashOptions; + // @ts-ignore + private _algorithm; + // @ts-ignore + private _hash; + + constructor(options: CryptoHashOptions) { + super(); + this.options = options; + if (!this.options?.crypto?.algorithm) { + throw new Error(this.name); + } + } + + /** + * Atomic hash calculation + * @returns base64 encoded hash + */ + async hash(input: ArrayBuffer, encoding: 'hex' | 'base64'): Promise { + // await this.preload(); + const algorithm = this.options?.crypto?.algorithm?.toLowerCase(); + try { + if (!crypto.createHash) { + throw new Error('crypto.createHash not available'); + } + const hash = crypto.createHash?.(algorithm); + const inputArray = new Uint8Array(input); + return hash.update(inputArray).digest('base64'); + } catch (error) { + throw Error(`${algorithm} hash not available. ${error}`); + } + } + + async *hashBatches( + asyncIterator: AsyncIterable | Iterable, + encoding: 'hex' | 'base64' = 'base64' + ): AsyncIterable { + // await this.preload(); + if (!crypto.createHash) { + throw new Error('crypto.createHash not available'); + } + const hash = crypto.createHash?.(this.options?.crypto?.algorithm?.toLowerCase()); + for await (const chunk of asyncIterator) { + // https://stackoverflow.com/questions/25567468/how-to-decrypt-an-arraybuffer + const inputArray = new Uint8Array(chunk); + hash.update(inputArray); + yield chunk; + } + // We can pass our encoding constant directly to Node.js digest as it already supports `hex` and `base64` + const digest = hash.digest(encoding); + this.options?.crypto?.onEnd?.({hash: digest}); + } +} diff --git a/modules/polyfills/src/node/fetch/utils/decode-data-uri.node.ts b/modules/polyfills/src/fetch/decode-data-uri.ts similarity index 100% rename from modules/polyfills/src/node/fetch/utils/decode-data-uri.node.ts rename to modules/polyfills/src/fetch/decode-data-uri.ts diff --git a/modules/polyfills/src/node/fetch/fetch.node.ts b/modules/polyfills/src/fetch/fetch-polyfill.ts similarity index 91% rename from modules/polyfills/src/node/fetch/fetch.node.ts rename to modules/polyfills/src/fetch/fetch-polyfill.ts index 71f178d05c..212afa30f0 100644 --- a/modules/polyfills/src/node/fetch/fetch.node.ts +++ b/modules/polyfills/src/fetch/fetch-polyfill.ts @@ -1,12 +1,11 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import http from 'http'; import https from 'https'; -import {Response} from './response.node'; -import {Headers} from './headers.node'; -import {decodeDataUri} from './utils/decode-data-uri.node'; - -import {fetchFileNode} from './fetch-file.node'; +import {Response} from './response-polyfill'; +import {Headers} from './headers-polyfill'; +import {decodeDataUri} from './decode-data-uri'; const isDataURL = (url: string): boolean => url.startsWith('data:'); const isRequestURL = (url: string): boolean => url.startsWith('http:') || url.startsWith('https:'); @@ -17,11 +16,13 @@ const isRequestURL = (url: string): boolean => url.startsWith('http:') || url.st * @param options */ // eslint-disable-next-line complexity -export async function fetchNode(url: string, options): Promise { +export async function fetchNode(url: string, options: RequestInit): Promise { try { // Handle file streams in node - if (!isRequestURL(url) && !isDataURL(url)) { - return await fetchFileNode(url, options); + // @ts-expect-error + if (globalThis.fetch !== fetchNode && (isRequestURL(url) || isDataURL(url))) { + // @ts-expect-error + return await fetch(url, options); } // Handle data urls in node, to match `fetch`` @@ -49,6 +50,7 @@ export async function fetchNode(url: string, options): Promise { const {status, statusText} = getStatus(body); const followRedirect = + // @ts-expect-error !options || options.followRedirect || options.followRedirect === undefined; if (status >= 300 && status < 400 && headers.has('location') && followRedirect) { diff --git a/modules/polyfills/src/node/fetch/headers.node.ts b/modules/polyfills/src/fetch/headers-polyfill.ts similarity index 100% rename from modules/polyfills/src/node/fetch/headers.node.ts rename to modules/polyfills/src/fetch/headers-polyfill.ts diff --git a/modules/polyfills/src/node/fetch/response.node.ts b/modules/polyfills/src/fetch/response-polyfill.ts similarity index 86% rename from modules/polyfills/src/node/fetch/response.node.ts rename to modules/polyfills/src/fetch/response-polyfill.ts index 1387a7619f..bc82a9f004 100644 --- a/modules/polyfills/src/node/fetch/response.node.ts +++ b/modules/polyfills/src/fetch/response-polyfill.ts @@ -1,8 +1,9 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import {assert} from '../../utils/assert'; -import {decompressReadStream, concatenateReadStream} from './utils/stream-utils.node'; -import {Headers} from './headers.node'; +import {assert} from '../utils/assert'; +import {decompressReadStream, concatenateReadStream} from '../filesystems/stream-utils.node'; +import {Headers} from './headers-polyfill'; const isBoolean = (x) => typeof x === 'boolean'; const isFunction = (x) => typeof x === 'function'; @@ -20,7 +21,7 @@ const isReadableNodeStream = (x) => * * See https://developer.mozilla.org/en-US/docs/Web/API/Response */ -import {Readable} from 'stream'; +import * as stream from 'stream'; export class Response { readonly ok: boolean; @@ -53,9 +54,9 @@ export class Response { if (isReadableNodeStream(body)) { this._body = decompressReadStream(body, headers); } else if (typeof body === 'string') { - this._body = Readable.from([new TextEncoder().encode(body)]); + this._body = stream.Readable.from([new TextEncoder().encode(body)]); } else { - this._body = Readable.from([body || new ArrayBuffer(0)]); + this._body = stream.Readable.from([body || new ArrayBuffer(0)]); } } diff --git a/modules/polyfills/src/fetch/utils/decode-data-uri.node.ts b/modules/polyfills/src/fetch/utils/decode-data-uri.node.ts new file mode 100644 index 0000000000..7f6f2dbeb9 --- /dev/null +++ b/modules/polyfills/src/fetch/utils/decode-data-uri.node.ts @@ -0,0 +1,69 @@ +// Based on binary-gltf-utils under MIT license: Copyright (c) 2016-17 Karl Cheng + +const isArrayBuffer = (x) => x && x instanceof ArrayBuffer; +const isBuffer = (x) => x && x instanceof Buffer; + +/** + * Parses a data URI into a buffer, as well as retrieving its declared MIME type. + * + * @param {string} uri - a data URI (assumed to be valid) + * @returns {Object} { buffer, mimeType } + */ +export function decodeDataUri(uri: string): {arrayBuffer: ArrayBuffer; mimeType: string} { + const dataIndex = uri.indexOf(','); + + let buffer; + let mimeType; + if (uri.slice(dataIndex - 7, dataIndex) === ';base64') { + buffer = Buffer.from(uri.slice(dataIndex + 1), 'base64'); + mimeType = uri.slice(5, dataIndex - 7).trim(); + } else { + buffer = Buffer.from(decodeURIComponent(uri.slice(dataIndex + 1))); + mimeType = uri.slice(5, dataIndex).trim(); + } + + if (!mimeType) { + mimeType = 'text/plain;charset=US-ASCII'; + } else if (mimeType.startsWith(';')) { + mimeType = `text/plain${mimeType}`; + } + + return {arrayBuffer: toArrayBuffer(buffer), mimeType}; +} + +/** + * @param data + * @todo Duplicate of core + */ +export function toArrayBuffer(data: unknown): ArrayBuffer { + if (isArrayBuffer(data)) { + return data as ArrayBuffer; + } + + // TODO - per docs we should just be able to call buffer.buffer, but there are issues + if (isBuffer(data)) { + // @ts-expect-error + const typedArray = new Uint8Array(data); + return typedArray.buffer; + } + + // Careful - Node Buffers will look like ArrayBuffers (keep after isBuffer) + if (ArrayBuffer.isView(data)) { + return data.buffer; + } + + if (typeof data === 'string') { + const text = data; + const uint8Array = new TextEncoder().encode(text); + return uint8Array.buffer; + } + + // HACK to support Blob polyfill + // @ts-expect-error + if (data && typeof data === 'object' && data._toArrayBuffer) { + // @ts-expect-error + return data._toArrayBuffer(); + } + + throw new Error(`toArrayBuffer(${JSON.stringify(data, null, 2).slice(10)})`); +} diff --git a/modules/polyfills/src/node/file/blob-stream-controller.ts b/modules/polyfills/src/file/blob-stream-controller.ts similarity index 100% rename from modules/polyfills/src/node/file/blob-stream-controller.ts rename to modules/polyfills/src/file/blob-stream-controller.ts diff --git a/modules/polyfills/src/node/file/blob-stream.ts b/modules/polyfills/src/file/blob-stream.ts similarity index 100% rename from modules/polyfills/src/node/file/blob-stream.ts rename to modules/polyfills/src/file/blob-stream.ts diff --git a/modules/polyfills/src/node/file/blob.ts b/modules/polyfills/src/file/blob.ts similarity index 100% rename from modules/polyfills/src/node/file/blob.ts rename to modules/polyfills/src/file/blob.ts diff --git a/modules/polyfills/src/node/file/file-reader.ts b/modules/polyfills/src/file/file-reader.ts similarity index 100% rename from modules/polyfills/src/node/file/file-reader.ts rename to modules/polyfills/src/file/file-reader.ts diff --git a/modules/polyfills/src/node/file/file.ts b/modules/polyfills/src/file/file.ts similarity index 93% rename from modules/polyfills/src/node/file/file.ts rename to modules/polyfills/src/file/file.ts index 7725d28bb8..53aab55af0 100644 --- a/modules/polyfills/src/node/file/file.ts +++ b/modules/polyfills/src/file/file.ts @@ -1,12 +1,11 @@ // Forked from @gozala's web-file under MIT license https://github.com/Gozala/web-file -import {BlobPolyfill} from './blob'; /** * Forked from @gozala's web-file under MIT license * @see https://github.com/Gozala/web-file */ // @ts-ignore -export class FilePolyfill extends BlobPolyfill { +export class FilePolyfill extends globalThis.Blob { // implements File { // public API /** The name of the file referenced by the File object. */ diff --git a/modules/polyfills/src/file/install-blob-polyfills.ts b/modules/polyfills/src/file/install-blob-polyfills.ts new file mode 100644 index 0000000000..091f7fa485 --- /dev/null +++ b/modules/polyfills/src/file/install-blob-polyfills.ts @@ -0,0 +1,13 @@ +// import {ReadableStreamPolyfill} from './readable-stream'; +import {BlobPolyfill} from './blob'; + +export function instalBlobPolyfills() { + if (typeof Blob === 'undefined' && !globalThis.Blob) { + // @ts-ignore; + globalThis.Blob = BlobPolyfill; + } + + return globalThis.Blob; +} + +export const Blob_ = instalBlobPolyfills(); diff --git a/modules/polyfills/src/file/install-file-polyfills.ts b/modules/polyfills/src/file/install-file-polyfills.ts new file mode 100644 index 0000000000..e083deabdf --- /dev/null +++ b/modules/polyfills/src/file/install-file-polyfills.ts @@ -0,0 +1,20 @@ +// import {ReadableStreamPolyfill} from './readable-stream'; +import {FileReaderPolyfill} from './file-reader'; +import {FilePolyfill} from './file'; + +export function installFilePolyfills() { + if (typeof FileReader === 'undefined' && !globalThis.FileReader) { + // @ts-ignore; + globalThis.FileReader = FileReaderPolyfill; + } + + // Install minimal Node.js File polyfill + if (typeof File === 'undefined' && !globalThis.File) { + // @ts-ignore; + globalThis.File = FilePolyfill; + } + + return global; +} + +export const File_ = installFilePolyfills(); diff --git a/modules/polyfills/src/node/file/readable-stream.ts b/modules/polyfills/src/file/readable-stream.ts similarity index 87% rename from modules/polyfills/src/node/file/readable-stream.ts rename to modules/polyfills/src/file/readable-stream.ts index 75fc3a72ee..1384164154 100644 --- a/modules/polyfills/src/node/file/readable-stream.ts +++ b/modules/polyfills/src/file/readable-stream.ts @@ -2,7 +2,7 @@ import {ReadableStream as WSPReadableStream} from 'web-streams-polyfill'; // Want a polyfill, but please don't install it // @ts-ignore -delete global.ReadableStream; +delete globalThis.ReadableStream; // @ts-ignore export class ReadableStreamPolyfill extends WSPReadableStream implements ReadableStream {} diff --git a/modules/polyfills/src/filesystems/fetch-node.ts b/modules/polyfills/src/filesystems/fetch-node.ts new file mode 100644 index 0000000000..c4b87efcb1 --- /dev/null +++ b/modules/polyfills/src/filesystems/fetch-node.ts @@ -0,0 +1,97 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import fs from 'fs'; +import {Readable} from 'stream'; +import {resolvePath} from '@loaders.gl/loader-utils'; +import {decompressReadStream} from './stream-utils.node'; + +const isBoolean = (x) => typeof x === 'boolean'; +const isFunction = (x) => typeof x === 'function'; +const isObject = (x) => x !== null && typeof x === 'object'; +const isReadableNodeStream = (x) => + isObject(x) && isFunction(x.read) && isFunction(x.pipe) && isBoolean(x.readable); + +/** + * Enables + * @param url + * @param options + * @returns + */ +// eslint-disable-next-line max-statements +export async function fetchNode(url: string, options?: RequestInit): Promise { + // Support `file://` protocol + const FILE_PROTOCOL_REGEX = /^file:\/\//; + url.replace(FILE_PROTOCOL_REGEX, '/'); + + // Remove any query parameters, as they have no meaning + let noqueryUrl = url.split('?')[0]; + noqueryUrl = resolvePath(noqueryUrl); + + const responseHeaders = new Headers(); + // Automatically decompress gzipped files with .gz extension + if (url.endsWith('.gz')) { + // url = url.slice(0, -3); + responseHeaders['content-encoding'] = 'gzip'; + } + if (url.endsWith('.br')) { + // url = url.slice(0, -3); + responseHeaders['content-encoding'] = 'br'; + } + + try { + // Now open the stream + const body = await new Promise((resolve, reject) => { + // @ts-ignore + const stream = fs.createReadStream(noqueryUrl, {encoding: null}); + stream.once('readable', () => resolve(stream)); + stream.on('error', (error) => reject(error)); + }); + + let bodyStream: Readable = body; + + // Check for content-encoding and create a decompression stream + if (isReadableNodeStream(body)) { + bodyStream = decompressReadStream(body, responseHeaders); + } else if (typeof body === 'string') { + bodyStream = Readable.from([new TextEncoder().encode(body)]); + } else { + bodyStream = Readable.from([body || new ArrayBuffer(0)]); + } + + const status = 200; + const statusText = 'OK'; + const headers = getHeadersForFile(noqueryUrl); + // @ts-expect-error + const response = new Response(bodyStream, {headers, status, statusText}); + Object.defineProperty(response, 'url', {value: url}); + return response; + } catch (error) { + // console.error(error); + const errorMessage = (error as Error).message; + const status = 400; + const statusText = errorMessage; + const headers = {}; + const response = new Response(errorMessage, {headers, status, statusText}); + Object.defineProperty(response, 'url', {value: url}); + return response; + } +} + +function getHeadersForFile(noqueryUrl: string): Headers { + const headers = {}; + + // Fix up content length if we can for best progress experience + if (!headers['content-length']) { + const stats = fs.statSync(noqueryUrl); + headers['content-length'] = stats.size; + } + + // Automatically decompress gzipped files with .gz extension + if (noqueryUrl.endsWith('.gz')) { + noqueryUrl = noqueryUrl.slice(0, -3); + headers['content-encoding'] = 'gzip'; + } + + return new Headers(headers); +} diff --git a/modules/polyfills/src/filesystems/node-file.ts b/modules/polyfills/src/filesystems/node-file.ts new file mode 100644 index 0000000000..6d7e0e3f0d --- /dev/null +++ b/modules/polyfills/src/filesystems/node-file.ts @@ -0,0 +1,137 @@ +import type {ReadableFile, WritableFile, Stat} from '@loaders.gl/loader-utils'; +import {resolvePath} from '@loaders.gl/loader-utils'; +import fs from 'fs'; + +export class NodeFile implements ReadableFile, WritableFile { + handle: number; + size: number; + bigsize: bigint; + url: string; + + constructor(path: string, flags: 'r' | 'w' | 'wx', mode?: number) { + path = resolvePath(path); + this.handle = fs.openSync(path, flags, mode); + const stats = fs.fstatSync(this.handle, {bigint: true}); + this.size = Number(stats.size); + this.bigsize = stats.size; + this.url = path; + } + + async close(): Promise { + return new Promise((resolve, reject) => { + fs.close(this.handle, (err) => (err ? reject(err) : resolve())); + }); + } + + async stat(): Promise { + return await new Promise((resolve, reject) => + fs.fstat(this.handle, {bigint: true}, (err, info) => { + const stats: Stat = { + size: Number(info.size), + bigsize: info.size, + isDirectory: info.isDirectory() + }; + if (err) { + reject(err); + } else { + resolve(stats); + } + }) + ); + } + + async read(offset: number | bigint, length: number): Promise { + const arrayBuffer = new ArrayBuffer(length); + let bigOffset = BigInt(offset); + + let totalBytesRead = 0; + const uint8Array = new Uint8Array(arrayBuffer); + + let position; + // Read in loop until we get required number of bytes + while (length > 0) { + const bytesRead = await readBytes(this.handle, uint8Array, 0, length, bigOffset); + + // Check if end of file reached + if (bytesRead === 0) { + break; + } + + totalBytesRead += bytesRead; + bigOffset += BigInt(bytesRead); + length -= bytesRead; + + // Advance position unless we are using built-in position advancement + if (position !== undefined) { + position += bytesRead; + } + } + return totalBytesRead < length ? arrayBuffer.slice(0, totalBytesRead) : arrayBuffer; + } + + async write( + arrayBuffer: ArrayBuffer, + offset: number | bigint = 0, + length: number = arrayBuffer.byteLength + ): Promise { + return new Promise((resolve, reject) => { + // TODO - Node.js doesn't offer write with bigint offsets??? + const nOffset = Number(offset); + const uint8Array = new Uint8Array(arrayBuffer, Number(offset), length); + fs.write(this.handle, uint8Array, 0, length, nOffset, (err, bytesWritten) => + err ? reject(err) : resolve(bytesWritten) + ); + }); + } +} + +async function readBytes( + fd: number, + uint8Array: Uint8Array, + offset: number, + length: number, + position: number | bigint | null +): Promise { + return await new Promise((resolve, reject) => + fs.read(fd, uint8Array, offset, length, position, (err, bytesRead) => + err ? reject(err) : resolve(bytesRead) + ) + ); +} + +// TODO - implement streaming write +/* +export interface WriteStreamOptions { + flags?: string; + encoding?: 'utf8'; + fd?: number; + mode?: number; + autoClose?: boolean; + start?: number; +} + +export class NodeStreamWritableFile implements WritableFile { + outputStream: fs.WriteStream | Writable; + + constructor(pathOrStream: string | Writable, options?: WriteStreamOptions) { + this.outputStream = + typeof pathOrStream === 'string' ? fs.createWriteStream(pathOrStream, options) : pathOrStream; + } + + async write(buffer: ArrayBuffer): Promise { + return new Promise((resolve, reject) => { + const uint8Array = new Uint8Array(buffer); + this.outputStream.write(uint8Array, (err) => (err ? reject(err) : resolve())); + }); + } + + async close(): Promise { + if (this.outputStream instanceof fs.WriteStream) { + return new Promise((resolve, reject) => { + const stream = this.outputStream as fs.WriteStream; + stream.close((err) => (err ? reject(err) : resolve())); + }); + } + } +} +*/ diff --git a/modules/polyfills/src/filesystems/node-filesystem.ts b/modules/polyfills/src/filesystems/node-filesystem.ts new file mode 100644 index 0000000000..16f85ad8a1 --- /dev/null +++ b/modules/polyfills/src/filesystems/node-filesystem.ts @@ -0,0 +1,53 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {Stat, RandomAccessFileSystem} from '@loaders.gl/loader-utils'; +import fsPromise from 'fs/promises'; +import {NodeFile} from './node-file'; +import {fetchNode} from './fetch-node'; + +// import {fetchFile} from "../fetch/fetch-file" +// import {selectLoader} from "../api/select-loader"; + +/** + * FileSystem pass-through for Node.js + * Compatible with BrowserFileSystem. + * @param options + */ +export class NodeFileSystem implements RandomAccessFileSystem { + readable: boolean = true; + writable: boolean = true; + + // implements FileSystem + constructor() {} + + async readdir(dirname = '.', options?: {}): Promise { + return await fsPromise.readdir(dirname, options); + } + + async stat(path: string): Promise { + const info = await fsPromise.stat(path, {bigint: true}); + return { + size: Number(info.size), + bigsize: info.size, + isDirectory: info.isDirectory() + }; + } + + async unlink(path: string): Promise { + return await fsPromise.unlink(path); + } + + async fetch(path: string, options: RequestInit): Promise { + return await fetchNode(path, options); + } + + // implements IRandomAccessFileSystem + async openReadableFile(path: string, flags: 'r' = 'r'): Promise { + return new NodeFile(path, flags); + } + + async openWritableFile(path: string, flags: 'w' | 'wx' = 'w', mode?: any): Promise { + return new NodeFile(path, flags, mode); + } +} diff --git a/modules/polyfills/src/node/fetch/utils/stream-utils.node.ts b/modules/polyfills/src/filesystems/stream-utils.node.ts similarity index 63% rename from modules/polyfills/src/node/fetch/utils/stream-utils.node.ts rename to modules/polyfills/src/filesystems/stream-utils.node.ts index 0ea98face6..3185aea6e9 100644 --- a/modules/polyfills/src/node/fetch/utils/stream-utils.node.ts +++ b/modules/polyfills/src/filesystems/stream-utils.node.ts @@ -1,14 +1,17 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import zlib from 'zlib'; +import {Readable} from 'stream'; -import {toArrayBuffer} from './decode-data-uri.node'; +const isArrayBuffer = (x) => x && x instanceof ArrayBuffer; +const isBuffer = (x) => x && x instanceof Buffer; /** * */ -export function decompressReadStream(readStream, headers) { - switch (headers.get('content-encoding')) { +export function decompressReadStream(readStream: Readable, headers?: Headers) { + switch (headers?.get('content-encoding')) { case 'br': return readStream.pipe(zlib.createBrotliDecompress()); case 'gzip': @@ -77,3 +80,40 @@ export function concatenateArrayBuffers(sources: (ArrayBuffer | Uint8Array)[]): // We work with ArrayBuffers, discard the typed array wrapper return result.buffer; } + +/** + * @param data + * @todo Duplicate of core + */ +export function toArrayBuffer(data: unknown): ArrayBuffer { + if (isArrayBuffer(data)) { + return data as ArrayBuffer; + } + + // TODO - per docs we should just be able to call buffer.buffer, but there are issues + if (isBuffer(data)) { + // @ts-expect-error + const typedArray = new Uint8Array(data); + return typedArray.buffer; + } + + // Careful - Node Buffers will look like ArrayBuffers (keep after isBuffer) + if (ArrayBuffer.isView(data)) { + return data.buffer; + } + + if (typeof data === 'string') { + const text = data; + const uint8Array = new TextEncoder().encode(text); + return uint8Array.buffer; + } + + // HACK to support Blob polyfill + // @ts-expect-error + if (data && typeof data === 'object' && data._toArrayBuffer) { + // @ts-expect-error + return data._toArrayBuffer(); + } + + throw new Error(`toArrayBuffer(${JSON.stringify(data, null, 2).slice(10)})`); +} diff --git a/modules/polyfills/src/node/images/encode-image.node.ts b/modules/polyfills/src/images/encode-image-node.ts similarity index 100% rename from modules/polyfills/src/node/images/encode-image.node.ts rename to modules/polyfills/src/images/encode-image-node.ts diff --git a/modules/polyfills/src/images/encode-image.node.ts b/modules/polyfills/src/images/encode-image.node.ts new file mode 100644 index 0000000000..12e2512106 --- /dev/null +++ b/modules/polyfills/src/images/encode-image.node.ts @@ -0,0 +1,41 @@ +// Use stackgl modules for DOM-less reading and writing of images + +import savePixels from 'save-pixels'; +import ndarray from 'ndarray'; +import {bufferToArrayBuffer} from '../buffer/to-array-buffer.node'; + +/** + * Returns data bytes representing a compressed image in PNG or JPG format, + * This data can be saved using file system (f) methods or + * used in a request. + * @param image to save + * @param options + * @param options.type='png' - png, jpg or image/png, image/jpg are valid + * @param options.dataURI - Whether to include a data URI header + * @return {*} bytes + */ +export function encodeImageToStreamNode( + image: {data: any; width: number; height: number}, + options: {type?: string; dataURI?: string} +) { + // Support MIME type strings + const type = options.type ? options.type.replace('image/', '') : 'jpeg'; + const pixels = ndarray(image.data, [image.width, image.height, 4], [4, image.width * 4, 1], 0); + + // Note: savePixels returns a stream + return savePixels(pixels, type, options); +} + +export function encodeImageNode(image, options) { + const imageStream = encodeImageToStreamNode(image, options); + + return new Promise((resolve) => { + const buffers: any[] = []; + imageStream.on('data', (buffer) => buffers.push(buffer)); + // TODO - convert to arraybuffer? + imageStream.on('end', () => { + const buffer = Buffer.concat(buffers); + resolve(bufferToArrayBuffer(buffer)); + }); + }); +} diff --git a/modules/polyfills/src/images/parse-image-node.ts b/modules/polyfills/src/images/parse-image-node.ts new file mode 100644 index 0000000000..d537080a09 --- /dev/null +++ b/modules/polyfills/src/images/parse-image-node.ts @@ -0,0 +1,54 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import getPixels from 'get-pixels'; + +/** Declares which image format mime types this loader polyfill supports */ +export const NODE_FORMAT_SUPPORT = ['image/png', 'image/jpeg', 'image/gif']; + +// Note: These types are also defined in @loaders.gl/images and need to be kept in sync +type NDArray = { + shape: number[]; + data: Uint8Array; + width: number; + height: number; + components: number; + layers: number[]; +}; + +export async function parseImageNode(arrayBuffer: ArrayBuffer, mimeType: string): Promise { + if (!mimeType) { + throw new Error('MIMEType is required to parse image under Node.js'); + } + + const buffer = arrayBuffer instanceof Buffer ? arrayBuffer : Buffer.from(arrayBuffer); + const ndarray = await getPixelsAsync(buffer, mimeType); + return ndarray; +} + +// TODO - check if getPixels callback is asynchronous if provided with buffer input +// if not, parseImage can be a sync function +function getPixelsAsync(buffer: Buffer, mimeType: string): Promise { + return new Promise((resolve) => + getPixels(buffer, mimeType, (err, ndarray) => { + if (err) { + throw err; + } + + const shape = [...ndarray.shape]; + const layers = ndarray.shape.length === 4 ? ndarray.shape.shift() : 1; + const data = ndarray.data instanceof Buffer ? new Uint8Array(ndarray.data) : ndarray.data; + + // extract width/height etc + resolve({ + shape, + data, + width: ndarray.shape[0], + height: ndarray.shape[1], + components: ndarray.shape[2], + // TODO - error + layers: layers ? [layers] : [] + }); + }) + ); +} diff --git a/modules/polyfills/src/node/images/parse-image.node.ts b/modules/polyfills/src/images/parse-image.node.ts similarity index 97% rename from modules/polyfills/src/node/images/parse-image.node.ts rename to modules/polyfills/src/images/parse-image.node.ts index 68959e6245..d537080a09 100644 --- a/modules/polyfills/src/node/images/parse-image.node.ts +++ b/modules/polyfills/src/images/parse-image.node.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import getPixels from 'get-pixels'; diff --git a/modules/polyfills/src/index.browser.ts b/modules/polyfills/src/index.browser.ts new file mode 100644 index 0000000000..38052a7f85 --- /dev/null +++ b/modules/polyfills/src/index.browser.ts @@ -0,0 +1,16 @@ +// loaders.gl, MIT License + +export function installFilePolyfills() {} + +// Dummy export to avoid import errors in browser tests +export const NodeFileSystem = null; + +export function fetchNode(path: string, options: RequestInit): Promise { + throw new Error('fetchNode not available in browser'); +} + +// Ensure process and process.env are defined, as Node code tends to crash on these +// @ts-ignore +globalThis.process = globalThis.process || {}; +// @ts-ignore +globalThis.process.env = globalThis.process.env || {}; diff --git a/modules/polyfills/src/index.ts b/modules/polyfills/src/index.ts index d5af3a202b..44fa093b1a 100644 --- a/modules/polyfills/src/index.ts +++ b/modules/polyfills/src/index.ts @@ -1,82 +1,128 @@ /* eslint-disable dot-notation */ -import {isBrowser, global} from './utils/globals'; +import {isBrowser} from './utils/is-browser'; -import {TextDecoder, TextEncoder} from './lib/encoding'; -import {allSettled} from './promise/all-settled'; +import {TextDecoder, TextEncoder} from './text-encoder/text-encoder'; // Node specific -import * as base64 from './node/buffer/btoa.node'; +import {atob, btoa} from './buffer/btoa.node'; -import {Headers as HeadersNode} from './node/fetch/headers.node'; -import {Response as ResponseNode} from './node/fetch/response.node'; -import {fetchNode as fetchNode} from './node/fetch/fetch.node'; +import {encodeImageNode} from './images/encode-image-node'; +import {parseImageNode, NODE_FORMAT_SUPPORT} from './images/parse-image-node'; -import {encodeImageNode} from './node/images/encode-image.node'; -import {parseImageNode, NODE_FORMAT_SUPPORT} from './node/images/parse-image.node'; +// FILESYSTEM POLYFILLS +import {NodeFile} from './filesystems/node-file'; +import {NodeFileSystem} from './filesystems/node-filesystem'; +import {fetchNode} from './filesystems/fetch-node'; -export {ReadableStreamPolyfill} from './node/file/readable-stream'; -export {BlobPolyfill} from './node/file/blob'; -export {FileReaderPolyfill} from './node/file/file-reader'; -export {FilePolyfill} from './node/file/file'; -export {installFilePolyfills} from './node/file/install-file-polyfills'; -export {fetchNode as _fetchNode} from './node/fetch/fetch.node'; -export {fetchFileNode as _fetchFileNode} from './node/fetch/fetch-file.node'; +import {NodeHash} from './crypto/node-hash'; + +// NODE VERSION +import {versions} from 'node:process'; +export const nodeVersion = parseInt(versions.node.split('.')[0]); + +// STREAM POLYFILLS +import {makeNodeStream} from './streams/make-node-stream'; + +// BLOB AND FILE POLYFILLS +export {Blob_ as Blob} from './file/install-blob-polyfills'; +export {File_ as File} from './file/install-file-polyfills'; + +if (isBrowser) { + // eslint-disable-next-line no-console + console.error( + 'loaders.gl: The @loaders.gl/polyfills should only be used in Node.js environments' + ); +} + +globalThis.loaders = globalThis.loaders || {}; + +// STREAM POLYFILLS +export {makeNodeStream} from './streams/make-node-stream'; +globalThis.loaders.makeNodeStream = makeNodeStream; + +// FILESYSTEM POLYFILLS +globalThis.loaders.NodeFile = NodeFile; +globalThis.loaders.NodeFileSystem = NodeFileSystem; +globalThis.loaders.fetchNode = fetchNode; + +// CRYPTO POLYFILLS +globalThis.loaders.NodeHash = NodeHash; // POLYFILLS: TextEncoder, TextDecoder // - Recent Node versions have these classes but virtually no encodings unless special build. // - Browser: Edge, IE11 do not have these -const installTextEncoder = !isBrowser || !('TextEncoder' in global); -if (installTextEncoder) { - global['TextEncoder'] = TextEncoder; +if (!globalThis.TextEncoder) { + // @ts-expect-error + globalThis.TextEncoder = TextEncoder; } -const installTextDecoder = !isBrowser || !('TextDecoder' in global); -if (installTextDecoder) { - global['TextDecoder'] = TextDecoder; +if (!globalThis.TextDecoder) { + // @ts-expect-error + globalThis.TextDecoder = TextDecoder; } // POLYFILLS: btoa, atob // - Node: Yes // - Browser: No -if (!isBrowser && !('atob' in global) && base64.atob) { - global['atob'] = base64.atob; +if (!('atob' in globalThis) && atob) { + globalThis['atob'] = atob; } -if (!isBrowser && !('btoa' in global) && base64.btoa) { - global['btoa'] = base64.btoa; -} - -// POLYFILL: fetch -// - Node: Yes -// - Browser: No. For This polyfill is node only, IE11 etc, install external polyfill - -if (!isBrowser && !('Headers' in global) && HeadersNode) { - global['Headers'] = HeadersNode; -} - -if (!isBrowser && !('Response' in global) && ResponseNode) { - global['Response'] = ResponseNode; -} - -if (!isBrowser && !('fetch' in global) && fetchNode) { - global['fetch'] = fetchNode; +if (!('btoa' in globalThis) && btoa) { + globalThis['btoa'] = btoa; } // NODE IMAGE FUNCTIONS: // These are not official polyfills but used by the @loaders.gl/images module if installed // TODO - is there an appropriate Image API we could polyfill using an adapter? -if (!isBrowser && !('_encodeImageNode' in global) && encodeImageNode) { - global['_encodeImageNode'] = encodeImageNode; -} - -if (!isBrowser && !('_parseImageNode' in global) && parseImageNode) { - global['_parseImageNode'] = parseImageNode; - global['_imageFormatsNode'] = NODE_FORMAT_SUPPORT; -} - -if (!('allSettled' in Promise)) { - // @ts-ignore - Promise.allSettled = allSettled; +globalThis.loaders.encodeImageNode = encodeImageNode; +globalThis.loaders.parseImageNode = parseImageNode; +globalThis.loaders.imageFormatsNode = NODE_FORMAT_SUPPORT; + +// Deprecated, remove after republish +globalThis._parseImageNode = parseImageNode; +globalThis._imageFormatsNode = NODE_FORMAT_SUPPORT; + +// LOAD LIBRARY + +import { + readFileAsArrayBuffer, + readFileAsText, + requireFromFile, + requireFromString +} from './load-library/require-utils.node'; + +globalThis.loaders.readFileAsArrayBuffer = readFileAsArrayBuffer; +globalThis.loaders.readFileAsText = readFileAsText; +globalThis.loaders.requireFromFile = requireFromFile; +globalThis.loaders.requireFromString = requireFromString; + +export {installFilePolyfills} from './file/install-file-polyfills'; + +// DEPRECATED POLYFILL: +// - Node v18+: No, not needed +// - Node v16 and lower: Yes +// - Browsers (evergreen): Not needed. +// - IE11: No. This polyfill is node only, install external polyfill +import {Headers as HeadersNode} from './fetch/headers-polyfill'; +import {Response as ResponseNode} from './fetch/response-polyfill'; +import {fetchNode as fetchNodePolyfill} from './fetch/fetch-polyfill'; + +if (nodeVersion < 18) { + if (!('Headers' in globalThis) && HeadersNode) { + // @ts-ignore + globalThis.Headers = HeadersNode; + } + + if (!('Response' in globalThis) && ResponseNode) { + // @ts-ignore + globalThis.Response = ResponseNode; + } + + if (!('fetch' in globalThis) && fetchNodePolyfill) { + // @ts-ignore + globalThis.fetch = fetchNodePolyfill; + } } diff --git a/modules/polyfills/src/load-library/require-utils.node.ts b/modules/polyfills/src/load-library/require-utils.node.ts new file mode 100644 index 0000000000..4238b689e9 --- /dev/null +++ b/modules/polyfills/src/load-library/require-utils.node.ts @@ -0,0 +1,101 @@ +// Fork of https://github.com/floatdrop/require-from-string/blob/master/index.js +// Copyright (c) Vsevolod Strukchinsky (github.com/floatdrop) +// MIT license + +// this file is not visible to webpack (it is excluded in the package.json "browser" field). + +import Module from 'module'; +import path from 'path'; +import fs from 'fs'; + +/** + * Load a file from local file system + * @param filename + * @returns + */ +export async function readFileAsArrayBuffer(filename: string): Promise { + if (filename.startsWith('http')) { + const response = await fetch(filename); + return await response.arrayBuffer(); + } + const buffer = fs.readFileSync(filename); + return buffer.buffer; +} + +/** + * Load a file from local file system + * @param filename + * @returns + */ +export async function readFileAsText(filename: string): Promise { + if (filename.startsWith('http')) { + const response = await fetch(filename); + return await response.text(); + } + const text = fs.readFileSync(filename, 'utf8'); + return text; +} + +// Node.js Dynamically require from file +// Relative names are resolved relative to cwd +// This indirect function is provided because webpack will try to bundle `module.require`. +// this file is not visible to webpack (it is excluded in the package.json "browser" field). +export async function requireFromFile(filename: string): Promise { + if (filename.startsWith('http')) { + const response = await fetch(filename); + const code = await response.text(); + return requireFromString(code); + } + + if (!filename.startsWith('/')) { + filename = `${process.cwd()}/${filename}`; + } + const code = await fs.readFileSync(filename, 'utf8'); + return requireFromString(code); +} + +// Dynamically require from string +// - `code` - Required - Type: string - Module code. +// - `filename` - Type: string - Default: '' - Optional filename. +// - `options.appendPaths` Type: Array List of paths, that will be appended to module paths. +// Useful, when you want to be able require modules from these paths. +// - `options.prependPaths` Type: Array Same as appendPaths, but paths will be prepended. +export function requireFromString( + code: string, + filename = '', + options?: { + prependPaths?: string[]; + appendPaths?: string[]; + } +): any { + if (typeof filename === 'object') { + options = filename; + filename = ''; + } + filename = filename.replace('file://', ''); + + if (typeof code !== 'string') { + throw new Error(`code must be a string, not ${typeof code}`); + } + + // @ts-ignore + const paths = Module._nodeModulePaths(path.dirname(filename)); + + const parent = typeof module !== 'undefined' && module?.parent; + + // @ts-ignore + const newModule = new Module(filename, parent); + newModule.filename = filename; + newModule.paths = ([] as string[]) + .concat(options?.prependPaths || []) + .concat(paths) + .concat(options?.appendPaths || []); + // @ts-ignore + newModule._compile(code, filename); + + if (parent && parent.children) { + parent.children.splice(parent.children.indexOf(newModule), 1); + } + + return newModule.exports; +} diff --git a/modules/polyfills/src/node/fetch/fetch-file.node.ts b/modules/polyfills/src/node/fetch/fetch-file.node.ts deleted file mode 100644 index c5b7f9ec21..0000000000 --- a/modules/polyfills/src/node/fetch/fetch-file.node.ts +++ /dev/null @@ -1,51 +0,0 @@ -// loaders.gl, MIT license - -import fs from 'fs'; // `fs` will be empty object in browsers (see package.json "browser" field). -import {Response} from './response.node'; -import {Headers} from './headers.node'; - -export function isRequestURL(url: string): boolean { - return url.startsWith('http:') || url.startsWith('https:'); -} - -export async function fetchFileNode(url: string, options): Promise { - const noqueryUrl = url.split('?')[0]; - - try { - // Now open the stream - const body = await new Promise((resolve, reject) => { - // @ts-ignore - const stream = fs.createReadStream(noqueryUrl, {encoding: null}); - stream.once('readable', () => resolve(stream)); - stream.on('error', (error) => reject(error)); - }); - - const status = 200; - const statusText = 'OK'; - const headers = getHeadersForFile(noqueryUrl); - return new Response(body, {headers, status, statusText, url}); - } catch (error) { - const status = 400; - const statusText = (error as Error).message; - const headers = {}; - return new Response((error as Error).message, {headers, status, statusText, url}); - } -} - -function getHeadersForFile(noqueryUrl: string): Headers { - const headers = {}; - - // Fix up content length if we can for best progress experience - if (!headers['content-length']) { - const stats = fs.statSync(noqueryUrl); - headers['content-length'] = stats.size; - } - - // Automatically decompress gzipped files with .gz extension - if (noqueryUrl.endsWith('.gz')) { - noqueryUrl = noqueryUrl.slice(0, -3); - headers['content-encoding'] = 'gzip'; - } - - return new Headers(headers); -} diff --git a/modules/polyfills/src/node/file/install-file-polyfills.ts b/modules/polyfills/src/node/file/install-file-polyfills.ts deleted file mode 100644 index 91e5203249..0000000000 --- a/modules/polyfills/src/node/file/install-file-polyfills.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {ReadableStreamPolyfill} from './readable-stream'; -import {BlobPolyfill} from './blob'; -import {FileReaderPolyfill} from './file-reader'; -import {FilePolyfill} from './file'; - -export function installFilePolyfills() { - if (typeof ReadableStream === 'undefined' && global) { - // @ts-ignore; - global.ReadableStream = ReadableStreamPolyfill; - } - - if (typeof Blob === 'undefined' && global) { - // @ts-ignore; - global.Blob = BlobPolyfill; - } - - if (typeof FileReader === 'undefined' && global) { - // @ts-ignore; - global.FileReader = FileReaderPolyfill; - } - - // Install minimal Node.js File polyfill - if (typeof File === 'undefined' && global) { - // @ts-ignore; - global.File = FilePolyfill; - } -} diff --git a/modules/polyfills/src/promise/all-settled.ts b/modules/polyfills/src/promise/all-settled.ts deleted file mode 100644 index a2a12a9c72..0000000000 --- a/modules/polyfills/src/promise/all-settled.ts +++ /dev/null @@ -1,21 +0,0 @@ -export const REJECTED_STATUS = 'rejected'; -export const FULFILLED_STATUS = 'fulfilled'; - -/** - * Handle list of promises and return all values regardless of results. - * Polyfill for Promise.allSettled() method. - * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled - * @param promises - */ -export function allSettled(promises: Promise[]): Promise { - const mappedPromises = promises.map((promise) => { - return promise - .then((value) => { - return {status: FULFILLED_STATUS, value}; - }) - .catch((reason) => { - return {status: REJECTED_STATUS, reason}; - }); - }); - return Promise.all(mappedPromises); -} diff --git a/modules/core/src/iterators/make-stream/make-node-stream.ts b/modules/polyfills/src/streams/make-node-stream.ts similarity index 90% rename from modules/core/src/iterators/make-stream/make-node-stream.ts rename to modules/polyfills/src/streams/make-node-stream.ts index ad960eb9ae..fb851e8a61 100644 --- a/modules/core/src/iterators/make-stream/make-node-stream.ts +++ b/modules/polyfills/src/streams/make-node-stream.ts @@ -9,13 +9,16 @@ const Readable = Stream.Readable || _Readable; export type MakeStreamOptions = ReadableOptions; /** Builds a node stream from an iterator */ -export function makeStream( +export function makeNodeStream( source: Iterable | AsyncIterable, options?: ReadableOptions ): ReadableType { + // @ts-ignore AsyncGenerator const iterator = source[Symbol.asyncIterator] - ? source[Symbol.asyncIterator]() - : source[Symbol.iterator](); + ? // @ts-ignore AsyncGenerator + source[Symbol.asyncIterator]() + : // @ts-ignore AsyncGenerator + source[Symbol.iterator](); return new AsyncIterableReadable(iterator, options); } diff --git a/modules/polyfills/src/lib/encoding-indexes.ts b/modules/polyfills/src/text-encoder/encoding-indexes.ts similarity index 100% rename from modules/polyfills/src/lib/encoding-indexes.ts rename to modules/polyfills/src/text-encoder/encoding-indexes.ts diff --git a/modules/polyfills/src/lib/encoding.ts b/modules/polyfills/src/text-encoder/text-encoder.ts similarity index 99% rename from modules/polyfills/src/lib/encoding.ts rename to modules/polyfills/src/text-encoder/text-encoder.ts index d1039c8885..729072cae6 100644 --- a/modules/polyfills/src/lib/encoding.ts +++ b/modules/polyfills/src/text-encoder/text-encoder.ts @@ -8,7 +8,7 @@ import indexes from './encoding-indexes'; // Note: Aaian character indices add half a megabyte to bundle. Ignore, since we really only want the built-in UTF8... // import indexes from './encoding-indexes-asian.js'; -global['encoding-indexes'] = indexes || {}; +globalThis['encoding-indexes'] = indexes || {}; // // Utilities @@ -720,10 +720,10 @@ function indexPointerFor(code_point, index) { * @return {(!Array.|!Array.>)} * */ function index(name) { - if (!('encoding-indexes' in global)) { + if (!('encoding-indexes' in globalThis)) { throw Error('Indexes missing.' + ' Did you forget to include encoding-indexes.js first?'); } - return global['encoding-indexes'][name]; + return globalThis['encoding-indexes'][name]; } /** @@ -1121,7 +1121,7 @@ function TextEncoder(label, options) { // Standard behavior. enc._encoding = getEncoding('utf-8'); - if (label !== undefined && 'console' in global) { + if (label !== undefined && 'console' in globalThis) { console.warn('TextEncoder constructor called with encoding label, ' + 'which is ignored.'); } } @@ -1483,7 +1483,7 @@ function SingleByteEncoder(index, options) { } (function () { - if (!('encoding-indexes' in global)) return; + if (!('encoding-indexes' in globalThis)) return; encodings.forEach(function (category) { if (category.heading !== 'Legacy single-byte encodings') return; category.encodings.forEach(function (encoding) { @@ -3077,8 +3077,8 @@ decoders['x-user-defined'] = function (options) { }; // FORK -// if (!global['TextEncoder']) global['TextEncoder'] = TextEncoder; -// if (!global['TextDecoder']) global['TextDecoder'] = TextDecoder; +// if (!globalThis['TextEncoder']) globalThis['TextEncoder'] = TextEncoder; +// if (!globalThis['TextDecoder']) globalThis['TextDecoder'] = TextDecoder; // babel.config.js skip transpiling files in `libs/` // module.exports = {TextEncoder, TextDecoder}; export {TextEncoder, TextDecoder}; diff --git a/modules/polyfills/src/utils/globals.ts b/modules/polyfills/src/utils/globals.ts deleted file mode 100644 index a37f569cf7..0000000000 --- a/modules/polyfills/src/utils/globals.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2015 - 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Purpose: include this in your module to avoids adding dependencies on -// micro modules like 'global' and 'is-browser'; - -/* eslint-disable no-restricted-globals */ -const isBrowser: boolean = - // @ts-ignore process.browser - typeof process !== 'object' || String(process) !== '[object process]' || process.browser; - -const globals = { - self: typeof self !== 'undefined' && self, - window: typeof window !== 'undefined' && window, - global: typeof global !== 'undefined' && global -}; - -const global_: object = (globals.global || globals.self || globals.window) as object; - -export {isBrowser, global_ as global}; diff --git a/modules/polyfills/src/utils/is-browser.ts b/modules/polyfills/src/utils/is-browser.ts new file mode 100644 index 0000000000..15dd4433cb --- /dev/null +++ b/modules/polyfills/src/utils/is-browser.ts @@ -0,0 +1,7 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +/* eslint-disable no-restricted-globals */ +export const isBrowser: boolean = + // @ts-ignore process.browser + typeof process !== 'object' || String(process) !== '[object process]' || process.browser; diff --git a/modules/polyfills/test/fetch-node/decode-data-uri.spec.js b/modules/polyfills/test/fetch-node/decode-data-uri.spec.js index a9b592df61..1376bd1a70 100644 --- a/modules/polyfills/test/fetch-node/decode-data-uri.spec.js +++ b/modules/polyfills/test/fetch-node/decode-data-uri.spec.js @@ -3,60 +3,62 @@ import test from 'tape-promise/tape'; import {isBrowser} from '@loaders.gl/core'; -import {decodeDataUri} from '../../src/node/fetch/utils/decode-data-uri.node'; +import {decodeDataUri} from '../../src/fetch/decode-data-uri'; const toString = (arrayBuffer) => new TextDecoder().decode(arrayBuffer); -// eslint-disable-next-line max-statements -test('decodeDataUri', (t) => { - if (isBrowser) { - t.comment('decodeDataUri() currently only works in Node.js'); +if (!isBrowser) { + // eslint-disable-next-line max-statements + test('decodeDataUri', (t) => { + if (isBrowser) { + t.comment('decodeDataUri() currently only works in Node.js'); + t.end(); + return; + } + + let obj; + let arrayBuffer; + + obj = decodeDataUri('data:text/html;base64,PGh0bWw+'); + t.equals(obj.mimeType, 'text/html', 'should record down correct MIME type'); + + obj = decodeDataUri('data:text/plain;base64,SSBsb3ZlIHlvdSE'); + arrayBuffer = obj.arrayBuffer; + t.ok(arrayBuffer instanceof ArrayBuffer); + t.equals(toString(arrayBuffer), 'I love you!', 'should work with non-padded base64 data URIs'); + + obj = decodeDataUri('data:text/plain;base64,SSBsb3ZlIHlvdSE='); + arrayBuffer = obj.arrayBuffer; + t.ok(arrayBuffer instanceof ArrayBuffer); + t.equals(toString(arrayBuffer), 'I love you!', 'should work with padded base64 data URIs'); + + obj = decodeDataUri('data:text/plain,important content!'); + arrayBuffer = obj.arrayBuffer; + t.ok(arrayBuffer instanceof ArrayBuffer); + t.equals(toString(arrayBuffer), 'important content!', 'should work with plain data URIs'); + + obj = decodeDataUri('data:,important content!'); + t.equals(obj.mimeType, 'text/plain;charset=US-ASCII', 'should set default MIME type'); + + arrayBuffer = obj.arrayBuffer; + t.ok(arrayBuffer instanceof ArrayBuffer); + t.equals(toString(arrayBuffer), 'important content!', 'should work with default MIME type'); + + obj = decodeDataUri('data:;charset=utf-8,important content!'); + t.equals( + obj.mimeType, + 'text/plain;charset=utf-8', + 'should allow implicit text/plain with charset' + ); + + arrayBuffer = obj.arrayBuffer; + t.ok(arrayBuffer instanceof ArrayBuffer); + t.equals( + toString(arrayBuffer), + 'important content!', + 'should allow implicit text/plain with charset' + ); + t.end(); - return; - } - - let obj; - let arrayBuffer; - - obj = decodeDataUri('data:text/html;base64,PGh0bWw+'); - t.equals(obj.mimeType, 'text/html', 'should record down correct MIME type'); - - obj = decodeDataUri('data:text/plain;base64,SSBsb3ZlIHlvdSE'); - arrayBuffer = obj.arrayBuffer; - t.ok(arrayBuffer instanceof ArrayBuffer); - t.equals(toString(arrayBuffer), 'I love you!', 'should work with non-padded base64 data URIs'); - - obj = decodeDataUri('data:text/plain;base64,SSBsb3ZlIHlvdSE='); - arrayBuffer = obj.arrayBuffer; - t.ok(arrayBuffer instanceof ArrayBuffer); - t.equals(toString(arrayBuffer), 'I love you!', 'should work with padded base64 data URIs'); - - obj = decodeDataUri('data:text/plain,important content!'); - arrayBuffer = obj.arrayBuffer; - t.ok(arrayBuffer instanceof ArrayBuffer); - t.equals(toString(arrayBuffer), 'important content!', 'should work with plain data URIs'); - - obj = decodeDataUri('data:,important content!'); - t.equals(obj.mimeType, 'text/plain;charset=US-ASCII', 'should set default MIME type'); - - arrayBuffer = obj.arrayBuffer; - t.ok(arrayBuffer instanceof ArrayBuffer); - t.equals(toString(arrayBuffer), 'important content!', 'should work with default MIME type'); - - obj = decodeDataUri('data:;charset=utf-8,important content!'); - t.equals( - obj.mimeType, - 'text/plain;charset=utf-8', - 'should allow implicit text/plain with charset' - ); - - arrayBuffer = obj.arrayBuffer; - t.ok(arrayBuffer instanceof ArrayBuffer); - t.equals( - toString(arrayBuffer), - 'important content!', - 'should allow implicit text/plain with charset' - ); - - t.end(); -}); + }); +} diff --git a/modules/polyfills/test/fetch-node/fetch.node.spec.js b/modules/polyfills/test/fetch-node/fetch.node.spec.js index 6a476c9d0c..bd4b49fd1d 100644 --- a/modules/polyfills/test/fetch-node/fetch.node.spec.js +++ b/modules/polyfills/test/fetch-node/fetch.node.spec.js @@ -1,5 +1,4 @@ import test from 'tape-promise/tape'; -import '@loaders.gl/polyfills'; import {isBrowser, fetchFile} from '@loaders.gl/core'; const GITHUB_MASTER = 'https://raw.githubusercontent.com/visgl/loaders.gl/master/modules/'; @@ -16,127 +15,129 @@ const TEXT_URL_WITH_REDIRECT = `https://unpkg.com/@loaders.gl/textures@beta/dist const REDIRECT_URL = 'https://github.com/visgl/deck.gl-data/raw/master/3d-tiles/RoyalExhibitionBuilding/1/1.pnts'; -test('polyfills#fetch() (NODE)', async (t) => { - if (!isBrowser) { - const response = await fetch(PLY_CUBE_ATT_URL); - t.ok(response.headers, 'fetch polyfill successfully returned headers under Node.js'); - const data = await response.arrayBuffer(); - t.ok(data, 'fetch polyfill successfully loaded data under Node.js'); - } - t.end(); -}); - -test('polyfills#fetch() ignores url query params when loading file (NODE)', async (t) => { - if (!isBrowser) { - const response = await fetch(`${PLY_CUBE_ATT_URL}?v=1.2.3`); - const data = await response.text(); - t.ok(response.headers, 'fetch polyfill successfully returned headers under Node.js'); - t.ok(data, 'fetch polyfill successfully loaded data under Node.js'); - } - t.end(); -}); - -test('polyfills#fetch() error handling (NODE)', async (t) => { - if (!isBrowser) { - let response = await fetch('non-existent-file'); - t.comment(response.statusText); - t.ok(response.statusText.includes('ENOENT'), 'fetch statusText forwards node ENOENT error'); - t.notOk(response.ok, 'fetch polyfill fails cleanly on non-existent file'); - t.ok(response.arrayBuffer(), 'Response.arrayBuffer() does not throw'); - - response = await fetch('.'); - t.comment(response.statusText); - t.ok(response.statusText.includes('EISDIR'), 'fetch statusText forwards node error'); - t.notOk(response.ok, 'fetch polyfill fails cleanly on directory'); - t.ok(response.arrayBuffer(), 'Response.arrayBuffer() does not throw'); - } - t.end(); -}); - -test('polyfills#fetch() able to handle "Accept-Encoding: gzip" (NODE)', async (t) => { - if (!isBrowser) { - // Github will serve the desired compression - const headers = { - 'Accept-Encoding': 'gzip' - }; - // Test will pass even if server will refuse to encode into 'gzip' and just return plaintext - // In case of GitHub URL, it's honoring gzip and properly returning compressed data - const response = await fetch(PLY_CUBE_ATT_URL, {headers}); - const data = await response.text(); - t.equal(data.length, PLY_CUBE_ATT_SIZE, 'fetch polyfill data size as expected'); - t.ok(data, 'fetch polyfill successfully loaded data under Node.js with "gzip" encoding'); - } - t.end(); -}); - -test('polyfills#fetch() able to handle "Accept-Encoding: br" (NODE)', async (t) => { - if (!isBrowser) { - // Github will serve the desired compression - const headers = { - 'Accept-Encoding': 'br' - }; - // Test will pass even if server will refuse to encode into 'br' and just return plaintext - const response = await fetch(PLY_CUBE_ATT_URL, {headers}); - const data = await response.text(); - t.ok(data.length === PLY_CUBE_ATT_SIZE, 'fetch polyfill data size as expected'); - t.ok(data, 'fetch polyfill successfully loaded data under Node.js with "br" encoding'); - } - t.end(); -}); - -test('polyfills#fetch() able to handle "Accept-Encoding: deflate"', async (t) => { - if (!isBrowser) { - // Github will serve the desired compression - const headers = { - 'Accept-Encoding': 'deflate' - }; - // Test will pass even if server will refuse to encode into 'deflate' and just return plaintext - const response = await fetch(PLY_CUBE_ATT_URL, {headers}); - const data = await response.text(); - t.ok(data.length === PLY_CUBE_ATT_SIZE, 'fetch polyfill data size as expected'); - t.ok(data, 'fetch polyfill successfully loaded data under Node.js with "deflate" encoding'); - } - t.end(); -}); - -test('polyfills#fetch() able to decompress .gz extension (NODE)', async (t) => { - let response = await fetchFile(TEXT_URL); - t.ok(response.ok, response.statusText); - let data = await response.text(); - t.equal(data, '123456', 'fetch polyfill correctly read text file'); - - if (!isBrowser) { - response = await fetchFile(TEXT_URL_GZIPPED); +if (!isBrowser) { + test('polyfills#fetch() (NODE)', async (t) => { + if (!isBrowser) { + const response = await fetch(PLY_CUBE_ATT_URL); + t.ok(response.headers, 'fetch polyfill successfully returned headers under Node.js'); + const data = await response.arrayBuffer(); + t.ok(data, 'fetch polyfill successfully loaded data under Node.js'); + } + t.end(); + }); + + test('polyfills#fetch() ignores url query params when loading file (NODE)', async (t) => { + if (!isBrowser) { + const response = await fetch(`${PLY_CUBE_ATT_URL}?v=1.2.3`); + const data = await response.text(); + t.ok(response.headers, 'fetch polyfill successfully returned headers under Node.js'); + t.ok(data, 'fetch polyfill successfully loaded data under Node.js'); + } + t.end(); + }); + + test.skip('polyfills#fetch() error handling (NODE)', async (t) => { + if (!isBrowser) { + let response = await fetch('non-existent-file'); + t.comment(response.statusText); + t.ok(response.statusText.includes('ENOENT'), 'fetch statusText forwards node ENOENT error'); + t.notOk(response.ok, 'fetch polyfill fails cleanly on non-existent file'); + t.ok(response.arrayBuffer(), 'Response.arrayBuffer() does not throw'); + + response = await fetch('.'); + t.comment(response.statusText); + t.ok(response.statusText.includes('EISDIR'), 'fetch statusText forwards node error'); + t.notOk(response.ok, 'fetch polyfill fails cleanly on directory'); + t.ok(response.arrayBuffer(), 'Response.arrayBuffer() does not throw'); + } + t.end(); + }); + + test('polyfills#fetch() able to handle "Accept-Encoding: gzip" (NODE)', async (t) => { + if (!isBrowser) { + // Github will serve the desired compression + const headers = { + 'Accept-Encoding': 'gzip' + }; + // Test will pass even if server will refuse to encode into 'gzip' and just return plaintext + // In case of GitHub URL, it's honoring gzip and properly returning compressed data + const response = await fetch(PLY_CUBE_ATT_URL, {headers}); + const data = await response.text(); + t.equal(data.length, PLY_CUBE_ATT_SIZE, 'fetch polyfill data size as expected'); + t.ok(data, 'fetch polyfill successfully loaded data under Node.js with "gzip" encoding'); + } + t.end(); + }); + + test('polyfills#fetch() able to handle "Accept-Encoding: br" (NODE)', async (t) => { + if (!isBrowser) { + // Github will serve the desired compression + const headers = { + 'Accept-Encoding': 'br' + }; + // Test will pass even if server will refuse to encode into 'br' and just return plaintext + const response = await fetch(PLY_CUBE_ATT_URL, {headers}); + const data = await response.text(); + t.ok(data.length === PLY_CUBE_ATT_SIZE, 'fetch polyfill data size as expected'); + t.ok(data, 'fetch polyfill successfully loaded data under Node.js with "br" encoding'); + } + t.end(); + }); + + test('polyfills#fetch() able to handle "Accept-Encoding: deflate"', async (t) => { + if (!isBrowser) { + // Github will serve the desired compression + const headers = { + 'Accept-Encoding': 'deflate' + }; + // Test will pass even if server will refuse to encode into 'deflate' and just return plaintext + const response = await fetch(PLY_CUBE_ATT_URL, {headers}); + const data = await response.text(); + t.ok(data.length === PLY_CUBE_ATT_SIZE, 'fetch polyfill data size as expected'); + t.ok(data, 'fetch polyfill successfully loaded data under Node.js with "deflate" encoding'); + } + t.end(); + }); + + test.skip('polyfills#fetch() able to decompress .gz extension (NODE)', async (t) => { + let response = await fetchFile(TEXT_URL); t.ok(response.ok, response.statusText); - data = await response.text(); - t.equal(data, '123456', 'fetch polyfill correctly decompressed gzipped ".gz" file'); - } - t.end(); -}); - -test('polyfills#fetch() should follow redirect if `followRedirect` option is true', async (t) => { - if (!isBrowser) { - const defaultFetchResponse = await fetch(REDIRECT_URL); - t.equal(defaultFetchResponse.status, 200); - - const defaultResponse = await fetchFile(REDIRECT_URL, {}); - t.equal(defaultResponse.status, 200); - - // @ts-ignore - TODO/ActionEngine - const successResponse = await fetchFile(REDIRECT_URL, {followRedirect: true}); - t.equal(successResponse.status, 200); - - // TODO/ActionEngine - restore - // const failedResponse = await fetchFile(REDIRECT_URL, {followRedirect: false}); - // t.equal(failedResponse.status, 302); - } - t.end(); -}); - -test('polyfills#fetch() should follow redirect if header location doesn`t have protocol and origin', async (t) => { - if (!isBrowser) { - const defaultFetchResponse = await fetch(TEXT_URL_WITH_REDIRECT); - t.equal(defaultFetchResponse.status, 200); - } - t.end(); -}); + let data = await response.text(); + t.equal(data, '123456', 'fetch polyfill correctly read text file'); + + if (!isBrowser) { + response = await fetchFile(TEXT_URL_GZIPPED); + t.ok(response.ok, response.statusText); + data = await response.text(); + t.equal(data, '123456', 'fetch polyfill correctly decompressed gzipped ".gz" file'); + } + t.end(); + }); + + test('polyfills#fetch() should follow redirect if `followRedirect` option is true', async (t) => { + if (!isBrowser) { + const defaultFetchResponse = await fetch(REDIRECT_URL); + t.equal(defaultFetchResponse.status, 200); + + const defaultResponse = await fetchFile(REDIRECT_URL, {}); + t.equal(defaultResponse.status, 200); + + // @ts-ignore - TODO/ActionEngine + const successResponse = await fetchFile(REDIRECT_URL, {followRedirect: true}); + t.equal(successResponse.status, 200); + + // TODO/ActionEngine - restore + // const failedResponse = await fetchFile(REDIRECT_URL, {followRedirect: false}); + // t.equal(failedResponse.status, 302); + } + t.end(); + }); + + test('polyfills#fetch() should follow redirect if header location doesn`t have protocol and origin', async (t) => { + if (!isBrowser) { + const defaultFetchResponse = await fetch(TEXT_URL_WITH_REDIRECT); + t.equal(defaultFetchResponse.status, 200); + } + t.end(); + }); +} diff --git a/modules/polyfills/test/fetch-node/headers.node.spec.ts b/modules/polyfills/test/fetch-node/headers.node.spec.ts index ab62622e11..2e92f22ce3 100644 --- a/modules/polyfills/test/fetch-node/headers.node.spec.ts +++ b/modules/polyfills/test/fetch-node/headers.node.spec.ts @@ -1,188 +1,192 @@ // Based on https://github.com/github/fetch under MIT license import test from 'tape-promise/tape'; -import '@loaders.gl/polyfills'; -// import {isBrowser} from '@loaders.gl/core'; - -// https://fetch.spec.whatwg.org/#headers-class -// Run the tests both under browser and Node (ensures they conform to built-in) -test('constructor copies headers', (t) => { - const original = new Headers(); - original.append('Accept', 'application/json'); - original.append('Accept', 'text/plain'); - original.append('Content-Type', 'text/html'); - - const headers = new Headers(original); - t.equal(headers.get('Accept'), 'application/json, text/plain'); - t.equal(headers.get('Content-type'), 'text/html'); - t.end(); -}); - -test('constructor works with arrays', (t) => { - const array: [string, string][] = [ - ['Content-Type', 'text/xml'], - ['Breaking-Bad', '<3'] - ]; - const headers = new Headers(array); - - t.equal(headers.get('Content-Type'), 'text/xml'); - t.equal(headers.get('Breaking-Bad'), '<3'); - t.end(); -}); - -test('headers are case insensitive', (t) => { - const headers = new Headers({Accept: 'application/json'}); - t.equal(headers.get('ACCEPT'), 'application/json'); - t.equal(headers.get('Accept'), 'application/json'); - t.equal(headers.get('accept'), 'application/json'); - t.end(); -}); - -test('appends to existing', (t) => { - const headers = new Headers({Accept: 'application/json'}); - t.notOk(headers.has('Content-Type')); - headers.append('Content-Type', 'application/json'); - t.ok(headers.has('Content-Type')); - t.equal(headers.get('Content-Type'), 'application/json'); - t.end(); -}); - -test('appends values to existing header name', (t) => { - const headers = new Headers({Accept: 'application/json'}); - headers.append('Accept', 'text/plain'); - t.equal(headers.get('Accept'), 'application/json, text/plain'); - t.end(); -}); - -test('sets header name and value', (t) => { - const headers = new Headers(); - headers.set('Content-Type', 'application/json'); - t.equal(headers.get('Content-Type'), 'application/json'); - t.end(); -}); - -test('returns null on no header found', (t) => { - const headers = new Headers(); - t.equals(headers.get('Content-Type'), null); - t.end(); -}); - -test('has headers that are set', (t) => { - const headers = new Headers(); - headers.set('Content-Type', 'application/json'); - t.ok(headers.has('Content-Type')); - t.end(); -}); - -test('deletes headers', (t) => { - const headers = new Headers(); - headers.set('Content-Type', 'application/json'); - t.ok(headers.has('Content-Type')); - headers.delete('Content-Type'); - t.notOk(headers.has('Content-Type')); - t.equals(headers.get('Content-Type'), null); - t.end(); -}); - -test('converts field name to string on set and get', (t) => { - const headers = new Headers(); - // @ts-ignore - headers.set(1, 'application/json'); - t.ok(headers.has('1')); - // @ts-ignore - t.equal(headers.get(1), 'application/json'); - t.end(); -}); - -test('converts field value to string on set and get', (t) => { - const headers = new Headers(); - // @ts-ignore - headers.set('Content-Type', 1); - // @ts-ignore - headers.set('X-CSRF-Token', undefined); - t.equal(headers.get('Content-Type'), '1'); - t.equal(headers.get('X-CSRF-Token'), 'undefined'); - t.end(); -}); - -test('throws TypeError on invalid character in field name', (t) => { - // @ts-ignore - t.throws(() => new Headers({'[Accept]': 'application/json'}), TypeError); - // @ts-ignore - t.throws(() => new Headers({'Accept:': 'application/json'}), TypeError); - // @ts-ignore - t.throws(() => new Headers().set({field: 'value'}, 'application/json'), TypeError); - // @ts-ignore - t.throws(() => new Headers({'': 'application/json'}), TypeError); - t.end(); -}); - -test('is iterable with forEach', (t) => { - // featureDependent(!brokenFF) - // featureDependent(test, !brokenFF); - const headers = new Headers(); - headers.append('Accept', 'application/json'); - headers.append('Accept', 'text/plain'); - headers.append('Content-Type', 'text/html'); - - const results: {value: string; key: string; object: Headers}[] = []; - headers.forEach((value, key, object) => results.push({value, key, object})); - - t.equal(results.length, 2); - t.deepEqual({key: 'accept', value: 'application/json, text/plain', object: headers}, results[0]); - t.deepEqual({key: 'content-type', value: 'text/html', object: headers}, results[1]); - t.end(); -}); - -test('forEach accepts second thisArg argument', (t) => { - const headers = new Headers({Accept: 'application/json'}); - const thisArg = 42; - // eslint-disable-next-line no-invalid-this - headers.forEach(function () { +import {isBrowser} from '@loaders.gl/core'; + +if (!isBrowser) { + // https://fetch.spec.whatwg.org/#headers-class + // Run the tests both under browser and Node (ensures they conform to built-in) + test('constructor copies headers', (t) => { + const original = new Headers(); + original.append('Accept', 'application/json'); + original.append('Accept', 'text/plain'); + original.append('Content-Type', 'text/html'); + + const headers = new Headers(original); + t.equal(headers.get('Accept'), 'application/json, text/plain'); + t.equal(headers.get('Content-type'), 'text/html'); + t.end(); + }); + + test('constructor works with arrays', (t) => { + const array: [string, string][] = [ + ['Content-Type', 'text/xml'], + ['Breaking-Bad', '<3'] + ]; + const headers = new Headers(array); + + t.equal(headers.get('Content-Type'), 'text/xml'); + t.equal(headers.get('Breaking-Bad'), '<3'); + t.end(); + }); + + test('headers are case insensitive', (t) => { + const headers = new Headers({Accept: 'application/json'}); + t.equal(headers.get('ACCEPT'), 'application/json'); + t.equal(headers.get('Accept'), 'application/json'); + t.equal(headers.get('accept'), 'application/json'); + t.end(); + }); + + test('appends to existing', (t) => { + const headers = new Headers({Accept: 'application/json'}); + t.notOk(headers.has('Content-Type')); + headers.append('Content-Type', 'application/json'); + t.ok(headers.has('Content-Type')); + t.equal(headers.get('Content-Type'), 'application/json'); + t.end(); + }); + + test('appends values to existing header name', (t) => { + const headers = new Headers({Accept: 'application/json'}); + headers.append('Accept', 'text/plain'); + t.equal(headers.get('Accept'), 'application/json, text/plain'); + t.end(); + }); + + test('sets header name and value', (t) => { + const headers = new Headers(); + headers.set('Content-Type', 'application/json'); + t.equal(headers.get('Content-Type'), 'application/json'); + t.end(); + }); + + test('returns null on no header found', (t) => { + const headers = new Headers(); + t.equals(headers.get('Content-Type'), null); + t.end(); + }); + + test('has headers that are set', (t) => { + const headers = new Headers(); + headers.set('Content-Type', 'application/json'); + t.ok(headers.has('Content-Type')); + t.end(); + }); + + test('deletes headers', (t) => { + const headers = new Headers(); + headers.set('Content-Type', 'application/json'); + t.ok(headers.has('Content-Type')); + headers.delete('Content-Type'); + t.notOk(headers.has('Content-Type')); + t.equals(headers.get('Content-Type'), null); + t.end(); + }); + + test('converts field name to string on set and get', (t) => { + const headers = new Headers(); // @ts-ignore - t.equal(this, thisArg); - }, thisArg); - t.end(); -}); - -test('is iterable with keys', (t) => { - // featureDependent(!brokenFF) - const headers = new Headers(); - headers.append('Accept', 'application/json'); - headers.append('Accept', 'text/plain'); - headers.append('Content-Type', 'text/html'); - - const iterator = headers.keys(); - t.deepEqual({done: false, value: 'accept'}, iterator.next()); - t.deepEqual({done: false, value: 'content-type'}, iterator.next()); - t.deepEqual({done: true, value: undefined}, iterator.next()); - t.end(); -}); - -test('is iterable with values', (t) => { - // featureDependent(!brokenFF) - const headers = new Headers(); - headers.append('Accept', 'application/json'); - headers.append('Accept', 'text/plain'); - headers.append('Content-Type', 'text/html'); - - const iterator = headers.values(); - t.deepEqual({done: false, value: 'application/json, text/plain'}, iterator.next()); - t.deepEqual({done: false, value: 'text/html'}, iterator.next()); - t.deepEqual({done: true, value: undefined}, iterator.next()); - t.end(); -}); - -test('is iterable with entries', (t) => { - // featureDependent(!brokenFF) - const headers = new Headers(); - headers.append('Accept', 'application/json'); - headers.append('Accept', 'text/plain'); - headers.append('Content-Type', 'text/html'); - - const iterator = headers.entries(); - t.deepEqual({done: false, value: ['accept', 'application/json, text/plain']}, iterator.next()); - t.deepEqual({done: false, value: ['content-type', 'text/html']}, iterator.next()); - t.deepEqual({done: true, value: undefined}, iterator.next()); - t.end(); -}); + headers.set(1, 'application/json'); + t.ok(headers.has('1')); + // @ts-ignore + t.equal(headers.get(1), 'application/json'); + t.end(); + }); + + test('converts field value to string on set and get', (t) => { + const headers = new Headers(); + // @ts-ignore + headers.set('Content-Type', 1); + // @ts-ignore + headers.set('X-CSRF-Token', undefined); + t.equal(headers.get('Content-Type'), '1'); + t.equal(headers.get('X-CSRF-Token'), 'undefined'); + t.end(); + }); + + test('throws TypeError on invalid character in field name', (t) => { + // @ts-ignore + // t.throws(() => new Headers({'[Accept]': 'application/json'}), TypeError); + // @ts-ignore + // t.throws(() => new Headers({'Accept:': 'application/json'}), TypeError); + // @ts-ignore + t.throws(() => new Headers().set({field: 'value'}, 'application/json'), TypeError); + // @ts-ignore + t.throws(() => new Headers({'': 'application/json'}), TypeError); + t.end(); + }); + + test('is iterable with forEach', (t) => { + // featureDependent(!brokenFF) + // featureDependent(test, !brokenFF); + const headers = new Headers(); + headers.append('Accept', 'application/json'); + headers.append('Accept', 'text/plain'); + headers.append('Content-Type', 'text/html'); + + const results: {value: string; key: string; object: Headers}[] = []; + headers.forEach((value, key, object) => results.push({value, key, object})); + + t.equal(results.length, 2); + t.deepEqual( + {key: 'accept', value: 'application/json, text/plain', object: headers}, + results[0] + ); + t.deepEqual({key: 'content-type', value: 'text/html', object: headers}, results[1]); + t.end(); + }); + + test('forEach accepts second thisArg argument', (t) => { + const headers = new Headers({Accept: 'application/json'}); + const thisArg = 42; + // eslint-disable-next-line no-invalid-this + headers.forEach(function () { + // @ts-ignore + t.equal(this, thisArg); + }, thisArg); + t.end(); + }); + + test('is iterable with keys', (t) => { + // featureDependent(!brokenFF) + const headers = new Headers(); + headers.append('Accept', 'application/json'); + headers.append('Accept', 'text/plain'); + headers.append('Content-Type', 'text/html'); + + const iterator = headers.keys(); + t.deepEqual({done: false, value: 'accept'}, iterator.next()); + t.deepEqual({done: false, value: 'content-type'}, iterator.next()); + t.deepEqual({done: true, value: undefined}, iterator.next()); + t.end(); + }); + + test('is iterable with values', (t) => { + // featureDependent(!brokenFF) + const headers = new Headers(); + headers.append('Accept', 'application/json'); + headers.append('Accept', 'text/plain'); + headers.append('Content-Type', 'text/html'); + + const iterator = headers.values(); + t.deepEqual({done: false, value: 'application/json, text/plain'}, iterator.next()); + t.deepEqual({done: false, value: 'text/html'}, iterator.next()); + t.deepEqual({done: true, value: undefined}, iterator.next()); + t.end(); + }); + + test('is iterable with entries', (t) => { + // featureDependent(!brokenFF) + const headers = new Headers(); + headers.append('Accept', 'application/json'); + headers.append('Accept', 'text/plain'); + headers.append('Content-Type', 'text/html'); + + const iterator = headers.entries(); + t.deepEqual({done: false, value: ['accept', 'application/json, text/plain']}, iterator.next()); + t.deepEqual({done: false, value: ['content-type', 'text/html']}, iterator.next()); + t.deepEqual({done: true, value: undefined}, iterator.next()); + t.end(); + }); +} diff --git a/modules/polyfills/test/fetch-node/response.node.spec.js b/modules/polyfills/test/fetch-node/response.node.spec.js index 7480472c65..eb3069381b 100644 --- a/modules/polyfills/test/fetch-node/response.node.spec.js +++ b/modules/polyfills/test/fetch-node/response.node.spec.js @@ -1,12 +1,14 @@ // Based on https://github.com/github/fetch under MIT license import test from 'tape-promise/tape'; -import '@loaders.gl/polyfills'; +import {isBrowser} from '@loaders.gl/core'; -// https://fetch.spec.whatwg.org/#response-class -// Run the tests both under browser and Node (ensures they conform to built-in) -test('constructor response', (t) => { - const response = new Response('', {}); - t.ok(response, 'Response constructed.'); - t.end(); -}); +if (!isBrowser) { + // https://fetch.spec.whatwg.org/#response-class + // Run the tests both under browser and Node (ensures they conform to built-in) + test('constructor response', (t) => { + const response = new Response('', {}); + t.ok(response, 'Response constructed.'); + t.end(); + }); +} diff --git a/modules/polyfills/test/file/blob-polyfill.spec.js b/modules/polyfills/test/file/blob-polyfill.spec.js index 1d3fa9e9ac..5d573e242f 100644 --- a/modules/polyfills/test/file/blob-polyfill.spec.js +++ b/modules/polyfills/test/file/blob-polyfill.spec.js @@ -1,186 +1,190 @@ // Forked from @Gozala's https://github.com/Gozala/web-blob under MIT license import test from 'tape-promise/tape'; -test('test basic', async (t) => { - t.isEqual(typeof Blob, 'function'); -}); - -test('test jsdom', async (t) => { - const blob = new Blob(['TEST']); - t.isEqual(blob.size, 4, 'Initial blob should have a size of 4'); -}); - -test('should encode a blob with proper size when given two strings as arguments', async (t) => { - const blob = new Blob(['hi', 'hello']); - t.isEqual(blob.size, 7); -}); - -test('should encode arraybuffers with right content', async (t) => { - const bytes = new Uint8Array(5); - for (let i = 0; i < 5; i++) bytes[i] = i; - const blob = new Blob([bytes.buffer]); - const buffer = await blob.arrayBuffer(); - const result = new Uint8Array(buffer); - for (let i = 0; i < 5; i++) { - t.isEqual(result[i], i); - } -}); - -test('should encode typed arrays with right content', async (t) => { - const bytes = new Uint8Array(5); - for (let i = 0; i < 5; i++) bytes[i] = i; - const blob = new Blob([bytes]); - - const buffer = await blob.arrayBuffer(); - const result = new Uint8Array(buffer); - - for (let i = 0; i < 5; i++) { - t.isEqual(result[i], i); - } -}); - -test('should encode sliced typed arrays with right content', async (t) => { - const bytes = new Uint8Array(5); - for (let i = 0; i < 5; i++) bytes[i] = i; - const blob = new Blob([bytes.subarray(2)]); - - const buffer = await blob.arrayBuffer(); - const result = new Uint8Array(buffer); - for (let i = 0; i < 3; i++) { - t.isEqual(result[i], i + 2); - } -}); - -test('should encode with blobs', async (t) => { - const bytes = new Uint8Array(5); - for (let i = 0; i < 5; i++) bytes[i] = i; - const blob = new Blob([new Blob([bytes.buffer])]); - const buffer = await blob.arrayBuffer(); - const result = new Uint8Array(buffer); - for (let i = 0; i < 5; i++) { - t.isEqual(result[i], i); - } -}); - -test('should enode mixed contents to right size', async (t) => { - const bytes = new Uint8Array(5); - for (let i = 0; i < 5; i++) { - bytes[i] = i; - } - const blob = new Blob([bytes.buffer, 'hello']); - t.isEqual(blob.size, 10); -}); - -test('should accept mime type', async (t) => { - const blob = new Blob(['hi', 'hello'], {type: 'text/html'}); - t.isEqual(blob.type, 'text/html'); -}); - -test('should be an instance of constructor', async (t) => { - const blob = new Blob(['hi']); - t.ok(blob instanceof Blob); -}); - -test('from text', async (t) => { - const blob = new Blob(['hello']); - t.isEqual(blob.size, 5, 'is right size'); - t.isEqual(blob.type, '', 'type is empty'); - t.isEqual(await blob.text(), 'hello', 'reads as text'); - t.isEquivalent(new Uint8Array(await blob.arrayBuffer()), [ - ...'hello'.split('').map((char) => char.charCodeAt(0)) - ]); -}); - -test('from text with type', async (t) => { - const blob = new Blob(['hello'], {type: 'text/markdown'}); - t.isEqual(blob.size, 5, 'is right size'); - t.isEqual(blob.type, 'text/markdown', 'type is set'); - t.isEqual(await blob.text(), 'hello', 'reads as text'); - t.isEquivalent(new Uint8Array(await blob.arrayBuffer()), [ - ...'hello'.split('').map((char) => char.charCodeAt(0)) - ]); -}); - -test('empty blob', async (t) => { - const blob = new Blob([]); - t.isEqual(blob.size, 0, 'size is 0'); - t.isEqual(blob.type, '', 'type is empty'); - t.isEqual(await blob.text(), '', 'reads as text'); - t.isEquivalent(await blob.arrayBuffer(), new ArrayBuffer(0), 'returns empty buffer'); -}); - -test('no args', async (t) => { - const blob = new Blob(); - t.isEqual(blob.size, 0, 'size is 0'); - t.isEqual(blob.type, '', 'type is empty'); - t.isEqual(await blob.text(), '', 'reads as text'); - t.isEquivalent(await blob.arrayBuffer(), new ArrayBuffer(0), 'returns empty buffer'); -}); - -test('all emtpy args', async (t) => { - const blob = new Blob(['', new Blob(), '', new Uint8Array(0), new ArrayBuffer(0)]); - t.isEqual(blob.size, 0, 'size is 0'); - t.isEqual(blob.type, '', 'type is empty'); - t.isEqual(await blob.text(), '', 'reads as text'); - t.isEquivalent(await blob.arrayBuffer(), new ArrayBuffer(0), 'returns empty buffer'); -}); - -test('combined blob', async (t) => { - const uint8 = new Uint8Array([1, 2, 3]); - const uint16 = new Uint16Array([8, 190]); - const float32 = new Float32Array([5.4, 9, 1.5]); - const string = 'hello world'; - const blob = new Blob([uint8, uint16, float32, string]); - - const b8 = blob.slice(0, uint8.byteLength); - const r8 = new Uint8Array(await b8.arrayBuffer()); - t.isEquivalent(uint8, r8); - - const b16 = blob.slice(uint8.byteLength, uint8.byteLength + uint16.byteLength); - const r16 = new Uint16Array(await b16.arrayBuffer()); - t.isEquivalent(uint16, r16); - - const b32 = blob.slice( - uint8.byteLength + uint16.byteLength, - uint8.byteLength + uint16.byteLength + float32.byteLength - ); - const r32 = new Float32Array(await b32.arrayBuffer()); - t.isEquivalent(float32, r32); - - const bs = blob.slice(uint8.byteLength + uint16.byteLength + float32.byteLength); - t.isEqual(string, await bs.text()); - - t.isEqual('wo', await bs.slice(6, 8).text()); - t.isEqual('world', await bs.slice(6).text()); - t.isEqual('world', await blob.slice(-5).text()); -}); - -test('emoji', async (t) => { - const emojis = `👍🤷🎉😤`; - const blob = new Blob([emojis]); - const nestle = new Blob([new Blob([blob, blob])]); - t.isEqual(emojis + emojis, await nestle.text()); -}); - -/* -test('streams', async t => { - const blob = new Blob(['hello', ' ', 'world'], {type: 'text/plain'}); - const stream = blob.stream(); - - const reader = stream.getReader(); - const chunks = []; - // eslint-disable-next-line no-constant-condition - while (true) { - const {done, value} = await reader.read(); - if (done) { - break; +import {isBrowser} from '@loaders.gl/core'; + +if (!isBrowser) { + test('test basic', async (t) => { + t.isEqual(typeof Blob, 'function'); + }); + + test('test jsdom', async (t) => { + const blob = new Blob(['TEST']); + t.isEqual(blob.size, 4, 'Initial blob should have a size of 4'); + }); + + test('should encode a blob with proper size when given two strings as arguments', async (t) => { + const blob = new Blob(['hi', 'hello']); + t.isEqual(blob.size, 7); + }); + + test('should encode arraybuffers with right content', async (t) => { + const bytes = new Uint8Array(5); + for (let i = 0; i < 5; i++) bytes[i] = i; + const blob = new Blob([bytes.buffer]); + const buffer = await blob.arrayBuffer(); + const result = new Uint8Array(buffer); + for (let i = 0; i < 5; i++) { + t.isEqual(result[i], i); } + }); - if (value !== null) { - chunks.push(Buffer.from(value)); + test('should encode typed arrays with right content', async (t) => { + const bytes = new Uint8Array(5); + for (let i = 0; i < 5; i++) bytes[i] = i; + const blob = new Blob([bytes]); + + const buffer = await blob.arrayBuffer(); + const result = new Uint8Array(buffer); + + for (let i = 0; i < 5; i++) { + t.isEqual(result[i], i); + } + }); + + test('should encode sliced typed arrays with right content', async (t) => { + const bytes = new Uint8Array(5); + for (let i = 0; i < 5; i++) bytes[i] = i; + const blob = new Blob([bytes.subarray(2)]); + + const buffer = await blob.arrayBuffer(); + const result = new Uint8Array(buffer); + for (let i = 0; i < 3; i++) { + t.isEqual(result[i], i + 2); + } + }); + + test('should encode with blobs', async (t) => { + const bytes = new Uint8Array(5); + for (let i = 0; i < 5; i++) bytes[i] = i; + const blob = new Blob([new Blob([bytes.buffer])]); + const buffer = await blob.arrayBuffer(); + const result = new Uint8Array(buffer); + for (let i = 0; i < 5; i++) { + t.isEqual(result[i], i); + } + }); + + test('should enode mixed contents to right size', async (t) => { + const bytes = new Uint8Array(5); + for (let i = 0; i < 5; i++) { + bytes[i] = i; + } + const blob = new Blob([bytes.buffer, 'hello']); + t.isEqual(blob.size, 10); + }); + + test('should accept mime type', async (t) => { + const blob = new Blob(['hi', 'hello'], {type: 'text/html'}); + t.isEqual(blob.type, 'text/html'); + }); + + test('should be an instance of constructor', async (t) => { + const blob = new Blob(['hi']); + t.ok(blob instanceof Blob); + }); + + test('from text', async (t) => { + const blob = new Blob(['hello']); + t.isEqual(blob.size, 5, 'is right size'); + t.isEqual(blob.type, '', 'type is empty'); + t.isEqual(await blob.text(), 'hello', 'reads as text'); + t.isEquivalent(new Uint8Array(await blob.arrayBuffer()), [ + ...'hello'.split('').map((char) => char.charCodeAt(0)) + ]); + }); + + test('from text with type', async (t) => { + const blob = new Blob(['hello'], {type: 'text/markdown'}); + t.isEqual(blob.size, 5, 'is right size'); + t.isEqual(blob.type, 'text/markdown', 'type is set'); + t.isEqual(await blob.text(), 'hello', 'reads as text'); + t.isEquivalent(new Uint8Array(await blob.arrayBuffer()), [ + ...'hello'.split('').map((char) => char.charCodeAt(0)) + ]); + }); + + test('empty blob', async (t) => { + const blob = new Blob([]); + t.isEqual(blob.size, 0, 'size is 0'); + t.isEqual(blob.type, '', 'type is empty'); + t.isEqual(await blob.text(), '', 'reads as text'); + t.isEquivalent(await blob.arrayBuffer(), new ArrayBuffer(0), 'returns empty buffer'); + }); + + test('no args', async (t) => { + const blob = new Blob(); + t.isEqual(blob.size, 0, 'size is 0'); + t.isEqual(blob.type, '', 'type is empty'); + t.isEqual(await blob.text(), '', 'reads as text'); + t.isEquivalent(await blob.arrayBuffer(), new ArrayBuffer(0), 'returns empty buffer'); + }); + + test('all emtpy args', async (t) => { + const blob = new Blob(['', new Blob(), '', new Uint8Array(0), new ArrayBuffer(0)]); + t.isEqual(blob.size, 0, 'size is 0'); + t.isEqual(blob.type, '', 'type is empty'); + t.isEqual(await blob.text(), '', 'reads as text'); + t.isEquivalent(await blob.arrayBuffer(), new ArrayBuffer(0), 'returns empty buffer'); + }); + + test('combined blob', async (t) => { + const uint8 = new Uint8Array([1, 2, 3]); + const uint16 = new Uint16Array([8, 190]); + const float32 = new Float32Array([5.4, 9, 1.5]); + const string = 'hello world'; + const blob = new Blob([uint8, uint16, float32, string]); + + const b8 = blob.slice(0, uint8.byteLength); + const r8 = new Uint8Array(await b8.arrayBuffer()); + t.isEquivalent(uint8, r8); + + const b16 = blob.slice(uint8.byteLength, uint8.byteLength + uint16.byteLength); + const r16 = new Uint16Array(await b16.arrayBuffer()); + t.isEquivalent(uint16, r16); + + const b32 = blob.slice( + uint8.byteLength + uint16.byteLength, + uint8.byteLength + uint16.byteLength + float32.byteLength + ); + const r32 = new Float32Array(await b32.arrayBuffer()); + t.isEquivalent(float32, r32); + + const bs = blob.slice(uint8.byteLength + uint16.byteLength + float32.byteLength); + t.isEqual(string, await bs.text()); + + t.isEqual('wo', await bs.slice(6, 8).text()); + t.isEqual('world', await bs.slice(6).text()); + t.isEqual('world', await blob.slice(-5).text()); + }); + + test('emoji', async (t) => { + const emojis = `👍🤷🎉😤`; + const blob = new Blob([emojis]); + const nestle = new Blob([new Blob([blob, blob])]); + t.isEqual(emojis + emojis, await nestle.text()); + }); + + /* + test('streams', async t => { + const blob = new Blob(['hello', ' ', 'world'], {type: 'text/plain'}); + const stream = blob.stream(); + + const reader = stream.getReader(); + const chunks = []; + // eslint-disable-next-line no-constant-condition + while (true) { + const {done, value} = await reader.read(); + if (done) { + break; + } + + if (value !== null) { + chunks.push(Buffer.from(value)); + } } - } - t.deepEqual('hello world', Buffer.concat(chunks).toString()); -}); -*/ + t.deepEqual('hello world', Buffer.concat(chunks).toString()); + }); + */ +} diff --git a/modules/polyfills/test/filesystems/fetch-node.spec.js b/modules/polyfills/test/filesystems/fetch-node.spec.js new file mode 100644 index 0000000000..0efaad9416 --- /dev/null +++ b/modules/polyfills/test/filesystems/fetch-node.spec.js @@ -0,0 +1,64 @@ +import test from 'tape-promise/tape'; +import {isBrowser} from '@loaders.gl/core'; + +const {fetchNode} = globalThis.loaders || {}; + +const GITHUB_MASTER = 'https://raw.githubusercontent.com/visgl/loaders.gl/master/modules/'; +const PLY_CUBE_ATT_URL = `${GITHUB_MASTER}ply/test/data/cube_att.ply`; +const TEXT_URL = `@loaders.gl/polyfills/test/data/data.txt`; +const TEXT_URL_GZIPPED = `@loaders.gl/polyfills/test/data/data.txt.gz`; + +test('polyfills#fetchNode() (NODE)', async (t) => { + if (!isBrowser) { + const response = await fetchNode(PLY_CUBE_ATT_URL); + t.ok(response.headers, 'fetchNode polyfill successfully returned headers under Node.js'); + const data = await response.arrayBuffer(); + t.ok(data, 'fetchNode polyfill successfully loaded data under Node.js'); + } + t.end(); +}); + +test('polyfills#fetchNode() ignores url query params when loading file (NODE)', async (t) => { + if (!isBrowser) { + const response = await fetchNode(`${PLY_CUBE_ATT_URL}?v=1.2.3`); + const data = await response.text(); + t.ok(response.headers, 'fetchNode polyfill successfully returned headers under Node.js'); + t.ok(data, 'fetchNode polyfill successfully loaded data under Node.js'); + } + t.end(); +}); + +test('polyfills#fetchNode() error handling (NODE)', async (t) => { + if (!isBrowser) { + let response = await fetchNode('non-existent-file'); + t.comment(response.statusText); + t.ok(response.statusText.includes('ENOENT'), 'fetchNode statusText forwards node ENOENT error'); + t.notOk(response.ok, 'fetchNode polyfill fails cleanly on non-existent file'); + t.ok(response.arrayBuffer(), 'Response.arrayBuffer() does not throw'); + + response = await fetchNode('.'); + t.comment(response.statusText); + t.ok(response.statusText.includes('EISDIR'), 'fetchNode statusText forwards node error'); + t.notOk(response.ok, 'fetchNode polyfill fails cleanly on directory'); + t.ok(response.arrayBuffer(), 'Response.arrayBuffer() does not throw'); + } + t.end(); +}); + +// TODO v4.0 restore this test +test.skip('polyfills#fetchNode() able to decompress .gz extension (NODE)', async (t) => { + if (!isBrowser) { + let response = await fetchNode(TEXT_URL); + t.ok(response.ok, response.statusText); + let data = await response.text(); + t.equal(data, '123456', 'fetchNode polyfill correctly read text file'); + + if (!isBrowser) { + response = await fetchNode(TEXT_URL_GZIPPED); + t.ok(response.ok, response.statusText); + data = await response.text(); + t.equal(data, '123456', 'fetchNode polyfill correctly decompressed gzipped ".gz" file'); + } + } + t.end(); +}); diff --git a/modules/polyfills/test/filesystems/node-file.spec.ts b/modules/polyfills/test/filesystems/node-file.spec.ts new file mode 100644 index 0000000000..84c5ce6e55 --- /dev/null +++ b/modules/polyfills/test/filesystems/node-file.spec.ts @@ -0,0 +1,17 @@ +import test from 'tape-promise/tape'; +import {isBrowser} from '@loaders.gl/core'; +import {NodeFile} from '@loaders.gl/loader-utils'; + +const SLPK_URL = '@loaders.gl/i3s/test/data/DA12_subset.slpk'; + +// TODO v4.0 restore this test +test.skip('NodeFile#open and read', async (t) => { + if (!isBrowser) { + const provider = new NodeFile(SLPK_URL); + const arrayBuffer = await provider.read(4, 1); + + const reference = new Buffer(new Uint8Array([0])); + t.equals(reference.compare(Buffer.from(arrayBuffer)), 0); + } + t.end(); +}); diff --git a/modules/polyfills/test/filesystems/node-filesystem.spec.ts b/modules/polyfills/test/filesystems/node-filesystem.spec.ts new file mode 100644 index 0000000000..e29d3c2348 --- /dev/null +++ b/modules/polyfills/test/filesystems/node-filesystem.spec.ts @@ -0,0 +1,19 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import {NodeFilesystem} from '@loaders.gl/loader-utils'; + +import {isBrowser} from '@loaders.gl/core'; + +if (!isBrowser) { + test('NodeFileSystem#import', (t) => { + if (!NodeFilesystem) { + t.comment('NodeFileSystem not defined'); + t.end(); + return; + } + t.ok(NodeFilesystem, 'NodeFileSystem defined'); + t.end(); + }); +} diff --git a/modules/polyfills/test/images-node/images-node.spec.js b/modules/polyfills/test/images-node/images-node.spec.js index 6a5f75077d..8dcdbe0566 100644 --- a/modules/polyfills/test/images-node/images-node.spec.js +++ b/modules/polyfills/test/images-node/images-node.spec.js @@ -1,9 +1,7 @@ /* eslint-disable max-len */ import test from 'tape-promise/tape'; -import '@loaders.gl/polyfills'; -import {isBrowser} from '../../src/utils/globals'; -import {fetchFile} from '@loaders.gl/core'; -import {parseImageNode} from '../../src/node/images/parse-image.node'; +import {isBrowser, fetchFile} from '@loaders.gl/core'; +import {parseImageNode} from '../../src/images/parse-image-node'; const images = [ ['@loaders.gl/images/test/data/img1-preview.png', 'image/png'], @@ -11,18 +9,25 @@ const images = [ ['@loaders.gl/images/test/data/img1-preview.gif', 'image/gif'] ]; -test('Node image polyfills', (t) => { - if (!isBrowser) { +if (!isBrowser) { + test('Node image polyfills', (t) => { // @ts-ignore - t.equals(typeof _encodeImageNode, 'function', 'global._encodeImageNode successfully installed'); + t.equals( + typeof globalThis.loaders?.encodeImageNode, + 'function', + 'encodeImageNode successfully installed' + ); // @ts-ignore - t.equals(typeof _parseImageNode, 'function', 'global._parseImageNode successfully installed'); - } - t.end(); -}); + t.equals( + typeof globalThis.loaders?.parseImageNode, + 'function', + 'parseImageNode successfully installed' + ); -test('Node image polyfills - should return Uint8Array data', async (t) => { - if (!isBrowser) { + t.end(); + }); + + test.skip('Node image polyfills - should return Uint8Array data', async (t) => { for (const image of images) { const [imageUrl, mimeType] = image; const response = await fetchFile(imageUrl); @@ -31,6 +36,6 @@ test('Node image polyfills - should return Uint8Array data', async (t) => { t.ok(result.data instanceof Uint8Array, `Loaded ${imageUrl} is Uint8Array`); t.notOk(result.data instanceof Buffer, `Loaded ${imageUrl} is not Buffer`); } - } - t.end(); -}); + t.end(); + }); +} diff --git a/modules/polyfills/test/index.ts b/modules/polyfills/test/index.ts index 8d176f66f5..c273590285 100644 --- a/modules/polyfills/test/index.ts +++ b/modules/polyfills/test/index.ts @@ -9,4 +9,11 @@ import './images-node/images-node.spec'; import './file/blob-polyfill.spec'; import './file/file-polyfill.spec'; -import './promise/all-settled.spec'; + +import './filesystems/fetch-node.spec'; +import './filesystems/node-file.spec'; +import './filesystems/node-filesystem.spec'; +import './filesystems/fetch-node.spec'; + +// TODO - v4.0 restore these tests +// import './load-library/require-utils.spec'; diff --git a/modules/polyfills/test/load-library/fixture/module.js b/modules/polyfills/test/load-library/fixture/module.js new file mode 100644 index 0000000000..568f6b3104 --- /dev/null +++ b/modules/polyfills/test/load-library/fixture/module.js @@ -0,0 +1 @@ +module.exports = require('./submodule.js'); diff --git a/modules/polyfills/test/load-library/fixture/submodule.js b/modules/polyfills/test/load-library/fixture/submodule.js new file mode 100644 index 0000000000..ebc030053d --- /dev/null +++ b/modules/polyfills/test/load-library/fixture/submodule.js @@ -0,0 +1,4 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +module.exports = {}; +// export const module = {}; diff --git a/modules/polyfills/test/load-library/require-utils.spec.ts b/modules/polyfills/test/load-library/require-utils.spec.ts new file mode 100644 index 0000000000..bfa4f7ed0e --- /dev/null +++ b/modules/polyfills/test/load-library/require-utils.spec.ts @@ -0,0 +1,93 @@ +// Fork of https://github.com/floatdrop/require-from-string/blob/master/index.js +// Copyright (c) Vsevolod Strukchinsky (github.com/floatdrop) +// MIT license + +import test from 'tape-promise/tape'; +import * as fs from 'fs'; +import * as path from 'path'; +import {isBrowser} from '@loaders.gl/core'; +import {requireFromFile, requireFromString} from '../../src/load-library/require-utils.node'; + +const DIR = path?.dirname?.(import.meta.url)?.replace('file://', '') || '.'; +const MODULE_URL = `${DIR}/fixture/module.js`; +const SUBMODULE_URL = `${DIR}/fixture/submodule.js`; + +test('polyfills#require-utils', (tt) => { + if (isBrowser) { + tt.end(); + return; + } + + test.skip('polyfills#requireFromFile#', (t) => { + t.ok(requireFromFile(MODULE_URL), 'Require from file worked'); + t.ok(requireFromFile(SUBMODULE_URL), 'Require from file worked'); + t.end(); + }); + + test('polyfills#requireFromString#should accept only string as code', (t) => { + // @ts-expect-error + t.throws(() => requireFromString(), /code must be a string, not undefined/); + t.end(); + }); + + test('polyfills#requireFromString#should require from string', (t) => { + t.equal(requireFromString('module.exports = 1;'), 1); + t.end(); + }); + + test('polyfills#requireFromString#should accept filename', (t) => { + t.throws(() => requireFromString('module.exports = ', 'bug.js'), /bug\.js|Unexpected/); + t.end(); + }); + + test.skip('polyfills#requireFromString#should work with relative require in file', (t) => { + const code = fs.readFileSync(MODULE_URL, 'utf8'); + const result = requireFromString(code, MODULE_URL); + + t.ok(result); + // TODO + // t.equal(module, result.parent.parent); + t.end(); + }); + + test.skip('polyfills#requireFromString#should have appended and preppended paths', (t) => { + const code = fs.readFileSync(SUBMODULE_URL, 'utf8'); + const result = requireFromString(code, SUBMODULE_URL, { + appendPaths: ['append'], + prependPaths: ['prepend'] + }); + + t.ok(result); + t.equal(result.paths.indexOf('append'), result.paths.length - 1); + t.equal(result.paths.indexOf('prepend'), 0); + t.end(); + }); + + // TODO + test.skip('requireFromString#should have meaningful error message', (t) => { + try { + requireFromString('throw new Error("Boom!");'); + } catch (error) { + // @ts-ignore + t.ok(/\(:1:69\)/.test(error.stack), 'should contain (:1:69) in stack'); + } + + try { + requireFromString('throw new Error("Boom!");', ''); + } catch (error) { + // @ts-ignore + t.ok(/\(:1:69\)/.test(error.stack), 'should contain (:1:69) in stack'); + } + t.end(); + }); + + test.skip('polyfills#requireFromString#should cleanup parent.children', (t) => { + const code = fs.readFileSync(SUBMODULE_URL, 'utf8'); + const result = requireFromString(code, SUBMODULE_URL); + + t.ok(module.children.indexOf(result) === -1); + t.end(); + }); + + tt.end(); +}); diff --git a/modules/polyfills/test/promise/all-settled.spec.js b/modules/polyfills/test/promise/all-settled.spec.js deleted file mode 100644 index 99e0f2ef03..0000000000 --- a/modules/polyfills/test/promise/all-settled.spec.js +++ /dev/null @@ -1,20 +0,0 @@ -import test from 'tape-promise/tape'; -import '@loaders.gl/polyfills'; - -function returnReject() { - return Promise.reject('Wrong request'); -} - -function returnFulfilled() { - return Promise.resolve('success'); -} - -test('polyfills#Promise.allSettled()', async (t) => { - const promises = [returnFulfilled(), returnReject()]; - const result = await Promise.allSettled(promises); - - t.ok(result); - t.ok(result.some((res) => res.status === 'fulfilled' && res.value === 'success')); - t.ok(result.some((res) => res.status === 'rejected' && res.reason === 'Wrong request')); - t.end(); -}); diff --git a/modules/polyfills/test/text-encoding/text-encoding.spec.js b/modules/polyfills/test/text-encoding/text-encoding.spec.js index 04f055a612..0c18009cc0 100644 --- a/modules/polyfills/test/text-encoding/text-encoding.spec.js +++ b/modules/polyfills/test/text-encoding/text-encoding.spec.js @@ -1,18 +1,24 @@ /* eslint-disable max-len */ import test from 'tape-promise/tape'; +import {isBrowser} from '@loaders.gl/core'; -test('TextEncoder', (t) => { - t.ok(new TextEncoder(), 'TextEncoder successfully instantiated (available or polyfilled)'); - t.end(); -}); +if (!isBrowser) { + test('TextEncoder', (t) => { + t.ok(new TextEncoder(), 'TextEncoder successfully instantiated (available or polyfilled)'); + t.end(); + }); -test('TextDecoder', async (t) => { - const buffer = Buffer.from('México', 'latin1'); - const arrayBuffer = buffer; - t.ok(arrayBuffer, 'node Buffer parses latin1 '); + test('TextDecoder', async (t) => { + t.ok(new TextDecoder(), 'TextDecoder successfully instantiated (available or polyfilled)'); - const textDecoder = new TextDecoder('latin1'); - t.ok(textDecoder, 'TextDecoder successfully instantiated (available or polyfilled)'); - t.equals(textDecoder.decode(arrayBuffer), 'México'); - t.end(); -}); + const buffer = Buffer.from('México', 'latin1'); + const arrayBuffer = buffer; + t.ok(arrayBuffer, 'node Buffer parses latin1 '); + + const textDecoder = new TextDecoder('latin1'); + t.ok(textDecoder, 'TextDecoder successfully instantiated (available or polyfilled)'); + t.equals(textDecoder.decode(arrayBuffer), 'México'); + + t.end(); + }); +} diff --git a/modules/polyfills/tsconfig.json b/modules/polyfills/tsconfig.json index 3dabf182c9..b362ea2da5 100644 --- a/modules/polyfills/tsconfig.json +++ b/modules/polyfills/tsconfig.json @@ -8,6 +8,7 @@ "outDir": "dist" }, "references": [ + {"path": "../crypto"}, {"path": "../images"}, {"path": "../loader-utils"}, {"path": "../worker-utils"} diff --git a/modules/potree/package.json b/modules/potree/package.json index 5bdb11a3e0..464248f4b4 100644 --- a/modules/potree/package.json +++ b/modules/potree/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/potree", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "potree loaders for large point clouds.", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -23,8 +24,15 @@ "pointcloud" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -32,12 +40,12 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { - "@loaders.gl/math": "4.0.0-alpha.13", - "@math.gl/core": "^3.5.1" + "@loaders.gl/math": "4.0.3", + "@math.gl/core": "^4.0.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/potree/src/bundle.ts b/modules/potree/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/potree/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/potree/src/parsers/parse-potree-bin.ts b/modules/potree/src/parsers/parse-potree-bin.ts index 1c1cd0dbdd..64bd217343 100644 --- a/modules/potree/src/parsers/parse-potree-bin.ts +++ b/modules/potree/src/parsers/parse-potree-bin.ts @@ -1,4 +1,4 @@ -export default function parsePotreeBin( +export function parsePotreeBin( arrayBuffer: ArrayBuffer, byteOffset: number, options: unknown, diff --git a/modules/potree/src/parsers/parse-potree-hierarchy-chunk.ts b/modules/potree/src/parsers/parse-potree-hierarchy-chunk.ts index 4b6da58a14..42496f9efd 100644 --- a/modules/potree/src/parsers/parse-potree-hierarchy-chunk.ts +++ b/modules/potree/src/parsers/parse-potree-hierarchy-chunk.ts @@ -54,10 +54,26 @@ Would have an index looking like this: | r36 | `0b00000000` (=0) | `1` | */ -// @ts-nocheck +/** @todo these types are an incorrect mess */ +export type POTreeTileHeader = { + childCount: number; + name: string; + childMask: number; +}; + +/** @todo these types are an incorrect mess */ +export type POTreeNode = { + header: POTreeTileHeader; + name: string; + pointCount: number; + children: POTreeNode[]; + childrenByIndex: POTreeNode[]; +}; + +// type POTreeTileNode = POTreeNode; // load hierarchy -export default function parsePotreeHierarchyChunk(arrayBuffer: ArrayBuffer) { +export function parsePotreeHierarchyChunk(arrayBuffer: ArrayBuffer) { const tileHeaders = parseBinaryChunk(arrayBuffer); return buildHierarchy(tileHeaders); } @@ -66,15 +82,16 @@ export default function parsePotreeHierarchyChunk(arrayBuffer: ArrayBuffer) { function parseBinaryChunk(arrayBuffer: ArrayBuffer, byteOffset = 0) { const dataView = new DataView(arrayBuffer); - const stack = []; + const stack: POTreeNode[] = []; // Get root mask - const topTileHeader = {}; + // @ts-expect-error + const topTileHeader: POTreeNode = {}; byteOffset = decodeRow(dataView, byteOffset, topTileHeader); stack.push(topTileHeader); - const tileHeaders = []; + const tileHeaders: POTreeTileHeader[] = []; while (stack.length > 0) { const snode = stack.shift(); @@ -82,10 +99,12 @@ function parseBinaryChunk(arrayBuffer: ArrayBuffer, byteOffset = 0) { for (let i = 0; i < 8; i++) { if (snode && (snode.header.childMask & mask) !== 0) { - const tileHeader = {}; + // @ts-expect-error + const tileHeader: POTreeTileHeader = {}; byteOffset = decodeRow(dataView, byteOffset, tileHeader); tileHeader.name = snode.name + i; + // @ts-expect-error stack.push(tileHeader); tileHeaders.push(tileHeader); snode.header.childCount++; @@ -112,7 +131,7 @@ function decodeRow(dataView, byteOffset, tileHeader) { } // Resolves the binary rows into a hierarchy (tree structure) -function buildHierarchy(tileHeaders, options = {}) { +function buildHierarchy(tileHeaders, options: {spacing?: number} = {}): POTreeNode { const DEFAULT_OPTIONS = {spacing: 100}; // TODO assert instead of default? options = {...DEFAULT_OPTIONS, ...options}; @@ -132,7 +151,7 @@ function buildHierarchy(tileHeaders, options = {}) { tileHeader.hasChildren = tileHeader.header.childCount; tileHeader.children = []; tileHeader.childrenByIndex = new Array(8).fill(null); - tileHeader.spacing = options.spacing / Math.pow(2, level); + tileHeader.spacing = (options?.spacing || 0) / Math.pow(2, level); // tileHeader.boundingVolume = Utils.createChildAABB(parentNode.boundingBox, index); if (parentNode) { diff --git a/modules/potree/src/potree-bin-loader.ts b/modules/potree/src/potree-bin-loader.ts index bb3ae5b964..e16b373841 100644 --- a/modules/potree/src/potree-bin-loader.ts +++ b/modules/potree/src/potree-bin-loader.ts @@ -1,11 +1,11 @@ import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; -import {default as parsePotreeBin} from './parsers/parse-potree-bin'; +import {parsePotreeBin} from './parsers/parse-potree-bin'; /** * Loader for potree Binary Point Attributes * */ // @ts-ignore -export const PotreeBinLoader: LoaderWithParser = { +export const PotreeBinLoader: LoaderWithParser<{}, never, LoaderOptions> = { name: 'potree Binary Point Attributes', id: 'potree', extensions: ['bin'], @@ -16,7 +16,7 @@ export const PotreeBinLoader: LoaderWithParser = { binary: true }; -function parseSync(arrayBuffer: ArrayBuffer, options?: LoaderOptions) { +function parseSync(arrayBuffer: ArrayBuffer, options?: LoaderOptions): {} { const index = {}; const byteOffset = 0; parsePotreeBin(arrayBuffer, byteOffset, options, index); diff --git a/modules/potree/src/potree-hierarchy-chunk-loader.ts b/modules/potree/src/potree-hierarchy-chunk-loader.ts index 09e71549a0..6e3a4f3edb 100644 --- a/modules/potree/src/potree-hierarchy-chunk-loader.ts +++ b/modules/potree/src/potree-hierarchy-chunk-loader.ts @@ -1,20 +1,19 @@ import type {LoaderWithParser} from '@loaders.gl/loader-utils'; -import {default as parsePotreeHierarchyChunk} from './parsers/parse-potree-hierarchy-chunk'; +import type {POTreeLoaderOptions} from './potree-loader'; +import type {POTreeNode} from './parsers/parse-potree-hierarchy-chunk'; +import {parsePotreeHierarchyChunk} from './parsers/parse-potree-hierarchy-chunk'; /** Potree hierarchy chunk loader */ -// @ts-ignore -export const PotreeHierarchyChunkLoader: LoaderWithParser = { - id: 'potree', - name: 'potree Hierarchy Chunk', - extensions: ['hrc'], - mimeTypes: ['application/octet-stream'], - // binary potree files have no header bytes, no content test function possible - // test: ['...'], - parse: async (arrayBuffer, options) => await parseSync(arrayBuffer), - parseSync, - binary: true -}; - -function parseSync(arrayBuffer) { - return parsePotreeHierarchyChunk(arrayBuffer); -} +// @ts-ignore not a valid loader +export const PotreeHierarchyChunkLoader: LoaderWithParser = + { + id: 'potree', + name: 'potree Hierarchy Chunk', + extensions: ['hrc'], + mimeTypes: ['application/octet-stream'], + // binary potree files have no header bytes, no content test function possible + // test: ['...'], + parse: async (arrayBuffer, options) => parsePotreeHierarchyChunk(arrayBuffer), + parseSync: (arrayBuffer, options) => parsePotreeHierarchyChunk(arrayBuffer), + binary: true + }; diff --git a/modules/potree/src/potree-loader.ts b/modules/potree/src/potree-loader.ts index 8d9f7a3b62..13ff722310 100644 --- a/modules/potree/src/potree-loader.ts +++ b/modules/potree/src/potree-loader.ts @@ -1,12 +1,16 @@ -import type {LoaderWithParser} from '@loaders.gl/loader-utils'; +import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; +export type POTreeLoaderOptions = LoaderOptions & { + potree?: {}; +}; + /** Potree loader */ // @ts-ignore -export const PotreeLoader: LoaderWithParser = { +export const PotreeLoader: LoaderWithParser = { name: 'potree', id: 'potree', module: 'potree', diff --git a/modules/schema/package.json b/modules/schema/package.json index 487c4a890e..d726803ad6 100644 --- a/modules/schema/package.json +++ b/modules/schema/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/schema", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Table format APIs for JSON, CSV, etc...", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "PLY" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -29,8 +37,8 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { "@types/geojson": "^7946.0.7" diff --git a/modules/schema/src/bundle.ts b/modules/schema/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/schema/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/schema/src/index.ts b/modules/schema/src/index.ts index 9fa2603799..71c04a605a 100644 --- a/modules/schema/src/index.ts +++ b/modules/schema/src/index.ts @@ -1,13 +1,20 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + // COMMON CATEGORY export type { TypedArray, + BigTypedArray, TypedArrayConstructor, + BigTypedArrayConstructor, NumberArray, ArrayType, AnyArray } from './types/types'; -export type {Schema, Field, DataType, Batch, SchemaMetadata, FieldMetadata} from './types/schema'; +export type {Schema, Field, DataType, SchemaMetadata, FieldMetadata} from './types/schema'; + +export type {Batch} from './types/batch'; // TABLE CATEGORY TYPES export type { @@ -15,7 +22,7 @@ export type { RowTable, ArrayRowTable, ObjectRowTable, - GeoJSONRowTable, + GeoJSONTable, ColumnarTable, ArrowTable, Tables @@ -24,7 +31,7 @@ export type { TableBatch, ArrayRowTableBatch, ObjectRowTableBatch, - GeoJSONRowTableBatch, + GeoJSONTableBatch, ColumnarTableBatch, ArrowTableBatch } from './types/category-table'; @@ -36,6 +43,7 @@ export {RowTableBatchAggregator} from './lib/table/batches/row-table-batch-aggre export {ColumnarTableBatchAggregator} from './lib/table/batches/columnar-table-batch-aggregator'; export { + isTable, getTableLength, getTableNumCols, getTableCell, @@ -52,6 +60,11 @@ export { export {ArrowLikeTable} from './lib/table/arrow-api/arrow-like-table'; export {makeTableFromData} from './lib/table/simple-table/make-table'; +export { + makeTableFromBatches, + makeBatchFromTable +} from './lib/table/simple-table/make-table-from-batches'; +export {convertTable} from './lib/table/simple-table/convert-table'; export {deduceTableSchema} from './lib/table/simple-table/table-schema'; export {convertToObjectRow, convertToArrayRow} from './lib/table/simple-table/row-utils'; export {getDataTypeFromArray} from './lib/table/simple-table/data-type'; @@ -122,10 +135,11 @@ export type { BinaryAttribute } from './types/category-gis'; export type { - BinaryFeatures, - BinaryPointFeatures, - BinaryLineFeatures, - BinaryPolygonFeatures + BinaryFeatureCollection, + BinaryFeature, + BinaryPointFeature, + BinaryLineFeature, + BinaryPolygonFeature } from './types/category-gis'; // SCHEMA @@ -172,6 +186,5 @@ export { // SCHEMA UTILS export {getTypeInfo} from './lib/table/arrow-api/get-type-info'; -export {getArrowType} from './lib/table/arrow/arrow-type-utils'; export {default as AsyncQueue} from './lib/utils/async-queue'; diff --git a/modules/schema/src/lib/mesh/convert-mesh.ts b/modules/schema/src/lib/mesh/convert-mesh.ts index fe208a18d5..48d8e27082 100644 --- a/modules/schema/src/lib/mesh/convert-mesh.ts +++ b/modules/schema/src/lib/mesh/convert-mesh.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Mesh} from '../../types/category-mesh'; import type {ColumnarTable, ArrowTable} from '../../types/category-table'; diff --git a/modules/schema/src/lib/mesh/deduce-mesh-schema.ts b/modules/schema/src/lib/mesh/deduce-mesh-schema.ts index a6da24e19e..7b3b5bd3dc 100644 --- a/modules/schema/src/lib/mesh/deduce-mesh-schema.ts +++ b/modules/schema/src/lib/mesh/deduce-mesh-schema.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {MeshAttribute, MeshAttributes} from '../../types/category-mesh'; import {Schema, Field} from '../../types/schema'; diff --git a/modules/schema/src/lib/mesh/mesh-to-arrow-table.ts b/modules/schema/src/lib/mesh/mesh-to-arrow-table.ts index f4a8e638d5..da14cf0f1e 100644 --- a/modules/schema/src/lib/mesh/mesh-to-arrow-table.ts +++ b/modules/schema/src/lib/mesh/mesh-to-arrow-table.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + /* Problem with arrow dependency... import { Table, diff --git a/modules/schema/src/lib/table/arrow-api/arrow-like-field.ts b/modules/schema/src/lib/table/arrow-api/arrow-like-field.ts index 2e8605dd39..3d0ac50133 100644 --- a/modules/schema/src/lib/table/arrow-api/arrow-like-field.ts +++ b/modules/schema/src/lib/table/arrow-api/arrow-like-field.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {DataType} from './arrow-like-type'; diff --git a/modules/schema/src/lib/table/arrow-api/arrow-like-schema.ts b/modules/schema/src/lib/table/arrow-api/arrow-like-schema.ts index e6d8e3ccc7..505afc9c94 100644 --- a/modules/schema/src/lib/table/arrow-api/arrow-like-schema.ts +++ b/modules/schema/src/lib/table/arrow-api/arrow-like-schema.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {SchemaMetadata, Field} from '../../../types/schema'; import {ArrowLikeField} from './arrow-like-field'; diff --git a/modules/schema/src/lib/table/arrow-api/arrow-like-table.ts b/modules/schema/src/lib/table/arrow-api/arrow-like-table.ts index 9f1726d21d..112a99fffc 100644 --- a/modules/schema/src/lib/table/arrow-api/arrow-like-table.ts +++ b/modules/schema/src/lib/table/arrow-api/arrow-like-table.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {Table} from '../../../types/category-table'; @@ -34,7 +35,8 @@ class ArrowLikeVector { toArray(): ArrayLike { switch (this.table.shape) { case 'arrow-table': - return this.table.data.getChild(this.columnName)?.toArray(); + const arrowTable = this.table.data as any; + return arrowTable.getChild(this.columnName)?.toArray(); case 'columnar-table': return this.table.data[this.columnName]; default: @@ -62,7 +64,7 @@ export class ArrowLikeTable { // } get data() { - return this.table.data; + return this.table.shape === 'geojson-table' ? this.table.features : this.table.data; } get numCols(): number { diff --git a/modules/schema/src/lib/table/arrow-api/arrow-like-type.ts b/modules/schema/src/lib/table/arrow-api/arrow-like-type.ts index 74eb5a9a83..1178a86bc1 100644 --- a/modules/schema/src/lib/table/arrow-api/arrow-like-type.ts +++ b/modules/schema/src/lib/table/arrow-api/arrow-like-type.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + // This code is adapted from ArrowJS https://github.com/apache/arrow // under Apache license http://www.apache.org/licenses/LICENSE-2.0 @@ -14,8 +17,6 @@ export type TypedIntArray = | Uint16Array | Int32Array | Uint32Array - | Int32Array - | Uint32Array | Uint8ClampedArray; export type TypedFloatArray = Float32Array | Float64Array; diff --git a/modules/schema/src/lib/table/arrow-api/enum.ts b/modules/schema/src/lib/table/arrow-api/enum.ts index dc7f9f510a..4b23960c74 100644 --- a/modules/schema/src/lib/table/arrow-api/enum.ts +++ b/modules/schema/src/lib/table/arrow-api/enum.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + // This code is adapted from ArrowJS https://github.com/apache/arrow // under Apache license http://www.apache.org/licenses/LICENSE-2.0 diff --git a/modules/schema/src/lib/table/arrow-api/get-type-info.ts b/modules/schema/src/lib/table/arrow-api/get-type-info.ts index 98f1d42ac1..d1c731f9a2 100644 --- a/modules/schema/src/lib/table/arrow-api/get-type-info.ts +++ b/modules/schema/src/lib/table/arrow-api/get-type-info.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {Type} from './arrow-like-type'; diff --git a/modules/schema/src/lib/table/arrow-api/index.ts b/modules/schema/src/lib/table/arrow-api/index.ts index 37505a0ae2..af94aecd25 100644 --- a/modules/schema/src/lib/table/arrow-api/index.ts +++ b/modules/schema/src/lib/table/arrow-api/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors export {ArrowLikeField as Field} from './arrow-like-field'; export {ArrowLikeSchema as Schema} from './arrow-like-schema'; diff --git a/modules/schema/src/lib/table/arrow/convert-schema-arrow.ts b/modules/schema/src/lib/table/arrow/convert-schema-arrow.ts deleted file mode 100644 index 58821fa710..0000000000 --- a/modules/schema/src/lib/table/arrow/convert-schema-arrow.ts +++ /dev/null @@ -1,232 +0,0 @@ -// loaders.gl, MIT license - -import type {DataType, Field, Schema, SchemaMetadata} from '../../../types/schema'; -import { - Field as ArrowField, - Schema as ArrowSchema, - DataType as ArrowDataType, - Null, - Binary, - Bool, - // Int, - Int8, - Int16, - Int32, - Int64, - Uint8, - Uint16, - Uint32, - Uint64, - // Float, - Float16, - Float32, - Float64, - Utf8, - // Date, - DateDay, - DateMillisecond, - // Time, - TimeMillisecond, - TimeSecond, - // Timestamp, - TimestampSecond, - TimestampMillisecond, - TimestampMicrosecond, - TimestampNanosecond, - // Interval, - IntervalDayTime, - IntervalYearMonth, - FixedSizeList, - Struct -} from 'apache-arrow/Arrow.dom'; - -export function serializeArrowSchema(arrowSchema: ArrowSchema): Schema { - return { - fields: arrowSchema.fields.map((arrowField) => serializeArrowField(arrowField)), - metadata: serializeArrowMetadata(arrowSchema.metadata) - }; -} - -export function deserializeArrowSchema(schema: Schema): ArrowSchema { - return new ArrowSchema( - schema.fields.map((field) => deserializeArrowField(field)), - deserializeArrowMetadata(schema.metadata) - ); -} - -export function serializeArrowMetadata(arrowMetadata: Map): SchemaMetadata { - return Object.fromEntries(arrowMetadata); -} - -export function deserializeArrowMetadata(metadata?: SchemaMetadata): Map { - return metadata ? new Map(Object.entries(metadata)) : new Map(); -} - -export function serializeArrowField(field: ArrowField): Field { - return { - name: field.name, - type: serializeArrowType(field.type), - nullable: field.nullable, - metadata: serializeArrowMetadata(field.metadata) - }; -} - -export function deserializeArrowField(field: Field): ArrowField { - return new ArrowField( - field.name, - deserializeArrowType(field.type), - field.nullable, - deserializeArrowMetadata(field.metadata) - ); -} - -/** Converts a serializable loaders.gl data type to hydrated arrow data type */ -// eslint-disable-next-line complexity -export function serializeArrowType(arrowType: ArrowDataType): DataType { - switch (arrowType.constructor) { - case Null: - return 'null'; - case Binary: - return 'binary'; - case Bool: - return 'bool'; - // case Int: return 'int'; - case Int8: - return 'int8'; - case Int16: - return 'int16'; - case Int32: - return 'int32'; - case Int64: - return 'int64'; - case Uint8: - return 'uint8'; - case Uint16: - return 'uint16'; - case Uint32: - return 'uint32'; - case Uint64: - return 'uint64'; - // case Float: return 'float'; - case Float16: - return 'float16'; - case Float32: - return 'float32'; - case Float64: - return 'float64'; - case Utf8: - return 'utf8'; - // case Date: return 'date'; - case DateDay: - return 'date-day'; - case DateMillisecond: - return 'date-millisecond'; - // case Time: return 'time'; - case TimeMillisecond: - return 'time-millisecond'; - case TimeSecond: - return 'time-second'; - // case Timestamp: return 'timestamp'; - case TimestampSecond: - return 'timestamp-second'; - case TimestampMillisecond: - return 'timestamp-millisecond'; - case TimestampMicrosecond: - return 'timestamp-microsecond'; - case TimestampNanosecond: - return 'timestamp-nanosecond'; - // case Interval: return 'interval'; - case IntervalDayTime: - return 'interval-daytime'; - case IntervalYearMonth: - return 'interval-yearmonth'; - case FixedSizeList: - return { - type: 'fixed-size-list', - listSize: (arrowType as FixedSizeList).listSize, - children: [serializeArrowField((arrowType as FixedSizeList).children[0])] - }; - // case Struct: - // return {type: 'struct', children: (arrowType as Struct).children}; - default: - throw new Error('array type not supported'); - } -} - -/** Converts a serializable loaders.gl data type to hydrated arrow data type */ -// eslint-disable-next-line complexity -export function deserializeArrowType(dataType: DataType): ArrowDataType { - if (typeof dataType === 'object') { - switch (dataType.type) { - case 'fixed-size-list': - const child = deserializeArrowField(dataType.children[0]); - return new FixedSizeList(dataType.listSize, child); - case 'struct': - const children = dataType.children.map((arrowField) => deserializeArrowField(arrowField)); - return new Struct(children); - default: - throw new Error('array type not supported'); - } - } - - switch (dataType) { - case 'null': - return new Null(); - case 'binary': - return new Binary(); - case 'bool': - return new Bool(); - // case 'int': return new Int(); - case 'int8': - return new Int8(); - case 'int16': - return new Int16(); - case 'int32': - return new Int32(); - case 'int64': - return new Int64(); - case 'uint8': - return new Uint8(); - case 'uint16': - return new Uint16(); - case 'uint32': - return new Uint32(); - case 'uint64': - return new Uint64(); - // case 'float': return new Float(); - case 'float16': - return new Float16(); - case 'float32': - return new Float32(); - case 'float64': - return new Float64(); - case 'utf8': - return new Utf8(); - // case 'date': return new Date(); - case 'date-day': - return new DateDay(); - case 'date-millisecond': - return new DateMillisecond(); - // case 'time': return new Time(); - case 'time-millisecond': - return new TimeMillisecond(); - case 'time-second': - return new TimeSecond(); - // case 'timestamp': return new Timestamp(); - case 'timestamp-second': - return new TimestampSecond(); - case 'timestamp-millisecond': - return new TimestampMillisecond(); - case 'timestamp-microsecond': - return new TimestampMicrosecond(); - case 'timestamp-nanosecond': - return new TimestampNanosecond(); - // case 'interval': return new Interval(); - case 'interval-daytime': - return new IntervalDayTime(); - case 'interval-yearmonth': - return new IntervalYearMonth(); - default: - throw new Error('array type not supported'); - } -} diff --git a/modules/schema/src/lib/table/batches/base-table-batch-aggregator.ts b/modules/schema/src/lib/table/batches/base-table-batch-aggregator.ts index 21c54e8b2e..fc082525c4 100644 --- a/modules/schema/src/lib/table/batches/base-table-batch-aggregator.ts +++ b/modules/schema/src/lib/table/batches/base-table-batch-aggregator.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Schema} from '../../../types/schema'; import type {TableBatch} from '../../../types/category-table'; diff --git a/modules/schema/src/lib/table/batches/columnar-table-batch-aggregator.ts b/modules/schema/src/lib/table/batches/columnar-table-batch-aggregator.ts index f0d07531fb..a6377977d8 100644 --- a/modules/schema/src/lib/table/batches/columnar-table-batch-aggregator.ts +++ b/modules/schema/src/lib/table/batches/columnar-table-batch-aggregator.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Schema} from '../../../types/schema'; import type {ColumnarTableBatch, ArrowTableBatch} from '../../../types/category-table'; diff --git a/modules/schema/src/lib/table/batches/row-table-batch-aggregator.ts b/modules/schema/src/lib/table/batches/row-table-batch-aggregator.ts index 1548fb4167..668059d9dc 100644 --- a/modules/schema/src/lib/table/batches/row-table-batch-aggregator.ts +++ b/modules/schema/src/lib/table/batches/row-table-batch-aggregator.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Schema} from '../../../types/schema'; import type {TableBatch} from '../../../types/category-table'; @@ -13,8 +14,8 @@ export class RowTableBatchAggregator implements TableBatchAggregator { options: TableBatchOptions; length: number = 0; - objectRows: {[columnName: string]: any} | null = null; - arrayRows: any[] | null = null; + objectRows: {[columnName: string]: unknown}[] | null = null; + arrayRows: unknown[][] | null = null; cursor: number = 0; private _headers: string[] = []; diff --git a/modules/schema/src/lib/table/batches/table-batch-aggregator.ts b/modules/schema/src/lib/table/batches/table-batch-aggregator.ts index 437bbc390c..965fb90613 100644 --- a/modules/schema/src/lib/table/batches/table-batch-aggregator.ts +++ b/modules/schema/src/lib/table/batches/table-batch-aggregator.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Schema} from '../../../types/schema'; import type {TableBatch} from '../../../types/category-table'; diff --git a/modules/schema/src/lib/table/batches/table-batch-builder.ts b/modules/schema/src/lib/table/batches/table-batch-builder.ts index ae940948e6..816aecaf0f 100644 --- a/modules/schema/src/lib/table/batches/table-batch-builder.ts +++ b/modules/schema/src/lib/table/batches/table-batch-builder.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Schema} from '../../../types/schema'; import type {TableBatch} from '../../../types/category-table'; diff --git a/modules/schema/src/lib/table/simple-table/convert-table.ts b/modules/schema/src/lib/table/simple-table/convert-table.ts index aa5cbf7124..187c756b44 100644 --- a/modules/schema/src/lib/table/simple-table/convert-table.ts +++ b/modules/schema/src/lib/table/simple-table/convert-table.ts @@ -1,14 +1,63 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import { getTableCell, getTableLength, getTableRowAsArray, getTableRowAsObject } from './table-accessors'; -import {Table, ArrayRowTable, ObjectRowTable, ColumnarTable} from '../../../types/category-table'; +import { + Table, + ArrayRowTable, + ObjectRowTable, + ColumnarTable, + ArrowTable +} from '../../../types/category-table'; import {deduceTableSchema} from './table-schema'; import {makeColumnFromField} from './table-column'; +export function convertTable(table: Table, shape: 'object-row-table'): ObjectRowTable; +export function convertTable(table: Table, shape: 'array-row-table'): ArrayRowTable; +export function convertTable(table: Table, shape: 'columnar-table'): ColumnarTable; +export function convertTable(table: Table, shape: 'arrow-table'): ArrowTable; + +/** + * Convert a table to a different shape + * @param table + * @param shape + * @returns + */ +export function convertTable( + table: Table, + shape: 'object-row-table' | 'array-row-table' | 'columnar-table' | 'arrow-table' +) { + switch (shape) { + case 'object-row-table': + return makeObjectRowTable(table); + case 'array-row-table': + return makeArrayRowTable(table); + case 'columnar-table': + return makeColumnarTable(table); + case 'arrow-table': + return makeArrowTable(table); + default: + throw new Error(shape); + } +} + +/** + * Convert a table to apache arrow format + * @note this depends on the `@loaders.gl/arrow module being imported + */ +export function makeArrowTable(table: Table): Table { + const _makeArrowTable = globalThis.__loaders?._makeArrowTable; + if (!_makeArrowTable) { + throw new Error(''); + } + return _makeArrowTable(table); +} + /** Convert any simple table into columnar format */ export function makeColumnarTable(table: Table): ColumnarTable { // TODO - should schema really be optional? @@ -70,3 +119,28 @@ export function makeObjectRowTable(table: Table): ObjectRowTable { data }; } + +/** +/** + * + * @note - should be part of schema module +export function convertColumnarToRowFormatTable(columnarTable: ColumnarTable): ObjectRowTable { + const tableKeys = ; + const tableRowsCount = columnarTable[tableKeys[0]].length; + + const objectRows: ObjectRowTable['data'] = []; + + for (let index = 0; index < tableRowsCount; index++) { + const objectRow = {}; + for (const fieldName of Object.keys(columnarTable.data)) { + objectRow[fieldName] = columnarTable[fieldName][index]; + } + objectRows.push(objectRow); + } + + return { + shape: 'object-row-table', + data: objectRows + }; +} + */ diff --git a/modules/schema/src/lib/table/simple-table/data-type.ts b/modules/schema/src/lib/table/simple-table/data-type.ts index 56e709c6c1..76a2f3acb5 100644 --- a/modules/schema/src/lib/table/simple-table/data-type.ts +++ b/modules/schema/src/lib/table/simple-table/data-type.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {DataType} from '../../../types/schema'; import {TypedArray, TypedArrayConstructor, ArrayType} from '../../../types/types'; diff --git a/modules/schema/src/lib/table/simple-table/make-table-from-batches.ts b/modules/schema/src/lib/table/simple-table/make-table-from-batches.ts new file mode 100644 index 0000000000..0e8416746d --- /dev/null +++ b/modules/schema/src/lib/table/simple-table/make-table-from-batches.ts @@ -0,0 +1,102 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type { + TableBatch, + Table, + Schema, + ObjectRowTable, + ArrayRowTable, + Feature +} from '@loaders.gl/schema'; +import {getTableLength} from '@loaders.gl/schema'; + +/** + * Returns an iterator that yields a single table as a sequence of batches. + * @note Currently only a single batch is yielded. + * @note All batches will have the same shape and schema as the original table. + * @returns + */ +export function* makeBatchesFromTable(table: Table): IterableIterator { + yield makeBatchFromTable(table); +} + +/** + * Returns a table packaged as a single table batch + * @note The batch will have the same shape and schema as the original table. + * @returns `null` if no batches are yielded by the async iterator + */ +export function makeBatchFromTable(table: Table): TableBatch { + return {...table, length: getTableLength(table), batchType: 'data'}; +} + +/** + * Assembles all batches from an async iterator into a single table. + * @note All batches must have the same shape and schema + * @param batchIterator + * @returns `null` if no batches are yielded by the async iterator + */ +// eslint-disable-next-line complexity +export async function makeTableFromBatches( + batchIterator: AsyncIterable | Iterable +): Promise { + let arrayRows: ArrayRowTable['data']; + let objectRows: ObjectRowTable['data']; + let features: Feature[]; + let shape: Table['shape'] | null = null; + let schema: Schema | undefined; + + for await (const batch of batchIterator) { + shape = shape || batch.shape; + schema = schema || batch.schema; + + switch (batch.shape) { + case 'array-row-table': + arrayRows = arrayRows! || []; + for (let rowIndex = 0; rowIndex < getTableLength(batch); rowIndex++) { + const row = batch.data[rowIndex]; + arrayRows.push(row); + } + break; + + case 'object-row-table': + objectRows = objectRows! || []; + for (let rowIndex = 0; rowIndex < getTableLength(batch); rowIndex++) { + const row = batch.data[rowIndex]; + objectRows.push(row); + } + break; + + case 'geojson-table': + features = features! || []; + for (let rowIndex = 0; rowIndex < getTableLength(batch); rowIndex++) { + const row = batch.features[rowIndex]; + features.push(row); + } + break; + + case 'columnar-table': + case 'arrow-table': + default: + throw new Error('shape'); + } + } + + if (!shape) { + return null; + } + + switch (shape) { + case 'array-row-table': + return {shape: 'array-row-table', data: arrayRows!, schema}; + + case 'object-row-table': + return {shape: 'object-row-table', data: objectRows!, schema}; + + case 'geojson-table': + return {shape: 'geojson-table', type: 'FeatureCollection', features: features!, schema}; + + default: + return null; + } +} diff --git a/modules/schema/src/lib/table/simple-table/make-table.ts b/modules/schema/src/lib/table/simple-table/make-table.ts index 201e45d84d..8f9e823a04 100644 --- a/modules/schema/src/lib/table/simple-table/make-table.ts +++ b/modules/schema/src/lib/table/simple-table/make-table.ts @@ -1,4 +1,6 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import {Table, ArrayRowTable, ObjectRowTable, ColumnarTable} from '../../../types/category-table'; import {deduceTableSchema} from './table-schema'; diff --git a/modules/schema/src/lib/table/simple-table/row-utils.ts b/modules/schema/src/lib/table/simple-table/row-utils.ts index 67da0ec461..1754575dfb 100644 --- a/modules/schema/src/lib/table/simple-table/row-utils.ts +++ b/modules/schema/src/lib/table/simple-table/row-utils.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + /** Convert an object row to an array row */ export function convertToObjectRow( arrayRow: any[], diff --git a/modules/schema/src/lib/table/simple-table/table-accessors.ts b/modules/schema/src/lib/table/simple-table/table-accessors.ts index 0995648c49..3a8dac714f 100644 --- a/modules/schema/src/lib/table/simple-table/table-accessors.ts +++ b/modules/schema/src/lib/table/simple-table/table-accessors.ts @@ -1,9 +1,31 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors /* eslint-disable no-else-return */ import {Table, ArrayRowTable, ObjectRowTable} from '../../../types/category-table'; +export function isTable(table: any): table is Table { + const shape = typeof table === 'object' && table?.shape; + switch (shape) { + case 'array-row-table': + case 'object-row-table': + return Array.isArray(table.data); + + case 'geojson-table': + return Array.isArray(table.features); + + case 'columnar-table': + return table.data && typeof table.data === 'object'; + + case 'arrow-table': + return Boolean(table?.data?.numRows !== undefined); + + default: + return false; + } +} + /** * Returns the length of the table (i.e. the number of rows) */ @@ -11,11 +33,14 @@ export function getTableLength(table: Table): number { switch (table.shape) { case 'array-row-table': case 'object-row-table': - case 'geojson-row-table': return table.data.length; + case 'geojson-table': + return table.features.length; + case 'arrow-table': - return table.data.numRows; + const arrowTable = table.data as any; + return arrowTable.numRows; case 'columnar-table': for (const column of Object.values(table.data)) { @@ -42,14 +67,14 @@ export function getTableNumCols(table: Table): number { case 'array-row-table': return table.data[0].length; case 'object-row-table': - case 'geojson-row-table': return Object.keys(table.data[0]).length; - + case 'geojson-table': + return Object.keys(table.features[0]).length; case 'columnar-table': return Object.keys(table.data).length; - case 'arrow-table': - return table.data.numCols; + const arrowTable = table.data as any; + return arrowTable.numCols; default: throw new Error('table'); } @@ -63,18 +88,21 @@ export function getTableCell(table: Table, rowIndex: number, columnName: string) return table.data[rowIndex][columnIndex]; case 'object-row-table': - case 'geojson-row-table': return table.data[rowIndex][columnName]; + case 'geojson-table': + return table.features[rowIndex][columnName]; + case 'columnar-table': const column = table.data[columnName]; return column[rowIndex]; case 'arrow-table': - const arrowColumnIndex = table.data.schema.fields.findIndex( + const arrowTable = table.data as any; + const arrowColumnIndex = arrowTable.schema.fields.findIndex( (field) => field.name === columnName ); - return table.data.getChildAt(arrowColumnIndex)?.get(rowIndex); + return arrowTable.getChildAt(arrowColumnIndex)?.get(rowIndex); default: throw new Error('todo'); @@ -88,17 +116,21 @@ export function getTableCellAt(table: Table, rowIndex: number, columnIndex: numb return table.data[rowIndex][columnIndex]; case 'object-row-table': - case 'geojson-row-table': - let columnName = getTableColumnName(table, columnIndex); - return table.data[rowIndex][columnName]; + const columnName1 = getTableColumnName(table, columnIndex); + return table.data[rowIndex][columnName1]; + + case 'geojson-table': + const columnName2 = getTableColumnName(table, columnIndex); + return table.features[rowIndex][columnName2]; case 'columnar-table': - columnName = getTableColumnName(table, columnIndex); - const column = table.data[columnName]; + const columnName3 = getTableColumnName(table, columnIndex); + const column = table.data[columnName3]; return column[rowIndex]; case 'arrow-table': - return table.data.getChildAt(columnIndex)?.get(rowIndex); + const arrowTable = table.data as any; + return arrowTable.getChildAt(columnIndex)?.get(rowIndex); default: throw new Error('todo'); @@ -112,7 +144,8 @@ export function getTableRowShape(table: Table): 'array-row-table' | 'object-row- case 'object-row-table': return table.shape; - case 'geojson-row-table': + case 'geojson-table': + // TODO - this is not correct, geojson-table is not a row table return 'object-row-table'; case 'columnar-table': @@ -156,7 +189,6 @@ export function getTableRowAsObject( return copy ? Object.fromEntries(Object.entries(table.data[rowIndex])) : table.data[rowIndex]; case 'array-row-table': - case 'geojson-row-table': if (table.schema) { const objectRow: {[columnName: string]: unknown} = target || {}; for (let i = 0; i < table.schema.fields.length; i++) { @@ -166,6 +198,17 @@ export function getTableRowAsObject( } throw new Error('no schema'); + case 'geojson-table': + if (table.schema) { + const objectRow: {[columnName: string]: unknown} = target || {}; + // TODO - should lift properties to top level + for (let i = 0; i < table.schema.fields.length; i++) { + objectRow[table.schema.fields[i].name] = table.features[rowIndex][i]; + } + return objectRow; + } + throw new Error('no schema'); + case 'columnar-table': if (table.schema) { const objectRow: {[columnName: string]: unknown} = target || {}; @@ -184,9 +227,10 @@ export function getTableRowAsObject( } case 'arrow-table': + const arrowTable = table.data as any; const objectRow: {[columnName: string]: unknown} = target || {}; - const row = table.data.get(rowIndex); - const schema = table.data.schema; + const row = arrowTable.get(rowIndex); + const schema = arrowTable.schema; for (let i = 0; i < schema.fields.length; i++) { objectRow[schema.fields[i].name] = row?.[schema.fields[i].name]; } @@ -214,7 +258,6 @@ export function getTableRowAsArray( return copy ? Array.from(table.data[rowIndex]) : table.data[rowIndex]; case 'object-row-table': - case 'geojson-row-table': if (table.schema) { const arrayRow: unknown[] = target || []; for (let i = 0; i < table.schema.fields.length; i++) { @@ -225,6 +268,18 @@ export function getTableRowAsArray( // Warning: just slap on the values, this risks mismatches between rows return Object.values(table.data[rowIndex]); + case 'geojson-table': + if (table.schema) { + const arrayRow: unknown[] = target || []; + // TODO - should lift properties to top level + for (let i = 0; i < table.schema.fields.length; i++) { + arrayRow[i] = table.features[rowIndex][table.schema.fields[i].name]; + } + return arrayRow; + } + // Warning: just slap on the values, this risks mismatches between rows + return Object.values(table.features[rowIndex]); + case 'columnar-table': if (table.schema) { const arrayRow: unknown[] = target || []; @@ -244,9 +299,10 @@ export function getTableRowAsArray( } case 'arrow-table': + const arrowTable = table.data as any; const arrayRow: unknown[] = target || []; - const row = table.data.get(rowIndex); - const schema = table.data.schema; + const row = arrowTable.get(rowIndex); + const schema = arrowTable.schema; for (let i = 0; i < schema.fields.length; i++) { arrayRow[i] = row?.[schema.fields[i].name]; } diff --git a/modules/schema/src/lib/table/simple-table/table-column.ts b/modules/schema/src/lib/table/simple-table/table-column.ts index 1089d722d2..6fd4466187 100644 --- a/modules/schema/src/lib/table/simple-table/table-column.ts +++ b/modules/schema/src/lib/table/simple-table/table-column.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // import type {TypedArray,} from '../../../types/types'; import {Field} from '../../../types/schema'; diff --git a/modules/schema/src/lib/table/simple-table/table-schema.ts b/modules/schema/src/lib/table/simple-table/table-schema.ts index cb7e7dd658..5a497b4289 100644 --- a/modules/schema/src/lib/table/simple-table/table-schema.ts +++ b/modules/schema/src/lib/table/simple-table/table-schema.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + // Type deduction import {Schema, Field} from '../../../types/schema'; import {ArrayType} from '../../../types/types'; diff --git a/modules/schema/src/types/batch.ts b/modules/schema/src/types/batch.ts new file mode 100644 index 0000000000..d227439fbe --- /dev/null +++ b/modules/schema/src/types/batch.ts @@ -0,0 +1,44 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {Schema} from './schema'; +// import type {RecordBatch} from 'apache-arrow'; + +type ApacheRecordBatch = unknown; + +/** + * A batch of data (or metadata/schema), from a streaming loader + * @see parseInBatches() + * @see loadInBatches() + */ +export type Batch = { + /** A batch can contain metadata, data, or in case of unstructured data (JSON) */ + batchType: 'data' | 'metadata' | 'partial-result' | 'final-result'; + /** A string identifying the shape of data in this batch (table, etc) */ + shape: string; + /** Schema of the data in this batch */ + schema?: Schema; + /** Data in this batch */ + data?: unknown; + /** If this is an arrow table. @deprecated Use `data` */ + recordBatch?: ApacheRecordBatch; + /** Length of data in this batch */ + length: number; + + /** A count of batches received */ + batch?: number; + + /** A count of batches received */ + count?: number; + + /** Bytes used so far */ + bytesUsed?: number; + /** cursor is the */ + cursor?: number; + + /** MIME type of the data generating this batch */ + mimeType?: string; + + /** Any other data */ + [key: string]: unknown; +}; diff --git a/modules/schema/src/types/binary-geometries.ts b/modules/schema/src/types/binary-geometries.ts new file mode 100644 index 0000000000..2f2d0632e5 --- /dev/null +++ b/modules/schema/src/types/binary-geometries.ts @@ -0,0 +1,66 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +// GIS +import type {TypedArray} from './types'; + +// BINARY FORMAT GEOMETRY + +export type BinaryAttribute = {value: TypedArray; size: number}; +export type BinaryGeometryType = 'Point' | 'LineString' | 'Polygon'; + +type NumericProps = {[key: string]: BinaryAttribute}; +type Properties = object[]; + +/** + * Represent a single Geometry, similar to a GeoJSON Geometry + */ +export type BinaryGeometry = BinaryPointGeometry | BinaryLineGeometry | BinaryPolygonGeometry; + +/** Binary point geometry: an array of positions */ +export type BinaryPointGeometry = { + type: 'Point'; + positions: BinaryAttribute; +}; + +/** Binary line geometry, array of positions and indices to the start of each line */ +export type BinaryLineGeometry = { + type: 'LineString'; + positions: BinaryAttribute; + pathIndices: BinaryAttribute; +}; + +/** Binary polygon geometry, an array of positions to each primitite polygon and polygon */ +export type BinaryPolygonGeometry = { + type: 'Polygon'; + positions: BinaryAttribute; + polygonIndices: BinaryAttribute; + primitivePolygonIndices: BinaryAttribute; + triangles?: BinaryAttribute; +}; + +/** Common properties for binary geometries */ +export type BinaryProperties = { + featureIds: BinaryAttribute; + globalFeatureIds: BinaryAttribute; + numericProps: NumericProps; + properties: Properties; + fields?: Properties; +}; + +/** Binary feature + binary attributes */ +export type BinaryFeature = BinaryPointFeature | BinaryLineFeature | BinaryPolygonFeature; + +export type BinaryPointFeature = BinaryPointGeometry & BinaryProperties; +export type BinaryLineFeature = BinaryLineGeometry & BinaryProperties; +export type BinaryPolygonFeature = BinaryPolygonGeometry & BinaryProperties; + +/** + * Represent a collection of Features, similar to a GeoJSON FeatureCollection + */ +export type BinaryFeatureCollection = { + shape: 'binary-feature-collection'; + points?: BinaryPointFeature; + lines?: BinaryLineFeature; + polygons?: BinaryPolygonFeature; +}; diff --git a/modules/schema/src/types/category-gis.ts b/modules/schema/src/types/category-gis.ts index 048ace0262..d137715cca 100644 --- a/modules/schema/src/types/category-gis.ts +++ b/modules/schema/src/types/category-gis.ts @@ -1,10 +1,9 @@ -// GIS -import type {TypedArray} from './types'; -import type {Feature, Geometry, Point, LineString, Polygon} from 'geojson'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors -// GEOJSON FORMAT GEOMETRY +// GIS -// eslint-disable-next-line import/no-unresolved +// NORMAL GEOJSON FORMAT GEOMETRY export type { GeoJSON, Feature, @@ -13,7 +12,7 @@ export type { Position, GeoJsonProperties } from 'geojson'; -// eslint-disable-next-line import/no-unresolved + export type { Point, MultiPoint, @@ -24,7 +23,34 @@ export type { GeometryCollection } from 'geojson'; -// Aggregate information for converting GeoJSON into other formats +// FLAT GEOJSON FORMAT GEOMETRY +export type { + FlatGeometryType, + FlatIndexedGeometry, + FlatPoint, + FlatLineString, + FlatPolygon, + FlatGeometry, + FlatFeature +} from './flat-geometries'; + +// BINARY FORMAT GEOMETRY +export type { + BinaryAttribute, + BinaryGeometryType, + BinaryGeometry, + BinaryPointGeometry, + BinaryLineGeometry, + BinaryPolygonGeometry, + BinaryProperties, + BinaryFeatureCollection, + BinaryFeature, + BinaryPointFeature, + BinaryLineFeature, + BinaryPolygonFeature +} from './binary-geometries'; + +/** Aggregate information for converting GeoJSON into other formats */ export type GeojsonGeometryInfo = { coordLength: number; pointPositionsCount: number; @@ -37,100 +63,3 @@ export type GeojsonGeometryInfo = { polygonRingsCount: number; polygonFeaturesCount: number; }; - -// FLAT GEOJSON FORMAT GEOMETRY -export type FlatGeometryType = 'Point' | 'LineString' | 'Polygon'; -type RemoveCoordinatesField = { - [Property in keyof Type as Exclude]: Type[Property]; -}; - -/** - * Generic flat geometry data storage type - */ -export type FlatIndexedGeometry = { - data: number[]; - indices: number[]; -}; - -/** - * GeoJSON (Multi)Point geometry with coordinate data flattened into `data` array and indexed by `indices` - */ -export type FlatPoint = RemoveCoordinatesField & FlatIndexedGeometry; - -/** - * GeoJSON (Multi)LineString geometry with coordinate data flattened into `data` array and indexed by `indices` - */ -export type FlatLineString = RemoveCoordinatesField & FlatIndexedGeometry; - -/** - * GeoJSON (Multi)Polygon geometry with coordinate data flattened into `data` array and indexed by 2D `indices` - */ -export type FlatPolygon = RemoveCoordinatesField & { - data: number[]; - indices: number[][]; - areas: number[][]; -}; - -export type FlatGeometry = FlatPoint | FlatLineString | FlatPolygon; - -type FlattenGeometry = { - [Property in keyof Type]: Type[Property] extends Geometry ? FlatGeometry : Type[Property]; -}; - -/** - * GeoJSON Feature with Geometry replaced by FlatGeometry - */ -export type FlatFeature = FlattenGeometry; - -// BINARY FORMAT GEOMETRY - -export type BinaryAttribute = {value: TypedArray; size: number}; -export type BinaryGeometryType = 'Point' | 'LineString' | 'Polygon'; - -type NumericProps = {[key: string]: BinaryAttribute}; -type Properties = object[]; - -/** - * Represent a single Geometry, similar to a GeoJSON Geometry - */ -export type BinaryGeometry = BinaryPointGeometry | BinaryLineGeometry | BinaryPolygonGeometry; - -export type BinaryPointGeometry = { - type: 'Point'; - positions: BinaryAttribute; -}; - -export type BinaryLineGeometry = { - type: 'LineString'; - positions: BinaryAttribute; - pathIndices: BinaryAttribute; -}; - -export type BinaryPolygonGeometry = { - type: 'Polygon'; - positions: BinaryAttribute; - polygonIndices: BinaryAttribute; - primitivePolygonIndices: BinaryAttribute; - triangles?: BinaryAttribute; -}; - -export type BinaryProperties = { - featureIds: BinaryAttribute; - globalFeatureIds: BinaryAttribute; - numericProps: NumericProps; - properties: Properties; - fields?: Properties; -}; - -export type BinaryPointFeatures = BinaryPointGeometry & BinaryProperties; -export type BinaryLineFeatures = BinaryLineGeometry & BinaryProperties; -export type BinaryPolygonFeatures = BinaryPolygonGeometry & BinaryProperties; - -/** - * Represent a collection of Features, similar to a GeoJSON FeatureCollection - */ -export type BinaryFeatures = { - points?: BinaryPointFeatures; - lines?: BinaryLineFeatures; - polygons?: BinaryPolygonFeatures; -}; diff --git a/modules/schema/src/types/category-image.ts b/modules/schema/src/types/category-image.ts index ee327abc25..1efbf7bd53 100644 --- a/modules/schema/src/types/category-image.ts +++ b/modules/schema/src/types/category-image.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + /** * data images */ @@ -11,7 +14,7 @@ export type ImageDataType = { /** * Supported Image Types */ -export type ImageType = ImageBitmap | typeof Image | ImageDataType; +export type ImageType = ImageBitmap | ImageDataType | HTMLImageElement; /** * Image type string used to control or determine the type of images returned from ImageLoader diff --git a/modules/schema/src/types/category-mesh.ts b/modules/schema/src/types/category-mesh.ts index 0a3d59a53a..cf8a49992d 100644 --- a/modules/schema/src/types/category-mesh.ts +++ b/modules/schema/src/types/category-mesh.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Schema} from './schema'; import type {TypedArray} from './types'; diff --git a/modules/schema/src/types/category-table.ts b/modules/schema/src/types/category-table.ts index 7be673383d..0c8245a6a2 100644 --- a/modules/schema/src/types/category-table.ts +++ b/modules/schema/src/types/category-table.ts @@ -1,26 +1,27 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import type {Table as ApacheArrowTable, RecordBatch} from 'apache-arrow'; -import type {Batch, Schema} from './schema'; +import type {Schema} from './schema'; +import type {Batch} from './batch'; import type {Feature} from './category-gis'; -// Idea was to just import types, but it seems -// Seems this triggers more bundling and build issues than it is worth... -// import type {Table as ApacheArrowTable, RecordBatch} from 'apache-arrow'; -// type ApacheArrowTable = any; -// type RecordBatch = any; +// Avoid a big dependency, apparently even a type import can pull in a lot of code +// import type {Table as ApacheArrowTable} from 'apache-arrow'; + +type ApacheArrowTable = unknown; +type ApacheRecordBatch = unknown; /** A general table */ export type Table = | RowTable | ArrayRowTable | ObjectRowTable - | GeoJSONRowTable + | GeoJSONTable | ColumnarTable | ArrowTable; /** A table organized as an array of rows */ -export type RowTable = ArrayRowTable | ObjectRowTable | GeoJSONRowTable; +export type RowTable = ArrayRowTable | ObjectRowTable | GeoJSONTable; /** A table organized as an array of rows, each row is an array of values */ export type ArrayRowTable = { @@ -36,11 +37,17 @@ export type ObjectRowTable = { data: {[columnName: string]: any}[]; }; -/** A table organized as an array of rows, each row is a GeoJSON Feature */ -export type GeoJSONRowTable = { - shape: 'geojson-row-table'; +/** + * A table organized as an array of rows, each row is a GeoJSON Feature + * @note For compatibility with GeoJSON, rows are stored in `table.features` instead of `table.data` + */ +export type GeoJSONTable = { + shape: 'geojson-table'; schema?: Schema; - data: Feature[]; + /** For compatibility with GeoJSON, the type field must always be set to `FeatureCollection` */ + type: 'FeatureCollection'; + /** For compatibility with GeoJSON, rows are stored in `table.features` instead of `table.data` */ + features: Feature[]; }; /** A table organized as a map of columns, each column is an array of value */ @@ -66,39 +73,55 @@ export type Tables = { // Batches /** Batch for a general table */ -export type TableBatch = Batch & { - data: any; - length: number; - schema?: Schema; - schemaType?: 'explicit' | 'deduced'; -}; +export type TableBatch = + | ArrayRowTableBatch + | ObjectRowTableBatch + | GeoJSONTableBatch + | ColumnarTableBatch + | ArrowTableBatch; /** Batch for a table organized as an array of rows, each row is an array of values */ -export type ArrayRowTableBatch = TableBatch & { +export type ArrayRowTableBatch = Batch & { shape: 'array-row-table'; + schema?: Schema; + schemaType?: 'explicit' | 'deduced'; data: any[][]; + length: number; }; /** Batch for a table organized as an array of rows, each row is an object mapping columns to values */ -export type ObjectRowTableBatch = TableBatch & { +export type ObjectRowTableBatch = Batch & { shape: 'object-row-table'; + schema?: Schema; + schemaType?: 'explicit' | 'deduced'; data: {[columnName: string]: any}[]; + length: number; }; /** Batch for a table organized as an array of rows, each row is an array of values */ -export type GeoJSONRowTableBatch = TableBatch & { - shape: 'geojson-row-table'; - data: Feature[]; +export type GeoJSONTableBatch = Batch & { + shape: 'geojson-table'; + schema?: Schema; + schemaType?: 'explicit' | 'deduced'; + type: 'FeatureCollection'; + features: Feature[]; + length: number; }; /** Batch for a table organized as a map of columns, each column is an array of value */ -export type ColumnarTableBatch = TableBatch & { +export type ColumnarTableBatch = Batch & { shape: 'columnar-table'; + schemaType?: 'explicit' | 'deduced'; + schema?: Schema; data: {[columnName: string]: ArrayLike}; + length: number; }; /** Batch for a table organized as an Apache Arrow table */ -export type ArrowTableBatch = TableBatch & { +export type ArrowTableBatch = Batch & { shape: 'arrow-table'; - data: RecordBatch; + schemaType?: 'explicit' | 'deduced'; + schema?: Schema; + data: ApacheRecordBatch; + length: number; }; diff --git a/modules/schema/src/types/category-texture.ts b/modules/schema/src/types/category-texture.ts index 8126822901..73f314c56f 100644 --- a/modules/schema/src/types/category-texture.ts +++ b/modules/schema/src/types/category-texture.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {ImageType} from './category-image'; diff --git a/modules/schema/src/types/flat-geometries.ts b/modules/schema/src/types/flat-geometries.ts new file mode 100644 index 0000000000..1a54111a71 --- /dev/null +++ b/modules/schema/src/types/flat-geometries.ts @@ -0,0 +1,41 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +// FLAT GEOJSON FORMAT GEOMETRY +import type {Feature, Geometry, Point, LineString, Polygon} from 'geojson'; + +/** Flat geometry type */ +export type FlatGeometryType = 'Point' | 'LineString' | 'Polygon'; + +type RemoveCoordinatesField = { + [Property in keyof Type as Exclude]: Type[Property]; +}; + +/** Generic flat geometry data storage type */ +export type FlatIndexedGeometry = { + data: number[]; + indices: number[]; +}; + +/** GeoJSON (Multi)Point geometry with coordinate data flattened into `data` array and indexed by `indices` */ +export type FlatPoint = RemoveCoordinatesField & FlatIndexedGeometry; + +/** GeoJSON (Multi)LineString geometry with coordinate data flattened into `data` array and indexed by `indices` */ +export type FlatLineString = RemoveCoordinatesField & FlatIndexedGeometry; + +/** GeoJSON (Multi)Polygon geometry with coordinate data flattened into `data` array and indexed by 2D `indices` */ +export type FlatPolygon = RemoveCoordinatesField & { + data: number[]; + indices: number[][]; + areas: number[][]; +}; + +/** GeoJSON geometry with coordinate data flattened into `data` array and indexed by 2D `indices` */ +export type FlatGeometry = FlatPoint | FlatLineString | FlatPolygon; + +type FlattenGeometry = { + [Property in keyof Type]: Type[Property] extends Geometry ? FlatGeometry : Type[Property]; +}; + +/** GeoJSON Feature with Geometry replaced by FlatGeometry */ +export type FlatFeature = FlattenGeometry; diff --git a/modules/schema/src/types/schema.ts b/modules/schema/src/types/schema.ts index 20614e3b4e..b2c823ef1d 100644 --- a/modules/schema/src/types/schema.ts +++ b/modules/schema/src/types/schema.ts @@ -1,6 +1,5 @@ // loaders.gl, MIT license - -import type {RecordBatch} from 'apache-arrow'; +// Copyright (c) vis.gl contributors /** For dictionary type */ export type KeyType = 'int8' | 'int16' | 'int32' | 'uint8' | 'uint16' | 'uint32'; @@ -38,6 +37,7 @@ export type DataType = | 'interval-daytime' | 'interval-yearmonth' // Composite types + | {type: 'decimal'; bitWidth: number; precision: number; scale: number} | {type: 'list'; children: Field[]} // one child only | {type: 'struct'; children: Field[]} | { @@ -80,18 +80,3 @@ export type Schema = { fields: Field[]; metadata: SchemaMetadata; }; - -export type Batch = { - batchType: 'data' | 'metadata' | 'partial-result' | 'final-result'; - batch?: number; - mimeType?: string; - shape: string; - data: any; - recordBatch?: RecordBatch; - length: number; - schema?: Schema; - bytesUsed?: number; - count?: number; - cursor?: number; - [key: string]: any; -}; diff --git a/modules/schema/src/types/types.ts b/modules/schema/src/types/types.ts index ce48fe595b..72817304c2 100644 --- a/modules/schema/src/types/types.ts +++ b/modules/schema/src/types/types.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + /** Any typed array */ export type TypedArray = | Int8Array @@ -19,8 +22,6 @@ export type TypedArrayConstructor = | Uint16ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor - | Int32ArrayConstructor - | Uint32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor; diff --git a/modules/schema/test/data/table/tables.ts b/modules/schema/test/data/table/tables.ts index eac6bcdece..d71ad52293 100644 --- a/modules/schema/test/data/table/tables.ts +++ b/modules/schema/test/data/table/tables.ts @@ -1,4 +1,6 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors + // These tables are taken from parquet /* eslint-disable camelcase */ @@ -172,7 +174,7 @@ export const BINARY_PLAIN_TABLE = () => { const result: {[key: string]: unknown}[] = []; for (let index = 0; index < 12; index++) { - result.push({foo: Buffer.from([index])}); + result.push({foo: new Uint8Array([index])}); } return result; @@ -277,7 +279,7 @@ export const NESTED_MAPS_PLAIN_TABLE = [ a: { key_value: [ { - key: Buffer.from([97]), + key: new Uint8Array([97]), value: { key_value: [ {key: '1', value: true}, @@ -294,7 +296,7 @@ export const NESTED_MAPS_PLAIN_TABLE = [ a: { key_value: [ { - key: Buffer.from([98]), + key: new Uint8Array([98]), value: { key_value: [{key: '1', value: true}] } @@ -306,7 +308,7 @@ export const NESTED_MAPS_PLAIN_TABLE = [ }, { a: { - key_value: [{key: Buffer.from([99])}] + key_value: [{key: new Uint8Array([99])}] }, b: '1', c: '1' @@ -315,7 +317,7 @@ export const NESTED_MAPS_PLAIN_TABLE = [ a: { key_value: [ { - key: Buffer.from([100]), + key: new Uint8Array([100]), value: {} } ] @@ -327,7 +329,7 @@ export const NESTED_MAPS_PLAIN_TABLE = [ a: { key_value: [ { - key: Buffer.from([101]), + key: new Uint8Array([101]), value: { key_value: [{key: '1', value: true}] } @@ -341,7 +343,7 @@ export const NESTED_MAPS_PLAIN_TABLE = [ a: { key_value: [ { - key: Buffer.from([102]), + key: new Uint8Array([102]), value: { key_value: [ {key: '3', value: true}, @@ -362,11 +364,11 @@ export const NO_NULLABLE_PLAIN_TABLE = [ ID: 8, Int_Array: {list: [{element: -1}]}, int_array_array: {list: [{element: {list: [{element: -1}, {element: -2}]}}, {element: {}}]}, - Int_Map: {map: [{key: Buffer.from([107, 49]), value: -1}]}, + Int_Map: {map: [{key: new Uint8Array([107, 49]), value: -1}]}, int_map_array: { list: [ {element: {}}, - {element: {map: [{key: Buffer.from([107, 49]), value: 1}]}}, + {element: {map: [{key: new Uint8Array([107, 49]), value: 1}]}}, {element: {}}, {element: {}} ] @@ -383,7 +385,7 @@ export const NO_NULLABLE_PLAIN_TABLE = [ { element: { e: -1, - f: Buffer.from([110, 111, 110, 110, 117, 108, 108, 97, 98, 108, 101]) + f: new Uint8Array([110, 111, 110, 110, 117, 108, 108, 97, 98, 108, 101]) } } ] @@ -586,14 +588,14 @@ export const DECIMAL_PLAIN_TABLE = [ ]; export const LZ4_RAW_COMPRESSED_LARGER_FIRST_PLAIN_TABLE = { - a: Buffer.from([ + a: new Uint8Array([ 99, 55, 99, 101, 54, 98, 101, 102, 45, 100, 53, 98, 48, 45, 52, 56, 54, 51, 45, 98, 49, 57, 57, 45, 56, 101, 97, 56, 99, 55, 102, 98, 49, 49, 55, 98 ]) }; export const LZ4_RAW_COMPRESSED_LARGER_LAST_PLAIN_TABLE = { - a: Buffer.from([ + a: new Uint8Array([ 56, 53, 52, 52, 48, 55, 55, 56, 45, 52, 54, 48, 97, 45, 52, 49, 97, 99, 45, 97, 97, 50, 101, 45, 97, 99, 51, 101, 101, 52, 49, 54, 57, 54, 98, 102 ]) @@ -602,22 +604,22 @@ export const LZ4_RAW_COMPRESSED_LARGER_LAST_PLAIN_TABLE = { export const LZ4_RAW_COMPRESSED_PLAIN_TABLE = [ { c0: 1593604800, - c1: Buffer.from([97, 98, 99]), + c1: new Uint8Array([97, 98, 99]), v11: 42 }, { c0: 1593604800, - c1: Buffer.from([100, 101, 102]), + c1: new Uint8Array([100, 101, 102]), v11: 7.7 }, { c0: 1593604801, - c1: Buffer.from([97, 98, 99]), + c1: new Uint8Array([97, 98, 99]), v11: 42.125 }, { c0: 1593604801, - c1: Buffer.from([100, 101, 102]), + c1: new Uint8Array([100, 101, 102]), v11: 7.7 } ]; diff --git a/modules/schema/test/index.ts b/modules/schema/test/index.ts index dd945a2c94..b822f7ab96 100644 --- a/modules/schema/test/index.ts +++ b/modules/schema/test/index.ts @@ -1,5 +1,9 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import './lib/utils/async-queue.spec'; import './lib/types/type-utils.spec'; -import './lib/table/table-utils.spec'; +import './lib/table/table-accessors.spec'; import './lib/table/make-table.spec'; +import './lib/table/make-table-from-batches.spec'; diff --git a/modules/schema/test/lib/table/convert-table.spec.ts b/modules/schema/test/lib/table/convert-table.spec.ts index 236baf1648..a890a8b807 100644 --- a/modules/schema/test/lib/table/convert-table.spec.ts +++ b/modules/schema/test/lib/table/convert-table.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; import {makeTableFromData} from '@loaders.gl/schema'; diff --git a/modules/schema/test/lib/table/deduce-table-schema.spec.ts b/modules/schema/test/lib/table/deduce-table-schema.spec.ts index e69de29bb2..192e833945 100644 --- a/modules/schema/test/lib/table/deduce-table-schema.spec.ts +++ b/modules/schema/test/lib/table/deduce-table-schema.spec.ts @@ -0,0 +1,2 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors diff --git a/modules/schema/test/lib/table/make-table-from-batches.spec.ts b/modules/schema/test/lib/table/make-table-from-batches.spec.ts new file mode 100644 index 0000000000..86c2613c28 --- /dev/null +++ b/modules/schema/test/lib/table/make-table-from-batches.spec.ts @@ -0,0 +1,50 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import { + makeTableFromBatches, + makeTableFromData, + makeBatchFromTable, + getTableLength +} from '../../../src'; // '@loaders.gl/schema'; why don't we get typings? + +// import * from '../../data/table/tables'; +import { + TABLES, + // ALL_TYPES_DICTIONARY_PLAIN_TABLE, + ALL_TYPES_PLAIN_PLAIN_TABLE + // ALL_TYPES_PLAIN_SNAPPY_PLAIN_TABLE, + // BINARY_PLAIN_TABLE, + // DECIMAL_PLAIN_TABLE, + // DICT_PLAIN_TABLE, + // LIST_COLUMNS_PLAIN_TABLE, + // NESTED_LIST_PLAIN_TABLE, + // NESTED_MAPS_PLAIN_TABLE, + // NO_NULLABLE_PLAIN_TABLE, + // NULLABLE_PLAIN_TABLE, + // NULLS_PLAIN_TABLE, + // REPEATED_NO_ANNOTATION_PLAIN_TABLE, + // LZ4_RAW_COMPRESSED_LARGER_FIRST_PLAIN_TABLE, + // LZ4_RAW_COMPRESSED_LARGER_LAST_PLAIN_TABLE, + // LZ4_RAW_COMPRESSED_PLAIN_TABLE, + // NON_HADOOP_LZ4_COMPRESSED_PLAIN_TABLE +} from '../../data/table/tables'; + +test.skip('makeTableFromBatches', async (t) => { + const tempTable = makeTableFromData(ALL_TYPES_PLAIN_PLAIN_TABLE); + const batch = makeBatchFromTable(tempTable); + const table = await makeTableFromBatches([batch]); + t.equal(getTableLength(table!), 8); + t.end(); +}); + +test('makeTableFromBatches', async (t) => { + for (const tc of TABLES) { + const tempTable = makeTableFromData(tc.table); + const batch = makeBatchFromTable(tempTable); + const table = await makeTableFromBatches([batch]); + t.equal(getTableLength(table!), tc.length, tc.name); + } + t.end(); +}); diff --git a/modules/schema/test/lib/table/make-table.spec.ts b/modules/schema/test/lib/table/make-table.spec.ts index a56cfd04a5..b3718245ed 100644 --- a/modules/schema/test/lib/table/make-table.spec.ts +++ b/modules/schema/test/lib/table/make-table.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; import {makeTableFromData} from '@loaders.gl/schema'; diff --git a/modules/schema/test/lib/table/table-accessors.spec.ts b/modules/schema/test/lib/table/table-accessors.spec.ts new file mode 100644 index 0000000000..35a1430971 --- /dev/null +++ b/modules/schema/test/lib/table/table-accessors.spec.ts @@ -0,0 +1,66 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +// TBA + +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import {Table, getTableLength, getTableNumCols, isTable} from '@loaders.gl/schema'; + +type TestCase = { + name: string; + table: Table; + isTable: boolean; + numRows: number; + numCols: number; +}; + +const TEST_CASES: TestCase[] = [ + { + name: 'array row table (good)', + table: { + shape: 'array-row-table', + data: [ + [1, 2, 3], + [4, 5, 6] + ] + }, + isTable: true, + numRows: 2, + numCols: 3 + }, + { + name: 'array row table (bad)', + table: { + shape: 'array-row-table', + // @ts-expect-error intentionally wrong shape + data: {a: [1, 2, 3], b: [4, 5, 6]} + }, + isTable: false, + numRows: 2, + numCols: 3 + }, + { + name: 'object row table (good)', + table: { + shape: 'object-row-table', + data: [{a: 1, b: 2}] + }, + isTable: true, + numRows: 1, + numCols: 2 + } +]; + +test('table accessors', async (t) => { + for (const tc of TEST_CASES) { + t.equal(isTable(tc.table), tc.isTable, `isTable() correct: ${tc.name}`); + if (isTable(tc.table)) { + t.equal(getTableLength(tc.table), tc.numRows, `getTableLength() correct: ${tc.name}`); + t.equal(getTableNumCols(tc.table), tc.numCols, `isTable() correct: ${tc.name}`); + } + } + t.end(); +}); diff --git a/modules/schema/test/lib/table/table-utils.spec.ts b/modules/schema/test/lib/table/table-utils.spec.ts deleted file mode 100644 index 9dd5f3baf9..0000000000 --- a/modules/schema/test/lib/table/table-utils.spec.ts +++ /dev/null @@ -1 +0,0 @@ -// TBA diff --git a/modules/schema/test/lib/types/type-utils.spec.ts b/modules/schema/test/lib/types/type-utils.spec.ts index 9dd5f3baf9..a8c0b5fb9c 100644 --- a/modules/schema/test/lib/types/type-utils.spec.ts +++ b/modules/schema/test/lib/types/type-utils.spec.ts @@ -1 +1,4 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + // TBA diff --git a/modules/schema/test/lib/utils/async-queue.spec.ts b/modules/schema/test/lib/utils/async-queue.spec.ts index 72cbdac4e8..e05226e774 100644 --- a/modules/schema/test/lib/utils/async-queue.spec.ts +++ b/modules/schema/test/lib/utils/async-queue.spec.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import test from 'tape-promise/tape'; import {AsyncQueue} from '@loaders.gl/schema'; diff --git a/modules/schema/test/shared-utils/index.ts b/modules/schema/test/shared-utils/index.ts index a82bf3cbc6..17612eb67f 100644 --- a/modules/schema/test/shared-utils/index.ts +++ b/modules/schema/test/shared-utils/index.ts @@ -1,5 +1,6 @@ // loaders.gl, MIT license -// Attribution: Copyright 2023 Foursquare Labs, Inc. +// Copyright (c) vis.gl contributors +// Copyright 2023 Foursquare Labs, Inc. export type {TestTableColumn} from './table-utils'; export {makeTestTable} from './table-utils'; diff --git a/modules/schema/test/shared-utils/table-fixtures.ts b/modules/schema/test/shared-utils/table-fixtures.ts index e3c2d57ae4..cec0f88f49 100644 --- a/modules/schema/test/shared-utils/table-fixtures.ts +++ b/modules/schema/test/shared-utils/table-fixtures.ts @@ -1,5 +1,6 @@ // loaders.gl, MIT license -// Attribution: Copyright 2023 Foursquare Labs, Inc. +// Copyright (c) vis.gl contributors +// Copyright 2023 Foursquare Labs, Inc. import {makeTestTable, makeTestTableFromSchemaAndColumns} from './table-utils'; diff --git a/modules/schema/test/shared-utils/table-utils.ts b/modules/schema/test/shared-utils/table-utils.ts index 3f31b1c5ba..6e6b325ad3 100644 --- a/modules/schema/test/shared-utils/table-utils.ts +++ b/modules/schema/test/shared-utils/table-utils.ts @@ -1,5 +1,6 @@ // loaders.gl, MIT license -// Attribution: Copyright 2023 Foursquare Labs, Inc. +// Copyright (c) vis.gl contributors +// Copyright 2023 Foursquare Labs, Inc. import {Table, Schema, Field} from '@loaders.gl/schema'; diff --git a/modules/shapefile/package.json b/modules/shapefile/package.json index a181091392..227b0c94ec 100644 --- a/modules/shapefile/package.json +++ b/modules/shapefile/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/shapefile", "description": "Loader for the Shapefile Format", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -19,11 +20,17 @@ "shp" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "browser": { - "./src/lib/filesystems/node-filesystem.js": false, "fs": false }, "files": [ @@ -32,16 +39,16 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-worker-shp && npm run build-worker-dbf && npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-worker-shp && npm run build-worker-dbf && npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker-shp": "esbuild src/workers/shp-worker.ts --bundle --outfile=dist/shp-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"", "build-worker-dbf": "esbuild src/workers/dbf-worker.ts --bundle --outfile=dist/dbf-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { - "@loaders.gl/gis": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", - "@math.gl/proj4": "^3.5.1" + "@loaders.gl/gis": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "@math.gl/proj4": "^4.0.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/shapefile/src/bundle.ts b/modules/shapefile/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/shapefile/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/shapefile/src/dbf-loader.ts b/modules/shapefile/src/dbf-loader.ts index 3ef648d5e4..9ef2beda90 100644 --- a/modules/shapefile/src/dbf-loader.ts +++ b/modules/shapefile/src/dbf-loader.ts @@ -29,5 +29,7 @@ export const DBFLoader: LoaderWithParser = { ...DBFWorkerLoader, parse: async (arrayBuffer, options) => parseDBF(arrayBuffer, options), parseSync: parseDBF, - parseInBatches: parseDBFInBatches + parseInBatches(arrayBufferIterator: AsyncIterable | Iterable, options) { + return parseDBFInBatches(arrayBufferIterator, options); + } }; diff --git a/modules/shapefile/src/index.ts b/modules/shapefile/src/index.ts index 49e64f901d..6f0504156c 100644 --- a/modules/shapefile/src/index.ts +++ b/modules/shapefile/src/index.ts @@ -1,3 +1,8 @@ export {ShapefileLoader} from './shapefile-loader'; export {DBFLoader, DBFWorkerLoader} from './dbf-loader'; export {SHPLoader, SHPWorkerLoader} from './shp-loader'; + +// EXPERIMENTAL +export {BinaryReader as _BinaryReader} from './lib/streaming/binary-reader'; +export {BinaryChunkReader as _BinaryChunkReader} from './lib/streaming/binary-chunk-reader'; +export {zipBatchIterators as _zipBatchIterators} from './lib/streaming/zip-batch-iterators'; diff --git a/modules/shapefile/src/lib/parsers/parse-dbf.ts b/modules/shapefile/src/lib/parsers/parse-dbf.ts index 9f6a0d73c0..915f447a1d 100644 --- a/modules/shapefile/src/lib/parsers/parse-dbf.ts +++ b/modules/shapefile/src/lib/parsers/parse-dbf.ts @@ -1,5 +1,5 @@ import {Field, ObjectRowTable} from '@loaders.gl/schema'; -import BinaryChunkReader from '../streaming/binary-chunk-reader'; +import {BinaryChunkReader} from '../streaming/binary-chunk-reader'; import { DBFLoaderOptions, DBFResult, @@ -73,7 +73,7 @@ export function parseDBF( dbfParser.end(); const {data, schema} = dbfParser.result; - const shape = options?.tables?.format || options?.dbf?.shape; + const shape = options?.dbf?.shape; switch (shape) { case 'object-row-table': { const table: ObjectRowTable = { diff --git a/modules/shapefile/src/lib/parsers/parse-shapefile.ts b/modules/shapefile/src/lib/parsers/parse-shapefile.ts index f13c1d76e0..c8d38bf694 100644 --- a/modules/shapefile/src/lib/parsers/parse-shapefile.ts +++ b/modules/shapefile/src/lib/parsers/parse-shapefile.ts @@ -1,11 +1,12 @@ // import type {Feature} from '@loaders.gl/gis'; +import {LoaderContext, parseInBatchesFromContext, parseFromContext} from '@loaders.gl/loader-utils'; +import {binaryToGeometry, transformGeoJsonCoords} from '@loaders.gl/gis'; +import type {BinaryGeometry, Geometry, ObjectRowTableBatch} from '@loaders.gl/schema'; +import {Proj4Projection} from '@math.gl/proj4'; + import type {SHXOutput} from './parse-shx'; import type {SHPHeader} from './parse-shp-header'; -import type {LoaderContext} from '@loaders.gl/loader-utils'; import type {ShapefileLoaderOptions} from './types'; - -import {binaryToGeometry, transformGeoJsonCoords} from '@loaders.gl/gis'; -import {Proj4Projection} from '@math.gl/proj4'; import {parseShx} from './parse-shx'; import {zipBatchIterators} from '../streaming/zip-batch-iterators'; import {SHPLoader} from '../../shp-loader'; @@ -32,52 +33,67 @@ export async function* parseShapefileInBatches( const {shx, cpg, prj} = await loadShapefileSidecarFiles(options, context); // parse geometries - // @ts-ignore context must be defined - const shapeIterable: any = await context.parseInBatches(asyncIterator, SHPLoader, options); + const shapeIterable = await parseInBatchesFromContext( + asyncIterator, + SHPLoader, + options, + context! + ); + + const shapeIterator: AsyncIterator = + shapeIterable[Symbol.asyncIterator]?.() || shapeIterable[Symbol.iterator]?.(); // parse properties - let propertyIterable: any; - // @ts-ignore context must be defined - const dbfResponse = await context.fetch(replaceExtension(context?.url || '', 'dbf')); - if (dbfResponse.ok) { - // @ts-ignore context must be defined - propertyIterable = await context.parseInBatches(dbfResponse, DBFLoader, { - ...options, - dbf: {encoding: cpg || 'latin1'} - }); + let propertyIterator: AsyncIterator | null = null; + const dbfResponse = await context?.fetch(replaceExtension(context?.url || '', 'dbf')); + if (dbfResponse?.ok) { + const propertyIterable = await parseInBatchesFromContext( + dbfResponse, + DBFLoader, + { + ...options, + dbf: {encoding: cpg || 'latin1'} + }, + context! + ); + propertyIterator = + propertyIterable[Symbol.asyncIterator]?.() || propertyIterable[Symbol.iterator](); } // When `options.metadata` is `true`, there's an extra initial `metadata` // object before the iterator starts. zipBatchIterators expects to receive // batches of Array objects, and will fail with non-iterable batches, so it's // important to skip over the first batch. - let shapeHeader = (await shapeIterable.next()).value; + let shapeHeader = (await shapeIterator.next()).value; if (shapeHeader && shapeHeader.batchType === 'metadata') { - shapeHeader = (await shapeIterable.next()).value; + shapeHeader = (await shapeIterator.next()).value; } let dbfHeader: {batchType?: string} = {}; - if (propertyIterable) { - dbfHeader = (await propertyIterable.next()).value; + if (propertyIterator) { + dbfHeader = (await propertyIterator.next()).value; if (dbfHeader && dbfHeader.batchType === 'metadata') { - dbfHeader = (await propertyIterable.next()).value; + dbfHeader = (await propertyIterator.next()).value; } } - let iterator: any; - if (propertyIterable) { - iterator = zipBatchIterators(shapeIterable, propertyIterable); - } else { - iterator = shapeIterable; - } + const zippedIterator: AsyncIterator = propertyIterator + ? zipBatchIterators(shapeIterator, propertyIterator, 'object-row-table') + : shapeIterator; + + const zippedBatchIterable: AsyncIterable = { + [Symbol.asyncIterator]() { + return zippedIterator; + } + }; - for await (const item of iterator) { + for await (const batch of zippedBatchIterable) { let geometries: any; let properties: any; - if (!propertyIterable) { - geometries = item; + if (!propertyIterator) { + geometries = batch; } else { - [geometries, properties] = item; + [geometries, properties] = batch.data; } const geojsonGeometries = parseGeometries(geometries); @@ -113,19 +129,21 @@ export async function parseShapefile( const {shx, cpg, prj} = await loadShapefileSidecarFiles(options, context); // parse geometries - // @ts-ignore context must be defined - const {header, geometries} = await context.parse(arrayBuffer, SHPLoader, options); // {shp: shx} + const {header, geometries} = await parseFromContext(arrayBuffer, SHPLoader, options, context!); // {shp: shx} const geojsonGeometries = parseGeometries(geometries); // parse properties let properties = []; - // @ts-ignore context must be defined - const dbfResponse = await context.fetch(replaceExtension(context.url, 'dbf')); - if (dbfResponse.ok) { - // @ts-ignore context must be defined - properties = await context.parse(dbfResponse, DBFLoader, {dbf: {encoding: cpg || 'latin1'}}); + const dbfResponse = await context?.fetch(replaceExtension(context?.url!, 'dbf')); + if (dbfResponse?.ok) { + properties = await parseFromContext( + dbfResponse as any, + DBFLoader, + {dbf: {encoding: cpg || 'latin1'}}, + context! + ); } let features = joinProperties(geojsonGeometries, properties); @@ -148,7 +166,7 @@ export async function parseShapefile( * @param geometries * @returns geometries as an array */ -function parseGeometries(geometries: any[]): any[] { +function parseGeometries(geometries: BinaryGeometry[]): Geometry[] { const geojsonGeometries: any[] = []; for (const geom of geometries) { geojsonGeometries.push(binaryToGeometry(geom)); @@ -163,7 +181,7 @@ function parseGeometries(geometries: any[]): any[] { * @param properties [description] * @return [description] */ -function joinProperties(geometries: object[], properties: object[]): Feature[] { +function joinProperties(geometries: Geometry[], properties: object[]): Feature[] { const features: Feature[] = []; for (let i = 0; i < geometries.length; i++) { const geometry = geometries[i]; @@ -204,7 +222,7 @@ function reprojectFeatures(features: Feature[], sourceCrs?: string, targetCrs?: */ // eslint-disable-next-line max-statements export async function loadShapefileSidecarFiles( - options?: object, + options?: ShapefileLoaderOptions, context?: LoaderContext ): Promise<{ shx?: SHXOutput; diff --git a/modules/shapefile/src/lib/parsers/parse-shp.ts b/modules/shapefile/src/lib/parsers/parse-shp.ts index d62bbb544b..94b2ce3712 100644 --- a/modules/shapefile/src/lib/parsers/parse-shp.ts +++ b/modules/shapefile/src/lib/parsers/parse-shp.ts @@ -1,5 +1,5 @@ import type {BinaryGeometry} from '@loaders.gl/schema'; -import BinaryChunkReader from '../streaming/binary-chunk-reader'; +import {BinaryChunkReader} from '../streaming/binary-chunk-reader'; import {parseSHPHeader, SHPHeader} from './parse-shp-header'; import {parseRecord} from './parse-shp-geometry'; import {SHPLoaderOptions} from './types'; @@ -84,7 +84,7 @@ export function parseSHP(arrayBuffer: ArrayBuffer, options?: SHPLoaderOptions): export async function* parseSHPInBatches( asyncIterator: AsyncIterable | Iterable, options?: SHPLoaderOptions -): AsyncIterable { +): AsyncGenerator { const parser = new SHPParser(options); let headerReturned = false; for await (const arrayBuffer of asyncIterator) { diff --git a/modules/shapefile/src/lib/parsers/types.ts b/modules/shapefile/src/lib/parsers/types.ts index 5819c51892..d6c1cd161a 100644 --- a/modules/shapefile/src/lib/parsers/types.ts +++ b/modules/shapefile/src/lib/parsers/types.ts @@ -12,23 +12,16 @@ export type DBFLoaderOptions = LoaderOptions & { encoding?: string; shape?: 'rows' | 'table' | 'object-row-table'; }; - /** @deprecated */ - tables?: { - /** @deprecated */ - format?: 'rows' | 'table' | 'object-row-table'; - }; }; export type ShapefileLoaderOptions = LoaderOptions & SHPLoaderOptions & { shapefile?: { - shape?: 'geojson'; + shape?: 'geojson-table'; }; gis?: { reproject?: boolean; _targetCrs?: string; - /** @deprecated. Use options.shapefile.shape */ - format?: 'geojson'; }; }; diff --git a/modules/shapefile/src/lib/streaming/binary-chunk-reader.ts b/modules/shapefile/src/lib/streaming/binary-chunk-reader.ts index 96be0a3898..c0065e6d80 100644 --- a/modules/shapefile/src/lib/streaming/binary-chunk-reader.ts +++ b/modules/shapefile/src/lib/streaming/binary-chunk-reader.ts @@ -1,8 +1,11 @@ -type BinaryChunkReaderOptions = { +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +export type BinaryChunkReaderOptions = { maxRewindBytes: number; }; -export default class BinaryChunkReader { +export class BinaryChunkReader { offset: number; arrayBuffers: ArrayBuffer[]; ended: boolean; @@ -110,7 +113,6 @@ export default class BinaryChunkReader { } if (!bufferOffsets) { - // @ts-ignore return null; } diff --git a/modules/shapefile/src/lib/streaming/binary-reader.ts b/modules/shapefile/src/lib/streaming/binary-reader.ts index 6dbe192643..643ed849f9 100644 --- a/modules/shapefile/src/lib/streaming/binary-reader.ts +++ b/modules/shapefile/src/lib/streaming/binary-reader.ts @@ -1,4 +1,7 @@ -export default class BinaryReader { +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +export class BinaryReader { offset: number; arrayBuffer: ArrayBuffer; diff --git a/modules/shapefile/src/lib/streaming/zip-batch-iterators.ts b/modules/shapefile/src/lib/streaming/zip-batch-iterators.ts index 57ea12242e..568a56ad89 100644 --- a/modules/shapefile/src/lib/streaming/zip-batch-iterators.ts +++ b/modules/shapefile/src/lib/streaming/zip-batch-iterators.ts @@ -1,3 +1,10 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {ObjectRowTableBatch, ArrayRowTableBatch} from '@loaders.gl/schema'; + +type RowTableBatch = ObjectRowTableBatch | ArrayRowTableBatch; + /** * Zip two iterators together * @@ -5,36 +12,44 @@ * @param iterator2 */ export async function* zipBatchIterators( - iterator1: AsyncIterator, - iterator2: AsyncIterator -): AsyncGenerator { - let batch1: number[] = []; - let batch2: number[] = []; + iterator1: AsyncIterator | Iterator, + iterator2: AsyncIterator | Iterator, + shape: 'object-row-table' | 'array-row-table' +): AsyncGenerator { + const batch1Data: unknown[] = []; + const batch2Data: unknown[] = []; let iterator1Done: boolean = false; let iterator2Done: boolean = false; // TODO - one could let all iterators flow at full speed using `Promise.race` // however we might end up with a big temporary buffer while (!iterator1Done && !iterator2Done) { - if (batch1.length === 0 && !iterator1Done) { + if (batch1Data.length === 0 && !iterator1Done) { const {value, done} = await iterator1.next(); if (done) { iterator1Done = true; } else { - batch1 = value; + // @ts-expect-error + batch1Data.push(...value); } - } else if (batch2.length === 0 && !iterator2Done) { + } + if (batch2Data.length === 0 && !iterator2Done) { const {value, done} = await iterator2.next(); if (done) { iterator2Done = true; } else { - batch2 = value; + batch2Data.push(...value); } } - const batch = extractBatch(batch1, batch2); - if (batch) { - yield batch; + const batchData = extractBatchData(batch1Data, batch2Data); + if (batchData) { + yield { + batchType: 'data', + shape, + length: batchData.length, + data: batchData + }; } } } @@ -46,14 +61,14 @@ export async function* zipBatchIterators( * @param batch2 * @return array | null */ -function extractBatch(batch1: number[], batch2: number[]): number[][] | null { +function extractBatchData(batch1: any[], batch2: any[]): any[] | null { const batchLength: number = Math.min(batch1.length, batch2.length); if (batchLength === 0) { return null; } // Non interleaved arrays - const batch: number[][] = [batch1.slice(0, batchLength), batch2.slice(0, batchLength)]; + const batch: any[] = [batch1.slice(0, batchLength), batch2.slice(0, batchLength)]; // Modify the 2 batches batch1.splice(0, batchLength); diff --git a/modules/shapefile/src/shapefile-loader.ts b/modules/shapefile/src/shapefile-loader.ts index c49fd15e2f..24980e2d23 100644 --- a/modules/shapefile/src/shapefile-loader.ts +++ b/modules/shapefile/src/shapefile-loader.ts @@ -1,4 +1,4 @@ -import type {LoaderWithParser} from '@loaders.gl/loader-utils'; +import type {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; import {SHP_MAGIC_NUMBER} from './shp-loader'; import {parseShapefile, parseShapefileInBatches} from './lib/parsers/parse-shapefile'; @@ -10,7 +10,7 @@ const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; * Shapefile loader * @note Shapefile is multifile format and requires providing additional files */ -export const ShapefileLoader = { +export const ShapefileLoader: LoaderWithParser = { name: 'Shapefile', id: 'shapefile', module: 'shapefile', @@ -28,5 +28,3 @@ export const ShapefileLoader = { parse: parseShapefile, parseInBatches: parseShapefileInBatches }; - -export const _typecheckShapefileLoader: LoaderWithParser = ShapefileLoader; diff --git a/modules/shapefile/src/shp-loader.ts b/modules/shapefile/src/shp-loader.ts index 20d6e7977b..74a2978514 100644 --- a/modules/shapefile/src/shp-loader.ts +++ b/modules/shapefile/src/shp-loader.ts @@ -33,5 +33,8 @@ export const SHPLoader: LoaderWithParser = { ...SHPWorkerLoader, parse: async (arrayBuffer, options?) => parseSHP(arrayBuffer, options), parseSync: parseSHP, - parseInBatches: parseSHPInBatches + parseInBatches: ( + arrayBufferIterator: AsyncIterable | Iterable, + options + ) => parseSHPInBatches(arrayBufferIterator, options) }; diff --git a/modules/shapefile/test/index.js b/modules/shapefile/test/index.ts similarity index 55% rename from modules/shapefile/test/index.js rename to modules/shapefile/test/index.ts index 61deb1f429..ad90fe526c 100644 --- a/modules/shapefile/test/index.js +++ b/modules/shapefile/test/index.ts @@ -1,4 +1,8 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import './streaming/binary-chunk-reader.spec'; +import './streaming/zip-batch-iterators.spec'; import './shp-loader.spec'; import './dbf-loader.spec'; diff --git a/modules/shapefile/test/shapefile-loader.spec.js b/modules/shapefile/test/shapefile-loader.spec.js index 5b6cf7eb81..0cc2cb0566 100644 --- a/modules/shapefile/test/shapefile-loader.spec.js +++ b/modules/shapefile/test/shapefile-loader.spec.js @@ -56,7 +56,7 @@ test('ShapefileLoader#load (from browser File objects)', async (t) => { const filename = `${testFileName}.shp`; // @ts-ignore const data = await load(filename, ShapefileLoader, {fetch}); - t.comment(`${filename}: ${JSON.stringify(data).slice(0, 70)}`); + // t.comment(`${filename}: ${JSON.stringify(data).slice(0, 70)}`); testShapefileData(t, testFileName, data); } @@ -69,7 +69,7 @@ test('ShapefileLoader#load (from files or URLs)', async (t) => { for (const testFileName in SHAPEFILE_JS_TEST_FILES) { const filename = `${SHAPEFILE_JS_DATA_FOLDER}/${testFileName}.shp`; const data = await load(filename, ShapefileLoader); - t.comment(`${filename}: ${JSON.stringify(data).slice(0, 70)}`); + // t.comment(`${filename}: ${JSON.stringify(data).slice(0, 70)}`); await testShapefileData(t, testFileName, data); } @@ -84,7 +84,7 @@ test('ShapefileLoader#load and reproject (from files or URLs)', async (t) => { const data = await load(filename, ShapefileLoader, { gis: {reproject: true, _targetCrs: 'EPSG:3857'} }); - t.comment(`${filename}: ${JSON.stringify(data).slice(0, 70)}`); + // t.comment(`${filename}: ${JSON.stringify(data).slice(0, 70)}`); // Compare with parsed json // This is a special case with reprojected coordinates; otherwise use the diff --git a/modules/shapefile/test/streaming/binary-chunk-reader.spec.js b/modules/shapefile/test/streaming/binary-chunk-reader.spec.ts similarity index 88% rename from modules/shapefile/test/streaming/binary-chunk-reader.spec.js rename to modules/shapefile/test/streaming/binary-chunk-reader.spec.ts index 46a89ddeff..29114a6a7e 100644 --- a/modules/shapefile/test/streaming/binary-chunk-reader.spec.js +++ b/modules/shapefile/test/streaming/binary-chunk-reader.spec.ts @@ -1,6 +1,5 @@ -// @ts-nocheck import test from 'tape-promise/tape'; -import BinaryChunkReader from '@loaders.gl/shapefile/lib/streaming/binary-chunk-reader'; +import {_BinaryChunkReader as BinaryChunkReader} from '@loaders.gl/shapefile'; const buf1 = new Uint8Array([1, 2, 3]).buffer; const buf2 = new Uint8Array([4, 5, 6]).buffer; @@ -82,13 +81,13 @@ test('BinaryChunkReader#getDataView single source array', (t) => { reader.write(buf3); let view = reader.getDataView(2); - t.equals(view.getUint8(0), 1); - t.equals(view.getUint8(1), 2); + t.equals(view?.getUint8(0), 1); + t.equals(view?.getUint8(1), 2); reader.skip(2); view = reader.getDataView(2); - t.equals(view.getUint8(0), 5); - t.equals(view.getUint8(1), 6); + t.equals(view?.getUint8(0), 5); + t.equals(view?.getUint8(1), 6); t.end(); }); @@ -100,14 +99,14 @@ test('BinaryChunkReader#getDataView multiple source arrays', (t) => { reader.skip(2); let view = reader.getDataView(2); - t.equals(view.getUint8(0), 3); - t.equals(view.getUint8(1), 4); + t.equals(view?.getUint8(0), 3); + t.equals(view?.getUint8(1), 4); view = reader.getDataView(4); - t.equals(view.getUint8(0), 5); - t.equals(view.getUint8(1), 6); - t.equals(view.getUint8(2), 7); - t.equals(view.getUint8(3), 8); + t.equals(view?.getUint8(0), 5); + t.equals(view?.getUint8(1), 6); + t.equals(view?.getUint8(2), 7); + t.equals(view?.getUint8(3), 8); t.end(); }); diff --git a/modules/shapefile/test/streaming/zip-batch-iterators.spec.ts b/modules/shapefile/test/streaming/zip-batch-iterators.spec.ts new file mode 100644 index 0000000000..9834448329 --- /dev/null +++ b/modules/shapefile/test/streaming/zip-batch-iterators.spec.ts @@ -0,0 +1,36 @@ +import test from 'tape-promise/tape'; +import type {ObjectRowTableBatch, ArrayRowTableBatch} from '@loaders.gl/schema'; +import {_zipBatchIterators as zipBatchIterators} from '@loaders.gl/shapefile'; + +type RowTableBatch = ObjectRowTableBatch | ArrayRowTableBatch; + +type TestCase = { + title: string; + iterator1: Iterator; + iterator2: Iterator; + shape: 'object-row-table' | 'array-row-table'; + result: RowTableBatch[]; +}; + +const TEST_CASES: TestCase[] = [ + { + title: 'empty iterators', + iterator1: [][Symbol.iterator](), + iterator2: [][Symbol.iterator](), + shape: 'object-row-table', + result: [] + } + // TODO - add some non-trivial cases +]; + +test('zipBatchIterators', async (t) => { + for (const tc of TEST_CASES) { + const zippedIterator = zipBatchIterators(tc.iterator1, tc.iterator2, tc.shape); + const batches: RowTableBatch[] = []; + for await (const batch of zippedIterator) { + batches.push(batch); + } + t.deepEquals(batches, tc.result, tc.title); + } + t.end(); +}); diff --git a/modules/terrain/package.json b/modules/terrain/package.json index 327979403d..6ba41810dc 100644 --- a/modules/terrain/package.json +++ b/modules/terrain/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/terrain", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loader for terrain raster formats", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "OBJ" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -29,16 +37,16 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-worker && npm run build-worker2 && npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-worker && npm run build-worker2 && npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/terrain-worker.ts --bundle --outfile=dist/terrain-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"", "build-worker2": "esbuild src/workers/quantized-mesh-worker.ts --bundle --outfile=dist/quantized-mesh-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { "@babel/runtime": "^7.3.1", - "@loaders.gl/images": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", + "@loaders.gl/images": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", "@mapbox/martini": "^0.2.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" diff --git a/modules/terrain/src/bundle.ts b/modules/terrain/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/terrain/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/terrain/src/index.ts b/modules/terrain/src/index.ts index 84daad1bec..b1d2cef76e 100644 --- a/modules/terrain/src/index.ts +++ b/modules/terrain/src/index.ts @@ -1,4 +1,5 @@ import type {LoaderContext, LoaderWithParser} from '@loaders.gl/loader-utils'; +import {parseFromContext} from '@loaders.gl/loader-utils'; import {parseQuantizedMesh} from './lib/parse-quantized-mesh'; import {TerrainOptions, makeTerrainMeshFromImage} from './lib/parse-terrain'; @@ -27,9 +28,10 @@ export async function parseTerrain( mimeType: 'application/x.image', image: {...options?.image, type: 'data'} }; - const image = await context?.parse(arrayBuffer, loadImageOptions); + const image = await parseFromContext(arrayBuffer, [], loadImageOptions, context!); // Extend function to support additional mesh generation options (square grid or delatin) const terrainOptions = {...TerrainLoader.options.terrain, ...options?.terrain} as TerrainOptions; + // @ts-expect-error sort out image typing asap return makeTerrainMeshFromImage(image, terrainOptions); } diff --git a/modules/terrain/src/lib/parse-terrain.ts b/modules/terrain/src/lib/parse-terrain.ts index 2818d2efc8..ea44ac4e05 100644 --- a/modules/terrain/src/lib/parse-terrain.ts +++ b/modules/terrain/src/lib/parse-terrain.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {getMeshBoundingBox} from '@loaders.gl/schema'; import Martini from '@mapbox/martini'; diff --git a/modules/terrain/test/terrain-loader.spec.js b/modules/terrain/test/terrain-loader.spec.js index 2a7e4fcf08..1b64b9109b 100644 --- a/modules/terrain/test/terrain-loader.spec.js +++ b/modules/terrain/test/terrain-loader.spec.js @@ -34,7 +34,8 @@ test('TerrainLoader#parse mapbox martini', async (t) => { meshMaxError: 5.0, bounds: [83, 329.5, 83.125, 329.625], // note: not the real tile bounds tesselator: 'martini' - } + }, + worker: false }); validateMeshCategoryData(t, data); // TODO: should there be a validateMeshCategoryData? diff --git a/modules/textures/package.json b/modules/textures/package.json index 56d1d6d596..197d3d5f05 100644 --- a/modules/textures/package.json +++ b/modules/textures/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/textures", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loaders for compressed and super compressed (basis) textures ", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -23,8 +24,15 @@ "basis" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -33,9 +41,9 @@ "README.md" ], "scripts": { - "pre-build": "npm run copy-libs && npm run build-bundle && npm run build-workers", + "pre-build": "npm run copy-libs && npm run build-bundle && npm run build-bundle -- --env=dev && npm run build-workers", "copy-libs": "cp -rf ./src/libs ./dist/libs", - "build-bundle": "esbuild src/bundle.ts --outfile=dist/dist.min.js --bundle", + "build-bundle": "ocular-bundle ./src/index.ts", "build-workers": "npm run build-basis-worker && npm run build-basis-worker-node && npm run build-npy-worker && npm run build-compressed-texture-worker && npm run build-crunch-worker && npm run build-ktx2-basis-writer-worker && npm run build-ktx2-basis-writer-worker-node", "build-basis-worker": "esbuild src/workers/basis-worker.ts --outfile=dist/basis-worker.js --target=esnext --bundle --define:__VERSION__=\\\"$npm_package_version\\\"", "build-basis-worker-node": "esbuild src/workers/basis-worker-node.ts --outfile=dist/basis-worker-node.js --target=node16 --platform=node --bundle --define:__VERSION__=\\\"$npm_package_version\\\"", @@ -46,15 +54,15 @@ "build-crunch-worker": "esbuild src/workers/crunch-worker.ts --outfile=dist/crunch-worker.js --target=esnext --bundle --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { - "@loaders.gl/images": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", - "@loaders.gl/worker-utils": "4.0.0-alpha.13", + "@loaders.gl/images": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "@loaders.gl/worker-utils": "4.0.3", "ktx-parse": "^0.0.4", "texture-compressor": "^1.0.2" }, "devDependencies": { - "@loaders.gl/polyfills": "4.0.0-alpha.13" + "@loaders.gl/polyfills": "4.0.3" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/textures/src/basis-loader.ts b/modules/textures/src/basis-loader.ts index ddf658fee8..1762087e82 100644 --- a/modules/textures/src/basis-loader.ts +++ b/modules/textures/src/basis-loader.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Loader, LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils'; import type {TextureLevel} from '@loaders.gl/schema'; import {VERSION} from './lib/utils/version'; diff --git a/modules/textures/src/bundle.ts b/modules/textures/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/textures/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/textures/src/compressed-texture-loader.ts b/modules/textures/src/compressed-texture-loader.ts index 978bba4b8a..9189e51b9a 100644 --- a/modules/textures/src/compressed-texture-loader.ts +++ b/modules/textures/src/compressed-texture-loader.ts @@ -10,17 +10,10 @@ export type TextureLoaderOptions = { }; }; -const DEFAULT_TEXTURE_LOADER_OPTIONS = { - 'compressed-texture': { - libraryPath: 'libs/', - useBasis: false - } -}; - /** * Worker Loader for KTX, DDS, and PVR texture container formats */ -export const CompressedTextureWorkerLoader = { +export const CompressedTextureWorkerLoader: Loader = { name: 'Texture Containers', id: 'compressed-texture', module: 'textures', @@ -40,31 +33,35 @@ export const CompressedTextureWorkerLoader = { 'application/octet-stream' ], binary: true, - options: DEFAULT_TEXTURE_LOADER_OPTIONS + options: { + 'compressed-texture': { + libraryPath: 'libs/', + useBasis: false + } + } }; /** * Loader for KTX, DDS, and PVR texture container formats */ -export const CompressedTextureLoader = { +export const CompressedTextureLoader: LoaderWithParser = { ...CompressedTextureWorkerLoader, - parse: async (arrayBuffer, options) => { - if (options['compressed-texture'].useBasis) { + parse: async (arrayBuffer: ArrayBuffer, options?: TextureLoaderOptions) => { + if (options?.['compressed-texture']?.useBasis) { + // @ts-expect-error TODO not allowed to modify inputs options.basis = { format: { alpha: 'BC3', noAlpha: 'BC1' }, + // @ts-expect-error TODO not allowed to modify inputs ...options.basis, containerFormat: 'ktx2', module: 'encoder' }; - return (await parseBasis(arrayBuffer, options))[0]; + const result = await parseBasis(arrayBuffer, options); + return result[0]; } return parseCompressedTexture(arrayBuffer); } }; - -// TYPE TESTS - TODO find a better way than exporting junk -export const _TypecheckCompressedTextureWorkerLoader: Loader = CompressedTextureWorkerLoader; -export const _TypecheckCompressedTextureLoader: LoaderWithParser = CompressedTextureLoader; diff --git a/modules/textures/src/compressed-texture-writer.ts b/modules/textures/src/compressed-texture-writer.ts index 96d56b5386..0fe37a7f80 100644 --- a/modules/textures/src/compressed-texture-writer.ts +++ b/modules/textures/src/compressed-texture-writer.ts @@ -1,4 +1,4 @@ -import type {Writer, WriterOptions} from '@loaders.gl/loader-utils'; +import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import {VERSION} from './lib/utils/version'; import {encodeImageURLToCompressedTextureURL} from './lib/encoders/encode-texture'; @@ -17,7 +17,11 @@ export type CompressedTextureWriterOptions = WriterOptions & { /** * DDS Texture Container Exporter */ -export const CompressedTextureWriter: Writer = { +export const CompressedTextureWriter: WriterWithEncoder< + unknown, + unknown, + CompressedTextureWriterOptions +> = { name: 'DDS Texture Container', id: 'dds', module: 'textures', @@ -36,7 +40,10 @@ export const CompressedTextureWriter: Writer = { id: 'crunch', name: 'Crunch', module: 'textures', @@ -19,8 +27,3 @@ export const CrunchLoader = { } } }; - -// We avoid bundling crunch - rare format, only offer worker loader - -// TYPE TESTS - TODO find a better way than exporting junk -export const _TypecheckCrunchLoader: Loader = CrunchLoader; diff --git a/modules/textures/src/index.ts b/modules/textures/src/index.ts index aff60ffca5..15f0e7b954 100644 --- a/modules/textures/src/index.ts +++ b/modules/textures/src/index.ts @@ -10,6 +10,10 @@ export {CompressedTextureLoader, CompressedTextureWorkerLoader} from './compress export {CrunchLoader} from './crunch-loader'; export {NPYLoader, NPYWorkerLoader} from './npy-loader'; +// Module constants +export {BASIS_EXTERNAL_LIBRARIES} from './lib/parsers/basis-module-loader'; +export {CRUNCH_EXTERNAL_LIBRARIES} from './lib/parsers/crunch-module-loader'; + // Writers export {CompressedTextureWriter} from './compressed-texture-writer'; export {KTX2BasisWriter} from './ktx2-basis-writer'; diff --git a/modules/textures/src/ktx2-basis-writer.ts b/modules/textures/src/ktx2-basis-writer.ts index 2201c77a87..4c3b730d08 100644 --- a/modules/textures/src/ktx2-basis-writer.ts +++ b/modules/textures/src/ktx2-basis-writer.ts @@ -1,23 +1,26 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors -import type {Writer} from '@loaders.gl/loader-utils'; +import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import {VERSION} from './lib/utils/version'; import type {ImageDataType} from '@loaders.gl/images'; import {encodeKTX2BasisTexture} from './lib/encoders/encode-ktx2-basis-texture'; /** @todo should be in basis sub-object */ -export type KTX2BasisWriterOptions = { - useSRGB?: boolean; - qualityLevel?: number; - encodeUASTC?: boolean; - mipmaps?: boolean; +export type KTX2BasisWriterOptions = WriterOptions & { + ['ktx2-basis-writer']?: { + useSRGB?: boolean; + qualityLevel?: number; + encodeUASTC?: boolean; + mipmaps?: boolean; + }; }; /** * Basis Universal Supercompressed GPU Texture. * Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.8/textureSetDefinitionFormat.cmn.md */ -export const KTX2BasisWriter: Writer = { +export const KTX2BasisWriter: WriterWithEncoder = { name: 'Basis Universal Supercompressed GPU Texture', id: 'ktx2-basis-writer', module: 'textures', @@ -25,14 +28,13 @@ export const KTX2BasisWriter: Writer { // TODO remove default values after writer options will be normalized like it done in load module. - const {useSRGB = false, qualityLevel = 10, encodeUASTC = false, mipmaps = false} = options; + const { + useSRGB = false, + qualityLevel = 10, + encodeUASTC = false, + mipmaps = false + } = options?.['ktx2-basis-writer'] || {}; const {BasisEncoder} = await loadBasisEncoderModule(options); const basisEncoder = new BasisEncoder(); diff --git a/modules/textures/src/lib/parsers/basis-module-loader.ts b/modules/textures/src/lib/parsers/basis-module-loader.ts index 2b93caa97c..a8b4bc028c 100644 --- a/modules/textures/src/lib/parsers/basis-module-loader.ts +++ b/modules/textures/src/lib/parsers/basis-module-loader.ts @@ -1,12 +1,15 @@ -// __VERSION__ is injected by babel-plugin-version-inline -// @ts-ignore TS2304: Cannot find name '__VERSION__'. -const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'beta'; - -// @ts-nocheck import {loadLibrary} from '@loaders.gl/worker-utils'; -const BASIS_CDN_ENCODER_WASM = `https://unpkg.com/@loaders.gl/textures@${VERSION}/dist/libs/basis_encoder.wasm`; -const BASIS_CDN_ENCODER_JS = `https://unpkg.com/@loaders.gl/textures@${VERSION}/dist/libs/basis_encoder.js`; +export const BASIS_EXTERNAL_LIBRARIES = { + /** Basis transcoder, javascript wrapper part */ + TRANSCODER: 'basis_transcoder.js', + /** Basis transcoder, compiled web assembly part */ + TRANSCODER_WASM: 'basis_transcoder.wasm', + /** Basis encoder, javascript wrapper part */ + ENCODER: 'basis_encoder.js', + /** Basis encoder, compiled web assembly part */ + ENCODER_WASM: 'basis_encoder.wasm' +}; let loadBasisTranscoderPromise; @@ -15,13 +18,13 @@ let loadBasisTranscoderPromise; * @param options * @returns {BasisFile} promise */ -export async function loadBasisTrascoderModule(options) { +export async function loadBasisTranscoderModule(options) { const modules = options.modules || {}; if (modules.basis) { return modules.basis; } - loadBasisTranscoderPromise = loadBasisTranscoderPromise || loadBasisTrascoder(options); + loadBasisTranscoderPromise = loadBasisTranscoderPromise || loadBasisTranscoder(options); return await loadBasisTranscoderPromise; } @@ -30,19 +33,19 @@ export async function loadBasisTrascoderModule(options) { * @param options * @returns {BasisFile} promise */ -async function loadBasisTrascoder(options) { +async function loadBasisTranscoder(options) { let BASIS = null; let wasmBinary = null; [BASIS, wasmBinary] = await Promise.all([ - await loadLibrary('basis_transcoder.js', 'textures', options), - await loadLibrary('basis_transcoder.wasm', 'textures', options) + await loadLibrary(BASIS_EXTERNAL_LIBRARIES.TRANSCODER, 'textures', options), + await loadLibrary(BASIS_EXTERNAL_LIBRARIES.TRANSCODER_WASM, 'textures', options) ]); // Depends on how import happened... // @ts-ignore TS2339: Property does not exist on type BASIS = BASIS || globalThis.BASIS; - return await initializeBasisTrascoderModule(BASIS, wasmBinary); + return await initializeBasisTranscoderModule(BASIS, wasmBinary); } /** @@ -51,7 +54,7 @@ async function loadBasisTrascoder(options) { * @param wasmBinary - wasm part of the module * @returns {BasisFile} promise */ -function initializeBasisTrascoderModule(BasisModule, wasmBinary) { +function initializeBasisTranscoderModule(BasisModule, wasmBinary) { const options: {wasmBinary?} = {}; if (wasmBinary) { @@ -95,8 +98,8 @@ async function loadBasisEncoder(options) { let wasmBinary = null; [BASIS_ENCODER, wasmBinary] = await Promise.all([ - await loadLibrary(BASIS_CDN_ENCODER_JS, 'textures', options), - await loadLibrary(BASIS_CDN_ENCODER_WASM, 'textures', options) + await loadLibrary(BASIS_EXTERNAL_LIBRARIES.ENCODER, 'textures', options), + await loadLibrary(BASIS_EXTERNAL_LIBRARIES.ENCODER_WASM, 'textures', options) ]); // Depends on how import happened... diff --git a/modules/textures/src/lib/parsers/crunch-module-loader.ts b/modules/textures/src/lib/parsers/crunch-module-loader.ts index 7b2a1e53a6..ef383a3c72 100644 --- a/modules/textures/src/lib/parsers/crunch-module-loader.ts +++ b/modules/textures/src/lib/parsers/crunch-module-loader.ts @@ -1,6 +1,11 @@ // @ts-nocheck import {loadLibrary} from '@loaders.gl/worker-utils'; +export const CRUNCH_EXTERNAL_LIBRARIES = { + /** Crunch decoder library. It is used as dynamically imported script */ + DECODER: 'crunch.js' +}; + /** * Load crunch decoder module * @param options - loader options @@ -27,7 +32,7 @@ async function loadCrunch(options) { return crunchModule; } - let loadCrunchDecoder = await loadLibrary('crunch.js', 'textures', options); + let loadCrunchDecoder = await loadLibrary(CRUNCH_EXTERNAL_LIBRARIES.DECODER, 'textures', options); // Depends on how import happened... // @ts-ignore TS2339: Property does not exist on type diff --git a/modules/textures/src/lib/parsers/parse-basis.ts b/modules/textures/src/lib/parsers/parse-basis.ts index cb2bce8cad..4bc8f3ff64 100644 --- a/modules/textures/src/lib/parsers/parse-basis.ts +++ b/modules/textures/src/lib/parsers/parse-basis.ts @@ -1,6 +1,6 @@ /* eslint-disable indent */ import type {TextureLevel} from '@loaders.gl/schema'; -import {loadBasisEncoderModule, loadBasisTrascoderModule} from './basis-module-loader'; +import {loadBasisEncoderModule, loadBasisTranscoderModule} from './basis-module-loader'; import {GL_EXTENSIONS_CONSTANTS} from '../gl-extensions'; import {getSupportedGPUTextureFormats} from '../utils/texture-formats'; import {isKTX} from './parse-ktx'; @@ -86,7 +86,7 @@ export default async function parseBasis(data: ArrayBuffer, options): Promise; @@ -9,7 +10,6 @@ export async function deepLoad(urlTree: unknown, load: Load, options: LoadOption } export async function shallowLoad(url: string, load: Load, options: LoadOptions): Promise { - // console.error('loading', url); const response = await fetch(url, options.fetch); const arrayBuffer = await response.arrayBuffer(); return await load(arrayBuffer, options); diff --git a/modules/textures/src/lib/texture-api/generate-url.ts b/modules/textures/src/lib/texture-api/generate-url.ts index 473cb879f2..cfb1b88ac7 100644 --- a/modules/textures/src/lib/texture-api/generate-url.ts +++ b/modules/textures/src/lib/texture-api/generate-url.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {resolvePath} from '@loaders.gl/loader-utils'; import type {GetUrl, UrlOptions} from './texture-api-types'; diff --git a/modules/textures/src/lib/texture-api/load-image-array.ts b/modules/textures/src/lib/texture-api/load-image-array.ts index 7d668a94d4..e2e681d33b 100644 --- a/modules/textures/src/lib/texture-api/load-image-array.ts +++ b/modules/textures/src/lib/texture-api/load-image-array.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {ImageLoader} from '@loaders.gl/images'; import type {GetUrl} from './texture-api-types'; import {getImageUrls} from './load-image'; diff --git a/modules/textures/src/lib/texture-api/load-image-cube.ts b/modules/textures/src/lib/texture-api/load-image-cube.ts index 684a840a9a..7d71e57154 100644 --- a/modules/textures/src/lib/texture-api/load-image-cube.ts +++ b/modules/textures/src/lib/texture-api/load-image-cube.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {ImageLoader} from '@loaders.gl/images'; import type {GetUrl, UrlOptions} from './texture-api-types'; import {getImageUrls} from './load-image'; diff --git a/modules/textures/src/lib/texture-api/texture-api-types.ts b/modules/textures/src/lib/texture-api/texture-api-types.ts index a99308f5c9..3dbd03337f 100644 --- a/modules/textures/src/lib/texture-api/texture-api-types.ts +++ b/modules/textures/src/lib/texture-api/texture-api-types.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors export type {ImageType} from '@loaders.gl/images'; diff --git a/modules/textures/src/lib/utils/version.ts b/modules/textures/src/lib/utils/version.ts index 2a73213b8d..73152aef19 100644 --- a/modules/textures/src/lib/utils/version.ts +++ b/modules/textures/src/lib/utils/version.ts @@ -1,5 +1,4 @@ // Version constant cannot be imported, it needs to correspond to the build version of **this** module. // __VERSION__ is injected by babel-plugin-version-inline -// TODO: use 'latest' instead of 'beta' when 3.0.0 version is released as 'latest' // @ts-ignore TS2304: Cannot find name '__VERSION__'. -export const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'beta'; +export const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; diff --git a/modules/textures/src/workers/compressed-texture-worker.ts b/modules/textures/src/workers/compressed-texture-worker.ts index 67a932e68c..a4830f5385 100644 --- a/modules/textures/src/workers/compressed-texture-worker.ts +++ b/modules/textures/src/workers/compressed-texture-worker.ts @@ -1,4 +1,5 @@ import {createLoaderWorker} from '@loaders.gl/loader-utils'; import {CompressedTextureLoader} from '../compressed-texture-loader'; +// @ts-expect-error createLoaderWorker(CompressedTextureLoader); diff --git a/modules/textures/src/workers/ktx2-basis-writer-worker-node.ts b/modules/textures/src/workers/ktx2-basis-writer-worker-node.ts index cabfcf8f26..783da122c4 100644 --- a/modules/textures/src/workers/ktx2-basis-writer-worker-node.ts +++ b/modules/textures/src/workers/ktx2-basis-writer-worker-node.ts @@ -1,11 +1,11 @@ // Polyfills increases the bundle size significantly. Use it for NodeJS worker only import '@loaders.gl/polyfills'; import {WorkerBody, WorkerMessagePayload} from '@loaders.gl/worker-utils'; -import {KTX2BasisWriter} from '../ktx2-basis-writer'; +import {KTX2BasisWriter, KTX2BasisWriterOptions} from '../ktx2-basis-writer'; -(() => { +(async () => { // Check that we are actually in a worker thread - if (!WorkerBody.inWorkerThread()) { + if (!(await WorkerBody.inWorkerThread())) { return; } @@ -14,7 +14,7 @@ import {KTX2BasisWriter} from '../ktx2-basis-writer'; case 'process': try { const {input, options} = payload; - const result = await KTX2BasisWriter.encode?.(input, options); + const result = await KTX2BasisWriter.encode?.(input, options as KTX2BasisWriterOptions); WorkerBody.postMessage('done', {result}); } catch (error) { const message = error instanceof Error ? error.message : ''; diff --git a/modules/textures/src/workers/ktx2-basis-writer-worker.ts b/modules/textures/src/workers/ktx2-basis-writer-worker.ts index 3376fe6dfa..c219f5d873 100644 --- a/modules/textures/src/workers/ktx2-basis-writer-worker.ts +++ b/modules/textures/src/workers/ktx2-basis-writer-worker.ts @@ -1,9 +1,9 @@ import {WorkerBody, WorkerMessagePayload} from '@loaders.gl/worker-utils'; -import {KTX2BasisWriter} from '../ktx2-basis-writer'; +import {KTX2BasisWriter, KTX2BasisWriterOptions} from '../ktx2-basis-writer'; -(() => { +(async () => { // Check that we are actually in a worker thread - if (!WorkerBody.inWorkerThread()) { + if (!(await WorkerBody.inWorkerThread())) { return; } @@ -12,7 +12,7 @@ import {KTX2BasisWriter} from '../ktx2-basis-writer'; case 'process': try { const {input, options} = payload; - const result = await KTX2BasisWriter.encode?.(input, options); + const result = await KTX2BasisWriter.encode?.(input, options as KTX2BasisWriterOptions); WorkerBody.postMessage('done', {result}); } catch (error) { const message = error instanceof Error ? error.message : ''; diff --git a/modules/textures/test/basis-loader.spec.js b/modules/textures/test/basis-loader.spec.ts similarity index 98% rename from modules/textures/test/basis-loader.spec.js rename to modules/textures/test/basis-loader.spec.ts index 76fb335db6..2fc89f2cb0 100644 --- a/modules/textures/test/basis-loader.spec.js +++ b/modules/textures/test/basis-loader.spec.ts @@ -78,7 +78,7 @@ test('BasisLoader#auto-select a target format', async (t) => { t.ok(image.compressed, 'Basis transcodes to compressed texture'); } else { t.notOk(image.format, 'Basis transcodes to RGB565 in NodeJS'); - t.notOk(image.compressed, "Basis can't transcode to compressed texture in NodeJS"); + t.notOk(image.compressed, 'Basis can\'t transcode to compressed texture in NodeJS'); } t.end(); diff --git a/modules/textures/test/compressed-texture-loader.spec.js b/modules/textures/test/compressed-texture-loader.spec.ts similarity index 100% rename from modules/textures/test/compressed-texture-loader.spec.js rename to modules/textures/test/compressed-texture-loader.spec.ts diff --git a/modules/textures/test/compressed-texture-writer.spec.js b/modules/textures/test/compressed-texture-writer.spec.ts similarity index 100% rename from modules/textures/test/compressed-texture-writer.spec.js rename to modules/textures/test/compressed-texture-writer.spec.ts diff --git a/modules/textures/test/crunch-loader.spec.js b/modules/textures/test/crunch-loader.spec.ts similarity index 100% rename from modules/textures/test/crunch-loader.spec.js rename to modules/textures/test/crunch-loader.spec.ts diff --git a/modules/textures/test/index.js b/modules/textures/test/index.ts similarity index 100% rename from modules/textures/test/index.js rename to modules/textures/test/index.ts index 0f2ecd1bd9..184497b399 100644 --- a/modules/textures/test/index.js +++ b/modules/textures/test/index.ts @@ -5,9 +5,9 @@ import './texture-api/load-image.spec'; import './basis-loader.spec'; import './crunch-loader.spec'; +import './ktx2-basis-universal-texture-writer.spec'; import './compressed-texture-loader.spec'; import './compressed-texture-writer.spec'; -import './ktx2-basis-universal-texture-writer.spec'; import './npy-loader.spec'; diff --git a/modules/textures/test/ktx2-basis-universal-texture-writer.spec.js b/modules/textures/test/ktx2-basis-universal-texture-writer.spec.ts similarity index 100% rename from modules/textures/test/ktx2-basis-universal-texture-writer.spec.js rename to modules/textures/test/ktx2-basis-universal-texture-writer.spec.ts diff --git a/modules/textures/test/lib/parsers/parse-dds.spec.js b/modules/textures/test/lib/parsers/parse-dds.spec.ts similarity index 100% rename from modules/textures/test/lib/parsers/parse-dds.spec.js rename to modules/textures/test/lib/parsers/parse-dds.spec.ts diff --git a/modules/textures/test/lib/parsers/parse-pvr.spec.js b/modules/textures/test/lib/parsers/parse-pvr.spec.ts similarity index 100% rename from modules/textures/test/lib/parsers/parse-pvr.spec.js rename to modules/textures/test/lib/parsers/parse-pvr.spec.ts diff --git a/modules/textures/test/npy-loader.spec.js b/modules/textures/test/npy-loader.spec.ts similarity index 100% rename from modules/textures/test/npy-loader.spec.js rename to modules/textures/test/npy-loader.spec.ts diff --git a/modules/textures/test/test-utils/check-compressed-texture.js b/modules/textures/test/test-utils/check-compressed-texture.ts similarity index 100% rename from modules/textures/test/test-utils/check-compressed-texture.js rename to modules/textures/test/test-utils/check-compressed-texture.ts diff --git a/modules/textures/test/texture-api/async-deep-map.spec.js b/modules/textures/test/texture-api/async-deep-map.spec.ts similarity index 100% rename from modules/textures/test/texture-api/async-deep-map.spec.js rename to modules/textures/test/texture-api/async-deep-map.spec.ts diff --git a/modules/textures/test/texture-api/load-image.spec.js b/modules/textures/test/texture-api/load-image.spec.ts similarity index 77% rename from modules/textures/test/texture-api/load-image.spec.js rename to modules/textures/test/texture-api/load-image.spec.ts index 132fbe2644..4842ae98aa 100644 --- a/modules/textures/test/texture-api/load-image.spec.js +++ b/modules/textures/test/texture-api/load-image.spec.ts @@ -5,13 +5,13 @@ import {isImage} from '@loaders.gl/images'; const LUT_URL = '@loaders.gl/images/test/data/ibl/brdfLUT.png'; const PAPERMILL_URL = '@loaders.gl/images/test/data/ibl/papermill'; -test('loadImageTexture#mipLevels=0', async (t) => { +test.skip('loadImageTexture#mipLevels=0', async (t) => { const image = await loadImageTexture(LUT_URL); t.ok(isImage(image)); t.end(); }); -test('loadImageTexture#mipLevels=auto', async (t) => { +test.skip('loadImageTexture#mipLevels=auto', async (t) => { const mipmappedImage = await loadImageTexture(({lod}) => `specular/specular_back_${lod}.jpg`, { baseUrl: PAPERMILL_URL, image: { @@ -22,7 +22,7 @@ test('loadImageTexture#mipLevels=auto', async (t) => { t.end(); }); -test('loadImageTextureArray#mipLevels=0', async (t) => { +test.skip('loadImageTextureArray#mipLevels=0', async (t) => { const images = await loadImageTextureArray( 10, ({index}) => `specular/specular_back_${index}.jpg`, @@ -35,7 +35,7 @@ test('loadImageTextureArray#mipLevels=0', async (t) => { t.end(); }); -test('loadImageTextureArray#mipLevels=auto', async (t) => { +test.skip('loadImageTextureArray#mipLevels=auto', async (t) => { const images = await loadImageTextureArray( 1, ({index, lod}) => `specular/specular_back_${lod}.jpg`, @@ -48,13 +48,13 @@ test('loadImageTextureArray#mipLevels=auto', async (t) => { ); t.equal(images.length, 1, 'loadArray loaded 1 image'); images.every((imageMips) => { - t.equal(imageMips.length, 10, `array of mip images has correct length`); - t.ok(imageMips.every(isImage), `entry is a valid array of mip images`); + t.equal(imageMips.length, 10, 'array of mip images has correct length'); + t.ok(imageMips.every(isImage), 'entry is a valid array of mip images'); }); t.end(); }); -test('loadImageTextureCube#mipLevels=0', async (t) => { +test.skip('loadImageTextureCube#mipLevels=0', async (t) => { const imageCube = await loadImageTextureCube( ({direction}) => `diffuse/diffuse_${direction}_0.jpg`, { @@ -69,7 +69,7 @@ test('loadImageTextureCube#mipLevels=0', async (t) => { t.end(); }); -test('loadImageTextureCube#mipLevels=auto', async (t) => { +test.skip('loadImageTextureCube#mipLevels=auto', async (t) => { const imageCube = await loadImageTextureCube( ({direction, lod}) => `specular/specular_${direction}_${lod}.jpg`, { @@ -82,7 +82,7 @@ test('loadImageTextureCube#mipLevels=auto', async (t) => { t.equal(Object.keys(imageCube).length, 6, 'image cube has 6 images'); for (const face in imageCube) { const imageMips = imageCube[face]; - t.equal(imageMips.length, 10, `array of mip images has correct length`); + t.equal(imageMips.length, 10, 'array of mip images has correct length'); // t.ok(imageMips.every(isImage) `face ${face} is a valid array of mip images`); } t.end(); diff --git a/modules/textures/test/utils/format-utils.spec.js b/modules/textures/test/utils/format-utils.spec.ts similarity index 100% rename from modules/textures/test/utils/format-utils.spec.js rename to modules/textures/test/utils/format-utils.spec.ts diff --git a/modules/tile-converter/Dockerfile b/modules/tile-converter/Dockerfile index a547e23e9f..290fbce380 100644 --- a/modules/tile-converter/Dockerfile +++ b/modules/tile-converter/Dockerfile @@ -26,14 +26,14 @@ FROM node:16-alpine # copy pgm and dist converter WORKDIR /loaders-bundle -COPY ./modules/tile-converter/dist/converter.min.js . +COPY ./modules/tile-converter/dist/converter.min.cjs . # install dependencies # join-images npm package related to the specific version of sharp RUN npm install sharp@^0.30.4 -RUN node converter.min.js --install-dependencies +RUN node converter.min.cjs --install-dependencies # install zip to be able to create SLPK files RUN apk --no-cache add zip unzip -ENTRYPOINT ["node", "converter.min.js"] +ENTRYPOINT ["node", "converter.min.cjs"] diff --git a/modules/tile-converter/bin/converter.js b/modules/tile-converter/bin/converter.js index 9183c84383..842932c303 100755 --- a/modules/tile-converter/bin/converter.js +++ b/modules/tile-converter/bin/converter.js @@ -1,4 +1,2 @@ #!/usr/bin/env node -// @ts-nocheck -/* eslint-disable import/no-extraneous-dependencies */ -require('@loaders.gl/tile-converter/dist/converter.min'); +import '../dist/converter.min.cjs'; diff --git a/modules/tile-converter/bin/i3s-server.js b/modules/tile-converter/bin/i3s-server.js new file mode 100755 index 0000000000..fd63fbcfc7 --- /dev/null +++ b/modules/tile-converter/bin/i3s-server.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import '../dist/i3s-server/bin/i3s-server.min.cjs'; diff --git a/modules/tile-converter/bin/slpk-extractor.js b/modules/tile-converter/bin/slpk-extractor.js index 2eb4e10179..6718c4b15d 100755 --- a/modules/tile-converter/bin/slpk-extractor.js +++ b/modules/tile-converter/bin/slpk-extractor.js @@ -1,4 +1,2 @@ #!/usr/bin/env node -// @ts-nocheck -/* eslint-disable import/no-extraneous-dependencies */ -require('@loaders.gl/tile-converter/dist/slpk-extractor.min'); +import '../dist/slpk-extractor.min.cjs'; diff --git a/modules/tile-converter/package.json b/modules/tile-converter/package.json index 5894f37144..4e35324444 100644 --- a/modules/tile-converter/package.json +++ b/modules/tile-converter/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/tile-converter", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Converter", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -17,13 +18,20 @@ "i3s" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "bin": { "tile-converter": "./bin/converter.js", "slpk-extractor": "./bin/slpk-extractor.js", - "i3s-server": "./src/i3s-server/bin/www" + "i3s-server": "./bin/i3s-server.js" }, "files": [ "src", @@ -39,47 +47,50 @@ "join-images": false }, "scripts": { - "pre-build": "npm run build-bundle && npm run build-converter-bundle && npm run build-i3s-attributes-worker && npm run build-slpk-extractor-bundle && npm run build-3d-tiles-attributes-worker", - "build-bundle": "esbuild ./src/index.ts --bundle --outfile=dist/dist.min.js --platform=node --external:join-images", - "build-converter-bundle": "esbuild src/converter-cli.ts --outfile=dist/converter.min.js --platform=node --target=esnext,node14 --external:join-images --minify --bundle --define:__VERSION__=\\\"$npm_package_version\\\"", - "build-slpk-extractor-bundle": "esbuild src/slpk-extractor-cli.ts --outfile=dist/slpk-extractor.min.js --platform=node --target=esnext,node14 --external:join-images --minify --bundle --define:__VERSION__=\\\"$npm_package_version\\\"", - "build-i3s-attributes-worker": "esbuild src/workers/i3s-attributes-worker.ts --outfile=dist/i3s-attributes-worker.js --platform=node --target=esnext,node14 --external:join-images --minify --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"", - "build-3d-tiles-attributes-worker": "esbuild src/workers/3d-tiles-attributes-worker.ts --outfile=dist/3d-tiles-attributes-worker.js --platform=node --target=esnext,node14 --external:join-images --minify --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"" + "pre-build": "npm run build-converter-bundle && npm run build-slpk-extractor-bundle && npm run build-i3s-server-bundle", + "build-converter-bundle": "esbuild src/converter-cli.ts --outfile=dist/converter.min.cjs --platform=node --target=esnext,node14 --external:join-images --minify --bundle --define:__VERSION__=\\\"$npm_package_version\\\"", + "build-slpk-extractor-bundle": "esbuild src/slpk-extractor-cli.ts --outfile=dist/slpk-extractor.min.cjs --platform=node --target=esnext,node14 --minify --bundle --define:__VERSION__=\\\"$npm_package_version\\\"", + "build-i3s-server-bundle": "esbuild src/i3s-server/bin/www.ts --outfile=dist/i3s-server/bin/i3s-server.min.cjs --platform=node --target=esnext,node14 --minify --bundle --define:__VERSION__=\\\"$npm_package_version\\\"" }, "dependencies": { - "@loaders.gl/3d-tiles": "4.0.0-alpha.13", - "@loaders.gl/crypto": "4.0.0-alpha.13", - "@loaders.gl/draco": "4.0.0-alpha.13", - "@loaders.gl/gltf": "4.0.0-alpha.13", - "@loaders.gl/i3s": "4.0.0-alpha.13", - "@loaders.gl/images": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/polyfills": "4.0.0-alpha.13", - "@loaders.gl/textures": "4.0.0-alpha.13", - "@loaders.gl/tiles": "4.0.0-alpha.13", - "@loaders.gl/worker-utils": "4.0.0-alpha.13", - "@loaders.gl/zip": "4.0.0-alpha.13", - "@math.gl/core": "^3.5.1", - "@math.gl/culling": "^3.5.1", - "@math.gl/geoid": "^3.5.1", - "@math.gl/geospatial": "^3.5.1", + "@loaders.gl/3d-tiles": "4.0.3", + "@loaders.gl/crypto": "4.0.3", + "@loaders.gl/draco": "4.0.3", + "@loaders.gl/gltf": "4.0.3", + "@loaders.gl/i3s": "4.0.3", + "@loaders.gl/images": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/polyfills": "4.0.3", + "@loaders.gl/textures": "4.0.3", + "@loaders.gl/tiles": "4.0.3", + "@loaders.gl/worker-utils": "4.0.3", + "@loaders.gl/zip": "4.0.3", + "@math.gl/core": "^4.0.0", + "@math.gl/culling": "^4.0.0", + "@math.gl/geoid": "^4.0.0", + "@math.gl/geospatial": "^4.0.0", "archiver": "^5.0.0", "cors": "^2.8.5", "crypt": "^0.0.2", - "debug": "~2.6.9", - "express": "~4.17.3", + "debug": "~4.3.4", + "express": "~4.18.2", + "inquirer": "^8.0.0", "json-map-transform": "^1.2.6", "jszip": "^3.5.0", "md5": "^2.3.0", "morgan": "~1.9.1", - "uuid": "^8.1.0" + "uuid": "^9.0.0" }, "peerDependencies": { - "@loaders.gl/core": "^4.0.0-alpha.8" + "@loaders.gl/core": "^4.0.0" }, "quarantinedDependencies": { "join-images": "^1.1.3", "sharp": "^0.31.3" }, - "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" + "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5", + "devDependencies": { + "@types/express": "^4.17.17", + "@types/node": "^20.4.2" + } } diff --git a/modules/tile-converter/src/3d-tiles-attributes-worker.ts b/modules/tile-converter/src/3d-tiles-attributes-worker.ts deleted file mode 100644 index 5361ec9c07..0000000000 --- a/modules/tile-converter/src/3d-tiles-attributes-worker.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type {WorkerObject} from '@loaders.gl/worker-utils'; -import type {FeatureAttribute} from '@loaders.gl/i3s'; - -import {processOnWorker} from '@loaders.gl/worker-utils'; - -// __VERSION__ is injected by babel-plugin-version-inline -// @ts-ignore TS2304: Cannot find name '__VERSION__'. -const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; - -export type Tile3DAttributesWorkerOptions = { - featureAttributes: FeatureAttribute | null; - source: string; -}; - -export type I3SAttributesData = { - tileContent: any; - textureFormat: string; -}; - -/** - * I3S Attributes Worker to handle B3DM object - */ -export const Tile3dAttributesWorker = { - id: '3d-tiles-attributes', - name: '3DTiles Attributes Worker', - module: 'tile-converter', - version: VERSION, - options: { - featureAttributes: null - } -}; - -/** - * Performs I3S attributes transformation - */ -export function transform3DTilesAttributesOnWorker( - i3sAttributesData: I3SAttributesData, - options: Tile3DAttributesWorkerOptions -): Promise { - return processOnWorker(Tile3dAttributesWorker, i3sAttributesData, options); -} - -export const _typecheckI3SAttributesWorker: WorkerObject = Tile3dAttributesWorker; diff --git a/modules/tile-converter/src/3d-tiles-converter/3d-tiles-converter.ts b/modules/tile-converter/src/3d-tiles-converter/3d-tiles-converter.ts index 8c4d28c99e..b29fdb0a34 100644 --- a/modules/tile-converter/src/3d-tiles-converter/3d-tiles-converter.ts +++ b/modules/tile-converter/src/3d-tiles-converter/3d-tiles-converter.ts @@ -1,12 +1,16 @@ -import type {AttributeStorageInfo, FeatureAttribute, NodeReference} from '@loaders.gl/i3s'; +import type { + AttributeStorageInfo, + FeatureAttribute, + NodeReference, + I3STilesetHeader +} from '@loaders.gl/i3s'; import type {Tiles3DTileJSON} from '@loaders.gl/3d-tiles'; import {join} from 'path'; import process from 'process'; import transform from 'json-map-transform'; -import {fetchFile, getLoaderOptions, load, isBrowser} from '@loaders.gl/core'; +import {load, isBrowser} from '@loaders.gl/core'; import {I3SLoader, I3SAttributeLoader, COORDINATE_SYSTEM} from '@loaders.gl/i3s'; -import {Tileset3D, Tile3D} from '@loaders.gl/tiles'; import {Geoid} from '@math.gl/geoid'; import {PGMLoader} from '../pgm-loader'; @@ -16,13 +20,12 @@ import {writeFile, removeDir} from '../lib/utils/file-utils'; import {calculateFilesSize, timeConverter} from '../lib/utils/statistic-utills'; import {TILESET as tilesetTemplate} from './json-templates/tileset'; import {createObbFromMbs} from '../i3s-converter/helpers/coordinate-converter'; -import { - I3SAttributesData, - Tile3dAttributesWorker, - transform3DTilesAttributesOnWorker -} from '../3d-tiles-attributes-worker'; -import {getWorkerURL, WorkerFarm} from '@loaders.gl/worker-utils'; +import {WorkerFarm} from '@loaders.gl/worker-utils'; import {BROWSER_ERROR_MESSAGE} from '../constants'; +import B3dmConverter, {I3SAttributesData} from './helpers/b3dm-converter'; +import {I3STileHeader} from '@loaders.gl/i3s/src/types'; +import {loadI3SContent} from './helpers/load-i3s'; +import {I3SLoaderOptions} from '@loaders.gl/i3s/src/i3s-loader'; const I3S = 'I3S'; @@ -35,9 +38,18 @@ export default class Tiles3DConverter { vertexCounter: number; conversionStartTime: [number, number]; geoidHeightModel: Geoid | null; - sourceTileset: Tileset3D | null; - attributeStorageInfo: AttributeStorageInfo | null; + sourceTileset: I3STilesetHeader | null; + attributeStorageInfo?: AttributeStorageInfo[] | null; workerSource: {[key: string]: string} = {}; + loaderOptions: I3SLoaderOptions = { + _nodeWorkers: true, + reuseWorkers: true, + i3s: {coordinateSystem: COORDINATE_SYSTEM.LNGLAT_OFFSETS, decodeTextures: false}, + // We need to load local fs workers because nodejs can't load workers from the Internet + 'i3s-content': { + workerUrl: './modules/i3s/dist/i3s-content-worker-node.js' + } + }; constructor() { this.options = {}; @@ -78,30 +90,19 @@ export default class Tiles3DConverter { this.geoidHeightModel = await load(egmFilePath, PGMLoader); console.log('Loading egm file completed!'); // eslint-disable-line - await this.loadWorkers(); - - const sourceTilesetJson = await load(inputUrl, I3SLoader, {}); + this.sourceTileset = await load(inputUrl, I3SLoader, this.loaderOptions); - this.sourceTileset = new Tileset3D(sourceTilesetJson, { - loadOptions: { - _nodeWorkers: true, - reuseWorkers: true, - i3s: {coordinateSystem: COORDINATE_SYSTEM.LNGLAT_OFFSETS, decodeTextures: false} - // TODO should no longer be needed with new workers - // 'i3s-content-nodejs': { - // workerUrl: './modules/i3s/dist/i3s-content-nodejs-worker.js' - // } - } - }); + if (!this.sourceTileset) { + return; + } - await this.sourceTileset.tilesetInitializationPromise; - const rootNode = this.sourceTileset.root!; - if (!rootNode.header.obb) { - rootNode.header.obb = createObbFromMbs(rootNode.header.mbs); + const rootNode = this.sourceTileset?.root; + if (!rootNode.obb) { + rootNode.obb = createObbFromMbs(rootNode.mbs); } this.tilesetPath = join(`${outputPath}`, `${tilesetName}`); - this.attributeStorageInfo = sourceTilesetJson.attributeStorageInfo; + this.attributeStorageInfo = this.sourceTileset.attributeStorageInfo; // Removing the tilesetPath needed to exclude erroneous files after conversion try { await removeDir(this.tilesetPath); @@ -111,7 +112,7 @@ export default class Tiles3DConverter { const rootTile: Tiles3DTileJSON = { boundingVolume: { - box: i3sObbTo3dTilesObb(rootNode.header.obb, this.geoidHeightModel) + box: i3sObbTo3dTilesObb(rootNode.obb, this.geoidHeightModel) }, geometricError: convertScreenThresholdToGeometricError(rootNode), children: [] @@ -137,28 +138,33 @@ export default class Tiles3DConverter { * @param childNodeInfo child node to convert */ private async convertChildNode( - parentSourceNode: Tile3D, + parentSourceNode: I3STileHeader, parentNode: Tiles3DTileJSON, level: number, childNodeInfo: NodeReference ): Promise { const sourceChild = await this._loadChildNode(parentSourceNode, childNodeInfo); - parentSourceNode.children.push(sourceChild); if (sourceChild.contentUrl) { - await this.sourceTileset!._loadTile(sourceChild); - this.vertexCounter += sourceChild.content.vertexCount; + const content = await loadI3SContent(this.sourceTileset, sourceChild, this.loaderOptions); + + if (!content) { + await this._addChildren(sourceChild, parentNode, level + 1); + return; + } + + this.vertexCounter += content?.vertexCount || 0; let featureAttributes: FeatureAttribute | null = null; if (this.attributeStorageInfo) { featureAttributes = await this._loadChildAttributes(sourceChild, this.attributeStorageInfo); } - if (!sourceChild.header.obb) { - sourceChild.header.obb = createObbFromMbs(sourceChild.header.mbs); + if (!sourceChild.obb) { + sourceChild.obb = createObbFromMbs(sourceChild.mbs); } const boundingVolume = { - box: i3sObbTo3dTilesObb(sourceChild.header.obb, this.geoidHeightModel) + box: i3sObbTo3dTilesObb(sourceChild.obb, this.geoidHeightModel) }; const child: Tiles3DTileJSON = { boundingVolume, @@ -167,14 +173,13 @@ export default class Tiles3DConverter { }; const i3sAttributesData: I3SAttributesData = { - tileContent: sourceChild.content, - textureFormat: sourceChild?.header?.textureFormat + tileContent: content, + box: boundingVolume.box, + textureFormat: sourceChild.textureFormat }; - const b3dm = await transform3DTilesAttributesOnWorker(i3sAttributesData, { - source: this.workerSource.tile3dWorkerSource, - featureAttributes - }); + const b3dmConverter = new B3dmConverter(); + const b3dm = await b3dmConverter.convert(i3sAttributesData, featureAttributes); child.content = { uri: `${sourceChild.id}.b3dm`, @@ -183,7 +188,6 @@ export default class Tiles3DConverter { await writeFile(this.tilesetPath, new Uint8Array(b3dm), `${sourceChild.id}.b3dm`); parentNode.children.push(child); - sourceChild.unloadContent(); await this._addChildren(sourceChild, child, level + 1); } else { await this._addChildren(sourceChild, parentNode, level + 1); @@ -197,7 +201,7 @@ export default class Tiles3DConverter { * @param level a current level of a tree depth */ private async _addChildren( - parentSourceNode: Tile3D, + parentSourceNode: I3STileHeader, parentNode: Tiles3DTileJSON, level: number ): Promise { @@ -205,7 +209,7 @@ export default class Tiles3DConverter { return; } const promises: Promise[] = []; - for (const childNodeInfo of parentSourceNode.header.children || []) { + for (const childNodeInfo of parentSourceNode.children || []) { promises.push(this.convertChildNode(parentSourceNode, parentNode, level, childNodeInfo)); } await Promise.all(promises); @@ -217,29 +221,31 @@ export default class Tiles3DConverter { * @param childNodeInfo child information from 3DNodeIndexDocument * (https://github.com/Esri/i3s-spec/blob/master/docs/1.7/nodeReference.cmn.md) */ - private async _loadChildNode(parentNode: Tile3D, childNodeInfo: NodeReference): Promise { + private async _loadChildNode( + parentNode: I3STileHeader, + childNodeInfo: NodeReference + ): Promise { let header; - if (this.sourceTileset!.tileset.nodePages) { + if (this.sourceTileset?.nodePagesTile) { console.log(`Node conversion: ${childNodeInfo.id}`); // eslint-disable-line no-console,no-undef - header = await this.sourceTileset!.tileset.nodePagesTile.formTileFromNodePages( - childNodeInfo.id + header = await this.sourceTileset.nodePagesTile.formTileFromNodePages( + parseInt(childNodeInfo.id) ); } else { - const {loader} = this.sourceTileset!; const nodeUrl = this._relativeUrlToFullUrl(parentNode.url, childNodeInfo.href!); // load metadata const options = { i3s: { - ...this.sourceTileset!.loadOptions, + ...this.loaderOptions, isTileHeader: true, loadContent: false } }; console.log(`Node conversion: ${nodeUrl}`); // eslint-disable-line no-console,no-undef - header = await load(nodeUrl, loader, options); + header = await load(nodeUrl, I3SLoader, options); } - return new Tile3D(this.sourceTileset!, header, parentNode); + return header; } /** @@ -247,7 +253,7 @@ export default class Tiles3DConverter { * @param baseUrl the base url. A resulting url will be related from this url * @param relativeUrl a realtive url of a resource */ - private _relativeUrlToFullUrl(baseUrl: string, relativeUrl: string): string { + private _relativeUrlToFullUrl(baseUrl: string = '', relativeUrl: string): string { let resultArray = baseUrl.split('/'); const relativeUrlArray = relativeUrl.split('/'); for (const folder of relativeUrlArray) { @@ -271,11 +277,11 @@ export default class Tiles3DConverter { * @returns Promise of attributes object. */ private async _loadChildAttributes( - sourceChild: Tile3D, - attributeStorageInfo: AttributeStorageInfo + sourceChild: I3STileHeader, + attributeStorageInfo: AttributeStorageInfo[] ): Promise { const promises: any[] = []; - const {attributeUrls} = sourceChild.header; + const {attributeUrls = []} = sourceChild; for (let index = 0; index < attributeUrls.length; index++) { const inputUrl = attributeUrls[index]; @@ -342,14 +348,4 @@ export default class Tiles3DConverter { console.log(`File(s) size: `, filesSize, ' bytes'); // eslint-disable-line console.log(`------------------------------------------------`); // eslint-disable-line } - - private async loadWorkers(): Promise { - console.log(`Loading workers source...`); // eslint-disable-line no-undef, no-console - const tile3dAttributesWorkerUrl = getWorkerURL(Tile3dAttributesWorker, {...getLoaderOptions()}); - const sourceResponse = await fetchFile(tile3dAttributesWorkerUrl); - const source = await sourceResponse.text(); - - this.workerSource.tile3dWorkerSource = source; - console.log(`Loading workers source completed!`); // eslint-disable-line no-undef, no-console - } } diff --git a/modules/tile-converter/src/3d-tiles-converter/helpers/b3dm-converter.ts b/modules/tile-converter/src/3d-tiles-converter/helpers/b3dm-converter.ts index 65b4d90298..24b754eee1 100644 --- a/modules/tile-converter/src/3d-tiles-converter/helpers/b3dm-converter.ts +++ b/modules/tile-converter/src/3d-tiles-converter/helpers/b3dm-converter.ts @@ -1,5 +1,4 @@ -import type {I3SAttributesData} from '../../3d-tiles-attributes-worker'; - +import type {I3STileContent} from '@loaders.gl/i3s'; import {encodeSync} from '@loaders.gl/core'; import {GLTFScenegraph, GLTFWriter} from '@loaders.gl/gltf'; import {Tile3DWriter} from '@loaders.gl/3d-tiles'; @@ -11,6 +10,12 @@ import {generateSyntheticIndices} from '../../lib/utils/geometry-utils'; const Z_UP_TO_Y_UP_MATRIX = new Matrix4([1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1]); const scratchVector = new Vector3(); +export type I3SAttributesData = { + tileContent: I3STileContent; + box: number[]; + textureFormat: string; +}; + /** * Converts content of an I3S node to *.b3dm's file content */ @@ -28,7 +33,7 @@ export default class B3dmConverter { i3sAttributesData: I3SAttributesData, featureAttributes: any = null ): Promise { - const gltf = await this.buildGltf(i3sAttributesData, featureAttributes); + const gltf = await this.buildGLTF(i3sAttributesData, featureAttributes); const b3dm = encodeSync( { gltfEncoded: new Uint8Array(gltf), @@ -46,23 +51,16 @@ export default class B3dmConverter { * @param i3sTile - Tile3D instance for I3S node * @returns - encoded glb content */ - async buildGltf( + async buildGLTF( i3sAttributesData: I3SAttributesData, featureAttributes: any ): Promise { - const {tileContent, textureFormat} = i3sAttributesData; - const { - material, - attributes, - indices: originalIndices, - cartesianOrigin, - cartographicOrigin, - modelMatrix - } = tileContent; + const {tileContent, textureFormat, box} = i3sAttributesData; + const {material, attributes, indices: originalIndices, modelMatrix} = tileContent; const gltfBuilder = new GLTFScenegraph(); - const textureIndex = await this._addI3sTextureToGltf(tileContent, textureFormat, gltfBuilder); - const pbrMaterialInfo = this._convertI3sMaterialToGltfMaterial(material, textureIndex); + const textureIndex = await this._addI3sTextureToGLTF(tileContent, textureFormat, gltfBuilder); + const pbrMaterialInfo = this._convertI3sMaterialToGLTFMaterial(material, textureIndex); const materialIndex = gltfBuilder.addMaterial(pbrMaterialInfo); const positions = attributes.positions; @@ -75,6 +73,12 @@ export default class B3dmConverter { ); } + const cartesianOrigin = new Vector3(box); + const cartographicOrigin = Ellipsoid.WGS84.cartesianToCartographic( + cartesianOrigin, + new Vector3() + ); + attributes.positions.value = this._normalizePositions( positionsValue, cartesianOrigin, @@ -111,7 +115,7 @@ export default class B3dmConverter { * @param {GLTFScenegraph} gltfBuilder - gltfScenegraph instance to construct GLTF * @returns {Promise} - GLTF texture index */ - async _addI3sTextureToGltf(tileContent, textureFormat, gltfBuilder) { + async _addI3sTextureToGLTF(tileContent, textureFormat, gltfBuilder) { const {texture, material, attributes} = tileContent; let textureIndex = null; let selectedTexture = texture; @@ -218,7 +222,7 @@ export default class B3dmConverter { * @param {number | null} textureIndex - texture index in GLTF * @returns {object} GLTF material */ - _convertI3sMaterialToGltfMaterial(material, textureIndex) { + _convertI3sMaterialToGLTFMaterial(material, textureIndex) { const isTextureIndexExists = textureIndex !== null; if (!material) { @@ -244,7 +248,7 @@ export default class B3dmConverter { } if (textureIndex !== null) { - material = this._setGltfTexture(material, textureIndex); + material = this._setGLTFTexture(material, textureIndex); } return material; @@ -256,7 +260,7 @@ export default class B3dmConverter { * @param {number} textureIndex - texture index in GLTF * @returns {void} */ - _setGltfTexture(materialDefinition, textureIndex) { + _setGLTFTexture(materialDefinition, textureIndex) { const material = { ...materialDefinition, pbrMetallicRoughness: {...materialDefinition.pbrMetallicRoughness} diff --git a/modules/tile-converter/src/3d-tiles-converter/helpers/load-i3s.ts b/modules/tile-converter/src/3d-tiles-converter/helpers/load-i3s.ts new file mode 100644 index 0000000000..f56aba2a85 --- /dev/null +++ b/modules/tile-converter/src/3d-tiles-converter/helpers/load-i3s.ts @@ -0,0 +1,52 @@ +import {load} from '@loaders.gl/core'; +import { + I3STileContent, + I3STileHeader, + I3STilesetHeader, + I3SLoader, + I3SLoaderOptions +} from '@loaders.gl/i3s'; + +/** + * Load I3S node content + * @param sourceTileset - source layer JSON + * @param sourceTile - source I3S node metadata + * @param tilesetLoadOptions - load options for Tiles3DLoader + * @returns - 3DTiles tile content or null + */ +export const loadI3SContent = async ( + sourceTileset: I3STilesetHeader | null, + sourceTile: I3STileHeader, + tilesetLoadOptions: I3SLoaderOptions +): Promise => { + if (!sourceTileset || !sourceTile.contentUrl) { + return null; + } + + const loadOptions = { + ...tilesetLoadOptions, + i3s: { + ...tilesetLoadOptions.i3s, + isTileset: false, + isTileHeader: false, + _tileOptions: { + attributeUrls: sourceTile.attributeUrls, + textureUrl: sourceTile.textureUrl, + textureFormat: sourceTile.textureFormat, + textureLoaderOptions: sourceTile.textureLoaderOptions, + materialDefinition: sourceTile.materialDefinition, + isDracoGeometry: sourceTile.isDracoGeometry, + mbs: sourceTile.mbs + }, + _tilesetOptions: { + store: sourceTileset.store, + attributeStorageInfo: sourceTileset.attributeStorageInfo, + fields: sourceTileset.fields + } + } + }; + const tileContent = await load(sourceTile.contentUrl, I3SLoader, loadOptions); + + // @ts-expect-error + return tileContent; +}; diff --git a/modules/tile-converter/src/3d-tiles-converter/helpers/texture-atlas.ts b/modules/tile-converter/src/3d-tiles-converter/helpers/texture-atlas.ts index f256c37814..5cee5eece2 100644 --- a/modules/tile-converter/src/3d-tiles-converter/helpers/texture-atlas.ts +++ b/modules/tile-converter/src/3d-tiles-converter/helpers/texture-atlas.ts @@ -1,3 +1,5 @@ +import type {TypedArray} from '@loaders.gl/loader-utils'; + /** * Apply uvRegions to texture coordinates. * Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/geometryUVRegion.cmn.md @@ -5,7 +7,7 @@ * @param texCoords * @param uvRegions */ -export function convertTextureAtlas(texCoords: Float32Array, uvRegions: Uint16Array): Float32Array { +export function convertTextureAtlas(texCoords: TypedArray, uvRegions: TypedArray): Float32Array { const convertedTexCoords = new Float32Array(texCoords.length); const normalisedRegions = normalizeRegions(uvRegions); @@ -43,7 +45,9 @@ function fract(uv: [number, number]): [number, number] { * Normalize uvRegions by dividing by the maximum Uint16 value * @param regions */ -function normalizeRegions(regions: Uint16Array): number[] { +function normalizeRegions(regions: TypedArray): number[] { + // The code is for Uint16Array because it is the spec requirement + // https://github.com/Esri/i3s-spec/blob/master/docs/1.8/geometryUVRegion.cmn.md const MAX_UINT_16_VALUE = 65535; const normalizedRegions: number[] = []; diff --git a/modules/tile-converter/src/bundle.ts b/modules/tile-converter/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/tile-converter/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/tile-converter/src/converter-cli.ts b/modules/tile-converter/src/converter-cli.ts index 7bf07a73a4..cfff49c8ab 100644 --- a/modules/tile-converter/src/converter-cli.ts +++ b/modules/tile-converter/src/converter-cli.ts @@ -1,6 +1,7 @@ /* eslint-disable no-console */ import '@loaders.gl/polyfills'; import {join} from 'path'; +import inquirer from 'inquirer'; import {I3SConverter, Tiles3DConverter} from '@loaders.gl/tile-converter'; import {DepsInstaller} from './deps-installer/deps-installer'; import { @@ -49,6 +50,10 @@ type TileConversionOptions = { maxDepth?: number; /** 3DTiles->I3S only. Whether the converter generates *.slpk (Scene Layer Package) I3S output file */ slpk: boolean; + /** Feature metadata class from EXT_FEATURE_METADATA or EXT_STRUCTURAL_METADATA extensions */ + metadataClass?: string; + /** With this options the tileset content will be analyzed without conversion */ + analyze?: boolean; }; /* During validation we check that particular options are defined so they can't be undefined */ @@ -132,8 +137,10 @@ function printHelp(): void { console.log( '--generate-textures [Enable KTX2 textures generation if only one of (JPG, PNG) texture is provided or generate JPG texture if only KTX2 is provided]' ); + console.log('--generate-bounding-volumes [Generate obb and mbs bounding volumes from geometry]'); + console.log('--analyze [Analyze the input tileset content without conversion, default: false]'); console.log( - '--generate-bounding-volumes [Will generate obb and mbs bounding volumes from geometry]' + '--metadata-class [One of the list of feature metadata classes, detected by converter on "analyze" stage, default: not set]' ); console.log('--validate [Enable validation]'); process.exit(0); // eslint-disable-line @@ -175,7 +182,10 @@ async function convert(options: ValidatedTileConversionOptions) { generateTextures: options.generateTextures, generateBoundingVolumes: options.generateBoundingVolumes, validate: options.validate, - instantNodeWriting: options.instantNodeWriting + instantNodeWriting: options.instantNodeWriting, + metadataClass: options.metadataClass, + analyze: options.analyze, + inquirer }); break; default: @@ -191,26 +201,35 @@ async function convert(options: ValidatedTileConversionOptions) { */ function validateOptions(options: TileConversionOptions): ValidatedTileConversionOptions { const mandatoryOptionsWithExceptions: { - [key: string]: () => void; + [key: string]: { + getMessage: () => void; + condition?: (optionValue: any) => boolean; + }; } = { - name: () => console.log('Missed: --name [Tileset name]'), - output: () => console.log('Missed: --output [Output path name]'), - sevenZipExe: () => console.log('Missed: --7zExe [7z archiver executable path]'), - egm: () => console.log('Missed: --egm [*.pgm earth gravity model file path]'), - tileset: () => console.log('Missed: --tileset [tileset.json file]'), - inputType: () => - console.log('Missed/Incorrect: --input-type [tileset input type: I3S or 3DTILES]') + name: { + getMessage: () => console.log('Missed: --name [Tileset name]'), + condition: (value: any) => Boolean(value) || Boolean(options.analyze) + }, + output: {getMessage: () => console.log('Missed: --output [Output path name]')}, + sevenZipExe: {getMessage: () => console.log('Missed: --7zExe [7z archiver executable path]')}, + egm: {getMessage: () => console.log('Missed: --egm [*.pgm earth gravity model file path]')}, + tileset: {getMessage: () => console.log('Missed: --tileset [tileset.json file]')}, + inputType: { + getMessage: () => + console.log('Missed/Incorrect: --input-type [tileset input type: I3S or 3DTILES]'), + condition: (value) => + Boolean(value) && Object.values(TILESET_TYPE).includes(value.toUpperCase()) + } }; const exceptions: (() => void)[] = []; for (const mandatoryOption in mandatoryOptionsWithExceptions) { const optionValue = options[mandatoryOption]; - const isWrongInputType = - Boolean(optionValue) && - mandatoryOption === 'inputType' && - !Object.values(TILESET_TYPE).includes(optionValue.toUpperCase()); - if (!optionValue || isWrongInputType) { - exceptions.push(mandatoryOptionsWithExceptions[mandatoryOption]); + const conditionFunc = mandatoryOptionsWithExceptions[mandatoryOption].condition; + const testValue = conditionFunc ? conditionFunc(optionValue) : optionValue; + + if (!testValue) { + exceptions.push(mandatoryOptionsWithExceptions[mandatoryOption].getMessage); } } if (exceptions.length) { @@ -292,6 +311,12 @@ function parseOptions(args: string[]): TileConversionOptions { case '--generate-bounding-volumes': opts.generateBoundingVolumes = getBooleanValue(index, args); break; + case '--analyze': + opts.analyze = getBooleanValue(index, args); + break; + case '--metadata-class': + opts.metadataClass = getStringValue(index, args); + break; case '--help': printHelp(); break; diff --git a/modules/tile-converter/src/deps-installer/deps-installer.ts b/modules/tile-converter/src/deps-installer/deps-installer.ts index a2f0d58027..9abc1d76ff 100644 --- a/modules/tile-converter/src/deps-installer/deps-installer.ts +++ b/modules/tile-converter/src/deps-installer/deps-installer.ts @@ -3,9 +3,11 @@ import {ZipLoader} from '@loaders.gl/zip'; import {writeFile} from '../lib/utils/file-utils'; import {join} from 'path'; import {ChildProcessProxy} from '@loaders.gl/worker-utils'; +import {DRACO_EXTERNAL_LIBRARIES, DRACO_EXTERNAL_LIBRARY_URLS} from '@loaders.gl/draco'; +import {BASIS_EXTERNAL_LIBRARIES} from '@loaders.gl/textures'; // @ts-ignore TS2304: Cannot find name '__VERSION__'. -const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'beta'; +const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; const PGM_LINK = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/egm/egm2008-5.zip'; @@ -23,7 +25,7 @@ export class DepsInstaller { * This path is '' by default and is not used by tile-converter. * It is used in tests to prevent rewriting actual workers during tests running */ - async install(path: string = '', workersPath: string = ''): Promise { + async install(path: string = ''): Promise { console.log('Installing "EGM2008-5" model...'); // eslint-disable-line no-console const fileMap = await load(PGM_LINK, ZipLoader, {}); @@ -34,14 +36,47 @@ export class DepsInstaller { await writeFile(depsPath, new Uint8Array(fileMap['geoids/egm2008-5.pgm']), 'egm2008-5.pgm'); - console.log('Installing "I3S Content Loader worker"'); // eslint-disable-line no-console - await this.installWorker('i3s', 'i3s-content-worker-node.js', workersPath); + console.log('Installing "I3S Content Loader" worker'); // eslint-disable-line no-console + await this.installFromNpm('i3s', 'i3s-content-worker-node.js'); - console.log('Installing "Draco Loader worker"'); // eslint-disable-line no-console - await this.installWorker('draco', 'draco-worker-node.js', workersPath); + console.log('Installing "Draco Loader" worker'); // eslint-disable-line no-console + await this.installFromNpm('draco', 'draco-worker-node.js'); - console.log('Installing "Basis Loader worker"'); // eslint-disable-line no-console - await this.installWorker('textures', 'basis-worker-node.js', workersPath); + console.log('Installing "Draco Writer" worker'); // eslint-disable-line no-console + await this.installFromNpm('draco', 'draco-writer-worker-node.js'); + + console.log('Installing "Basis Loader" worker'); // eslint-disable-line no-console + await this.installFromNpm('textures', 'basis-worker-node.js'); + + console.log('Installing "KTX2 Basis Writer" worker'); // eslint-disable-line no-console + await this.installFromNpm('textures', 'ktx2-basis-writer-worker-node.js'); + + console.log('Installing "Draco decoder" library'); // eslint-disable-line no-console + await this.installFromUrl( + DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER], + 'draco', + DRACO_EXTERNAL_LIBRARIES.DECODER + ); + await this.installFromUrl( + DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.DECODER_WASM], + 'draco', + DRACO_EXTERNAL_LIBRARIES.DECODER_WASM + ); + + console.log('Installing "Draco encoder" library'); // eslint-disable-line no-console + await this.installFromUrl( + DRACO_EXTERNAL_LIBRARY_URLS[DRACO_EXTERNAL_LIBRARIES.ENCODER], + 'draco', + DRACO_EXTERNAL_LIBRARIES.ENCODER + ); + + console.log('Installing "Basis transcoder" library'); // eslint-disable-line no-console + await this.installFromNpm('textures', BASIS_EXTERNAL_LIBRARIES.TRANSCODER, 'libs'); + await this.installFromNpm('textures', BASIS_EXTERNAL_LIBRARIES.TRANSCODER_WASM, 'libs'); + + console.log('Installing "Basis encoder" library'); // eslint-disable-line no-console + await this.installFromNpm('textures', BASIS_EXTERNAL_LIBRARIES.ENCODER, 'libs'); + await this.installFromNpm('textures', BASIS_EXTERNAL_LIBRARIES.ENCODER_WASM, 'libs'); console.log('Installing "join-images" npm package'); const childProcess = new ChildProcessProxy(); @@ -58,15 +93,25 @@ export class DepsInstaller { console.log('All dependencies were installed succesfully.'); // eslint-disable-line no-console } - private async installWorker(module: string, name: string, extraPath: string) { + private async installFromNpm(module: string, name: string, extraPath: string = '') { const fileResponse = await fetchFile( - `https://unpkg.com/@loaders.gl/${module}@${VERSION}/dist/${name}` + `https://unpkg.com/@loaders.gl/${module}@${VERSION}/dist/${extraPath}/${name}` ); const fileData = await fileResponse.arrayBuffer(); if (!fileData) { return; } - const path = join(process.cwd(), extraPath, 'modules', module, 'dist'); + const path = join(process.cwd(), 'modules', module, 'dist', extraPath); + await writeFile(path, fileData, name); + } + + private async installFromUrl(url: string, module: string, name: string) { + const fileResponse = await fetchFile(url); + const fileData = await fileResponse.arrayBuffer(); + if (!fileData) { + return; + } + const path = join(process.cwd(), 'modules', module, 'dist', 'libs'); await writeFile(path, fileData, name); } } diff --git a/modules/tile-converter/src/i3s-attributes-worker.ts b/modules/tile-converter/src/i3s-attributes-worker.ts deleted file mode 100644 index 0de06d79fc..0000000000 --- a/modules/tile-converter/src/i3s-attributes-worker.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type {WorkerObject} from '@loaders.gl/worker-utils'; -import type {ConvertedAttributes} from './i3s-converter/types'; -import type {Matrix4, Vector3} from '@math.gl/core'; -import type {GLTFNodePostprocessed} from '@loaders.gl/gltf'; - -import {processOnWorker} from '@loaders.gl/worker-utils'; - -// __VERSION__ is injected by babel-plugin-version-inline -// @ts-ignore TS2304: Cannot find name '__VERSION__'. -const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; - -export type I3SAttributesWorkerOptions = { - _nodeWorkers: boolean; - reuseWorkers: boolean; - useCartesianPositions: boolean; - source: string; -}; - -export type TextureImageProperties = { - data: Uint8Array; - compressed?: boolean; - height?: number; - width?: number; - components?: number; - mimeType?: string; -}; - -export type B3DMAttributesData = { - gltfMaterials?: {id: string}[]; - nodes: GLTFNodePostprocessed[]; - images: (null | TextureImageProperties)[]; - cartographicOrigin: Vector3; - cartesianModelMatrix: Matrix4; -}; - -/** - * I3S Attributes Worker to handle B3DM object - */ -export const I3SAttributesWorker = { - id: 'i3s-attributes', - name: 'I3S Attributes Worker', - module: 'tile-converter', - version: VERSION, - options: { - useCartesianPositions: false - } -}; - -/** - * Performs I3S attributes transformation - */ -export function transformI3SAttributesOnWorker( - attributesData: B3DMAttributesData, - options: I3SAttributesWorkerOptions -): Promise> { - return processOnWorker(I3SAttributesWorker, attributesData, options); -} - -export const _typecheckI3SAttributesWorker: WorkerObject = I3SAttributesWorker; diff --git a/modules/tile-converter/src/i3s-converter/helpers/attribute-metadata-info.ts b/modules/tile-converter/src/i3s-converter/helpers/attribute-metadata-info.ts new file mode 100644 index 0000000000..d0cef304a4 --- /dev/null +++ b/modules/tile-converter/src/i3s-converter/helpers/attribute-metadata-info.ts @@ -0,0 +1,246 @@ +import type { + Attribute, + AttributeStorageInfo, + ESRIField, + Field, + FieldInfo, + PopupInfo +} from '@loaders.gl/i3s'; + +import {AttributeType} from '../types'; + +export class AttributeMetadataInfo { + private _attributeStorageInfo: AttributeStorageInfo[]; + private _fields: Field[]; + private _popupInfo: PopupInfo | undefined; + + constructor() { + this._attributeStorageInfo = []; + this._fields = []; + } + + get attributeStorageInfo(): AttributeStorageInfo[] { + return this._attributeStorageInfo; + } + + get fields(): Field[] { + return this._fields; + } + + get popupInfo(): PopupInfo | undefined { + return this._popupInfo; + } + + /** + * Creates and stores Attribute Storage Info, Fields and PopupInfo objects based on attribute's types. + * Appends objects that have not been stored yet. + * @param attributeTypesMap - set of attribute's types + * @example AttributeStorageInfo, Fields and PopupInfo already contain objects for the following attributes: + * { + * color: 'string', + * name: 'string', + * opt_uint8: 'Int32' + * } + * Then, we call the addMetadataInfo method with the following attributeTypesMap: + * { + * // The same attributes + * color: 'string', + * name: 'string', + * opt_uint8: 'Int32', + * // New attributes + * opt_uint64: 'string', + * opt_float32: 'double', + * } + * The method creates and stores objects for opt_uint64, opt_float32 attributes. + */ + addMetadataInfo(attributeTypesMap: Record): void { + if (!Object.keys(attributeTypesMap).length) { + return; + } + const attributeTypes: Record = { + OBJECTID: AttributeType.OBJECT_ID_TYPE, + ...attributeTypesMap + }; + + let isUpdated = false; + let attributeIndex = this._attributeStorageInfo.length; + for (const key in attributeTypes) { + /* + We will append a new attribute only in case it has not been added to the attribute storage info yet. + */ + const elementFound = this._attributeStorageInfo.find((element) => element.name === key); + if (!elementFound) { + const attributeType = attributeTypes[key]; + + const storageAttribute = this.createStorageAttribute(attributeIndex, key, attributeType); + const fieldAttributeType = this.getFieldAttributeType(attributeType); + const fieldAttribute = this.createFieldAttribute(key, fieldAttributeType); + + this._attributeStorageInfo.push(storageAttribute); + this._fields.push(fieldAttribute); + attributeIndex += 1; + isUpdated = true; + } + } + if (isUpdated) { + /* + The attributeStorageInfo is updated. So, popupInfo should be recreated. + Use attributeStorageInfo as a source of attribute names to create the popupInfo. + */ + const attributeNames: string[] = []; + for (let info of this._attributeStorageInfo) { + attributeNames.push(info.name); + } + this._popupInfo = this.createPopupInfo(attributeNames); + } + } + + /** + * Generates storage attribute for map segmentation. + * @param attributeIndex - order index of attribute (f_0, f_1 ...). + * @param key - attribute key from propertyTable. + * @param attributeType - attribute type. + * @return Updated storageAttribute. + */ + private createStorageAttribute( + attributeIndex: number, + key: string, + attributeType: Attribute + ): AttributeStorageInfo { + const storageAttribute = { + key: `f_${attributeIndex}`, + name: key, + ordering: ['attributeValues'], + header: [{property: 'count', valueType: 'UInt32'}], + attributeValues: {valueType: 'Int32', valuesPerElement: 1} + }; + + switch (attributeType) { + case AttributeType.OBJECT_ID_TYPE: + this.setupIdAttribute(storageAttribute); + break; + case AttributeType.STRING_TYPE: + this.setupStringAttribute(storageAttribute); + break; + case AttributeType.DOUBLE_TYPE: + this.setupDoubleAttribute(storageAttribute); + break; + case AttributeType.SHORT_INT_TYPE: + break; + default: + this.setupStringAttribute(storageAttribute); + } + + return storageAttribute; + } + + /** + * Finds and returns attribute type based on key form propertyTable. + * @param attributeType + */ + private getFieldAttributeType(attributeType: Attribute): ESRIField { + switch (attributeType) { + case AttributeType.OBJECT_ID_TYPE: + return 'esriFieldTypeOID'; + case AttributeType.STRING_TYPE: + return 'esriFieldTypeString'; + case AttributeType.SHORT_INT_TYPE: + return 'esriFieldTypeInteger'; + case AttributeType.DOUBLE_TYPE: + return 'esriFieldTypeDouble'; + default: + return 'esriFieldTypeString'; + } + } + + /** + * Sets up Id attribute for map segmentation. + * @param storageAttribute - attribute for map segmentation . + */ + private setupIdAttribute(storageAttribute: AttributeStorageInfo): void { + storageAttribute.attributeValues = { + valueType: 'Oid32', + valuesPerElement: 1 + }; + } + + /** + * Sets up storage attribute as string. + * @param storageAttribute - attribute for map segmentation. + */ + private setupStringAttribute(storageAttribute: AttributeStorageInfo): void { + // @ts-expect-error + storageAttribute.ordering.unshift('attributeByteCounts'); + storageAttribute.header.push({property: 'attributeValuesByteCount', valueType: 'UInt32'}); + storageAttribute.attributeValues = { + valueType: 'String', + encoding: 'UTF-8', + valuesPerElement: 1 + }; + storageAttribute.attributeByteCounts = { + valueType: 'UInt32', + valuesPerElement: 1 + }; + } + + /** + * Sets up double attribute for map segmentation. + * @param storageAttribute - attribute for map segmentation . + */ + private setupDoubleAttribute(storageAttribute: AttributeStorageInfo): void { + storageAttribute.attributeValues = { + valueType: 'Float64', + valuesPerElement: 1 + }; + } + + /** + * Sets up field attribute for map segmentation. + * @param key - attribute for map segmentation. + * @param fieldAttributeType - esri attribute type ('esriFieldTypeString' or 'esriFieldTypeOID'). + */ + private createFieldAttribute(key: string, fieldAttributeType: ESRIField): Field { + return { + name: key, + type: fieldAttributeType, + alias: key + }; + } + + /** + * Generates popup info to show metadata on the map. + * @param propertyNames - array of property names including OBJECTID. + * @return data for correct rendering of popup. + */ + private createPopupInfo(propertyNames: string[]): PopupInfo { + const title = '{OBJECTID}'; + const mediaInfos = []; + const fieldInfos: FieldInfo[] = []; + const popupElements: { + fieldInfos: FieldInfo[]; + type: string; + }[] = []; + const expressionInfos = []; + + for (const propertyName of propertyNames) { + fieldInfos.push({ + fieldName: propertyName, + visible: true, + isEditable: false, + label: propertyName + }); + } + popupElements.push({ + fieldInfos, + type: 'fields' + }); + + return { + title, + mediaInfos, + popupElements, + fieldInfos, + expressionInfos + }; + } +} diff --git a/modules/tile-converter/src/i3s-converter/helpers/batch-ids-extensions.ts b/modules/tile-converter/src/i3s-converter/helpers/batch-ids-extensions.ts index 4cd55aefab..6a6e19c3a2 100644 --- a/modules/tile-converter/src/i3s-converter/helpers/batch-ids-extensions.ts +++ b/modules/tile-converter/src/i3s-converter/helpers/batch-ids-extensions.ts @@ -2,29 +2,63 @@ import {GLTFAccessorPostprocessed, GLTFMeshPrimitivePostprocessed} from '@loader import type {NumericArray} from '@loaders.gl/loader-utils'; import type { GLTF_EXT_feature_metadata_FeatureIdTexture, + GLTF_EXT_feature_metadata_GLTF, GLTF_EXT_feature_metadata_Primitive } from '@loaders.gl/gltf'; + +import type {GLTF_EXT_mesh_features} from '@loaders.gl/gltf'; + import {TypedArray} from '@math.gl/core'; -import {TextureImageProperties} from '../../i3s-attributes-worker'; +import {TextureImageProperties} from '../types'; +import {emod} from '@loaders.gl/math'; +import {EXT_MESH_FEATURES, EXT_FEATURE_METADATA} from '@loaders.gl/gltf'; +import {Tiles3DTileContent} from '@loaders.gl/3d-tiles'; -const EXT_MESH_FEATURES = 'EXT_mesh_features'; -const EXT_FEATURE_METADATA = 'EXT_feature_metadata'; +/** + * Get featureTexture by a metadata class. + * Metadata classes come from a structural metadata extesion (EXT_feature_metadata or EXT_structural_metadata). + * The glTF might contain multiple texel-level metadata textures related to different classes. Having only one metadata class + * selected to convert to I3S, we have to pick only one texture to convert to per-vertex property. + * @param tileContent - 3d tile content + * @param metadataClass - user selected feature metadata class name + * @returns featureTexture key + */ +export function getTextureByMetadataClass( + tileContent: Tiles3DTileContent, + metadataClass?: string +): string | null { + const extFeatureMetadata = tileContent.gltf?.extensions?.[ + EXT_FEATURE_METADATA + ] as GLTF_EXT_feature_metadata_GLTF; + if (!extFeatureMetadata?.featureTextures) { + return null; + } + for (const textureKey in extFeatureMetadata.featureTextures) { + const texture = extFeatureMetadata.featureTextures[textureKey]; + if (texture.class === metadataClass) { + return textureKey; + } + } + return null; +} /** * Getting batchIds from 3DTilesNext extensions. * @param attributes - gltf accessors * @param primitive - gltf primitive data * @param images - gltf texture images + * @param featureTexture - feature texture key + * @return array of batch IDs */ export function handleBatchIdsExtensions( attributes: { [key: string]: GLTFAccessorPostprocessed; }, primitive: GLTFMeshPrimitivePostprocessed, - images: (TextureImageProperties | null)[] + images: (TextureImageProperties | null)[], + featureTexture: string | null ): NumericArray { const extensions = primitive?.extensions; - if (!extensions) { return []; } @@ -35,11 +69,11 @@ export function handleBatchIdsExtensions( return handleExtFeatureMetadataExtension( attributes, extensionData as GLTF_EXT_feature_metadata_Primitive, - images + images, + featureTexture ); case EXT_MESH_FEATURES: - console.warn('EXT_mesh_features extension is not supported yet'); - return []; + return handleExtMeshFeaturesExtension(attributes, extensionData as GLTF_EXT_mesh_features); default: return []; } @@ -48,19 +82,43 @@ export function handleBatchIdsExtensions( return []; } +/** + * Getting batchIds from EXT_mesh_features extensions. + * @param attributes - gltf accessors + * @param extMeshFeatures - EXT_mesh_features extension + * @returns an array of attribute values + */ +function handleExtMeshFeaturesExtension( + attributes: { + [key: string]: GLTFAccessorPostprocessed; + }, + extMeshFeatures: GLTF_EXT_mesh_features +): NumericArray { + for (let ids of extMeshFeatures.featureIds) { + if (typeof ids.propertyTable !== 'undefined') { + // propertyTable is an index that can be 0 + // return the first featureID set that corresponts to property table. + return ids.data as NumericArray; + } + } + return []; +} + /** * Get batchIds from EXT_feature_metadata extension. - * Docs - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata - * @param attributes - * @param extFeatureMetadata - * @param textures + * @see - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata + * @param attributes - glTF attributes + * @param extFeatureMetadata - primitive-level EXT_FEATURE_METADATA extension data + * @param textures - texture images + * @param featureTexture - feature texture key */ function handleExtFeatureMetadataExtension( attributes: { [key: string]: GLTFAccessorPostprocessed; }, extFeatureMetadata: GLTF_EXT_feature_metadata_Primitive, - images: (TextureImageProperties | null)[] + images: (TextureImageProperties | null)[], + featureTexture: string | null ): NumericArray { // Take only first extension object to get batchIds attribute name. const featureIdAttribute = extFeatureMetadata?.featureIdAttributes?.[0]; @@ -93,10 +151,6 @@ function handleExtFeatureMetadataExtension( return generateBatchIdsFromTexture(featureIdTexture, textureCoordinates, images); } - // Take only first extension texture to get batchIds from the root EXT_feature_metadata object. - const featureTexture = - extFeatureMetadata?.featureTextures && extFeatureMetadata?.featureTextures[0]; - if (featureTexture) { const batchIdsAttribute = attributes[featureTexture]; return batchIdsAttribute.value; @@ -107,7 +161,7 @@ function handleExtFeatureMetadataExtension( /** * Generates implicit feature ids - * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#implicit-feature-ids + * @see - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata#implicit-feature-ids * @param featuresCount * @param constant * @param devisor @@ -191,12 +245,3 @@ function generateBatchIdsFromTexture( return batchIds; } - -/** - * Handle UVs if they are out of range [0,1]. - * @param n - * @param m - */ -function emod(n: number): number { - return ((n % 1) + 1) % 1; -} diff --git a/modules/tile-converter/src/i3s-converter/helpers/feature-attributes.ts b/modules/tile-converter/src/i3s-converter/helpers/feature-attributes.ts index 7b83df5db4..34129e3e5a 100644 --- a/modules/tile-converter/src/i3s-converter/helpers/feature-attributes.ts +++ b/modules/tile-converter/src/i3s-converter/helpers/feature-attributes.ts @@ -1,34 +1,38 @@ import type {FeatureTableJson} from '@loaders.gl/3d-tiles'; -import { - Attribute, - AttributeStorageInfo, - ESRIField, - Field, - FieldInfo, - PopupInfo -} from '@loaders.gl/i3s'; +import type {Attribute} from '@loaders.gl/i3s'; +import type { + GLTFPostprocessed, + GLTF_EXT_feature_metadata_GLTF, + GLTF_EXT_feature_metadata_ClassProperty, + GLTF_EXT_structural_metadata_GLTF, + GLTF_EXT_structural_metadata_ClassProperty +} from '@loaders.gl/gltf'; + +import {AttributeType} from '../types'; + +import {EXT_FEATURE_METADATA, EXT_STRUCTURAL_METADATA} from '@loaders.gl/gltf'; /** - * Takes attributes from property table based on featureIds. + * Takes attributes from property table based on featureIdsMap. * If there is no property value for particular featureId (index) the property will be null. * Example: * Initial data: - * OBJECTID: [0, 1, 5] + * OBJECTID: {0: 0, 3: 33, 4: 333} * component: ['Windows', 'Frames', 'Wall', 'Roof', 'Skylight'] * Result: - * OBJECTID: [0, 1, 5] - * component: ['Windows', 'Frames', 'null'] - * @param featureIds + * OBJECTID: [0, 33, 333] + * component: ['Windows', 'Roof', 'Skylight'] + * @param featureIdsMap * @param propertyTable */ export function flattenPropertyTableByFeatureIds( - featureIds: number[], + featureIdsMap: Record, propertyTable: FeatureTableJson ): FeatureTableJson { const resultPropertyTable: FeatureTableJson = {}; for (const propertyName in propertyTable) { const properties = propertyTable[propertyName]; - resultPropertyTable[propertyName] = getPropertiesByFeatureIds(properties, featureIds); + resultPropertyTable[propertyName] = getPropertiesByFeatureIds(properties, featureIdsMap); } return resultPropertyTable; @@ -37,14 +41,19 @@ export function flattenPropertyTableByFeatureIds( /** * Getting properties by featureId index * @param properties - * @param featureIds + * @param featureIdsMap */ -function getPropertiesByFeatureIds(properties: any[], featureIds: number[]): any[] { - const resultProperties: any = []; - - for (const featureId of featureIds) { - const property = properties[featureId] || null; - resultProperties.push(property); +function getPropertiesByFeatureIds( + properties: unknown[], + featureIdsMap: Record +): unknown[] { + const resultProperties: unknown[] = []; + + if (properties) { + for (const featureIdKey in featureIdsMap) { + const property = properties[featureIdKey] || null; + resultProperties.push(property); + } } return resultProperties; @@ -64,7 +73,7 @@ export function checkPropertiesLength( let needFlatten = false; for (const attribute of Object.values(propertyTable)) { - if (featureIds.length !== attribute.length) { + if (!featureIds || !attribute || featureIds.length !== attribute.length) { needFlatten = true; } } @@ -72,176 +81,163 @@ export function checkPropertiesLength( return needFlatten; } -/** String data type name for feature attributes */ -const STRING_TYPE = 'string'; -/** Integer data type name for feature attributes */ -const SHORT_INT_TYPE = 'Int32'; -/** Double data type name for feature attributes */ -const DOUBLE_TYPE = 'double'; -/** Type of attribute that is linked with feature ids */ -const OBJECT_ID_TYPE = 'OBJECTID'; /** * Get the attribute type for attributeStorageInfo https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md - * @param key - attribute's key - * @param attribute - attribute's type in propertyTable + * @param attribute - attribute taken from propertyTable */ -export function getAttributeType(key: string, attribute: string): string { - if (key === OBJECT_ID_TYPE) { - return OBJECT_ID_TYPE; - } - if (typeof attribute === STRING_TYPE) { - return STRING_TYPE; +export function getAttributeType(attribute: unknown): string { + if (typeof attribute === 'string' || typeof attribute === 'bigint') { + return AttributeType.STRING_TYPE; } else if (typeof attribute === 'number') { - return Number.isInteger(attribute) ? SHORT_INT_TYPE : DOUBLE_TYPE; + return Number.isInteger(attribute) ? AttributeType.SHORT_INT_TYPE : AttributeType.DOUBLE_TYPE; } - return STRING_TYPE; + return AttributeType.STRING_TYPE; } /** - * Generate storage attribute for map segmentation. - * @param attributeIndex - order index of attribute (f_0, f_1 ...). - * @param key - attribute key from propertyTable. - * @param attributeType - attribute type. - * @return Updated storageAttribute. + * Gets attribute's types based on the property table records. + * @param propertyTable - Table with layer meta data. + * @returns set of attribute types + * @example of returned object: + * { + * "opt_uint8": "Int32", + * "opt_uint64": "string" + * } */ -export function createdStorageAttribute( - attributeIndex: number, - key: string, - attributeType: Attribute -): AttributeStorageInfo { - const storageAttribute = { - key: `f_${attributeIndex}`, - name: key, - ordering: ['attributeValues'], - header: [{property: 'count', valueType: 'UInt32'}], - attributeValues: {valueType: 'Int32', valuesPerElement: 1} - }; - - switch (attributeType) { - case OBJECT_ID_TYPE: - setupIdAttribute(storageAttribute); - break; - case STRING_TYPE: - setupStringAttribute(storageAttribute); - break; - case DOUBLE_TYPE: - setupDoubleAttribute(storageAttribute); - break; - case SHORT_INT_TYPE: - break; - default: - setupStringAttribute(storageAttribute); +export function getAttributeTypesMapFromPropertyTable( + propertyTable: FeatureTableJson +): Record { + const attributeTypesMap: Record = {}; + for (const key in propertyTable) { + // Get attribute type based on the first element of each property. + const firstAttribute = propertyTable[key][0]; + const attributeType = getAttributeType(firstAttribute); + attributeTypesMap[key] = attributeType; } - - return storageAttribute; + return attributeTypesMap; } /** - * Find and return attribute type based on key form propertyTable. - * @param attributeType + * Gets attribute's types from the extension schema selected by the class name 'metadataClass'. + * @param gltfJson - JSON part of GLB content + * @param metadataClass - name of the schema class + * @returns set of attribute's types + * @example of returned object: + * { + * "opt_uint8": "Int32", + * "opt_uint64": "string" + * } */ -export function getFieldAttributeType(attributeType: Attribute): ESRIField { - switch (attributeType) { - case OBJECT_ID_TYPE: - return 'esriFieldTypeOID'; - case STRING_TYPE: - return 'esriFieldTypeString'; - case SHORT_INT_TYPE: - return 'esriFieldTypeInteger'; - case DOUBLE_TYPE: - return 'esriFieldTypeDouble'; - default: - return 'esriFieldTypeString'; +export const getAttributeTypesMapFromSchema = ( + gltfJson: GLTFPostprocessed, + metadataClass: string +): Record | null => { + const attributeTypesMap: Record = {}; + const extFeatureMetadataSchemaClass = ( + gltfJson.extensions?.[EXT_FEATURE_METADATA] as GLTF_EXT_feature_metadata_GLTF + )?.schema?.classes?.[metadataClass]; + if (extFeatureMetadataSchemaClass) { + for (let propertyName in extFeatureMetadataSchemaClass.properties) { + const property = extFeatureMetadataSchemaClass.properties[propertyName]; + const attributeProperty = getAttributeTypeFromExtFeatureMetadata(property); + attributeTypesMap[propertyName] = attributeProperty; + } + return attributeTypesMap; } -} -/** - * Setup field attribute for map segmentation. - * @param key - attribute for map segmentation. - * @param fieldAttributeType - esri attribute type ('esriFieldTypeString' or 'esriFieldTypeOID'). - */ -export function createFieldAttribute(key: string, fieldAttributeType: ESRIField): Field { - return { - name: key, - type: fieldAttributeType, - alias: key - }; -} + const extStructuralMetadataSchemaClass = ( + gltfJson.extensions?.[EXT_STRUCTURAL_METADATA] as GLTF_EXT_structural_metadata_GLTF + )?.schema?.classes?.[metadataClass]; + if (extStructuralMetadataSchemaClass) { + for (let propertyName in extStructuralMetadataSchemaClass.properties) { + const property = extStructuralMetadataSchemaClass.properties[propertyName]; + const attributeProperty = getAttributeTypeFromExtStructuralMetadata(property); + attributeTypesMap[propertyName] = attributeProperty; + } + return attributeTypesMap; + } + + return null; +}; /** - * Generate popup info to show metadata on the map. - * @param propertyTable - table data with OBJECTID. - * @return data for correct rendering of popup. + * Gets the attribute type according to the Ext_feature_metadata extension class schema + * @see https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md + * @param property - schema of the class property for Ext_feature_metadata + * @returns attribute's type */ -export function createPopupInfo(propertyTable: FeatureTableJson): PopupInfo { - const title = '{OBJECTID}'; - const mediaInfos = []; - const fieldInfos: FieldInfo[] = []; - const popupElements: { - fieldInfos: FieldInfo[]; - type: string; - }[] = []; - const expressionInfos = []; +const getAttributeTypeFromExtFeatureMetadata = ( + property: GLTF_EXT_feature_metadata_ClassProperty +): Attribute => { + let attributeType: Attribute; + switch (property.type) { + case 'INT8': + case 'UINT8': + case 'INT16': + case 'UINT16': + case 'INT32': + case 'UINT32': + attributeType = AttributeType.SHORT_INT_TYPE; + break; - for (const key in propertyTable) { - fieldInfos.push({ - fieldName: key, - visible: true, - isEditable: false, - label: key - }); - } - popupElements.push({ - fieldInfos, - type: 'fields' - }); - - return { - title, - mediaInfos, - popupElements, - fieldInfos, - expressionInfos - }; -} + case 'FLOAT32': + case 'FLOAT64': + attributeType = AttributeType.DOUBLE_TYPE; + break; -/** - * Setup storage attribute as string. - * @param storageAttribute - attribute for map segmentation. - */ -function setupStringAttribute(storageAttribute: AttributeStorageInfo): void { - // @ts-expect-error - storageAttribute.ordering.unshift('attributeByteCounts'); - storageAttribute.header.push({property: 'attributeValuesByteCount', valueType: 'UInt32'}); - storageAttribute.attributeValues = { - valueType: 'String', - encoding: 'UTF-8', - valuesPerElement: 1 - }; - storageAttribute.attributeByteCounts = { - valueType: 'UInt32', - valuesPerElement: 1 - }; -} + case 'INT64': + case 'UINT64': + case 'BOOLEAN': + case 'ENUM': + case 'STRING': + case 'ARRAY': + attributeType = AttributeType.STRING_TYPE; + break; -/** - * Setup Id attribute for map segmentation. - * @param storageAttribute - attribute for map segmentation . - */ -function setupIdAttribute(storageAttribute: AttributeStorageInfo): void { - storageAttribute.attributeValues = { - valueType: 'Oid32', - valuesPerElement: 1 - }; -} + default: + attributeType = AttributeType.STRING_TYPE; + break; + } + return attributeType; +}; /** - * Setup double attribute for map segmentation. - * @param storageAttribute - attribute for map segmentation . + * Gets the attribute type according to the Ext_structural_metadata extension class schema + * @see https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md + * @param property - schema of the class property for Ext_structural_metadata + * @returns attribute's type */ -function setupDoubleAttribute(storageAttribute: AttributeStorageInfo): void { - storageAttribute.attributeValues = { - valueType: 'Float64', - valuesPerElement: 1 - }; -} +const getAttributeTypeFromExtStructuralMetadata = ( + property: GLTF_EXT_structural_metadata_ClassProperty +): Attribute => { + let attributeType: Attribute; + if (property.array) { + attributeType = AttributeType.STRING_TYPE; + } else { + switch (property.componentType) { + case 'INT8': + case 'UINT8': + case 'INT16': + case 'UINT16': + case 'INT32': + case 'UINT32': + attributeType = AttributeType.SHORT_INT_TYPE; + break; + + case 'FLOAT32': + case 'FLOAT64': + attributeType = AttributeType.DOUBLE_TYPE; + break; + + case 'INT64': + case 'UINT64': + attributeType = AttributeType.STRING_TYPE; + break; + + default: + attributeType = AttributeType.STRING_TYPE; + break; + } + } + return attributeType; +}; diff --git a/modules/tile-converter/src/i3s-converter/helpers/geometry-attributes.ts b/modules/tile-converter/src/i3s-converter/helpers/geometry-attributes.ts index 56ac501f7d..6209cd1d88 100644 --- a/modules/tile-converter/src/i3s-converter/helpers/geometry-attributes.ts +++ b/modules/tile-converter/src/i3s-converter/helpers/geometry-attributes.ts @@ -1,4 +1,9 @@ -import type {GeometryAttributes, ConvertedAttributes, GroupedByFeatureIdAttributes} from '../types'; +import type { + GeometryAttributes, + ConvertedAttributes, + GroupedByFeatureIdAttributes, + GroupedAttributes +} from '../types'; import {concatenateTypedArrays} from '@loaders.gl/loader-utils'; const VALUES_PER_VERTEX = 3; @@ -125,6 +130,11 @@ function makeAttributeObjects(attributes: GeometryAttributes): GroupedByFeatureI let colorsList = new Uint8Array(colors); let texCoordsList = new Float32Array(texCoords); let uvRegionsList = new Uint16Array(uvRegions); + let positionsOffset = 0; + let normalsOffset = 0; + let colorsOffset = 0; + let uvRegionsOffset = 0; + let texCoordsOffset = 0; for (let index = 0; index < featureIds.length; index++) { const startIndex = faceRange[index * 2]; @@ -138,21 +148,21 @@ function makeAttributeObjects(attributes: GeometryAttributes): GroupedByFeatureI groupedData.push({ featureId: featureIds[index], - positions: positionsList.slice(0, positionsCount), - normals: normalsList.slice(0, normalsCount), - colors: colorsList.slice(0, colorsCount), - uvRegions: uvRegionsList.slice(0, uvRegionsCount), - texCoords: texCoordsList.slice(0, texCoordsCount) + positions: positionsList.subarray(positionsOffset, positionsOffset + positionsCount), + normals: normalsList.subarray(normalsOffset, normalsOffset + normalsCount), + colors: colorsList.subarray(colorsOffset, colorsOffset + colorsCount), + uvRegions: uvRegionsList.subarray(uvRegionsOffset, uvRegionsOffset + uvRegionsCount), + texCoords: texCoordsList.subarray(texCoordsOffset, texCoordsOffset + texCoordsCount) }); - positionsList = positionsList.slice(positionsCount); - normalsList = normalsList.slice(normalsCount); - colorsList = colorsList.slice(colorsCount); - uvRegionsList = uvRegionsList.slice(uvRegionsCount); - texCoordsList = texCoordsList.slice(texCoordsCount); + positionsOffset += positionsCount; + normalsOffset += normalsCount; + colorsOffset += colorsCount; + uvRegionsOffset += uvRegionsCount; + texCoordsOffset += texCoordsCount; } - return groupedData.sort((first, second) => first.featureId - second.featureId); + return groupedData; } /** @@ -195,28 +205,32 @@ function getSliceAttributeCount( function unifyObjectsByFeatureId( sortedData: GroupedByFeatureIdAttributes[] ): GroupedByFeatureIdAttributes[] { - const uniqueObjects: GroupedByFeatureIdAttributes[] = []; - - for (let index = 0; index < sortedData.length; index++) { - const currentObject = sortedData[index]; - const existedObject = uniqueObjects.find((obj) => obj.featureId === currentObject.featureId); - - if (existedObject) { - existedObject.positions = concatenateTypedArrays( - existedObject.positions, - currentObject.positions - ); - existedObject.normals = concatenateTypedArrays(existedObject.normals, currentObject.normals); - existedObject.colors = concatenateTypedArrays(existedObject.colors, currentObject.colors); - existedObject.texCoords = concatenateTypedArrays( - existedObject.texCoords, - currentObject.texCoords - ); + const groupedMetadata: { + featureId: number; + attributes: GroupedByFeatureIdAttributes[]; + }[] = []; + for (const data of sortedData) { + const existingObject = groupedMetadata.find((obj) => obj.featureId === data.featureId); + if (existingObject) { + existingObject.attributes.push(data); } else { - uniqueObjects.push(currentObject); + groupedMetadata.push({ + featureId: data.featureId, + attributes: [data] + }); } } + const uniqueObjects: GroupedByFeatureIdAttributes[] = []; + for (const metatada of groupedMetadata) { + const attributes = concatenateAttributes(metatada.attributes); + + uniqueObjects.push({ + featureId: metatada.featureId, + ...attributes + }); + } + return uniqueObjects; } @@ -231,12 +245,6 @@ function groupAttributesAndRangesByFeatureId( ): GeometryAttributes { const firstAttributeObject = unifiedObjects[0]; const featureIds = [firstAttributeObject.featureId || 0]; - - let positions = new Float32Array(firstAttributeObject.positions); - let normals = new Float32Array(firstAttributeObject.normals); - let colors = new Uint8Array(firstAttributeObject.colors); - let uvRegions = new Uint16Array(firstAttributeObject.uvRegions); - let texCoords = new Float32Array(firstAttributeObject.texCoords); const range = [0]; let objIndex = 0; @@ -246,12 +254,6 @@ function groupAttributesAndRangesByFeatureId( const currentAttributesObject = unifiedObjects[index]; featureIds.push(currentAttributesObject.featureId || 0); - positions = concatenateTypedArrays(positions, currentAttributesObject.positions); - normals = concatenateTypedArrays(normals, currentAttributesObject.normals); - colors = concatenateTypedArrays(colors, currentAttributesObject.colors); - uvRegions = concatenateTypedArrays(uvRegions, currentAttributesObject.uvRegions); - texCoords = concatenateTypedArrays(texCoords, currentAttributesObject.texCoords); - const groupedObject = unifiedObjects[objIndex]; range.push(groupedObject.positions.length / POSITIONS_AND_NORMALS_PER_TRIANGLE - 1 + sum); range.push(groupedObject.positions.length / POSITIONS_AND_NORMALS_PER_TRIANGLE + sum); @@ -260,8 +262,43 @@ function groupAttributesAndRangesByFeatureId( objIndex += 1; } - range.push(positions.length / POSITIONS_AND_NORMALS_PER_TRIANGLE - 1); + const attributes = concatenateAttributes(unifiedObjects); + + range.push(attributes.positions.length / POSITIONS_AND_NORMALS_PER_TRIANGLE - 1); const faceRange = new Uint32Array(range); - return {faceRange, featureIds, positions, normals, colors, uvRegions, texCoords, featureCount}; + return {faceRange, featureIds, featureCount, ...attributes}; +} + +/** + * Concatenate attributes typed arrays + * @param attributes - grouped by featureId typed arrays + * @returns - concatenated typed array list + */ +function concatenateAttributes(attributes: GroupedByFeatureIdAttributes[]): GroupedAttributes { + const positionGroups = attributes.map(({positions}) => positions); + const positions = + positionGroups.length > 1 ? concatenateTypedArrays(...positionGroups) : positionGroups[0]; + + const normalGroups = attributes.map(({normals}) => normals); + const normals = + normalGroups.length > 1 ? concatenateTypedArrays(...normalGroups) : normalGroups[0]; + + const colorGroups = attributes.map(({colors}) => colors); + const colors = colorGroups.length > 1 ? concatenateTypedArrays(...colorGroups) : colorGroups[0]; + + const texCoordGroups = attributes.map(({texCoords}) => texCoords); + const texCoords = + texCoordGroups.length > 1 ? concatenateTypedArrays(...texCoordGroups) : texCoordGroups[0]; + + const uvRegionGroups = attributes.map(({uvRegions}) => uvRegions); + const uvRegions = + uvRegionGroups.length > 1 ? concatenateTypedArrays(...uvRegionGroups) : uvRegionGroups[0]; + return { + positions, + normals, + colors, + texCoords, + uvRegions + }; } diff --git a/modules/tile-converter/src/i3s-converter/helpers/geometry-converter.ts b/modules/tile-converter/src/i3s-converter/helpers/geometry-converter.ts index 313f231eba..076c9f0aa6 100644 --- a/modules/tile-converter/src/i3s-converter/helpers/geometry-converter.ts +++ b/modules/tile-converter/src/i3s-converter/helpers/geometry-converter.ts @@ -6,7 +6,12 @@ import type { GLTFMeshPrimitivePostprocessed, GLTFMeshPostprocessed, GLTFTexturePostprocessed, - GLTF_EXT_feature_metadata_GLTF + GLTF_EXT_feature_metadata_GLTF, + GLTF_EXT_feature_metadata_FeatureTable, + GLTF_EXT_feature_metadata_FeatureTexture, + GLTF_EXT_structural_metadata_GLTF, + GLTF_EXT_structural_metadata_PropertyTable, + GLTF_EXT_structural_metadata_PropertyTexture } from '@loaders.gl/gltf'; import {Vector3, Matrix4, Vector4} from '@math.gl/core'; @@ -34,13 +39,8 @@ import { } from '@loaders.gl/i3s'; import {NumberArray, TypedArray} from '@loaders.gl/loader-utils'; import {Geoid} from '@math.gl/geoid'; -/** Usage of worker here brings more overhead than advantage */ -import { - B3DMAttributesData /*, transformI3SAttributesOnWorker*/, - TextureImageProperties -} from '../../i3s-attributes-worker'; import {prepareDataForAttributesConversion} from './gltf-attributes'; -import {handleBatchIdsExtensions} from './batch-ids-extensions'; +import {getTextureByMetadataClass, handleBatchIdsExtensions} from './batch-ids-extensions'; import {checkPropertiesLength, flattenPropertyTableByFeatureIds} from './feature-attributes'; import {GL} from '@loaders.gl/math'; @@ -49,10 +49,12 @@ import {GL} from '@loaders.gl/math'; So the following import is replaced with the local import import type {TypedArrayConstructor} from '@math.gl/types'; */ -import type {TypedArrayConstructor} from '../types'; +import type {GLTFAttributesData, TextureImageProperties, TypedArrayConstructor} from '../types'; import {generateSyntheticIndices} from '../../lib/utils/geometry-utils'; import {BoundingSphere, OrientedBoundingBox} from '@math.gl/culling'; +import {EXT_FEATURE_METADATA, EXT_STRUCTURAL_METADATA} from '@loaders.gl/gltf'; + // Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/pbrMetallicRoughness.cmn.md const DEFAULT_ROUGHNESS_FACTOR = 1; const DEFAULT_METALLIC_FACTOR = 1; @@ -72,9 +74,6 @@ const OBJECT_ID_TYPE = 'Oid32'; */ const BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES = ['CUSTOM_ATTRIBUTE_2', '_BATCHID', 'BATCHID']; -const EXT_FEATURE_METADATA = 'EXT_feature_metadata'; -const EXT_MESH_FEATURES = 'EXT_mesh_features'; - let scratchVector = new Vector3(); /** @@ -92,7 +91,8 @@ let scratchVector = new Vector3(); * @param generateBoundingVolumes - is converter should create accurate bounding voulmes from geometry attributes * @param shouldMergeMaterials - Try to merge similar materials to be able to merge meshes into one node * @param geoidHeightModel - model to convert elevation from elipsoidal to geoid - * @param workerSource - source code of used workers + * @param libraries - dynamicaly loaded 3rd-party libraries + * @param metadataClass `- user selected feature metadata class name` * @returns Array of node resources to create one or more i3s nodes */ export default async function convertB3dmToI3sGeometry( @@ -107,7 +107,8 @@ export default async function convertB3dmToI3sGeometry( generateBoundingVolumes: boolean, shouldMergeMaterials: boolean, geoidHeightModel: Geoid, - workerSource: {[key: string]: string} + libraries: Record, + metadataClass?: string ): Promise { const useCartesianPositions = generateBoundingVolumes; const materialAndTextureList: I3SMaterialWithTexture[] = await convertMaterials( @@ -120,10 +121,12 @@ export default async function convertB3dmToI3sGeometry( tileTransform, tileBoundingVolume ); + const featureTexture = getTextureByMetadataClass(tileContent, metadataClass); const convertedAttributesMap: Map = await convertAttributes( dataForAttributesConversion, materialAndTextureList, - useCartesianPositions + useCartesianPositions, + featureTexture ); /** Usage of worker here brings more overhead than advantage */ // const convertedAttributesMap: Map = @@ -161,7 +164,7 @@ export default async function convertB3dmToI3sGeometry( propertyTable, attributeStorageInfo, draco, - workerSource + libraries }) ); } @@ -213,7 +216,7 @@ function _generateBoundingVolumesFromGeometry( * @param params.propertyTable - batch table (corresponding to feature attributes data) * @param params.attributeStorageInfo - attributes metadata from 3DSceneLayer json * @param params.draco - is converter should create draco compressed geometry - * @param params.workerSource - source code of used workers + * @param libraries - dynamicaly loaded 3rd-party libraries * @returns Array of I3S node resources */ async function _makeNodeResources({ @@ -226,7 +229,7 @@ async function _makeNodeResources({ propertyTable, attributeStorageInfo, draco, - workerSource + libraries }: { convertedAttributes: ConvertedAttributes; material: I3SMaterialDefinition; @@ -237,19 +240,25 @@ async function _makeNodeResources({ propertyTable: FeatureTableJson | null; attributeStorageInfo?: AttributeStorageInfo[]; draco: boolean; - workerSource: {[key: string]: string}; + libraries: Record; }): Promise { const boundingVolumes = convertedAttributes.boundingVolumes; const vertexCount = convertedAttributes.positions.length / VALUES_PER_VERTEX; const {faceRange, featureIds, positions, normals, colors, uvRegions, texCoords, featureCount} = generateAttributes(convertedAttributes); - if (tileContent.batchTableJson) { - makeFeatureIdsUnique( + let featureIdsMap: Record = {}; + if (propertyTable) { + /** + * 3DTiles has featureIndices unique only for one tile. + * In I3S featureIds are unique layer-wide. We create featureIds from all feature properties. + * If 3DTiles features has equal set of properties they are considered as same feature in I3S. + */ + featureIdsMap = makeFeatureIdsUnique( featureIds, convertedAttributes.featureIndices, featuresHashArray, - tileContent.batchTableJson + propertyTable ); } @@ -282,7 +291,7 @@ async function _makeNodeResources({ featureIds, faceRange }, - workerSource.draco + libraries ) : null; @@ -291,6 +300,7 @@ async function _makeNodeResources({ if (attributeStorageInfo && propertyTable) { attributes = convertPropertyTableToAttributeBuffers( featureIds, + featureIdsMap, propertyTable, attributeStorageInfo ); @@ -317,12 +327,14 @@ async function _makeNodeResources({ * @param materialAndTextureList - array of data about materials and textures of the content * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets. * Cartesian coordinates will be required for creating bounding voulmest from geometry positions + * @param featureTexture - feature texture key * @returns map of converted geometry attributes */ export async function convertAttributes( - attributesData: B3DMAttributesData, + attributesData: GLTFAttributesData, materialAndTextureList: I3SMaterialWithTexture[], - useCartesianPositions: boolean + useCartesianPositions: boolean, + featureTexture: string | null ): Promise> { const {nodes, images, cartographicOrigin, cartesianModelMatrix} = attributesData; const attributesMap = new Map(); @@ -350,7 +362,9 @@ export async function convertAttributes( cartographicOrigin, cartesianModelMatrix, attributesMap, - useCartesianPositions + useCartesianPositions, + undefined, + featureTexture ); for (const attrKey of attributesMap.keys()) { @@ -374,7 +388,7 @@ export async function convertAttributes( } /** - * Gltf has hierarchical structure of nodes. This function converts nodes starting from those which are in gltf scene object. + * glTF has hierarchical structure of nodes. This function converts nodes starting from those which are in gltf scene object. * The goal is applying tranformation matrix for all children. Functions "convertNodes" and "convertNode" work together recursively. * @param nodes - gltf nodes array * @param images - gltf images array @@ -384,6 +398,7 @@ export async function convertAttributes( * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets. * Cartesian coordinates will be required for creating bounding voulmest from geometry positions * @param matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices + * @param featureTexture - feature texture key * @returns {void} */ function convertNodes( @@ -393,7 +408,8 @@ function convertNodes( cartesianModelMatrix: Matrix4, attributesMap: Map, useCartesianPositions: boolean, - matrix: Matrix4 = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) + matrix: Matrix4 = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), + featureTexture: string | null ) { if (nodes) { for (const node of nodes) { @@ -404,7 +420,8 @@ function convertNodes( cartesianModelMatrix, attributesMap, useCartesianPositions, - matrix + matrix, + featureTexture ); } } @@ -446,11 +463,12 @@ function getCompositeTransformationMatrix(node: GLTFNodePostprocessed, matrix: M * @param images - gltf images array * @param cartographicOrigin - cartographic origin of bounding volume * @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic - * @param {Map} attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of + * @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of * attributes * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets. * Cartesian coordinates will be required for creating bounding voulmest from geometry positions - * @param {Matrix4} matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices + * @param matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices + * @param featureTexture - feature texture key */ function convertNode( node: GLTFNodePostprocessed, @@ -459,7 +477,8 @@ function convertNode( cartesianModelMatrix: Matrix4, attributesMap: Map, useCartesianPositions, - matrix = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) + matrix = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), + featureTexture: string | null ) { const transformationMatrix = getCompositeTransformationMatrix(node, matrix); @@ -473,7 +492,8 @@ function convertNode( cartesianModelMatrix, attributesMap, useCartesianPositions, - transformationMatrix + transformationMatrix, + featureTexture ); } @@ -484,7 +504,8 @@ function convertNode( cartesianModelMatrix, attributesMap, useCartesianPositions, - transformationMatrix + transformationMatrix, + featureTexture ); } @@ -496,12 +517,12 @@ function convertNode( * @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic * @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of * attributes - * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets. + * @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets. * Cartesian coordinates will be required for creating bounding voulmest from geometry positions * @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of * attributes - - * @param {Matrix4} matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices + * @param matrix - transformation matrix - cumulative transformation matrix formed from all parent node matrices + * @param featureTexture - feature texture key */ function convertMesh( mesh: GLTFMeshPostprocessed, @@ -510,7 +531,8 @@ function convertMesh( cartesianModelMatrix: Matrix4, attributesMap: Map, useCartesianPositions = false, - matrix = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) + matrix = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), + featureTexture: string | null ) { for (const primitive of mesh.primitives) { let outputAttributes: ConvertedAttributes | null | undefined = null; @@ -581,7 +603,7 @@ function convertMesh( outputAttributes.featureIndicesGroups = outputAttributes.featureIndicesGroups || []; outputAttributes.featureIndicesGroups.push( - flattenBatchIds(getBatchIds(attributes, primitive, images), indices) + flattenBatchIds(getBatchIds(attributes, primitive, images, featureTexture), indices) ); } } @@ -808,15 +830,23 @@ function flattenBatchIds(batchedIds: NumberArray, indices: TypedArray): number[] * @param attributes - gltf accessors * @param primitive - gltf primitive data * @param images - gltf texture images + * @param featureTexture - feature texture key + * @return batch IDs */ function getBatchIds( attributes: { [key: string]: GLTFAccessorPostprocessed; }, primitive: GLTFMeshPrimitivePostprocessed, - images: (TextureImageProperties | null)[] + images: (TextureImageProperties | null)[], + featureTexture: string | null ): NumberArray { - const batchIds: NumberArray = handleBatchIdsExtensions(attributes, primitive, images); + const batchIds: NumberArray = handleBatchIdsExtensions( + attributes, + primitive, + images, + featureTexture + ); if (batchIds.length) { return batchIds; @@ -1026,7 +1056,7 @@ function convertMaterial(sourceMaterial: GLTFMaterialPostprocessed): I3SMaterial /** * Converts from `alphaMode` material property from GLTF to I3S format - * @param gltfAlphaMode Gltf material `alphaMode` property + * @param gltfAlphaMode glTF material `alphaMode` property * @returns I3SMaterialDefinition.alphaMode property */ function convertAlphaMode( @@ -1232,17 +1262,18 @@ function generateImageId(texture: GLTFTexturePostprocessed, nodeId: number) { * @param featureIndices * @param featuresHashArray * @param batchTable - * @returns {void} + * @returns propertyTable indices to map featureIds */ function makeFeatureIdsUnique( featureIds: number[], featureIndices: number[], featuresHashArray: string[], batchTable: {[key: string]: any} -) { +): Record { const replaceMap = getFeaturesReplaceMap(featureIds, batchTable, featuresHashArray); replaceIndicesByUnique(featureIndices, replaceMap); replaceIndicesByUnique(featureIds, replaceMap); + return replaceMap; } /** @@ -1256,8 +1287,8 @@ function getFeaturesReplaceMap( featureIds: any[], batchTable: object, featuresHashArray: any[] -): Record { - const featureMap: Record = {}; +): Record { + const featureMap: Record = {}; for (let index = 0; index < featureIds.length; index++) { const oldFeatureId = featureIds[index]; @@ -1309,7 +1340,7 @@ function getOrCreateUniqueFeatureId( * @param featureMap * @returns */ -function replaceIndicesByUnique(indicesArray: any[], featureMap: Record) { +function replaceIndicesByUnique(indicesArray: number[], featureMap: Record) { for (let index = 0; index < indicesArray.length; index++) { indicesArray[index] = featureMap[indicesArray[index]]; } @@ -1324,6 +1355,7 @@ function replaceIndicesByUnique(indicesArray: any[], featureMap: Record, propertyTable: FeatureTableJson, attributeStorageInfo: AttributeStorageInfo[] ): any[] { @@ -1331,7 +1363,7 @@ function convertPropertyTableToAttributeBuffers( const needFlattenPropertyTable = checkPropertiesLength(featureIds, propertyTable); const properties = needFlattenPropertyTable - ? flattenPropertyTableByFeatureIds(featureIds, propertyTable) + ? flattenPropertyTableByFeatureIds(featureIdsMap, propertyTable) : propertyTable; const propertyTableWithObjectIds = { @@ -1341,10 +1373,12 @@ function convertPropertyTableToAttributeBuffers( for (const propertyName in propertyTableWithObjectIds) { const type = getAttributeType(propertyName, attributeStorageInfo); - const value = propertyTableWithObjectIds[propertyName]; - const attributeBuffer = generateAttributeBuffer(type, value); + if (type) { + const value = propertyTableWithObjectIds[propertyName]; + const attributeBuffer = generateAttributeBuffer(type, value); - attributeBuffers.push(attributeBuffer); + attributeBuffers.push(attributeBuffer); + } } return attributeBuffers; @@ -1384,6 +1418,20 @@ function generateAttributeBuffer(type: string, value: any): ArrayBuffer { */ function getAttributeType(key: string, attributeStorageInfo: any[]): string { const attribute = attributeStorageInfo.find((attr) => attr.name === key); + if (!attribute) { + console.error( + `attribute is null, key=${key}, attributeStorageInfo=${JSON.stringify( + attributeStorageInfo, + null, + 2 + )}` + ); + return ''; + } + if (!attribute.attributeValues) { + console.error(`attributeValues is null, attribute=${attribute}`); + return ''; + } return attribute.attributeValues.valueType; } @@ -1456,17 +1504,17 @@ function generateBigUint64Array(featureIds: any[]): BigUint64Array { /** * Generates draco compressed geometry - * @param {Number} vertexCount - * @param {Object} convertedAttributes - get rid of this argument here - * @param {Object} attributes - geometry attributes to compress - * @param {string} dracoWorkerSoure - draco worker source code - * @returns {Promise} - COmpressed geometry. + * @param vertexCount + * @param convertedAttributes - get rid of this argument here + * @param attributes - geometry attributes to compress + * @param libraries - dynamicaly loaded 3rd-party libraries + * @returns - Compressed geometry. */ async function generateCompressedGeometry( vertexCount: number, convertedAttributes: Record, attributes: Record, - dracoWorkerSoure: string + libraries: Record ): Promise { const {positions, normals, texCoords, colors, uvRegions, featureIds, faceRange} = attributes; const indices = new Uint32Array(vertexCount); @@ -1513,16 +1561,26 @@ async function generateCompressedGeometry( }; } - return encode({attributes: compressedAttributes, indices}, DracoWriterWorker, { - ...DracoWriterWorker.options, - source: dracoWorkerSoure, - reuseWorkers: true, - _nodeWorkers: true, - draco: { - method: 'MESH_SEQUENTIAL_ENCODING', - attributesMetadata + return encode( + {attributes: compressedAttributes, indices}, + // @ts-expect-error if encoded supports worker writer, we should update its type signature + DracoWriterWorker, + { + ...DracoWriterWorker.options, + reuseWorkers: true, + _nodeWorkers: true, + modules: libraries, + useLocalLibraries: true, + draco: { + method: 'MESH_SEQUENTIAL_ENCODING', + attributesMetadata + }, + ['draco-writer']: { + // We need to load local fs workers because nodejs can't load workers from the Internet + workerUrl: './modules/draco/dist/draco-writer-worker-node.js' + } } - }); + ); } /** @@ -1555,14 +1613,18 @@ function generateFeatureIndexAttribute( * Find property table in tile * For example it can be batchTable for b3dm files or property table in gLTF extension. * @param tileContent - 3DTiles tile content - * @return batch table from b3dm / feature properties from EXT_FEATURE_METADATA + * @param metadataClass - user selected feature metadata class name + * @return batch table from b3dm / feature properties from EXT_FEATURE_METADATA or EXT_STRUCTURAL_METADATA. */ -export function getPropertyTable(tileContent: Tiles3DTileContent | null): FeatureTableJson | null { +export function getPropertyTable( + tileContent: Tiles3DTileContent | null, + metadataClass?: string +): FeatureTableJson | null { if (!tileContent) { return null; } - - const batchTableJson = tileContent?.batchTableJson; + let propertyTable: FeatureTableJson | null; + const batchTableJson = tileContent.batchTableJson; if (batchTableJson) { return batchTableJson; @@ -1571,27 +1633,127 @@ export function getPropertyTable(tileContent: Tiles3DTileContent | null): Featur const {extensionName, extension} = getPropertyTableExtension(tileContent); switch (extensionName) { - case EXT_MESH_FEATURES: { - console.warn('The I3S converter does not yet support the EXT_mesh_features extension'); - return null; + case EXT_STRUCTURAL_METADATA: { + propertyTable = getPropertyTableFromExtStructuralMetadata( + extension as GLTF_EXT_structural_metadata_GLTF, + metadataClass + ); + return propertyTable; } case EXT_FEATURE_METADATA: { - return getPropertyTableFromExtFeatureMetadata(extension as GLTF_EXT_feature_metadata_GLTF); + propertyTable = getPropertyTableFromExtFeatureMetadata( + extension as GLTF_EXT_feature_metadata_GLTF, + metadataClass + ); + return propertyTable; } default: return null; } } +/** + * Handles EXT_structural_metadata to get property table. + * @param extension - Global level of EXT_STRUCTURAL_METADATA extension. + * @param metadataClass - User selected feature metadata class name. + * @returns {FeatureTableJson | null} Property table or null if the extension can't be handled properly. + */ +function getPropertyTableFromExtStructuralMetadata( + extension: GLTF_EXT_structural_metadata_GLTF, + metadataClass?: string +): FeatureTableJson | null { + /** + * Note, 3dTiles is able to have multiple featureId attributes and multiple feature tables. + * In I3S we should decide which featureIds attribute will be passed to geometry data. + * So, we take only the feature table / feature texture to generate attributes storage info object. + * If the user has selected the metadataClass, the table with the corresponding class will be used, + * or just the first one otherwise. + */ + if (extension.propertyTables) { + for (const propertyTable of extension.propertyTables) { + if (propertyTable.class === metadataClass || !metadataClass) { + return getPropertyData(propertyTable); + } + } + } + + if (extension.propertyTextures) { + for (const propertyTexture of extension.propertyTextures) { + if (propertyTexture.class === metadataClass || !metadataClass) { + return getPropertyData(propertyTexture); + } + } + } + + return null; +} + +/** + * Handles EXT_feature_metadata to get property table. + * @param extension - Global level of EXT_FEATURE_METADATA extension. + * @param metadataClass - User selected feature metadata class name. + * @returns {FeatureTableJson | null} Property table or null if the extension can't be handled properly. + */ +function getPropertyTableFromExtFeatureMetadata( + extension: GLTF_EXT_feature_metadata_GLTF, + metadataClass?: string +): FeatureTableJson | null { + /** + * Note, 3dTiles is able to have multiple featureId attributes and multiple feature tables. + * In I3S we should decide which featureIds attribute will be passed to geometry data. + * So, we take only the feature table / feature texture to generate attributes storage info object. + * If the user has selected the metadataClass, the table with the corresponding class will be used, + * or just the first one otherwise. + */ + if (extension.featureTables) { + for (const featureTableName in extension.featureTables) { + const featureTable = extension.featureTables[featureTableName]; + if (featureTable.class === metadataClass || !metadataClass) { + return getPropertyData(featureTable); + } + } + } + + if (extension.featureTextures) { + for (const featureTextureName in extension.featureTextures) { + const featureTexture = extension.featureTextures[featureTextureName]; + if (featureTexture.class === metadataClass || !metadataClass) { + return getPropertyData(featureTexture); + } + } + } + + return null; +} + +/** + * Gets data from Property Table or Property Texture + * @param featureObject - property table or texture from the extension + * @returns Table containing property data + */ +function getPropertyData< + Type extends + | GLTF_EXT_structural_metadata_PropertyTable + | GLTF_EXT_structural_metadata_PropertyTexture + | GLTF_EXT_feature_metadata_FeatureTable + | GLTF_EXT_feature_metadata_FeatureTexture +>(featureObject: Type) { + const propertyTableWithData = {}; + for (const propertyName in featureObject.properties) { + propertyTableWithData[propertyName] = featureObject.properties[propertyName].data; + } + return propertyTableWithData; +} + /** * Check extensions which can be with property table inside. * @param tileContent - 3DTiles tile content */ function getPropertyTableExtension(tileContent: Tiles3DTileContent): { extensionName: null | string; - extension: string | GLTF_EXT_feature_metadata_GLTF | null; + extension: string | GLTF_EXT_feature_metadata_GLTF | GLTF_EXT_structural_metadata_GLTF | null; } { - const extensionsWithPropertyTables = [EXT_FEATURE_METADATA, EXT_MESH_FEATURES]; + const extensionsWithPropertyTables = [EXT_FEATURE_METADATA, EXT_STRUCTURAL_METADATA]; const extensionsUsed = tileContent?.gltf?.extensionsUsed; if (!extensionsUsed) { @@ -1602,6 +1764,12 @@ function getPropertyTableExtension(tileContent: Tiles3DTileContent): { for (const extensionItem of tileContent?.gltf?.extensionsUsed || []) { if (extensionsWithPropertyTables.includes(extensionItem)) { extensionName = extensionItem; + /* + It returns the first extension containing the property table. + We assume that there can be only one extension containing the property table: + either EXT_FEATURE_METADATA, which is a depricated extension, + or EXT_STRUCTURAL_METADATA. + */ break; } } @@ -1612,61 +1780,8 @@ function getPropertyTableExtension(tileContent: Tiles3DTileContent): { const extension = tileContent?.gltf?.extensions?.[extensionName] as | string // EXT_mesh_features doesn't have global metadata - | GLTF_EXT_feature_metadata_GLTF; + | GLTF_EXT_feature_metadata_GLTF + | GLTF_EXT_structural_metadata_GLTF; return {extensionName, extension}; } - -/** - * Handle EXT_feature_metadata to get property table - * @param extension - */ -function getPropertyTableFromExtFeatureMetadata( - extension: GLTF_EXT_feature_metadata_GLTF -): FeatureTableJson | null { - if (extension?.featureTables) { - /** - * Take only first feature table to generate attributes storage info object. - * TODO: Think about getting data from all feature tables? - * It can be tricky just because 3dTiles is able to have multiple featureId attributes and multiple feature tables. - * In I3S we should decide which featureIds attribute will be passed to geometry data. - */ - const firstFeatureTableName = Object.keys(extension.featureTables)?.[0]; - - if (firstFeatureTableName) { - const featureTable = extension?.featureTables[firstFeatureTableName]; - const propertyTable = {}; - - for (const propertyName in featureTable.properties) { - propertyTable[propertyName] = featureTable.properties[propertyName].data; - } - - return propertyTable; - } - } - - if (extension?.featureTextures) { - /** - * Take only first feature texture to generate attributes storage info object. - * TODO: Think about getting data from all feature textures? - * It can be tricky just because 3dTiles is able to have multiple featureTextures. - * In I3S we should decide which featureTextures will be passed to geometry data. - */ - const firstTextureName = Object.keys(extension.featureTextures)?.[0]; - if (firstTextureName) { - const featureTable = extension?.featureTextures[firstTextureName]; - const propertyTable = {}; - - for (const propertyName in featureTable.properties) { - propertyTable[propertyName] = featureTable.properties[propertyName].data; - } - - return propertyTable; - } - } - - console.warn( - "The I3S converter couldn't handle EXT_feature_metadata extension: There is neither featureTables, no featureTextures in the extension." - ); - return null; -} diff --git a/modules/tile-converter/src/i3s-converter/helpers/gltf-attributes.ts b/modules/tile-converter/src/i3s-converter/helpers/gltf-attributes.ts index eb83890cb1..0c2af1d9c7 100644 --- a/modules/tile-converter/src/i3s-converter/helpers/gltf-attributes.ts +++ b/modules/tile-converter/src/i3s-converter/helpers/gltf-attributes.ts @@ -1,9 +1,9 @@ import type {Tiles3DTileContent} from '@loaders.gl/3d-tiles'; import type {GLTFAccessorPostprocessed, GLTFNodePostprocessed} from '@loaders.gl/gltf'; -import type {B3DMAttributesData} from '../../i3s-attributes-worker'; import {Matrix4, TypedArray, Vector3} from '@math.gl/core'; import {BoundingSphere, OrientedBoundingBox} from '@math.gl/culling'; import {Ellipsoid} from '@math.gl/geospatial'; +import {GLTFAttributesData} from '../types'; /** * Prepare attributes for conversion to avoid binary data breaking in worker thread. @@ -17,7 +17,7 @@ export function prepareDataForAttributesConversion( tileContent: Tiles3DTileContent, tileTransform: Matrix4, boundingVolume: OrientedBoundingBox | BoundingSphere -): B3DMAttributesData { +): GLTFAttributesData { let nodes = tileContent.gltf?.scene?.nodes || tileContent.gltf?.scenes?.[0]?.nodes || diff --git a/modules/tile-converter/src/i3s-converter/helpers/load-3d-tiles.ts b/modules/tile-converter/src/i3s-converter/helpers/load-3d-tiles.ts index b46191ef7d..39457ed018 100644 --- a/modules/tile-converter/src/i3s-converter/helpers/load-3d-tiles.ts +++ b/modules/tile-converter/src/i3s-converter/helpers/load-3d-tiles.ts @@ -4,7 +4,8 @@ import type { Tiles3DTileJSONPostprocessed, Tiles3DTilesetJSONPostprocessed } from '@loaders.gl/3d-tiles'; -import {load} from '@loaders.gl/core'; +import {Tiles3DArchiveFileSystem} from '@loaders.gl/3d-tiles'; +import {LoaderWithParser, load} from '@loaders.gl/core'; /** * Load nested 3DTiles tileset. If the sourceTile is not nested tileset - do nothing @@ -18,7 +19,7 @@ export const loadNestedTileset = async ( sourceTile: Tiles3DTileJSONPostprocessed, tilesetLoadOptions: Tiles3DLoaderOptions ): Promise => { - const isTileset = sourceTile.type === 'json'; + const isTileset = isNestedTileset(sourceTile); if (!sourceTileset || !sourceTile.contentUrl || !isTileset) { return; } @@ -30,7 +31,11 @@ export const loadNestedTileset = async ( assetGltfUpAxis: (sourceTileset.asset && sourceTileset.asset.gltfUpAxis) || 'Y' } }; - const tileContent = await load(sourceTile.contentUrl, sourceTileset.loader, loadOptions); + const tileContent = await loadFromArchive( + sourceTile.contentUrl, + sourceTileset.loader, + loadOptions + ); if (tileContent.root) { sourceTile.children = [tileContent.root]; @@ -49,7 +54,7 @@ export const loadTile3DContent = async ( sourceTile: Tiles3DTileJSONPostprocessed, tilesetLoadOptions: Tiles3DLoaderOptions ): Promise => { - const isTileset = sourceTile.type === 'json'; + const isTileset = isNestedTileset(sourceTile); if (!sourceTileset || !sourceTile.contentUrl || isTileset) { return null; } @@ -57,12 +62,64 @@ export const loadTile3DContent = async ( const loadOptions = { ...tilesetLoadOptions, [sourceTileset.loader.id]: { + // @ts-ignore ...(tilesetLoadOptions[sourceTileset.loader.id] || {}), isTileset, assetGltfUpAxis: (sourceTileset.asset && sourceTileset.asset.gltfUpAxis) || 'Y' } }; - const tileContent = await load(sourceTile.contentUrl, sourceTileset.loader, loadOptions); + const tileContent = await loadFromArchive( + sourceTile.contentUrl, + sourceTileset.loader, + loadOptions + ); return tileContent; }; + +/** + * Load a resource with load options and .3tz format support + * @param url - resource URL + * @param loader - loader to parse data (Tiles3DLoader / CesiumIonLoader) + * @param loadOptions - 3d-tiles loader options + * @returns 3d-tiles resource + */ +export async function loadFromArchive( + url: string, + loader: LoaderWithParser, + loadOptions: Tiles3DLoaderOptions +) { + const tz3UrlParts = url.split('.3tz'); + let filename: string | null; + // No '.3tz'. The file will be loaded with global fetch function + if (tz3UrlParts.length === 1) { + filename = null; + } else if (tz3UrlParts.length === 2) { + filename = tz3UrlParts[1].slice(1); + if (filename === '') { + filename = 'tileset.json'; + } + } else { + throw new Error('Unexpected URL format'); + } + if (filename) { + const tz3Path = `${tz3UrlParts[0]}.3tz`; + const fileSystem = new Tiles3DArchiveFileSystem(tz3Path); + const content = await load(filename, loader, { + ...loadOptions, + fetch: fileSystem.fetch.bind(fileSystem) + }); + await fileSystem.destroy(); + return content; + } + return await load(url, loader, loadOptions); +} + +/** + * Check if tile is nested tileset + * @param tile - 3DTiles header data + * @returns true if tile is nested tileset + */ +export function isNestedTileset(tile: Tiles3DTileJSONPostprocessed) { + return tile?.type === 'json' || tile?.type === '3tz'; +} diff --git a/modules/tile-converter/src/i3s-converter/helpers/node-index-document.ts b/modules/tile-converter/src/i3s-converter/helpers/node-index-document.ts index d5c069a9c0..6a0a47d775 100644 --- a/modules/tile-converter/src/i3s-converter/helpers/node-index-document.ts +++ b/modules/tile-converter/src/i3s-converter/helpers/node-index-document.ts @@ -323,7 +323,11 @@ export class NodeIndexDocument { parentNode.converter.layers0?.attributeStorageInfo?.length ) { node.attributeData = []; - for (let index = 0; index < attributes.length; index++) { + const minimumLength = + attributes.length < parentNode.converter.layers0.attributeStorageInfo.length + ? attributes.length + : parentNode.converter.layers0.attributeStorageInfo.length; + for (let index = 0; index < minimumLength; index++) { const folderName = parentNode.converter.layers0.attributeStorageInfo[index].key; node.attributeData.push({href: `./attributes/${folderName}/0`}); } diff --git a/modules/tile-converter/src/i3s-converter/helpers/preprocess-3d-tiles.ts b/modules/tile-converter/src/i3s-converter/helpers/preprocess-3d-tiles.ts index 11730e3922..bc4f96216a 100644 --- a/modules/tile-converter/src/i3s-converter/helpers/preprocess-3d-tiles.ts +++ b/modules/tile-converter/src/i3s-converter/helpers/preprocess-3d-tiles.ts @@ -1,20 +1,27 @@ import {Tiles3DTileContent} from '@loaders.gl/3d-tiles'; -import {GltfPrimitiveModeString, PreprocessData} from '../types'; -import {GLTF, GLTFLoader} from '@loaders.gl/gltf'; +import {GLTFPrimitiveModeString, PreprocessData} from '../types'; +import { + EXT_STRUCTURAL_METADATA, + GLTF, + GLTFLoader, + GLTF_EXT_feature_metadata_GLTF, + GLTF_EXT_structural_metadata_GLTF +} from '@loaders.gl/gltf'; import {parse} from '@loaders.gl/core'; +import {EXT_FEATURE_METADATA} from '@loaders.gl/gltf'; /** * glTF primitive modes * @see https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#_mesh_primitive_mode */ export const GLTF_PRIMITIVE_MODES = [ - GltfPrimitiveModeString.POINTS, // 0 - GltfPrimitiveModeString.LINES, // 1 - GltfPrimitiveModeString.LINE_LOOP, // 2 - GltfPrimitiveModeString.LINE_STRIP, // 3 - GltfPrimitiveModeString.TRIANGLES, // 4 - GltfPrimitiveModeString.TRIANGLE_STRIP, // 5 - GltfPrimitiveModeString.TRIANGLE_FAN // 6 + GLTFPrimitiveModeString.POINTS, // 0 + GLTFPrimitiveModeString.LINES, // 1 + GLTFPrimitiveModeString.LINE_LOOP, // 2 + GLTFPrimitiveModeString.LINE_STRIP, // 3 + GLTFPrimitiveModeString.TRIANGLES, // 4 + GLTFPrimitiveModeString.TRIANGLE_STRIP, // 5 + GLTFPrimitiveModeString.TRIANGLE_FAN // 6 ]; /** @@ -26,11 +33,12 @@ export const GLTF_PRIMITIVE_MODES = [ export const analyzeTileContent = async ( tileContent: Tiles3DTileContent | null ): Promise => { - const result: PreprocessData = { - meshTopologyTypes: new Set() + const defaultResult = { + meshTopologyTypes: new Set(), + metadataClasses: new Set() }; if (!tileContent?.gltfArrayBuffer) { - return result; + return defaultResult; } const gltfData = await parse(tileContent.gltfArrayBuffer, GLTFLoader, { @@ -39,11 +47,14 @@ export const analyzeTileContent = async ( const gltf = gltfData.json; if (!gltf) { - return result; + return defaultResult; } - const meshTypes = getMeshTypesFromGltf(gltf); - result.meshTopologyTypes = meshTypes; - return result; + const meshTopologyTypes = getMeshTypesFromGLTF(gltf); + const metadataClasses = getMetadataClassesFromGLTF(gltf); + return { + meshTopologyTypes, + metadataClasses + }; }; /** @@ -51,8 +62,8 @@ export const analyzeTileContent = async ( * @param gltfJson - JSON part of GLB content * @returns array of mesh types found */ -const getMeshTypesFromGltf = (gltfJson: GLTF): Set => { - const result: Set = new Set(); +const getMeshTypesFromGLTF = (gltfJson: GLTF): Set => { + const result: Set = new Set(); for (const mesh of gltfJson.meshes || []) { for (const primitive of mesh.primitives) { let {mode} = primitive; @@ -65,6 +76,41 @@ const getMeshTypesFromGltf = (gltfJson: GLTF): Set => { return result; }; +/** + * Get feature metadata classes from glTF + * The tileset might contain multiple metadata classes provided by EXT_feature_metadata and EXT_structural_metadata extensions. + * Every class is a set of properties. But I3S can consume only one set of properties. + * On the pre-process we collect all classes from the tileset in order to show the prompt to select one class for conversion to I3S. + * @param gltfJson - JSON part of GLB content + * @returns array of classes + */ +const getMetadataClassesFromGLTF = (gltfJson: GLTF): Set => { + const result: Set = new Set(); + + // Try to parse from EXT_feature_metadata + const extFeatureMetadataClasses = ( + gltfJson.extensions?.[EXT_FEATURE_METADATA] as GLTF_EXT_feature_metadata_GLTF + )?.schema?.classes; + + if (extFeatureMetadataClasses) { + for (const classKey of Object.keys(extFeatureMetadataClasses)) { + result.add(classKey); + } + } + + // Try to parse from EXT_structural_metadata + const extStructuralMetadataClasses = ( + gltfJson.extensions?.[EXT_STRUCTURAL_METADATA] as GLTF_EXT_structural_metadata_GLTF + )?.schema?.classes; + if (extStructuralMetadataClasses) { + for (const classKey of Object.keys(extStructuralMetadataClasses)) { + result.add(classKey); + } + } + + return result; +}; + /** * Merge object2 into object1 * @param object1 @@ -76,4 +122,9 @@ export const mergePreprocessData = (object1: PreprocessData, object2: Preprocess for (const type of object2.meshTopologyTypes) { object1.meshTopologyTypes.add(type); } + + // Merge feature metadata classes + for (const metadataClass of object2.metadataClasses) { + object1.metadataClasses.add(metadataClass); + } }; diff --git a/modules/tile-converter/src/i3s-converter/helpers/progress.ts b/modules/tile-converter/src/i3s-converter/helpers/progress.ts new file mode 100644 index 0000000000..6845160c09 --- /dev/null +++ b/modules/tile-converter/src/i3s-converter/helpers/progress.ts @@ -0,0 +1,166 @@ +import process from 'process'; +import {timeConverter} from '../../lib/utils/statistic-utills'; + +/** Defines a threshold that is used to check if the process velocity can be consifered trust. */ +const THRESHOLD_DEFAULT = 0.2; + +/** + * Implements methods to keep track on the progress of a long process. + */ +export class Progress { + /** Total amount of work, e.g. number of files to save or number of bytes to send */ + private _stepsTotal: number = 0; + /** Amount of work already done */ + private _stepsDone: number = 0; + /** Time in milli-seconds when the process started */ + private startTime: number = 0; + /** Time in milli-seconds when the process stopped */ + private stopTime: number = 0; + /** Time in milli-seconds when stepsDone was updated */ + private timeOfUpdatingStepsDone: number = 0; + /** Time in milli-seconds spent for performing one step*/ + private milliSecForOneStep: number = 0; + private trust: boolean = false; + /** + * The number of digits to appear after decimal point in the string representation of the count of steps already done. + * It's calculated based on the total count of steps. + */ + private numberOfDigitsInPercentage: number = 0; + /** Defines a threshold that is used to check if the process velocity can be consifered trust. */ + private threshold: number; + /** Function that is used to get the time stamp */ + private getTime: () => bigint; + + constructor(options: {threshold?: number; getTime?: () => bigint} = {}) { + this.getTime = options.getTime || process.hrtime.bigint; + this.threshold = options.threshold || THRESHOLD_DEFAULT; + } + + /** Total amount of work, e.g. number of files to save or number of bytes to send */ + get stepsTotal() { + return this._stepsTotal; + } + + set stepsTotal(stepsTotal) { + this._stepsTotal = stepsTotal; + this.numberOfDigitsInPercentage = + this.stepsTotal > 100 ? Math.ceil(Math.log10(this.stepsTotal)) - 2 : 0; + } + + /** Amount of work already done */ + get stepsDone() { + return this._stepsDone; + } + + set stepsDone(stepsDone) { + this._stepsDone = stepsDone; + this.timeOfUpdatingStepsDone = this.getCurrentTimeInMilliSeconds(); + if (this._stepsDone) { + const diff = this.timeOfUpdatingStepsDone - this.startTime; + const milliSecForOneStep = diff / this._stepsDone; + + this.trust = this.isVelocityTrust(milliSecForOneStep, this.milliSecForOneStep); + this.milliSecForOneStep = milliSecForOneStep; + } + } + + /** + * Saves the current time as we start monitoring the process. + */ + startMonitoring() { + this.startTime = this.getCurrentTimeInMilliSeconds(); + this.milliSecForOneStep = 0; + this.trust = false; + this.timeOfUpdatingStepsDone = 0; + this.stopTime = 0; + this.stepsDone = 0; + } + + /** + * Saves the current time as we stop monitoring the process. + */ + stopMonitoring() { + this.stopTime = this.getCurrentTimeInMilliSeconds(); + } + + /** + * Gets percentage of the work already done. + * @returns percentage of the work already done. + */ + getPercent(): number | null { + if (!this._stepsTotal) { + return null; + } + const percent = (this._stepsDone / this._stepsTotal) * 100.0; + return percent; + } + + /** + * Gets string representation of percentage of the work already done. + * @returns string representation of percentage or an empty string if the percetage value cannot be calculated. + */ + getPercentString() { + const percent = this.getPercent(); + return percent !== null ? percent.toFixed(this.numberOfDigitsInPercentage) : ''; + } + + /** + * Gets the time elapsed since the monitoring started + * @returns Number of milliseconds elapsed + */ + getTimeCurrentlyElapsed(): number { + const currentTime = this.stopTime ? this.stopTime : this.getCurrentTimeInMilliSeconds(); + const diff = currentTime - this.startTime; + return diff; + } + + /** + * Gets the time remaining (expected at the moment of updating 'stepsDone') to complete the work. + * @returns Number of milliseconds remaining + */ + getTimeRemaining(): {timeRemaining: number; trust: boolean} | null { + if (!this._stepsDone || !this.startTime) { + return null; + } + + const timeRemainingInMilliSeconds = + (this._stepsTotal - this._stepsDone) * this.milliSecForOneStep; + return {timeRemaining: timeRemainingInMilliSeconds, trust: this.trust}; + } + + /** + * Gets the string representation of the time remaining (expected at the moment of updating 'stepsDone') to complete the work. + * @returns string representation of the time remaining. + * It's an empty string if the time cannot be pedicted or it's still being calculated. + */ + getTimeRemainingString(): string { + const timeRemainingObject = this.getTimeRemaining(); + return timeRemainingObject?.trust ? timeConverter(timeRemainingObject.timeRemaining) : ''; + } + + /** + * Check if the computed velociy of the process can be considered trust. + * At the beginning of the process the number of samples collected ('time necessary to perform one step' averaged) is too small, + * which results in huge deviation of the cumputed velocity of the process. + * It makes sense to perform the check before reporting the time remainig so the end user is not confused. + * @param current - current value + * @param previous - previous value + * @returns true if the computed velociy can be considered trust, or false otherwise + */ + private isVelocityTrust(current: number, previous: number): boolean { + if (previous) { + const dev = Math.abs((current - previous) / previous); + return dev < this.threshold; + } + return false; + } + + /** + * Gets current time in milliseconds. + * @returns current time in milliseconds. + */ + private getCurrentTimeInMilliSeconds(): number { + // process.hrtime.bigint() returns the time in nanoseconds. We need the time in milliseconds. + return Number(this.getTime() / BigInt(1e6)); + } +} diff --git a/modules/tile-converter/src/i3s-converter/i3s-converter.ts b/modules/tile-converter/src/i3s-converter/i3s-converter.ts index a95a57dc92..7f9e8f241c 100644 --- a/modules/tile-converter/src/i3s-converter/i3s-converter.ts +++ b/modules/tile-converter/src/i3s-converter/i3s-converter.ts @@ -1,4 +1,7 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {AttributeMetadataInfo} from './helpers/attribute-metadata-info'; import type { FeatureTableJson, @@ -12,9 +15,10 @@ import type { SceneLayer3D, BoundingVolumes, MaxScreenThresholdSQ, - NodeInPage + NodeInPage, + Attribute } from '@loaders.gl/i3s'; -import {load, encode, fetchFile, getLoaderOptions, isBrowser} from '@loaders.gl/core'; +import {load, encode, isBrowser} from '@loaders.gl/core'; import {CesiumIonLoader, Tiles3DLoader} from '@loaders.gl/3d-tiles'; import {Geoid} from '@math.gl/geoid'; import {join} from 'path'; @@ -51,45 +55,46 @@ import {I3SMaterialDefinition, TextureSetDefinitionFormats} from '@loaders.gl/i3 import {ImageWriter} from '@loaders.gl/images'; import {GLTFImagePostprocessed} from '@loaders.gl/gltf'; import { - GltfPrimitiveModeString, + GLTFPrimitiveModeString, I3SConvertedResources, PreprocessData, SharedResourcesArrays } from './types'; -import {getWorkerURL, WorkerFarm} from '@loaders.gl/worker-utils'; -import {DracoWriterWorker} from '@loaders.gl/draco'; +import {WorkerFarm} from '@loaders.gl/worker-utils'; import WriteQueue from '../lib/utils/write-queue'; -import {I3SAttributesWorker} from '../i3s-attributes-worker'; import {BROWSER_ERROR_MESSAGE} from '../constants'; import { - createdStorageAttribute, - createFieldAttribute, - createPopupInfo, - getAttributeType, - getFieldAttributeType + getAttributeTypesMapFromPropertyTable, + getAttributeTypesMapFromSchema } from './helpers/feature-attributes'; import {NodeIndexDocument} from './helpers/node-index-document'; -import {loadNestedTileset, loadTile3DContent} from './helpers/load-3d-tiles'; +import { + isNestedTileset, + loadNestedTileset, + loadTile3DContent, + loadFromArchive +} from './helpers/load-3d-tiles'; import {Matrix4} from '@math.gl/core'; import {BoundingSphere, OrientedBoundingBox} from '@math.gl/culling'; import {createBoundingVolume} from '@loaders.gl/tiles'; import {TraversalConversionProps, traverseDatasetWith} from './helpers/tileset-traversal'; import {analyzeTileContent, mergePreprocessData} from './helpers/preprocess-3d-tiles'; +import {Progress} from './helpers/progress'; -const ION_DEFAULT_TOKEN = - process.env?.IonToken || // eslint-disable-line - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlYWMxMzcyYy0zZjJkLTQwODctODNlNi01MDRkZmMzMjIxOWIiLCJpZCI6OTYyMCwic2NvcGVzIjpbImFzbCIsImFzciIsImdjIl0sImlhdCI6MTU2Mjg2NjI3M30.1FNiClUyk00YH_nWfSGpiQAjR5V2OvREDq1PJ5QMjWQ'; // eslint-disable-line +const ION_DEFAULT_TOKEN = process.env?.IonToken; const HARDCODED_NODES_PER_PAGE = 64; const _3D_TILES = '3DTILES'; const _3D_OBJECT_LAYER_TYPE = '3DObject'; const REFRESH_TOKEN_TIMEOUT = 1800; // 30 minutes in seconds const CESIUM_DATASET_PREFIX = 'https://'; // const FS_FILE_TOO_LARGE = 'ERR_FS_FILE_TOO_LARGE'; +const PROGRESS_PHASE1_COUNT = 'phase1-count'; /** * Converter from 3d-tiles tileset to i3s layer */ export default class I3SConverter { + attributeMetadataInfo: AttributeMetadataInfo; nodePages: NodePages; options: any; layers0Path: string; @@ -112,6 +117,7 @@ export default class I3SConverter { loadOptions: Tiles3DLoaderOptions = { _nodeWorkers: true, reuseWorkers: true, + useLocalLibraries: true, basis: { format: 'rgba32', // We need to load local fs workers because nodejs can't load workers from the Internet @@ -119,7 +125,8 @@ export default class I3SConverter { }, // We need to load local fs workers because nodejs can't load workers from the Internet draco: {workerUrl: './modules/draco/dist/draco-worker-node.js'}, - fetch: {} + fetch: {}, + modules: {} }; geoidHeightModel: Geoid | null = null; Loader: LoaderWithParser = Tiles3DLoader; @@ -130,10 +137,13 @@ export default class I3SConverter { writeQueue: WriteQueue = new WriteQueue(); compressList: string[] | null = null; preprocessData: PreprocessData = { - meshTopologyTypes: new Set() + meshTopologyTypes: new Set(), + metadataClasses: new Set() }; + progresses: Record = {}; constructor() { + this.attributeMetadataInfo = new AttributeMetadataInfo(); this.nodePages = new NodePages(writeFile, HARDCODED_NODES_PER_PAGE, this); this.options = {}; this.layers0Path = ''; @@ -187,6 +197,9 @@ export default class I3SConverter { generateTextures?: boolean; generateBoundingVolumes?: boolean; instantNodeWriting?: boolean; + inquirer?: Promise; + metadataClass?: string; + analyze?: boolean; }): Promise { if (isBrowser) { console.log(BROWSER_ERROR_MESSAGE); @@ -207,7 +220,10 @@ export default class I3SConverter { generateTextures, generateBoundingVolumes, instantNodeWriting = false, - mergeMaterials = true + mergeMaterials = true, + inquirer, + metadataClass, + analyze = false } = options; this.options = { maxDepth, @@ -218,8 +234,11 @@ export default class I3SConverter { token, inputUrl, instantNodeWriting, - mergeMaterials + mergeMaterials, + inquirer, + metadataClass }; + this.progresses[PROGRESS_PHASE1_COUNT] = new Progress(); this.compressList = (this.options.instantNodeWriting && []) || null; this.validate = Boolean(validate); this.Loader = inputUrl.indexOf(CESIUM_DATASET_PREFIX) !== -1 ? CesiumIonLoader : Tiles3DLoader; @@ -237,20 +256,26 @@ export default class I3SConverter { this.nodePages.useWriteFunction(writeFileForSlpk); } - await this.loadWorkers(); - try { const preloadOptions = await this._fetchPreloadOptions(); + let tilesetUrl = inputUrl; + if (preloadOptions.url) { + tilesetUrl = preloadOptions.url; + } if (preloadOptions.headers) { this.loadOptions.fetch = {headers: preloadOptions.headers}; } - this.sourceTileset = await load(inputUrl, this.Loader, this.loadOptions); + this.sourceTileset = await loadFromArchive(tilesetUrl, this.Loader, this.loadOptions); - const preprocessResult = await this.preprocessConversion(); + const preprocessResult = + this.Loader === Tiles3DLoader || analyze ? await this.preprocessConversion() : true; - if (preprocessResult) { - await this._createAndSaveTileset(outputPath, tilesetName); - await this._finishConversion({slpk: Boolean(slpk), outputPath, tilesetName}); + if (preprocessResult && !analyze) { + const selectMetadataClassResult = await this.selectMetadataClass(); + if (selectMetadataClassResult) { + await this._createAndSaveTileset(outputPath, tilesetName); + await this._finishConversion({slpk: Boolean(slpk), outputPath, tilesetName}); + } } } catch (error) { throw error; @@ -278,14 +303,25 @@ export default class I3SConverter { undefined, this.options.maxDepth ); - const {meshTopologyTypes} = this.preprocessData; + const {meshTopologyTypes, metadataClasses} = this.preprocessData; + console.log(`------------------------------------------------`); console.log(`Preprocess results:`); + console.log(`Tile count: ${this.progresses[PROGRESS_PHASE1_COUNT].stepsTotal}`); console.log(`glTF mesh topology types: ${Array.from(meshTopologyTypes).join(', ')}`); + + if (metadataClasses.size) { + console.log( + `Feature metadata classes have been found: ${Array.from(metadataClasses).join(', ')}` + ); + } else { + console.log('Feature metadata classes have not been found'); + } + console.log(`------------------------------------------------`); if ( - !meshTopologyTypes.has(GltfPrimitiveModeString.TRIANGLES) && - !meshTopologyTypes.has(GltfPrimitiveModeString.TRIANGLE_STRIP) + !meshTopologyTypes.has(GLTFPrimitiveModeString.TRIANGLES) && + !meshTopologyTypes.has(GLTFPrimitiveModeString.TRIANGLE_STRIP) ) { console.log( 'The tileset is of unsupported mesh topology types. The conversion will be interrupted.' @@ -293,6 +329,7 @@ export default class I3SConverter { console.log(`------------------------------------------------`); return false; } + return true; } @@ -306,11 +343,13 @@ export default class I3SConverter { sourceTile: Tiles3DTileJSONPostprocessed, traversalProps: null ): Promise { - if (sourceTile.type === 'json') { + const isTileset = isNestedTileset(sourceTile); + if (isTileset) { await loadNestedTileset(this.sourceTileset, sourceTile, this.loadOptions); return null; } if (sourceTile.id) { + this.progresses[PROGRESS_PHASE1_COUNT].stepsTotal += 1; console.log(`[analyze]: ${sourceTile.id}`); // eslint-disable-line } @@ -327,10 +366,42 @@ export default class I3SConverter { } const tilePreprocessData = await analyzeTileContent(tileContent); mergePreprocessData(this.preprocessData, tilePreprocessData); - return null; } + /** + * Select metadata class associated with the set of feature attributes + * @returns true if the metadata class has been successfully selected + */ + private async selectMetadataClass() { + const {metadataClasses} = this.preprocessData; + if (metadataClasses.size > 1) { + if (this.options.metadataClass?.length) { + console.log(`${this.options.metadataClass} has been selected`); + } else if (this.options.inquirer) { + const result = await this.options.inquirer.prompt([ + { + name: 'metadataClass', + type: 'list', + message: 'Select feature metadata data class to convert...', + choices: Array.from(metadataClasses) + } + ]); + this.options.metadataClass = result.metadataClass; + console.log(`${result.metadataClass} has been selected`); + } else { + console.log( + `A feature metadata class has not been selected. Start the converter with option "--metadata-class". For example, "npx tile-converter ... --metadata-class ${ + Array.from(metadataClasses)[0] + }"` + ); + console.log(`------------------------------------------------`); + return false; + } + } + return true; + } + /** * Convert and save the layer and embedded tiles * @param outputPath - path to save output data @@ -371,7 +442,7 @@ export default class I3SConverter { obb: boundingVolumes.obb, children: [] }); - + this.progresses[PROGRESS_PHASE1_COUNT].startMonitoring(); const rootNode = await NodeIndexDocument.createRootNode(boundingVolumes, this); await traverseDatasetWith( sourceRootTile, @@ -383,6 +454,15 @@ export default class I3SConverter { this.finalizeTile.bind(this), this.options.maxDepth ); + this.progresses[PROGRESS_PHASE1_COUNT].stopMonitoring(); + + this.layers0!.attributeStorageInfo = this.attributeMetadataInfo.attributeStorageInfo; + this.layers0!.fields = this.attributeMetadataInfo.fields; + this.layers0!.popupInfo = this.attributeMetadataInfo.popupInfo; + + if (this.attributeMetadataInfo.attributeStorageInfo.length) { + this.layers0!.layerType = _3D_OBJECT_LAYER_TYPE; + } this.layers0!.materialDefinitions = this.materialDefinitions; // @ts-ignore @@ -519,8 +599,9 @@ export default class I3SConverter { sourceTile: Tiles3DTileJSONPostprocessed, traversalProps: TraversalConversionProps ): Promise { - if (sourceTile.type === 'json' || sourceTile.type === 'empty') { - if (sourceTile.type === 'json') { + const isTileset = isNestedTileset(sourceTile); + if (isTileset || sourceTile.type === 'empty') { + if (isTileset) { if (sourceTile.id) { console.log(`[load]: ${sourceTile.id}`); // eslint-disable-line } @@ -531,7 +612,6 @@ export default class I3SConverter { if (sourceTile.id) { console.log(`[convert]: ${sourceTile.id}`); // eslint-disable-line } - const {parentNodes, transform} = traversalProps; let transformationMatrix: Matrix4 = transform.clone(); if (sourceTile.transform) { @@ -545,6 +625,20 @@ export default class I3SConverter { transform: transformationMatrix, parentNodes: childNodes }; + + if (sourceTile.id) { + this.progresses[PROGRESS_PHASE1_COUNT].stepsDone += 1; + + let timeRemainingString = 'Calculating time left...'; + const timeRemainingStringBasedOnCount = + this.progresses[PROGRESS_PHASE1_COUNT].getTimeRemainingString(); + if (timeRemainingStringBasedOnCount) { + timeRemainingString = `${timeRemainingStringBasedOnCount} left`; + } + + let percentString = this.progresses[PROGRESS_PHASE1_COUNT].getPercentString(); + console.log(`[converted ${percentString}%, ${timeRemainingString}]: ${sourceTile.id}`); // eslint-disable-line + } return newTraversalProps; } @@ -597,11 +691,8 @@ export default class I3SConverter { ); let boundingVolumes = createBoundingVolumes(sourceBoundingVolume, this.geoidHeightModel!); - const propertyTable = getPropertyTable(tileContent); - - if (propertyTable && !this.layers0?.attributeStorageInfo?.length) { - this._convertPropertyTableToNodeAttributes(propertyTable); - } + const propertyTable = getPropertyTable(tileContent, this.options.metadataClass); + this.createAttributeStorageInfo(tileContent, propertyTable); const resourcesData = await this._convertResources( sourceTile, @@ -685,7 +776,7 @@ export default class I3SConverter { * @param boundingVolume - initialized bounding volume of the source tile * @param tileContent - content of the source tile * @param parentId - id of parent node in node pages - * @param propertyTable - batch table from b3dm / feature properties from EXT_FEATURE_METADATA + * @param propertyTable - batch table from b3dm / feature properties from EXT_FEATURE_METADATA, EXT_MESH_FEATURES or EXT_STRUCTURAL_METADATA * @returns - converted node resources */ private async _convertResources( @@ -711,12 +802,13 @@ export default class I3SConverter { async () => (await this.nodePages.push({index: 0, obb: draftObb}, parentId)).index, propertyTable, this.featuresHashArray, - this.layers0?.attributeStorageInfo, + this.attributeMetadataInfo.attributeStorageInfo, this.options.draco, this.generateBoundingVolumes, this.options.mergeMaterials, this.geoidHeightModel!, - this.workerSource + this.loadOptions.modules as Record, + this.options.metadataClass ); return resourcesData; } @@ -932,9 +1024,13 @@ export default class I3SConverter { KTX2BasisWriterWorker, { ...KTX2BasisWriterWorker.options, - source: this.workerSource.ktx2, + ['ktx2-basis-writer']: { + // We need to load local fs workers because nodejs can't load workers from the Internet + workerUrl: './modules/textures/dist/ktx2-basis-writer-worker-node.js' + }, reuseWorkers: true, - _nodeWorkers: true + _nodeWorkers: true, + useLocalLibraries: true } ); @@ -1012,9 +1108,14 @@ export default class I3SConverter { childPath: string, slpkChildPath: string ): Promise { - if (attributes?.length && this.layers0?.attributeStorageInfo?.length) { - for (let index = 0; index < attributes.length; index++) { - const folderName = this.layers0.attributeStorageInfo[index].key; + if (attributes?.length && this.attributeMetadataInfo.attributeStorageInfo.length) { + const minimumLength = + attributes.length < this.attributeMetadataInfo.attributeStorageInfo.length + ? attributes.length + : this.attributeMetadataInfo.attributeStorageInfo.length; + + for (let index = 0; index < minimumLength; index++) { + const folderName = this.attributeMetadataInfo.attributeStorageInfo[index].key; const fileBuffer = new Uint8Array(attributes[index]); if (this.options.slpk) { @@ -1084,31 +1185,35 @@ export default class I3SConverter { } /** - * Do conversion of 3DTiles property table to I3s node attributes. - * @param propertyTable - Table with layer meta data. + * Creates attribute storage info based on either extension schema or property table. + * @param tileContent - content of the source tile + * @param propertyTable - feature properties from EXT_FEATURE_METADATA, EXT_STRUCTURAL_METADATA */ - private _convertPropertyTableToNodeAttributes(propertyTable: FeatureTableJson): void { - let attributeIndex = 0; - const propertyTableWithObjectId = { - OBJECTID: [0], - ...propertyTable - }; - - for (const key in propertyTableWithObjectId) { - const firstAttribute = propertyTableWithObjectId[key][0]; - const attributeType = getAttributeType(key, firstAttribute); - - const storageAttribute = createdStorageAttribute(attributeIndex, key, attributeType); - const fieldAttributeType = getFieldAttributeType(attributeType); - const fieldAttribute = createFieldAttribute(key, fieldAttributeType); - const popupInfo = createPopupInfo(propertyTableWithObjectId); - - this.layers0!.attributeStorageInfo!.push(storageAttribute); - this.layers0!.fields!.push(fieldAttribute); - this.layers0!.popupInfo = popupInfo; - this.layers0!.layerType = _3D_OBJECT_LAYER_TYPE; + private createAttributeStorageInfo( + tileContent: Tiles3DTileContent | null, + propertyTable: FeatureTableJson | null + ): void { + /* + In case the tileset doesn't have either EXT_structural_metadata or EXT_feature_metadata + that can be a source of attribute information so metadataClass is not specified + we will collect attribute information for node attributes from the property table + taken from each tile. + */ + let attributeTypesMap: Record | null = null; + if (this.options.metadataClass) { + if (!this.attributeMetadataInfo.attributeStorageInfo.length && tileContent?.gltf) { + attributeTypesMap = getAttributeTypesMapFromSchema( + tileContent.gltf, + this.options.metadataClass + ); + } + } else if (propertyTable) { + attributeTypesMap = getAttributeTypesMapFromPropertyTable(propertyTable); + } - attributeIndex += 1; + if (attributeTypesMap) { + // Add new storage attributes, fields and create popupInfo + this.attributeMetadataInfo.addMetadataInfo(attributeTypesMap); } } @@ -1194,27 +1299,4 @@ export default class I3SConverter { private isContentSupported(sourceTile: Tiles3DTileJSONPostprocessed): boolean { return ['b3dm', 'glTF', 'scenegraph'].includes(sourceTile.type || ''); } - - private async loadWorkers(): Promise { - console.log(`Loading workers source...`); // eslint-disable-line no-undef, no-console - if (this.options.draco) { - const url = getWorkerURL(DracoWriterWorker, {...getLoaderOptions()}); - const sourceResponse = await fetchFile(url); - const source = await sourceResponse.text(); - this.workerSource.draco = source; - } - - if (this.generateTextures) { - const url = getWorkerURL(KTX2BasisWriterWorker, {...getLoaderOptions()}); - const sourceResponse = await fetchFile(url); - const source = await sourceResponse.text(); - this.workerSource.ktx2 = source; - } - - const i3sAttributesWorkerUrl = getWorkerURL(I3SAttributesWorker, {...getLoaderOptions()}); - const sourceResponse = await fetchFile(i3sAttributesWorkerUrl); - const source = await sourceResponse.text(); - this.workerSource.I3SAttributes = source; - console.log(`Loading workers source completed!`); // eslint-disable-line no-undef, no-console - } } diff --git a/modules/tile-converter/src/i3s-converter/types.ts b/modules/tile-converter/src/i3s-converter/types.ts index aedd2ca84e..d18998bf43 100644 --- a/modules/tile-converter/src/i3s-converter/types.ts +++ b/modules/tile-converter/src/i3s-converter/types.ts @@ -1,10 +1,11 @@ -import {GLTFImagePostprocessed} from '@loaders.gl/gltf'; +import {GLTFImagePostprocessed, GLTFNodePostprocessed} from '@loaders.gl/gltf'; import { BoundingVolumes, I3SMaterialDefinition, MaterialDefinitionInfo, TextureDefinitionInfo } from '@loaders.gl/i3s'; +import {Matrix4, Vector3} from '@math.gl/core'; /** Converted resources for specific node */ export type I3SConvertedResources = { @@ -105,10 +106,8 @@ export type GeometryAttributes = { featureCount: number; }; -/** Geometry attributes specific for the particular feature */ -export type GroupedByFeatureIdAttributes = { - /** Feature Id */ - featureId: number; +/** Geometry attributes applicable for reordering by featureId */ +export type GroupedAttributes = { /** POSITION attribute value */ positions: Float32Array; /** NORMAL attribute value */ @@ -121,6 +120,12 @@ export type GroupedByFeatureIdAttributes = { texCoords: Float32Array; }; +/** Geometry attributes specific for the particular feature */ +export type GroupedByFeatureIdAttributes = GroupedAttributes & { + /** Feature Id */ + featureId: number; +}; + /** Shared resources made from GLTF material */ export type SharedResourcesArrays = { /** material definitions list https://github.com/Esri/i3s-spec/blob/master/docs/1.8/materialDefinitionInfo.cmn.md */ @@ -168,7 +173,7 @@ export type TypedArrayConstructor = * glTF primitive modes (mesh topology types) * @see https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#_mesh_primitive_mode */ -export enum GltfPrimitiveModeString { +export enum GLTFPrimitiveModeString { POINTS = 'POINTS', LINES = 'LINES', LINE_LOOP = 'LINE_LOOP', @@ -181,5 +186,58 @@ export enum GltfPrimitiveModeString { /** Preprocessed data gathered from child tiles binary content */ export type PreprocessData = { /** Mesh topology types used in gltf primitives of the tileset */ - meshTopologyTypes: Set; + meshTopologyTypes: Set; + /** + * Feature metadata classes found in glTF extensions + * The tileset might contain multiple metadata classes provided by EXT_feature_metadata and EXT_structural_metadata extensions. + * Every class is a set of properties. But I3S can consume only one set of properties. + * On the pre-process we collect all classes from the tileset in order to show the prompt to select one class for conversion to I3S. + */ + metadataClasses: Set; +}; + +/** Texture image properties required for conversion */ +export type TextureImageProperties = { + /** Array with image data */ + data: Uint8Array; + /** Is the texture compressed */ + compressed?: boolean; + /** Height of the texture's image */ + height?: number; + /** Width of the texture's image */ + width?: number; + /** Number of components (3 for RGB, 4 for RGBA) */ + components?: number; + /** Mime type of the texture's image */ + mimeType?: string; +}; + +/** glTF attributes data, prepared for conversion */ +export type GLTFAttributesData = { + /** glTF PBR materials (only id is required) */ + gltfMaterials?: {id: string}[]; + /** glTF geometry nodes */ + nodes: GLTFNodePostprocessed[]; + /** glTF texture images (set to null for compressed textures) */ + images: (null | TextureImageProperties)[]; + /** Source tile origin coordinates in cartographic coordinate system */ + cartographicOrigin: Vector3; + /** Model matrix to convert coordinate system of POSITION and NORMAL attributes from METER_OFFSETS to CARTESIAN */ + cartesianModelMatrix: Matrix4; }; + +/** + * I3S' types difine the following: + * type Attribute = 'OBJECTID' | 'string' | 'double' | 'Int32' | string; + * The AttributeType contains the string values of the Attribute type. + */ +export const AttributeType = { + /** Type of attribute that is linked with feature ids */ + OBJECT_ID_TYPE: 'OBJECTID', + /** String data type name for feature attributes */ + STRING_TYPE: 'string', + /** Double data type name for feature attributes */ + DOUBLE_TYPE: 'double', + /** Integer data type name for feature attributes */ + SHORT_INT_TYPE: 'Int32' +} as const; diff --git a/modules/tile-converter/src/i3s-server/README.md b/modules/tile-converter/src/i3s-server/README.md index 27d27d215e..9c010579ce 100644 --- a/modules/tile-converter/src/i3s-server/README.md +++ b/modules/tile-converter/src/i3s-server/README.md @@ -8,12 +8,56 @@ The server provides I3S Rest endpoints per specification https://github.com/Esri ### Serve 3DTiles to I3S converted dataset - Convert data set from 3DTiles to I3S without `--slpk` option -- Serve output folder `I3sLayerPath="./data/BatchedTextured" DEBUG=i3s-server:* npx i3s-server` +- Serve output folder + +Example for path `./data/BatchTextured/SceneServer/layers/0/...`: + +#### Start the server + +```bash +PORT=8080 HTTPS_PORT=4443 I3sLayerPath="./data" DEBUG=i3s-server:* npx i3s-server +``` +#### Check the layer availability + +The layer should be available on URLs: + +- `http://localhost:8080/BatchTextured/SceneServer/layers/0/...` +- `https://localhost:4443/BatchTextured/SceneServer/layers/0/...` + +#### Open in ArcGIS + +`https://www.arcgis.com/home/webscene/viewer.html?url=http://localhost:8080/BatchTextured/SceneServer/layers/0/` + +#### Open in I3S Explorer + +`https://i3s.loaders.gl/viewer?tileset=http://localhost:8080/BatchTextured/SceneServer/layers/0` ### Serve SLPK -- Serve slpk file `I3sLayerPath="../datasets/Rancho_Mesh_mesh_v17_1.slpk" DEBUG=i3s-server:* npx i3s-server` +Example for path `../datasets/Rancho_Mesh_mesh_v17_1.slpk`: + +#### Start the server + +```bash +PORT=8080 HTTPS_PORT=4443 I3sLayerPath="../datasets/Rancho_Mesh_mesh_v17_1.slpk" DEBUG=i3s-server:* npx i3s-server +``` +#### Check the layer availability + +The layer should be available on URLs: + +- `http://localhost:8080/SceneServer/layers/0/...` +- `https://localhost:4443/SceneServer/layers/0/...` + +#### Open in ArcGIS + +`https://www.arcgis.com/home/webscene/viewer.html?url=http://localhost:8080/SceneServer` + +#### Open in I3S Explorer + +`https://i3s.loaders.gl/viewer?tileset=http://localhost:8080/SceneServer/layers/0` ## ENV variables - `I3sLayerPath` - path to converted data or SLPK file. +- `PORT` - HTTP port. Eg for `PORT = 8080 npx i3s-server` the server will work on host `http://localhost:8080/...`. Default value is `80`; +- `HTTPS_PORT` - HTTPS port. Eg for `PORT = 4443 npx i3s-server` the server will work on host `https://localhost:4443/...`. Default value is `443` diff --git a/modules/tile-converter/src/i3s-server/app.js b/modules/tile-converter/src/i3s-server/app.js deleted file mode 100644 index aa65065d01..0000000000 --- a/modules/tile-converter/src/i3s-server/app.js +++ /dev/null @@ -1,25 +0,0 @@ -const express = require('express'); -const path = require('path'); -const logger = require('morgan'); -const cors = require('cors'); - -const indexRouter = require('./routes/index'); -const {sceneServerRouter, router} = require('./routes/slpk-router'); - -const I3S_LAYER_PATH = process.env.I3sLayerPath || ''; // eslint-disable-line no-process-env, no-undef -const app = express(); - -app.use(logger('dev')); -app.use(express.json()); -app.use(express.urlencoded({extended: false})); -app.use(express.static(path.join(__dirname, 'public'))); -app.use(cors()); - -if (/\.slpk$/.test(I3S_LAYER_PATH)) { - app.use('/SceneServer/layers/0', router); - app.use('/SceneServer', sceneServerRouter); -} else { - app.use('/', indexRouter); -} - -module.exports = app; diff --git a/modules/tile-converter/src/i3s-server/app.ts b/modules/tile-converter/src/i3s-server/app.ts new file mode 100644 index 0000000000..cfb2296136 --- /dev/null +++ b/modules/tile-converter/src/i3s-server/app.ts @@ -0,0 +1,32 @@ +import express from 'express'; +import path from 'path'; +import logger from 'morgan'; +import cors from 'cors'; +// For local debug +// import {fileURLToPath} from 'url'; +import {loadArchive} from './controllers/slpk-controller'; +import {router as indexRouter} from './routes'; +import {sceneServerRouter, router} from './routes/slpk-router'; + +// For local debug +// const __filename = fileURLToPath(import.meta.url); +// const __dirname = path.dirname(__filename); + +const I3S_LAYER_PATH = process.env.I3sLayerPath || ''; // eslint-disable-line no-process-env, no-undef +const FULL_LAYER_PATH = path.join(process.cwd(), I3S_LAYER_PATH); // eslint-disable-line no-undef + +export const app = express(); + +app.use(logger('dev')); +app.use(express.json()); +app.use(express.urlencoded({extended: false})); +app.use(express.static(path.join(__dirname, 'public'))); +app.use(cors()); + +if (/\.slpk$/.test(I3S_LAYER_PATH)) { + loadArchive(FULL_LAYER_PATH); + app.use('/SceneServer/layers/0', router); + app.use('/SceneServer', sceneServerRouter); +} else { + app.use('/', indexRouter); +} diff --git a/modules/tile-converter/src/i3s-server/bin/www b/modules/tile-converter/src/i3s-server/bin/www deleted file mode 100755 index 8b09bdd2a0..0000000000 --- a/modules/tile-converter/src/i3s-server/bin/www +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env node - -/** - * Module dependencies. - */ - -const app = require('../app'); -const debug = require('debug')('i3s-server:server'); -const https = require('https'); -const http = require('http'); -const fs = require('fs'); -const path = require('path'); - -/** - * Get port from environment and store in Express. - */ - -const httpPort = normalizePort(process.env.PORT || '80'); // eslint-disable-line no-process-env, no-undef -const httpsPort = normalizePort(process.env.HTTPS_PORT || '443'); // eslint-disable-line no-process-env, no-undef - -/** - * Create HTTP server. - */ - -const options = { - key: fs.readFileSync(path.join(__dirname, '../certs/key.pem')), - cert: fs.readFileSync(path.join(__dirname, '../certs/cert.pem')) -}; - -const httpServer = http.createServer(app); -const httpsServer = https.createServer(options, app); - -/** - * Listen on provided port, on all network interfaces. - */ - -httpServer.listen(httpPort); -httpServer.on('error', formErrorHandler(httpPort)); -httpServer.on('listening', formListeningHandler(httpServer)); - -httpsServer.listen(httpsPort); -httpsServer.on('error', formErrorHandler(httpsPort)); -httpsServer.on('listening', formListeningHandler(httpsServer)); - -/** - * Normalize a port into a number, string, or false. - */ - -function normalizePort(val) { - const chkPort = parseInt(val, 10); - - if (isNaN(chkPort)) { - // named pipe - return val; - } - - if (chkPort >= 0) { - // port number - return chkPort; - } - - return false; -} - -/** - * Event listener for HTTP/HTTPS server "error" event. - */ - -function formErrorHandler(optionalPort) { - return function onError(error) { - if (error.syscall !== 'listen') { - throw error; - } - - const bind = typeof port === 'string' ? `Pipe ${optionalPort}`: `Port ${optionalPort}`; - - // handle specific listen errors with friendly messages - switch (error.code) { - case 'EACCES': - console.error(`${bind} requires elevated privileges`);// eslint-disable-line no-console, no-undef - process.exit(1);// eslint-disable-line no-process-exit, no-undef - break; - case 'EADDRINUSE': - console.error(`${bind} is already in use`);// eslint-disable-line no-console, no-undef - process.exit(1);// eslint-disable-line no-process-exit, no-undef - break; - default: - throw error; - } - } -} - -/** - * Event listener for HTTP/HTTPS server "listening" event. - */ -function formListeningHandler(optionalServer) { - return function onListening() { - const addr = optionalServer.address(); - const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`; - debug(`Listening on ${bind}`); - } -} diff --git a/modules/tile-converter/src/i3s-server/bin/www.ts b/modules/tile-converter/src/i3s-server/bin/www.ts new file mode 100755 index 0000000000..1709879002 --- /dev/null +++ b/modules/tile-converter/src/i3s-server/bin/www.ts @@ -0,0 +1,44 @@ +#!/usr/bin/env node + +import {app} from '../app'; +import https from 'https'; +import http from 'http'; +import fs from 'fs'; +import path from 'path'; +// For local debug +// import {fileURLToPath} from 'url'; +import {formErrorHandler, formListeningHandler, normalizePort} from '../utils/server-utils'; + +// For local debug +// const __filename = fileURLToPath(import.meta.url); +// const __dirname = path.dirname(__filename); + +/** Get port from environment and store in Express. */ +const httpPort = normalizePort(process.env.PORT || '80'); +if (httpPort === false) { + console.error(`Incorrect HTTP port`); + process.exit(1); +} +const httpsPort = normalizePort(process.env.HTTPS_PORT || '443'); +if (httpsPort === false) { + console.error(`Incorrect HTTPs port`); + process.exit(1); +} + +/** Create HTTP server. */ +const options = { + key: fs.readFileSync(path.join(__dirname, '../certs/key.pem')), + cert: fs.readFileSync(path.join(__dirname, '../certs/cert.pem')) +}; + +const httpServer = http.createServer(app); +const httpsServer = https.createServer(options, app); + +/** Listen on provided port, on all network interfaces. */ +httpServer.listen(httpPort); +httpServer.on('error', formErrorHandler(httpPort)); +httpServer.on('listening', formListeningHandler(httpServer)); + +httpsServer.listen(httpsPort); +httpsServer.on('error', formErrorHandler(httpsPort)); +httpsServer.on('listening', formListeningHandler(httpsServer)); diff --git a/modules/tile-converter/src/i3s-server/controllers/index-controller.js b/modules/tile-converter/src/i3s-server/controllers/index-controller.ts similarity index 50% rename from modules/tile-converter/src/i3s-server/controllers/index-controller.js rename to modules/tile-converter/src/i3s-server/controllers/index-controller.ts index 1cc225d419..fdc4ade617 100644 --- a/modules/tile-converter/src/i3s-server/controllers/index-controller.js +++ b/modules/tile-converter/src/i3s-server/controllers/index-controller.ts @@ -1,13 +1,18 @@ -const path = require('path'); -const fs = require('fs'); +import path from 'path'; +import fs from 'fs'; const {promises} = fs; -const I3S_LAYER_PATH = process.env.I3sLayerPath || ''; // eslint-disable-line no-process-env, no-undef -const FULL_LAYER_PATH = path.join(process.cwd(), I3S_LAYER_PATH); // eslint-disable-line no-undef +const I3S_LAYER_PATH = process.env.I3sLayerPath || ''; -async function getFileNameByUrl(url) { +/** + * Get local file name by input HTTP URL + * @param url - I3S HTTP service url + * @returns - local file name + */ +export async function getFileNameByUrl(url: string): Promise { const extensions = ['json', 'bin', 'jpg', 'jpeg', 'png', 'bin.dds', 'ktx2']; + const FULL_LAYER_PATH = path.join(process.cwd(), I3S_LAYER_PATH); for (const ext of extensions) { const fileName = `${FULL_LAYER_PATH}${url}/index.${ext}`; try { @@ -19,7 +24,3 @@ async function getFileNameByUrl(url) { } return null; } - -module.exports = { - getFileNameByUrl -}; diff --git a/modules/tile-converter/src/i3s-server/controllers/slpk-controller.js b/modules/tile-converter/src/i3s-server/controllers/slpk-controller.js deleted file mode 100644 index 590574dd29..0000000000 --- a/modules/tile-converter/src/i3s-server/controllers/slpk-controller.js +++ /dev/null @@ -1,38 +0,0 @@ -require('@loaders.gl/polyfills'); -const {fetchFile, parse} = require('@loaders.gl/core'); -const {SLPKLoader} = require('@loaders.gl/i3s'); -const path = require('path'); - -let slpkArchive; - -const loadArchive = async (fullLayerPath) => { - slpkArchive = await (await fetchFile(fullLayerPath)).arrayBuffer(); -}; - -const I3S_LAYER_PATH = process.env.I3sLayerPath || ''; // eslint-disable-line no-process-env, no-undef -const FULL_LAYER_PATH = path.join(process.cwd(), I3S_LAYER_PATH); // eslint-disable-line no-undef - -loadArchive(FULL_LAYER_PATH); - -async function getFileByUrl(url) { - const trimmedPath = /^\/?(.*)\/?$/.exec(url); - let uncompressedFile; - if (trimmedPath) { - try { - uncompressedFile = Buffer.from( - await parse(slpkArchive, SLPKLoader, { - slpk: { - path: trimmedPath[1], - pathMode: 'http' - } - }) - ); - } catch (e) {} - } - return uncompressedFile; -} - -module.exports = { - loadArchive, - getFileByUrl -}; diff --git a/modules/tile-converter/src/i3s-server/controllers/slpk-controller.ts b/modules/tile-converter/src/i3s-server/controllers/slpk-controller.ts new file mode 100644 index 0000000000..ffed50486a --- /dev/null +++ b/modules/tile-converter/src/i3s-server/controllers/slpk-controller.ts @@ -0,0 +1,34 @@ +import '@loaders.gl/polyfills'; +import {parseSLPKArchive, SLPKArchive} from '@loaders.gl/i3s'; +import {FileHandleFile} from '@loaders.gl/loader-utils'; + +let slpkArchive: SLPKArchive; + +/** + * Open SLPK file for reading and load HASH file + * @param fullLayerPath - full path to SLPK file + */ +export async function loadArchive(fullLayerPath: string): Promise { + slpkArchive = await parseSLPKArchive(new FileHandleFile(fullLayerPath), (msg) => + console.log(msg) + ); + console.log('The server is ready to use'); +} + +/** + * Get a file from SLPK + * @param url - I3S HTTP URL + * @returns - file content + */ +export async function getFileByUrl(url: string): Promise { + const trimmedPath = /^\/?(.*)\/?$/.exec(url); + if (trimmedPath) { + try { + const uncompressedFile = await slpkArchive.getFile(trimmedPath[1], 'http'); + return uncompressedFile; + } catch { + // TODO - log error? + } + } + return null; +} diff --git a/modules/tile-converter/src/i3s-server/routes/index.js b/modules/tile-converter/src/i3s-server/routes/index.ts similarity index 59% rename from modules/tile-converter/src/i3s-server/routes/index.js rename to modules/tile-converter/src/i3s-server/routes/index.ts index 4d289caf16..b20b6bb8b5 100644 --- a/modules/tile-converter/src/i3s-server/routes/index.js +++ b/modules/tile-converter/src/i3s-server/routes/index.ts @@ -1,6 +1,7 @@ -const express = require('express'); -const router = express.Router(); -const {getFileNameByUrl} = require('../controllers/index-controller'); +import express from 'express'; +import {getFileNameByUrl} from '../controllers/index-controller'; + +export const router = express.Router(); /* GET home page. */ router.get('*', async function (req, res, next) { @@ -12,5 +13,3 @@ router.get('*', async function (req, res, next) { res.send('File not found'); } }); - -module.exports = router; diff --git a/modules/tile-converter/src/i3s-server/routes/slpk-router.js b/modules/tile-converter/src/i3s-server/routes/slpk-router.ts similarity index 54% rename from modules/tile-converter/src/i3s-server/routes/slpk-router.js rename to modules/tile-converter/src/i3s-server/routes/slpk-router.ts index e530b49c53..73ca3d7b62 100644 --- a/modules/tile-converter/src/i3s-server/routes/slpk-router.js +++ b/modules/tile-converter/src/i3s-server/routes/slpk-router.ts @@ -1,12 +1,14 @@ -const express = require('express'); -const {getFileByUrl} = require('../controllers/slpk-controller'); -const createSceneServer = require('../utils/create-scene-server'); +import express from 'express'; +import {getFileByUrl} from '../controllers/slpk-controller'; +import {createSceneServer} from '../utils/create-scene-server'; -const sceneServerRouter = express.Router(); +const textDecoder = new TextDecoder(); + +export const sceneServerRouter = express.Router(); sceneServerRouter.get('*', async function (req, res, next) { const file = await getFileByUrl('/'); if (file) { - const layer = JSON.parse(file.toString()); + const layer = JSON.parse(textDecoder.decode(file)); const sceneServerResponse = createSceneServer(layer.name, layer); res.send(sceneServerResponse); } else { @@ -15,19 +17,13 @@ sceneServerRouter.get('*', async function (req, res, next) { } }); -const router = express.Router(); +export const router = express.Router(); router.get('*', async function (req, res, next) { - console.log(req.path); const file = await getFileByUrl(req.path); if (file) { - res.send(file); + res.send(Buffer.from(file)); } else { res.status(404); res.send('File not found'); } }); - -module.exports = { - sceneServerRouter, - router -}; diff --git a/modules/tile-converter/src/i3s-server/utils/create-scene-server.js b/modules/tile-converter/src/i3s-server/utils/create-scene-server.js deleted file mode 100644 index fc67c8831b..0000000000 --- a/modules/tile-converter/src/i3s-server/utils/create-scene-server.js +++ /dev/null @@ -1,15 +0,0 @@ -const {v4: uuidv4} = require('uuid'); - -const createSceneServer = (name, layer) => { - return { - serviceItemId: uuidv4().replace(/-/gi, ''), - serviceName: name, - name, - currentVersion: '10.7', - serviceVersion: '1.8', - supportedBindings: ['REST'], - layers: [layer] - }; -}; - -module.exports = createSceneServer; diff --git a/modules/tile-converter/src/i3s-server/utils/create-scene-server.ts b/modules/tile-converter/src/i3s-server/utils/create-scene-server.ts new file mode 100644 index 0000000000..40675fe69f --- /dev/null +++ b/modules/tile-converter/src/i3s-server/utils/create-scene-server.ts @@ -0,0 +1,20 @@ +import {SceneLayer3D} from '@loaders.gl/i3s'; +import {v4 as uuidv4} from 'uuid'; + +/** + * Create `/SceneServer` response + * @param name - service name, custom user-friendly name of the service + * @param layer - I3S layer JSON + * @returns reponse JSON for `/SceneServer` route + */ +export const createSceneServer = (name: string, layer: SceneLayer3D) => { + return { + serviceItemId: uuidv4().replace(/-/gi, ''), + serviceName: name, + name, + currentVersion: '10.7', + serviceVersion: '1.8', + supportedBindings: ['REST'], + layers: [layer] + }; +}; diff --git a/modules/tile-converter/src/i3s-server/utils/server-utils.ts b/modules/tile-converter/src/i3s-server/utils/server-utils.ts new file mode 100644 index 0000000000..e3b2acbb66 --- /dev/null +++ b/modules/tile-converter/src/i3s-server/utils/server-utils.ts @@ -0,0 +1,70 @@ +import type {Server as HttpsServer} from 'https'; +import type {Server as HttpServer} from 'http'; + +import debugFactory from 'debug'; +const debug = debugFactory('i3s-server:server'); + +/** + * Normalize a port into a number, string, or false. + * @param val - port value from env variables + * @returns - `number` for port, `string` for a named pipe, or `false` if the port number is not correct + */ +export function normalizePort(val: string): number | string | false { + const chkPort = parseInt(val, 10); + + if (Number.isNaN(chkPort)) { + // named pipe + return val; + } + + if (chkPort >= 0) { + // port number + return chkPort; + } + + return false; +} + +/** + * Event listener creator for HTTP/HTTPS server "error" event. + * @param optionalPort - the port/named pipe the server is started on + * @return callback to handle server errors + */ +export function formErrorHandler( + optionalPort: string | number +): (error: NodeJS.ErrnoException) => void { + return function onError(error: NodeJS.ErrnoException) { + if (error.syscall !== 'listen') { + throw error; + } + + const bind = typeof optionalPort === 'string' ? `Pipe ${optionalPort}` : `Port ${optionalPort}`; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(`${bind} requires elevated privileges`); // eslint-disable-line no-console, no-undef + process.exit(1); // eslint-disable-line no-process-exit, no-undef + break; + case 'EADDRINUSE': + console.error(`${bind} is already in use`); // eslint-disable-line no-console, no-undef + process.exit(1); // eslint-disable-line no-process-exit, no-undef + break; + default: + throw error; + } + }; +} + +/** + * Event listener for HTTP/HTTPS server "listening" event. + * @param optionalServer - http or https NodeJS server + * @return callback that is triggered when the server has started + */ +export function formListeningHandler(optionalServer: HttpsServer | HttpServer): () => void { + return function onListening() { + const addr = optionalServer.address(); + const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr?.port}`; + debug(`Listening on ${bind}`); + }; +} diff --git a/modules/tile-converter/src/lib/utils/compress-util.ts b/modules/tile-converter/src/lib/utils/compress-util.ts index 4ae475c6f7..f1ecc1f03a 100644 --- a/modules/tile-converter/src/lib/utils/compress-util.ts +++ b/modules/tile-converter/src/lib/utils/compress-util.ts @@ -206,7 +206,7 @@ export async function generateHash128FromZip(inputZipFile: string, outputFile: s const content = zipEntry[_data].compressedContent; if (zipEntry.dir) continue; // eslint-disable-line no-continue // eslint-disable-next-line no-undef - const hash = await new MD5Hash().hash(Buffer.from(relativePath.toLowerCase())); + const hash = await new MD5Hash().hash(Buffer.from(relativePath.toLowerCase()), 'base64'); // eslint-disable-next-line no-undef hashTable.push({key: atob(hash), value: content.byteOffset}); } diff --git a/modules/tile-converter/src/lib/utils/lod-conversion-utils.ts b/modules/tile-converter/src/lib/utils/lod-conversion-utils.ts index e817f2578d..63abdc8b58 100644 --- a/modules/tile-converter/src/lib/utils/lod-conversion-utils.ts +++ b/modules/tile-converter/src/lib/utils/lod-conversion-utils.ts @@ -1,6 +1,6 @@ import {Tiles3DTileJSONPostprocessed} from '@loaders.gl/3d-tiles'; import {BoundingVolumes} from '@loaders.gl/i3s'; -import {Tile3D} from '@loaders.gl/tiles'; +import {I3STileHeader} from '@loaders.gl/i3s/src/types'; // https://cesium.com/docs/cesiumjs-ref-doc/Cesium3DTileset.html const DEFAULT_MAXIMUM_SCREEN_SPACE_ERROR = 16; @@ -64,11 +64,15 @@ export function convertGeometricErrorToScreenThreshold( * @param node - i3s node data * @returns lod metric in 3d-tiles format */ -export function convertScreenThresholdToGeometricError(node: Tile3D): number { - const metricData = node.header.lodSelection.maxScreenThreshold || {}; - let maxError = metricData.maxError; +export function convertScreenThresholdToGeometricError(node: I3STileHeader): number { + const metricData = node.lodSelection?.find( + (metric) => metric.metricType === 'maxScreenThreshold' + ); + let maxError = metricData?.maxError; if (!maxError) { - const sqMetricData = node.header.lodSelection.maxScreenThresholdSQ; + const sqMetricData = node.lodSelection?.find( + (metric) => metric.metricType === 'maxScreenThresholdSQ' + ); if (sqMetricData) { maxError = Math.sqrt(sqMetricData.maxError / (Math.PI * 0.25)); } @@ -78,5 +82,5 @@ export function convertScreenThresholdToGeometricError(node: Tile3D): number { maxError = DEFAULT_MAXIMUM_SCREEN_SPACE_ERROR; } - return (node.header.mbs[3] * 2 * DEFAULT_MAXIMUM_SCREEN_SPACE_ERROR) / maxError; + return (node.mbs[3] * 2 * DEFAULT_MAXIMUM_SCREEN_SPACE_ERROR) / maxError; } diff --git a/modules/tile-converter/src/lib/utils/statistic-utills.ts b/modules/tile-converter/src/lib/utils/statistic-utills.ts index abb7b1ba28..0e6e358f1f 100644 --- a/modules/tile-converter/src/lib/utils/statistic-utills.ts +++ b/modules/tile-converter/src/lib/utils/statistic-utills.ts @@ -2,15 +2,33 @@ import {join} from 'path'; import {promises as fs} from 'fs'; import {getAbsoluteFilePath} from './file-utils'; -export function timeConverter(time) { - const nanoSecondsInMillisecond = 1e6; - let timeInSeconds = time[0]; +/** + * Converts time value to string. + * @param time - high-resolution real time in a [seconds, nanoseconds] tuple Array, or a value on milliseconds. + * @returns string representation of the time + */ +export function timeConverter(time: number | [number, number]): string { + if (typeof time === 'number') { + // time - real time in milli-seconds + const milliSecondsInSecond = 1e3; + const timeInSeconds = Math.floor(time / milliSecondsInSecond); + const milliseconds = time - timeInSeconds * milliSecondsInSecond; + return timeConverterFromSecondsAndMilliseconds(timeInSeconds, milliseconds); + } else { + // time - high-resolution real time in a [seconds, nanoseconds] tuple Array + const nanoSecondsInMillisecond = 1e6; + const timeInSeconds = time[0]; + const milliseconds = time[1] / nanoSecondsInMillisecond; + return timeConverterFromSecondsAndMilliseconds(timeInSeconds, milliseconds); + } +} + +function timeConverterFromSecondsAndMilliseconds(timeInSeconds: number, milliseconds: number) { const hours = Math.floor(timeInSeconds / 3600); timeInSeconds = timeInSeconds - hours * 3600; const minutes = Math.floor(timeInSeconds / 60); timeInSeconds = timeInSeconds - minutes * 60; const seconds = Math.floor(timeInSeconds); - const milliseconds = time[1] / nanoSecondsInMillisecond; let result = ''; if (hours) { diff --git a/modules/tile-converter/src/pgm-loader.ts b/modules/tile-converter/src/pgm-loader.ts index 4c4704ff67..bb6e0d0101 100644 --- a/modules/tile-converter/src/pgm-loader.ts +++ b/modules/tile-converter/src/pgm-loader.ts @@ -1,24 +1,32 @@ -import type {LoaderWithParser} from '@loaders.gl/loader-utils'; -import {parsePGM} from '@math.gl/geoid'; +import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; +import {Geoid, parsePGM} from '@math.gl/geoid'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest'; +export {Geoid}; + +export type PGMLoaderOptions = LoaderOptions & { + pgm?: { + cubic?: boolean; + }; +}; + /** * Loader for PGM - Netpbm grayscale image format */ -export const PGMLoader: LoaderWithParser = { +export const PGMLoader: LoaderWithParser = { name: 'PGM - Netpbm grayscale image format', id: 'pgm', module: 'tile-converter', version: VERSION, mimeTypes: ['image/x-portable-graymap'], - // @ts-expect-error LoaderOptions does not have cubic parameter - parse: async (arrayBuffer, options) => parsePGM(new Uint8Array(arrayBuffer), options), + parse: async (arrayBuffer, options) => parsePGM(new Uint8Array(arrayBuffer), options?.pgm || {}), extensions: ['pgm'], options: { - // TODO - use pgm namespace - cubic: false + pgm: { + cubic: false + } } }; diff --git a/modules/tile-converter/src/slpk-extractor/helpers/file-handle-provider.ts b/modules/tile-converter/src/slpk-extractor/helpers/file-handle-provider.ts deleted file mode 100644 index 777a7ef942..0000000000 --- a/modules/tile-converter/src/slpk-extractor/helpers/file-handle-provider.ts +++ /dev/null @@ -1,91 +0,0 @@ -import {FileProvider} from '@loaders.gl/i3s'; -import {promises as fsPromises, PathLike} from 'fs'; - -/** - * Provides file data using node fs library - */ -export class FileHandleProvider implements FileProvider { - /** - * Returns a new copy of FileHandleProvider - * @param path The path to the file in file system - */ - static async from(path: PathLike): Promise { - const fileDescriptor = await fsPromises.open(path); - return new FileHandleProvider(fileDescriptor, (await fileDescriptor.stat()).size); - } - - /** - * The FileHandle from which data is provided - */ - private fileDescriptor: fsPromises.FileHandle; - - /** - * The file length in bytes - */ - private size: number; - - private constructor(fileDescriptor: fsPromises.FileHandle, size: number) { - this.fileDescriptor = fileDescriptor; - this.size = size; - } - - /** - * Gets an unsigned 8-bit integer (unsigned byte) at the specified byte offset from the start of the file. - * @param offset The offset, in bytes, from the start of the file where to read the data. - */ - async getUint8(offset: number): Promise { - const val = new Uint8Array( - (await this.fileDescriptor.read(Buffer.alloc(1), 0, 1, offset)).buffer.buffer - ).at(0); - if (val === undefined) { - throw new Error('something went wrong'); - } - return val; - } - - /** - * Gets an unsigned 16-bit integer (unsigned byte) at the specified byte offset from the start of the file. - * @param offset The offset, in bytes, from the start of the file where to read the data. - */ - async getUint16(offset: number): Promise { - const val = new Uint16Array( - (await this.fileDescriptor.read(Buffer.alloc(2), 0, 2, offset)).buffer.buffer - ).at(0); - if (val === undefined) { - throw new Error('something went wrong'); - } - return val; - } - - /** - * Gets an unsigned 32-bit integer (unsigned byte) at the specified byte offset from the start of the file. - * @param offset The offset, in bytes, from the start of the file where to read the data. - */ - async getUint32(offset: number): Promise { - const val = new Uint32Array( - (await this.fileDescriptor.read(Buffer.alloc(4), 0, 4, offset)).buffer.buffer - ).at(0); - if (val === undefined) { - throw new Error('something went wrong'); - } - return val; - } - - /** - * returns an ArrayBuffer whose contents are a copy of this file bytes from startOffset, inclusive, up to endOffset, exclusive. - * @param startOffset The offset, in bytes, from the start of the file where to start reading the data. - * @param endOffset The offset, in bytes, from the start of the file where to end reading the data. - */ - async slice(startOffset: number, endOffset: number): Promise { - const length = endOffset - startOffset; - return (await this.fileDescriptor.read(Buffer.alloc(length), 0, length, startOffset)).buffer - .buffer; - } - - /** - * the length (in bytes) of the data. - */ - get length(): number { - return this.size; - } -} diff --git a/modules/tile-converter/src/slpk-extractor/slpk-extractor.ts b/modules/tile-converter/src/slpk-extractor/slpk-extractor.ts index 897a3a48bb..ea6de7d097 100644 --- a/modules/tile-converter/src/slpk-extractor/slpk-extractor.ts +++ b/modules/tile-converter/src/slpk-extractor/slpk-extractor.ts @@ -1,15 +1,12 @@ import {isBrowser} from '@loaders.gl/core'; import {BROWSER_ERROR_MESSAGE} from '../constants'; -import {FileHandleProvider} from './helpers/file-handle-provider'; -import {parseZipLocalFileHeader} from '@loaders.gl/i3s'; import {path} from '@loaders.gl/loader-utils'; +import {FileHandleFile} from '@loaders.gl/loader-utils'; +import {parseZipLocalFileHeader} from '@loaders.gl/zip'; import {GZipCompression} from '@loaders.gl/compression'; import {writeFile} from '../lib/utils/file-utils'; -/** - * names of files that should be changed to index - */ const indexNames = [ '3dSceneLayer.json.gz', '3dNodeIndexDocument.json.gz', @@ -29,7 +26,7 @@ type File = { */ export default class SLPKExtractor { /** - * extract slpk to i3s + * Extract slpk to i3s * @param options * @param options.inputUrl the url to read SLPK file * @param options.outputPath the output filename @@ -41,9 +38,9 @@ export default class SLPKExtractor { } const {inputUrl} = options; - const provider = await FileHandleProvider.from(inputUrl); + const provider = new FileHandleFile(inputUrl); - let localHeader = await parseZipLocalFileHeader(0, provider); + let localHeader = await parseZipLocalFileHeader(0n, provider); while (localHeader) { await this.writeFile( await this.unGzip({ @@ -56,7 +53,7 @@ export default class SLPKExtractor { options.outputPath ); localHeader = await parseZipLocalFileHeader( - localHeader?.fileDataOffset + localHeader?.compressedSize, + localHeader.fileDataOffset + localHeader?.compressedSize, provider ); } @@ -68,6 +65,7 @@ export default class SLPKExtractor { * Defines file name and path for i3s format * @param fileName initial file name and path */ + private correctIndexNames(fileName: string): string | null { if (indexNames.includes(path.filename(path.join('/', fileName)))) { return path.join(path.dirname(fileName), 'index.json.gz'); @@ -85,6 +83,7 @@ export default class SLPKExtractor { const compression = new GZipCompression(); const decompressedData = await compression.decompress(file.data); + return {data: decompressedData, name: (file.name ?? '').slice(0, -3)}; } return Promise.resolve(file); diff --git a/modules/tile-converter/src/workers/3d-tiles-attributes-worker.ts b/modules/tile-converter/src/workers/3d-tiles-attributes-worker.ts deleted file mode 100644 index 7361d4ebcc..0000000000 --- a/modules/tile-converter/src/workers/3d-tiles-attributes-worker.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {createWorker} from '@loaders.gl/worker-utils'; -import B3dmConverter from '../3d-tiles-converter/helpers/b3dm-converter'; - -const b3dmConverter = new B3dmConverter(); - -createWorker(async (data, options = {}) => b3dmConverter.convert(data, options.featureAttributes)); diff --git a/modules/tile-converter/src/workers/i3s-attributes-worker.ts b/modules/tile-converter/src/workers/i3s-attributes-worker.ts deleted file mode 100644 index 3d78b9ce0c..0000000000 --- a/modules/tile-converter/src/workers/i3s-attributes-worker.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {createWorker} from '@loaders.gl/worker-utils'; -import {convertAttributes} from '../i3s-converter/helpers/geometry-converter'; - -createWorker( - async (data, options = {}) => - await convertAttributes(data, options.materialAndTextureList, options.useCartesianPositions) -); diff --git a/modules/tile-converter/test/3d-tiles-converter/helpers/b3dm-converter.spec.js b/modules/tile-converter/test/3d-tiles-converter/helpers/b3dm-converter.spec.js index 3a9fb0c9e4..1ec15b8acf 100644 --- a/modules/tile-converter/test/3d-tiles-converter/helpers/b3dm-converter.spec.js +++ b/modules/tile-converter/test/3d-tiles-converter/helpers/b3dm-converter.spec.js @@ -59,7 +59,11 @@ test('tile-converter(3d-tiles)#b3dm converter - should convert i3s node data to const attributes = await _loadAttributes(tile, ATTRIBUTES_STORAGE_INFO_STUB); const b3dmConverter = new B3dmConverter(); const encodedContent = await b3dmConverter.convert( - {tileContent: tile.content, textureFormat: tile.header.textureFormat}, + { + tileContent: tile.content, + textureFormat: tile.header.textureFormat, + box: tile.header.boundingVolume.box + }, attributes ); const batchId = i3sContent.featureIds; @@ -86,7 +90,8 @@ test('tile-converter(3d-tiles)#b3dm converter - should normalise positions corre const encodedContent = await b3dmConverter.convert({ tileContent: i3sContent, - textureFormat: tile.header.textureFormat + textureFormat: tile.header.textureFormat, + box: tile.header.boundingVolume.box }); const decodedContent = await load(encodedContent, Tiles3DLoader, { @@ -112,7 +117,8 @@ test('tile-converter(3d-tiles)#b3dm converter - should convert material', async const b3dmConverter = new B3dmConverter(); const encodedContent = await b3dmConverter.convert({ tileContent: tile.content, - textureFormat: tile.header.textureFormat + textureFormat: tile.header.textureFormat, + box: tile.header.boundingVolume.box }); const decodedContent = await load(encodedContent, Tiles3DLoader, { @@ -138,7 +144,8 @@ test('tile-converter(3d-tiles)#b3dm converter - should not convert incorrect nor const b3dmConverter = new B3dmConverter(); const encodedContent = await b3dmConverter.convert({ tileContent: tile.content, - textureFormat: tile.header.textureFormat + textureFormat: tile.header.textureFormat, + box: tile.header.boundingVolume.box }); const decodedContent = await load(encodedContent, Tiles3DLoader, { '3d-tiles': { @@ -155,7 +162,8 @@ test('tile-converter(3d-tiles)#b3dm converter - should not convert incorrect nor tile.content.attributes.normals.value.fill(0); const encodedContent2 = await b3dmConverter.convert({ tileContent: tile.content, - textureFormat: tile.header.textureFormat + textureFormat: tile.header.textureFormat, + box: tile.header.boundingVolume.box }); const decodedContent2 = await load(encodedContent2, Tiles3DLoader, { '3d-tiles': { @@ -179,7 +187,8 @@ test('tile-converter(3d-tiles)#b3dm converter - should handle geometry without n delete tile.content.attributes.normals; const encodedContent = await b3dmConverter.convert({ tileContent: tile.content, - textureFormat: tile.header.textureFormat + textureFormat: tile.header.textureFormat, + box: tile.header.boundingVolume.box }); const decodedContent = await load(encodedContent, Tiles3DLoader, { '3d-tiles': { @@ -209,7 +218,11 @@ test('tile-converter(3d-tiles)#b3dm converter - should convert i3s node data to const attributes = await _loadAttributes(tile, ATTRIBUTES_STORAGE_INFO_STUB); const b3dmConverter = new B3dmConverter(); const encodedContent = await b3dmConverter.convert( - {tileContent: tile.content, textureFormat: tile.header.textureFormat}, + { + tileContent: tile.content, + textureFormat: tile.header.textureFormat, + box: tile.header.boundingVolume.box + }, attributes ); t.ok(encodedContent); @@ -223,7 +236,11 @@ test('tile-converter(3d-tiles)#b3dm converter - should generate batchIds during const attributes = await _loadAttributes(tile, ATTRIBUTES_STORAGE_INFO_STUB); const b3dmConverter = new B3dmConverter(); const encodedContent = await b3dmConverter.convert( - {tileContent: tile.content, textureFormat: tile.header.textureFormat}, + { + tileContent: tile.content, + textureFormat: tile.header.textureFormat, + box: tile.header.boundingVolume.box + }, attributes ); diff --git a/modules/tile-converter/test/3d-tiles-converter/helpers/geometry-attributes.spec.js b/modules/tile-converter/test/3d-tiles-converter/helpers/geometry-attributes.spec.js index a764a0462a..c40fe06b0a 100644 --- a/modules/tile-converter/test/3d-tiles-converter/helpers/geometry-attributes.spec.js +++ b/modules/tile-converter/test/3d-tiles-converter/helpers/geometry-attributes.spec.js @@ -102,3 +102,52 @@ test('tile-converter(3d-tiles)#geometry-attributes - should return reordered att t.end(); } }); + +test('tile-converter(3d-tiles)#geometry-attributes - should support uvRegions', async (t) => { + if (!isBrowser) { + const oldAttributes = { + // prettier-ignore + positions: new Float32Array([-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-34,-25,-26,-27]), + // prettier-ignore + normals: new Float32Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,34,25,26,27]), + // prettier-ignore + texCoords: new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]), + // prettier-ignore + colors: new Uint8Array([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 + ]), + // prettier-ignore + uvRegions: new Uint16Array([ + 0, 65535, 32767, 65535, 0, 65535, 32767, 65535, 0, 65535, 32767, 65535, + 32767, 65535, 65535, 65535, 32767, 65535, 65535, 65535, 32767, 65535, 65535, 65535, + 0, 65535, 32767, 65535, 0, 65535, 32767, 65535, 0, 65535, 32767, 65535 + ]), + featureIndices: [0, 0, 0, 1, 1, 1, 0, 0, 0], + boundingVolumes: null, + mergedMaterials: [] + }; + + const resultAttributes = { + featureIds: [0, 1], + faceRange: new Uint32Array([0, 1, 2, 2]), + featureCount: 2, + // prettier-ignore + positions: new Float32Array([-1,-2,-3,-4,-5,-6,-7,-8,-9,-19,-20,-21,-22,-23,-34,-25,-26,-27,-10,-11,-12,-13,-14,-15,-16,-17,-18]), + // prettier-ignore + normals: new Float32Array([1,2,3,4,5,6,7,8,9,19,20,21,22,23,34,25,26,27,10,11,12,13,14,15,16,17,18]), + texCoords: new Float32Array([1, 2, 3, 4, 5, 6, 13, 14, 15, 16, 17, 18, 7, 8, 9, 10, 11, 12]), + // prettier-ignore + colors: new Uint8Array([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]), + // prettier-ignore + uvRegions: new Uint16Array([ + 0, 65535, 32767, 65535, 0, 65535, 32767, 65535, 0, 65535, 32767, 65535, + 0, 65535, 32767, 65535, 0, 65535, 32767, 65535, 0, 65535, 32767, 65535, + 32767, 65535, 65535, 65535, 32767, 65535, 65535, 65535, 32767, 65535, 65535, 65535 + ]) + }; + + const attributes = generateAttributes(oldAttributes); + t.ok(attributes); + t.deepEqual(attributes, resultAttributes); + t.end(); + } +}); diff --git a/modules/tile-converter/test/3d-tiles-converter/helpers/load-i3s.spec.ts b/modules/tile-converter/test/3d-tiles-converter/helpers/load-i3s.spec.ts new file mode 100644 index 0000000000..9d16264739 --- /dev/null +++ b/modules/tile-converter/test/3d-tiles-converter/helpers/load-i3s.spec.ts @@ -0,0 +1,21 @@ +import test from 'tape-promise/tape'; + +// @ts-expect-error it required `@loaders.gl/i3s/src/lib/helpers/i3s-nodepages-tiles` but it fails because tries to get the dependency from +// `@loaders.gl/i3s/src/src/lib/helpers/i3s-nodepages-tiles` +import I3SNodePagesTiles from '@loaders.gl/i3s/lib/helpers/i3s-nodepages-tiles'; +import { + TILESET_STUB, + getI3sTileHeader, + TEST_LAYER_URL +} from '@loaders.gl/i3s/test/test-utils/load-utils'; +import {loadI3SContent} from '../../../src/3d-tiles-converter/helpers/load-i3s'; + +test('tile-converter(i3s)#loadNestedTileset', async (t) => { + const i3sTilesetData = TILESET_STUB(); + const i3SNodePagesTiles = new I3SNodePagesTiles(i3sTilesetData, TEST_LAYER_URL, {}); + const node1 = await i3SNodePagesTiles.formTileFromNodePages(1); + const i3sTilesetHeader = await getI3sTileHeader({}, false, i3sTilesetData); + const content = await loadI3SContent(i3sTilesetHeader, node1, {}); + t.ok(content); + t.equal(content?.vertexCount, 25638); +}); diff --git a/modules/tile-converter/test/data/baseglobe/0/0/0/0.glb b/modules/tile-converter/test/data/baseglobe/0/0/0/0.glb new file mode 100644 index 0000000000..6f2b190fa1 Binary files /dev/null and b/modules/tile-converter/test/data/baseglobe/0/0/0/0.glb differ diff --git a/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/index.json b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/index.json new file mode 100644 index 0000000000..58ff5b1405 --- /dev/null +++ b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/index.json @@ -0,0 +1 @@ +{"serviceItemId":"82a6203820c740e88bbafb94132f6b03","serviceName":"Frankfurt-md-2","name":"Frankfurt-md-2","currentVersion":10.7,"serviceVersion":"1.8","supportedBindings":["REST"],"layers":[{"version":"{1F518F6A-9621-4476-8194-DD034FF34614}","id":0,"name":"Frankfurt-md-2","href":"./layers/0","layerType":"IntegratedMesh","spatialReference":{"wkid":4326,"latestWkid":4326,"vcsWkid":5773,"latestVcsWkid":5773},"capabilities":["View","Query"],"store":{"id":"{F23F2D74-EE90-4C42-AFF3-56CE04640CE6}","profile":"meshpyramids","version":"1.8","resourcePattern":["3dNodeIndexDocument","Attributes","SharedResource","Geometry"],"rootNode":"./nodes/root","extent":[8.638486093840738,50.10162577927056,8.714439334060673,50.11520548628318],"indexCRS":"http://www.opengis.net/def/crs/EPSG/0/4326","vertexCRS":"http://www.opengis.net/def/crs/EPSG/0/4326","normalReferenceFrame":"east-north-up","attributeEncoding":"application/octet-stream; version=1.6","textureEncoding":["image/jpeg","image/ktx2"],"lodType":"MeshPyramid","lodModel":"node-switching","defaultGeometrySchema":{"geometryType":"triangles","header":[{"property":"vertexCount","type":"UInt32"},{"property":"featureCount","type":"UInt32"}],"topology":"PerAttributeArray","ordering":["position","normal","uv0","color"],"vertexAttributes":{"position":{"valueType":"Float32","valuesPerElement":3},"normal":{"valueType":"Float32","valuesPerElement":3},"uv0":{"valueType":"Float32","valuesPerElement":2},"color":{"valueType":"UInt8","valuesPerElement":4}},"featureAttributeOrder":["id","faceRange"],"featureAttributes":{"id":{"valueType":"UInt64","valuesPerElement":1},"faceRange":{"valueType":"UInt32","valuesPerElement":2}}}},"fullExtent":{"xmin":8.638486093840738,"ymin":50.10162577927056,"xmax":8.714439334060673,"ymax":50.11520548628318,"zmin":-4668.025435449932,"zmax":5048.2198269797045},"heightModelInfo":{"heightModel":"gravity_related_height","vertCRS":"EGM96_Geoid","heightUnit":"meter"},"nodePages":{"nodesPerPage":64,"lodSelectionMetricType":"maxScreenThresholdSQ"},"materialDefinitions":[{"doubleSided":true,"emissiveFactor":[255,255,255],"alphaMode":"opaque","pbrMetallicRoughness":{"roughnessFactor":1,"metallicFactor":1,"baseColorTexture":{"textureSetDefinitionId":0}}}],"textureSetDefinitions":[{"formats":[{"name":"0","format":"jpg"},{"name":"1","format":"ktx2"}]},{"formats":[{"name":"0","format":"jpg"},{"name":"1","format":"ktx2"}],"atlas":true}],"geometryDefinitions":[{"geometryBuffers":[{"offset":8,"position":{"type":"Float32","component":3},"normal":{"type":"Float32","component":3},"uv0":{"type":"Float32","component":2},"color":{"type":"UInt8","component":4},"featureId":{"binding":"per-feature","type":"UInt64","component":1},"faceRange":{"binding":"per-feature","type":"UInt32","component":2}},{"compressedAttributes":{"encoding":"draco","attributes":["position","normal","uv0","color","feature-index"]}}]}],"attributeStorageInfo":[],"fields":[]}]} \ No newline at end of file diff --git a/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/index.json b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/index.json new file mode 100644 index 0000000000..0750502d87 --- /dev/null +++ b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/index.json @@ -0,0 +1 @@ +{"version":"{1F518F6A-9621-4476-8194-DD034FF34614}","id":0,"name":"Frankfurt-md-2","href":"./layers/0","layerType":"IntegratedMesh","spatialReference":{"wkid":4326,"latestWkid":4326,"vcsWkid":5773,"latestVcsWkid":5773},"capabilities":["View","Query"],"store":{"id":"{F23F2D74-EE90-4C42-AFF3-56CE04640CE6}","profile":"meshpyramids","version":"1.8","resourcePattern":["3dNodeIndexDocument","Attributes","SharedResource","Geometry"],"rootNode":"./nodes/root","extent":[8.638486093840738,50.10162577927056,8.714439334060673,50.11520548628318],"indexCRS":"http://www.opengis.net/def/crs/EPSG/0/4326","vertexCRS":"http://www.opengis.net/def/crs/EPSG/0/4326","normalReferenceFrame":"east-north-up","attributeEncoding":"application/octet-stream; version=1.6","textureEncoding":["image/jpeg","image/ktx2"],"lodType":"MeshPyramid","lodModel":"node-switching","defaultGeometrySchema":{"geometryType":"triangles","header":[{"property":"vertexCount","type":"UInt32"},{"property":"featureCount","type":"UInt32"}],"topology":"PerAttributeArray","ordering":["position","normal","uv0","color"],"vertexAttributes":{"position":{"valueType":"Float32","valuesPerElement":3},"normal":{"valueType":"Float32","valuesPerElement":3},"uv0":{"valueType":"Float32","valuesPerElement":2},"color":{"valueType":"UInt8","valuesPerElement":4}},"featureAttributeOrder":["id","faceRange"],"featureAttributes":{"id":{"valueType":"UInt64","valuesPerElement":1},"faceRange":{"valueType":"UInt32","valuesPerElement":2}}}},"fullExtent":{"xmin":8.638486093840738,"ymin":50.10162577927056,"xmax":8.714439334060673,"ymax":50.11520548628318,"zmin":-4668.025435449932,"zmax":5048.2198269797045},"heightModelInfo":{"heightModel":"gravity_related_height","vertCRS":"EGM96_Geoid","heightUnit":"meter"},"nodePages":{"nodesPerPage":64,"lodSelectionMetricType":"maxScreenThresholdSQ"},"materialDefinitions":[{"doubleSided":true,"emissiveFactor":[255,255,255],"alphaMode":"opaque","pbrMetallicRoughness":{"roughnessFactor":1,"metallicFactor":1,"baseColorTexture":{"textureSetDefinitionId":0}}}],"textureSetDefinitions":[{"formats":[{"name":"0","format":"jpg"},{"name":"1","format":"ktx2"}]},{"formats":[{"name":"0","format":"jpg"},{"name":"1","format":"ktx2"}],"atlas":true}],"geometryDefinitions":[{"geometryBuffers":[{"offset":8,"position":{"type":"Float32","component":3},"normal":{"type":"Float32","component":3},"uv0":{"type":"Float32","component":2},"color":{"type":"UInt8","component":4},"featureId":{"binding":"per-feature","type":"UInt64","component":1},"faceRange":{"binding":"per-feature","type":"UInt32","component":2}},{"compressedAttributes":{"encoding":"draco","attributes":["position","normal","uv0","color","feature-index"]}}]}],"attributeStorageInfo":[],"fields":[]} \ No newline at end of file diff --git a/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodepages/0/index.json b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodepages/0/index.json new file mode 100644 index 0000000000..f908de3a64 --- /dev/null +++ b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodepages/0/index.json @@ -0,0 +1 @@ +{"nodes":[{"index":0,"lodThreshold":0,"obb":{"center":[8.676496951388435,50.10841667136257,141.39774178531218],"halfSize":[2311.620410505966,2062.4371056037326,959.9614393664732],"quaternion":[0.2227952683502938,0.25834836931465127,0.7144546321993598,0.6109373295678506]},"children":[1]},{"index":1,"obb":{"center":[8.676496951388435,50.108416671362576,141.39774178662847],"halfSize":[2311.620410505966,2062.4371056037326,959.9614393664732],"quaternion":[0.2227952683502938,0.25834836931465127,0.7144546321993598,0.6109373295678506]},"lodThreshold":217717.37806230574,"children":[],"mesh":{"geometry":{"definition":0,"resource":1,"vertexCount":15312,"featureCount":1},"attribute":{"resource":1},"material":{"definition":0,"resource":1,"texelCountHint":524288}}}]} \ No newline at end of file diff --git a/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/geometries/0/index.bin b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/geometries/0/index.bin new file mode 100644 index 0000000000..39930c2449 Binary files /dev/null and b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/geometries/0/index.bin differ diff --git a/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/geometries/1/index.bin b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/geometries/1/index.bin new file mode 100644 index 0000000000..06e1051096 Binary files /dev/null and b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/geometries/1/index.bin differ diff --git a/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/index.json b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/index.json new file mode 100644 index 0000000000..7d2b60e1eb --- /dev/null +++ b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/index.json @@ -0,0 +1 @@ +{"version":"{9CF428F9-E6D2-47C1-86C7-20589A1510B2}","id":"1","level":1,"mbs":[8.676496951388435,50.108416671362576,141.39774178662847,3243.2640505992454],"obb":{"center":[8.676496951388435,50.108416671362576,141.39774178662847],"halfSize":[2311.620410505966,2062.4371056037326,959.9614393664732],"quaternion":[0.2227952683502938,0.25834836931465127,0.7144546321993598,0.6109373295678506]},"lodSelection":[{"metricType":"maxScreenThreshold","maxError":526.503917672968},{"metricType":"maxScreenThresholdSQ","maxError":217717.37806230574}],"children":[],"neighbors":[],"parentNode":{"id":"root","href":"../root","mbs":[8.676496951388435,50.10841667136257,141.39774178531218,3243.2640505992454],"obb":{"center":[8.676496951388435,50.10841667136257,141.39774178531218],"halfSize":[2311.620410505966,2062.4371056037326,959.9614393664732],"quaternion":[0.2227952683502938,0.25834836931465127,0.7144546321993598,0.6109373295678506]}},"sharedResource":{"href":"./shared"},"geometryData":[{"href":"./geometries/0"}],"textureData":[{"href":"./textures/0"},{"href":"./textures/1"}]} \ No newline at end of file diff --git a/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/shared/index.json b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/shared/index.json new file mode 100644 index 0000000000..5051cb2d18 --- /dev/null +++ b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/shared/index.json @@ -0,0 +1 @@ +{"materialDefinitions":{"Mat10":{"name":"standard","type":"standard","params":{"renderMode":"solid","shininess":1,"reflectivity":0,"ambient":[1,1,1],"diffuse":[1,1,1],"specular":[0,0,0],"useVertexColorAlpha":false,"vertexRegions":false,"vertexColors":true}}},"textureDefinitions":{"10":{"encoding":["image/jpeg"],"wrap":["none"],"atlas":false,"uvSet":"uv0","channels":"rgb","images":[{"id":"1170920505658572802","size":1024,"href":["../textures/0"],"length":[2097152]}]}}} \ No newline at end of file diff --git a/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/textures/0/index.jpg b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/textures/0/index.jpg new file mode 100644 index 0000000000..cf7f57c119 Binary files /dev/null and b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/textures/0/index.jpg differ diff --git a/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/textures/1/index.ktx2 b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/textures/1/index.ktx2 new file mode 100644 index 0000000000..a1f87635f4 Binary files /dev/null and b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/1/textures/1/index.ktx2 differ diff --git a/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/root/index.json b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/root/index.json new file mode 100644 index 0000000000..02ec05e642 --- /dev/null +++ b/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0/nodes/root/index.json @@ -0,0 +1 @@ +{"version":"{9CF428F9-E6D2-47C1-86C7-20589A1510B2}","id":"root","level":0,"mbs":[8.676496951388435,50.10841667136257,141.39774178531218,3243.2640505992454],"obb":{"center":[8.676496951388435,50.10841667136257,141.39774178531218],"halfSize":[2311.620410505966,2062.4371056037326,959.9614393664732],"quaternion":[0.2227952683502938,0.25834836931465127,0.7144546321993598,0.6109373295678506]},"lodSelection":[{"metricType":"maxScreenThresholdSQ","maxError":0},{"metricType":"maxScreenThreshold","maxError":0}],"children":[{"id":"1","href":"../1","obb":{"center":[8.676496951388435,50.108416671362576,141.39774178662847],"halfSize":[2311.620410505966,2062.4371056037326,959.9614393664732],"quaternion":[0.2227952683502938,0.25834836931465127,0.7144546321993598,0.6109373295678506]},"mbs":[8.676496951388435,50.108416671362576,141.39774178662847,3243.2640505992454]}],"parentNode":{}} \ No newline at end of file diff --git a/modules/tile-converter/test/deps-installer/deps-installer.spec.js b/modules/tile-converter/test/deps-installer/deps-installer.spec.js deleted file mode 100644 index 327eb86795..0000000000 --- a/modules/tile-converter/test/deps-installer/deps-installer.spec.js +++ /dev/null @@ -1,21 +0,0 @@ -import test from 'tape-promise/tape'; -import {isBrowser} from '@loaders.gl/core'; - -import {DepsInstaller} from '../../src/deps-installer/deps-installer'; -import {cleanUpPath, isFileExists} from '../utils/file-utils'; - -// The test cannot be run due to failing `npm install sharp join-images` in the test environment -test.skip('tile-converter(i3s-converter)#Install dependencies', async (t) => { - if (!isBrowser) { - const depsInstaller = new DepsInstaller(); - await depsInstaller.install('tmp', 'tmp-workers'); - - t.ok(await isFileExists('tmp/egm2008-5.pgm')); - t.ok(await isFileExists('tmp-workers/modules/i3s/dist/i3s-content-worker-node.js')); - t.ok(await isFileExists('tmp-workers/modules/draco/dist/draco-worker-node.js')); - t.ok(await isFileExists('tmp-workers/modules/textures/dist/basis-worker-node.js')); - } - await cleanUpPath('tmp'); - await cleanUpPath('tmp-workers'); - t.end(); -}); diff --git a/modules/tile-converter/test/i3s-converter/helpers/attribute-metadata-info.spec.ts b/modules/tile-converter/test/i3s-converter/helpers/attribute-metadata-info.spec.ts new file mode 100644 index 0000000000..baf1f0fae0 --- /dev/null +++ b/modules/tile-converter/test/i3s-converter/helpers/attribute-metadata-info.spec.ts @@ -0,0 +1,575 @@ +import test from 'tape-promise/tape'; +import {AttributeMetadataInfo} from '../../../src/i3s-converter/helpers/attribute-metadata-info'; + +test('tile-converter(i3s)#createPopupInfo - Should create popup info', async (t) => { + const attributeNames = ['OBJECTID', 'color', 'name', 'opt_uint8']; + + const popupInfo_expected = { + title: '{OBJECTID}', + mediaInfos: [], + popupElements: [ + { + fieldInfos: [ + { + fieldName: 'OBJECTID', + visible: true, + isEditable: false, + label: 'OBJECTID' + }, + { + fieldName: 'color', + visible: true, + isEditable: false, + label: 'color' + }, + { + fieldName: 'name', + visible: true, + isEditable: false, + label: 'name' + }, + { + fieldName: 'opt_uint8', + visible: true, + isEditable: false, + label: 'opt_uint8' + } + ], + type: 'fields' + } + ], + fieldInfos: [ + { + fieldName: 'OBJECTID', + visible: true, + isEditable: false, + label: 'OBJECTID' + }, + { + fieldName: 'color', + visible: true, + isEditable: false, + label: 'color' + }, + { + fieldName: 'name', + visible: true, + isEditable: false, + label: 'name' + }, + { + fieldName: 'opt_uint8', + visible: true, + isEditable: false, + label: 'opt_uint8' + } + ], + expressionInfos: [] + }; + + const attributeMetadataInfo: AttributeMetadataInfo = new AttributeMetadataInfo(); + // @ts-expect-error + // Calling a private method + const popupInfo = attributeMetadataInfo.createPopupInfo(attributeNames); + t.deepEqual(popupInfo, popupInfo_expected, 'popupInfo'); +}); + +test('tile-converter(i3s)#createStorageAttributes - Should create Attribute storage info', async (t) => { + const attributeTypesMap1 = { + color: 'string', + name: 'string', + opt_uint8: 'Int32' + }; + + const attributeTypesMap2 = { + // The same attributes as in #1 + color: 'string', + name: 'string', + opt_uint8: 'Int32', + // New attributes + opt_uint64: 'string', + opt_float32: 'double', + opt_enum: 'string' + }; + + const attributeTypesMap3 = {}; + + const attributeStorageInfo1_expected = [ + { + key: 'f_0', + name: 'OBJECTID', + ordering: ['attributeValues'], + header: [ + { + property: 'count', + valueType: 'UInt32' + } + ], + attributeValues: { + valueType: 'Oid32', + valuesPerElement: 1 + } + }, + { + key: 'f_1', + name: 'color', + ordering: ['attributeByteCounts', 'attributeValues'], + header: [ + { + property: 'count', + valueType: 'UInt32' + }, + { + property: 'attributeValuesByteCount', + valueType: 'UInt32' + } + ], + attributeValues: { + valueType: 'String', + encoding: 'UTF-8', + valuesPerElement: 1 + }, + attributeByteCounts: { + valueType: 'UInt32', + valuesPerElement: 1 + } + }, + { + key: 'f_2', + name: 'name', + ordering: ['attributeByteCounts', 'attributeValues'], + header: [ + { + property: 'count', + valueType: 'UInt32' + }, + { + property: 'attributeValuesByteCount', + valueType: 'UInt32' + } + ], + attributeValues: { + valueType: 'String', + encoding: 'UTF-8', + valuesPerElement: 1 + }, + attributeByteCounts: { + valueType: 'UInt32', + valuesPerElement: 1 + } + }, + { + key: 'f_3', + name: 'opt_uint8', + ordering: ['attributeValues'], + header: [ + { + property: 'count', + valueType: 'UInt32' + } + ], + attributeValues: { + valueType: 'Int32', + valuesPerElement: 1 + } + } + ]; + + const fields1_expected = [ + { + name: 'OBJECTID', + type: 'esriFieldTypeOID', + alias: 'OBJECTID' + }, + { + name: 'color', + type: 'esriFieldTypeString', + alias: 'color' + }, + { + name: 'name', + type: 'esriFieldTypeString', + alias: 'name' + }, + { + name: 'opt_uint8', + type: 'esriFieldTypeInteger', + alias: 'opt_uint8' + } + ]; + + const popupInfo1_expected = { + title: '{OBJECTID}', + mediaInfos: [], + popupElements: [ + { + fieldInfos: [ + { + fieldName: 'OBJECTID', + visible: true, + isEditable: false, + label: 'OBJECTID' + }, + { + fieldName: 'color', + visible: true, + isEditable: false, + label: 'color' + }, + { + fieldName: 'name', + visible: true, + isEditable: false, + label: 'name' + }, + { + fieldName: 'opt_uint8', + visible: true, + isEditable: false, + label: 'opt_uint8' + } + ], + type: 'fields' + } + ], + fieldInfos: [ + { + fieldName: 'OBJECTID', + visible: true, + isEditable: false, + label: 'OBJECTID' + }, + { + fieldName: 'color', + visible: true, + isEditable: false, + label: 'color' + }, + { + fieldName: 'name', + visible: true, + isEditable: false, + label: 'name' + }, + { + fieldName: 'opt_uint8', + visible: true, + isEditable: false, + label: 'opt_uint8' + } + ], + expressionInfos: [] + }; + ////////////////////////// + const attributeStorageInfo2_expected = [ + { + key: 'f_0', + name: 'OBJECTID', + ordering: ['attributeValues'], + header: [ + { + property: 'count', + valueType: 'UInt32' + } + ], + attributeValues: { + valueType: 'Oid32', + valuesPerElement: 1 + } + }, + { + key: 'f_1', + name: 'color', + ordering: ['attributeByteCounts', 'attributeValues'], + header: [ + { + property: 'count', + valueType: 'UInt32' + }, + { + property: 'attributeValuesByteCount', + valueType: 'UInt32' + } + ], + attributeValues: { + valueType: 'String', + encoding: 'UTF-8', + valuesPerElement: 1 + }, + attributeByteCounts: { + valueType: 'UInt32', + valuesPerElement: 1 + } + }, + { + key: 'f_2', + name: 'name', + ordering: ['attributeByteCounts', 'attributeValues'], + header: [ + { + property: 'count', + valueType: 'UInt32' + }, + { + property: 'attributeValuesByteCount', + valueType: 'UInt32' + } + ], + attributeValues: { + valueType: 'String', + encoding: 'UTF-8', + valuesPerElement: 1 + }, + attributeByteCounts: { + valueType: 'UInt32', + valuesPerElement: 1 + } + }, + { + key: 'f_3', + name: 'opt_uint8', + ordering: ['attributeValues'], + header: [ + { + property: 'count', + valueType: 'UInt32' + } + ], + attributeValues: { + valueType: 'Int32', + valuesPerElement: 1 + } + }, + { + key: 'f_4', + name: 'opt_uint64', + ordering: ['attributeByteCounts', 'attributeValues'], + header: [ + { + property: 'count', + valueType: 'UInt32' + }, + { + property: 'attributeValuesByteCount', + valueType: 'UInt32' + } + ], + attributeValues: { + valueType: 'String', + encoding: 'UTF-8', + valuesPerElement: 1 + }, + attributeByteCounts: { + valueType: 'UInt32', + valuesPerElement: 1 + } + }, + { + key: 'f_5', + name: 'opt_float32', + ordering: ['attributeValues'], + header: [ + { + property: 'count', + valueType: 'UInt32' + } + ], + attributeValues: { + valueType: 'Float64', + valuesPerElement: 1 + } + }, + { + key: 'f_6', + name: 'opt_enum', + ordering: ['attributeByteCounts', 'attributeValues'], + header: [ + { + property: 'count', + valueType: 'UInt32' + }, + { + property: 'attributeValuesByteCount', + valueType: 'UInt32' + } + ], + attributeValues: { + valueType: 'String', + encoding: 'UTF-8', + valuesPerElement: 1 + }, + attributeByteCounts: { + valueType: 'UInt32', + valuesPerElement: 1 + } + } + ]; + + const fields2_expected = [ + { + name: 'OBJECTID', + type: 'esriFieldTypeOID', + alias: 'OBJECTID' + }, + { + name: 'color', + type: 'esriFieldTypeString', + alias: 'color' + }, + { + name: 'name', + type: 'esriFieldTypeString', + alias: 'name' + }, + { + name: 'opt_uint8', + type: 'esriFieldTypeInteger', + alias: 'opt_uint8' + }, + { + name: 'opt_uint64', + type: 'esriFieldTypeString', + alias: 'opt_uint64' + }, + { + name: 'opt_float32', + type: 'esriFieldTypeDouble', + alias: 'opt_float32' + }, + { + name: 'opt_enum', + type: 'esriFieldTypeString', + alias: 'opt_enum' + } + ]; + + const popupInfo2_expected = { + title: '{OBJECTID}', + mediaInfos: [], + popupElements: [ + { + fieldInfos: [ + { + fieldName: 'OBJECTID', + visible: true, + isEditable: false, + label: 'OBJECTID' + }, + { + fieldName: 'color', + visible: true, + isEditable: false, + label: 'color' + }, + { + fieldName: 'name', + visible: true, + isEditable: false, + label: 'name' + }, + { + fieldName: 'opt_uint8', + visible: true, + isEditable: false, + label: 'opt_uint8' + }, + { + fieldName: 'opt_uint64', + visible: true, + isEditable: false, + label: 'opt_uint64' + }, + { + fieldName: 'opt_float32', + visible: true, + isEditable: false, + label: 'opt_float32' + }, + { + fieldName: 'opt_enum', + visible: true, + isEditable: false, + label: 'opt_enum' + } + ], + type: 'fields' + } + ], + fieldInfos: [ + { + fieldName: 'OBJECTID', + visible: true, + isEditable: false, + label: 'OBJECTID' + }, + { + fieldName: 'color', + visible: true, + isEditable: false, + label: 'color' + }, + { + fieldName: 'name', + visible: true, + isEditable: false, + label: 'name' + }, + { + fieldName: 'opt_uint8', + visible: true, + isEditable: false, + label: 'opt_uint8' + }, + { + fieldName: 'opt_uint64', + visible: true, + isEditable: false, + label: 'opt_uint64' + }, + { + fieldName: 'opt_float32', + visible: true, + isEditable: false, + label: 'opt_float32' + }, + { + fieldName: 'opt_enum', + visible: true, + isEditable: false, + label: 'opt_enum' + } + ], + expressionInfos: [] + }; + + const attributeMetadataInfo: AttributeMetadataInfo = new AttributeMetadataInfo(); + attributeMetadataInfo.addMetadataInfo(attributeTypesMap1); + + t.deepEqual( + attributeMetadataInfo.attributeStorageInfo, + attributeStorageInfo1_expected, + 'attributeStorageInfo #1' + ); + t.deepEqual(attributeMetadataInfo.fields, fields1_expected, 'fields #1'); + t.deepEqual(attributeMetadataInfo.popupInfo, popupInfo1_expected, 'popupInfo #1'); + + attributeMetadataInfo.addMetadataInfo(attributeTypesMap2); + + t.deepEqual( + attributeMetadataInfo.attributeStorageInfo, + attributeStorageInfo2_expected, + 'attributeStorageInfo #2' + ); + t.deepEqual(attributeMetadataInfo.fields, fields2_expected, 'fields #2'); + t.deepEqual(attributeMetadataInfo.popupInfo, popupInfo2_expected, 'popupInfo #2'); + + attributeMetadataInfo.addMetadataInfo(attributeTypesMap3); + // The result should be the same as for #2 + t.deepEqual( + attributeMetadataInfo.attributeStorageInfo, + attributeStorageInfo2_expected, + 'attributeStorageInfo #3' + ); + t.deepEqual(attributeMetadataInfo.fields, fields2_expected, 'fields #3'); + t.deepEqual(attributeMetadataInfo.popupInfo, popupInfo2_expected, 'popupInfo #3'); +}); diff --git a/modules/tile-converter/test/i3s-converter/helpers/batch-ids-extensions.spec.js b/modules/tile-converter/test/i3s-converter/helpers/batch-ids-extensions.spec.js index cb4251464e..b7fae4e914 100644 --- a/modules/tile-converter/test/i3s-converter/helpers/batch-ids-extensions.spec.js +++ b/modules/tile-converter/test/i3s-converter/helpers/batch-ids-extensions.spec.js @@ -10,21 +10,7 @@ test('tile-converter(i3s)#handleBatchIdsExtensions - Should return empty array i const batchIds = handleBatchIdsExtensions(attributes, primitive, images); t.deepEqual(batchIds, []); -}); - -test('tile-converter(i3s)#handleBatchIdsExtensions - Should return empty array for not supported EXT_mesh_features yet', async (t) => { - const attributes = {}; - const primitive = { - extensions: { - EXT_mesh_features: {} - } - }; - const images = []; - - // @ts-ignore - const batchIds = handleBatchIdsExtensions(attributes, primitive, images); - - t.deepEqual(batchIds, []); + t.end(); }); test('tile-converter(i3s)#handleBatchIdsExtensions - Should return empty array for not supported extensions', async (t) => { @@ -40,6 +26,7 @@ test('tile-converter(i3s)#handleBatchIdsExtensions - Should return empty array f const batchIds = handleBatchIdsExtensions(attributes, primitive, images); t.deepEqual(batchIds, []); + t.end(); }); test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Should return empty array for empty EXT_feature_metadata extension', async (t) => { @@ -55,6 +42,7 @@ test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Shou const batchIds = handleBatchIdsExtensions(attributes, primitive, images); t.deepEqual(batchIds, []); + t.end(); }); test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Should return batchIds if attribute exists', async (t) => { @@ -81,6 +69,7 @@ test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Shou const expectedResult = new Float32Array([1, 2, 3]); t.deepEqual(batchIds, expectedResult); + t.end(); }); test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Should return empty array for implicit batchIds if no POSITIONS', async (t) => { @@ -105,6 +94,7 @@ test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Shou const batchIds = handleBatchIdsExtensions(attributes, primitive, images); t.deepEqual(batchIds, []); + t.end(); }); test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Should return implicit batchIds for divisor = 0 ', async (t) => { @@ -132,6 +122,7 @@ test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Shou const expectedResult = new Float32Array([0, 0, 0]); t.deepEqual(batchIds, expectedResult); + t.end(); }); test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Should return implicit batchIds for divisor = 1', async (t) => { @@ -159,6 +150,7 @@ test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Shou const expectedResult = new Float32Array([0, 1, 2]); t.deepEqual(batchIds, expectedResult); + t.end(); }); test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Should return implicit batchIds for divisor = 2', async (t) => { @@ -186,9 +178,10 @@ test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Shou const expectedResult = new Float32Array([0, 0, 1, 1, 2]); t.deepEqual(batchIds, expectedResult); + t.end(); }); -test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Should return empty batchIds for compressed texture', async (t) => { +test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Should return empty batchIds for compressed texture', (t) => { const attributes = { POSITIONS: {value: new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9])}, TEXCOORD_0: { @@ -230,6 +223,7 @@ test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Shou const batchIds = handleBatchIdsExtensions(attributes, primitive, images); t.deepEqual(batchIds, []); + t.end(); }); test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Should return batchIds for featureTextures provided', async (t) => { @@ -255,9 +249,10 @@ test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Shou } ]; // @ts-ignore - const batchIds = handleBatchIdsExtensions(attributes, primitive, images); + const batchIds = handleBatchIdsExtensions(attributes, primitive, images, 'firstTextureName'); t.deepEqual(batchIds, [33, 35, 29, 32, 24, 28]); + t.end(); }); test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Should return batchIds for texture', async (t) => { @@ -298,4 +293,5 @@ test('tile-converter(i3s)#handleBatchIdsExtensions - EXT_feature_metadata - Shou const expectedResult = [255, 0, 255]; t.deepEqual(batchIds, expectedResult); + t.end(); }); diff --git a/modules/tile-converter/test/i3s-converter/helpers/coordinate-converter.spec.js b/modules/tile-converter/test/i3s-converter/helpers/coordinate-converter.spec.js index e7125502e5..d4894761b4 100644 --- a/modules/tile-converter/test/i3s-converter/helpers/coordinate-converter.spec.js +++ b/modules/tile-converter/test/i3s-converter/helpers/coordinate-converter.spec.js @@ -29,6 +29,6 @@ test('tile-converter(i3s)#convertBoundingVolumeToI3SFullExtent', async (t) => { ymin: 37.61647458074144, ymax: 37.908958954561705, zmin: -4625.401184284537, - zmax: 4877.104767145071 + zmax: 4877.1047671450715 }); }); diff --git a/modules/tile-converter/test/i3s-converter/helpers/feature-attributes.spec.js b/modules/tile-converter/test/i3s-converter/helpers/feature-attributes.spec.js deleted file mode 100644 index a20c668ddd..0000000000 --- a/modules/tile-converter/test/i3s-converter/helpers/feature-attributes.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -import test from 'tape-promise/tape'; -import { - flattenPropertyTableByFeatureIds, - checkPropertiesLength -} from '../../../src/i3s-converter/helpers/feature-attributes'; - -test('tile-converter(i3s)#flattenPropertyTableByFeatureIds - Should return flatten property table', async (t) => { - const featureIds = [0, 1, 3]; - const propertyTable = { - component: ['Wall', 'Roof', 'Clock', 'Frames'], - color: ['red', 'green', 'blue', 'white'] - }; - const expectedResult = { - component: ['Wall', 'Roof', 'Frames'], - color: ['red', 'green', 'white'] - }; - const result = flattenPropertyTableByFeatureIds(featureIds, propertyTable); - t.deepEqual(result, expectedResult); -}); - -test('tile-converter(i3s)#checkPropertiesLength - Should return false if properies count is the same as featureIds count', async (t) => { - const featureIds = [0, 1, 3]; - const propertyTable = { - component: ['Wall', 'Roof', 'Clock'], - color: ['red', 'green', 'blue'] - }; - const result = checkPropertiesLength(featureIds, propertyTable); - t.deepEqual(result, false); -}); - -test('tile-converter(i3s)#checkPropertiesLength - Should return true if properies count is not the same as featureIds count', async (t) => { - const featureIds = [0, 1, 3]; - const propertyTable = { - component: ['Wall', 'Roof', 'Clock', 'Frames'], - color: ['red', 'green', 'blue', 'white'] - }; - const result = checkPropertiesLength(featureIds, propertyTable); - t.deepEqual(result, true); -}); diff --git a/modules/tile-converter/test/i3s-converter/helpers/feature-attributes.spec.ts b/modules/tile-converter/test/i3s-converter/helpers/feature-attributes.spec.ts new file mode 100644 index 0000000000..bcb8cfa665 --- /dev/null +++ b/modules/tile-converter/test/i3s-converter/helpers/feature-attributes.spec.ts @@ -0,0 +1,134 @@ +import test from 'tape-promise/tape'; +import { + flattenPropertyTableByFeatureIds, + checkPropertiesLength, + getAttributeTypesMapFromSchema, + getAttributeTypesMapFromPropertyTable, + getAttributeType +} from '../../../src/i3s-converter/helpers/feature-attributes'; +import type {GLTFPostprocessed} from '@loaders.gl/gltf'; + +test('tile-converter(i3s)#flattenPropertyTableByFeatureIds - Should return flatten property table', async (t) => { + const featureIdsMap = {0: 0, 1: 1, 3: 3}; + const propertyTable = { + component: ['Wall', 'Roof', 'Clock', 'Frames'], + color: ['red', 'green', 'blue', 'white'] + }; + const expectedResult = { + component: ['Wall', 'Roof', 'Frames'], + color: ['red', 'green', 'white'] + }; + const result = flattenPropertyTableByFeatureIds(featureIdsMap, propertyTable); + t.deepEqual(result, expectedResult); +}); + +test('tile-converter(i3s)#checkPropertiesLength - Should return false if properies count is the same as featureIds count', async (t) => { + const featureIds = [0, 1, 3]; + const propertyTable = { + component: ['Wall', 'Roof', 'Clock'], + color: ['red', 'green', 'blue'] + }; + const result = checkPropertiesLength(featureIds, propertyTable); + t.deepEqual(result, false); +}); + +test('tile-converter(i3s)#checkPropertiesLength - Should return true if properies count is not the same as featureIds count', async (t) => { + const featureIds = [0, 1, 3]; + const propertyTable = { + component: ['Wall', 'Roof', 'Clock', 'Frames'], + color: ['red', 'green', 'blue', 'white'] + }; + const result = checkPropertiesLength(featureIds, propertyTable); + t.deepEqual(result, true); +}); + +test('tile-converter(i3s)#getAttributeType - Should return the type of attribute', async (t) => { + const attributes = ['', 'myName', 0, 1, 2n, 3.5]; + const typesExpected = ['string', 'string', 'Int32', 'Int32', 'string', 'double']; + const types: string[] = []; + for (let attribute of attributes) { + types.push(getAttributeType(attribute)); + } + t.deepEqual(types, typesExpected, 'popupInfo'); +}); + +test('tile-converter(i3s)#getAttributeTypesFromSchema - Should return attributes type taken from the extension schema', async (t) => { + const gltfJson = { + extensions: { + EXT_structural_metadata: { + schema: { + id: 'schema', + classes: { + owt_lulc: { + properties: { + color: { + name: 'Color', + description: 'This is ARRAY of UINT8', + type: 'SCALAR', + componentType: 'UINT8', + array: true, + count: 3, + required: true + }, + name: { + name: 'Name', + description: 'This is a NAME', + type: 'STRING', + required: true + }, + opt_uint8: { + componentType: 'UINT8' + }, + opt_uint64: { + componentType: 'UINT64' + }, + opt_float32: { + componentType: 'FLOAT32' + }, + opt_enum: { + type: 'ENUM' + } + } + } + } + } + } + } + }; + + const schema_expected = { + color: 'string', + name: 'string', + opt_uint8: 'Int32', + opt_uint64: 'string', + opt_float32: 'double', + opt_enum: 'string' + }; + + let attributePropertySet = getAttributeTypesMapFromSchema( + gltfJson as unknown as GLTFPostprocessed, + 'owt_lulc' + ); + t.deepEqual(attributePropertySet, schema_expected, 'attribute type taken from the schema'); +}); + +test('tile-converter(i3s)#getAttributeTypesFromPropertyTable - Should return attributes type taken from the extension schema', async (t) => { + const propertyTable = { + color: ['red', 'green'], + name: ['myRed', 'myGreen'], + opt_uint8: [255, 255], + opt_uint64: [2n, 3n], + opt_float32: [3.5, 4.0] + }; + + const typesExpected = { + color: 'string', + name: 'string', + opt_uint8: 'Int32', + opt_uint64: 'string', + opt_float32: 'double' + }; + + let attributeTypes = getAttributeTypesMapFromPropertyTable(propertyTable); + t.deepEqual(attributeTypes, typesExpected, 'attribute type taken from the property table'); +}); diff --git a/modules/tile-converter/test/i3s-converter/helpers/geometry-attributes.spec.ts b/modules/tile-converter/test/i3s-converter/helpers/geometry-attributes.spec.ts new file mode 100644 index 0000000000..3489892bfe --- /dev/null +++ b/modules/tile-converter/test/i3s-converter/helpers/geometry-attributes.spec.ts @@ -0,0 +1,80 @@ +import test from 'tape-promise/tape'; +import {ConvertedAttributes, GeometryAttributes} from '../../../src/i3s-converter/types'; +import {generateAttributes} from '../../../src/i3s-converter/helpers/geometry-attributes'; +import {areNumberArraysEqual} from '../../utils/compareArrays'; + +test('tile-converter(i3s)#generateAttributes - Should re-arrange attributes by featureIds', async (t) => { + const attributes: ConvertedAttributes = { + // prettier-ignore + positions: new Float32Array([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, + -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9 + ]), + // prettier-ignore + normals: new Float32Array([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, + -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9 + ]), + // prettier-ignore + texCoords: new Float32Array([ + 0, 1, 2, 3, 4, 5, + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, + -0.1, -0.2, -0.3, -0.4, -0.5, -0.6 + ]), + // prettier-ignore + colors: new Uint8Array([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212 + ]), + // prettier-ignore + uvRegions: new Uint16Array(), + featureIndices: [1, 1, 1, 120, 120, 120, 1, 1, 1], + boundingVolumes: null, + mergedMaterials: [] + }; + + const expectedResult: GeometryAttributes = { + // prettier-ignore + positions: new Float32Array([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9, + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 + ]), + // prettier-ignore + normals: new Float32Array([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, + -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9, + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 + ]), + // prettier-ignore + texCoords: new Float32Array([ + 0, 1, 2, 3, 4, 5, + -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6 + ]), + // prettier-ignore + colors: new Uint8Array([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111 + ]), + uvRegions: new Uint16Array(), + faceRange: new Uint32Array([0, 1, 2, 2]), + featureIds: [1, 120], + featureCount: 2 + }; + + const result = generateAttributes(attributes); + t.ok(areNumberArraysEqual(result.positions, expectedResult.positions)); + t.ok(areNumberArraysEqual(result.normals, expectedResult.normals)); + t.ok(areNumberArraysEqual(result.texCoords, expectedResult.texCoords)); + t.deepEqual(result.colors, expectedResult.colors); + t.deepEqual(result.uvRegions, expectedResult.uvRegions); + t.deepEqual(result.faceRange, expectedResult.faceRange); + t.deepEqual(result.featureIds, expectedResult.featureIds); + t.equal(result.featureCount, 2); + t.end(); +}); diff --git a/modules/tile-converter/test/i3s-converter/helpers/geometry-converter.spec.js b/modules/tile-converter/test/i3s-converter/helpers/geometry-converter.spec.ts similarity index 71% rename from modules/tile-converter/test/i3s-converter/helpers/geometry-converter.spec.js rename to modules/tile-converter/test/i3s-converter/helpers/geometry-converter.spec.ts index d312c4da54..428b319959 100644 --- a/modules/tile-converter/test/i3s-converter/helpers/geometry-converter.spec.js +++ b/modules/tile-converter/test/i3s-converter/helpers/geometry-converter.spec.ts @@ -1,15 +1,15 @@ import test from 'tape-promise/tape'; import {Tiles3DLoader} from '@loaders.gl/3d-tiles'; -import {load, fetchFile, setLoaderOptions, getLoaderOptions, isBrowser} from '@loaders.gl/core'; -import {getWorkerURL, WorkerFarm} from '@loaders.gl/worker-utils'; -import {DracoWriterWorker} from '@loaders.gl/draco'; +import {load, setLoaderOptions, isBrowser} from '@loaders.gl/core'; +import {WorkerFarm} from '@loaders.gl/worker-utils'; import convertB3dmToI3sGeometry, { getPropertyTable } from '../../../src/i3s-converter/helpers/geometry-converter'; import {PGMLoader} from '../../../src/pgm-loader'; -import {createdStorageAttribute} from '../../../src/i3s-converter/helpers/feature-attributes'; -import {I3SAttributesWorker} from '../../../src/i3s-attributes-worker'; -import {BoundingSphere} from '@math.gl/culling'; +import {getAttributeTypesMapFromPropertyTable} from '../../../src/i3s-converter/helpers/feature-attributes'; +import {AttributeMetadataInfo} from '../../../src/i3s-converter/helpers/attribute-metadata-info'; + +import {BoundingSphere, OrientedBoundingBox} from '@math.gl/culling'; import {Matrix4} from '@math.gl/core'; const PGM_FILE_PATH = '@loaders.gl/tile-converter/test/data/egm84-30.pgm'; @@ -24,6 +24,7 @@ const TRIANGLE_STRIP_B3DM_FILE_PATH = '@loaders.gl/tile-converter/test/data/TriangleStrip/lod1_0.b3dm'; const HELSINKI_GLB_FILE_PATH = '@loaders.gl/tile-converter/test/data/helsinki-glb-8-meshopt/0/0.glb'; +const BASEGLOBE_GLB_FILE_PATH = '@loaders.gl/tile-converter/test/data/baseglobe/0/0/0/0.glb'; setLoaderOptions({ _worker: 'test' @@ -47,7 +48,6 @@ test('tile-converter(i3s)#convert B3dmToI3sGeometry - should convert Frankfurt t 4051833.805439, 618316.801881, 4870677.172590001 ]); const geoidHeightModel = await load(PGM_FILE_PATH, PGMLoader); - const workerSource = await getWorkersSource(); const attributeStorageInfo = []; const shouldMergeMaterials = false; try { @@ -63,7 +63,7 @@ test('tile-converter(i3s)#convert B3dmToI3sGeometry - should convert Frankfurt t generateBoundingVolumes, shouldMergeMaterials, geoidHeightModel, - workerSource + {} ); t.ok(convertedResources); if (!convertedResources) { @@ -133,7 +133,6 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert Berlin tile 3781178.760596639, 902182.0936989671, 5039803.738586299 ]); const geoidHeightModel = await load(PGM_FILE_PATH, PGMLoader); - const workerSource = await getWorkersSource(); const attributeStorageInfo = []; try { const convertedResources = await convertB3dmToI3sGeometry( @@ -148,7 +147,7 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert Berlin tile generageBoundingVolumes, shouldMergeMaterials, geoidHeightModel, - workerSource + {} ); t.ok(convertedResources); if (!convertedResources) { @@ -214,7 +213,6 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert New York til 1319833.032477655, -4673588.626640962, 4120866.796624521 ]); const geoidHeightModel = await load(PGM_FILE_PATH, PGMLoader); - const workerSource = await getWorkersSource(); const attributeStorageInfo = getAttributeStorageInfo(propertyTable); try { const convertedResources = await convertB3dmToI3sGeometry( @@ -229,7 +227,7 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert New York til generageBoundingVolumes, shouldMergeMaterials, geoidHeightModel, - workerSource + {} ); t.ok(convertedResources); if (!convertedResources) { @@ -280,7 +278,6 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert Ferry tile c -2703528.7614193764, -4261014.993900511, 3887572.9889940596 ]); const geoidHeightModel = await load(PGM_FILE_PATH, PGMLoader); - const workerSource = await getWorkersSource(); const attributeStorageInfo = getAttributeStorageInfo(propertyTable); try { const convertedResources = await convertB3dmToI3sGeometry( @@ -295,7 +292,7 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert Ferry tile c generageBoundingVolumes, shouldMergeMaterials, geoidHeightModel, - workerSource + {} ); t.ok(convertedResources); if (!convertedResources) { @@ -354,7 +351,6 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - TRIANGLE_STRIPS should be c -4386794.587985844, 733486.8163247632, -4556196.147240348 ]); const geoidHeightModel = await load(PGM_FILE_PATH, PGMLoader); - const workerSource = await getWorkersSource(); const attributeStorageInfo = getAttributeStorageInfo(propertyTable); try { const convertedResources = await convertB3dmToI3sGeometry( @@ -369,7 +365,7 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - TRIANGLE_STRIPS should be c generageBoundingVolumes, shouldMergeMaterials, geoidHeightModel, - workerSource + {} ); t.ok(convertedResources); if (!convertedResources) { @@ -411,7 +407,6 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should not convert point ge 2881727.346362028, 1342482.044833547, 5510923.203394569 ]); const geoidHeightModel = await load(PGM_FILE_PATH, PGMLoader); - const workerSource = await getWorkersSource(); const attributeStorageInfo = getAttributeStorageInfo(propertyTable); try { await convertB3dmToI3sGeometry( @@ -426,7 +421,7 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should not convert point ge generageBoundingVolumes, shouldMergeMaterials, geoidHeightModel, - workerSource + {} ); t.fail('The conversion should fail'); } catch (e) { @@ -441,6 +436,26 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should not convert point ge t.end(); }); +test('tile-converter(i3s)#getPropertyTable - should get the property table from EXT_feature_metadata extension', async (t) => { + const propertyTableExpected = { + 'r3dm::uncertainty_ce90sum': [33, 35, 29, 32, 24, 28, 25, 39, 30, 34, 27, 36, 31, 37, 23] + }; + + if (isBrowser) { + t.end(); + return; + } + const tileContent = await load(MUSCATATUCK_GLB_FILE_PATH, Tiles3DLoader); + const propertyTable = getPropertyTable(tileContent, 'r3dm::uncertainty_ce90sum'); + t.deepEquals(propertyTable, propertyTableExpected, 'Returns property table'); + + // Clean up worker pools + const workerFarm = WorkerFarm.getWorkerFarm({}); + workerFarm.destroy(); + + t.end(); +}); + test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert tile content with EXT_feature_metadata extension', async (t) => { if (isBrowser) { t.end(); @@ -453,11 +468,10 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert tile content const generageBoundingVolumes = false; const shouldMergeMaterials = false; const tileContent = await load(MUSCATATUCK_GLB_FILE_PATH, Tiles3DLoader); - const propertyTable = getPropertyTable(tileContent); + const propertyTable = getPropertyTable(tileContent, 'r3dm::uncertainty_ce90sum'); const tileTransform = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); const tileBoundingVolume = new BoundingSphere([386500, -4945000, 3997000]); const geoidHeightModel = await load(PGM_FILE_PATH, PGMLoader); - const workerSource = await getWorkersSource(); const attributeStorageInfo = getAttributeStorageInfo(propertyTable); try { const convertedResources = await convertB3dmToI3sGeometry( @@ -472,7 +486,8 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert tile content generageBoundingVolumes, shouldMergeMaterials, geoidHeightModel, - workerSource + {}, + 'r3dm::uncertainty_ce90sum' ); t.ok(convertedResources); if (!convertedResources) { @@ -488,7 +503,7 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert tile content attributesLength: 2, featureCount: 12, nonCompressedGeometryByteLength: 11324, - compressedGeometryByteLength: 5701, + compressedGeometryByteLength: 5700, texture: { mimeType: 'image/jpeg', width: 512, @@ -507,53 +522,159 @@ test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert tile content t.end(); }); -async function getWorkersSource() { - const result = {draco: '', I3SAttributes: ''}; - const url = getWorkerURL(DracoWriterWorker, {...getLoaderOptions()}); - let sourceResponse = await fetchFile(url); - let source = await sourceResponse.text(); - result.draco = source; - - const i3sAttributesWorkerUrl = getWorkerURL(I3SAttributesWorker, {...getLoaderOptions()}); - sourceResponse = await fetchFile(i3sAttributesWorkerUrl); - source = await sourceResponse.text(); - result.I3SAttributes = source; +test('tile-converter(i3s)#convertB3dmToI3sGeometry - array of UINTxx should be converted to a node attibute of type string', async (t) => { + if (isBrowser) { + t.end(); + return; + } - return result; -} + /* + This is "color" attributes that is actually an array[8] of arrays[3] of UINT8: + 255,0,0 + 255,255,0 + 255,125,0 + 0,255,0 + 0,255,125 + 63,255,0 + 63,170,0 + 0,0,255 + It should be converted to the following attributes buffer: + */ + const expectedArray = new Uint8Array([ + 8, 0, 0, 0, 72, 0, 0, 0, 8, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 8, 0, 0, 0, 10, 0, 0, 0, 9, 0, 0, + 0, 9, 0, 0, 0, 8, 0, 0, 0, 50, 53, 53, 44, 48, 44, 48, 0, 50, 53, 53, 44, 50, 53, 53, 44, 48, 0, + 50, 53, 53, 44, 49, 50, 53, 44, 48, 0, 48, 44, 50, 53, 53, 44, 48, 0, 48, 44, 50, 53, 53, 44, + 49, 50, 53, 0, 54, 51, 44, 50, 53, 53, 44, 48, 0, 54, 51, 44, 49, 55, 48, 44, 48, 0, 48, 44, 48, + 44, 50, 53, 53, 0 + ]); -const OBJECT_ID_TYPE = 'OBJECTID'; -const STRING_TYPE = 'string'; -const SHORT_INT_TYPE = 'Int32'; -const DOUBLE_TYPE = 'double'; -function getAttributeType(key, attribute) { - if (key === OBJECT_ID_TYPE) { - return OBJECT_ID_TYPE; + let nodeId = 1; + const addNodeToNodePage = async () => nodeId++; + const featuresHashArray = []; + const draco = true; + const generageBoundingVolumes = false; + const shouldMergeMaterials = false; + const tileContent = await load(BASEGLOBE_GLB_FILE_PATH, Tiles3DLoader); + const metadataClass = 'owt_lulc'; + const propertyTable = getPropertyTable(tileContent, metadataClass); + const tileTransform = new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + const center = [3189000, 0, -433.4]; + const halfAxes = [ + 1673260.3982534984, 0, 1521488.2883266362, 0, 45, 0, -1595354.0829185273, 0, 1595787.497284685 + ]; + const tileBoundingVolume = new OrientedBoundingBox(center, halfAxes); + const geoidHeightModel = await load(PGM_FILE_PATH, PGMLoader); + const attributeStorageInfo = getAttributeStorageInfo(propertyTable); + try { + const convertedResources = await convertB3dmToI3sGeometry( + tileContent, + tileTransform, + tileBoundingVolume, + addNodeToNodePage, + propertyTable, + featuresHashArray, + attributeStorageInfo, + draco, + generageBoundingVolumes, + shouldMergeMaterials, + geoidHeightModel, + {}, + metadataClass + ); + const attributes = convertedResources?.[0].attributes?.[1]; + t.deepEquals(new Uint8Array(attributes || []), expectedArray, 'Array represents '); + } finally { + // Clean up worker pools + const workerFarm = WorkerFarm.getWorkerFarm({}); + workerFarm.destroy(); } - if (typeof attribute === STRING_TYPE) { - return STRING_TYPE; - } else if (typeof attribute === 'number') { - return Number.isInteger(attribute) ? SHORT_INT_TYPE : DOUBLE_TYPE; + + t.end(); +}); + +test('tile-converter(i3s)#convertB3dmToI3sGeometry - should convert 64-bit attributes to strings', async (t) => { + if (isBrowser) { + t.end(); + return; } - return STRING_TYPE; -} -function getAttributeStorageInfo(propertyTable) { - let attributeIndex = 0; - const propertyTableWithObjectId = { - OBJECTID: [0], - ...propertyTable + const propertyTable = { + color: [11n, 22n, 33n, 44n, 55n, 66n, 77n, 88n, 99n, 111n, 222n, 333n], + component: [ + 'Windows', + 'Frames', + 'Wall', + 'Roof', + 'Skylight', + 'Air conditioner (white box)', + 'Air conditioner (black box)', + 'Air conditioner (tall black/white box)', + 'Clock', + 'Pillars', + 'Street Light', + 'Traffic Light' + ] }; + /* + 'color' 64-bit values from propertyTable should be converted to strings hat are in attributeBufferExpected. + */ + const attributeBufferExpected = [ + 3, 0, 0, 0, 12, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 51, 51, 0, 50, 50, 50, 0, 110, 117, + 108, 108, 0 + ]; - const result = []; - for (const key in propertyTableWithObjectId) { - const firstAttribute = propertyTableWithObjectId[key][0]; - const attributeType = getAttributeType(key, firstAttribute); - const storageAttribute = createdStorageAttribute(attributeIndex, key, attributeType); - result.push(storageAttribute); - attributeIndex += 1; + let nodeId = 1; + const addNodeToNodePage = async () => nodeId++; + const featuresHashArray = []; + const draco = true; + const generageBoundingVolumes = false; + const shouldMergeMaterials = false; + const tileContent = await load(FERRY_GLTF_FILE_PATH, Tiles3DLoader); + const tileTransform = new Matrix4([ + 0.8443837640659682, -0.5357387973460459, 0, 0, 0.32832660036003297, 0.5174791372742712, + 0.7902005985709575, 0, -0.42334111834053034, -0.667232555788526, 0.6128482797708588, 0, + -2703514.4440963655, -4261038.614006309, 3887533.151398322, 1 + ]); + const tileBoundingVolume = new BoundingSphere([ + -2703528.7614193764, -4261014.993900511, 3887572.9889940596 + ]); + const geoidHeightModel = await load(PGM_FILE_PATH, PGMLoader); + const attributeStorageInfo = getAttributeStorageInfo(propertyTable); + try { + const convertedResources = await convertB3dmToI3sGeometry( + tileContent, + tileTransform, + tileBoundingVolume, + addNodeToNodePage, + propertyTable, + featuresHashArray, + attributeStorageInfo, + draco, + generageBoundingVolumes, + shouldMergeMaterials, + geoidHeightModel, + {} + ); + if (!convertedResources?.[0].attributes?.[1]) { + return; + } + const attributes = new Uint8Array(convertedResources[0].attributes[1]); + const attributesExpected = new Uint8Array(attributeBufferExpected); + t.deepEquals(attributes, attributesExpected, '64-bit int values converted to strings'); + } finally { + // Clean up worker pools + const workerFarm = WorkerFarm.getWorkerFarm({}); + workerFarm.destroy(); } - return result; + + t.end(); +}); + +function getAttributeStorageInfo(propertyTable) { + const attributeTypesMap = getAttributeTypesMapFromPropertyTable(propertyTable); + const attributeMetadataInfo: AttributeMetadataInfo = new AttributeMetadataInfo(); + attributeMetadataInfo.addMetadataInfo(attributeTypesMap); + return attributeMetadataInfo.attributeStorageInfo; } async function checkNodeResources(resources, expectedValues, t) { diff --git a/modules/tile-converter/test/i3s-converter/helpers/gltf-attributes.spec.js b/modules/tile-converter/test/i3s-converter/helpers/gltf-attributes.spec.js index 7d70efbfc9..64c3bb65cd 100644 --- a/modules/tile-converter/test/i3s-converter/helpers/gltf-attributes.spec.js +++ b/modules/tile-converter/test/i3s-converter/helpers/gltf-attributes.spec.js @@ -7,6 +7,7 @@ import test from 'tape-promise/tape'; import {Matrix4} from '@math.gl/core'; import {load} from '@loaders.gl/core'; import {Tiles3DLoader} from '@loaders.gl/3d-tiles'; +import {areNumberArraysEqual} from '../../utils/compareArrays'; const FRANKFURT_B3DM_FILE_PATH = '@loaders.gl/tile-converter/test/data/Frankfurt/L5/OF/474_5548_-1_lv5_group_0.osgb_3.b3dm'; @@ -259,18 +260,3 @@ test('tile-converter(i3s)#calculateTransformProps', async (t) => { t.end(); }); - -const EPSILON = 0.000000001; -function areNumberArraysEqual(array1, array2) { - let result = true; - if (array1.length !== array2.length) { - return false; - } - for (let i = 0; i < array1.length; i++) { - if (Math.abs(array1[i] - array2[i]) > EPSILON) { - result = false; - break; - } - } - return result; -} diff --git a/modules/tile-converter/test/i3s-converter/helpers/preprocess-3d-tiles.spec.ts b/modules/tile-converter/test/i3s-converter/helpers/preprocess-3d-tiles.spec.ts index f508d6cc7f..9db644d14a 100644 --- a/modules/tile-converter/test/i3s-converter/helpers/preprocess-3d-tiles.spec.ts +++ b/modules/tile-converter/test/i3s-converter/helpers/preprocess-3d-tiles.spec.ts @@ -6,7 +6,7 @@ import { analyzeTileContent, mergePreprocessData } from '../../../src/i3s-converter/helpers/preprocess-3d-tiles'; -import {GltfPrimitiveModeString} from '../../../src/i3s-converter/types'; +import {GLTFPrimitiveModeString} from '../../../src/i3s-converter/types'; const FRANKFURT_B3DM_FILE_PATH = '@loaders.gl/tile-converter/test/data/Frankfurt/L5/OF/474_5548_-1_lv5_group_0.osgb_3.b3dm'; @@ -25,13 +25,24 @@ test('tile-converter(i3s)#analyzeTileContent', async (t) => { }); test('tile-converter(i3s)#mergePreprocessData', async (t) => { - const targetSet = new Set(); - targetSet.add(GLTF_PRIMITIVE_MODES[0]); - const target = {meshTopologyTypes: targetSet}; + const targetMeshTypeSet = new Set(); + targetMeshTypeSet.add(GLTF_PRIMITIVE_MODES[0]); + const targetMetadataClassesSet = new Set(); + targetMetadataClassesSet.add('metadata_class'); + const target = { + meshTopologyTypes: targetMeshTypeSet, + metadataClasses: targetMetadataClassesSet + }; - const newSet = new Set(); - newSet.add(GLTF_PRIMITIVE_MODES[4]); - mergePreprocessData(target, {meshTopologyTypes: newSet}); + const meshTypeSet = new Set(); + meshTypeSet.add(GLTF_PRIMITIVE_MODES[4]); + const metadataClassesSet = new Set(); + metadataClassesSet.add('metadata_class_2'); + mergePreprocessData(target, { + meshTopologyTypes: meshTypeSet, + metadataClasses: metadataClassesSet + }); t.deepEqual(Array.from(target.meshTopologyTypes), ['POINTS', 'TRIANGLES']); + t.deepEqual(Array.from(target.metadataClasses), ['metadata_class', 'metadata_class_2']); t.end(); }); diff --git a/modules/tile-converter/test/i3s-converter/helpers/progress.spec.ts b/modules/tile-converter/test/i3s-converter/helpers/progress.spec.ts new file mode 100644 index 0000000000..580383f7f4 --- /dev/null +++ b/modules/tile-converter/test/i3s-converter/helpers/progress.spec.ts @@ -0,0 +1,69 @@ +import test from 'tape-promise/tape'; +import {Progress} from '../../../src/i3s-converter/helpers/progress'; + +test('tile-converter(i3s)#Progress methods', async (t) => { + let currentTimeMS: number = 0; + /* + Normally the Progress class takes the current time from the system and makes the necessary calculations based on it. + While testing we can't use the usual workflow, because it would take too long. + Instead we emulate getting the real time by using a special function to get time values specified in the test. + */ + const getTime = () => { + return BigInt(currentTimeMS) * BigInt(1e6); + }; + const progress = new Progress({getTime: getTime}); + + // stepsTotal has not been set yet + t.equal(progress.getPercentString(), ''); + + progress.stepsTotal += 10; + t.equal(progress.stepsTotal, 10); + + progress.stepsDone += 1; + t.equal(progress.stepsDone, 1); + + t.equal(progress.getPercent(), 10); + t.equal(progress.getPercentString(), '10'); + + currentTimeMS = 1000; + progress.startMonitoring(); + + currentTimeMS = 3672000; + t.equal(progress.getTimeCurrentlyElapsed(), 3671000); + progress.stopMonitoring(); + + currentTimeMS = 1000; // 10s + progress.startMonitoring(); + + currentTimeMS = 11000; // 10s + progress.stepsDone += 1; + // 1 step completion took 10s + let timeRemainingObject = progress.getTimeRemaining(); + let timeRemainingString = progress.getTimeRemainingString(); + t.notOk(timeRemainingObject?.trust); + t.equal(timeRemainingObject?.timeRemaining, 90000); + t.equal(timeRemainingString, ''); + t.equal(progress.getTimeCurrentlyElapsed(), 10000); + + currentTimeMS = 12000; // 11s + progress.stepsDone += 1; + // 2 steps completion took 11s, which is much faster + timeRemainingObject = progress.getTimeRemaining(); + timeRemainingString = progress.getTimeRemainingString(); + t.notOk(timeRemainingObject?.trust); + t.equal(timeRemainingObject?.timeRemaining, 44000); + t.equal(timeRemainingString, ''); + t.equal(progress.getTimeCurrentlyElapsed(), 11000); + + currentTimeMS = 17500; // 16.5s + progress.stepsDone += 1; + // 3 steps completion took 16.5s. The velocity of processing has been stabilized on the 3rd step. + timeRemainingObject = progress.getTimeRemaining(); + timeRemainingString = progress.getTimeRemainingString(); + t.ok(timeRemainingObject?.trust); + t.equal(timeRemainingObject?.timeRemaining, 38500); + t.equal(timeRemainingString, '38s'); + t.equal(progress.getTimeCurrentlyElapsed(), 16500); + + t.end(); +}); diff --git a/modules/tile-converter/test/i3s-converter/i3s-converter.spec.js b/modules/tile-converter/test/i3s-converter/i3s-converter.spec.js index f395155f17..5daf7e858a 100644 --- a/modules/tile-converter/test/i3s-converter/i3s-converter.spec.js +++ b/modules/tile-converter/test/i3s-converter/i3s-converter.spec.js @@ -11,6 +11,7 @@ const TILESET_WITH_TEXTURES = '@loaders.gl/3d-tiles/test/data/Batched/BatchedTex const TILESET_WITH_KTX_2_TEXTURE = '@loaders.gl/3d-tiles/test/data/VNext/agi-ktx2/tileset.json'; const TILESET_WITH_FAILING_CONTENT = '@loaders.gl/tile-converter/test/data/failing-content-error/tileset.json'; +const TILESET_CDB_YEMEN = '@loaders.gl/3d-tiles/test/data/VNext/cdb-yemen-cut/tileset.json'; const PGM_FILE_PATH = '@loaders.gl/tile-converter/test/data/egm84-30.pgm'; @@ -28,10 +29,10 @@ const TEST_TEXTURE_MATERIAL = { }; const TEST_FULL_EXTENT = { - xmin: -75.61412212128346, - ymin: 40.04095693133301, - xmax: -75.6100663747417, - ymax: 40.04410425830655, + xmin: -75.61412210800641, + ymin: 40.040956941636935, + xmax: -75.61006638801986, + ymax: 40.04410424800317, zmin: 0, zmax: 20 }; @@ -48,9 +49,7 @@ test('tile-converter(i3s)#converts 3d-tiles tileset to i3s tileset', async (t) = tilesetName: 'BatchedColors', slpk: false, sevenZipExe: 'C:\\Program Files\\7-Zip\\7z.exe', - egmFilePath: PGM_FILE_PATH, - token: - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlYWMxMzcyYy0zZjJkLTQwODctODNlNi01MDRkZmMzMjIxOWIiLCJpZCI6OTYyMCwic2NvcGVzIjpbImFzbCIsImFzciIsImdjIl0sImlhdCI6MTU2Mjg2NjI3M30.1FNiClUyk00YH_nWfSGpiQAjR5V2OvREDq1PJ5QMjWQ' + egmFilePath: PGM_FILE_PATH }); if (!isBrowser) { t.ok(tilesetJson); @@ -71,9 +70,7 @@ test('tile-converter(i3s)#should create Draco compressed geometry', async (t) => slpk: false, draco: true, sevenZipExe: 'C:\\Program Files\\7-Zip\\7z.exe', - egmFilePath: PGM_FILE_PATH, - token: - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlYWMxMzcyYy0zZjJkLTQwODctODNlNi01MDRkZmMzMjIxOWIiLCJpZCI6OTYyMCwic2NvcGVzIjpbImFzbCIsImFzciIsImdjIl0sImlhdCI6MTU2Mjg2NjI3M30.1FNiClUyk00YH_nWfSGpiQAjR5V2OvREDq1PJ5QMjWQ' + egmFilePath: PGM_FILE_PATH }); t.ok(tilesetJson); } @@ -91,9 +88,7 @@ test('tile-converter(i3s)#converts 3d-tiles tileset to i3s tileset with validati slpk: true, sevenZipExe: 'C:\\Program Files\\7-Zip\\7z.exe', egmFilePath: PGM_FILE_PATH, - validate: true, - token: - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlYWMxMzcyYy0zZjJkLTQwODctODNlNi01MDRkZmMzMjIxOWIiLCJpZCI6OTYyMCwic2NvcGVzIjpbImFzbCIsImFzciIsImdjIl0sImlhdCI6MTU2Mjg2NjI3M30.1FNiClUyk00YH_nWfSGpiQAjR5V2OvREDq1PJ5QMjWQ' + validate: true }); t.ok(tilesetJson); } @@ -319,9 +314,7 @@ test('tile-converter(i3s)#converts 3d-tiles tileset to i3s tileset with bounding generateBoundingVolumes: true, slpk: false, sevenZipExe: 'C:\\Program Files\\7-Zip\\7z.exe', - egmFilePath: PGM_FILE_PATH, - token: - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlYWMxMzcyYy0zZjJkLTQwODctODNlNi01MDRkZmMzMjIxOWIiLCJpZCI6OTYyMCwic2NvcGVzIjpbImFzbCIsImFzciIsImdjIl0sImlhdCI6MTU2Mjg2NjI3M30.1FNiClUyk00YH_nWfSGpiQAjR5V2OvREDq1PJ5QMjWQ' + egmFilePath: PGM_FILE_PATH }); t.ok(tilesetJson); } @@ -345,7 +338,9 @@ test('tile-converter(i3s)#layer json should contain fullExtent field', async (t) ); const layer = JSON.parse(layerJson); t.ok(layer.fullExtent); - t.deepEqual(layer.fullExtent, TEST_FULL_EXTENT); + for (const key in layer.fullExtent) { + t.equal(layer.fullExtent[key], TEST_FULL_EXTENT[key]); + } } await cleanUpPath('data/BatchedTextured'); t.end(); @@ -375,3 +370,24 @@ test('tile-converter(i3s)#proceed with failing content', async (t) => { await cleanUpPath('data/FailingContent'); t.end(); }); + +test('tile-converter(i3s)#convert with --metadata-class option', async (t) => { + if (!isBrowser) { + const converter = new I3SConverter(); + await converter.convert({ + inputUrl: TILESET_CDB_YEMEN, + outputPath: 'data', + tilesetName: 'CDB_Yemen', + sevenZipExe: 'C:\\Program Files\\7-Zip\\7z.exe', + egmFilePath: PGM_FILE_PATH, + metadataClass: 'CDBMaterialsClass' + }); + const nodePageJson = await fs.readFile( + 'data/CDB_Yemen/SceneServer/layers/0/nodepages/0/index.json', + 'utf8' + ); + t.ok(nodePageJson); + } + await cleanUpPath('data/CDB_Yemen'); + t.end(); +}); diff --git a/modules/tile-converter/test/i3s-server/controllers/index-controller.spec.ts b/modules/tile-converter/test/i3s-server/controllers/index-controller.spec.ts new file mode 100644 index 0000000000..7e79f70f7c --- /dev/null +++ b/modules/tile-converter/test/i3s-server/controllers/index-controller.spec.ts @@ -0,0 +1,33 @@ +import test from 'tape-promise/tape'; +import {isBrowser} from '@loaders.gl/core'; +import {getFileNameByUrl} from '../../../src/i3s-server/controllers/index-controller'; + +const URL_PREFIX = + '/modules/tile-converter/test/data/i3s-server/Frankfurt-md-2/SceneServer/layers/0'; +const TEST_CASES = [ + {input: '', output: 'index.json'}, + {input: '/nodepages/0', output: 'nodepages/0/index.json'}, + {input: '/nodes/root', output: 'nodes/root/index.json'}, + {input: '/nodes/1', output: 'nodes/1/index.json'}, + {input: '/nodes/1/geometries/0', output: 'nodes/1/geometries/0/index.bin'}, + {input: '/nodes/1/geometries/1', output: 'nodes/1/geometries/1/index.bin'}, + {input: '/nodes/1/shared', output: 'nodes/1/shared/index.json'}, + {input: '/nodes/1/textures/0', output: 'nodes/1/textures/0/index.jpg'}, + {input: '/nodes/1/textures/1', output: 'nodes/1/textures/1/index.ktx2'} +]; + +test('tile-converter(i3s-server)#getFileNameByUrl', async (t) => { + if (isBrowser) { + t.end(); + return; + } + + const cwd = process.cwd(); + + for (const testCase of TEST_CASES) { + const result = await getFileNameByUrl(`${URL_PREFIX}${testCase.input}`); + t.equals(result, `${cwd}${URL_PREFIX}/${testCase.output}`); + } + + t.end(); +}); diff --git a/modules/tile-converter/test/i3s-server/controllers/slpk-controller.spec.ts b/modules/tile-converter/test/i3s-server/controllers/slpk-controller.spec.ts new file mode 100644 index 0000000000..85f0f3137f --- /dev/null +++ b/modules/tile-converter/test/i3s-server/controllers/slpk-controller.spec.ts @@ -0,0 +1,44 @@ +import test from 'tape-promise/tape'; +import {isBrowser} from '@loaders.gl/core'; +import {path} from '@loaders.gl/loader-utils'; +import {getFileByUrl, loadArchive} from '../../../src/i3s-server/controllers/slpk-controller'; + +const URL_PREFIX = ''; +const SLPK_URL = './modules/i3s/test/data/DA12_subset.slpk'; +const TEST_CASES = [ + {input: '', output: 4780}, + {input: 'nodepages/0', output: 16153}, + {input: 'nodes/root', output: 11550}, + {input: 'nodes/1', output: 1175}, + {input: 'nodes/1/geometries/0', output: 25620}, + {input: 'nodes/1/geometries/1', output: 1767}, + {input: 'nodes/1/shared', output: 333} +]; + +test('tile-converter(i3s-server)#getFileByUrl return null if file is not loaded', async (t) => { + if (isBrowser) { + t.end(); + return; + } + + const result = await getFileByUrl('layers/0'); + t.equals(result, null); + + t.end(); +}); + +test('tile-converter(i3s-server)#getFileByUrl return files content', async (t) => { + if (isBrowser) { + t.end(); + return; + } + const FULL_LAYER_PATH = path.join(process.cwd(), SLPK_URL); // eslint-disable-line no-undef + await loadArchive(FULL_LAYER_PATH); + + for (const testCase of TEST_CASES) { + const result = await getFileByUrl(`${URL_PREFIX}${testCase.input}`); + t.equals(result?.byteLength, testCase.output); + } + + t.end(); +}); diff --git a/modules/tile-converter/test/i3s-server/utils/create-scene-server.spec.ts b/modules/tile-converter/test/i3s-server/utils/create-scene-server.spec.ts new file mode 100644 index 0000000000..7e8fd8fbe1 --- /dev/null +++ b/modules/tile-converter/test/i3s-server/utils/create-scene-server.spec.ts @@ -0,0 +1,164 @@ +import test from 'tape-promise/tape'; +import {isBrowser} from '@loaders.gl/core'; +import {SceneLayer3D} from '@loaders.gl/i3s'; +import {createSceneServer} from '../../../src/i3s-server/utils/create-scene-server'; + +test('tile-converter(i3s-server)#createSceneServer', async (t) => { + if (isBrowser) { + t.end(); + return; + } + + const result = await createSceneServer('Buildings_3D_Multipatch_DA12_Subset', LAYER); + t.equals(JSON.stringify(result).length, 4889); + + t.end(); +}); + +const LAYER: SceneLayer3D = { + id: 0, + version: 'ECB1F245-BAB6-4CF3-86CE-9CF6047E9239', + name: 'Buildings_3D_Multipatch_DA12_Subset', + serviceUpdateTimeStamp: {lastUpdate: 1570746568000}, + href: './layers/0', + layerType: '3DObject', + spatialReference: {wkid: 4326, latestWkid: 4326, vcsWkid: 5773, latestVcsWkid: 5773}, + heightModelInfo: { + heightModel: 'gravity_related_height', + vertCRS: 'EGM96_Geoid', + heightUnit: 'meter' + }, + ZFactor: 0.30480060960121924, + alias: 'Buildings_3D_Multipatch_DA12_Subset', + description: 'Buildings_3D_Multipatch_DA12_Subset', + capabilities: ['View', 'Query'], + cachedDrawingInfo: {color: false}, + popupInfo: { + title: '{BIN}', + mediaInfos: [], + fieldInfos: [ + {fieldName: 'OBJECTID', visible: true, isEditable: false, label: 'OBJECTID'}, + {fieldName: 'BIN', visible: true, isEditable: true, label: 'BIN'}, + {fieldName: 'DOITT_ID', visible: true, isEditable: true, label: 'DOITT_ID'}, + {fieldName: 'SOURCE_ID', visible: true, isEditable: true, label: 'SOURCE_ID'} + ], + popupElements: [ + { + fieldInfos: [ + {fieldName: 'OBJECTID', visible: true, isEditable: false, label: 'OBJECTID'}, + {fieldName: 'BIN', visible: true, isEditable: true, label: 'BIN'}, + {fieldName: 'DOITT_ID', visible: true, isEditable: true, label: 'DOITT_ID'}, + {fieldName: 'SOURCE_ID', visible: true, isEditable: true, label: 'SOURCE_ID'} + ], + type: 'fields' + } + ], + expressionInfos: [] + }, + disablePopup: false, + fields: [ + {name: 'OBJECTID', type: 'esriFieldTypeOID', alias: 'OBJECTID'}, + {name: 'BIN', type: 'esriFieldTypeInteger', alias: 'BIN'}, + {name: 'DOITT_ID', type: 'esriFieldTypeInteger', alias: 'DOITT_ID'}, + {name: 'SOURCE_ID', type: 'esriFieldTypeDouble', alias: 'SOURCE_ID'} + ], + statisticsInfo: [ + {key: 'f_1', name: 'BIN', href: './statistics/f_1/0'}, + {key: 'f_2', name: 'DOITT_ID', href: './statistics/f_2/0'}, + {key: 'f_3', name: 'SOURCE_ID', href: './statistics/f_3/0'} + ], + attributeStorageInfo: [ + { + key: 'f_0', + name: 'OBJECTID', + header: [{property: 'count', valueType: 'UInt32'}], + ordering: ['attributeValues'], + attributeValues: {valueType: 'Oid32', valuesPerElement: 1} + }, + { + key: 'f_1', + name: 'BIN', + header: [{property: 'count', valueType: 'UInt32'}], + ordering: ['attributeValues'], + attributeValues: {valueType: 'Int32', valuesPerElement: 1} + }, + { + key: 'f_2', + name: 'DOITT_ID', + header: [{property: 'count', valueType: 'UInt32'}], + ordering: ['attributeValues'], + attributeValues: {valueType: 'Int32', valuesPerElement: 1} + }, + { + key: 'f_3', + name: 'SOURCE_ID', + header: [{property: 'count', valueType: 'UInt32'}], + ordering: ['attributeValues'], + attributeValues: {valueType: 'Float64', valuesPerElement: 1} + } + ], + store: { + id: 'D20A5DA0-623B-4359-880D-474D634F7158', + profile: 'meshpyramids', + resourcePattern: ['3dNodeIndexDocument', 'Attributes', 'SharedResource', 'Geometry'], + rootNode: './nodes/root', + extent: [ + -74.01610254118118348, 40.70883303940371434, -74.00660086904034074, 40.71625626184324886 + ], + indexCRS: 'http://www.opengis.net/def/crs/EPSG/0/4326', + vertexCRS: 'http://www.opengis.net/def/crs/EPSG/0/4326', + normalReferenceFrame: 'earth-centered', + nidEncoding: 'application/vnd.esri.i3s.json+gzip; version=1.7', + featureEncoding: 'application/vnd.esri.i3s.json+gzip; version=1.7', + geometryEncoding: 'application/octet-stream; version=1.7', + attributeEncoding: 'application/octet-stream; version=1.7', + lodType: 'MeshPyramid', + lodModel: 'node-switching', + defaultGeometrySchema: { + geometryType: 'triangles', + header: [ + {property: 'vertexCount', type: 'UInt32'}, + {property: 'featureCount', type: 'UInt32'} + ], + topology: 'PerAttributeArray', + ordering: ['position', 'normal', 'uv0', 'color'], + vertexAttributes: { + position: {valueType: 'Float32', valuesPerElement: 3}, + normal: {valueType: 'Float32', valuesPerElement: 3}, + uv0: {valueType: 'Float32', valuesPerElement: 2}, + color: {valueType: 'UInt8', valuesPerElement: 4} + }, + featureAttributeOrder: ['id', 'faceRange'], + featureAttributes: { + id: {valueType: 'UInt64', valuesPerElement: 1}, + faceRange: {valueType: 'UInt32', valuesPerElement: 2} + } + }, + textureEncoding: ['image/jpeg', 'image/vnd-ms.dds'], + version: '1.7' + }, + nodePages: {nodesPerPage: 64, lodSelectionMetricType: 'maxScreenThresholdSQ'}, + materialDefinitions: [], + geometryDefinitions: [ + { + topology: 'triangle', + geometryBuffers: [ + { + offset: 8, + position: {type: 'Float32', component: 3, binding: 'per-vertex'}, + normal: {type: 'Float32', component: 3, binding: 'per-vertex'}, + uv0: {type: 'Float32', component: 2, binding: 'per-vertex'}, + color: {type: 'UInt8', component: 4, binding: 'per-vertex'}, + featureId: {type: 'UInt64', component: 1, binding: 'per-feature'}, + faceRange: {type: 'UInt32', component: 2, binding: 'per-feature'} + }, + { + compressedAttributes: { + encoding: 'draco', + attributes: ['position', 'uv0', 'color', 'feature-index'] + } + } + ] + } + ] +}; diff --git a/modules/tile-converter/test/i3s-server/utils/server-utils.spec.ts b/modules/tile-converter/test/i3s-server/utils/server-utils.spec.ts new file mode 100644 index 0000000000..8da19052b9 --- /dev/null +++ b/modules/tile-converter/test/i3s-server/utils/server-utils.spec.ts @@ -0,0 +1,33 @@ +import test from 'tape-promise/tape'; +import {isBrowser} from '@loaders.gl/core'; +import {formErrorHandler, normalizePort} from '../../../src/i3s-server/utils/server-utils'; + +test('tile-converter(i3s-server)#normalizePort', async (t) => { + if (isBrowser) { + t.end(); + return; + } + + const result1 = normalizePort('8080'); + t.strictEquals(result1, 8080); + + const result2 = normalizePort('\\\\.\\pipe\\PIPE_NAME'); + t.strictEquals(result2, '\\\\.\\pipe\\PIPE_NAME'); + + const result3 = normalizePort('-1000'); + t.strictEquals(result3, false); + + t.end(); +}); + +test('tile-converter(i3s-server)#formErrorHandler', async (t) => { + if (isBrowser) { + t.end(); + return; + } + + const func = formErrorHandler(8080); + t.ok(func); + + t.end(); +}); diff --git a/modules/tile-converter/test/index.js b/modules/tile-converter/test/index.js index aa5222a129..b1b323b1f5 100644 --- a/modules/tile-converter/test/index.js +++ b/modules/tile-converter/test/index.js @@ -10,6 +10,9 @@ import './i3s-converter/helpers/shared-resources.spec'; import './i3s-converter/helpers/load-3d-tiles.spec'; import './i3s-converter/helpers/tileset-traversal.spec'; import './i3s-converter/helpers/preprocess-3d-tiles.spec'; +import './i3s-converter/helpers/geometry-attributes.spec'; +import './i3s-converter/helpers/attribute-metadata-info.spec.ts'; +import './i3s-converter/helpers/progress.spec.ts'; import './i3s-converter/i3s-converter.spec'; import './slpk-extractor/slpk-extractor.spec'; @@ -18,8 +21,12 @@ import './utils/cli-utils.spec'; import './3d-tiles-converter/helpers/b3dm-converter.spec'; import './3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.spec'; import './3d-tiles-converter/helpers/texture-atlas.spec'; +import './3d-tiles-converter/helpers/load-i3s.spec'; import './3d-tiles-converter/3d-tiles-converter.spec'; import './3d-tiles-converter/helpers/geometry-attributes.spec'; -import './deps-installer/deps-installer.spec'; +import './i3s-server/controllers/index-controller.spec'; +import './i3s-server/controllers/slpk-controller.spec'; +import './i3s-server/utils/create-scene-server.spec.ts'; +import './i3s-server/utils/server-utils.spec'; diff --git a/modules/tile-converter/test/utils/compareArrays.ts b/modules/tile-converter/test/utils/compareArrays.ts new file mode 100644 index 0000000000..dea91c4b0a --- /dev/null +++ b/modules/tile-converter/test/utils/compareArrays.ts @@ -0,0 +1,14 @@ +const EPSILON = 0.000000001; +export function areNumberArraysEqual(array1, array2) { + let result = true; + if (array1.length !== array2.length) { + return false; + } + for (let i = 0; i < array1.length; i++) { + if (Math.abs(array1[i] - array2[i]) > EPSILON) { + result = false; + break; + } + } + return result; +} diff --git a/modules/tiles/package.json b/modules/tiles/package.json index 8904ae64b9..35b17f7479 100644 --- a/modules/tiles/package.json +++ b/modules/tiles/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/tiles", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Common components for different tiles loaders.", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -21,8 +22,15 @@ "pointcloud" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -30,20 +38,20 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/math": "4.0.0-alpha.13", - "@math.gl/core": "^3.5.1", - "@math.gl/culling": "^3.5.1", - "@math.gl/geospatial": "^3.5.1", - "@math.gl/web-mercator": "^3.5.1", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/math": "4.0.3", + "@math.gl/core": "^4.0.0", + "@math.gl/culling": "^4.0.0", + "@math.gl/geospatial": "^4.0.0", + "@math.gl/web-mercator": "^4.0.0", "@probe.gl/stats": "^4.0.2" }, "peerDependencies": { - "@loaders.gl/core": "^4.0.0-alpha.8" + "@loaders.gl/core": "^4.0.0" }, "devDependencies": { "@deck.gl/core": "^8.9.0" diff --git a/modules/tiles/src/bundle.ts b/modules/tiles/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/tiles/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/tiles/src/constants.ts b/modules/tiles/src/constants.ts index 23d7cc3a38..4e5d3d64f6 100644 --- a/modules/tiles/src/constants.ts +++ b/modules/tiles/src/constants.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors export type TileContentState = | 'unloaded' // Has never been requested diff --git a/modules/tiles/src/index.ts b/modules/tiles/src/index.ts index e7b61508c4..4b733150f5 100644 --- a/modules/tiles/src/index.ts +++ b/modules/tiles/src/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors export type {Tileset3DProps} from './tileset/tileset-3d'; export {Tileset3D} from './tileset/tileset-3d'; diff --git a/modules/tiles/src/tileset/format-3d-tiles/tileset-3d-traverser.ts b/modules/tiles/src/tileset/format-3d-tiles/tileset-3d-traverser.ts index 4b1a157289..09d1eb3942 100644 --- a/modules/tiles/src/tileset/format-3d-tiles/tileset-3d-traverser.ts +++ b/modules/tiles/src/tileset/format-3d-tiles/tileset-3d-traverser.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // This file is derived from the Cesium code base under Apache 2 license // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md diff --git a/modules/tiles/src/tileset/helpers/bounding-volume.ts b/modules/tiles/src/tileset/helpers/bounding-volume.ts index 7c544e5d0d..026342a172 100644 --- a/modules/tiles/src/tileset/helpers/bounding-volume.ts +++ b/modules/tiles/src/tileset/helpers/bounding-volume.ts @@ -18,6 +18,10 @@ const scratchPoint = new Vector3(); const scratchScale = new Vector3(); const scratchNorthWest = new Vector3(); const scratchSouthEast = new Vector3(); +const scratchCenter = new Vector3(); +const scratchXAxis = new Vector3(); +const scratchYAxis = new Vector3(); +const scratchZAxis = new Vector3(); // const scratchRectangle = new Rectangle(); // const scratchOrientedBoundingBox = new OrientedBoundingBox(); // const scratchTransform = new Matrix4(); @@ -29,7 +33,7 @@ const scratchSouthEast = new Vector3(); * @param [result] The object onto which to store the result. * @returns The modified result parameter or a new TileBoundingVolume instance if none was provided. */ -export function createBoundingVolume(boundingVolumeHeader, transform, result) { +export function createBoundingVolume(boundingVolumeHeader, transform, result?) { assert(boundingVolumeHeader, '3D Tile: boundingVolume must be defined'); // boundingVolume schema: @@ -38,28 +42,7 @@ export function createBoundingVolume(boundingVolumeHeader, transform, result) { return createBox(boundingVolumeHeader.box, transform, result); } if (boundingVolumeHeader.region) { - // [west, south, east, north, minimum height, maximum height] - // Latitudes and longitudes are in the WGS 84 datum as defined in EPSG 4979 and are in radians. - // Heights are in meters above (or below) the WGS 84 ellipsoid. - const [west, south, east, north, minHeight, maxHeight] = boundingVolumeHeader.region; - - const northWest = Ellipsoid.WGS84.cartographicToCartesian( - [degrees(west), degrees(north), minHeight], - scratchNorthWest - ); - const southEast = Ellipsoid.WGS84.cartographicToCartesian( - [degrees(east), degrees(south), maxHeight], - scratchSouthEast - ); - const centerInCartesian = new Vector3().addVectors(northWest, southEast).multiplyScalar(0.5); - const radius = new Vector3().subVectors(northWest, southEast).len() / 2.0; - - // TODO improve region boundingVolume - // for now, create a sphere as the boundingVolume instead of box - return createSphere( - [centerInCartesian[0], centerInCartesian[1], centerInCartesian[2], radius], - new Matrix4() - ); + return createObbFromRegion(boundingVolumeHeader.region); } if (boundingVolumeHeader.sphere) { @@ -106,7 +89,7 @@ export function getCartographicBounds( throw new Error('Unkown boundingVolume type'); } -function createBox(box, transform, result) { +function createBox(box, transform, result?) { // https://math.gl/modules/culling/docs/api-reference/oriented-bounding-box // 1. A half-axes based representation. // box: An array of 12 numbers that define an oriented bounding box. @@ -236,6 +219,52 @@ function createSphere(sphere, transform, result?) { return new BoundingSphere(center, radius); } +/** + * Create OrientedBoundingBox instance from region 3D tiles bounding volume + * @param region - region 3D tiles bounding volume + * @returns OrientedBoundingBox instance + */ +function createObbFromRegion(region: number[]): OrientedBoundingBox { + // [west, south, east, north, minimum height, maximum height] + // Latitudes and longitudes are in the WGS 84 datum as defined in EPSG 4979 and are in radians. + // Heights are in meters above (or below) the WGS 84 ellipsoid. + const [west, south, east, north, minHeight, maxHeight] = region; + + const northWest = Ellipsoid.WGS84.cartographicToCartesian( + [degrees(west), degrees(north), minHeight], + scratchNorthWest + ); + const southEast = Ellipsoid.WGS84.cartographicToCartesian( + [degrees(east), degrees(south), maxHeight], + scratchSouthEast + ); + const centerInCartesian = new Vector3().addVectors(northWest, southEast).multiplyByScalar(0.5); + Ellipsoid.WGS84.cartesianToCartographic(centerInCartesian, scratchCenter); + + Ellipsoid.WGS84.cartographicToCartesian( + [degrees(east), scratchCenter[1], scratchCenter[2]], + scratchXAxis + ); + Ellipsoid.WGS84.cartographicToCartesian( + [scratchCenter[0], degrees(north), scratchCenter[2]], + scratchYAxis + ); + Ellipsoid.WGS84.cartographicToCartesian( + [scratchCenter[0], scratchCenter[1], maxHeight], + scratchZAxis + ); + + return createBox( + [ + ...centerInCartesian, + ...scratchXAxis.subtract(centerInCartesian), + ...scratchYAxis.subtract(centerInCartesian), + ...scratchZAxis.subtract(centerInCartesian) + ], + new Matrix4() + ); +} + /** * Convert a bounding volume defined by OrientedBoundingBox to cartographic bounds * @returns {CartographicBounds} diff --git a/modules/tiles/src/tileset/helpers/i3s-lod.ts b/modules/tiles/src/tileset/helpers/i3s-lod.ts index 3b3c0ac32d..0505cf829e 100644 --- a/modules/tiles/src/tileset/helpers/i3s-lod.ts +++ b/modules/tiles/src/tileset/helpers/i3s-lod.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {Matrix4, Vector3} from '@math.gl/core'; import {Ellipsoid} from '@math.gl/geospatial'; diff --git a/modules/tiles/src/tileset/helpers/tiles-3d-lod.ts b/modules/tiles/src/tileset/helpers/tiles-3d-lod.ts index 24f2618edb..b128f07248 100644 --- a/modules/tiles/src/tileset/helpers/tiles-3d-lod.ts +++ b/modules/tiles/src/tileset/helpers/tiles-3d-lod.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // This file is derived from the Cesium code base under Apache 2 license // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md diff --git a/modules/tiles/src/tileset/helpers/transform-utils.ts b/modules/tiles/src/tileset/helpers/transform-utils.ts index 9c6b6a07ac..2aaa42876e 100644 --- a/modules/tiles/src/tileset/helpers/transform-utils.ts +++ b/modules/tiles/src/tileset/helpers/transform-utils.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {Ellipsoid} from '@math.gl/geospatial'; import {Matrix4, Vector3} from '@math.gl/core'; diff --git a/modules/tiles/src/tileset/helpers/zoom.ts b/modules/tiles/src/tileset/helpers/zoom.ts index 78c40b03c7..3c0a9ccd49 100644 --- a/modules/tiles/src/tileset/helpers/zoom.ts +++ b/modules/tiles/src/tileset/helpers/zoom.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {Vector3} from '@math.gl/core'; import {BoundingSphere, OrientedBoundingBox} from '@math.gl/culling'; @@ -71,14 +72,14 @@ export function getZoomFromFullExtent( cartorgraphicCenter: Vector3, cartesianCenter: Vector3 ) { - const extentVertex = Ellipsoid.WGS84.cartographicToCartesian( + Ellipsoid.WGS84.cartographicToCartesian( [fullExtent.xmax, fullExtent.ymax, fullExtent.zmax], - new Vector3() + scratchVector ); const extentSize = Math.sqrt( - Math.pow(extentVertex[0] - cartesianCenter[0], 2) + - Math.pow(extentVertex[1] - cartesianCenter[1], 2) + - Math.pow(extentVertex[2] - cartesianCenter[2], 2) + Math.pow(scratchVector[0] - cartesianCenter[0], 2) + + Math.pow(scratchVector[1] - cartesianCenter[1], 2) + + Math.pow(scratchVector[2] - cartesianCenter[2], 2) ); return Math.log2(WGS84_RADIUS_Z / (extentSize + cartorgraphicCenter[2])); } diff --git a/modules/tiles/src/tileset/tile-3d.ts b/modules/tiles/src/tileset/tile-3d.ts index 45ed046743..a7c315c93f 100644 --- a/modules/tiles/src/tileset/tile-3d.ts +++ b/modules/tiles/src/tileset/tile-3d.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // This file is derived from the Cesium code base under Apache 2 license // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md @@ -102,7 +103,7 @@ export class Tile3D { private _expireDate: any = null; private _expiredContent: any = null; - private _boundingBox?: CartographicBounds; + private _boundingBox?: CartographicBounds = undefined; /** updated every frame for tree traversal and rendering optimizations: */ public _distanceToCamera: number = 0; diff --git a/modules/tiles/src/tileset/tileset-3d.ts b/modules/tiles/src/tileset/tileset-3d.ts index 131626c54b..b515207092 100644 --- a/modules/tiles/src/tileset/tileset-3d.ts +++ b/modules/tiles/src/tileset/tileset-3d.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // This file is derived from the Cesium code base under Apache 2 license // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md @@ -341,9 +342,9 @@ export class Tileset3D { } /** @deprecated */ - setOptions(options: Tileset3DProps): void { - this.options = {...this.options, ...options}; - } + // setOptions(options: Tileset3DProps): void { + // this.options = {...this.options, ...options}; + // } /** * Return a loadable tile url for a specific tile subpath @@ -354,7 +355,12 @@ export class Tileset3D { if (isDataUrl) { return tilePath; } - return `${tilePath}${tilePath.includes('?') ? '&' : '?'}${this.queryParams}`; + + let tileUrl = tilePath; + if (this.queryParams.length) { + tileUrl = `${tilePath}${tilePath.includes('?') ? '&' : '?'}${this.queryParams}`; + } + return tileUrl; } // TODO CESIUM specific @@ -597,10 +603,8 @@ export class Tileset3D { ymin + (ymax - ymin) / 2, zmin + (zmax - zmin) / 2 ); - this.cartesianCenter = Ellipsoid.WGS84.cartographicToCartesian( - this.cartographicCenter, - new Vector3() - ); + this.cartesianCenter = new Vector3(); + Ellipsoid.WGS84.cartographicToCartesian(this.cartographicCenter, this.cartesianCenter); this.zoom = getZoomFromFullExtent(fullExtent, this.cartographicCenter, this.cartesianCenter); return; } @@ -609,10 +613,8 @@ export class Tileset3D { if (extent) { const [xmin, ymin, xmax, ymax] = extent; this.cartographicCenter = new Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, 0); - this.cartesianCenter = Ellipsoid.WGS84.cartographicToCartesian( - this.cartographicCenter, - new Vector3() - ); + this.cartesianCenter = new Vector3(); + Ellipsoid.WGS84.cartographicToCartesian(this.cartographicCenter, this.cartesianCenter); this.zoom = getZoomFromExtent(extent, this.cartographicCenter, this.cartesianCenter); return; } @@ -643,7 +645,8 @@ export class Tileset3D { // cartographic coordinates are undefined at the center of the ellipsoid if (center[0] !== 0 || center[1] !== 0 || center[2] !== 0) { - this.cartographicCenter = Ellipsoid.WGS84.cartesianToCartographic(center, new Vector3()); + this.cartographicCenter = new Vector3(); + Ellipsoid.WGS84.cartesianToCartographic(center, this.cartographicCenter); } else { this.cartographicCenter = new Vector3(0, 0, -Ellipsoid.WGS84.radii[0]); } @@ -695,6 +698,7 @@ export class Tileset3D { if (childTile.contentUrl?.includes('?session=')) { const url = new URL(childTile.contentUrl); const session = url.searchParams.get('session'); + // eslint-disable-next-line max-depth if (session) { this._queryParams.session = session; } @@ -930,6 +934,7 @@ export class Tileset3D { _initializeI3STileset() { // @ts-expect-error if (this.loadOptions.i3s && 'token' in this.loadOptions.i3s) { + // @ts-ignore this._queryParams.token = this.loadOptions.i3s.token as string; } } diff --git a/modules/tiles/src/tileset/tileset-cache.ts b/modules/tiles/src/tileset/tileset-cache.ts index b63b90ad74..3454fd93ea 100644 --- a/modules/tiles/src/tileset/tileset-cache.ts +++ b/modules/tiles/src/tileset/tileset-cache.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // This file is derived from the Cesium code base under Apache 2 license // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md diff --git a/modules/tiles/src/tileset/tileset-traverser.ts b/modules/tiles/src/tileset/tileset-traverser.ts index 3664032fe6..119892de9e 100644 --- a/modules/tiles/src/tileset/tileset-traverser.ts +++ b/modules/tiles/src/tileset/tileset-traverser.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {Tile3D} from './tile-3d'; import {ManagedArray} from '../utils/managed-array'; @@ -390,7 +391,7 @@ export class TilesetTraverser { } stack.push(child); } - } else if (!tile.contentAvailable) { + } else if (!tile.contentAvailable && !tile.hasEmptyContent) { allDescendantsLoaded = false; } } diff --git a/modules/tiles/src/utils/doubly-linked-list-node.ts b/modules/tiles/src/utils/doubly-linked-list-node.ts index c35cba0ab6..1b37e1f8e0 100644 --- a/modules/tiles/src/utils/doubly-linked-list-node.ts +++ b/modules/tiles/src/utils/doubly-linked-list-node.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // This file is derived from the Cesium code base under Apache 2 license // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md diff --git a/modules/tiles/test/data/tile-header-examples.js b/modules/tiles/test/data/tile-header-examples.ts similarity index 95% rename from modules/tiles/test/data/tile-header-examples.js rename to modules/tiles/test/data/tile-header-examples.ts index 4925593956..d136c72750 100644 --- a/modules/tiles/test/data/tile-header-examples.js +++ b/modules/tiles/test/data/tile-header-examples.ts @@ -1,12 +1,17 @@ -export const ROOT_TILE_HEADER = { - id: 0, +import {I3STileHeader} from '@loaders.gl/i3s/src'; +import {TILE_TYPE} from '../../src'; + +/* eslint-disable @typescript-eslint/no-loss-of-precision */ +export const ROOT_TILE_HEADER: I3STileHeader = { + id: '0', lodSelection: [ { metricType: 'maxScreenThreshold', - maxError: null + maxError: 1 }, { - metricType: 'maxScreenThresholdSQ' + metricType: 'maxScreenThresholdSQ', + maxError: 2 } ], obb: { @@ -14,15 +19,14 @@ export const ROOT_TILE_HEADER = { halfSize: [6821.31591796875, 7171.64501953125, 704.45751953125], quaternion: [-0.49739304184913635, 0.7455593347549438, 0.18463276326656342, 0.40330156683921814] }, - contentUrl: null, - textureUrl: null, + contentUrl: '', + textureUrl: '', attributeUrls: [], - materialDefinition: null, - textureFormat: 'jpeg', + textureFormat: 'jpg', textureLoaderOptions: {}, children: [ { - id: 41506, + id: '41506', obb: { center: [-122.46338343363956, 37.79157575904703, 87.41352929640561], halfSize: [860.66259765625, 1499.0565185546875, 72.74445343017578], @@ -30,7 +34,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41507, + id: '41507', obb: { center: [-122.44325696222157, 37.77435558658971, 113.91670957952738], halfSize: [1911.0234375, 864.5419311523438, 69.09400177001953], @@ -40,7 +44,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41508, + id: '41508', obb: { center: [-122.38714173013278, 37.785036373210346, 25.63612724095583], halfSize: [687.1192016601562, 270.4031677246094, 43.18417739868164], @@ -50,7 +54,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41509, + id: '41509', obb: { center: [-122.47322609289643, 37.804771562538335, 46.25938205327839], halfSize: [754.618896484375, 427.3918762207031, 25.311925888061523], @@ -60,7 +64,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41510, + id: '41510', obb: { center: [-122.40543456303817, 37.70896490290964, 14.203033368103206], halfSize: [1293.4400634765625, 451.307861328125, 42.441471099853516], @@ -70,7 +74,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41511, + id: '41511', obb: { center: [-122.46275199805805, 37.71733136931502, 101.90323915611953], halfSize: [1331.4241943359375, 757.920654296875, 129.77085876464844], @@ -80,7 +84,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41512, + id: '41512', obb: { center: [-122.36913707510038, 37.819276327228096, 32.53019021265209], halfSize: [1462.7674560546875, 565.540283203125, 75.977783203125], @@ -90,7 +94,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41513, + id: '41513', obb: { center: [-122.4221455608589, 37.77030288270862, 34.55862785037607], halfSize: [864.1947631835938, 381.7113342285156, 37.28173828125], @@ -100,7 +104,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41514, + id: '41514', obb: { center: [-122.4052730819273, 37.80431308938842, 26.91191199608147], halfSize: [880.1900024414062, 510.4002990722656, 42.31467056274414], @@ -110,7 +114,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41515, + id: '41515', obb: { center: [-122.43723668667397, 37.74831154832598, 128.71616963483393], halfSize: [1392.220947265625, 1130.8299560546875, 74.29374694824219], @@ -118,7 +122,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41516, + id: '41516', obb: { center: [-122.42768915729269, 37.76088413788145, 68.22871365770698], halfSize: [608.5289306640625, 1213.05859375, 52.2633171081543], @@ -128,7 +132,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41517, + id: '41517', obb: { center: [-122.3955458161164, 37.786343202741016, 105.05504272133112], halfSize: [1441.6737060546875, 703.1207275390625, 117.90045166015625], @@ -138,7 +142,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41518, + id: '41518', obb: { center: [-122.46512838833831, 37.73880705250267, 158.66555406618863], halfSize: [1281.4132080078125, 730.6826171875, 71.8216552734375], @@ -148,7 +152,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41519, + id: '41519', obb: { center: [-122.47753503119182, 37.72035872566019, 54.88497630506754], halfSize: [1560.1402587890625, 729.7100830078125, 45.21710968017578], @@ -158,7 +162,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41520, + id: '41520', obb: { center: [-122.50833686170412, 37.77645621843504, 52.783406156115234], halfSize: [441.5261535644531, 45.46664047241211, 716.30419921875], @@ -168,7 +172,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41521, + id: '41521', obb: { center: [-122.49739952427169, 37.7310144709836, 23.204138641245663], halfSize: [803.75732421875, 335.3168640136719, 20.387283325195312], @@ -178,7 +182,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41522, + id: '41522', obb: { center: [-122.48768114332691, 37.734718942057945, 43.50286930799484], halfSize: [735.1307983398438, 584.945556640625, 28.22525405883789], @@ -186,7 +190,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41523, + id: '41523', obb: { center: [-122.40622594664637, 37.76981207438519, 67.59241663012654], halfSize: [2105.51611328125, 1169.5235595703125, 77.37430572509766], @@ -194,7 +198,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41524, + id: '41524', obb: { center: [-122.40607935300977, 37.74079131700362, 72.22274730075151], halfSize: [1257.2979736328125, 720.8424682617188, 71.56165313720703], @@ -204,7 +208,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41525, + id: '41525', obb: { center: [-122.40189525147514, 37.716178370340096, 51.319483746774495], halfSize: [973.6466674804688, 467.41363525390625, 45.367671966552734], @@ -214,7 +218,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41526, + id: '41526', obb: { center: [-122.49892888718392, 37.766488543994754, 43.22436154168099], halfSize: [1095.7742919921875, 1005.9462890625, 25.965656280517578], @@ -224,7 +228,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41527, + id: '41527', obb: { center: [-122.45404557126503, 37.739094984993336, 178.40252659004182], halfSize: [1298.6903076171875, 989.6760864257812, 78.60284423828125], @@ -234,7 +238,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41528, + id: '41528', obb: { center: [-122.47853781869429, 37.76170486191846, 91.05843559093773], halfSize: [915.3900756835938, 690.7364501953125, 31.619583129882812], @@ -244,7 +248,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41529, + id: '41529', obb: { center: [-122.43071682121908, 37.78254134689609, 85.59938156232238], halfSize: [835.1835327148438, 2051.666015625, 73.65888214111328], @@ -254,7 +258,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41530, + id: '41530', obb: { center: [-122.47664939586704, 37.741481191326706, 78.08638980146497], halfSize: [882.01123046875, 1864.734375, 63.895729064941406], @@ -264,7 +268,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41531, + id: '41531', obb: { center: [-122.42361079762696, 37.71666171688701, 11.226608323864639], halfSize: [1377.7069091796875, 157.40052795410156, 1113.192626953125], @@ -274,7 +278,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41532, + id: '41532', obb: { center: [-122.42284792143784, 37.78625663850666, 70.05951850209385], halfSize: [960.7679443359375, 57.74011993408203, 781.2603759765625], @@ -284,7 +288,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41533, + id: '41533', obb: { center: [-122.45310238320084, 37.764423237419415, 125.66769764199853], halfSize: [1909.5545654296875, 683.7047729492188, 307.73065185546875], @@ -294,7 +298,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41534, + id: '41534', obb: { center: [-122.44096500198518, 37.713308883580034, 64.96163192857057], halfSize: [799.1813354492188, 192.2926483154297, 809.0003051757812], @@ -304,7 +308,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41535, + id: '41535', obb: { center: [-122.48787112244179, 37.77940823881972, 53.63671676069498], halfSize: [1264.1429443359375, 838.586669921875, 41.3796501159668], @@ -314,7 +318,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41536, + id: '41536', obb: { center: [-122.4207171502184, 37.80236273660287, 72.39202263299376], halfSize: [1312.76708984375, 1009.3028564453125, 79.23953247070312], @@ -324,7 +328,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41537, + id: '41537', obb: { center: [-122.41816826700511, 37.74769717783828, 64.53814112767577], halfSize: [923.3890991210938, 1227.7459716796875, 47.02762985229492], @@ -334,7 +338,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41538, + id: '41538', obb: { center: [-122.49715044450468, 37.71484687187544, 40.32073772419244], halfSize: [638.0300903320312, 317.633056640625, 39.06645965576172], @@ -344,7 +348,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41539, + id: '41539', obb: { center: [-122.45332663988128, 37.79608365161349, 56.905715997330844], halfSize: [1051.6903076171875, 619.3232421875, 46.37852478027344], @@ -354,7 +358,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41540, + id: '41540', obb: { center: [-122.36719805562464, 37.72493030186783, 54.42812561523169], halfSize: [770.9337768554688, 822.1390380859375, 56.43045425415039], @@ -364,7 +368,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41541, + id: '41541', obb: { center: [-122.37486334976339, 37.74319949870364, 36.940472713671625], halfSize: [632.3360595703125, 295.60211181640625, 35.15031051635742], @@ -374,7 +378,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41542, + id: '41542', obb: { center: [-122.38335121801714, 37.73386716789014, 28.973584176041186], halfSize: [1071.7855224609375, 861.2364501953125, 63.71754455566406], @@ -384,7 +388,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41543, + id: '41543', obb: { center: [-122.40892227790425, 37.79412655271593, 143.70168329402804], halfSize: [946.2178955078125, 966.2469482421875, 136.01101684570312], @@ -394,7 +398,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41544, + id: '41544', obb: { center: [-122.39951295179227, 37.72423544264045, 53.146655006334186], halfSize: [1467.816650390625, 1197.76123046875, 68.97917938232422], @@ -402,7 +406,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41545, + id: '41545', obb: { center: [-122.45108890152824, 37.70957173273802, 4.935595765709877], halfSize: [746.944091796875, 460.1228942871094, 121.1246337890625], @@ -412,7 +416,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41546, + id: '41546', obb: { center: [-122.39089569336122, 37.75022996548035, 51.74469989910722], halfSize: [1342.734375, 1331.123779296875, 54.23269271850586], @@ -422,7 +426,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41547, + id: '41547', obb: { center: [-122.48835992922028, 37.71800134615249, 36.15773425064981], halfSize: [1443.7041015625, 663.3406372070312, 53.296207427978516], @@ -432,7 +436,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41548, + id: '41548', obb: { center: [-122.38678793429358, 37.720122320511905, 39.30954947974533], halfSize: [1094.1553955078125, 566.8689575195312, 36.92178726196289], @@ -442,7 +446,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41549, + id: '41549', obb: { center: [-122.49142617782246, 37.74980974833983, 78.82620551344007], halfSize: [1412.5701904296875, 879.9523315429688, 37.0567626953125], @@ -452,7 +456,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41550, + id: '41550', obb: { center: [-122.39147198105519, 37.76979271692245, 32.93882753327489], halfSize: [1242.7596435546875, 931.6583862304688, 61.4817008972168], @@ -462,7 +466,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41551, + id: '41551', obb: { center: [-122.4183814362624, 37.73116883832189, 73.38644873537123], halfSize: [790.16552734375, 1083.8994140625, 65.22572326660156], @@ -472,7 +476,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41552, + id: '41552', obb: { center: [-122.47952497948472, 37.785843651521475, 66.14011090993881], halfSize: [1761.522216796875, 863.6688842773438, 44.46645736694336], @@ -482,7 +486,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41553, + id: '41553', obb: { center: [-122.4544336205876, 37.719663107816416, 113.54782512038946], halfSize: [1355.514404296875, 600.526123046875, 41.309486389160156], @@ -492,7 +496,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41554, + id: '41554', obb: { center: [-122.46719942908598, 37.770225572220276, 89.35076693631709], halfSize: [1310.6097412109375, 639.2066040039062, 34.590518951416016], @@ -502,7 +506,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41555, + id: '41555', obb: { center: [-122.44047192720666, 37.72800003248351, 78.00553898885846], halfSize: [975.6348876953125, 733.91357421875, 37.54833984375], @@ -512,7 +516,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41556, + id: '41556', obb: { center: [-122.44390501645742, 37.79918800898806, 53.64893603697419], halfSize: [961.279296875, 745.121337890625, 46.31787109375], @@ -522,7 +526,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41557, + id: '41557', obb: { center: [-122.42826724456995, 37.73449210947414, 98.31865843851119], halfSize: [952.12255859375, 74.5724868774414, 825.378173828125], @@ -532,7 +536,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41558, + id: '41558', obb: { center: [-122.50099896954035, 37.777554086374636, 76.88974235299975], halfSize: [651.64453125, 776.2841186523438, 34.5166130065918], @@ -542,7 +546,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41559, + id: '41559', obb: { center: [-122.46592973484405, 37.756748199452794, 136.66738384682685], halfSize: [983.4131469726562, 85.38414001464844, 641.7821655273438], @@ -552,7 +556,7 @@ export const ROOT_TILE_HEADER = { } }, { - id: 41560, + id: '41560', obb: { center: [-122.49969683419471, 37.74523000448434, 43.06540256924927], halfSize: [736.8759155273438, 1740.930419921875, 37.1663703918457], @@ -573,8 +577,7 @@ export const ROOT_TILE_HEADER = { ] }, lodMetricType: 'maxScreenThreshold', - lodMetricValue: null, - type: 'mesh', + type: TILE_TYPE.MESH, refine: 2 }; diff --git a/modules/tiles/test/index.ts b/modules/tiles/test/index.ts index 794488d1b2..697e9a3158 100644 --- a/modules/tiles/test/index.ts +++ b/modules/tiles/test/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import './utils/doubly-linked-list.spec'; @@ -8,6 +9,7 @@ import './tileset/tileset-traverser.spec'; import './tileset/helpers/get-frame-state.spec'; import './tileset/helpers/zoom.spec'; +import './tileset/helpers/bounding-volume.spec'; // I3S Specific tests // TODO - deck.gl dependency diff --git a/modules/tiles/test/test-utils/compareArrays.ts b/modules/tiles/test/test-utils/compareArrays.ts new file mode 100644 index 0000000000..dea91c4b0a --- /dev/null +++ b/modules/tiles/test/test-utils/compareArrays.ts @@ -0,0 +1,14 @@ +const EPSILON = 0.000000001; +export function areNumberArraysEqual(array1, array2) { + let result = true; + if (array1.length !== array2.length) { + return false; + } + for (let i = 0; i < array1.length; i++) { + if (Math.abs(array1[i] - array2[i]) > EPSILON) { + result = false; + break; + } + } + return result; +} diff --git a/modules/tiles/test/tileset/format-i3s/i3s-lod.spec.ts b/modules/tiles/test/tileset/format-i3s/i3s-lod.spec.ts index 65938e3a0f..e43be81acd 100644 --- a/modules/tiles/test/tileset/format-i3s/i3s-lod.spec.ts +++ b/modules/tiles/test/tileset/format-i3s/i3s-lod.spec.ts @@ -1,6 +1,6 @@ import test from 'tape-promise/tape'; import {WebMercatorViewport} from '@deck.gl/core'; -import {TILESET_STUB} from '@loaders.gl/i3s/test/test-utils/load-utils'; +import {getI3sTileHeader} from '@loaders.gl/i3s/test/test-utils/load-utils'; import {getFrameState, Tile3D, Tileset3D, getLodStatus} from '@loaders.gl/tiles'; import { getBigLodMetricTileHeader, @@ -16,8 +16,8 @@ import { VIEWPORT_ZOOM_OUT_OPTS } from '../../data/viewport-opts-examples'; -test('I3S LOD#lodJudge - should return "DIG" if lodMetric is 0 or NaN', (t) => { - const tilesetHeader = TILESET_STUB(); +test('I3S LOD#lodJudge - should return "DIG" if lodMetric is 0 or NaN', async (t) => { + const tilesetHeader = await getI3sTileHeader(); tilesetHeader.root = ROOT_TILE_HEADER; const tileset = new Tileset3D(tilesetHeader); const viewport = new WebMercatorViewport(VIEWPORT_DEFAULT); @@ -37,8 +37,8 @@ test('I3S LOD#lodJudge - should return "DIG" if lodMetric is 0 or NaN', (t) => { t.end(); }); -test('I3S LOD#lodJudge - should return "DRAW" if tile size projected on the screen plane less then LOD metric value', (t) => { - const tilesetHeader = TILESET_STUB(); +test('I3S LOD#lodJudge - should return "DRAW" if tile size projected on the screen plane less then LOD metric value', async (t) => { + const tilesetHeader = await getI3sTileHeader(); tilesetHeader.root = ROOT_TILE_HEADER; const tileset = new Tileset3D(tilesetHeader); const viewport = new WebMercatorViewport(VIEWPORT_DEFAULT); @@ -52,8 +52,8 @@ test('I3S LOD#lodJudge - should return "DRAW" if tile size projected on the scre t.end(); }); -test('I3S LOD#lodJudge - should return "DIG" when zoom in', (t) => { - const tilesetHeader = TILESET_STUB(); +test('I3S LOD#lodJudge - should return "DIG" when zoom in', async (t) => { + const tilesetHeader = await getI3sTileHeader(); tilesetHeader.root = ROOT_TILE_HEADER; const tileset = new Tileset3D(tilesetHeader); const viewport = new WebMercatorViewport(VIEWPORT_ZOOM_OPTS); @@ -67,8 +67,8 @@ test('I3S LOD#lodJudge - should return "DIG" when zoom in', (t) => { t.end(); }); -test('I3S LOD#lodJudge - should return "DRAW" after rotation', (t) => { - const tilesetHeader = TILESET_STUB(); +test('I3S LOD#lodJudge - should return "DRAW" after rotation', async (t) => { + const tilesetHeader = await getI3sTileHeader(); tilesetHeader.root = ROOT_TILE_HEADER; const tileset = new Tileset3D(tilesetHeader); const viewport = new WebMercatorViewport(VIEWPORT_ROTATED_OPTS); @@ -82,8 +82,8 @@ test('I3S LOD#lodJudge - should return "DRAW" after rotation', (t) => { t.end(); }); -test('I3S LOD#lodJudge - should return "OUT" if projected size too small', (t) => { - const tilesetHeader = TILESET_STUB(); +test('I3S LOD#lodJudge - should return "OUT" if projected size too small', async (t) => { + const tilesetHeader = await getI3sTileHeader(); tilesetHeader.root = ROOT_TILE_HEADER; const tileset = new Tileset3D(tilesetHeader); const viewport = new WebMercatorViewport(VIEWPORT_ZOOM_OUT_OPTS); @@ -97,8 +97,8 @@ test('I3S LOD#lodJudge - should return "OUT" if projected size too small', (t) = t.end(); }); -test('I3S LOD#lodJudge - should return "DIG" in the large LOD metric value case', (t) => { - const tilesetHeader = TILESET_STUB(); +test('I3S LOD#lodJudge - should return "DIG" in the large LOD metric value case', async (t) => { + const tilesetHeader = await getI3sTileHeader(); tilesetHeader.root = ROOT_TILE_HEADER; const tileset = new Tileset3D(tilesetHeader); const viewport = new WebMercatorViewport(VIEWPORT_NEW_YORK_OPTS); diff --git a/modules/tiles/test/tileset/helpers/bounding-volume.spec.ts b/modules/tiles/test/tileset/helpers/bounding-volume.spec.ts new file mode 100644 index 0000000000..ec74ec0b20 --- /dev/null +++ b/modules/tiles/test/tileset/helpers/bounding-volume.spec.ts @@ -0,0 +1,33 @@ +import test from 'tape-promise/tape'; +import {createBoundingVolume} from '../../../src/tileset/helpers/bounding-volume'; +import {Matrix4} from '@math.gl/core'; +import {OrientedBoundingBox} from '@math.gl/culling'; +import {areNumberArraysEqual} from '../../test-utils/compareArrays'; + +test('Tiles bounding-volume#createBoundingVolume - should convert region to obb', (t) => { + const result = createBoundingVolume( + { + region: [ + 0.7853981633974483, 0.22689280275926285, 0.8028514559173916, 0.24434609527920614, + -17.29296875, 2493.5625 + ] + }, + new Matrix4() + ); + t.ok(result instanceof OrientedBoundingBox); + t.ok( + areNumberArraysEqual(result.center, [4348195.679745842, 4424932.075472673, 1479471.892147189]) + ); + t.ok( + areNumberArraysEqual( + result.halfAxes, + // prettier-ignore + [ + -38691.151309899986, 37690.50114047807, -2.3283064365386963e-10, + -9209.347353689373, -9371.872726273723, 53695.496290528914, + 1176.6872740909457, 1197.4532991172746, 403.06533637456596 + ] + ) + ); + t.end(); +}); diff --git a/modules/tiles/test/tileset/helpers/get-frame-state.spec.ts b/modules/tiles/test/tileset/helpers/get-frame-state.spec.ts index 61fda8d7b1..c0e14092ae 100644 --- a/modules/tiles/test/tileset/helpers/get-frame-state.spec.ts +++ b/modules/tiles/test/tileset/helpers/get-frame-state.spec.ts @@ -1,22 +1,5 @@ -// Copyright (c) 2015 - 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; import {getFrameState} from '@loaders.gl/tiles'; diff --git a/modules/tiles/test/tileset/tileset-3d.spec.ts b/modules/tiles/test/tileset/tileset-3d.spec.ts index 212179bc7c..55b9bccb37 100644 --- a/modules/tiles/test/tileset/tileset-3d.spec.ts +++ b/modules/tiles/test/tileset/tileset-3d.spec.ts @@ -162,12 +162,23 @@ test('Tileset3D#url set up correctly given path with query string', async (t) => '/test/data/Tilesets/TilesetOfTilesets/tileset2.json?param3=3&session=sesh¶m1=1¶m2=2&v=1.2.3', 'child url content parameters preserved' ); + const urlEnds = tileset.getTileUrl(tile.contentUrl).slice(-1); + t.equals('?&'.includes(urlEnds), false); } else { t.fail('no tile'); } t.end(); }); +test('Tileset3D#getTileUrl should not ends with sign ? or &', async (t) => { + const path = '@loaders.gl/3d-tiles/test/data/Tilesets/TilesetOfTilesets/tileset2.json'; + const tilesetJson = await load(path, Tiles3DLoader); + const tileset = new Tileset3D(tilesetJson); + const urlEnds = tileset.getTileUrl(tileset.url).slice(-1); + t.equals('?&'.includes(urlEnds), false); + t.end(); +}); + test('Tileset3D#loads and initializes with tileset JSON file', async (t) => { const tilesetJson = await load(TILESET_URL, Tiles3DLoader); const tileset = new Tileset3D(tilesetJson); diff --git a/modules/tiles/test/tileset/tileset-traverser.spec.ts b/modules/tiles/test/tileset/tileset-traverser.spec.ts index a6176bcdd9..1e7f78735d 100644 --- a/modules/tiles/test/tileset/tileset-traverser.spec.ts +++ b/modules/tiles/test/tileset/tileset-traverser.spec.ts @@ -12,6 +12,11 @@ const TILESET_URL = '@loaders.gl/3d-tiles/test/data/Tilesets/Tileset/tileset.jso test('Tileset3D#traverser base class', async (t) => { const tilesetJson = await load(TILESET_URL, Tiles3DLoader); + if (tilesetJson.shape !== 'tileset3d') { + t.fail('tileset'); + t.end(); + return; + } // Create Tileset3D to have initialized Tile3Ds tree const tileset = new Tileset3D(tilesetJson); diff --git a/modules/video/package.json b/modules/video/package.json index e31ecfa302..b2c6e0425d 100644 --- a/modules/video/package.json +++ b/modules/video/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/video", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loaders and writers for video (MP4, WEBM, ...)", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "PLY" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -29,12 +37,12 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/worker-utils": "4.0.0-alpha.13", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/worker-utils": "4.0.3", "gifshot": "^0.4.5" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" diff --git a/modules/video/src/bundle.ts b/modules/video/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/video/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/video/src/lib/gifshot/gifshot.ts b/modules/video/src/lib/gifshot/gifshot.ts index 2ee7e4f901..c4d02633a2 100644 --- a/modules/video/src/lib/gifshot/gifshot.ts +++ b/modules/video/src/lib/gifshot/gifshot.ts @@ -1,6 +1,8 @@ // @ts-nocheck /* eslint-disable */ +const document = globalThis.document || {}; + /* Copyrights for code authored by Yahoo Inc. is licensed under the following terms: MIT License Copyright 2017 Yahoo Inc. diff --git a/modules/video/src/video-loader.ts b/modules/video/src/video-loader.ts index 1b2c3ee9f1..ff4ca7df6b 100644 --- a/modules/video/src/video-loader.ts +++ b/modules/video/src/video-loader.ts @@ -18,7 +18,7 @@ const DEFAULT_LOADER_OPTIONS: VideoLoaderOptions = { video: {} }; -export const VideoLoader = { +export const VideoLoader: LoaderWithParser = { name: 'Video', id: 'video', module: 'video', @@ -31,5 +31,3 @@ export const VideoLoader = { // tests: arrayBuffer => Boolean(getBinaryImageMetadata(new DataView(arrayBuffer))), options: DEFAULT_LOADER_OPTIONS }; - -export const _typecheckVideoLoader: LoaderWithParser = VideoLoader; diff --git a/modules/wkt/README.md b/modules/wkt/README.md index fd4055f2ef..51d35a75ed 100644 --- a/modules/wkt/README.md +++ b/modules/wkt/README.md @@ -1,5 +1,8 @@ # @loaders.gl/wkt -This module contains a geometry loader for the Well-Known Text (WKT) and Well-Known Binary (WKB) formats. +This module contains a geometry loader for the following formats: +- Well-Known Text (WKT) +- Well-Known Text Coordinate Reference System (WKT-CRS) +- Well-Known Binary (WKB) [loaders.gl](https://loaders.gl/docs) is a collection of framework-independent visualization-focused loaders (parsers). diff --git a/modules/wkt/package.json b/modules/wkt/package.json index 19ee42a815..492fdad689 100644 --- a/modules/wkt/package.json +++ b/modules/wkt/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/wkt", "description": "Loader and Writer for the WKT (Well Known Text) Format", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -19,8 +20,15 @@ "Well Known Text" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -28,16 +36,16 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-worker && npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js", + "pre-build": "npm run build-worker && npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts", "build-worker": "esbuild src/workers/wkt-worker.ts --bundle --outfile=dist/wkt-worker.js --define:__VERSION__=\\\"$npm_package_version\\\"" }, "devDependencies": { "fuzzer": "^0.2.1" }, "dependencies": { - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13" + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/wkt/src/bundle.ts b/modules/wkt/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/wkt/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/wkt/src/hex-wkb-loader.ts b/modules/wkt/src/hex-wkb-loader.ts new file mode 100644 index 0000000000..94ad0c9f67 --- /dev/null +++ b/modules/wkt/src/hex-wkb-loader.ts @@ -0,0 +1,62 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {LoaderWithParser} from '@loaders.gl/loader-utils'; +import {BinaryGeometry} from '@loaders.gl/schema'; + +import type {WKBLoaderOptions} from './wkb-loader'; +import {WKBLoader} from './wkb-loader'; +import {VERSION} from './lib/utils/version'; +import {decodeHex} from './lib/utils/hex-transcoder'; + +export type HexWKBLoaderOptions = WKBLoaderOptions; + +/** + * Worker loader for Hex-encoded WKB (Well-Known Binary) + */ +export const HexWKBLoader: LoaderWithParser = { + name: 'Hexadecimal WKB', + id: 'wkb', + module: 'wkt', + version: VERSION, + worker: true, + category: 'geometry', + extensions: ['wkb'], + mimeTypes: [], + options: WKBLoader.options, + text: true, + testText: isHexWKB, + // TODO - encoding here seems wasteful - extend hex transcoder? + parse: async (arrayBuffer: ArrayBuffer) => parseHexWKB(new TextDecoder().decode(arrayBuffer)), + parseTextSync: parseHexWKB +}; + +function parseHexWKB(text: string, options?: HexWKBLoaderOptions): BinaryGeometry { + const uint8Array = decodeHex(text); + const binaryGeometry = WKBLoader.parseSync?.(uint8Array.buffer, options); + // @ts-expect-error + return binaryGeometry; +} + +/** + * Check if string is a valid Well-known binary (WKB) in HEX format + * https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry + * + * @param str input string + * @returns true if string is a valid WKB in HEX format + */ +export function isHexWKB(string: string | null): boolean { + if (!string) { + return false; + } + // check if the length of the string is even and is at least 10 characters long + if (string.length < 10 || string.length % 2 !== 0) { + return false; + } + // check if first two characters are 00 or 01 + if (!string.startsWith('00') && !string.startsWith('01')) { + return false; + } + // check if the rest of the string is a valid hex + return /^[0-9a-fA-F]+$/.test(string.slice(2)); +} diff --git a/modules/wkt/src/index.ts b/modules/wkt/src/index.ts index 118a7adb5b..8db56c642a 100644 --- a/modules/wkt/src/index.ts +++ b/modules/wkt/src/index.ts @@ -1,4 +1,26 @@ -export {WKBLoader, WKBWorkerLoader} from './wkb-loader'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +export {WKTCRSLoader} from './wkt-crs-loader'; +export {WKTCRSWriter} from './wkt-crs-writer'; + export {WKTLoader, WKTWorkerLoader} from './wkt-loader'; export {WKTWriter} from './wkt-writer'; + +export {WKBLoader, WKBWorkerLoader} from './wkb-loader'; export {WKBWriter} from './wkb-writer'; + +export {HexWKBLoader} from './hex-wkb-loader'; + +export {TWKBLoader} from './twkb-loader'; +export {TWKBWriter} from './twkb-writer'; + +// EXPERIMENTAL APIs +export {isWKT} from './lib/parse-wkt'; + +export {isWKB, parseWKBHeader} from './lib/parse-wkb-header'; +export type {WKBHeader} from './lib/parse-wkb-header'; + +export {isTWKB} from './lib/parse-twkb'; + +export {encodeHex, decodeHex} from './lib/utils/hex-transcoder'; diff --git a/modules/wkt/src/lib/encode-twkb.ts b/modules/wkt/src/lib/encode-twkb.ts new file mode 100644 index 0000000000..f119c3d8f0 --- /dev/null +++ b/modules/wkt/src/lib/encode-twkb.ts @@ -0,0 +1,305 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// Forked from https://github.com/cschwarz/wkx under MIT license, Copyright (c) 2013 Christian Schwarz + +import type {Point, MultiPoint, LineString} from '@loaders.gl/schema'; +import type { + MultiLineString, + Polygon, + MultiPolygon, + GeometryCollection, + Geometry +} from '@loaders.gl/schema'; + +import {BinaryWriter} from './utils/binary-writer'; +import {WKBGeometryType} from './parse-wkb-header'; + +type TWKBPrecision = { + xy: number; + z: number; + m: number; + xyFactor: number; + zFactor: number; + mFactor: number; +}; + +type TWKBEncoderContext = TWKBPrecision & { + hasZ?: boolean; + hasM?: boolean; +}; + +export function encodeTWKB( + geometry: Geometry, + options?: {hasZ?: boolean; hasM?: boolean} +): ArrayBuffer { + const writer = new BinaryWriter(0, true); + + const context: TWKBEncoderContext = { + ...getTwkbPrecision(5, 0, 0), + hasZ: options?.hasZ, + hasM: options?.hasM + }; + + encodeGeometry(writer, geometry, context); + + // TODO - we need to slice it? + return writer.arrayBuffer; +} + +function encodeGeometry(writer: BinaryWriter, geometry: Geometry, context: TWKBEncoderContext) { + switch (geometry.type) { + case 'Point': + return encodePoint(writer, context, geometry); + case 'LineString': + return encodeLineString(writer, context, geometry); + case 'Polygon': + return encodePolygon(writer, context, geometry); + case 'MultiPoint': + return encodeMultiPoint(writer, context, geometry); + case 'MultiLineString': + return encodeMultiLineString(writer, context, geometry); + case 'MultiPolygon': + return encodeMultiPolygon(writer, context, geometry); + case 'GeometryCollection': + return encodeGeometryCollection(writer, context, geometry); + default: + throw new Error('unsupported geometry type'); + } +} + +function encodePoint(writer: BinaryWriter, context: TWKBEncoderContext, point: Point): void { + const isEmpty = + point.coordinates.length === 0 || point[0] === 'undefined' || point[1] === 'undefined'; + + writeTwkbHeader(writer, context, WKBGeometryType.Point, isEmpty); + + if (!isEmpty) { + const previousPoint = [0, 0, 0, 0]; + writeTwkbPoint(writer, context, point.coordinates, previousPoint); + } +} + +function encodeLineString( + writer: BinaryWriter, + context: TWKBEncoderContext, + lineString: LineString +): ArrayBuffer { + const points = lineString.coordinates; + const isEmpty = points.length === 0; + + writeTwkbHeader(writer, context, WKBGeometryType.LineString, isEmpty); + + if (!isEmpty) { + writer.writeVarInt(points.length); + const previousPoint = [0, 0, 0, 0]; + for (const point of points) { + writeTwkbPoint(writer, context, point, previousPoint); + } + } + + return writer.arrayBuffer; +} + +function encodePolygon( + writer: BinaryWriter, + context: TWKBEncoderContext, + polygon: Polygon +): ArrayBuffer { + const polygonRings = polygon.coordinates; + + const isEmpty = polygonRings.length === 0; + + writeTwkbHeader(writer, context, WKBGeometryType.Polygon, isEmpty); + + if (!isEmpty) { + writer.writeVarInt(polygonRings.length); + + const previousPoint = [0, 0, 0, 0]; + for (const ring of polygonRings) { + writer.writeVarInt(ring.length); + for (const point of ring) { + writeTwkbPoint(writer, context, previousPoint, point); + } + } + } + + return writer.arrayBuffer; +} + +function encodeMultiPoint( + writer: BinaryWriter, + context: TWKBEncoderContext, + multiPoint: MultiPoint +): void { + const points = multiPoint.coordinates; + const isEmpty = points.length === 0; + + writeTwkbHeader(writer, context, WKBGeometryType.MultiPoint, isEmpty); + + if (!isEmpty) { + writer.writeVarInt(points.length); + + const previousPoint = [0, 0, 0, 0]; + for (let i = 0; i < points.length; i++) { + writeTwkbPoint(writer, context, previousPoint, points[i]); + } + } +} + +function encodeMultiLineString( + writer: BinaryWriter, + context: TWKBEncoderContext, + multiLineStrings: MultiLineString +): ArrayBuffer { + const lineStrings = multiLineStrings.coordinates; + const isEmpty = lineStrings.length === 0; + + writeTwkbHeader(writer, context, WKBGeometryType.MultiLineString, isEmpty); + + if (!isEmpty) { + writer.writeVarInt(lineStrings.length); + + const previousPoint = [0, 0, 0, 0]; + for (const lineString of lineStrings) { + writer.writeVarInt(lineString.length); + + for (const point of lineString) { + writeTwkbPoint(writer, context, previousPoint, point); + } + } + } + + return writer.arrayBuffer; +} + +function encodeMultiPolygon( + writer: BinaryWriter, + context: TWKBEncoderContext, + multiPolygon: MultiPolygon +): void { + const {coordinates} = multiPolygon; + const isEmpty = coordinates.length === 0; + + writeTwkbHeader(writer, context, WKBGeometryType.MultiPolygon, isEmpty); + + if (!isEmpty) { + const polygons = coordinates; + writer.writeVarInt(polygons.length); + + const previousPoint = [0, 0, 0, 0]; + + for (const polygonRings of polygons) { + writer.writeVarInt(polygonRings.length); + for (const ring of polygonRings) { + writer.writeVarInt(ring.length); + for (const point of ring) { + writeTwkbPoint(writer, context, previousPoint, point); + } + } + } + } +} + +function encodeGeometryCollection( + writer: BinaryWriter, + context: TWKBEncoderContext, + geometryCollection: GeometryCollection +): void { + const {geometries} = geometryCollection; + const isEmpty = geometries.length === 0; + + writeTwkbHeader(writer, context, WKBGeometryType.GeometryCollection, isEmpty); + + if (geometries.length > 0) { + writer.writeVarInt(geometries.length); + for (const geometry of geometries) { + encodeGeometry(writer, geometry, context); + } + } +} + +/** + * + * @param writer + * @param context + * @param geometryType + * @param isEmpty + */ +function writeTwkbHeader( + writer: BinaryWriter, + context: TWKBEncoderContext, + geometryType: WKBGeometryType, + isEmpty: boolean +) { + const type = (zigZagEncode(context.xy) << 4) + geometryType; + let metadataHeader = context.hasZ || context.hasM ? 1 << 3 : 0; + metadataHeader += isEmpty ? 1 << 4 : 0; + + writer.writeUInt8(type); + writer.writeUInt8(metadataHeader); + + if (context.hasZ || context.hasM) { + let extendedPrecision = 0; + if (context.hasZ) { + extendedPrecision |= 0x1; + } + if (context.hasM) { + extendedPrecision |= 0x2; + } + writer.writeUInt8(extendedPrecision); + } +} + +/** + * Write one point to array buffer. ZigZagEncoding the delta fdrom the previous point. Mutates previousPoint. + * @param writer + * @param context + * @param previousPoint - Mutated by this function + * @param point + */ +function writeTwkbPoint( + writer: BinaryWriter, + context: TWKBEncoderContext, + point: number[], + previousPoint: number[] +): void { + const x = point[0] * context.xyFactor; + const y = point[1] * context.xyFactor; + const z = point[2] * context.zFactor; + const m = point[3] * context.mFactor; + + writer.writeVarInt(zigZagEncode(x - previousPoint[0])); + writer.writeVarInt(zigZagEncode(y - previousPoint[1])); + if (context.hasZ) { + writer.writeVarInt(zigZagEncode(z - previousPoint[2])); + } + if (context.hasM) { + writer.writeVarInt(zigZagEncode(m - previousPoint[3])); + } + + previousPoint[0] = x; + previousPoint[1] = y; + previousPoint[2] = z; + previousPoint[3] = m; +} + +// HELPERS + +function zigZagEncode(value: number): number { + return (value << 1) ^ (value >> 31); +} + +function getTwkbPrecision( + xyPrecision: number, + zPrecision: number, + mPrecision: number +): TWKBPrecision { + return { + xy: xyPrecision, + z: zPrecision, + m: mPrecision, + xyFactor: Math.pow(10, xyPrecision), + zFactor: Math.pow(10, zPrecision), + mFactor: Math.pow(10, mPrecision) + }; +} diff --git a/modules/wkt/src/lib/encode-wkb.ts b/modules/wkt/src/lib/encode-wkb.ts index 6203d93430..9fd8ae8f0e 100644 --- a/modules/wkt/src/lib/encode-wkb.ts +++ b/modules/wkt/src/lib/encode-wkb.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/cschwarz/wkx under MIT license, Copyright (c) 2013 Christian Schwarz // Reference: https://www.ogc.org/standards/sfa @@ -14,13 +15,13 @@ import type { GeometryCollection } from '@loaders.gl/schema'; -import BinaryWriter from './utils/binary-writer'; +import {BinaryWriter} from './utils/binary-writer'; /** * Integer code for geometry type * Reference: https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary */ -enum WKB { +export enum WKB { Point = 1, LineString = 2, Polygon = 3, @@ -33,7 +34,7 @@ enum WKB { /** * Options for encodeWKB */ -interface WKBOptions { +type WKBOptions = { /** Does the GeoJSON input have Z values? */ hasZ?: boolean; @@ -42,27 +43,18 @@ interface WKBOptions { /** Spatial reference for input GeoJSON */ srid?: any; -} +}; /** * Encodes a GeoJSON object into WKB * @param geojson A GeoJSON Feature or Geometry * @returns string */ -export default function encodeWKB( - geometry: Geometry | Feature, - options: WKBOptions | {wkb: WKBOptions} = {} -): ArrayBuffer { +export function encodeWKB(geometry: Geometry | Feature, options: WKBOptions = {}): ArrayBuffer { if (geometry.type === 'Feature') { geometry = geometry.geometry; } - // Options should be wrapped in a `wkb` key, but we allow top-level options here for backwards - // compatibility - if ('wkb' in options) { - options = options.wkb; - } - switch (geometry.type) { case 'Point': return encodePoint(geometry.coordinates, options); diff --git a/modules/wkt/src/lib/encode-wkt-crs.ts b/modules/wkt/src/lib/encode-wkt-crs.ts new file mode 100644 index 0000000000..4096178732 --- /dev/null +++ b/modules/wkt/src/lib/encode-wkt-crs.ts @@ -0,0 +1,40 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// parse-wkt-crs was forked from https://github.com/DanielJDufour/wkt-crs under Creative Commons CC0 1.0 license. + +import type {WKTCRS} from './parse-wkt-crs'; + +export type EncodeWKTCRSOptions = { + debug?: boolean; +}; + +/** + * convert JSON representation of Well-Known Text + * back to standard Well-Known Text + */ +export function encodeWKTCRS(wkt: WKTCRS, options?: EncodeWKTCRSOptions): string { + if (Array.isArray(wkt) && wkt.length === 1 && Array.isArray(wkt[0])) { + wkt = wkt[0]; // ignore first extra wrapper array + } + + const [kw, ...attrs] = wkt; + const str = `${kw}[${attrs + .map((attr) => { + if (Array.isArray(attr)) { + return encodeWKTCRS(attr, options); + } else if (typeof attr === 'number') { + return attr.toString(); + } else if (typeof attr === 'string') { + // can't automatically convert all caps to varibale + // because EPSG is string in AUTHORITY["EPSG", ...] + if (attr.startsWith('raw:')) { + // convert "raw:NORTH" to NORTH + return attr.replace('raw:', ''); + } + return `"${attr}"`; + } + throw new Error(`[wktcrs] unexpected attribute "${attr}"`); + }) + .join(',')}]`; + return str; +} diff --git a/modules/wkt/src/lib/encode-wkt.ts b/modules/wkt/src/lib/encode-wkt.ts index a637f9bd4b..5ac53e2e49 100644 --- a/modules/wkt/src/lib/encode-wkt.ts +++ b/modules/wkt/src/lib/encode-wkt.ts @@ -1,5 +1,7 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Fork of https://github.com/mapbox/wellknown under ISC license (MIT/BSD-2-clause equivalent) -// eslint-disable-next-line import/no-unresolved + import type {Feature, Geometry} from '@loaders.gl/schema'; /** @@ -7,7 +9,7 @@ import type {Feature, Geometry} from '@loaders.gl/schema'; * @param geojson * @returns string */ -export default function encodeWKT(geometry: Geometry | Feature): string { +export function encodeWKT(geometry: Geometry | Feature): string { if (geometry.type === 'Feature') { geometry = geometry.geometry; } diff --git a/docs/arrowjs/paul-drafts/data-types/index.md b/modules/wkt/src/lib/parse-hex-wkb.ts similarity index 100% rename from docs/arrowjs/paul-drafts/data-types/index.md rename to modules/wkt/src/lib/parse-hex-wkb.ts diff --git a/modules/wkt/src/lib/parse-twkb.ts b/modules/wkt/src/lib/parse-twkb.ts new file mode 100644 index 0000000000..2da82029e3 --- /dev/null +++ b/modules/wkt/src/lib/parse-twkb.ts @@ -0,0 +1,357 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// Forked from https://github.com/cschwarz/wkx under MIT license, Copyright (c) 2013 Christian Schwarz + +import type {Geometry, GeometryCollection} from '@loaders.gl/schema'; +import type {Point, LineString, Polygon} from '@loaders.gl/schema'; +import type {MultiPoint, MultiLineString, MultiPolygon} from '@loaders.gl/schema'; +import {BinaryReader} from './utils/binary-reader'; +import {WKBGeometryType} from './parse-wkb-header'; + +/** + * Check if an array buffer might be a TWKB array buffer + * @param arrayBuffer The array buffer to check + * @returns false if this is definitely not a TWKB array buffer, true if it might be a TWKB array buffer + */ +export function isTWKB(arrayBuffer: ArrayBuffer): boolean { + const binaryReader = new BinaryReader(arrayBuffer); + + const type = binaryReader.readUInt8(); + const geometryType = type & 0x0f; + + // Only geometry types 1 to 7 (point to geometry collection are currently defined) + if (geometryType < 1 || geometryType > 7) { + return false; + } + + return true; +} + +/** Passed around between parsing functions, extracted from the header */ +type TWKBHeader = { + geometryType: WKBGeometryType; + + hasBoundingBox: boolean; + hasSizeAttribute: boolean; + hasIdList: boolean; + hasExtendedPrecision: boolean; + isEmpty: boolean; + + precision: number; + precisionFactor: number; + + hasZ: boolean; + zPrecision: number; + zPrecisionFactor: number; + + hasM: boolean; + mPrecision: number; + mPrecisionFactor: number; +}; + +export function parseTWKBGeometry(arrayBuffer: ArrayBuffer): Geometry { + const binaryReader = new BinaryReader(arrayBuffer); + + const context = parseTWKBHeader(binaryReader); + + if (context.hasSizeAttribute) { + binaryReader.readVarInt(); + } + + if (context.hasBoundingBox) { + let dimensions = 2; + + if (context.hasZ) { + dimensions++; + } + if (context.hasM) { + dimensions++; + } + + // TODO why are we throwing away these datums? + for (let i = 0; i < dimensions; i++) { + binaryReader.readVarInt(); + binaryReader.readVarInt(); + } + } + + return parseGeometry(binaryReader, context, context.geometryType); +} + +function parseTWKBHeader(binaryReader: BinaryReader): TWKBHeader { + const type = binaryReader.readUInt8(); + const metadataHeader = binaryReader.readUInt8(); + + const geometryType = type & 0x0f; + + const precision = zigZagDecode(type >> 4); + + const hasExtendedPrecision = Boolean((metadataHeader >> 3) & 1); + let hasZ = false; + let hasM = false; + let zPrecision = 0; + let zPrecisionFactor = 1; + let mPrecision = 0; + let mPrecisionFactor = 1; + + if (hasExtendedPrecision) { + const extendedPrecision = binaryReader.readUInt8(); + hasZ = (extendedPrecision & 0x01) === 0x01; + hasM = (extendedPrecision & 0x02) === 0x02; + + zPrecision = zigZagDecode((extendedPrecision & 0x1c) >> 2); + zPrecisionFactor = Math.pow(10, zPrecision); + + mPrecision = zigZagDecode((extendedPrecision & 0xe0) >> 5); + mPrecisionFactor = Math.pow(10, mPrecision); + } + + return { + geometryType, + + precision, + precisionFactor: Math.pow(10, precision), + + hasBoundingBox: Boolean((metadataHeader >> 0) & 1), + hasSizeAttribute: Boolean((metadataHeader >> 1) & 1), + hasIdList: Boolean((metadataHeader >> 2) & 1), + hasExtendedPrecision, + isEmpty: Boolean((metadataHeader >> 4) & 1), + + hasZ, + hasM, + zPrecision, + zPrecisionFactor, + mPrecision, + mPrecisionFactor + }; +} + +function parseGeometry( + binaryReader: BinaryReader, + context: TWKBHeader, + geometryType: WKBGeometryType +): Geometry { + switch (geometryType) { + case WKBGeometryType.Point: + return parsePoint(binaryReader, context); + case WKBGeometryType.LineString: + return parseLineString(binaryReader, context); + case WKBGeometryType.Polygon: + return parsePolygon(binaryReader, context); + case WKBGeometryType.MultiPoint: + return parseMultiPoint(binaryReader, context); + case WKBGeometryType.MultiLineString: + return parseMultiLineString(binaryReader, context); + case WKBGeometryType.MultiPolygon: + return parseMultiPolygon(binaryReader, context); + case WKBGeometryType.GeometryCollection: + return parseGeometryCollection(binaryReader, context); + default: + throw new Error(`GeometryType ${geometryType} not supported`); + } +} + +// GEOMETRIES + +function parsePoint(reader: BinaryReader, context: TWKBHeader): Point { + if (context.isEmpty) { + return {type: 'Point', coordinates: []}; + } + + return {type: 'Point', coordinates: readFirstPoint(reader, context)}; +} + +function parseLineString(reader: BinaryReader, context: TWKBHeader): LineString { + if (context.isEmpty) { + return {type: 'LineString', coordinates: []}; + } + + const pointCount = reader.readVarInt(); + + const previousPoint = makePreviousPoint(context); + + const points: number[][] = []; + for (let i = 0; i < pointCount; i++) { + points.push(parseNextPoint(reader, context, previousPoint)); + } + + return {type: 'LineString', coordinates: points}; +} + +function parsePolygon(reader: BinaryReader, context: TWKBHeader): Polygon { + if (context.isEmpty) { + return {type: 'Polygon', coordinates: []}; + } + + const ringCount = reader.readVarInt(); + + const previousPoint = makePreviousPoint(context); + + const exteriorRingLength = reader.readVarInt(); + const exteriorRing: number[][] = []; + + for (let i = 0; i < exteriorRingLength; i++) { + exteriorRing.push(parseNextPoint(reader, context, previousPoint)); + } + + const polygon: number[][][] = [exteriorRing]; + for (let i = 1; i < ringCount; i++) { + const interiorRingCount = reader.readVarInt(); + + const interiorRing: number[][] = []; + for (let j = 0; j < interiorRingCount; j++) { + interiorRing.push(parseNextPoint(reader, context, previousPoint)); + } + + polygon.push(interiorRing); + } + + return {type: 'Polygon', coordinates: polygon}; +} + +function parseMultiPoint(reader: BinaryReader, context: TWKBHeader): MultiPoint { + if (context.isEmpty) { + return {type: 'MultiPoint', coordinates: []}; + } + + const previousPoint = makePreviousPoint(context); + const pointCount = reader.readVarInt(); + + const coordinates: number[][] = []; + for (let i = 0; i < pointCount; i++) { + coordinates.push(parseNextPoint(reader, context, previousPoint)); + } + + return {type: 'MultiPoint', coordinates}; +} + +function parseMultiLineString(reader: BinaryReader, context: TWKBHeader): MultiLineString { + if (context.isEmpty) { + return {type: 'MultiLineString', coordinates: []}; + } + + const previousPoint = makePreviousPoint(context); + const lineStringCount = reader.readVarInt(); + + const coordinates: number[][][] = []; + for (let i = 0; i < lineStringCount; i++) { + const pointCount = reader.readVarInt(); + + const lineString: number[][] = []; + for (let j = 0; j < pointCount; j++) { + lineString.push(parseNextPoint(reader, context, previousPoint)); + } + + coordinates.push(lineString); + } + + return {type: 'MultiLineString', coordinates}; +} + +function parseMultiPolygon(reader: BinaryReader, context: TWKBHeader): MultiPolygon { + if (context.isEmpty) { + return {type: 'MultiPolygon', coordinates: []}; + } + + const previousPoint = makePreviousPoint(context); + const polygonCount = reader.readVarInt(); + + const polygons: number[][][][] = []; + for (let i = 0; i < polygonCount; i++) { + const ringCount = reader.readVarInt(); + + const exteriorPointCount = reader.readVarInt(); + + const exteriorRing: number[][] = []; + for (let j = 0; j < exteriorPointCount; j++) { + exteriorRing.push(parseNextPoint(reader, context, previousPoint)); + } + + const polygon: number[][][] = exteriorRing ? [exteriorRing] : []; + + for (let j = 1; j < ringCount; j++) { + const interiorRing: number[][] = []; + + const interiorRingLength = reader.readVarInt(); + + for (let k = 0; k < interiorRingLength; k++) { + interiorRing.push(parseNextPoint(reader, context, previousPoint)); + } + + polygon.push(interiorRing); + } + + polygons.push(polygon); + } + + return {type: 'MultiPolygon', coordinates: polygons}; +} + +/** Geometry collection not yet supported */ +function parseGeometryCollection(reader: BinaryReader, context: TWKBHeader): GeometryCollection { + return {type: 'GeometryCollection', geometries: []}; + /** + if (context.isEmpty) { + return {type: 'GeometryCollection', geometries: []}; + } + + const geometryCount = reader.readVarInt(); + + const geometries: Geometry[] = new Array(geometryCount); + for (let i = 0; i < geometryCount; i++) { + const geometry = parseGeometry(reader, context, geometryType); + geometries.push(geometry); + } + + return {type: 'GeometryCollection', geometries: []}; + */ +} + +// HELPERS + +/** + * Maps negative values to positive values while going back and + forth (0 = 0, -1 = 1, 1 = 2, -2 = 3, 2 = 4, -3 = 5, 3 = 6 ...) + */ +function zigZagDecode(value: number): number { + return (value >> 1) ^ -(value & 1); +} + +function makePointCoordinates(x: number, y: number, z?: number, m?: number): number[] { + return (z !== undefined ? (m !== undefined ? [x, y, z, m] : [x, y, z]) : [x, y]) as number[]; +} + +function makePreviousPoint(context: TWKBHeader): number[] { + return makePointCoordinates(0, 0, context.hasZ ? 0 : undefined, context.hasM ? 0 : undefined); +} + +function readFirstPoint(reader: BinaryReader, context: TWKBHeader): number[] { + const x = zigZagDecode(reader.readVarInt()) / context.precisionFactor; + const y = zigZagDecode(reader.readVarInt()) / context.precisionFactor; + const z = context.hasZ ? zigZagDecode(reader.readVarInt()) / context.zPrecisionFactor : undefined; + const m = context.hasM ? zigZagDecode(reader.readVarInt()) / context.mPrecisionFactor : undefined; + return makePointCoordinates(x, y, z, m); +} + +/** + * Modifies previousPoint + */ +function parseNextPoint( + reader: BinaryReader, + context: TWKBHeader, + previousPoint: number[] +): number[] { + previousPoint[0] += zigZagDecode(reader.readVarInt()) / context.precisionFactor; + previousPoint[1] += zigZagDecode(reader.readVarInt()) / context.precisionFactor; + + if (context.hasZ) { + previousPoint[2] += zigZagDecode(reader.readVarInt()) / context.zPrecisionFactor; + } + if (context.hasM) { + previousPoint[3] += zigZagDecode(reader.readVarInt()) / context.mPrecisionFactor; + } + + // Copy the point + return previousPoint.slice(); +} diff --git a/modules/wkt/src/lib/parse-wkb-header.ts b/modules/wkt/src/lib/parse-wkb-header.ts new file mode 100644 index 0000000000..05ffe87ad8 --- /dev/null +++ b/modules/wkt/src/lib/parse-wkb-header.ts @@ -0,0 +1,173 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +const EWKB_FLAG_Z = 0x80000000; +const EWKB_FLAG_M = 0x40000000; +const EWKB_FLAG_SRID = 0x20000000; + +const MAX_SRID = 10000; // TBD: Assume no more than 10K SRIDs are defined + +/** + * Integer code for geometry types in WKB and related formats + * Reference: https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary + */ +export enum WKBGeometryType { + Point = 1, + LineString = 2, + Polygon = 3, + MultiPoint = 4, + MultiLineString = 5, + MultiPolygon = 6, + GeometryCollection = 7 +} + +/** Parsed WKB header */ +export type WKBHeader = { + /** WKB variant */ + type: 'wkb' | 'ewkb' | 'iso-wkb' | 'twkb'; + /** geometry type encoded in this WKB: point, line, polygon etc */ + geometryType: 1 | 2 | 3 | 4 | 5 | 6 | 7; + /** Number of dimensions for coordinate data */ + dimensions: 2 | 3 | 4; + /** Coordinate names, Z and M are controlled by flags */ + coordinates: 'xy' | 'xyz' | 'xym' | 'xyzm'; + srid?: number; + /** true if binary data is stored as little endian */ + littleEndian: boolean; + /** Offset to start of geometry */ + byteOffset: number; +}; + +/** Sanity checks that first to 5-9 bytes could represent a supported WKB dialect header */ +export function isWKB(arrayBuffer: ArrayBuffer): boolean { + const dataView = new DataView(arrayBuffer); + let byteOffset = 0; + + const endianness = dataView.getUint8(byteOffset); + byteOffset += 1; + + // Check valid endianness (only 0 or 1 are allowed) + if (endianness > 1) { + return false; + } + + const littleEndian = endianness === 1; + + const geometry = dataView.getUint32(byteOffset, littleEndian); + byteOffset += 4; + + // check valid geometry type (we don't support extension geometries) + const geometryType = geometry & 0x07; + if (geometryType === 0 || geometryType > 7) { + return false; + } + + const geometryFlags = geometry - geometryType; + + // Accept iso-wkb flags + if ( + geometryFlags === 0 || + geometryFlags === 1000 || + geometryFlags === 2000 || + geometryFlags === 3000 + ) { + return true; + } + + // Accept ewkb flags but reject otherwise + if ((geometryFlags & ~(EWKB_FLAG_Z | EWKB_FLAG_M | EWKB_FLAG_SRID)) !== 0) { + return false; + } + + if (geometryFlags & EWKB_FLAG_SRID) { + const srid = dataView.getUint32(byteOffset, littleEndian); + byteOffset += 4; + + if (srid > MAX_SRID) { + return false; + } + } + + return true; +} + +/** + * Parses header and provides a byteOffset to start of geometry data + * @param dataView + * @param target optionally supply a WKBHeader object to avoid creating a new object for every call + * @returns a header object describing the WKB data + */ +// eslint-disable-next-line max-statements +export function parseWKBHeader(dataView: DataView, target?: WKBHeader): WKBHeader { + const wkbHeader: WKBHeader = Object.assign(target || {}, { + type: 'wkb', + geometryType: 1, + dimensions: 2, + coordinates: 'xy', + littleEndian: true, + byteOffset: 0 + } as WKBHeader); + + // Check endianness of data + wkbHeader.littleEndian = dataView.getUint8(wkbHeader.byteOffset) === 1; + wkbHeader.byteOffset++; + + // 4-digit code representing dimension and type of geometry + const geometryCode = dataView.getUint32(wkbHeader.byteOffset, wkbHeader.littleEndian); + wkbHeader.byteOffset += 4; + + wkbHeader.geometryType = (geometryCode & 0x7) as 1 | 2 | 3 | 4 | 5 | 6 | 7; + + // Check if iso-wkb variant: iso-wkb adds 1000, 2000 or 3000 to the geometry code + const isoType = (geometryCode - wkbHeader.geometryType) / 1000; + switch (isoType) { + case 0: + break; + case 1: + wkbHeader.type = 'iso-wkb'; + wkbHeader.dimensions = 3; + wkbHeader.coordinates = 'xyz'; + break; + case 2: + wkbHeader.type = 'iso-wkb'; + wkbHeader.dimensions = 3; + wkbHeader.coordinates = 'xym'; + break; + case 3: + wkbHeader.type = 'iso-wkb'; + wkbHeader.dimensions = 4; + wkbHeader.coordinates = 'xyzm'; + break; + default: + throw new Error(`WKB: Unsupported iso-wkb type: ${isoType}`); + } + + // Check if EWKB variant. Uses bitmasks for Z&M dimensions as well as optional SRID field + const ewkbZ = geometryCode & EWKB_FLAG_Z; + const ewkbM = geometryCode & EWKB_FLAG_M; + const ewkbSRID = geometryCode & EWKB_FLAG_SRID; + + if (ewkbZ && ewkbM) { + wkbHeader.type = 'ewkb'; + wkbHeader.dimensions = 4; + wkbHeader.coordinates = 'xyzm'; + } else if (ewkbZ) { + wkbHeader.type = 'ewkb'; + wkbHeader.dimensions = 3; + wkbHeader.coordinates = 'xyz'; + } else if (ewkbM) { + wkbHeader.type = 'ewkb'; + wkbHeader.dimensions = 3; + wkbHeader.coordinates = 'xym'; + } + + // If SRID present read four more bytes + if (ewkbSRID) { + wkbHeader.type = 'ewkb'; + // 4-digit code representing dimension and type of geometry + wkbHeader.srid = dataView.getUint32(wkbHeader.byteOffset, wkbHeader.littleEndian); + wkbHeader.byteOffset += 4; + } + + return wkbHeader; +} diff --git a/modules/wkt/src/lib/parse-wkb.ts b/modules/wkt/src/lib/parse-wkb.ts index 32b0ec8676..7410f25db4 100644 --- a/modules/wkt/src/lib/parse-wkb.ts +++ b/modules/wkt/src/lib/parse-wkb.ts @@ -1,59 +1,71 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type { TypedArray, BinaryGeometry, BinaryPointGeometry, BinaryLineGeometry, - BinaryPolygonGeometry + BinaryPolygonGeometry, + Geometry } from '@loaders.gl/schema'; +import {binaryToGeometry} from '@loaders.gl/gis'; +import type {WKBLoaderOptions} from '../wkb-loader'; + +import {parseWKBHeader, WKBGeometryType} from './parse-wkb-header'; + +export function parseWKB( + arrayBuffer: ArrayBuffer, + options?: WKBLoaderOptions +): BinaryGeometry | Geometry { + const binaryGeometry = parseWKBToBinary(arrayBuffer, options); + const shape = options?.wkb?.shape || 'binary-geometry'; + switch (shape) { + case 'binary-geometry': + return binaryGeometry; + case 'geometry': + return binaryToGeometry(binaryGeometry); + default: + throw new Error(shape); + } +} -const NUM_DIMENSIONS = { - 0: 2, // 2D - 1: 3, // 3D (Z) - 2: 3, // 3D (M) - 3: 4 // 4D (ZM) -}; - -export default function parseWKB(arrayBuffer: ArrayBuffer): BinaryGeometry { - const view = new DataView(arrayBuffer); - let offset = 0; - - // Check endianness of data - const littleEndian = view.getUint8(offset) === 1; - offset++; +export function parseWKBToBinary( + arrayBuffer: ArrayBuffer, + options?: WKBLoaderOptions +): BinaryGeometry { + const dataView = new DataView(arrayBuffer); - // 4-digit code representing dimension and type of geometry - const geometryCode = view.getUint32(offset, littleEndian); - offset += 4; + const wkbHeader = parseWKBHeader(dataView); - const geometryType = geometryCode % 1000; - const type = ((geometryCode - geometryType) / 1000) as 0 | 1 | 2 | 3; - const dimension = NUM_DIMENSIONS[type]; + const {geometryType, dimensions, littleEndian} = wkbHeader; + const offset = wkbHeader.byteOffset; switch (geometryType) { - case 1: - const point = parsePoint(view, offset, dimension, littleEndian); + case WKBGeometryType.Point: + const point = parsePoint(dataView, offset, dimensions, littleEndian); return point.geometry; - case 2: - const line = parseLineString(view, offset, dimension, littleEndian); + case WKBGeometryType.LineString: + const line = parseLineString(dataView, offset, dimensions, littleEndian); return line.geometry; - case 3: - const polygon = parsePolygon(view, offset, dimension, littleEndian); + case WKBGeometryType.Polygon: + const polygon = parsePolygon(dataView, offset, dimensions, littleEndian); return polygon.geometry; - case 4: - const multiPoint = parseMultiPoint(view, offset, dimension, littleEndian); + case WKBGeometryType.MultiPoint: + const multiPoint = parseMultiPoint(dataView, offset, dimensions, littleEndian); multiPoint.type = 'Point'; return multiPoint; - case 5: - const multiLine = parseMultiLineString(view, offset, dimension, littleEndian); + case WKBGeometryType.MultiLineString: + const multiLine = parseMultiLineString(dataView, offset, dimensions, littleEndian); multiLine.type = 'LineString'; return multiLine; - case 6: - const multiPolygon = parseMultiPolygon(view, offset, dimension, littleEndian); + case WKBGeometryType.MultiPolygon: + const multiPolygon = parseMultiPolygon(dataView, offset, dimensions, littleEndian); multiPolygon.type = 'Polygon'; return multiPolygon; - // case 7: + // case WKBGeometryType.GeometryCollection: // TODO: handle GeometryCollections - // return parseGeometryCollection(view, offset, dimension, littleEndian); + // return parseGeometryCollection(dataView, offset, dimensions, littleEndian); default: throw new Error(`WKB: Unsupported geometry type: ${geometryType}`); } @@ -61,14 +73,14 @@ export default function parseWKB(arrayBuffer: ArrayBuffer): BinaryGeometry { // Primitives; parse point and linear ring function parsePoint( - view: DataView, + dataView: DataView, offset: number, dimension: number, littleEndian: boolean ): {geometry: BinaryPointGeometry; offset: number} { const positions = new Float64Array(dimension); for (let i = 0; i < dimension; i++) { - positions[i] = view.getFloat64(offset, littleEndian); + positions[i] = dataView.getFloat64(offset, littleEndian); offset += 8; } @@ -79,18 +91,18 @@ function parsePoint( } function parseLineString( - view: DataView, + dataView: DataView, offset: number, dimension: number, littleEndian: boolean ): {geometry: BinaryLineGeometry; offset: number} { - const nPoints = view.getUint32(offset, littleEndian); + const nPoints = dataView.getUint32(offset, littleEndian); offset += 4; // Instantiate array const positions = new Float64Array(nPoints * dimension); for (let i = 0; i < nPoints * dimension; i++) { - positions[i] = view.getFloat64(offset, littleEndian); + positions[i] = dataView.getFloat64(offset, littleEndian); offset += 8; } @@ -113,17 +125,17 @@ function parseLineString( const cumulativeSum = (sum: number) => (value: number) => (sum += value); function parsePolygon( - view: DataView, + dataView: DataView, offset: number, dimension: number, littleEndian: boolean ): {geometry: BinaryPolygonGeometry; offset: number} { - const nRings = view.getUint32(offset, littleEndian); + const nRings = dataView.getUint32(offset, littleEndian); offset += 4; const rings: TypedArray[] = []; for (let i = 0; i < nRings; i++) { - const parsed = parseLineString(view, offset, dimension, littleEndian); + const parsed = parseLineString(dataView, offset, dimension, littleEndian); const {positions} = parsed.geometry; offset = parsed.offset; rings.push(positions.value); @@ -152,28 +164,28 @@ function parsePolygon( } function parseMultiPoint( - view: DataView, + dataView: DataView, offset: number, dimension: number, littleEndian: boolean ): BinaryPointGeometry { - const nPoints = view.getUint32(offset, littleEndian); + const nPoints = dataView.getUint32(offset, littleEndian); offset += 4; const binaryPointGeometries: BinaryPointGeometry[] = []; for (let i = 0; i < nPoints; i++) { // Byte order for point - const littleEndianPoint = view.getUint8(offset) === 1; + const littleEndianPoint = dataView.getUint8(offset) === 1; offset++; // Assert point type - if (view.getUint32(offset, littleEndianPoint) % 1000 !== 1) { + if (dataView.getUint32(offset, littleEndianPoint) % 1000 !== 1) { throw new Error('WKB: Inner geometries of MultiPoint not of type Point'); } offset += 4; - const parsed = parsePoint(view, offset, dimension, littleEndianPoint); + const parsed = parsePoint(dataView, offset, dimension, littleEndianPoint); offset = parsed.offset; binaryPointGeometries.push(parsed.geometry); } @@ -182,27 +194,27 @@ function parseMultiPoint( } function parseMultiLineString( - view: DataView, + dataView: DataView, offset: number, dimension: number, littleEndian: boolean ): BinaryLineGeometry { - const nLines = view.getUint32(offset, littleEndian); + const nLines = dataView.getUint32(offset, littleEndian); offset += 4; const binaryLineGeometries: BinaryLineGeometry[] = []; for (let i = 0; i < nLines; i++) { // Byte order for line - const littleEndianLine = view.getUint8(offset) === 1; + const littleEndianLine = dataView.getUint8(offset) === 1; offset++; // Assert type LineString - if (view.getUint32(offset, littleEndianLine) % 1000 !== 2) { + if (dataView.getUint32(offset, littleEndianLine) % 1000 !== 2) { throw new Error('WKB: Inner geometries of MultiLineString not of type LineString'); } offset += 4; - const parsed = parseLineString(view, offset, dimension, littleEndianLine); + const parsed = parseLineString(dataView, offset, dimension, littleEndianLine); offset = parsed.offset; binaryLineGeometries.push(parsed.geometry); } @@ -211,27 +223,27 @@ function parseMultiLineString( } function parseMultiPolygon( - view: DataView, + dataView: DataView, offset: number, dimension: number, littleEndian: boolean ): BinaryPolygonGeometry { - const nPolygons = view.getUint32(offset, littleEndian); + const nPolygons = dataView.getUint32(offset, littleEndian); offset += 4; const binaryPolygonGeometries: BinaryPolygonGeometry[] = []; for (let i = 0; i < nPolygons; i++) { // Byte order for polygon - const littleEndianPolygon = view.getUint8(offset) === 1; + const littleEndianPolygon = dataView.getUint8(offset) === 1; offset++; // Assert type Polygon - if (view.getUint32(offset, littleEndianPolygon) % 1000 !== 3) { + if (dataView.getUint32(offset, littleEndianPolygon) % 1000 !== 3) { throw new Error('WKB: Inner geometries of MultiPolygon not of type Polygon'); } offset += 4; - const parsed = parsePolygon(view, offset, dimension, littleEndianPolygon); + const parsed = parsePolygon(dataView, offset, dimension, littleEndianPolygon); offset = parsed.offset; binaryPolygonGeometries.push(parsed.geometry); } diff --git a/modules/wkt/src/lib/parse-wkt-crs.ts b/modules/wkt/src/lib/parse-wkt-crs.ts new file mode 100644 index 0000000000..7ff3460dc8 --- /dev/null +++ b/modules/wkt/src/lib/parse-wkt-crs.ts @@ -0,0 +1,148 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// parse-wkt-crs was forked from https://github.com/DanielJDufour/wkt-crs under Creative Commons CC0 1.0 license. + +/* eslint-disable no-console */ // TODO switch to options.log + +export type ParseWKTCRSOptions = { + sort?: boolean; + keywords?: string[]; + raw?: boolean; + debug?: boolean; +}; + +export type WKTCRS = any; + +/** + * + * @param wkt + * @param options + * @returns + */ +export function parseWKTCRS(wkt: string, options?: ParseWKTCRSOptions): WKTCRS { + if (options?.debug) { + console.log('[wktcrs] parse starting with\n', wkt); + } + + // move all keywords into first array item slot + // from PARAM[12345, 67890] to ["PARAM", 12345, 67890] + wkt = wkt.replace(/[A-Z][A-Z\d_]+\[/gi, (match) => `["${match.substr(0, match.length - 1)}",`); + + // wrap variables in strings + // from [...,NORTH] to [...,"NORTH"] + wkt = wkt.replace(/, ?([A-Z][A-Z\d_]+[,\]])/gi, (match, p1) => { + const varname = p1.substr(0, p1.length - 1); + return ',' + `"${options?.raw ? 'raw:' : ''}${varname}"${p1[p1.length - 1]}`; + }); + + if (options?.raw) { + // replace all numbers with strings + wkt = wkt.replace(/, {0,2}(-?[\.\d]+)(?=,|\])/g, function (match, p1) { + return ',' + `"${options?.raw ? 'raw:' : ''}${p1}"`; + }); + } + + // str should now be valid JSON + if (options?.debug) { + console.log(`[wktcrs] json'd wkt: '${wkt}'`); + } + + let data; + try { + data = JSON.parse(wkt); + } catch (error) { + console.error(`[wktcrs] failed to parse '${wkt}'`); + throw error; + } + + if (options?.debug) { + console.log(`[wktcrs] json parsed: '${wkt}'`); + } + + function process(data, parent) { + const kw = data[0]; + + // after removing the first element with .shift() + // data is now just an array of attributes + + data.forEach(function (it) { + if (Array.isArray(it)) { + process(it, data); + } + }); + + const kwarr = `MULTIPLE_${kw}`; + + if (kwarr in parent) { + parent[kwarr].push(data); + } else if (kw in parent) { + parent[kwarr] = [parent[kw], data]; + delete parent[kw]; + } else { + parent[kw] = data; + } + return parent; + } + + const result = process(data, [data]); + if (options?.debug) { + console.log('[wktcrs] parse returning', result); + } + + if (options?.sort) { + sort(result, options); + } + + return result; +} + +function sort(data: string[], options?: {keywords?: string[]}) { + const keys = Object.keys(data).filter((k) => !/\d+/.test(k)); + + const keywords: string[] = options?.keywords || []; + if (!options?.keywords) { + // try to find multiples + const counts = {}; + if (Array.isArray(data)) { + data.forEach((it) => { + if (Array.isArray(it) && it.length >= 2 && typeof it[1] === 'string') { + const k = it[0]; + if (!counts[k]) counts[k] = 0; + counts[k]++; + } + }); + for (const k in counts) { + if (counts[k] > 0) keywords.push(k); + } + } + } + + keys.forEach((key) => { + data[key] = sort(data[key]); + }); + + keywords.forEach((key) => { + const indices: number[] = []; + const params: string[] = []; + + data.forEach((item, i) => { + if (Array.isArray(item) && item[0] === key) { + indices.push(i); + params.push(item); + } + }); + + params.sort((a, b) => { + a = a[1].toString(); + b = b[1].toString(); + return a < b ? -1 : a > b ? 1 : 0; + }); + + // replace in order + params.forEach((param, i) => { + data[indices[i]] = param; + }); + }); + + return data; +} diff --git a/modules/wkt/src/lib/parse-wkt.ts b/modules/wkt/src/lib/parse-wkt.ts index da7700bcf6..a37b9f65ee 100644 --- a/modules/wkt/src/lib/parse-wkt.ts +++ b/modules/wkt/src/lib/parse-wkt.ts @@ -1,5 +1,9 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Fork of https://github.com/mapbox/wellknown under ISC license (MIT/BSD-2-clause equivalent) +import {Geometry} from '@loaders.gl/schema'; + /* eslint-disable */ // @ts-nocheck @@ -7,216 +11,316 @@ const numberRegexp = /[-+]?([0-9]*\.[0-9]+|[0-9]+)([eE][-+]?[0-9]+)?/; // Matches sequences like '100 100' or '100 100 100'. const tuples = new RegExp('^' + numberRegexp.source + '(\\s' + numberRegexp.source + '){1,}'); +export const WKT_MAGIC_STRINGS = [ + 'POINT(', + 'LINESTRING(', + 'POLYGON(', + 'MULTIPOINT(', + 'MULTILINESTRING(', + 'MULTIPOLYGON(', + 'GEOMETRYCOLLECTION(' + // We only support this "geojson" subset of the OGC simple features standard +]; + +export type ParseWKTOptions = { + wkt?: { + /** Whether to add any CRS, if found, as undocumented CRS property on the return geometry */ + crs?: boolean; + }; +}; + +/** + * Check if a string is WKT. + * @param input A potential WKT geometry string + * @return `true` if input appears to be a WKT geometry string, `false` otherwise + + * @note We only support the "geojson" subset of the OGC simple features standard + * @todo Does not handle leading spaces which appear to be permitted per the spec: + * "A WKT string contains no white space outside of double quotes. + * However padding with white space to improve human readability is permitted; + * the examples of WKT that are included in this document have + * spaces and line feeds inserted to improve clarity. Any padding is stripped out or ignored by parsers." + */ +export function isWKT(input: string): boolean { + return WKT_MAGIC_STRINGS.some((magicString) => input.startsWith(magicString)); +} + /** * Parse WKT and return GeoJSON. + * @param input A WKT geometry string + * @return A GeoJSON geometry object * - * @param {string} _ A WKT geometry - * @return {?Object} A GeoJSON geometry object + * @note We only support the "geojson" subset of the OGC simple features standard **/ -export default function parseWKT(input) { +export function parseWKT(input: string, options?: ParseWKTOptions): Geometry { + // TODO handle options.wkt.shape + return parseWKTToGeometry(input, options)!; +} + +/** State of parser, passed around between parser functions */ +type ParseWKTState = { + parts: string[]; + _: string | undefined; + i: number; +}; + +/** Parse into GeoJSON geometry */ +function parseWKTToGeometry(input: string, options?: ParseWKTOptions): Geometry | null { const parts = input.split(';'); let _ = parts.pop(); const srid = (parts.shift() || '').split('=').pop(); - let i = 0; - - function $(re) { - const match = _.substring(i).match(re); - if (!match) return null; - else { - i += match[0].length; - return match[0]; - } - } + const state: ParseWKTState = {parts, _, i: 0}; - function crs(obj) { - if (obj && srid.match(/\d+/)) { - obj.crs = { - type: 'name', - properties: { - name: 'urn:ogc:def:crs:EPSG::' + srid - } - }; - } + const geometry = parseGeometry(state); - return obj; - } - - function white() { - $(/^\s*/); - } - - function multicoords() { - white(); - let depth = 0; - const rings = []; - const stack = [rings]; - let pointer = rings; - let elem; - - while ((elem = $(/^(\()/) || $(/^(\))/) || $(/^(,)/) || $(tuples))) { - if (elem === '(') { - stack.push(pointer); - pointer = []; - stack[stack.length - 1].push(pointer); - depth++; - } else if (elem === ')') { - // For the case: Polygon(), ... - if (pointer.length === 0) return null; - - // @ts-ignore - pointer = stack.pop(); - // the stack was empty, input was malformed - if (!pointer) return null; - depth--; - if (depth === 0) break; - } else if (elem === ',') { - pointer = []; - stack[stack.length - 1].push(pointer); - } else if (!elem.split(/\s/g).some(isNaN)) { - Array.prototype.push.apply(pointer, elem.split(/\s/g).map(parseFloat)); - } else { - return null; - } - white(); - } + return options?.wkt?.crs ? addCRS(geometry, srid) : geometry; +} - if (depth !== 0) return null; +function parseGeometry(state: ParseWKTState): Geometry | null { + return ( + parsePoint(state) || + parseLineString(state) || + parsePolygon(state) || + parseMultiPoint(state) || + parseMultiLineString(state) || + parseMultiPolygon(state) || + parseGeometryCollection(state) + ); +} - return rings; +/** Adds a coordinate reference system as an undocumented */ +function addCRS(obj: Geometry | null, srid?: string): Geometry | null { + if (obj && srid?.match(/\d+/)) { + const crs = { + type: 'name', + properties: { + name: 'urn:ogc:def:crs:EPSG::' + srid + } + }; + // @ts-expect-error we assign an undocumented property on the geometry + obj.crs = crs; } - function coords() { - const list = []; - let item; - let pt; - while ((pt = $(tuples) || $(/^(,)/))) { - if (pt === ',') { - list.push(item); - item = []; - } else if (!pt.split(/\s/g).some(isNaN)) { - if (!item) item = []; - Array.prototype.push.apply(item, pt.split(/\s/g).map(parseFloat)); - } - white(); - } + return obj; +} - if (item) list.push(item); - else return null; +// GEOMETRIES - return list.length ? list : null; +function parsePoint(state: ParseWKTState): Geometry | null { + if (!$(/^(POINT(\sz)?)/i, state)) { + return null; + } + white(state); + if (!$(/^(\()/, state)) { + return null; + } + const c = coords(state); + if (!c) { + return null; } + white(state); + if (!$(/^(\))/, state)) { + return null; + } + return { + type: 'Point', + coordinates: c[0] + }; +} - function point() { - if (!$(/^(point(\sz)?)/i)) return null; - white(); - if (!$(/^(\()/)) return null; - const c = coords(); - if (!c) return null; - white(); - if (!$(/^(\))/)) return null; - return { - type: 'Point', - coordinates: c[0] - }; +function parseMultiPoint(state: ParseWKTState): Geometry | null { + if (!$(/^(MULTIPOINT)/i, state)) { + return null; + } + white(state); + const newCoordsFormat = state._?.substring(state._?.indexOf('(') + 1, state._.length - 1) + .replace(/\(/g, '') + .replace(/\)/g, ''); + state._ = 'MULTIPOINT (' + newCoordsFormat + ')'; + const c = multicoords(state); + if (!c) { + return null; } + white(state); + return { + type: 'MultiPoint', + coordinates: c + }; +} - function multipoint() { - if (!$(/^(multipoint)/i)) return null; - white(); - const newCoordsFormat = _.substring(_.indexOf('(') + 1, _.length - 1) - .replace(/\(/g, '') - .replace(/\)/g, ''); - _ = 'MULTIPOINT (' + newCoordsFormat + ')'; - const c = multicoords(); - if (!c) return null; - white(); - return { - type: 'MultiPoint', - coordinates: c - }; +function parseLineString(state: ParseWKTState): Geometry | null { + if (!$(/^(LINESTRING(\sz)?)/i, state)) { + return null; + } + white(state); + if (!$(/^(\()/, state)) { + return null; } + const c = coords(state); + if (!c) { + return null; + } + if (!$(/^(\))/, state)) { + return null; + } + return { + type: 'LineString', + coordinates: c + }; +} - function multilinestring() { - if (!$(/^(multilinestring)/i)) return null; - white(); - const c = multicoords(); - if (!c) return null; - white(); - return { - type: 'MultiLineString', - coordinates: c - }; +function parseMultiLineString(state: ParseWKTState): Geometry | null { + if (!$(/^(MULTILINESTRING)/i, state)) return null; + white(state); + const c = multicoords(state); + if (!c) { + return null; } + white(state); + return { + // @ts-ignore + type: 'MultiLineString', + // @ts-expect-error + coordinates: c + }; +} - function linestring() { - if (!$(/^(linestring(\sz)?)/i)) return null; - white(); - if (!$(/^(\()/)) return null; - const c = coords(); - if (!c) return null; - if (!$(/^(\))/)) return null; - return { - type: 'LineString', - coordinates: c - }; +function parsePolygon(state: ParseWKTState): Geometry | null { + if (!$(/^(POLYGON(\sz)?)/i, state)) { + return null; } + white(state); + const c = multicoords(state); + if (!c) { + return null; + } + return { + // @ts-ignore + type: 'Polygon', + // @ts-expect-error + coordinates: c + }; +} - function polygon() { - if (!$(/^(polygon(\sz)?)/i)) return null; - white(); - const c = multicoords(); - if (!c) return null; - return { - type: 'Polygon', - coordinates: c - }; +function parseMultiPolygon(state: ParseWKTState): Geometry | null { + if (!$(/^(MULTIPOLYGON)/i, state)) { + return null; + } + white(state); + const c = multicoords(state); + if (!c) { + return null; } + return { + type: 'MultiPolygon', + // @ts-expect-error + coordinates: c + }; +} - function multipolygon() { - if (!$(/^(multipolygon)/i)) return null; - white(); - const c = multicoords(); - if (!c) return null; - return { - type: 'MultiPolygon', - coordinates: c - }; +function parseGeometryCollection(state: ParseWKTState): Geometry | null { + const geometries: Geometry[] = []; + let geometry: Geometry | null; + + if (!$(/^(GEOMETRYCOLLECTION)/i, state)) { + return null; } + white(state); - function geometrycollection() { - const geometries = []; - let geometry; + if (!$(/^(\()/, state)) { + return null; + } + while ((geometry = parseGeometry(state))) { + geometries.push(geometry); + white(state); + $(/^(,)/, state); + white(state); + } + if (!$(/^(\))/, state)) { + return null; + } - if (!$(/^(geometrycollection)/i)) return null; - white(); + return { + type: 'GeometryCollection', + geometries: geometries + }; +} + +// COORDINATES + +function multicoords(state: ParseWKTState): number[][] | null { + white(state); + let depth = 0; + const rings: number[][] = []; + const stack = [rings]; + let pointer: any = rings; + let elem; + + while ((elem = $(/^(\()/, state) || $(/^(\))/, state) || $(/^(,)/, state) || $(tuples, state))) { + if (elem === '(') { + stack.push(pointer); + pointer = []; + stack[stack.length - 1].push(pointer); + depth++; + } else if (elem === ')') { + // For the case: Polygon(), ... + if (pointer.length === 0) return null; - if (!$(/^(\()/)) return null; - while ((geometry = root())) { - geometries.push(geometry); - white(); - $(/^(,)/); - white(); + // @ts-ignore + pointer = stack.pop(); + // the stack was empty, input was malformed + if (!pointer) return null; + depth--; + if (depth === 0) break; + } else if (elem === ',') { + pointer = []; + stack[stack.length - 1].push(pointer); + } else if (!elem.split(/\s/g).some(isNaN)) { + Array.prototype.push.apply(pointer, elem.split(/\s/g).map(parseFloat)); + } else { + return null; } - if (!$(/^(\))/)) return null; + white(state); + } - return { - type: 'GeometryCollection', - geometries: geometries - }; + if (depth !== 0) return null; + + return rings; +} + +function coords(state: ParseWKTState): number[][] | null { + const list: number[][] = []; + let item: any; + let pt; + while ((pt = $(tuples, state) || $(/^(,)/, state))) { + if (pt === ',') { + list.push(item); + item = []; + } else if (!pt.split(/\s/g).some(isNaN)) { + if (!item) item = []; + Array.prototype.push.apply(item, pt.split(/\s/g).map(parseFloat)); + } + white(state); } - function root() { - return ( - point() || - linestring() || - polygon() || - multipoint() || - multilinestring() || - multipolygon() || - geometrycollection() - ); + if (item) list.push(item); + else return null; + + return list.length ? list : null; +} + +// HELPERS + +function $(regexp: RegExp, state: ParseWKTState) { + const match = state._?.substring(state.i).match(regexp); + if (!match) return null; + else { + state.i += match[0].length; + return match[0]; } +} - return crs(root()); +function white(state: ParseWKTState) { + $(/^\s*/, state); } diff --git a/modules/wkt/src/lib/utils/base64-encoder.ts b/modules/wkt/src/lib/utils/base64-encoder.ts new file mode 100644 index 0000000000..18f859051e --- /dev/null +++ b/modules/wkt/src/lib/utils/base64-encoder.ts @@ -0,0 +1,153 @@ +/* +const binary_to_b64_map = [ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', + 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', + 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', + '2', '3', '4', '5', '6', '7', '8', '9', '+', + '/', '=' +]; +const b64_to_binary_map = { + '0': 52, '1': 53, '2': 54, '3': 55, '4': 56, '5': 57, '6': 58, '7': 59, '8': 60, '9': 61, + A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8, J: 9, K: 10, L: 11, M: 12, N: 13, O: 14, + P: 15, Q: 16, R: 17, S: 18, T: 19, U: 20, V: 21, W: 22, X: 23, Y: 24, Z: 25, a: 26, b: 27, + c: 28, d: 29, e: 30, f: 31, g: 32, h: 33, i: 34, j: 35, k: 36, l: 37, m: 38, n: 39, o: 40, + p: 41, q: 42, r: 43, s: 44, t: 45, u: 46, v: 47, w: 48, x: 49, y: 50, z: 51, '+': 62, '/': 63, + '=': 64 +}; + +*/ + +export class Base64Encoder { + getDecodedLength(array: Uint8Array): number { + return Math.ceil(array.byteLength / 4) * 3; + } + + decode(array: Uint8Array, target?: ArrayBuffer) {} // eslint-disable-line +} + +/* + +//generates an array iterator that returns 3 elements at a time. Use to loop through the Uint8Array Array Buffer +// to be converted to Base64. (binary array buffer) 8bits * 3 = 6bits * 4 (base64 representation) +const generateTripleIterator = (arr) => { + return { + *[Symbol.iterator]() { + for(let n = 0; n < arr.length; n+=3) { + let result = []; + result.push(arr[n]); + + if(n+1 < arr.length) + result.push(arr[n+1]); + if(n+2 < arr.length) + result.push(arr[n+2]); + + yield result; + } + } + }; +}; + +//generates an array iterator that returns 4 elements at a time. Use to loop through +// Base64 string because Base64 string is multiples of 4 characters. +const generateQuadrupleIterator = (arr) => { + return { + *[Symbol.iterator]() { + for(let n = 0; n < arr.length; n+=4) { + yield [...arr.slice(n, n+4)]; + } + } + }; +}; + +// Converts a triple of 8 bits into a quadruple of 6 bits. use to convert binary to base64 representation +const tripleConvert = (first, second, third) => { + let [] = triple; + let binary = null, a = null, b = null, c = null, d = null; + if (triple.length === 1) { + binary = (first << 4); + a = ((binary & 4032) >>> 6); + b = (binary & 63); + c = 64; + d = 64; + } else if (triple.length === 2) { + binary = ((first << 10) | (second << 2)); + a = ((binary & 258048) >>> 12); + b = ((binary & 4032) >>> 6); + c = (binary & 63); + d = 64; + } else { + binary = ((first << 16) | (second << 8) | third); + a = ((binary & 16515072) >>> 18); + b = ((binary & 258048) >>> 12); + c = ((binary & 4032) >>> 6); + d = (binary & 63); + } + + return [a, b, c, d]; +}; + +// Converts a quadruple of 6 bits into a triple of 8 bits. use to convert base64 representation into binary +const quadrupleConvert = (quadruple) => { + let [a, b, c, d] = quadruple; + let binary = null, first = null, second = null, third = null; + + if(c === 64 && d === 64) { + //two padding + binary = ((a << 6) | b); + first = (binary >> 4); //shift off 4 bits, 2 bits per padding + } else if(d === 64) { + //one padding + binary = ((a << 12) | (b << 6) | c ); + binary = (binary >> 2); //shift off 2 bits + first = binary >> 8; + second = ((binary << 24) >>> 24); + } else { + //no padding + binary = ((a << 18) | (b << 12) | (c << 6) | d ); + first = (binary >>> 16); + second = ((binary << 16) >>> 24); + third = ((binary << 24) >>> 24); + } + + return [first, second, third]; +}; + +// Convert 8Bits Array Buffer to Base64 string +export const ab2b64 = (buffer) => { + const b64strArray = []; + const view = new Uint8Array(buffer); + let iterator = generateTripleIterator(view); + for(let triple of iterator) { + b64strArray.push(...tripleConvert(triple)); + } + return b64strArray.map(b64CharCodePoint => binary_to_b64_map[b64CharCodePoint]).join(""); +}; + +// Convert Base64 String to 8Bits Array Buffer +export const b642ab = (b64str) => { + let buffer_length = (b64str.length / 4) * 3; + if(b64str.slice(-2) === '==') { + buffer_length -= 2; + } else if(b64str.slice(-1) === '=') { + buffer_length -= 1; + } + + let buffer = new ArrayBuffer(buffer_length); + const view = new Uint8Array(buffer); + let iterator = generateQuadrupleIterator(b64str.split("").map(b64char => b64_to_binary_map[b64char])); + let byteIndex = 0; + for(let quadruple of iterator) { + quadrupleConvert(quadruple).forEach(byte => { + if(byte != null) { + view[byteIndex] = byte; + byteIndex++; + } + }); + } + return buffer; +}; + +*/ diff --git a/modules/wkt/src/lib/utils/binary-reader.ts b/modules/wkt/src/lib/utils/binary-reader.ts new file mode 100644 index 0000000000..94d1a23582 --- /dev/null +++ b/modules/wkt/src/lib/utils/binary-reader.ts @@ -0,0 +1,72 @@ +/** A DataView that tracks byte offset when reading. */ +export class BinaryReader { + arrayBuffer: ArrayBuffer; + dataView: DataView; + byteOffset: number; + littleEndian: boolean; + + constructor(arrayBuffer: ArrayBuffer, isBigEndian: boolean = false) { + this.arrayBuffer = arrayBuffer; + this.dataView = new DataView(arrayBuffer); + this.byteOffset = 0; + this.littleEndian = !isBigEndian; + } + + readUInt8() { + const value = this.dataView.getUint8(this.byteOffset); + this.byteOffset += 1; + return value; + } + readUInt16() { + const value = this.dataView.getUint16(this.byteOffset, this.littleEndian); + this.byteOffset += 2; + return value; + } + readUInt32() { + const value = this.dataView.getUint32(this.byteOffset, this.littleEndian); + this.byteOffset += 4; + return value; + } + readInt8() { + const value = this.dataView.getInt8(this.byteOffset); + this.byteOffset += 1; + return value; + } + readInt16() { + const value = this.dataView.getInt16(this.byteOffset, this.littleEndian); + this.byteOffset += 2; + return value; + } + readInt32() { + const value = this.dataView.getInt32(this.byteOffset, this.littleEndian); + this.byteOffset += 4; + return value; + } + readFloat() { + const value = this.dataView.getFloat32(this.byteOffset, this.littleEndian); + this.byteOffset += 4; + return value; + } + readDouble() { + const value = this.dataView.getFloat64(this.byteOffset, this.littleEndian); + this.byteOffset += 8; + return value; + } + + readVarInt() { + let result = 0; + let bytesRead = 0; + + let nextByte; + do { + // TODO - this needs to be accessed via data view? + nextByte = this.dataView.getUint8(this.byteOffset + bytesRead); + result += (nextByte & 0x7f) << (7 * bytesRead); + bytesRead++; + } while (nextByte >= 0x80); + + this.byteOffset += bytesRead; + + return result; + } +} diff --git a/modules/wkt/src/lib/utils/binary-writer.ts b/modules/wkt/src/lib/utils/binary-writer.ts index 19683995a0..72d944f30d 100644 --- a/modules/wkt/src/lib/utils/binary-writer.ts +++ b/modules/wkt/src/lib/utils/binary-writer.ts @@ -1,10 +1,11 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/cschwarz/wkx under MIT license, Copyright (c) 2013 Christian Schwarz const LE = true; const BE = false; -export default class BinaryWriter { +export class BinaryWriter { arrayBuffer: ArrayBuffer; dataView: DataView; byteOffset: number = 0; diff --git a/modules/wkt/src/lib/utils/hex-encoder.ts b/modules/wkt/src/lib/utils/hex-encoder.ts new file mode 100644 index 0000000000..503508569c --- /dev/null +++ b/modules/wkt/src/lib/utils/hex-encoder.ts @@ -0,0 +1,59 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +/** + * Simple helper to decode and encode "hex encoded" binary buffers + * without first converting to string. + */ +export class HexEncoder { + /** Get length in bytes required to store encoded data */ + getEncodedLength(array: Uint8Array): number { + return array.byteLength * 2; + } + + /** Get length in bytes required to store decoded data */ + getDecodedLength(array: Uint8Array): number { + return Math.ceil(array.byteLength / 2); + } + + /** Decode hexadecimal */ + decode(array: Uint8Array, result: Uint8Array): Uint8Array { + for (let i = 0; i < array.byteLength / 2; ++i) { + const halfByte1 = hexDecode(array[i]); + const halfByte2 = hexDecode(array[i + 1]); + result[i] = halfByte1 * 16 + halfByte2; + } + // Check if final half byte (is that legal?) + // if (array.byteLength % 2) { + // const halfByte1 = hexDecode(array[i]); + // } + return result; + } + + /** Encode hexadecimal */ + encode(array: Uint8Array, result: Uint8Array): Uint8Array { + for (let i = 0; i < array.byteLength; ++i) { + const byte = array[i]; + result[i * 2] = hexEncode(byte & 0x0f); + result[i * 2 + 1] = hexEncode(byte & 0xf0); + } + return result; + } +} + +function hexEncode(value: number): number { + if (value < 10) { + return value + 48; // ASCII of 0 + } + return value - 10 + 65; // ASCII of capital A +} + +function hexDecode(value: number): number { + if (value >= 65) { + return value - 65 + 10; // ASCII of A + } + if (value >= 97) { + return value - 97 + 10; // ASCII of a + } + return value - 48; // ASCII of 0 +} diff --git a/modules/wkt/src/lib/utils/hex-transcoder.ts b/modules/wkt/src/lib/utils/hex-transcoder.ts new file mode 100644 index 0000000000..5eeb82eca1 --- /dev/null +++ b/modules/wkt/src/lib/utils/hex-transcoder.ts @@ -0,0 +1,50 @@ +// Forked from https://github.com/jessetane/hex-transcoder under MIT license + +const alphabet = '0123456789abcdef'; +const encodeLookup: string[] = []; +const decodeLookup: number[] = []; + +for (let i = 0; i < 256; i++) { + encodeLookup[i] = alphabet[(i >> 4) & 0xf] + alphabet[i & 0xf]; + if (i < 16) { + if (i < 10) { + decodeLookup[0x30 + i] = i; + } else { + decodeLookup[0x61 - 10 + i] = i; + } + } +} + +/** + * Encode a Uint8Array to a hex string + * + * @param array Bytes to encode to string + * @return hex string + */ +export function encodeHex(array: Uint8Array): string { + const length = array.length; + let string = ''; + let i = 0; + while (i < length) { + string += encodeLookup[array[i++]]; + } + return string; +} + +/** + * Decodes a hex string to a Uint8Array + * + * @param string hex string to decode to Uint8Array + * @return Uint8Array + */ +export function decodeHex(string: string): Uint8Array { + const sizeof = string.length >> 1; + const length = sizeof << 1; + const array = new Uint8Array(sizeof); + let n = 0; + let i = 0; + while (i < length) { + array[n++] = (decodeLookup[string.charCodeAt(i++)] << 4) | decodeLookup[string.charCodeAt(i++)]; + } + return array; +} diff --git a/modules/wkt/src/twkb-loader.ts b/modules/wkt/src/twkb-loader.ts new file mode 100644 index 0000000000..45add0b394 --- /dev/null +++ b/modules/wkt/src/twkb-loader.ts @@ -0,0 +1,43 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; +import {BinaryGeometry, Geometry} from '@loaders.gl/schema'; +import {VERSION} from './lib/utils/version'; +import {parseTWKBGeometry, isTWKB} from './lib/parse-twkb'; + +export type WKBLoaderOptions = LoaderOptions & { + wkb?: { + shape: 'binary-geometry' | 'geometry'; + }; +}; + +/** + * Worker loader for WKB (Well-Known Binary) + */ +export const TWKBWorkerLoader: Loader = { + name: 'TWKB (Tiny Well-Known Binary)', + id: 'twkb', + module: 'wkt', + version: VERSION, + worker: true, + category: 'geometry', + extensions: ['twkb'], + mimeTypes: [], + // TODO can we define static, serializable tests, eg. some binary strings? + tests: [isTWKB], + options: { + wkb: { + shape: 'binary-geometry' + } + } +}; + +/** + * Loader for WKB (Well-Known Binary) + */ +export const TWKBLoader: LoaderWithParser = { + ...TWKBWorkerLoader, + parse: async (arrayBuffer: ArrayBuffer) => parseTWKBGeometry(arrayBuffer), + parseSync: parseTWKBGeometry +}; diff --git a/modules/wkt/src/twkb-writer.ts b/modules/wkt/src/twkb-writer.ts new file mode 100644 index 0000000000..2252eb7843 --- /dev/null +++ b/modules/wkt/src/twkb-writer.ts @@ -0,0 +1,26 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {Writer, WriterOptions} from '@loaders.gl/loader-utils'; +import {VERSION} from './lib/utils/version'; +import {encodeTWKB} from './lib/encode-twkb'; +import {BinaryGeometry} from '@loaders.gl/schema'; + +/** + * WKB exporter + */ +export const TWKBWriter: Writer = { + name: 'TWKB (Tiny Well Known Binary)', + id: 'twkb', + module: 'wkt', + version: VERSION, + extensions: ['twkb'], + // @ts-expect-error not implemented yet + encodeSync: async (data: BinaryGeometry, options) => encodeTWKB, + options: { + twkb: { + // hasZ: false, + // hasM: false + } + } +}; diff --git a/modules/wkt/src/wkb-loader.ts b/modules/wkt/src/wkb-loader.ts index 75ffaca396..9e05a4e2c5 100644 --- a/modules/wkt/src/wkb-loader.ts +++ b/modules/wkt/src/wkb-loader.ts @@ -1,11 +1,22 @@ -import type {Loader, LoaderWithParser} from '@loaders.gl/loader-utils'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; +import {BinaryGeometry, Geometry} from '@loaders.gl/schema'; import {VERSION} from './lib/utils/version'; -import parseWKB from './lib/parse-wkb'; +import {parseWKB} from './lib/parse-wkb'; +import {isWKB} from './lib/parse-wkb-header'; + +export type WKBLoaderOptions = LoaderOptions & { + wkb?: { + shape: 'binary-geometry' | 'geometry'; + }; +}; /** * Worker loader for WKB (Well-Known Binary) */ -export const WKBWorkerLoader = { +export const WKBWorkerLoader: Loader = { name: 'WKB', id: 'wkb', module: 'wkt', @@ -14,19 +25,20 @@ export const WKBWorkerLoader = { category: 'geometry', extensions: ['wkb'], mimeTypes: [], + // TODO can we define static, serializable tests, eg. some binary strings? + tests: [isWKB], options: { - wkb: {} + wkb: { + shape: 'binary-geometry' + } } }; /** * Loader for WKB (Well-Known Binary) */ -export const WKBLoader = { +export const WKBLoader: LoaderWithParser = { ...WKBWorkerLoader, parse: async (arrayBuffer: ArrayBuffer) => parseWKB(arrayBuffer), parseSync: parseWKB }; - -export const _typecheckWKBWorkerLoader: Loader = WKBWorkerLoader; -export const _typecheckWKBLoader: LoaderWithParser = WKBLoader; diff --git a/modules/wkt/src/wkb-writer.ts b/modules/wkt/src/wkb-writer.ts index d563f7cc97..9d6696f615 100644 --- a/modules/wkt/src/wkb-writer.ts +++ b/modules/wkt/src/wkb-writer.ts @@ -1,22 +1,43 @@ -import type {Writer} from '@loaders.gl/loader-utils'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; import {VERSION} from './lib/utils/version'; -import encodeWKB from './lib/encode-wkb'; +import {encodeWKB} from './lib/encode-wkb'; +import type {Geometry, Feature} from '@loaders.gl/schema'; + +export type WKBWriterOptions = WriterOptions & { + wkb?: { + /** Does the GeoJSON input have Z values? */ + hasZ?: boolean; + + /** Does the GeoJSON input have M values? */ + hasM?: boolean; + + /** Spatial reference for input GeoJSON */ + srid?: any; + }; +}; /** * WKB exporter */ -export const WKBWriter: Writer = { +export const WKBWriter: WriterWithEncoder = { name: 'WKB (Well Known Binary)', id: 'wkb', module: 'wkt', version: VERSION, extensions: ['wkb'], - // @ts-ignore - encodeSync: encodeWKB, options: { wkb: { hasZ: false, hasM: false } + }, + async encode(data: Geometry | Feature, options?: WriterOptions): Promise { + return encodeWKB(data, options?.wkb); + }, + encodeSync(data: Geometry | Feature, options?: WriterOptions): ArrayBuffer { + return encodeWKB(data, options?.wkb); } }; diff --git a/modules/wkt/src/wkt-crs-loader.ts b/modules/wkt/src/wkt-crs-loader.ts new file mode 100644 index 0000000000..2acd65d393 --- /dev/null +++ b/modules/wkt/src/wkt-crs-loader.ts @@ -0,0 +1,34 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; +import {VERSION} from './lib/utils/version'; +import type {ParseWKTCRSOptions, WKTCRS} from './lib/parse-wkt-crs'; +import {parseWKTCRS} from './lib/parse-wkt-crs'; + +export type WKTCRSLoaderOptions = LoaderOptions & { + 'wkt-crs'?: ParseWKTCRSOptions; +}; + +/** + * Well-Known text CRS loader + * @see OGC Standard: https://www.ogc.org/standards/wkt-crs + * @see Wikipedia Page: https://en.wikipedia.org/wiki/Well-known_text_representation_of_coordinate_reference_systems + */ +export const WKTCRSLoader: LoaderWithParser = { + name: 'WKT CRS (Well-Known Text Coordinate Reference System)', + id: 'wkt-crs', + module: 'wkt-crs', + version: VERSION, + worker: true, + extensions: [], + mimeTypes: ['text/plain'], + category: 'json', + text: true, + options: { + 'wkt-crs': {} + }, + parse: async (arrayBuffer, options) => + parseWKTCRS(new TextDecoder().decode(arrayBuffer), options?.['wkt-crs']), + parseTextSync: (string, options) => parseWKTCRS(string, options?.['wkt-crs']) +}; diff --git a/modules/wkt/src/wkt-crs-writer.ts b/modules/wkt/src/wkt-crs-writer.ts new file mode 100644 index 0000000000..f93cd1d516 --- /dev/null +++ b/modules/wkt/src/wkt-crs-writer.ts @@ -0,0 +1,38 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {WriterWithEncoder, WriterOptions} from '@loaders.gl/loader-utils'; +import {VERSION} from './lib/utils/version'; + +import type {WKTCRS} from './lib/parse-wkt-crs'; +import type {EncodeWKTCRSOptions} from './lib/encode-wkt-crs'; +import {encodeWKTCRS} from './lib/encode-wkt-crs'; + +export type WKTCRSWriterOptions = WriterOptions & { + 'wkt-crs'?: EncodeWKTCRSOptions; +}; + +/** + * Well-Known text CRS loader + * @see OGC Standard: https://www.ogc.org/standards/wkt-crs + * @see Wikipedia Page: https://en.wikipedia.org/wiki/Well-known_text_representation_of_coordinate_reference_systems + */ +export const WKTCRSWriter: WriterWithEncoder = { + name: 'WKT CRS (Well-Known Text Coordinate Reference System)', + id: 'wkt-crs', + module: 'wkt-crs', + version: VERSION, + worker: true, + extensions: [], + mimeTypes: ['text/plain'], + // category: 'json', + text: true, + options: { + 'wkt-crs': {} + }, + encode: async (wktcrs, options) => + new TextEncoder().encode(encodeWKTCRS(wktcrs, options?.['wkt-crs'])), + encodeSync: (wktcrs, options) => + new TextEncoder().encode(encodeWKTCRS(wktcrs, options?.['wkt-crs'])), + encodeTextSync: (wktcrs, options) => encodeWKTCRS(wktcrs, options?.['wkt-crs']) +}; diff --git a/modules/wkt/src/wkt-loader.ts b/modules/wkt/src/wkt-loader.ts index ad32359c3b..1675d88144 100644 --- a/modules/wkt/src/wkt-loader.ts +++ b/modules/wkt/src/wkt-loader.ts @@ -1,11 +1,24 @@ -import type {Loader, LoaderWithParser} from '@loaders.gl/loader-utils'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {Loader, LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; import {VERSION} from './lib/utils/version'; -import parseWKT from './lib/parse-wkt'; +import {parseWKT} from './lib/parse-wkt'; +import {Geometry} from '@loaders.gl/schema'; +import {isWKT, WKT_MAGIC_STRINGS} from './lib/parse-wkt'; + +export type WKTLoaderOptions = LoaderOptions & { + /** Options for the WKT parser */ + wkt?: { + /** Whether to add any CRS, if found, as undocumented CRS property on the return geometry */ + crs?: boolean; + }; +}; /** * Well-Known text loader */ -export const WKTWorkerLoader: Loader = { +export const WKTWorkerLoader: Loader = { name: 'WKT (Well-Known Text)', id: 'wkt', module: 'wkt', @@ -15,16 +28,20 @@ export const WKTWorkerLoader: Loader = { mimeTypes: ['text/plain'], category: 'geometry', text: true, + tests: WKT_MAGIC_STRINGS, + testText: isWKT, options: { - wkt: {} + wkt: { + crs: true + } } }; /** * Well-Known text loader */ -export const WKTLoader: LoaderWithParser = { +export const WKTLoader: LoaderWithParser = { ...WKTWorkerLoader, - parse: async (arrayBuffer) => parseWKT(new TextDecoder().decode(arrayBuffer)), + parse: async (arrayBuffer, options) => parseWKT(new TextDecoder().decode(arrayBuffer), options), parseTextSync: parseWKT }; diff --git a/modules/wkt/src/wkt-writer.ts b/modules/wkt/src/wkt-writer.ts index 4c07120979..90aecdbea0 100644 --- a/modules/wkt/src/wkt-writer.ts +++ b/modules/wkt/src/wkt-writer.ts @@ -1,18 +1,26 @@ -import type {Writer} from '@loaders.gl/loader-utils'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {Writer, WriterOptions} from '@loaders.gl/loader-utils'; import {VERSION} from './lib/utils/version'; -import encodeWKT from './lib/encode-wkt'; +import {encodeWKT} from './lib/encode-wkt'; +import {Geometry} from '@loaders.gl/schema'; + +export type WKTWriterOptions = WriterOptions & { + wkt?: {}; +}; /** * WKT exporter */ -export const WKTWriter: Writer = { +export const WKTWriter: Writer = { name: 'WKT (Well Known Text)', id: 'wkt', module: 'wkt', version: VERSION, extensions: ['wkt'], - // @ts-ignore - encode: encodeWKT, + // @ts-expect-error + encodeSync: encodeWKT, options: { wkt: {} } diff --git a/modules/wkt/src/workers/wkb-worker.ts b/modules/wkt/src/workers/wkb-worker.ts index 18af2cb766..f5ca5e6f0f 100644 --- a/modules/wkt/src/workers/wkb-worker.ts +++ b/modules/wkt/src/workers/wkb-worker.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import {createLoaderWorker} from '@loaders.gl/loader-utils'; import {WKBLoader} from '../wkb-loader'; diff --git a/modules/wkt/src/workers/wkt-worker.ts b/modules/wkt/src/workers/wkt-worker.ts index 9a65444f2c..db3c465024 100644 --- a/modules/wkt/src/workers/wkt-worker.ts +++ b/modules/wkt/src/workers/wkt-worker.ts @@ -1,3 +1,6 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import {createLoaderWorker} from '@loaders.gl/loader-utils'; import {WKTLoader} from '../wkt-loader'; diff --git a/modules/wkt/test/hex-wkb-loader.spec.ts b/modules/wkt/test/hex-wkb-loader.spec.ts new file mode 100644 index 0000000000..079aff6a56 --- /dev/null +++ b/modules/wkt/test/hex-wkb-loader.spec.ts @@ -0,0 +1,55 @@ +import test from 'tape-promise/tape'; +import {fetchFile, parseSync} from '@loaders.gl/core'; +import {HexWKBLoader} from '@loaders.gl/wkt'; +import {parseTestCases} from './utils/parse-test-cases'; + +const WKB_2D_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d.json'; +const WKB_Z_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ.json'; + +test('HexWKBLoader#2D', async (t) => { + const response = await fetchFile(WKB_2D_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + // TODO parseWKB outputs TypedArrays; testCase contains regular arrays + for (const testCase of Object.values(TEST_CASES)) { + // Little endian + if (testCase.wkbHex && testCase.binary) { + t.deepEqual(parseSync(testCase.wkbHex, HexWKBLoader), testCase.binary); + } + + // Big endian + if (testCase.wkbHexXdr && testCase.binary) { + t.deepEqual(parseSync(testCase.wkbHexXdr, HexWKBLoader), testCase.binary); + } + } + + t.end(); +}); + +test('HexWKBLoader#Z', async (t) => { + const response = await fetchFile(WKB_Z_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + // TODO parseWKB outputs TypedArrays; testCase contains regular arrays + for (const testCase of Object.values(TEST_CASES)) { + // Little endian + if (testCase.wkbHex && testCase.binary) { + t.deepEqual( + parseSync(testCase.wkbHex, HexWKBLoader), + testCase.binary, + testCase.wkbHex.slice(0, 60) + ); + } + + // Big endian + if (testCase.wkbHexXdr && testCase.binary) { + t.deepEqual( + parseSync(testCase.wkbHexXdr, HexWKBLoader), + testCase.binary, + testCase.wkbHexXdr.slice(0, 60) + ); + } + } + + t.end(); +}); diff --git a/modules/wkt/test/index.js b/modules/wkt/test/index.js deleted file mode 100644 index 43d51b71bd..0000000000 --- a/modules/wkt/test/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import './wkb/parse-wkb.spec'; -import './wkb/encode-wkb.spec'; -import './wkt/parse-wkt.spec'; -import './wkt/encode-wkt.spec'; -import './wkt/fuzz.spec'; - -import './wkt-loader.spec'; diff --git a/modules/wkt/test/index.ts b/modules/wkt/test/index.ts new file mode 100644 index 0000000000..5452a5b3a1 --- /dev/null +++ b/modules/wkt/test/index.ts @@ -0,0 +1,17 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import './lib/utils/hex-transcoder.spec'; + +import './wkb-loader.spec'; +import './wkb-writer.spec'; + +import './twkb-loader.spec'; +import './twkb-writer.spec'; + +import './hex-wkb-loader.spec'; + +import './wkt-loader.spec'; +import './wkt-writer.spec'; + +import './wkt-crs-loader.spec'; diff --git a/modules/wkt/test/lib/utils/hex-transcoder.spec.ts b/modules/wkt/test/lib/utils/hex-transcoder.spec.ts new file mode 100644 index 0000000000..d7079e4ff4 --- /dev/null +++ b/modules/wkt/test/lib/utils/hex-transcoder.spec.ts @@ -0,0 +1,37 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import test from 'tape-promise/tape'; +import {HexWKBLoader} from '@loaders.gl/wkt'; + +const isHexWKB = HexWKBLoader.testText!; + +test('datasetUtils.isHexWKB', (t) => { + t.notOk(isHexWKB(''), 'empty string is not a valid hex wkb'); + + // @ts-ignore null is not a string + t.notOk(isHexWKB(null), 'null is not a valid hex wkb'); + + const countyFIPS = '06075'; + t.notOk(isHexWKB(countyFIPS), 'FIPS code should not be a valid hex wkb'); + + const h3Code = '8a2a1072b59ffff'; + t.notOk(isHexWKB(h3Code), 'H3 code should not be a valid hex wkb'); + + const randomHexStr = '8a2a1072b59ffff'; + t.notOk(isHexWKB(randomHexStr), 'A random hex string should not be a valid hex wkb'); + + const validWkt = '0101000000000000000000f03f0000000000000040'; + t.ok(isHexWKB(validWkt), 'A valid hex wkb should be valid'); + + const validEWkt = '0101000020e6100000000000000000f03f0000000000000040'; + t.ok(isHexWKB(validEWkt), 'A valid hex ewkb should be valid'); + + const validWktNDR = '00000000013ff0000000000000400000000000000040'; + t.ok(isHexWKB(validWktNDR), 'A valid hex wkb in NDR should be valid'); + + const validEWktNDR = '0020000001000013ff0000000000400000000000000040'; + t.ok(isHexWKB(validEWktNDR), 'A valid hex ewkb in NDR should be valid'); + + t.end(); +}); diff --git a/modules/wkt/test/twkb-loader.spec.ts b/modules/wkt/test/twkb-loader.spec.ts new file mode 100644 index 0000000000..22522d6ab6 --- /dev/null +++ b/modules/wkt/test/twkb-loader.spec.ts @@ -0,0 +1,56 @@ +import test from 'tape-promise/tape'; +import {fetchFile, parseSync} from '@loaders.gl/core'; +import {TWKBLoader, isTWKB} from '@loaders.gl/wkt'; +import {parseTestCases} from './utils/parse-test-cases'; + +const WKB_2D_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d.json'; +// const WKB_Z_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ.json'; + +test('TWKBLoader#2D', async (t) => { + const response = await fetchFile(WKB_2D_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + // TODO parseWKB outputs TypedArrays; testCase contains regular arrays + for (const testCase of Object.values(TEST_CASES)) { + if (testCase.geoJSON.type === 'GeometryCollection') { + continue; // eslint-disable-line + } + + // Big endian + if (testCase.twkb && testCase.binary) { + t.ok(isTWKB(testCase.twkb), 'isTWKB(2D)'); + const geometry = {...testCase.geoJSON}; + // TODO - Weird empty geometry case, is that coorrect per spec? + if ( + geometry.coordinates.length === 1 && + // @ts-ignore + geometry.coordinates[0].length === 1 && + // @ts-ignore + geometry.coordinates[0][0].length === 0 + ) { + geometry.coordinates = []; + } + t.deepEqual(parseSync(testCase.twkb, TWKBLoader), geometry); + } + } + + t.end(); +}); + +// test('TWKBLoader#Z', async (t) => { +// const response = await fetchFile(WKB_Z_TEST_CASES); +// const TEST_CASES = parseTestCases(await response.json()); + +// // TODO parseWKB outputs TypedArrays; testCase contains regular arrays +// for (const testCase of Object.values(TEST_CASES)) { +// if (testCase.geoJSON.type === 'GeometryCollection') { +// continue; +// } + +// if (testCase.wkbXdr && testCase.binary && testCase.geoJSON) { +// t.deepEqual(parseSync(testCase.twkbXdr, TWKBLoader, {wkb: {shape: 'geometry'}}), testCase.geoJSON); +// } +// } + +// t.end(); +// }); diff --git a/modules/wkt/test/twkb-writer.spec.ts b/modules/wkt/test/twkb-writer.spec.ts new file mode 100644 index 0000000000..746dc17a50 --- /dev/null +++ b/modules/wkt/test/twkb-writer.spec.ts @@ -0,0 +1,63 @@ +/** +import test from 'tape-promise/tape'; +import {fetchFile, encodeSync} from '@loaders.gl/core'; +import {WKBWriter} from '@loaders.gl/wkt'; +import {parseTestCases} from './utils/parse-test-cases'; + +const WKB_2D_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d.json'; +const WKB_2D_NAN_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d-nan.json'; +const WKB_Z_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ.json'; +const WKB_Z_NAN_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ-nan.json'; + +test('WKBWriter#2D', async (t) => { + const response = await fetchFile(WKB_2D_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + for (const testCase of Object.values(TEST_CASES)) { + const {geoJSON, wkb} = testCase; + const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: false, hasM: false}}); + t.deepEqual(encoded, wkb); + } + + t.end(); +}); + +test('WKBWriter#2D NaN', async (t) => { + const response = await fetchFile(WKB_2D_NAN_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + for (const testCase of Object.values(TEST_CASES)) { + const {geoJSON, wkb} = testCase; + const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: false, hasM: false}}); + t.deepEqual(encoded, wkb); + } + + t.end(); +}); + +test('WKBWriter#Z', async (t) => { + const response = await fetchFile(WKB_Z_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + for (const testCase of Object.values(TEST_CASES)) { + const {geoJSON, wkb} = testCase; + const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: true, hasM: false}}); + t.deepEqual(encoded, wkb); + } + + t.end(); +}); + +test('WKBWriter#Z NaN', async (t) => { + const response = await fetchFile(WKB_Z_NAN_TEST_CASES); + const TEST_CASES = parseTestCases(await response.json()); + + for (const testCase of Object.values(TEST_CASES)) { + const {geoJSON, wkb} = testCase; + const encoded = encodeSync(geoJSON, WKBWriter, {wkb: {hasZ: true, hasM: false}}); + t.deepEqual(encoded, wkb); + } + + t.end(); +}); + */ diff --git a/modules/wkt/test/wkb/utils.ts b/modules/wkt/test/utils/parse-test-cases.ts similarity index 94% rename from modules/wkt/test/wkb/utils.ts rename to modules/wkt/test/utils/parse-test-cases.ts index efff562393..686ea46e84 100644 --- a/modules/wkt/test/wkb/utils.ts +++ b/modules/wkt/test/utils/parse-test-cases.ts @@ -1,6 +1,6 @@ import {Geometry, BinaryGeometry} from '@loaders.gl/schema'; -interface TestCase { +export interface TestCase { /** Geometry in WKT */ wkt: string; @@ -36,6 +36,9 @@ interface ParsedTestCase { /** Geometry in WKT */ wkt: string; + /** Geometry in WKB, stored in hex */ + wkbHex: string; + /** Geometry in WKB */ wkb: ArrayBuffer; @@ -45,6 +48,9 @@ interface ParsedTestCase { /** Geometry in WKB XDR (big endian) */ wkbXdr: ArrayBuffer; + /** Geometry in WKB, stored in hex */ + wkbHexXdr: string; + /** Geometry in EWKB XDR (big endian) */ ewkbXdr: ArrayBuffer; @@ -106,6 +112,8 @@ export function parseTestCases( const parsedTestCase: ParsedTestCase = { wkt, geoJSON, + wkbHex: wkb, + wkbHexXdr: wkbXdr, wkb: hexStringToArrayBuffer(wkb), ewkb: hexStringToArrayBuffer(ewkb), twkb: hexStringToArrayBuffer(twkb), diff --git a/modules/wkt/test/wkb/parse-wkb.spec.ts b/modules/wkt/test/wkb-loader.spec.ts similarity index 52% rename from modules/wkt/test/wkb/parse-wkb.spec.ts rename to modules/wkt/test/wkb-loader.spec.ts index 7005d5cbff..65fa6b3a93 100644 --- a/modules/wkt/test/wkb/parse-wkb.spec.ts +++ b/modules/wkt/test/wkb-loader.spec.ts @@ -1,12 +1,12 @@ import test from 'tape-promise/tape'; -import {fetchFile} from '@loaders.gl/core'; -import parseWKB from '../../src/lib/parse-wkb'; -import {parseTestCases} from './utils'; +import {fetchFile, parseSync} from '@loaders.gl/core'; +import {WKBLoader, isWKB} from '@loaders.gl/wkt'; +import {parseTestCases} from './utils/parse-test-cases'; const WKB_2D_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d.json'; const WKB_Z_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ.json'; -test('parseWKB2D', async (t) => { +test('WKBLoader#2D', async (t) => { const response = await fetchFile(WKB_2D_TEST_CASES); const TEST_CASES = parseTestCases(await response.json()); @@ -14,19 +14,21 @@ test('parseWKB2D', async (t) => { for (const testCase of Object.values(TEST_CASES)) { // Little endian if (testCase.wkb && testCase.binary) { - t.deepEqual(parseWKB(testCase.wkb), testCase.binary); + t.ok(isWKB(testCase.wkb), 'isWKB(2D)'); + t.deepEqual(parseSync(testCase.wkb, WKBLoader), testCase.binary); } // Big endian if (testCase.wkbXdr && testCase.binary) { - t.deepEqual(parseWKB(testCase.wkbXdr), testCase.binary); + t.ok(isWKB(testCase.wkbXdr), 'isWKB(2D)'); + t.deepEqual(parseSync(testCase.wkbXdr, WKBLoader), testCase.binary); } } t.end(); }); -test('parseWKB Z', async (t) => { +test('WKBLoader#Z', async (t) => { const response = await fetchFile(WKB_Z_TEST_CASES); const TEST_CASES = parseTestCases(await response.json()); @@ -34,13 +36,19 @@ test('parseWKB Z', async (t) => { for (const testCase of Object.values(TEST_CASES)) { // Little endian if (testCase.wkb && testCase.binary) { - t.deepEqual(parseWKB(testCase.wkb), testCase.binary); + t.ok(isWKB(testCase.wkb), 'isWKB(Z)'); + t.deepEqual(parseSync(testCase.wkb, WKBLoader), testCase.binary); } // Big endian if (testCase.wkbXdr && testCase.binary) { - t.deepEqual(parseWKB(testCase.wkbXdr), testCase.binary); + t.ok(isWKB(testCase.wkbXdr), 'isWKB(Z)'); + t.deepEqual(parseSync(testCase.wkbXdr, WKBLoader), testCase.binary); } + + // if (testCase.wkbXdr && testCase.binary && testCase.geoJSON) { + // t.deepEqual(parseSync(testCase.wkbXdr, WKBLoader, {wkb: {shape: 'geometry'}}), testCase.geoJSON); + // } } t.end(); diff --git a/modules/wkt/test/wkb/encode-wkb.spec.ts b/modules/wkt/test/wkb-writer.spec.ts similarity index 89% rename from modules/wkt/test/wkb/encode-wkb.spec.ts rename to modules/wkt/test/wkb-writer.spec.ts index cfcd68238b..4db3abe6cc 100644 --- a/modules/wkt/test/wkb/encode-wkb.spec.ts +++ b/modules/wkt/test/wkb-writer.spec.ts @@ -1,14 +1,14 @@ import test from 'tape-promise/tape'; import {fetchFile, encodeSync} from '@loaders.gl/core'; import {WKBWriter} from '@loaders.gl/wkt'; -import {parseTestCases} from './utils'; +import {parseTestCases} from './utils/parse-test-cases'; const WKB_2D_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d.json'; const WKB_2D_NAN_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdata2d-nan.json'; const WKB_Z_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ.json'; const WKB_Z_NAN_TEST_CASES = '@loaders.gl/wkt/test/data/wkb-testdataZ-nan.json'; -test('encodeWKB 2D', async (t) => { +test('WKBWriter#2D', async (t) => { const response = await fetchFile(WKB_2D_TEST_CASES); const TEST_CASES = parseTestCases(await response.json()); @@ -21,7 +21,7 @@ test('encodeWKB 2D', async (t) => { t.end(); }); -test('encodeWKB 2D NaN', async (t) => { +test('WKBWriter#2D NaN', async (t) => { const response = await fetchFile(WKB_2D_NAN_TEST_CASES); const TEST_CASES = parseTestCases(await response.json()); @@ -34,7 +34,7 @@ test('encodeWKB 2D NaN', async (t) => { t.end(); }); -test('encodeWKB Z', async (t) => { +test('WKBWriter#Z', async (t) => { const response = await fetchFile(WKB_Z_TEST_CASES); const TEST_CASES = parseTestCases(await response.json()); @@ -47,7 +47,7 @@ test('encodeWKB Z', async (t) => { t.end(); }); -test('encodeWKB Z NaN', async (t) => { +test('WKBWriter#Z NaN', async (t) => { const response = await fetchFile(WKB_Z_NAN_TEST_CASES); const TEST_CASES = parseTestCases(await response.json()); diff --git a/modules/wkt/test/wkt-crs-loader.spec.ts b/modules/wkt/test/wkt-crs-loader.spec.ts new file mode 100644 index 0000000000..3bb642a756 --- /dev/null +++ b/modules/wkt/test/wkt-crs-loader.spec.ts @@ -0,0 +1,264 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +// parse-wkt-crs was forked from https://github.com/DanielJDufour/wkt-crs under Creative Commons CC0 1.0 license. + +import test from 'tape-promise/tape'; +import {parseSync, encodeTextSync} from '@loaders.gl/core'; +import {WKTCRSLoader, WKTCRSWriter} from '@loaders.gl/wkt'; + +const roundtrip = (wkt) => encodeTextSync(parseSync(wkt, WKTCRSLoader, {raw: true}), WKTCRSWriter); + +const condense = (wkt) => wkt.trim().replace(/(?<=[,\[\]])[ \n]+/g, ''); + +test('WKTCRSLoader#NAD27 / UTM zone 16N', (t) => { + const wkt = + 'PROJCS["NAD27 / UTM zone 16N",GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982139006,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4267"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-87],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","26716"]]'; + const data = parseSync(wkt, WKTCRSLoader, {raw: false, debug: false}); + // console.log(JSON.stringify(data, undefined, 2)); + t.deepEqual(data.length, 1); + t.deepEqual(Object.keys(data), ['0', 'PROJCS']); + t.deepEqual(data.PROJCS.AUTHORITY, ['AUTHORITY', 'EPSG', '26716']); + t.deepEqual(data.PROJCS === data[0], true); + t.deepEqual(data.PROJCS[1] === 'NAD27 / UTM zone 16N', true); + t.deepEqual(data.PROJCS.GEOGCS === data[0][2], true); + + // raw mode + // t.deepEqual(roundtrip(wkt), wkt); + t.end(); +}); + +test('WKTCRSLoader#wikipedia example', (t) => { + const wkt = `GEODCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84", 6378137, 298.257223563, LENGTHUNIT["metre", 1]]], + CS[ellipsoidal, 2], + AXIS["Latitude (lat)", north, ORDER[1]], + AXIS["Longitude (lon)", east, ORDER[2]], + ANGLEUNIT["degree", 0.0174532925199433]]`; + const data = parseSync(wkt, WKTCRSLoader, {debug: false}); + t.deepEqual(data.GEODCRS[1], 'WGS 84'); + t.deepEqual(data.GEODCRS.DATUM.ELLIPSOID[3], 298.257223563); + t.deepEqual(data.GEODCRS.CS[1], 'ellipsoidal'); + t.deepEqual(data.GEODCRS.ANGLEUNIT[2], 0.0174532925199433); + // t.deepEqual(roundtrip(wkt), condense(wkt)); + t.end(); +}); + +test.skip('WKTCRSLoader#wikipedia raw', (t) => { + const wkt = `GEODCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84", 6378137, 298.257223563, LENGTHUNIT["metre", 1]]], + CS[ellipsoidal, 2], + AXIS["Latitude (lat)", north, ORDER[1]], + AXIS["Longitude (lon)", east, ORDER[2]], + ANGLEUNIT["degree", 0.0174532925199433]]`; + const data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true}); + t.deepEqual(data.GEODCRS[1], 'WGS 84'); + t.deepEqual(data.GEODCRS.DATUM.ELLIPSOID[3], 'raw:298.257223563'); + t.deepEqual(data.GEODCRS.CS[1], 'raw:ellipsoidal'); + t.deepEqual(data.GEODCRS.ANGLEUNIT[2], 'raw:0.0174532925199433'); + t.deepEqual(roundtrip(wkt), condense(wkt)); + t.end(); +}); + +test('WKTCRSLoader#wikipedia concat', (t) => { + const wkt = ` + CONCAT_MT[ + PARAM_MT["Mercator_2SP", + PARAMETER["semi_major",6370997.0], + PARAMETER["semi_minor",6370997.0], + PARAMETER["central_meridian",180.0], + PARAMETER["false_easting",-500000.0], + PARAMETER["false_northing",-1000000.0], + PARAMETER["standard parallel 1",60.0]], + PARAM_MT["Affine", + PARAMETER["num_row",3], + PARAMETER["num_col",3], + PARAMETER["elt_0_1",1], + PARAMETER["elt_0_2",2], + PARAMETER["elt 1 2",3]]] + `; + const data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true}); + t.deepEqual(data.CONCAT_MT.PARAM_MT, undefined); + t.deepEqual(data.CONCAT_MT.MULTIPLE_PARAM_MT.length, 2); + // t.deepEqual(roundtrip(wkt), condense(wkt)); + t.end(); +}); + +test.skip('WKTCRSLoader#wikipedia datum shift', (t) => { + const wkt = ` + COORDINATEOPERATION["AGD84 to GDA94 Auslig 5m", + SOURCECRS["…full CRS definition required here but omitted for brevity…"], + TARGETCRS["…full CRS definition required here but omitted for brevity…"], + METHOD["Geocentric translations", ID["EPSG", 1031]], + PARAMETER["X-axis translation", -128.5, LENGTHUNIT["metre", 1]], + PARAMETER["Y-axis translation", -53.0, LENGTHUNIT["metre", 1]], + PARAMETER["Z-axis translation", 153.4, LENGTHUNIT["metre", 1]], + OPERATIONACCURACY[5], + AREA["Australia onshore"], + BBOX[-43.7, 112.85, -9.87, 153.68]] + `; + const data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true}); + // stringifying array ignores keys added on + const str = JSON.stringify(data); + t.deepEqual( + str, + '[["COORDINATEOPERATION","AGD84 to GDA94 Auslig 5m",["SOURCECRS","…full CRS definition required here but omitted for brevity…"],["TARGETCRS","…full CRS definition required here but omitted for brevity…"],["METHOD","Geocentric translations",["ID","EPSG","raw:1031"]],["PARAMETER","X-axis translation","raw:-128.5",["LENGTHUNIT","metre","raw:1"]],["PARAMETER","Y-axis translation","raw:-53.0",["LENGTHUNIT","metre","raw:1"]],["PARAMETER","Z-axis translation","raw:153.4",["LENGTHUNIT","metre","raw:1"]],["OPERATIONACCURACY","raw:5"],["AREA","Australia onshore"],["BBOX","raw:-43.7","raw:112.85","raw:-9.87","raw:153.68"]]]' + ); + t.deepEqual(roundtrip(wkt), condense(wkt)); + t.end(); +}); + +test('WKTCRSLoader#proj4js example', (t) => { + const wkt = + 'PROJCS["NAD83 / Massachusetts Mainland",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",42.68333333333333],PARAMETER["standard_parallel_2",41.71666666666667],PARAMETER["latitude_of_origin",41],PARAMETER["central_meridian",-71.5],PARAMETER["false_easting",200000],PARAMETER["false_northing",750000],AUTHORITY["EPSG","26986"],AXIS["X",EAST],AXIS["Y",NORTH]]'; + const data = parseSync(wkt, WKTCRSLoader); + t.deepEqual(data.PROJCS[1], 'NAD83 / Massachusetts Mainland'); + t.end(); +}); + +test('WKTCRSLoader#parse attribute that ends in number (TOWGS84)', (t) => { + const wkt = + ' GEOGCS["SAD69",DATUM["South_American_Datum_1969",SPHEROID["GRS 1967 Modified",6378160,298.25,AUTHORITY["EPSG","7050"]],TOWGS84[-57,1,-41,0,0,0,0],AUTHORITY["EPSG","6618"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4618"]]'; + t.deepEqual(roundtrip(wkt), condense(wkt)); + t.end(); +}); + +test.skip('WKTCRSLoader#another parse bug', (t) => { + const wkt = + 'PROJCS["ETRS89 / TM35FIN(E,N)",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",27],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","3067"]]'; + const data = parseSync(wkt, WKTCRSLoader, {debug: false}); + t.deepEqual(data.PROJCS[1], 'ETRS89 / TM35FIN(E,N)'); + t.deepEqual(data.PROJCS.MULTIPLE_AXIS[1][2], 'NORTH'); + t.deepEqual(roundtrip(wkt), wkt); + t.end(); +}); + +// Not clear where to find crs.json +// test.skip('WKTCRSLoader#try to parse everything in crs.json', (t) => { +// let data = require('./crs.json'); +// data = data.map(({wkt, esriwkt, prettywkt}) => ({ +// raw: { +// wkt: parseSync(wkt, WKTCRSLoader, {raw: true}), +// esriwkt: parseSync(esriwkt, WKTCRSLoader, {raw: true}), +// prettywkt: parseSync(prettywkt, WKTCRSLoader, {raw: true}) +// }, +// dynamic: { +// wkt: parseSync(wkt, WKTCRSLoader, {raw: false}), +// esriwkt: parseSync(esriwkt, WKTCRSLoader, {raw: false}), +// prettywkt: parseSync(prettywkt, WKTCRSLoader, {raw: false}) +// } +// })); + +// // prettywkt and wkt should be equivalent +// // only difference was white space +// data.every(({raw, dynamic}) => { +// t.deepEqual(raw.wkt, raw.prettywkt); +// t.deepEqual(dynamic.wkt, dynamic.prettywkt); +// }); +// }); + +// test("7.5.6.3 Axis unit for ordinal coordinate systems", t => { +// const wkt = `NULL[CS[ordinal,2], +// AXIS["inline (I)",southeast,ORDER[1]], +// AXIS["crossline (J)",northeast,ORDER[2]]]`; +// const data = parseSync(wkt, WKTCRSLoader, { debug: true }); +// t.deepEqual(data.CS[0], "ordinal"); +// t.end(); +// }); + +test.skip('WKTCRSLoader#sort parameters', (t) => { + const wkt = + 'PROJCS["WGS_1984_Antarctic_Polar_Stereographic",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Stereographic_South_Pole"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",0.0],PARAMETER["Standard_Parallel_1",-71.0],UNIT["Meter",1.0]]'; + const data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true, sort: true}); + t.deepEqual( + encodeTextSync(data, WKTCRSWriter), + 'PROJCS["WGS_1984_Antarctic_Polar_Stereographic",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Stereographic_South_Pole"],PARAMETER["Central_Meridian",0.0],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Standard_Parallel_1",-71.0],UNIT["Meter",1.0]]' + ); + t.end(); +}); + +// test("sort example", t => { +// const data = ["EXAMPLE", ["AXIS", "Northing", "raw:NORTH"], ["AXIS", "Easting", "raw:EAST"]]; +// wktcrs.sort(data); +// t.deepEqual(data, ["EXAMPLE", ["AXIS", "Easting", "raw:EAST"], ["AXIS", "Northing", "raw:NORTH"]]); +// t.end(); +// }); + +test.skip('WKTCRSLoader#sort params', (t) => { + const wkt = + 'PARAMETERS[PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-87],PARAMETER["scale_factor",0.9996]]'; + let data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true}); + t.deepEqual(data[0].MULTIPLE_PARAMETER, [ + ['PARAMETER', 'latitude_of_origin', 'raw:0'], + ['PARAMETER', 'central_meridian', 'raw:-87'], + ['PARAMETER', 'scale_factor', 'raw:0.9996'] + ]); + data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true, sort: true}); + t.deepEqual(data[0].MULTIPLE_PARAMETER, [ + ['PARAMETER', 'central_meridian', 'raw:-87'], + ['PARAMETER', 'latitude_of_origin', 'raw:0'], + ['PARAMETER', 'scale_factor', 'raw:0.9996'] + ]); + t.deepEqual( + encodeTextSync(data, WKTCRSWriter), + 'PARAMETERS[PARAMETER["central_meridian",-87],PARAMETER["latitude_of_origin",0],PARAMETER["scale_factor",0.9996]]' + ); + t.end(); +}); + +test('WKTCRSLoader#parse inner parens', (t) => { + const wkt = + 'GEOGCS["GRS 1980(IUGG, 1980)",DATUM["unknown",SPHEROID["GRS80",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["epsg","7686"]]'; + const data = parseSync(wkt, WKTCRSLoader, {debug: false, raw: true}); + t.deepEqual(data.GEOGCS[0], 'GEOGCS'); + t.end(); +}); + +test.skip('WKTCRSWriter#authority', (t) => { + const authority = ['AUTHORITY', 'EPSG', '9122']; + const unparsed = encodeTextSync(authority, WKTCRSWriter); + t.deepEqual(unparsed, {data: 'AUTHORITY["EPSG","9122"]'}); + t.end(); +}); + +test.skip('WKTCRSWriter#PRIMEM', (t) => { + const authority = ['PRIMEM', 'Greenwich', 0, ['AUTHORITY', 'EPSG', '8901']]; + const unparsed = encodeTextSync(authority, WKTCRSWriter); + t.deepEqual(unparsed, {data: 'PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]]'}); + t.end(); +}); + +test.skip('WKTCRSWriter#DATUM', (t) => { + const datum = [ + 'DATUM', + 'North_American_Datum_1927', + ['SPHEROID', 'Clarke 1866', 6378206.4, 294.9786982139006, ['AUTHORITY', 'EPSG', '7008']], + ['AUTHORITY', 'EPSG', '6267'] + ]; + const unparsed = encodeTextSync(datum, WKTCRSWriter); + t.deepEqual(unparsed, { + data: 'DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982139006,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]]' + }); + t.end(); +}); + +test.skip('WKTCRSWriter#GEOGCS', (t) => { + const data = [ + 'GEOGCS', + 'NAD27', + [ + 'DATUM', + 'North_American_Datum_1927', + ['SPHEROID', 'Clarke 1866', 6378206.4, 294.9786982139006, ['AUTHORITY', 'EPSG', '7008']], + ['AUTHORITY', 'EPSG', '6267'] + ], + ['PRIMEM', 'Greenwich', 0, ['AUTHORITY', 'EPSG', '8901']], + ['UNIT', 'degree', 0.0174532925199433, ['AUTHORITY', 'EPSG', '9122']], + ['AUTHORITY', 'EPSG', '4267'] + ]; + const unparsed = encodeTextSync(data, WKTCRSWriter); + t.deepEqual(unparsed, { + data: 'GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982139006,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4267"]]' + }); +}); diff --git a/modules/wkt/test/wkt-loader.spec.js b/modules/wkt/test/wkt-loader.spec.ts similarity index 94% rename from modules/wkt/test/wkt-loader.spec.js rename to modules/wkt/test/wkt-loader.spec.ts index 244873d00f..9bba289ddd 100644 --- a/modules/wkt/test/wkt-loader.spec.js +++ b/modules/wkt/test/wkt-loader.spec.ts @@ -5,6 +5,8 @@ import {validateLoader} from 'test/common/conformance'; import {WKTLoader, WKTWorkerLoader} from '@loaders.gl/wkt'; import {setLoaderOptions, fetchFile, parseSync} from '@loaders.gl/core'; +import fuzzer from 'fuzzer'; + const GEOMETRYCOLLECTION_WKT_URL = '@loaders.gl/wkt/test/data/geometrycollection.wkt'; const GEOMETRYCOLLECTION_GEOJSON_URL = '@loaders.gl/wkt/test/data/geometrycollection.geojson'; @@ -489,3 +491,24 @@ test('WKTLoader', async (t) => { // t.deepEqual(parseSync(GEOMETRYCOLLECTION_WKT, WKTLoader), GEOMETRYCOLLECTION_GEOJSON); // t.end(); // }); + +test('WKTLoader#fuzz', (t) => { + fuzzer.seed(0); + const inputs = [ + 'MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))', + 'POINT(1.1 1.1)', + 'LINESTRING (30 10, 10 30, 40 40)', + 'GeometryCollection(POINT(4 6),\nLINESTRING(4 6,7 10))' + ]; + inputs.forEach(function (str) { + for (let i = 0; i < 10000; i++) { + const input = fuzzer.mutate.string(str); + try { + parseSync(input, WKTLoader); + } catch (e) { + t.fail(`could not parse ${input}, exception ${e}`); + } + } + }); + t.end(); +}); diff --git a/modules/wkt/test/wkt-writer.spec.ts b/modules/wkt/test/wkt-writer.spec.ts new file mode 100644 index 0000000000..37218f2768 --- /dev/null +++ b/modules/wkt/test/wkt-writer.spec.ts @@ -0,0 +1,45 @@ +import test from 'tape-promise/tape'; + +import {encodeSync} from '@loaders.gl/core'; +import {WKTWriter} from '@loaders.gl/wkt'; + +test('WKTWriter', (t) => { + t.throws( + () => encodeSync({type: 'FeatureCollection'}, WKTWriter), + 'does not accept featurecollections' + ); + + // const fixtures = [ + // 'LINESTRING (30 10, 10 30, 40 40)', + // 'POINT (1 1)', + // 'POINT (1 1 1 1)', + // 'LINESTRING (1 2 3, 4 5 6)', + // 'LINESTRING (1 2 3 4, 5 6 7 8)', + // 'POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))', + // 'POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10), (20 30, 35 35, 30 20, 20 30))', + // 'MULTIPOINT (1 1, 2 3)', + // 'MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5), (10 10, 15 10, 15 15, 10 10)))', + // 'MULTILINESTRING ((30 10, 10 30, 40 40), (30 10, 10 30, 40 40))', + // 'GEOMETRYCOLLECTION (POINT (4 6), LINESTRING (4 6, 7 10))' + // ]; + + // fixtures.forEach((fix) => t.equal(fix, encodeSync(parse(fix, WKTLoader), WKTWriter), fix)); + + t.equal( + encodeSync( + { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [42, 20] + } + }, + WKTWriter + ), + 'POINT (42 20)', + 'point equal' + ); + + t.end(); +}); diff --git a/modules/wkt/test/wkt/encode-wkt.spec.js b/modules/wkt/test/wkt/encode-wkt.spec.js deleted file mode 100644 index 749913be89..0000000000 --- a/modules/wkt/test/wkt/encode-wkt.spec.js +++ /dev/null @@ -1,44 +0,0 @@ -import test from 'tape-promise/tape'; - -import encodeWKT from '../../src/lib/encode-wkt'; -import parseWKT from '../../src/lib/parse-wkt'; - -test('encodeWKT', (t) => { - // @ts-ignore - t.throws(() => encodeWKT({type: 'FeatureCollection'}), 'does not accept featurecollections'); - - const fixtures = [ - 'LINESTRING (30 10, 10 30, 40 40)', - 'POINT (1 1)', - 'POINT (1 1 1 1)', - 'LINESTRING (1 2 3, 4 5 6)', - 'LINESTRING (1 2 3 4, 5 6 7 8)', - 'POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))', - 'POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10), (20 30, 35 35, 30 20, 20 30))', - 'MULTIPOINT (1 1, 2 3)', - 'MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5), (10 10, 15 10, 15 15, 10 10)))', - 'MULTILINESTRING ((30 10, 10 30, 40 40), (30 10, 10 30, 40 40))', - 'GEOMETRYCOLLECTION (POINT (4 6), LINESTRING (4 6, 7 10))' - ]; - - fixtures.forEach((fix) => t.equal(fix, loop(fix), fix)); - - function loop(s) { - return encodeWKT(parseWKT(s)); - } - - t.equal( - encodeWKT({ - type: 'Feature', - properties: {}, - geometry: { - type: 'Point', - coordinates: [42, 20] - } - }), - 'POINT (42 20)', - 'point equal' - ); - - t.end(); -}); diff --git a/modules/wkt/test/wkt/fuzz.spec.js b/modules/wkt/test/wkt/fuzz.spec.js deleted file mode 100644 index f7089f696c..0000000000 --- a/modules/wkt/test/wkt/fuzz.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -// Fork of https://github.com/mapbox/wellknown under ISC license (MIT/BSD-2-clause equivalent) - -import test from 'tape-promise/tape'; -import parseWKT from '../../src/lib/parse-wkt'; -import fuzzer from 'fuzzer'; - -test('parseWKT#fuzz', (t) => { - fuzzer.seed(0); - const inputs = [ - 'MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))', - 'POINT(1.1 1.1)', - 'LINESTRING (30 10, 10 30, 40 40)', - 'GeometryCollection(POINT(4 6),\nLINESTRING(4 6,7 10))' - ]; - inputs.forEach(function (str) { - for (let i = 0; i < 10000; i++) { - const input = fuzzer.mutate.string(str); - try { - parseWKT(input); - } catch (e) { - t.fail(`could not parse ${input}, exception ${e}`); - } - } - }); - t.end(); -}); diff --git a/modules/wkt/test/wkt/parse-wkt.spec.js b/modules/wkt/test/wkt/parse-wkt.spec.js deleted file mode 100644 index 19f3d64407..0000000000 --- a/modules/wkt/test/wkt/parse-wkt.spec.js +++ /dev/null @@ -1,449 +0,0 @@ -// Fork of https://github.com/mapbox/wellknown under ISC license (MIT/BSD-2-clause equivalent) - -import test from 'tape-promise/tape'; -import parseWKT from '../../src/lib/parse-wkt'; -import {fetchFile} from '@loaders.gl/core'; - -const GEOMETRYCOLLECTION_WKT_URL = '@loaders.gl/wkt/test/data/geometrycollection.wkt'; -const GEOMETRYCOLLECTION_GEOJSON_URL = '@loaders.gl/wkt/test/data/geometrycollection.geojson'; - -// eslint-disable-next-line max-statements -test('parseWKT', async (t) => { - let response = await fetchFile(GEOMETRYCOLLECTION_WKT_URL); - const GEOMETRYCOLLECTION_WKT = await response.text(); - - response = await fetchFile(GEOMETRYCOLLECTION_GEOJSON_URL); - const GEOMETRYCOLLECTION_GEOJSON = await response.json(); - - t.deepEqual(parseWKT('POINT (0 1)'), { - type: 'Point', - coordinates: [0, 1] - }); - t.deepEqual(parseWKT('POINT (1 1)'), { - type: 'Point', - coordinates: [1, 1] - }); - t.deepEqual(parseWKT('POINT(1 1)'), { - type: 'Point', - coordinates: [1, 1] - }); - t.deepEqual(parseWKT('POINT\n\r(1 1)'), { - type: 'Point', - coordinates: [1, 1] - }); - t.deepEqual(parseWKT('POINT(1.1 1.1)'), { - type: 'Point', - coordinates: [1.1, 1.1] - }); - t.deepEqual(parseWKT('point(1.1 1.1)'), { - type: 'Point', - coordinates: [1.1, 1.1] - }); - t.deepEqual(parseWKT('point(1 2 3)'), { - type: 'Point', - coordinates: [1, 2, 3] - }); - t.deepEqual(parseWKT('point(1 2 3 4)'), { - type: 'Point', - coordinates: [1, 2, 3, 4] - }); - t.deepEqual(parseWKT('SRID=3857;POINT (1 2 3)'), { - type: 'Point', - coordinates: [1, 2, 3], - crs: { - type: 'name', - properties: { - name: 'urn:ogc:def:crs:EPSG::3857' - } - } - }); - t.deepEqual(parseWKT('LINESTRING (30 10, 10 30, 40 40)'), { - type: 'LineString', - coordinates: [ - [30, 10], - [10, 30], - [40, 40] - ] - }); - t.deepEqual(parseWKT('LINESTRING(30 10, 10 30, 40 40)'), { - type: 'LineString', - coordinates: [ - [30, 10], - [10, 30], - [40, 40] - ] - }); - t.deepEqual(parseWKT('LineString(30 10, 10 30, 40 40)'), { - type: 'LineString', - coordinates: [ - [30, 10], - [10, 30], - [40, 40] - ] - }); - t.deepEqual(parseWKT('LINESTRING (1 2 3, 4 5 6)'), { - type: 'LineString', - coordinates: [ - [1, 2, 3], - [4, 5, 6] - ] - }); - t.deepEqual(parseWKT('LINESTRING (1 2 3 4, 5 6 7 8)'), { - type: 'LineString', - coordinates: [ - [1, 2, 3, 4], - [5, 6, 7, 8] - ] - }); - t.deepEqual(parseWKT('SRID=3857;LINESTRING (30 10, 10 30, 40 40)'), { - type: 'LineString', - coordinates: [ - [30, 10], - [10, 30], - [40, 40] - ], - crs: { - type: 'name', - properties: { - name: 'urn:ogc:def:crs:EPSG::3857' - } - } - }); - t.deepEqual(parseWKT('POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))'), { - type: 'Polygon', - coordinates: [ - [ - [30, 10], - [10, 20], - [20, 40], - [40, 40], - [30, 10] - ] - ] - }); - t.deepEqual(parseWKT('POLYGON((30 10, 10 20, 20 40, 40 40, 30 10))'), { - type: 'Polygon', - coordinates: [ - [ - [30, 10], - [10, 20], - [20, 40], - [40, 40], - [30, 10] - ] - ] - }); - t.deepEqual(parseWKT('SRID=3857;POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))'), { - type: 'Polygon', - coordinates: [ - [ - [30, 10], - [10, 20], - [20, 40], - [40, 40], - [30, 10] - ] - ], - crs: { - type: 'name', - properties: { - name: 'urn:ogc:def:crs:EPSG::3857' - } - } - }); - t.deepEqual( - parseWKT('POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10),(20 30, 35 35, 30 20, 20 30))'), - { - type: 'Polygon', - coordinates: [ - [ - [35, 10], - [10, 20], - [15, 40], - [45, 45], - [35, 10] - ], - [ - [20, 30], - [35, 35], - [30, 20], - [20, 30] - ] - ] - } - ); - t.deepEqual(parseWKT('MULTIPOINT (0 0, 2 3)'), { - type: 'MultiPoint', - coordinates: [ - [0, 0], - [2, 3] - ] - }); - t.deepEqual(parseWKT('MULTIPOINT (1 1, 2 3)'), { - type: 'MultiPoint', - coordinates: [ - [1, 1], - [2, 3] - ] - }); - t.deepEqual(parseWKT('MultiPoint (1 1, 2 3)'), { - type: 'MultiPoint', - coordinates: [ - [1, 1], - [2, 3] - ] - }); - t.deepEqual(parseWKT('SRID=3857;MULTIPOINT (1 1, 2 3)'), { - type: 'MultiPoint', - coordinates: [ - [1, 1], - [2, 3] - ], - crs: { - type: 'name', - properties: { - name: 'urn:ogc:def:crs:EPSG::3857' - } - } - }); - t.deepEqual(parseWKT('MULTIPOINT ((0 0), (2 3))'), { - type: 'MultiPoint', - coordinates: [ - [0, 0], - [2, 3] - ] - }); - t.deepEqual(parseWKT('MULTIPOINT ((1 1), (2 3))'), { - type: 'MultiPoint', - coordinates: [ - [1, 1], - [2, 3] - ] - }); - t.deepEqual(parseWKT('MultiPoint ((1 1), (2 3))'), { - type: 'MultiPoint', - coordinates: [ - [1, 1], - [2, 3] - ] - }); - t.deepEqual(parseWKT('SRID=3857;MULTIPOINT ((1 1), (2 3))'), { - type: 'MultiPoint', - coordinates: [ - [1, 1], - [2, 3] - ], - crs: { - type: 'name', - properties: { - name: 'urn:ogc:def:crs:EPSG::3857' - } - } - }); - t.deepEqual(parseWKT('MULTILINESTRING ((30 10, 10 30, 40 40), (30 10, 10 30, 40 40))'), { - type: 'MultiLineString', - coordinates: [ - [ - [30, 10], - [10, 30], - [40, 40] - ], - [ - [30, 10], - [10, 30], - [40, 40] - ] - ] - }); - t.deepEqual( - parseWKT('SRID=3857;MULTILINESTRING ((30 10, 10 30, 40 40), (30 10, 10 30, 40 40))'), - { - type: 'MultiLineString', - coordinates: [ - [ - [30, 10], - [10, 30], - [40, 40] - ], - [ - [30, 10], - [10, 30], - [40, 40] - ] - ], - crs: { - type: 'name', - properties: { - name: 'urn:ogc:def:crs:EPSG::3857' - } - } - } - ); - t.deepEqual( - parseWKT('MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))'), - { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [30, 20], - [10, 40], - [45, 40], - [30, 20] - ] - ], - [ - [ - [15, 5], - [40, 10], - [10, 20], - [5, 10], - [15, 5] - ] - ] - ] - } - ); - t.deepEqual(parseWKT('MULTIPOLYGON (((-74.03349399999999 40.688348)))'), { - type: 'MultiPolygon', - coordinates: [[[[-74.03349399999999, 40.688348]]]] - }); - t.deepEqual( - parseWKT( - 'MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5), (10 10, 15 10, 15 15, 10 10)))' - ), - { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [30, 20], - [10, 40], - [45, 40], - [30, 20] - ] - ], - [ - [ - [15, 5], - [40, 10], - [10, 20], - [5, 10], - [15, 5] - ], - [ - [10, 10], - [15, 10], - [15, 15], - [10, 10] - ] - ] - ] - } - ); - t.deepEqual( - parseWKT( - 'SRID=3857;MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))' - ), - { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [30, 20], - [10, 40], - [45, 40], - [30, 20] - ] - ], - [ - [ - [15, 5], - [40, 10], - [10, 20], - [5, 10], - [15, 5] - ] - ] - ], - crs: { - type: 'name', - properties: { - name: 'urn:ogc:def:crs:EPSG::3857' - } - } - } - ); - t.deepEqual(parseWKT(GEOMETRYCOLLECTION_WKT), GEOMETRYCOLLECTION_GEOJSON); - t.deepEqual(parseWKT('GeometryCollection(POINT(4 6),LINESTRING(4 6,7 10))'), { - type: 'GeometryCollection', - geometries: [ - { - type: 'Point', - coordinates: [4, 6] - }, - { - type: 'LineString', - coordinates: [ - [4, 6], - [7, 10] - ] - } - ] - }); - t.deepEqual(parseWKT('GeometryCollection(POINT(4 6),\nLINESTRING(4 6,7 10))'), { - type: 'GeometryCollection', - geometries: [ - { - type: 'Point', - coordinates: [4, 6] - }, - { - type: 'LineString', - coordinates: [ - [4, 6], - [7, 10] - ] - } - ] - }); - t.deepEqual(parseWKT('POINT (1e-6 1E+2)'), { - type: 'Point', - coordinates: [1e-6, 1e2] - }); - t.equal(parseWKT('POINT(100)'), null); - t.equal(parseWKT('POINT(100, 100)'), null); - t.equal(parseWKT('POINT()'), null); - t.equal(parseWKT('MULTIPOINT()'), null); - t.equal(parseWKT('MULTIPOINT(1)'), null); - t.equal(parseWKT('MULTIPOINT(1 1, 1)'), null); - - t.deepEqual(parseWKT('POINT Z (1 2 3)'), { - type: 'Point', - coordinates: [1, 2, 3] - }); - - t.deepEqual(parseWKT('LINESTRING Z (30 10 1, 10 30 2, 40 40 3)'), { - type: 'LineString', - coordinates: [ - [30, 10, 1], - [10, 30, 2], - [40, 40, 3] - ] - }); - - t.deepEqual(parseWKT('POLYGON Z ((30 10 1, 10 20 2, 20 40 3, 40 40 4, 30 10 5))'), { - type: 'Polygon', - coordinates: [ - [ - [30, 10, 1], - [10, 20, 2], - [20, 40, 3], - [40, 40, 4], - [30, 10, 5] - ] - ] - }); - - t.end(); -}); diff --git a/modules/wkt/tsconfig.json b/modules/wkt/tsconfig.json index 4e11b7c942..f6a2a8083f 100644 --- a/modules/wkt/tsconfig.json +++ b/modules/wkt/tsconfig.json @@ -9,6 +9,7 @@ }, "references": [ {"path": "../loader-utils"}, + {"path": "../gis"}, {"path": "../schema"} ] } diff --git a/modules/wms/package.json b/modules/wms/package.json index fa0604b37d..31762804fa 100644 --- a/modules/wms/package.json +++ b/modules/wms/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/wms", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loaders for the WMS (Web Map Service) standard", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -26,8 +27,15 @@ "OGC" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -35,21 +43,21 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "# ocular-bundle ./src/index.ts --external:{util,fs,path}" + }, + "browser": { + "fs": false, + "path": false }, "dependencies": { "@babel/runtime": "^7.3.1", - "@loaders.gl/images": "4.0.0-alpha.13", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", - "@loaders.gl/xml": "4.0.0-alpha.13", + "@loaders.gl/images": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", + "@loaders.gl/xml": "4.0.3", "@turf/rewind": "^5.1.5", - "deep-strict-equal": "^0.2.0", - "lerc": "^4.0.1" - }, - "devDependencies": { - "xmldom": "0.6.0" + "deep-strict-equal": "^0.2.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/wms/src/bundle.ts b/modules/wms/src/bundle.ts deleted file mode 100644 index 51573415f6..0000000000 --- a/modules/wms/src/bundle.ts +++ /dev/null @@ -1,6 +0,0 @@ -// loaders.gl, MIT license - -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/wms/src/csw-capabilities-loader.ts b/modules/wms/src/csw-capabilities-loader.ts index f70bff9f93..985e15739c 100644 --- a/modules/wms/src/csw-capabilities-loader.ts +++ b/modules/wms/src/csw-capabilities-loader.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {LoaderWithParser} from '@loaders.gl/loader-utils'; import type {XMLLoaderOptions} from '@loaders.gl/xml'; @@ -20,10 +21,9 @@ export type CSWLoaderOptions = XMLLoaderOptions & { /** * Loader for the response to the CSW GetCapability request */ -export const CSWCapabilitiesLoader = { +export const CSWCapabilitiesLoader: LoaderWithParser = { id: 'csw-capabilities', name: 'CSW Capabilities', - module: 'wms', version: VERSION, worker: false, @@ -42,5 +42,3 @@ function testXMLFile(text: string): boolean { // TODO - There could be space first. return text.startsWith(' = { id: 'csw-domain', name: 'CSW Domain', @@ -40,5 +41,3 @@ function testXMLFile(text: string): boolean { // TODO - There could be space first. return text.startsWith(' = { id: 'csw-records', - name: 'CSW Domain', - + name: 'CSW Records', module: 'wms', version: VERSION, worker: false, diff --git a/modules/wms/src/gml-loader.ts b/modules/wms/src/gml-loader.ts index eec092c7c0..e093832e5d 100644 --- a/modules/wms/src/gml-loader.ts +++ b/modules/wms/src/gml-loader.ts @@ -1,6 +1,8 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; +import type {Geometry} from './lib/parsers/gml/parse-gml'; import {parseGML} from './lib/parsers/gml/parse-gml'; // __VERSION__ is injected by babel-plugin-version-inline @@ -14,7 +16,7 @@ export type GMLLoaderOptions = LoaderOptions & { /** * Loader for the response to the GML GetCapability request */ -export const GMLLoader = { +export const GMLLoader: LoaderWithParser = { name: 'GML', id: 'gml', @@ -36,5 +38,3 @@ function testXMLFile(text: string): boolean { // TODO - There could be space first. return text.startsWith(' { - const {type = 'auto'} = props; - const serviceType = type === 'auto' ? guessServiceType(props.url) : type; - switch (serviceType) { - case 'template': - return new ImageService(props); - case 'wms': - return new WMSService(props); - default: - // currently only wms service supported - throw new Error('Not a valid image source type'); - } -} - -/** Guess service type from URL */ -function guessServiceType(url: string): ImageServiceType { - for (const Service of SERVICES) { - if (Service.testURL && Service.testURL(url)) { - return Service.type; - } - } - // If all else fails, guess that this is MS service - return 'wms'; -} diff --git a/modules/wms/src/lib/parsers/csw/parse-csw-capabilities.ts b/modules/wms/src/lib/parsers/csw/parse-csw-capabilities.ts index 7b2bb561f5..7e0f24844c 100644 --- a/modules/wms/src/lib/parsers/csw/parse-csw-capabilities.ts +++ b/modules/wms/src/lib/parsers/csw/parse-csw-capabilities.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {XMLLoaderOptions} from '@loaders.gl/xml'; import {XMLLoader} from '@loaders.gl/xml'; diff --git a/modules/wms/src/lib/parsers/csw/parse-csw-domain.ts b/modules/wms/src/lib/parsers/csw/parse-csw-domain.ts index 64d5989d41..d552bd859f 100644 --- a/modules/wms/src/lib/parsers/csw/parse-csw-domain.ts +++ b/modules/wms/src/lib/parsers/csw/parse-csw-domain.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {XMLLoaderOptions} from '@loaders.gl/xml'; import {XMLLoader} from '@loaders.gl/xml'; diff --git a/modules/wms/src/lib/parsers/csw/parse-csw-records.ts b/modules/wms/src/lib/parsers/csw/parse-csw-records.ts index 2e8b2520c5..ae7cc5e68d 100644 --- a/modules/wms/src/lib/parsers/csw/parse-csw-records.ts +++ b/modules/wms/src/lib/parsers/csw/parse-csw-records.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {XMLLoaderOptions, convertXMLFieldToArrayInPlace} from '@loaders.gl/xml'; import {XMLLoader} from '@loaders.gl/xml'; diff --git a/modules/wms/src/lib/parsers/csw/parse-exception-report.ts b/modules/wms/src/lib/parsers/csw/parse-exception-report.ts index 55e0782078..28ee58e0e9 100644 --- a/modules/wms/src/lib/parsers/csw/parse-exception-report.ts +++ b/modules/wms/src/lib/parsers/csw/parse-exception-report.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // import type {XMLLoaderOptions} from '@loaders.gl/xml'; // import {XMLLoader} from '@loaders.gl/xml'; diff --git a/modules/wms/src/lib/parsers/gml/deep-strict-equal.ts b/modules/wms/src/lib/parsers/gml/deep-strict-equal.ts index da2e682a9d..a387f6f669 100644 --- a/modules/wms/src/lib/parsers/gml/deep-strict-equal.ts +++ b/modules/wms/src/lib/parsers/gml/deep-strict-equal.ts @@ -95,8 +95,8 @@ function objEquiv(a: unknown, b: unknown, strict) { b = pSlice.call(b); return deepStrictEqual(a, b, strict); } - const ka = Object.keys(a); - const kb = Object.keys(b); + const ka = Object.keys(a as object); + const kb = Object.keys(b as object); let key; let i; // having the same number of owned properties (keys incorporates @@ -113,6 +113,7 @@ function objEquiv(a: unknown, b: unknown, strict) { // ~~~possibly expensive deep test for (i = ka.length - 1; i >= 0; i--) { key = ka[i]; + // @ts-ignore if (!deepStrictEqual(a[key], b[key], strict)) return false; } return true; diff --git a/modules/wms/src/lib/parsers/gml/parse-gml.ts b/modules/wms/src/lib/parsers/gml/parse-gml.ts index eb0621eb5f..1114b09ae4 100644 --- a/modules/wms/src/lib/parsers/gml/parse-gml.ts +++ b/modules/wms/src/lib/parsers/gml/parse-gml.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/derhuerst/parse-gml-polygon/blob/master/index.js // under ISC license @@ -29,6 +30,8 @@ function noTransform(...coords) { return coords; } +export type {Geometry}; + export type ParseGMLOptions = { transformCoords?: Function; stride?: 2 | 3 | 4; diff --git a/modules/wms/src/lib/parsers/wms/parse-wms-capabilities.ts b/modules/wms/src/lib/parsers/wms/parse-wms-capabilities.ts index 5f6daeb92f..4cd7fd5bf9 100644 --- a/modules/wms/src/lib/parsers/wms/parse-wms-capabilities.ts +++ b/modules/wms/src/lib/parsers/wms/parse-wms-capabilities.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {XMLLoader} from '@loaders.gl/xml'; import { @@ -37,8 +38,8 @@ export type WMSCapabilities = { requests: Record; /** Information about any exceptions that the service will report (HTTP status != 2xx) */ exceptions?: WMSExceptions; - /** Only if `options.raw`: raw untyped JSON parsed from XML. Can include information not extracted in the typed response. */ - raw?: Record; + /** Only if `options.json`: raw untyped JSON parsed from XML. Can include information not extracted in the typed response. */ + json?: Record; /** Only if `options.xml`, the unparsed XML string can be requested */ xml?: string; }; @@ -147,12 +148,9 @@ export type ParseWMSCapabilitiesOptions = { /** Add inherited layer information to sub layers */ inheritedLayerProps?: boolean; /** Include the "raw" JSON (parsed but untyped, unprocessed XML). May contain additional fields */ - includeRawData?: boolean; + includeRawJSON?: boolean; /** Include the original XML document text. May contain additional information. */ includeXMLText?: boolean; - /** @deprecated Use includeRawData` */ - raw?: boolean; - // xml options are passed through to xml loader }; /** @@ -177,8 +175,8 @@ export function parseWMSCapabilities( // Not yet implemented } - if (options?.includeRawData || options?.raw) { - capabilities.raw = xmlCapabilities; + if (options?.includeRawJSON) { + capabilities.json = xmlCapabilities; } if (options?.includeXMLText) { diff --git a/modules/wms/src/lib/parsers/wms/parse-wms-error.ts b/modules/wms/src/lib/parsers/wms/parse-wms-error.ts index dec4637128..b1292122a8 100644 --- a/modules/wms/src/lib/parsers/wms/parse-wms-error.ts +++ b/modules/wms/src/lib/parsers/wms/parse-wms-error.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {XMLLoader} from '@loaders.gl/xml'; diff --git a/modules/wms/src/lib/parsers/wms/parse-wms-features.ts b/modules/wms/src/lib/parsers/wms/parse-wms-features.ts index fd6e6b5460..05180a89f7 100644 --- a/modules/wms/src/lib/parsers/wms/parse-wms-features.ts +++ b/modules/wms/src/lib/parsers/wms/parse-wms-features.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {XMLLoader} from '@loaders.gl/xml'; diff --git a/modules/wms/src/lib/parsers/wms/parse-wms-layer-description.ts b/modules/wms/src/lib/parsers/wms/parse-wms-layer-description.ts index 4171de6d13..0510f9ab19 100644 --- a/modules/wms/src/lib/parsers/wms/parse-wms-layer-description.ts +++ b/modules/wms/src/lib/parsers/wms/parse-wms-layer-description.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {XMLLoaderOptions} from '@loaders.gl/xml'; import {XMLLoader} from '@loaders.gl/xml'; diff --git a/modules/wms/src/lib/parsers/xml/parse-xml-helpers.ts b/modules/wms/src/lib/parsers/xml/parse-xml-helpers.ts index 971a12a19b..613bbc61e0 100644 --- a/modules/wms/src/lib/parsers/xml/parse-xml-helpers.ts +++ b/modules/wms/src/lib/parsers/xml/parse-xml-helpers.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors /** A single element of an array is not represented as an array in XML */ export function getXMLArray(xmlValue: any): any[] { diff --git a/modules/wms/src/lib/services/create-image-service.ts b/modules/wms/src/lib/services/create-image-service.ts new file mode 100644 index 0000000000..2eb07030d6 --- /dev/null +++ b/modules/wms/src/lib/services/create-image-service.ts @@ -0,0 +1,56 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import {ImageSource, Service} from '@loaders.gl/loader-utils'; +import {ImageServiceProps} from './image-service'; + +export type CreateImageServiceProps = ImageServiceProps & { + type?: string | 'auto'; +}; + +/** + * Creates an image source + * If type is not supplied, will try to automatically detect the the + * @param url URL to the image source + * @param type type of source. if not known, set to 'auto' + * @returns an ImageSource instance + */ +export function createImageService( + props: CreateImageServiceProps, + services: Service[] +): ImageSource { + const {type = 'auto'} = props; + const service: Service | null = + type === 'auto' ? guessServiceType(props.url, services) : getServiceOfType(type, services); + + if (!service) { + throw new Error('Not a valid image source type'); + } + return service.create(props); +} + +/** Guess service type from URL */ +function getServiceOfType(type: string, services: Service[]): Service | null { + // if (type === 'template') { + // return ImageService; + // } + + for (const service of services) { + if (service.type === type) { + return service; + } + } + + return null; +} + +/** Guess service type from URL */ +function guessServiceType(url: string, services: Service[]): Service | null { + for (const service of services) { + if (service.testURL && service.testURL(url)) { + return service; + } + } + + return null; +} diff --git a/modules/wms/src/lib/services/generic/image-service.ts b/modules/wms/src/lib/services/image-service.ts similarity index 83% rename from modules/wms/src/lib/services/generic/image-service.ts rename to modules/wms/src/lib/services/image-service.ts index 9d26be6b08..cedf38f50c 100644 --- a/modules/wms/src/lib/services/generic/image-service.ts +++ b/modules/wms/src/lib/services/image-service.ts @@ -1,11 +1,12 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {LoaderOptions} from '@loaders.gl/loader-utils'; import type {ImageType} from '@loaders.gl/images'; import {ImageLoader} from '@loaders.gl/images'; -import type {ImageSourceMetadata, GetImageParameters} from '../../sources/image-source'; -import {ImageSource} from '../../sources/image-source'; +import type {ImageSourceMetadata, GetImageParameters} from '@loaders.gl/loader-utils'; +import {ImageSource} from '@loaders.gl/loader-utils'; /** Template URL string should contain `${width}` etc which will be substituted. */ export type ImageServiceProps = { @@ -20,8 +21,10 @@ export type ImageServiceProps = { * ImageSource allows template url strings to be used to ad hoc connect to arbitrary image data sources * Accepts a template url string and builds requests URLs */ -export class ImageService extends ImageSource { - static type: 'template' = 'template'; +export abstract class ImageService< + PropsT extends ImageServiceProps = ImageServiceProps +> extends ImageSource { + static type: string = 'template'; static testURL = (url: string): boolean => url.toLowerCase().includes('{'); constructor(props: PropsT) { @@ -46,7 +49,7 @@ export class ImageService extends ImageSource< /** Break up bounding box in east, north, south, west */ protected getGranularParameters(parameters: GetImageParameters): Record { - const [east, north, west, south] = parameters.bbox; + const [[east, north], [west, south]] = parameters.boundingBox; return {...parameters, east, north, south, west}; } diff --git a/modules/wms/src/lib/sources/tile-source.ts b/modules/wms/src/lib/sources/tile-source.ts deleted file mode 100644 index e7ec113662..0000000000 --- a/modules/wms/src/lib/sources/tile-source.ts +++ /dev/null @@ -1,61 +0,0 @@ -// loaders.gl, MIT license - -import type {ImageType} from '@loaders.gl/images'; -import {DataSource, DataSourceProps} from './data-source'; - -/** - * Normalized capabilities of an Image service - * @example - * The WMSService will normalize the response to the WMS `GetCapabilities` data structure extracted from WMS XML response - * into an TileSourceMetadata. - */ -export type TileSourceMetadata = { - name: string; - title?: string; - abstract?: string; - keywords: string[]; - layer: { - name: string; - title?: string; - srs?: string[]; - boundingBox?: [number, number, number, number]; - layers: TileSourceLayer[]; - }; -}; - -export type TileSourceLayer = { - name: string; - title?: string; - srs?: string[]; - boundingBox?: [number, number, number, number]; - layers: TileSourceLayer[]; -}; - -export type GetTileParameters = { - /** Layers to render */ - layers: string | string[]; - /** Styling */ - styles?: unknown; - /** bounding box of the requested map image */ - zoom: number; - /** tile x coordinate */ - x: number; - /** tile y coordinate */ - y: number; - /** srs for the image (not the bounding box) */ - srs?: string; - /** requested format for the return image */ - format?: 'image/png'; -}; - -type TileSourceProps = DataSourceProps; - -/** - * MapTileSource - data sources that allow data to be queried by (geospatial) extents - * @note - * - If geospatial, bounding box is expected to be in web mercator coordinates - */ -export abstract class TileSource extends DataSource { - abstract getMetadata(): Promise; - abstract getTile(parameters: GetTileParameters): Promise; -} diff --git a/modules/wms/src/lib/services/arcgis/arcgis-image-service.ts b/modules/wms/src/services/arcgis/arcgis-image-service.ts similarity index 85% rename from modules/wms/src/lib/services/arcgis/arcgis-image-service.ts rename to modules/wms/src/services/arcgis/arcgis-image-service.ts index ac63f9109c..9dd39b843b 100644 --- a/modules/wms/src/lib/services/arcgis/arcgis-image-service.ts +++ b/modules/wms/src/services/arcgis/arcgis-image-service.ts @@ -1,11 +1,16 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {ImageType} from '@loaders.gl/images'; -import type {ImageSourceMetadata, GetImageParameters} from '../../sources/image-source'; -import type {ImageSourceProps} from '../../sources/image-source'; -import {ImageSource} from '../../sources/image-source'; +import type {Service, ImageSourceMetadata, GetImageParameters} from '@loaders.gl/loader-utils'; -export type ArcGISImageServerProps = ImageSourceProps & { +import type {ImageServiceProps} from '../../lib/services/image-service'; +import {ImageService} from '../../lib/services/image-service'; + +// import type {ImageSourceProps} from '@loaders.gl/loader-utils'; +// import {ImageSource} from '@loaders.gl/loader-utils'; + +export type ArcGISImageServerProps = ImageServiceProps & { url: string; }; @@ -14,12 +19,12 @@ export type ArcGISImageServerProps = ImageSourceProps & { * Note - exports a big API, that could be exposed here if there is a use case * @see https://developers.arcgis.com/rest/services-reference/enterprise/image-service.htm */ -export class ArcGISImageServer extends ImageSource { - static type: 'arcgis-image-server' = 'arcgis-image-server'; - static testURL = (url: string): boolean => url.toLowerCase().includes('ImageServer'); +export class ArcGISImageSource extends ImageService { + data: string; constructor(props: ArcGISImageServerProps) { super(props); + this.data = props.url; } // ImageSource (normalized endpoints) @@ -149,3 +154,9 @@ export class ArcGISImageServer extends ImageSource { } } } + +export const ArcGISImageService: Service = { + type: 'arcgis-image-server', + testURL: (url: string): boolean => url.toLowerCase().includes('ImageServer'), + create: (props: ArcGISImageServerProps): ArcGISImageSource => new ArcGISImageSource(props) +}; diff --git a/modules/wms/src/lib/services/arcgis/arcgis-server.ts b/modules/wms/src/services/arcgis/arcgis-server.ts similarity index 97% rename from modules/wms/src/lib/services/arcgis/arcgis-server.ts rename to modules/wms/src/services/arcgis/arcgis-server.ts index 18394e3ea6..20973c7da5 100644 --- a/modules/wms/src/lib/services/arcgis/arcgis-server.ts +++ b/modules/wms/src/services/arcgis/arcgis-server.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors export type Service = {name: string; type: string; url: string}; diff --git a/modules/wms/src/services/create-image-source.ts b/modules/wms/src/services/create-image-source.ts new file mode 100644 index 0000000000..8cb2bf8593 --- /dev/null +++ b/modules/wms/src/services/create-image-source.ts @@ -0,0 +1,32 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {Service, ImageSource} from '@loaders.gl/loader-utils'; +// import {ImageService} from '../lib/services/image-service'; +import {ImageServiceProps} from '../lib/services/image-service'; +import {createImageService, CreateImageServiceProps} from '../lib/services/create-image-service'; + +import type {WMSSourceProps} from './ogc/wms-service'; +import {WMSService} from './ogc/wms-service'; +import {ArcGISImageService} from './arcgis/arcgis-image-service'; + +export type ImageServiceType = 'wms' | 'arcgis-image-server' | 'template'; + +const SERVICES: Service[] = [WMSService, ArcGISImageService]; + +type CreateImageSourceProps = CreateImageServiceProps & + ImageServiceProps & + WMSSourceProps & { + type?: ImageServiceType | 'auto'; + }; + +/** + * Creates an image source + * If type is not supplied, will try to automatically detect the the + * @param url URL to the image source + * @param type type of source. if not known, set to 'auto' + * @returns an ImageSource instance + */ +export function createImageSource(props: CreateImageSourceProps): ImageSource { + return createImageService(props, SERVICES); +} diff --git a/modules/wms/src/lib/services/ogc/csw-service.ts b/modules/wms/src/services/ogc/csw-service.ts similarity index 89% rename from modules/wms/src/lib/services/ogc/csw-service.ts rename to modules/wms/src/services/ogc/csw-service.ts index 4c6c7505d1..2159d6f82f 100644 --- a/modules/wms/src/lib/services/ogc/csw-service.ts +++ b/modules/wms/src/services/ogc/csw-service.ts @@ -1,20 +1,21 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors /* eslint-disable camelcase */ -import type {DataSourceProps} from '../../sources/data-source'; -import {DataSource} from '../../sources/data-source'; +import type {DataSourceProps} from '@loaders.gl/loader-utils'; +import {DataSource} from '@loaders.gl/loader-utils'; -import type {CSWCapabilities} from '../../../csw-capabilities-loader'; -import {CSWCapabilitiesLoader} from '../../../csw-capabilities-loader'; +import type {CSWCapabilities} from '../../csw-capabilities-loader'; +import {CSWCapabilitiesLoader} from '../../csw-capabilities-loader'; -import type {CSWRecords} from '../../../csw-records-loader'; -import {CSWRecordsLoader} from '../../../csw-records-loader'; +import type {CSWRecords} from '../../csw-records-loader'; +import {CSWRecordsLoader} from '../../csw-records-loader'; -import type {CSWDomain} from '../../../csw-domain-loader'; -import {CSWDomainLoader} from '../../../csw-domain-loader'; +import type {CSWDomain} from '../../csw-domain-loader'; +import {CSWDomainLoader} from '../../csw-domain-loader'; -import {WMSErrorLoader as CSWErrorLoader} from '../../../wms-error-loader'; +import {WMSErrorLoader as CSWErrorLoader} from '../../wms-error-loader'; type CSWCommonParameters = { /** In case the endpoint supports multiple services */ @@ -63,10 +64,12 @@ export type CSWServiceProps = DataSourceProps & { * @note Only the URL parameter conversion is supported. XML posts are not supported. */ export class CSWService extends DataSource { - static type: 'csw' = 'csw'; + static readonly type = 'csw'; static testURL = (url: string): boolean => url.toLowerCase().includes('csw'); capabilities: CSWCapabilities | null = null; + data: string; + url: string; /** A list of loaders used by the CSWService methods */ readonly loaders = [CSWErrorLoader, CSWCapabilitiesLoader]; @@ -74,6 +77,8 @@ export class CSWService extends DataSource { /** Create a CSWService */ constructor(props: CSWServiceProps) { super(props); + this.url = props.url; + this.data = props.url; } async getMetadata(): Promise { @@ -244,14 +249,14 @@ export class CSWService extends DataSource { protected _checkResponse(response: Response, arrayBuffer: ArrayBuffer): void { const contentType = response.headers['content-type']; if (!response.ok || CSWErrorLoader.mimeTypes.includes(contentType)) { - const error = CSWErrorLoader.parseSync(arrayBuffer, this.props.loadOptions); + const error = CSWErrorLoader.parseSync?.(arrayBuffer, this.props.loadOptions); throw new Error(error); } } /** Error situation detected */ protected _parseError(arrayBuffer: ArrayBuffer): Error { - const error = CSWErrorLoader.parseSync(arrayBuffer, this.props.loadOptions); + const error = CSWErrorLoader.parseSync?.(arrayBuffer, this.props.loadOptions); return new Error(error); } } diff --git a/modules/wms/src/lib/services/ogc/wms-service.ts b/modules/wms/src/services/ogc/wms-service.ts similarity index 86% rename from modules/wms/src/lib/services/ogc/wms-service.ts rename to modules/wms/src/services/ogc/wms-service.ts index 0fa19917c2..13b78f3cfe 100644 --- a/modules/wms/src/lib/services/ogc/wms-service.ts +++ b/modules/wms/src/services/ogc/wms-service.ts @@ -1,28 +1,35 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors /* eslint-disable camelcase */ import type {ImageType} from '@loaders.gl/images'; import {ImageLoader} from '@loaders.gl/images'; import {mergeLoaderOptions} from '@loaders.gl/loader-utils'; -import type {ImageSourceMetadata, GetImageParameters} from '../../sources/image-source'; -import type {ImageSourceProps} from '../../sources/image-source'; -import {ImageSource} from '../../sources/image-source'; +import type {Service, ImageSourceMetadata, GetImageParameters} from '@loaders.gl/loader-utils'; +import {ImageSource} from '@loaders.gl/loader-utils'; +import type {ImageServiceProps} from '../../lib/services/image-service'; -import type {WMSCapabilities} from '../../../wms-capabilities-loader'; -import type {WMSFeatureInfo} from '../../../wip/wms-feature-info-loader'; -import type {WMSLayerDescription} from '../../../wip/wms-layer-description-loader'; +import type {WMSCapabilities} from '../../wms-capabilities-loader'; +import type {WMSFeatureInfo} from '../../wip/wms-feature-info-loader'; +import type {WMSLayerDescription} from '../../wip/wms-layer-description-loader'; -import {WMSCapabilitiesLoader} from '../../../wms-capabilities-loader'; -import {WMSFeatureInfoLoader} from '../../../wip/wms-feature-info-loader'; -import {WMSLayerDescriptionLoader} from '../../../wip/wms-layer-description-loader'; +import {WMSCapabilitiesLoader} from '../../wms-capabilities-loader'; +import {WMSFeatureInfoLoader} from '../../wip/wms-feature-info-loader'; +import {WMSLayerDescriptionLoader} from '../../wip/wms-layer-description-loader'; -import type {WMSLoaderOptions} from '../../../wms-error-loader'; -import {WMSErrorLoader} from '../../../wms-error-loader'; +import type {WMSLoaderOptions} from '../../wms-error-loader'; +import {WMSErrorLoader} from '../../wms-error-loader'; + +export const WMSService: Service = { + type: 'wms', + testURL: (url: string): boolean => url.toLowerCase().includes('wms'), + create: (props: WMSSourceProps) => new WMSSource(props) +}; /** * "Static" WMS parameters (not viewport or selected pixel dependent) - * These can be provided as defaults in the WMSService constructor + * These can be provided as defaults in the WMSSource constructor */ export type WMSParameters = { /** WMS version (all requests) */ @@ -40,7 +47,7 @@ export type WMSParameters = { info_format?: 'text/plain' | 'application/geojson' | 'application/vnd.ogc.gml'; /** Styling - Not yet supported */ styles?: unknown; - /** Any additional parameters specific to this WMSService (GetMap) */ + /** Any additional parameters specific to this WMSSource (GetMap) */ transparent?: boolean; /** If layer supports time dimension */ time?: string; @@ -58,7 +65,9 @@ export type WMSGetCapabilitiesParameters = { export type WMSGetMapParameters = { /** In case the endpoint supports multiple WMS versions */ version?: '1.3.0' | '1.1.1'; - /** bounding box of the requested map image */ + /** bounding box of the requested map image `[[w, s], [e, n]]` */ + // boundingBox: [min: [x: number, y: number], max: [x: number, y: number]]; + /** bounding box of the requested map image @deprecated Use .boundingBox */ bbox: [number, number, number, number]; /** pixel width of returned image */ width: number; @@ -80,17 +89,17 @@ export type WMSGetMapParameters = { elevation?: string; }; -/** GetMap parameters that are specific to the current view */ -export type WMSGetMapViewParameters = { - /** pixel width of returned image */ - width: number; - /** pixels */ - height: number; - /** bounding box of the requested map image */ - bbox: [number, number, number, number]; - /** Coordinate Reference System for the image (not bounding box). can be provided in service constructor. */ - crs?: string; -}; +// /** GetMap parameters that are specific to the current view */ +// export type WMSGetMapViewParameters = { +// /** pixel width of returned image */ +// width: number; +// /** pixels */ +// height: number; +// /** bounding box of the requested map image */ +// bbox: [number, number, number, number]; +// /** Coordinate Reference System for the image (not bounding box). can be provided in service constructor. */ +// crs?: string; +// }; /** * Parameters for GetFeatureInfo @@ -152,12 +161,11 @@ export type WMSGetLegendGraphicParameters = { // /** Properties for creating a enw WMS service */ -export type WMSServiceProps = ImageSourceProps & { +export type WMSSourceProps = ImageServiceProps & { /** Base URL to the service */ url: string; /** In 1.3.0, replaces references to EPSG:4326 with CRS:84 */ substituteCRS84?: boolean; - /** Default WMS parameters. If not provided here, must be provided in the various request */ wmsParameters?: WMSParameters; /** Any additional service specific parameters */ @@ -165,18 +173,16 @@ export type WMSServiceProps = ImageSourceProps & { }; /** - * The WMSService class provides + * The WMSSource class provides * - provides type safe methods to form URLs to a WMS service * - provides type safe methods to query and parse results (and errors) from a WMS service * - implements the ImageService interface * @note Only the URL parameter conversion is supported. XML posts are not supported. */ -export class WMSService extends ImageSource { - static type: 'wms' = 'wms'; - static testURL = (url: string): boolean => url.toLowerCase().includes('wms'); - +export class WMSSource extends ImageSource { /** Base URL to the service */ readonly url: string; + readonly data: string; /** In WMS 1.3.0, replaces references to EPSG:4326 with CRS:84. But not always supported. Default: false */ substituteCRS84: boolean; @@ -190,17 +196,8 @@ export class WMSService extends ImageSource { capabilities: WMSCapabilities | null = null; - /** A list of loaders used by the WMSService methods */ - readonly loaders = [ - ImageLoader, - WMSErrorLoader, - WMSCapabilitiesLoader, - WMSFeatureInfoLoader, - WMSLayerDescriptionLoader - ]; - - /** Create a WMSService */ - constructor(props: WMSServiceProps) { + /** Create a WMSSource */ + constructor(props: WMSSourceProps) { super(props); // TODO - defaults such as version, layers etc could be extracted from a base URL with parameters @@ -208,6 +205,7 @@ export class WMSService extends ImageSource { // const {baseUrl, parameters} = this._parseWMSUrl(props.url); this.url = props.url; + this.data = props.url; this.substituteCRS84 = props.substituteCRS84 ?? false; this.flipCRS = ['EPSG:4326']; @@ -229,13 +227,14 @@ export class WMSService extends ImageSource { this.vendorParameters = props.vendorParameters || {}; } - // ImageSource implementation + // ImageService implementation async getMetadata(): Promise { const capabilities = await this.getCapabilities(); return this.normalizeMetadata(capabilities); } async getImage(parameters: GetImageParameters): Promise { + // @ts-expect-error return await this.getMap(parameters); } @@ -370,6 +369,7 @@ export class WMSService extends ImageSource { wmsParameters: WMSGetFeatureInfoParameters, vendorParameters?: Record ): string { + wmsParameters = this._getWMS130Parameters(wmsParameters); const options: Required = { version: this.wmsParameters.version, // query_layers: [], @@ -476,7 +476,7 @@ export class WMSService extends ImageSource { return newParameters; } - // eslint-disable-complexity + // eslint-disable-next-line complexity _getURLParameter(key: string, value: unknown, wmsParameters: WMSParameters): string { // Substitute by key switch (key) { @@ -498,6 +498,16 @@ export class WMSService extends ImageSource { } break; + case 'boundingBox': + // Coordinate order is flipped for certain CRS in WMS 1.3.0 + const boundingBox = value as [[number, number], [number, number]]; + let bbox2: number[] | null = [...boundingBox[0], ...boundingBox[1]]; + bbox2 = this._flipBoundingBox(boundingBox, wmsParameters); + if (bbox2) { + value = bbox2; + } + break; + case 'bbox': // Coordinate order is flipped for certain CRS in WMS 1.3.0 const bbox = this._flipBoundingBox(value, wmsParameters); @@ -555,14 +565,14 @@ export class WMSService extends ImageSource { const loadOptions = mergeLoaderOptions(this.loadOptions, { wms: {throwOnError: true} }); - const error = WMSErrorLoader.parseSync(arrayBuffer, loadOptions); + const error = WMSErrorLoader.parseSync?.(arrayBuffer, loadOptions); throw new Error(error); } } /** Error situation detected */ protected _parseError(arrayBuffer: ArrayBuffer): Error { - const error = WMSErrorLoader.parseSync(arrayBuffer, this.loadOptions); + const error = WMSErrorLoader.parseSync?.(arrayBuffer, this.loadOptions); return new Error(error); } } diff --git a/modules/wms/src/lib/wfs/parse-wfs.ts b/modules/wms/src/wip/lib/wfs/parse-wfs.ts similarity index 100% rename from modules/wms/src/lib/wfs/parse-wfs.ts rename to modules/wms/src/wip/lib/wfs/parse-wfs.ts diff --git a/modules/wms/src/lib/wmts/parse-wmts.ts b/modules/wms/src/wip/lib/wmts/parse-wmts.ts similarity index 100% rename from modules/wms/src/lib/wmts/parse-wmts.ts rename to modules/wms/src/wip/lib/wmts/parse-wmts.ts diff --git a/modules/wms/src/wip/arcgis-feature-service.ts b/modules/wms/src/wip/services/arcgis-feature-service.ts similarity index 100% rename from modules/wms/src/wip/arcgis-feature-service.ts rename to modules/wms/src/wip/services/arcgis-feature-service.ts diff --git a/modules/wms/src/lib/services/ogc/wmts-service.ts.disabled b/modules/wms/src/wip/services/wmts-service.ts.disabled similarity index 100% rename from modules/wms/src/lib/services/ogc/wmts-service.ts.disabled rename to modules/wms/src/wip/services/wmts-service.ts.disabled diff --git a/modules/wms/src/wip/wfs-capabilities-loader.ts b/modules/wms/src/wip/wfs-capabilities-loader.ts index 079265653b..0876945fe8 100644 --- a/modules/wms/src/wip/wfs-capabilities-loader.ts +++ b/modules/wms/src/wip/wfs-capabilities-loader.ts @@ -17,7 +17,7 @@ export type WFSLoaderOptions = LoaderOptions & { /** * Loader for the response to the WFS GetCapability request */ -export const WFSCapabilitiesLoader = { +export const WFSCapabilitiesLoader: LoaderWithParser = { id: 'wfs-capabilities', name: 'WFS Capabilities', @@ -28,7 +28,7 @@ export const WFSCapabilitiesLoader = { mimeTypes: ['application/vnd.ogc.wfs_xml', 'application/xml', 'text/xml'], testText: testXMLFile, options: { - wms: {} + wfs: {} }, parse: async (arrayBuffer: ArrayBuffer, options?: WFSLoaderOptions) => parseWFSCapabilities(new TextDecoder().decode(arrayBuffer), options), diff --git a/modules/wms/src/wip/wms-feature-info-loader.ts b/modules/wms/src/wip/wms-feature-info-loader.ts index 9446325064..cb8f3359dd 100644 --- a/modules/wms/src/wip/wms-feature-info-loader.ts +++ b/modules/wms/src/wip/wms-feature-info-loader.ts @@ -12,7 +12,8 @@ export {WMSFeatureInfo}; /** * Loader for the response to the WMS GetFeatureInfo request */ -export const WMSFeatureInfoLoader = { +// @ts-expect-error +export const WMSFeatureInfoLoader: LoaderWithParser = { ...WMSCapabilitiesLoader, id: 'wms-feature-info', diff --git a/modules/wms/src/wms-capabilities-loader.ts b/modules/wms/src/wms-capabilities-loader.ts index 4d0d528c0c..4e3b98883c 100644 --- a/modules/wms/src/wms-capabilities-loader.ts +++ b/modules/wms/src/wms-capabilities-loader.ts @@ -1,8 +1,9 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {LoaderWithParser} from '@loaders.gl/loader-utils'; import type {XMLLoaderOptions} from '@loaders.gl/xml'; -import {parseWMSCapabilities} from './lib/parsers/wms/parse-wms-capabilities'; +import {WMSCapabilities, parseWMSCapabilities} from './lib/parsers/wms/parse-wms-capabilities'; // __VERSION__ is injected by babel-plugin-version-inline // @ts-ignore TS2304: Cannot find name '__VERSION__'. @@ -23,18 +24,20 @@ export type WMSCapabilitiesLoaderOptions = XMLLoaderOptions & { /** Add inherited layer information to sub layers */ inheritedLayerProps?: boolean; /** Include the "raw" JSON (parsed but untyped, unprocessed XML). May contain additional fields */ - includeRawData?: boolean; + includeRawJSON?: boolean; /** Include the original XML document text. May contain additional information. */ includeXMLText?: boolean; - /** @deprecated Use options.includeRawData` */ - raw?: boolean; }; }; /** * Loader for the response to the WMS GetCapability request */ -export const WMSCapabilitiesLoader = { +export const WMSCapabilitiesLoader: LoaderWithParser< + WMSCapabilities, + never, + WMSCapabilitiesLoaderOptions +> = { id: 'wms-capabilities', name: 'WMS Capabilities', @@ -59,5 +62,3 @@ function testXMLFile(text: string): boolean { // TODO - There could be space first. return text.startsWith(' = { id: 'wms-error', name: 'WMS Error', @@ -55,5 +56,3 @@ function parseTextSync(text: string, options?: WMSLoaderOptions): string { } return message; } - -export const _typecheckWMSErrorLoader: LoaderWithParser = WMSErrorLoader; diff --git a/modules/wms/test/csw/csw-capabilities-loader.spec.ts b/modules/wms/test/csw/csw-capabilities-loader.spec.ts index 96ff2e8511..69ed571aca 100644 --- a/modules/wms/test/csw/csw-capabilities-loader.spec.ts +++ b/modules/wms/test/csw/csw-capabilities-loader.spec.ts @@ -1,16 +1,17 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; -import {CSWCapabilitiesLoader, CSWCapabilities} from '@loaders.gl/wms'; +import {CSWCapabilitiesLoader} from '@loaders.gl/wms'; import {load} from '@loaders.gl/core'; const CSW_CAPABILITIES_URL = '@loaders.gl/wms/test/data/csw/get-capabilities.xml'; test('CSWCapabilitiesLoader#forecasts.xml', async (t) => { - const capabilities = (await load(CSW_CAPABILITIES_URL, CSWCapabilitiesLoader)) as CSWCapabilities; - t.comment(JSON.stringify(capabilities)); + const capabilities = await load(CSW_CAPABILITIES_URL, CSWCapabilitiesLoader); + // t.comment(JSON.stringify(capabilities)); t.equal(typeof capabilities, 'object', 'parsed'); // t.equal(capabilities.layer.layers[2]?.name, 'world_rivers', 'contents'); diff --git a/modules/wms/test/csw/csw-domain-loader.spec.ts b/modules/wms/test/csw/csw-domain-loader.spec.ts index 517c40e530..a788d74dde 100644 --- a/modules/wms/test/csw/csw-domain-loader.spec.ts +++ b/modules/wms/test/csw/csw-domain-loader.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/chrelad/openlayers/blob/master/tests/Format/ // under OpenLayers license (only used for test cases) @@ -7,7 +8,7 @@ import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; -import {CSWDomainLoader, CSWDomain} from '@loaders.gl/wms'; +import {CSWDomainLoader} from '@loaders.gl/wms'; import {parse} from '@loaders.gl/core'; // const CSW_REQUEST_2_0_2 = @@ -27,7 +28,7 @@ const CSW_RESPONSE_2_0_2 = '' + ''; test('CSWGetDomainLoader', async (t) => { - const domain = (await parse(CSW_RESPONSE_2_0_2, CSWDomainLoader)) as CSWDomain; + const domain = await parse(CSW_RESPONSE_2_0_2, CSWDomainLoader); t.comment(JSON.stringify(domain)); const domainValues = domain.domainValues; diff --git a/modules/wms/test/csw/csw-records-loader.spec.ts b/modules/wms/test/csw/csw-records-loader.spec.ts index f8e13b68d4..0d44862bff 100644 --- a/modules/wms/test/csw/csw-records-loader.spec.ts +++ b/modules/wms/test/csw/csw-records-loader.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/chrelad/openlayers/blob/master/tests/Format/ // under OpenLayers license (only used for test cases) @@ -7,7 +8,7 @@ import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; -import {CSWRecordsLoader, CSWRecords} from '@loaders.gl/wms'; +import {CSWRecordsLoader} from '@loaders.gl/wms'; import {parse} from '@loaders.gl/core'; // const CSW_REQUEST_2_0_2 = @@ -53,7 +54,7 @@ const CSW_RESPONSE_2_0_2 = '' + ''; test('CSWGetRecordsLoader', async (t) => { - const cswRecords = (await parse(CSW_RESPONSE_2_0_2, CSWRecordsLoader)) as CSWRecords; + const cswRecords = await parse(CSW_RESPONSE_2_0_2, CSWRecordsLoader); t.comment(JSON.stringify(cswRecords)); const searchStatus = cswRecords.searchStatus; diff --git a/modules/wms/test/data/gml/v3/tests.ts b/modules/wms/test/data/gml/v3/tests.ts index ca5292c8de..38d0f04b45 100644 --- a/modules/wms/test/data/gml/v3/tests.ts +++ b/modules/wms/test/data/gml/v3/tests.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // @ts-nocheck diff --git a/modules/wms/test/data/wms/errors.ts b/modules/wms/test/data/wms/errors.ts index b7d35c37f2..a92cf7853c 100644 --- a/modules/wms/test/data/wms/errors.ts +++ b/modules/wms/test/data/wms/errors.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors export const ERROR_TEST_CASES: {xml: string; parsed: string}[] = [ { diff --git a/modules/wms/test/gml/gml-loader.spec.ts b/modules/wms/test/gml/gml-loader.spec.ts index 8367a0c748..54edfb5477 100644 --- a/modules/wms/test/gml/gml-loader.spec.ts +++ b/modules/wms/test/gml/gml-loader.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/chrelad/openlayers/blob/master/tests/Format/GML/v3.html // under OpenLayers license (only used for test cases) diff --git a/modules/wms/test/index.ts b/modules/wms/test/index.ts index 0c91e5d6e5..909e1846a7 100644 --- a/modules/wms/test/index.ts +++ b/modules/wms/test/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // CSW - Catalog Service for the Web @@ -26,11 +27,6 @@ import './wfs/wfs-capabilities-loader.spec'; import './gml/gml-loader.spec'; -// LERC - Limited Error Raster Compression - -// import './lerc/lerc-sanity.spec'; -// import './lerc/lerc-level2.spec'; - // Services import './services/wms-service.spec'; diff --git a/modules/wms/test/services/arcgis-server.spec.ts b/modules/wms/test/services/arcgis-server.spec.ts index 89a60842b7..60b800c820 100644 --- a/modules/wms/test/services/arcgis-server.spec.ts +++ b/modules/wms/test/services/arcgis-server.spec.ts @@ -1,11 +1,12 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; -import {_ArcGISImageServer as ArcGISImageServer} from '@loaders.gl/wms'; +import {_ArcGISImageService as ArcGISImageService} from '@loaders.gl/wms'; test('ArcGISImageService#test cases', async (t) => { - t.ok(ArcGISImageServer); + t.ok(ArcGISImageService); t.end(); }); diff --git a/modules/wms/test/services/wms-service.spec.ts b/modules/wms/test/services/wms-service.spec.ts index c7b06468da..33d5a3e139 100644 --- a/modules/wms/test/services/wms-service.spec.ts +++ b/modules/wms/test/services/wms-service.spec.ts @@ -1,15 +1,16 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; import {withFetchMock, mockResults, requestInits} from '../test-utils/fetch-spy'; -import {WMSService} from '@loaders.gl/wms'; +import {WMSSource} from '@loaders.gl/wms'; const WMS_SERVICE_URL = 'https:/mock-wms-service'; const WMS_VERSION = '1.3.0'; -test('WMSService#', async (t) => { - const wmsService = new WMSService({url: WMS_SERVICE_URL}); +test('WMSSource#', async (t) => { + const wmsService = new WMSSource({url: WMS_SERVICE_URL}); const getCapabilitiesUrl = wmsService.getCapabilitiesURL(); t.equal( @@ -20,8 +21,8 @@ test('WMSService#', async (t) => { t.end(); }); -test('WMSService#getMapURL', async (t) => { - let wmsService = new WMSService({url: WMS_SERVICE_URL}); +test('WMSSource#getMapURL', async (t) => { + let wmsService = new WMSSource({url: WMS_SERVICE_URL}); let getMapUrl = wmsService.getMapURL({ width: 800, height: 600, @@ -35,7 +36,7 @@ test('WMSService#getMapURL', async (t) => { 'getMapURL layers in params' ); - wmsService = new WMSService({ + wmsService = new WMSSource({ url: WMS_SERVICE_URL, wmsParameters: {layers: ['oms'], crs: 'EPSG:3857'} }); @@ -52,15 +53,15 @@ test('WMSService#getMapURL', async (t) => { t.end(); }); -test('WMSService#getFeatureInfoURL', async (t) => { - // const wmsService = new WMSService({url: WMS_SERVICE_URL}); +test('WMSSource#getFeatureInfoURL', async (t) => { + // const wmsService = new WMSSource({url: WMS_SERVICE_URL}); // const getFeatureInfoUrl = wmsService.getFeatureInfoURL({x: 400, y: 300}); // t.equal(getFeatureInfoUrl, 'https:/mock-wms-service?REQUEST=GetFeatureInfo', 'getFeatureInfoURL'); t.end(); }); -test('WMSService#describeLayerURL', async (t) => { - const wmsService = new WMSService({url: WMS_SERVICE_URL}); +test('WMSSource#describeLayerURL', async (t) => { + const wmsService = new WMSSource({url: WMS_SERVICE_URL}); const describeLayerUrl = wmsService.describeLayerURL({}); t.equal( describeLayerUrl, @@ -70,8 +71,8 @@ test('WMSService#describeLayerURL', async (t) => { t.end(); }); -test('WMSService#getLegendGraphicURL', async (t) => { - const wmsService = new WMSService({url: WMS_SERVICE_URL}); +test('WMSSource#getLegendGraphicURL', async (t) => { + const wmsService = new WMSSource({url: WMS_SERVICE_URL}); const getLegendGraphicUrl = wmsService.getLegendGraphicURL({}); t.equal( getLegendGraphicUrl, @@ -82,8 +83,8 @@ test('WMSService#getLegendGraphicURL', async (t) => { t.end(); }); -test('WMSService#WMS versions', async (t) => { - const wms111Service = new WMSService({ +test('WMSSource#WMS versions', async (t) => { + const wms111Service = new WMSSource({ url: WMS_SERVICE_URL, wmsParameters: {version: '1.1.1', layers: ['oms']} }); @@ -97,7 +98,7 @@ test('WMSService#WMS versions', async (t) => { 'https:/mock-wms-service?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image/png&LAYERS=oms&STYLES=&SRS=EPSG:4326&WIDTH=800&HEIGHT=600&BBOX=30,70,35,75', 'getMapURL replaces CRS with SRS in WMS 1.1.1' ); - const wms130Service = new WMSService({ + const wms130Service = new WMSSource({ url: WMS_SERVICE_URL, substituteCRS84: true, wmsParameters: {version: '1.3.0', layers: ['oms']} @@ -116,9 +117,9 @@ test('WMSService#WMS versions', async (t) => { }); // TODO - move to image-source.spec.ts -test('WMSService#fetch override', async (t) => { +test('WMSSource#fetch override', async (t) => { const loadOptions = {fetch: {headers: {Authorization: 'Bearer abc'}}}; - const wmsService = new WMSService({url: WMS_SERVICE_URL, loadOptions, substituteCRS84: true}); + const wmsService = new WMSSource({url: WMS_SERVICE_URL, loadOptions, substituteCRS84: true}); const generatedUrl = wmsService.getFeatureInfoURL({ x: 1, y: 1, diff --git a/modules/wms/test/test-utils/fetch-spy.ts b/modules/wms/test/test-utils/fetch-spy.ts index 457c6b3290..1f44ed9308 100644 --- a/modules/wms/test/test-utils/fetch-spy.ts +++ b/modules/wms/test/test-utils/fetch-spy.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors /** Stores a reference to the intercepted requestInit object under the url */ export const requestInits: Record = {}; @@ -14,6 +15,7 @@ export async function fetchSpy( if (typeof input === 'string') { requestInits[input] = init; } + // @ts-ignore return originalFetch(input, init); } diff --git a/modules/wms/test/wfs/wfs-capabilities-loader.spec.ts b/modules/wms/test/wfs/wfs-capabilities-loader.spec.ts index ee6e23d37d..beba838c03 100644 --- a/modules/wms/test/wfs/wfs-capabilities-loader.spec.ts +++ b/modules/wms/test/wfs/wfs-capabilities-loader.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/chrelad/openlayers/blob/master/tests/Format/WFSCapabilities/v1_0_0.html // under OpenLayers license (only used for test cases) @@ -9,17 +10,17 @@ import test from 'tape-promise/tape'; // @ts-nocheck -import {_WFSCapabilitiesLoader as WFSCapabilitiesLoader, _WFSCapabilities as WFSCapabilities} from '@loaders.gl/wms'; +import {_WFSCapabilitiesLoader as WFSCapabilitiesLoader} from '@loaders.gl/wms'; import {load} from '@loaders.gl/core'; const WFS_CAPABILITIES_RESPONSE_URL = '@loaders.gl/wms/test/data/wmts/get-capabilities-response.xml'; test('WFSCapabilitiesLoader#response.xml', async (t) => { - const capabilities = (await load( + const capabilities = await load( WFS_CAPABILITIES_RESPONSE_URL, WFSCapabilitiesLoader - )) as WFSCapabilities; + ); t.equal(typeof capabilities, 'object', 'parsed'); @@ -29,10 +30,10 @@ test('WFSCapabilitiesLoader#response.xml', async (t) => { // TODO - copied from WMTS test.skip('WFSCapabilitiesLoader#response.xml#OWS', async (t) => { - const capabilities = (await load( + const capabilities = await load( WFS_CAPABILITIES_RESPONSE_URL, WFSCapabilitiesLoader - )) as WFSCapabilities; + ); // ows:ServiceIdentification const serviceIdentification = capabilities.serviceIdentification; @@ -129,10 +130,10 @@ test.skip('WFSCapabilitiesLoader#response.xml#OWS', async (t) => { // eslint-disable-next-line max-statements test.skip('WFSCapabilitiesLoader#response.xml#layers', async (t) => { - const capabilities = (await load( + const capabilities = await load( WFS_CAPABILITIES_RESPONSE_URL, WFSCapabilitiesLoader - )) as WFSCapabilities; + ); const contents = capabilities.contents; diff --git a/modules/wms/test/wms/wms-capabilities-loader.spec.ts b/modules/wms/test/wms/wms-capabilities-loader.spec.ts index 12a3c32ead..2b0647e4c2 100644 --- a/modules/wms/test/wms/wms-capabilities-loader.spec.ts +++ b/modules/wms/test/wms/wms-capabilities-loader.spec.ts @@ -1,9 +1,10 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; -import {WMSCapabilitiesLoader, WMSCapabilities} from '@loaders.gl/wms'; +import {WMSCapabilitiesLoader} from '@loaders.gl/wms'; import {load} from '@loaders.gl/core'; const WMS_ANALYSES_URL = '@loaders.gl/wms/test/data/wms/get-capabilities/analyses.xml'; @@ -15,7 +16,7 @@ const WMS_WWA_URL = '@loaders.gl/wms/test/data/wms/get-capabilities/wwa.xml'; const WMS_ADHOC_URL = '@loaders.gl/wms/test/data/wms/get-capabilities/?.xml'; test('WMSCapabilitiesLoader#forecasts.xml', async (t) => { - const capabilities = (await load(WMS_FORECASTS_URL, WMSCapabilitiesLoader)) as WMSCapabilities; + const capabilities = await load(WMS_FORECASTS_URL, WMSCapabilitiesLoader); t.equal(typeof capabilities, 'object', 'parsed'); t.equal(capabilities.version, '1.1.1', 'version'); @@ -25,7 +26,7 @@ test('WMSCapabilitiesLoader#forecasts.xml', async (t) => { }); test('WMSCapabilitiesLoader#obs.xml', async (t) => { - const capabilities = (await load(WMS_OBS_URL, WMSCapabilitiesLoader)) as WMSCapabilities; + const capabilities = await load(WMS_OBS_URL, WMSCapabilitiesLoader); t.equal(typeof capabilities, 'object', 'parsed'); t.equal(capabilities.version, '1.1.1', 'version'); @@ -34,7 +35,7 @@ test('WMSCapabilitiesLoader#obs.xml', async (t) => { }); test('WMSCapabilitiesLoader#wwa.xml', async (t) => { - const capabilities = (await load(WMS_WWA_URL, WMSCapabilitiesLoader)) as WMSCapabilities; + const capabilities = await load(WMS_WWA_URL, WMSCapabilitiesLoader); t.equal(typeof capabilities, 'object', 'parsed'); t.equal(capabilities.version, '1.1.1', 'version'); @@ -44,7 +45,7 @@ test('WMSCapabilitiesLoader#wwa.xml', async (t) => { }); test('WMSCapabilitiesLoader#analyses.xml', async (t) => { - const capabilities = (await load(WMS_ANALYSES_URL, WMSCapabilitiesLoader)) as WMSCapabilities; + const capabilities = await load(WMS_ANALYSES_URL, WMSCapabilitiesLoader); t.equal(typeof capabilities, 'object', 'parsed'); t.equal(capabilities.version, '1.1.1', 'version'); @@ -54,7 +55,7 @@ test('WMSCapabilitiesLoader#analyses.xml', async (t) => { }); test('WMSCapabilitiesLoader#dmsp.xml', async (t) => { - const capabilities = (await load(WMS_DMSP_URL, WMSCapabilitiesLoader)) as WMSCapabilities; + const capabilities = await load(WMS_DMSP_URL, WMSCapabilitiesLoader); t.equal(typeof capabilities, 'object', 'parsed'); @@ -69,7 +70,7 @@ test('WMSCapabilitiesLoader#dmsp.xml', async (t) => { // For adhoc testing (non-committed XML files or direct from server) test.skip('WMSCapabilitiesLoader#ad-hoc-test', async (t) => { - const capabilities = (await load(WMS_ADHOC_URL, WMSCapabilitiesLoader)) as WMSCapabilities; + const capabilities = await load(WMS_ADHOC_URL, WMSCapabilitiesLoader); t.equal(typeof capabilities, 'object', 'parsed'); t.equal(capabilities.version, '1.1.1', 'version'); diff --git a/modules/wms/test/wms/wms-error-loader.spec.ts b/modules/wms/test/wms/wms-error-loader.spec.ts index 76f57189cc..495eda679f 100644 --- a/modules/wms/test/wms/wms-error-loader.spec.ts +++ b/modules/wms/test/wms/wms-error-loader.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; @@ -34,7 +35,7 @@ http://schemas.opengis.net/wms/1.3.0/exceptions_1_3_0.xsd"> test('WMSErrorLoader#test cases', async (t) => { for (const tc of ERROR_TEST_CASES) { - const error = (await parse(tc.xml, WMSErrorLoader, {wms: {minimalErrors: true}})) as string; + const error = (await parse(tc.xml, WMSErrorLoader, {wms: {minimalErrors: true}})); t.equal(error, tc.parsed, `Error message: "${error}"`); } t.end(); diff --git a/modules/wms/test/wms/wms-feature-info-loader.spec.ts b/modules/wms/test/wms/wms-feature-info-loader.spec.ts index 98c3492cc0..8379df3815 100644 --- a/modules/wms/test/wms/wms-feature-info-loader.spec.ts +++ b/modules/wms/test/wms/wms-feature-info-loader.spec.ts @@ -1,11 +1,12 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/chrelad/openlayers/blob/master/tests/Format/WMSGetFeatureInfo.html // under OpenLayers license (only used for test cases) // See README.md in `./data` directory for full license text copy. import test from 'tape-promise/tape'; -import {_WMSFeatureInfoLoader as WMSFeatureInfoLoader, _WMSFeatureInfo as WMSFeatureInfo} from '@loaders.gl/wms'; +import {_WMSFeatureInfoLoader as WMSFeatureInfoLoader} from '@loaders.gl/wms'; import {parse} from '@loaders.gl/core'; test('WMSFeatureInfoLoader#read_FeatureInfoResponse', async (t) => { @@ -13,7 +14,7 @@ test('WMSFeatureInfoLoader#read_FeatureInfoResponse', async (t) => { let text = '' + '' + ''; - let featureInfo = (await parse(text, WMSFeatureInfoLoader)) as WMSFeatureInfo; + let featureInfo = await parse(text, WMSFeatureInfoLoader); t.equal(featureInfo.features.length, 0, 'Parsing empty FeatureInfoResponse response successful'); // read 1 feature @@ -23,7 +24,7 @@ test('WMSFeatureInfoLoader#read_FeatureInfoResponse', async (t) => { ' ' + ''; - featureInfo = (await parse(text, WMSFeatureInfoLoader)) as WMSFeatureInfo; + featureInfo = await parse(text, WMSFeatureInfoLoader); t.equal(featureInfo.features.length, 1, 'Parsed 1 feature in total'); t.equal( @@ -41,7 +42,7 @@ test('WMSFeatureInfoLoader#read_FeatureInfoResponse', async (t) => { ' ' + ''; - featureInfo = (await parse(text, WMSFeatureInfoLoader)) as WMSFeatureInfo; + featureInfo = await parse(text, WMSFeatureInfoLoader); t.equal(featureInfo.features.length, 3, 'Parsed 3 features in total'); @@ -65,7 +66,7 @@ test.skip('WMSFeatureInfoLoader#msGMLOutput', async (t) => { ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + ''; - let featureInfo = (await parse(text, WMSFeatureInfoLoader)) as WMSFeatureInfo; + let featureInfo = await parse(text, WMSFeatureInfoLoader); t.equal(featureInfo.features.length, 0, 'Parsing empty msGMLOutput response succesfull'); // read 1 feature from 1 layer @@ -94,7 +95,7 @@ test.skip('WMSFeatureInfoLoader#msGMLOutput', async (t) => { ' ' + ''; - featureInfo = (await parse(text, WMSFeatureInfoLoader)) as WMSFeatureInfo; + featureInfo = await parse(text, WMSFeatureInfoLoader); t.equal(featureInfo.features.length, 1, 'Parsed 1 feature in total'); @@ -163,7 +164,7 @@ test.skip('WMSFeatureInfoLoader#msGMLOutput', async (t) => { ' ' + ''; - featureInfo = (await parse(text, WMSFeatureInfoLoader)) as WMSFeatureInfo; + featureInfo = await parse(text, WMSFeatureInfoLoader); t.equal(featureInfo.features.length, 2, 'Parsed 2 features in total'); @@ -200,7 +201,7 @@ test.skip('WMSFeatureInfoLoader#msGMLOutput', async (t) => { ' ' + ''; - featureInfo = (await parse(text, WMSFeatureInfoLoader)) as WMSFeatureInfo; + featureInfo = await parse(text, WMSFeatureInfoLoader); // t.equal( // featureInfo.features[0].geometry instanceof OpenLayers.Geometry.MultiLineString, @@ -250,7 +251,7 @@ test.skip('WMSFeatureInfoLoader#Ionic/GeoServer', async (t) => { ' ' + ' '; - let featureInfo = (await parse(text, WMSFeatureInfoLoader)) as WMSFeatureInfo; + let featureInfo = await parse(text, WMSFeatureInfoLoader); t.equal( featureInfo.features.length, @@ -271,7 +272,7 @@ test.skip('WMSFeatureInfoLoader#Ionic/GeoServer', async (t) => { text = '591943.9375,4925605 593045.625,49258453secondary highway, hard surface593045.60746465,4925605.0059156 593024.32382915,4925606.79305411 592907.54863574,4925624.85647524 592687.35111096,4925670.76834012 592430.76279218,4925678.79393165 592285.97636109,4925715.70811767 592173.39165655,4925761.83511156 592071.1753393,4925793.95523514 591985.96972625,4925831.59842486 591943.98769455,4925844.93220071'; - featureInfo = (await parse(text, WMSFeatureInfoLoader)) as WMSFeatureInfo; + featureInfo = await parse(text, WMSFeatureInfoLoader); t.equal( featureInfo.features.length, diff --git a/modules/wms/test/wms/wms-layer-description-loader.spec.ts b/modules/wms/test/wms/wms-layer-description-loader.spec.ts index 5f3b9d0d85..7ca5c3e589 100644 --- a/modules/wms/test/wms/wms-layer-description-loader.spec.ts +++ b/modules/wms/test/wms/wms-layer-description-loader.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from https://github.com/chrelad/openlayers/blob/master/tests/Format/WMSDescribeLayer.html // under OpenLayers license (only used for test cases) diff --git a/modules/wms/test/wmts/wmts-capabilities-loader.spec.ts b/modules/wms/test/wmts/wmts-capabilities-loader.spec.ts index 79e3a3ff20..ab407eb55f 100644 --- a/modules/wms/test/wmts/wmts-capabilities-loader.spec.ts +++ b/modules/wms/test/wmts/wmts-capabilities-loader.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // @ts-nocheck diff --git a/modules/worker-utils/package.json b/modules/worker-utils/package.json index 940ec0aaf3..8041a4a016 100644 --- a/modules/worker-utils/package.json +++ b/modules/worker-utils/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/worker-utils", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Utilities for running tasks on worker threads", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -17,8 +18,15 @@ "thread" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -27,24 +35,18 @@ ], "browser": { "child_process": false, + "module": false, + "path": false, "fs": false, "./src/lib/node/worker_threads.ts": "./src/lib/node/worker_threads-browser.ts", "./dist/lib/node/worker_threads.js": "./dist/lib/node/worker_threads-browser.js", - "./dist/es5/lib/node/worker_threads.js": "./dist/es5/lib/node/worker_threads-browser.js", - "./dist/esm/lib/node/worker_threads.js": "./dist/esm/lib/node/worker_threads-browser.js", "./src/lib/node/require-utils.node.ts": false, "./dist/lib/node/require-utils.node.js": false, - "./dist/es5/lib/node/require-utils.node.js": false, - "./dist/esm/lib/node/require-utils.node.js": false, "./src/lib/process-utils/child-process-proxy.ts": false, - "./dist/lib/process-utils/child-process-proxy.js": false, - "./dist/es5/lib/process-utils/child-process-proxy.js": false, "./dist/esm/lib/process-utils/child-process-proxy.js": false }, "scripts": { "pre-build": "npm run build-worker && npm run build-worker-node", - "pre-build-disabled": "npm run build-bundle && npm run build-workers", - "build-bundle": "esbuild src/bundle.ts --outfile=dist/dist.min.js", "build-worker": "esbuild src/workers/null-worker.ts --outfile=dist/null-worker.js --target=esnext --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"", "build-worker-node": "esbuild src/workers/null-worker.ts --outfile=dist/null-worker-node.js --platform=node --target=node16 --bundle --sourcemap --define:__VERSION__=\\\"$npm_package_version\\\"" }, diff --git a/modules/worker-utils/src/lib/env-utils/version.ts b/modules/worker-utils/src/lib/env-utils/version.ts index 3da1c14137..c9f5a40e7b 100644 --- a/modules/worker-utils/src/lib/env-utils/version.ts +++ b/modules/worker-utils/src/lib/env-utils/version.ts @@ -1,13 +1,29 @@ // Version constant cannot be imported, it needs to correspond to the build version of **this** module. -// __VERSION__ is injected by babel-plugin-version-inline -// Change to `latest` on production branches -const DEFAULT_VERSION = 'beta'; +/** + * TODO - unpkg.com doesn't seem to have a `latest` specifier for alpha releases... + * 'beta' on beta branch, 'latest' on prod branch + */ +export const NPM_TAG = 'latest'; + declare let __VERSION__: string; -export const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : DEFAULT_VERSION; -if (typeof __VERSION__ === 'undefined') { - // eslint-disable-next-line - console.error( - 'loaders.gl: The __VERSION__ variable is not injected using babel plugin. Latest unstable workers would be fetched from the CDN.' - ); + +function getVersion() { + if (!globalThis._loadersgl_?.version) { + globalThis._loadersgl_ = globalThis._loadersgl_ || {}; + // __VERSION__ is injected by babel-plugin-version-inline + if (typeof __VERSION__ === 'undefined') { + // eslint-disable-next-line + console.warn( + 'loaders.gl: The __VERSION__ variable is not injected using babel plugin. Latest unstable workers would be fetched from the CDN.' + ); + globalThis._loadersgl_.version = NPM_TAG; + } else { + globalThis._loadersgl_.version = __VERSION__; + } + } + + return globalThis._loadersgl_.version; } + +export const VERSION = getVersion(); diff --git a/modules/worker-utils/src/lib/library-utils/library-utils.ts b/modules/worker-utils/src/lib/library-utils/library-utils.ts index 1a873ba01f..6a192b3c9b 100644 --- a/modules/worker-utils/src/lib/library-utils/library-utils.ts +++ b/modules/worker-utils/src/lib/library-utils/library-utils.ts @@ -1,15 +1,8 @@ /* global importScripts */ -import {global, isBrowser, isWorker} from '../env-utils/globals'; +import {isBrowser, isWorker} from '../env-utils/globals'; import * as node from '../node/require-utils.node'; import {assert} from '../env-utils/assert'; -import {VERSION as __VERSION__} from '../env-utils/version'; - -/** - * TODO - unpkg.com doesn't seem to have a `latest` specifier for alpha releases... - * 'beta' on beta branch, 'latest' on prod branch - */ -const LATEST = 'beta'; -const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : LATEST; +import {VERSION} from '../env-utils/version'; const loadLibraryPromises: Record> = {}; // promises @@ -31,12 +24,12 @@ const loadLibraryPromises: Record> = {}; // promises export async function loadLibrary( libraryUrl: string, moduleName: string | null = null, - options: object = {} + options: object = {}, + libraryName: string | null = null ): Promise { if (moduleName) { - libraryUrl = getLibraryUrl(libraryUrl, moduleName, options); + libraryUrl = getLibraryUrl(libraryUrl, moduleName, options, libraryName); } - // Ensure libraries are only loaded once loadLibraryPromises[libraryUrl] = @@ -46,48 +39,61 @@ export async function loadLibrary( } // TODO - sort out how to resolve paths for main/worker and dev/prod -export function getLibraryUrl(library: string, moduleName?: string, options?: any): string { +export function getLibraryUrl( + library: string, + moduleName?: string, + options: any = {}, + libraryName: string | null = null +): string { // Check if already a URL - if (library.startsWith('http')) { + if (!options.useLocalLibraries && library.startsWith('http')) { return library; } + libraryName = libraryName || library; + // Allow application to import and supply libraries through `options.modules` const modules = options.modules || {}; - if (modules[library]) { - return modules[library]; + if (modules[libraryName]) { + return modules[libraryName]; } // Load from local files, not from CDN scripts in Node.js // TODO - needs to locate the modules directory when installed! if (!isBrowser) { - return `modules/${moduleName}/dist/libs/${library}`; + return `modules/${moduleName}/dist/libs/${libraryName}`; } // In browser, load from external scripts if (options.CDN) { assert(options.CDN.startsWith('http')); - return `${options.CDN}/${moduleName}@${VERSION}/dist/libs/${library}`; + return `${options.CDN}/${moduleName}@${VERSION}/dist/libs/${libraryName}`; } // TODO - loading inside workers requires paths relative to worker script location... if (isWorker) { - return `../src/libs/${library}`; + return `../src/libs/${libraryName}`; } - return `modules/${moduleName}/src/libs/${library}`; + return `modules/${moduleName}/src/libs/${libraryName}`; } async function loadLibraryFromFile(libraryUrl: string): Promise { if (libraryUrl.endsWith('wasm')) { - const response = await fetch(libraryUrl); - return await response.arrayBuffer(); + return await loadAsArrayBuffer(libraryUrl); } if (!isBrowser) { + // TODO - Node doesn't yet support dynamic import from https URLs + // try { + // return await import(libraryUrl); + // } catch (error) { + // console.error(error); + // } try { return node && node.requireFromFile && (await node.requireFromFile(libraryUrl)); - } catch { + } catch (error) { + console.error(error); // eslint-disable-line no-console return null; } } @@ -99,8 +105,7 @@ async function loadLibraryFromFile(libraryUrl: string): Promise { // return await loadScriptFromFile(libraryUrl); // } - const response = await fetch(libraryUrl); - const scriptSource = await response.text(); + const scriptSource = await loadAsText(libraryUrl); return loadLibraryFromString(scriptSource, libraryUrl); } @@ -129,7 +134,7 @@ function loadLibraryFromString(scriptSource: string, id: string): null | any { if (isWorker) { // Use lvalue trick to make eval run in global scope - eval.call(global, scriptSource); // eslint-disable-line no-eval + eval.call(globalThis, scriptSource); // eslint-disable-line no-eval // https://stackoverflow.com/questions/9107240/1-evalthis-vs-evalthis-in-javascript // http://perfectionkills.com/global-eval-what-are-the-options/ return null; @@ -161,3 +166,24 @@ function combineWorkerWithLibrary(worker, jsContent) { this.workerSourceURL = URL.createObjectURL(new Blob([body])); } */ + +async function loadAsArrayBuffer(url: string): Promise { + if (isBrowser || !node.readFileAsArrayBuffer || url.startsWith('http')) { + const response = await fetch(url); + return await response.arrayBuffer(); + } + return await node.readFileAsArrayBuffer(url); +} + +/** + * Load a file from local file system + * @param filename + * @returns + */ +async function loadAsText(url: string): Promise { + if (isBrowser || !node.readFileAsText || url.startsWith('http')) { + const response = await fetch(url); + return await response.text(); + } + return await node.readFileAsText(url); +} diff --git a/modules/worker-utils/src/lib/node/require-utils.node.ts b/modules/worker-utils/src/lib/node/require-utils.node.ts index 0a4557199d..22aa18f946 100644 --- a/modules/worker-utils/src/lib/node/require-utils.node.ts +++ b/modules/worker-utils/src/lib/node/require-utils.node.ts @@ -5,7 +5,36 @@ // this file is not visible to webpack (it is excluded in the package.json "browser" field). import Module from 'module'; -import path from 'path'; +import * as path from 'path'; +import * as fs from 'fs'; + +/** + * Load a file from local file system + * @param filename + * @returns + */ +export async function readFileAsArrayBuffer(filename: string): Promise { + if (filename.startsWith('http')) { + const response = await fetch(filename); + return await response.arrayBuffer(); + } + const buffer = fs.readFileSync(filename); + return buffer.buffer; +} + +/** + * Load a file from local file system + * @param filename + * @returns + */ +export async function readFileAsText(filename: string): Promise { + if (filename.startsWith('http')) { + const response = await fetch(filename); + return await response.text(); + } + const text = fs.readFileSync(filename, 'utf8'); + return text; +} // Node.js Dynamically require from file // Relative names are resolved relative to cwd @@ -21,7 +50,8 @@ export async function requireFromFile(filename: string): Promise { if (!filename.startsWith('/')) { filename = `${process.cwd()}/${filename}`; } - return require(filename); + const code = await fs.promises.readFile(filename, 'utf8'); + return requireFromString(code); } // Dynamically require from string @@ -50,7 +80,8 @@ export function requireFromString( // @ts-ignore const paths = Module._nodeModulePaths(path.dirname(filename)); - const parent = module.parent; + const parent = typeof module !== 'undefined' && module?.parent; + // @ts-ignore const newModule = new Module(filename, parent); newModule.filename = filename; diff --git a/modules/worker-utils/src/lib/node/worker_threads-browser.ts b/modules/worker-utils/src/lib/node/worker_threads-browser.ts index 555037f88a..b8c393fcba 100644 --- a/modules/worker-utils/src/lib/node/worker_threads-browser.ts +++ b/modules/worker-utils/src/lib/node/worker_threads-browser.ts @@ -3,7 +3,6 @@ // `import 'worker_threads` doesn't break browser builds. // The replacement is done in package.json browser field export class Worker { - // eslint-disable-next-line @typescript-eslint/no-empty-function terminate() {} } diff --git a/modules/worker-utils/src/lib/node/worker_threads.ts b/modules/worker-utils/src/lib/node/worker_threads.ts index 172e82677f..689dabe72b 100644 --- a/modules/worker-utils/src/lib/node/worker_threads.ts +++ b/modules/worker-utils/src/lib/node/worker_threads.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import * as WorkerThreads from 'worker_threads'; export * from 'worker_threads'; diff --git a/modules/worker-utils/src/lib/worker-api/create-worker.ts b/modules/worker-utils/src/lib/worker-api/create-worker.ts index cd899453fb..0226024237 100644 --- a/modules/worker-utils/src/lib/worker-api/create-worker.ts +++ b/modules/worker-utils/src/lib/worker-api/create-worker.ts @@ -23,8 +23,11 @@ export type ProcessOnMainThread = ( /** * Set up a WebWorkerGlobalScope to talk with the main thread */ -export function createWorker(process: Process, processInBatches?: ProcessInBatches): void { - if (!WorkerBody.inWorkerThread()) { +export async function createWorker( + process: Process, + processInBatches?: ProcessInBatches +): Promise { + if (!(await WorkerBody.inWorkerThread())) { return; } diff --git a/modules/worker-utils/src/lib/worker-api/get-worker-url.ts b/modules/worker-utils/src/lib/worker-api/get-worker-url.ts index 8539834e47..f400c25423 100644 --- a/modules/worker-utils/src/lib/worker-api/get-worker-url.ts +++ b/modules/worker-utils/src/lib/worker-api/get-worker-url.ts @@ -1,12 +1,10 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {WorkerObject, WorkerOptions} from '../../types'; import {assert} from '../env-utils/assert'; import {isBrowser} from '../env-utils/globals'; -import {VERSION as __VERSION__} from '../env-utils/version'; - -const NPM_TAG = 'beta'; // 'beta', or 'latest' on release-branch -const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : NPM_TAG; +import {VERSION, NPM_TAG} from '../env-utils/version'; /** * Gets worker object's name (for debugging in Chrome thread inspector window) @@ -43,7 +41,12 @@ export function getWorkerURL(worker: WorkerObject, options: WorkerOptions = {}): // If URL is test, generate local loaders.gl url // @ts-ignore _workerType if (options._workerType === 'test') { - url = `modules/${worker.module}/dist/${workerFile}`; + if (isBrowser) { + url = `modules/${worker.module}/dist/${workerFile}`; + } else { + // In the test environment the ts-node loader requires TypeScript code + url = `modules/${worker.module}/src/workers/${worker.id}-worker-node.ts`; + } } // If url override is not provided, generate a URL to published version on npm CDN unpkg.com diff --git a/modules/worker-utils/src/lib/worker-farm/worker-body.ts b/modules/worker-utils/src/lib/worker-farm/worker-body.ts index 2efb1b970c..c8d07138e2 100644 --- a/modules/worker-utils/src/lib/worker-farm/worker-body.ts +++ b/modules/worker-utils/src/lib/worker-farm/worker-body.ts @@ -2,15 +2,23 @@ import type {WorkerMessageData, WorkerMessageType, WorkerMessagePayload} from '. import {getTransferList} from '../worker-utils/get-transfer-list'; /** Vile hack to defeat over-zealous bundlers from stripping out the require */ -function getParentPort() { +async function getParentPort() { // const isNode = globalThis.process; let parentPort; try { // prettier-ignore eval('globalThis.parentPort = require(\'worker_threads\').parentPort'); // eslint-disable-line no-eval parentPort = globalThis.parentPort; - // eslint-disable-next-line no-empty - } catch {} + } catch { + try { + // prettier-ignore + eval('globalThis.workerThreadsPromise = import(\'worker_threads\')'); // eslint-disable-line no-eval + const workerThreads = await globalThis.workerThreadsPromise; + parentPort = workerThreads.parentPort; + } catch (error) { + console.error((error as Error).message); // eslint-disable-line no-console + } + } return parentPort; } @@ -21,17 +29,17 @@ const onMessageWrapperMap = new Map(); */ export default class WorkerBody { /** Check that we are actually in a worker thread */ - static inWorkerThread(): boolean { - return typeof self !== 'undefined' || Boolean(getParentPort()); + static async inWorkerThread(): Promise { + return typeof self !== 'undefined' || Boolean(await getParentPort()); } /* * (type: WorkerMessageType, payload: WorkerMessagePayload) => any */ static set onmessage(onMessage: (type: WorkerMessageType, payload: WorkerMessagePayload) => any) { - function handleMessage(message) { + async function handleMessage(message) { + const parentPort = await getParentPort(); // Confusingly the message itself also has a 'type' field which is always set to 'message' - const parentPort = getParentPort(); const {type, payload} = parentPort ? message : message.data; // if (!isKnownMessage(message)) { // return; @@ -39,37 +47,38 @@ export default class WorkerBody { onMessage(type, payload); } - const parentPort = getParentPort(); - if (parentPort) { - parentPort.on('message', handleMessage); - // if (message == 'exit') { parentPort.unref(); } - // eslint-disable-next-line - parentPort.on('exit', () => console.debug('Node worker closing')); - } else { - // eslint-disable-next-line no-restricted-globals - globalThis.onmessage = handleMessage; - } + getParentPort().then((parentPort) => { + if (parentPort) { + parentPort.on('message', handleMessage); + // if (message == 'exit') { parentPort.unref(); } + // eslint-disable-next-line + parentPort.on('exit', () => console.debug('Node worker closing')); + } else { + // eslint-disable-next-line no-restricted-globals + globalThis.onmessage = handleMessage; + } + }); } - static addEventListener( + static async addEventListener( onMessage: (type: WorkerMessageType, payload: WorkerMessagePayload) => any ) { let onMessageWrapper = onMessageWrapperMap.get(onMessage); if (!onMessageWrapper) { - onMessageWrapper = (message: MessageEvent) => { + onMessageWrapper = async (message: MessageEvent) => { if (!isKnownMessage(message)) { return; } + const parentPort = await getParentPort(); // Confusingly in the browser, the message itself also has a 'type' field which is always set to 'message' - const parentPort = getParentPort(); const {type, payload} = parentPort ? message : message.data; onMessage(type, payload); }; } - const parentPort = getParentPort(); + const parentPort = await getParentPort(); if (parentPort) { console.error('not implemented'); // eslint-disable-line } else { @@ -77,12 +86,12 @@ export default class WorkerBody { } } - static removeEventListener( + static async removeEventListener( onMessage: (type: WorkerMessageType, payload: WorkerMessagePayload) => any ) { const onMessageWrapper = onMessageWrapperMap.get(onMessage); onMessageWrapperMap.delete(onMessage); - const parentPort = getParentPort(); + const parentPort = await getParentPort(); if (parentPort) { console.error('not implemented'); // eslint-disable-line } else { @@ -95,12 +104,12 @@ export default class WorkerBody { * @param type * @param payload */ - static postMessage(type: WorkerMessageType, payload: WorkerMessagePayload): void { + static async postMessage(type: WorkerMessageType, payload: WorkerMessagePayload): Promise { const data: WorkerMessageData = {source: 'loaders.gl', type, payload}; // console.log('posting message', data); const transferList = getTransferList(payload); - const parentPort = getParentPort(); + const parentPort = await getParentPort(); if (parentPort) { parentPort.postMessage(data, transferList); // console.log('posted message', data); diff --git a/modules/worker-utils/src/lib/worker-farm/worker-pool.ts b/modules/worker-utils/src/lib/worker-farm/worker-pool.ts index bab7acd103..071d846401 100644 --- a/modules/worker-utils/src/lib/worker-farm/worker-pool.ts +++ b/modules/worker-utils/src/lib/worker-farm/worker-pool.ts @@ -154,6 +154,9 @@ export default class WorkerPool { // Wait for the app to signal that the job is complete, then return worker to queue try { await job.result; + } catch (error) { + // eslint-disable-next-line no-console + console.error(`Worker exception: ${error}`); } finally { this.returnWorkerToQueue(workerThread); } diff --git a/modules/worker-utils/src/lib/worker-farm/worker-thread.ts b/modules/worker-utils/src/lib/worker-farm/worker-thread.ts index acab3b5ab9..e8d17af3ad 100644 --- a/modules/worker-utils/src/lib/worker-farm/worker-thread.ts +++ b/modules/worker-utils/src/lib/worker-farm/worker-thread.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import {NodeWorker, NodeWorkerType} from '../node/worker_threads'; import {isBrowser} from '../env-utils/globals'; diff --git a/modules/worker-utils/test/index.ts b/modules/worker-utils/test/index.ts index 8656273ec9..57a10d4fca 100644 --- a/modules/worker-utils/test/index.ts +++ b/modules/worker-utils/test/index.ts @@ -1,6 +1,6 @@ import './lib/async-queue/async-queue.spec'; -import './lib/library-utils/require-utils.spec'; +// import './lib/library-utils/require-utils.spec'; import './lib/library-utils/library-utils.spec'; import './lib/worker-utils/get-transfer-list.spec'; diff --git a/modules/worker-utils/test/lib/library-utils/library-utils.spec.ts b/modules/worker-utils/test/lib/library-utils/library-utils.spec.ts index 6598b06ffc..7bc08fad77 100644 --- a/modules/worker-utils/test/lib/library-utils/library-utils.spec.ts +++ b/modules/worker-utils/test/lib/library-utils/library-utils.spec.ts @@ -1,12 +1,44 @@ import test from 'tape-promise/tape'; -// import {loadLibrary, getLibraryUrl} from '@loaders.gl/worker-utils'; +import {getLibraryUrl, isBrowser} from '@loaders.gl/worker-utils'; +import {VERSION} from '../../../src/lib/env-utils/version'; + +const DRACO_DECODER_URL = + 'https://www.gstatic.com/draco/versioned/decoders/1.5.6/draco_decoder.wasm'; + +test('getLibraryUrl # should return URL', (t) => { + const result = getLibraryUrl(DRACO_DECODER_URL); + t.equals(result, DRACO_DECODER_URL); + t.end(); +}); + +test('getLibraryUrl # should not return URL', (t) => { + const result = getLibraryUrl( + DRACO_DECODER_URL, + 'draco', + {useLocalLibraries: true, CDN: 'https://c.d.n'}, + 'draco_decoder.wasm' + ); + if (isBrowser) { + t.equals(result, `https://c.d.n/draco@${VERSION}/dist/libs/draco_decoder.wasm`); + } else { + t.equals(result, 'modules/draco/dist/libs/draco_decoder.wasm'); + } + + t.end(); +}); + +test('getLibraryUrl # should get url from modules option', (t) => { + const result = getLibraryUrl('draco_decoder.wasm', 'draco', { + modules: { + 'draco_decoder.wasm': 'https://c.d.n/draco_decoder.wasm' + } + }); + t.equals(result, 'https://c.d.n/draco_decoder.wasm'); -test('getLibraryUrl', (t) => { - // getLibraryUrl({}); t.end(); }); -test('getLibraryUrl', (t) => { +test('loadLibrary', (t) => { // loadLibrary({}); t.end(); }); diff --git a/modules/worker-utils/test/lib/library-utils/require-utils.spec.ts b/modules/worker-utils/test/lib/library-utils/require-utils.spec.ts index fd939c0e2f..fb34cc0c09 100644 --- a/modules/worker-utils/test/lib/library-utils/require-utils.spec.ts +++ b/modules/worker-utils/test/lib/library-utils/require-utils.spec.ts @@ -3,12 +3,14 @@ // MIT license import test from 'tape-promise/tape'; -import fs from 'fs'; +import * as fs from 'fs'; +import * as path from 'path'; import {isBrowser} from '@loaders.gl/worker-utils'; import {requireFromFile, requireFromString} from '../../../src/lib/node/require-utils.node'; -const MODULE_URL = `${__dirname}/fixture/module.js`; -const SUBMODULE_URL = `${__dirname}/fixture/submodule.js`; +const DIR = path?.dirname?.(import.meta.url) || '.'; +const MODULE_URL = `${DIR}/fixture/module.js`; +const SUBMODULE_URL = `${DIR}/fixture/submodule.js`; test('require-utils', (tt) => { if (isBrowser) { diff --git a/modules/worker-utils/test/lib/worker-api/get-worker-url.spec.ts b/modules/worker-utils/test/lib/worker-api/get-worker-url.spec.ts index d082c5fc26..7d9806b146 100644 --- a/modules/worker-utils/test/lib/worker-api/get-worker-url.spec.ts +++ b/modules/worker-utils/test/lib/worker-api/get-worker-url.spec.ts @@ -21,7 +21,7 @@ test('getWorkerURL', (t) => { getWorkerURL(NullWorker, {_workerType: 'test'}), isBrowser ? 'modules/worker-utils/dist/null-worker.js' - : 'modules/worker-utils/dist/null-worker-node.js', + : 'modules/worker-utils/src/workers/null-worker-node.ts', 'worker url with _useLocalWorkers options' ); diff --git a/modules/worker-utils/test/lib/worker-utils/get-transfer-list.spec.ts b/modules/worker-utils/test/lib/worker-utils/get-transfer-list.spec.ts index 93d76fee6b..d64220a293 100644 --- a/modules/worker-utils/test/lib/worker-utils/get-transfer-list.spec.ts +++ b/modules/worker-utils/test/lib/worker-utils/get-transfer-list.spec.ts @@ -124,7 +124,7 @@ test('getTransferListForWriter - Should handle hested options.', async (t) => { } }, four: { - five: function test1() {} + five: () => {} }, six: { deep: { diff --git a/modules/xml/package.json b/modules/xml/package.json index 8e8faade4b..6d509168a8 100644 --- a/modules/xml/package.json +++ b/modules/xml/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/xml", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loaders for the XML (eXtensible Markup Language) format", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -20,8 +21,15 @@ "OGC" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -29,17 +37,14 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { "@babel/runtime": "^7.3.1", - "@loaders.gl/loader-utils": "4.0.0-alpha.13", - "@loaders.gl/schema": "4.0.0-alpha.13", + "@loaders.gl/loader-utils": "4.0.3", + "@loaders.gl/schema": "4.0.3", "fast-xml-parser": "^4.2.5" }, - "devDependencies": { - "xmldom": "0.6.0" - }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/xml/src/bundle.ts b/modules/xml/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/xml/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/xml/src/html-loader.ts b/modules/xml/src/html-loader.ts index 49e7795667..1acb1d7fa3 100644 --- a/modules/xml/src/html-loader.ts +++ b/modules/xml/src/html-loader.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {LoaderWithParser} from '@loaders.gl/loader-utils'; import {mergeLoaderOptions} from '@loaders.gl/loader-utils'; @@ -12,7 +13,7 @@ export type HTMLLoaderOptions = XMLLoaderOptions; * This split enables applications can control whether they want HTML responses to be parsed by the XML loader or not. * This loader does not have any additional understanding of the structure of HTML or the document. */ -export const HTMLLoader: LoaderWithParser = { +export const HTMLLoader: LoaderWithParser = { ...XMLLoader, name: 'HTML', id: 'html', diff --git a/modules/xml/src/index.ts b/modules/xml/src/index.ts index 87246561cc..f52408266c 100644 --- a/modules/xml/src/index.ts +++ b/modules/xml/src/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // XMLLoader diff --git a/modules/xml/src/lib/parsers/parse-xml.ts b/modules/xml/src/lib/parsers/parse-xml.ts index d3adca2da7..a395ab8889 100644 --- a/modules/xml/src/lib/parsers/parse-xml.ts +++ b/modules/xml/src/lib/parsers/parse-xml.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import type {SAXParserOptions} from '../../sax-ts/sax'; import {StreamingXMLParser} from './streaming-xml-parser'; diff --git a/modules/xml/src/lib/xml-utils/uncapitalize.ts b/modules/xml/src/lib/xml-utils/uncapitalize.ts index 544cd7a67a..ae9ea912a2 100644 --- a/modules/xml/src/lib/xml-utils/uncapitalize.ts +++ b/modules/xml/src/lib/xml-utils/uncapitalize.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors /** * Uncapitalize first letter of a string diff --git a/modules/xml/src/sax-ts/sax.ts b/modules/xml/src/sax-ts/sax.ts index bbd5f10fd3..c914fd402e 100644 --- a/modules/xml/src/sax-ts/sax.ts +++ b/modules/xml/src/sax-ts/sax.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // This file is forked from https://github.com/Maxim-Mazurok/sax-ts under ISC license, // which in turn is forked from https://github.com/isaacs/sax-js under ISC license diff --git a/modules/xml/test/html-loader.spec.ts b/modules/xml/test/html-loader.spec.ts index fb29cb7f67..aea237c3fd 100644 --- a/modules/xml/test/html-loader.spec.ts +++ b/modules/xml/test/html-loader.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; diff --git a/modules/xml/test/index.ts b/modules/xml/test/index.ts index 6632d0e008..dc8ae4aee4 100644 --- a/modules/xml/test/index.ts +++ b/modules/xml/test/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // For streaming XML parsing // import './sax-ts'; diff --git a/modules/xml/test/lib/pretty-print.spec.ts b/modules/xml/test/lib/pretty-print.spec.ts index b82d7fe442..71e07a2234 100644 --- a/modules/xml/test/lib/pretty-print.spec.ts +++ b/modules/xml/test/lib/pretty-print.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Inspired by a sax-js example under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/index.ts b/modules/xml/test/sax-ts/index.ts index b314fc9e2e..1ee24f325d 100644 --- a/modules/xml/test/sax-ts/index.ts +++ b/modules/xml/test/sax-ts/index.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import './testcases/attribute-name.spec'; import './testcases/attribute-no-space.spec'; diff --git a/modules/xml/test/sax-ts/testcases/attribute-name.spec.ts b/modules/xml/test/sax-ts/testcases/attribute-name.spec.ts index a6caefaf10..27dd814c75 100644 --- a/modules/xml/test/sax-ts/testcases/attribute-name.spec.ts +++ b/modules/xml/test/sax-ts/testcases/attribute-name.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/attribute-no-space.spec.ts b/modules/xml/test/sax-ts/testcases/attribute-no-space.spec.ts index 02e1818953..3917a78b65 100644 --- a/modules/xml/test/sax-ts/testcases/attribute-no-space.spec.ts +++ b/modules/xml/test/sax-ts/testcases/attribute-no-space.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/attribute-unquoted.spec.ts b/modules/xml/test/sax-ts/testcases/attribute-unquoted.spec.ts index 1664b02e14..4da59928fe 100644 --- a/modules/xml/test/sax-ts/testcases/attribute-unquoted.spec.ts +++ b/modules/xml/test/sax-ts/testcases/attribute-unquoted.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/bom.spec.ts b/modules/xml/test/sax-ts/testcases/bom.spec.ts index 2508fc251a..4dbf9f6fd5 100644 --- a/modules/xml/test/sax-ts/testcases/bom.spec.ts +++ b/modules/xml/test/sax-ts/testcases/bom.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/buffer-overrun.spec.ts b/modules/xml/test/sax-ts/testcases/buffer-overrun.spec.ts index 5761cb4cf7..683b649024 100644 --- a/modules/xml/test/sax-ts/testcases/buffer-overrun.spec.ts +++ b/modules/xml/test/sax-ts/testcases/buffer-overrun.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/case.spec.ts b/modules/xml/test/sax-ts/testcases/case.spec.ts index 7ba28e1d81..243d181c84 100644 --- a/modules/xml/test/sax-ts/testcases/case.spec.ts +++ b/modules/xml/test/sax-ts/testcases/case.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/cdata-chunked.spec.ts b/modules/xml/test/sax-ts/testcases/cdata-chunked.spec.ts index daa40230c6..2e09fe3f75 100644 --- a/modules/xml/test/sax-ts/testcases/cdata-chunked.spec.ts +++ b/modules/xml/test/sax-ts/testcases/cdata-chunked.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/cdata-end-split.spec.ts b/modules/xml/test/sax-ts/testcases/cdata-end-split.spec.ts index ff9b1bad9b..8b34f6f70b 100644 --- a/modules/xml/test/sax-ts/testcases/cdata-end-split.spec.ts +++ b/modules/xml/test/sax-ts/testcases/cdata-end-split.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/cdata-fake-end.spec.ts b/modules/xml/test/sax-ts/testcases/cdata-fake-end.spec.ts index 0ee2580822..1e95113f18 100644 --- a/modules/xml/test/sax-ts/testcases/cdata-fake-end.spec.ts +++ b/modules/xml/test/sax-ts/testcases/cdata-fake-end.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/cdata-multiple.spec.ts b/modules/xml/test/sax-ts/testcases/cdata-multiple.spec.ts index 2b3f7d20be..8d5533ad19 100644 --- a/modules/xml/test/sax-ts/testcases/cdata-multiple.spec.ts +++ b/modules/xml/test/sax-ts/testcases/cdata-multiple.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/cdata.spec.ts b/modules/xml/test/sax-ts/testcases/cdata.spec.ts index 7c977d6f3b..3a70bbed46 100644 --- a/modules/xml/test/sax-ts/testcases/cdata.spec.ts +++ b/modules/xml/test/sax-ts/testcases/cdata.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/cyrillic.spec.ts b/modules/xml/test/sax-ts/testcases/cyrillic.spec.ts index d2d874460c..84d3971c43 100644 --- a/modules/xml/test/sax-ts/testcases/cyrillic.spec.ts +++ b/modules/xml/test/sax-ts/testcases/cyrillic.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/duplicate-attribute.spec.ts b/modules/xml/test/sax-ts/testcases/duplicate-attribute.spec.ts index 41ee8f7ab7..4899646937 100644 --- a/modules/xml/test/sax-ts/testcases/duplicate-attribute.spec.ts +++ b/modules/xml/test/sax-ts/testcases/duplicate-attribute.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/emoji.spec.ts b/modules/xml/test/sax-ts/testcases/emoji.spec.ts index 8b6cd972c3..c72a1752c0 100644 --- a/modules/xml/test/sax-ts/testcases/emoji.spec.ts +++ b/modules/xml/test/sax-ts/testcases/emoji.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/end_empty_stream.spec.ts b/modules/xml/test/sax-ts/testcases/end_empty_stream.spec.ts index 2961b99d52..3a79bd4533 100644 --- a/modules/xml/test/sax-ts/testcases/end_empty_stream.spec.ts +++ b/modules/xml/test/sax-ts/testcases/end_empty_stream.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license // import {testSax} from '../utils/test-utils'; diff --git a/modules/xml/test/sax-ts/testcases/entities.spec.ts b/modules/xml/test/sax-ts/testcases/entities.spec.ts index f6069ecbea..56c2ace4ad 100644 --- a/modules/xml/test/sax-ts/testcases/entities.spec.ts +++ b/modules/xml/test/sax-ts/testcases/entities.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/entity-mega.spec.ts b/modules/xml/test/sax-ts/testcases/entity-mega.spec.ts index 9d474aef07..afc749f831 100644 --- a/modules/xml/test/sax-ts/testcases/entity-mega.spec.ts +++ b/modules/xml/test/sax-ts/testcases/entity-mega.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/entity-nan.spec.ts b/modules/xml/test/sax-ts/testcases/entity-nan.spec.ts index 79ebef90f4..c061d0493d 100644 --- a/modules/xml/test/sax-ts/testcases/entity-nan.spec.ts +++ b/modules/xml/test/sax-ts/testcases/entity-nan.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/flush.spec.ts b/modules/xml/test/sax-ts/testcases/flush.spec.ts index 0f5704697b..83528467b5 100644 --- a/modules/xml/test/sax-ts/testcases/flush.spec.ts +++ b/modules/xml/test/sax-ts/testcases/flush.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/issue-23.spec.ts b/modules/xml/test/sax-ts/testcases/issue-23.spec.ts index af8b22a6d4..d954722070 100644 --- a/modules/xml/test/sax-ts/testcases/issue-23.spec.ts +++ b/modules/xml/test/sax-ts/testcases/issue-23.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/issue-30.spec.ts b/modules/xml/test/sax-ts/testcases/issue-30.spec.ts index 89d3faca68..f01c5da639 100644 --- a/modules/xml/test/sax-ts/testcases/issue-30.spec.ts +++ b/modules/xml/test/sax-ts/testcases/issue-30.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/issue-35.spec.ts b/modules/xml/test/sax-ts/testcases/issue-35.spec.ts index a5c88826fe..05d12370cc 100644 --- a/modules/xml/test/sax-ts/testcases/issue-35.spec.ts +++ b/modules/xml/test/sax-ts/testcases/issue-35.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/issue-47.spec.ts b/modules/xml/test/sax-ts/testcases/issue-47.spec.ts index cc0f91fdef..f199bb52f3 100644 --- a/modules/xml/test/sax-ts/testcases/issue-47.spec.ts +++ b/modules/xml/test/sax-ts/testcases/issue-47.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/issue-49.spec.ts b/modules/xml/test/sax-ts/testcases/issue-49.spec.ts index c257e53def..96761827f5 100644 --- a/modules/xml/test/sax-ts/testcases/issue-49.spec.ts +++ b/modules/xml/test/sax-ts/testcases/issue-49.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/issue-84.spec.ts b/modules/xml/test/sax-ts/testcases/issue-84.spec.ts index a49003bf91..7f5821e718 100644 --- a/modules/xml/test/sax-ts/testcases/issue-84.spec.ts +++ b/modules/xml/test/sax-ts/testcases/issue-84.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/issue-86.spec.ts b/modules/xml/test/sax-ts/testcases/issue-86.spec.ts index e6a12e76f4..b96a629c0a 100644 --- a/modules/xml/test/sax-ts/testcases/issue-86.spec.ts +++ b/modules/xml/test/sax-ts/testcases/issue-86.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/not-string.spec.ts b/modules/xml/test/sax-ts/testcases/not-string.spec.ts index c8753572e3..6e822f8675 100644 --- a/modules/xml/test/sax-ts/testcases/not-string.spec.ts +++ b/modules/xml/test/sax-ts/testcases/not-string.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/opentagstart.spec.ts b/modules/xml/test/sax-ts/testcases/opentagstart.spec.ts index 1f3e46c54a..84954a1ebc 100644 --- a/modules/xml/test/sax-ts/testcases/opentagstart.spec.ts +++ b/modules/xml/test/sax-ts/testcases/opentagstart.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/parser-position.spec.ts b/modules/xml/test/sax-ts/testcases/parser-position.spec.ts index c418202ecb..dd31857ef3 100644 --- a/modules/xml/test/sax-ts/testcases/parser-position.spec.ts +++ b/modules/xml/test/sax-ts/testcases/parser-position.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/script-close-better.spec.ts b/modules/xml/test/sax-ts/testcases/script-close-better.spec.ts index 23041a3973..246153101a 100644 --- a/modules/xml/test/sax-ts/testcases/script-close-better.spec.ts +++ b/modules/xml/test/sax-ts/testcases/script-close-better.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/script.spec.ts b/modules/xml/test/sax-ts/testcases/script.spec.ts index 9f810e5ce8..d5a65653ec 100644 --- a/modules/xml/test/sax-ts/testcases/script.spec.ts +++ b/modules/xml/test/sax-ts/testcases/script.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/self-closing-child-strict.spec.ts b/modules/xml/test/sax-ts/testcases/self-closing-child-strict.spec.ts index 4e644cb8b9..6779f59179 100644 --- a/modules/xml/test/sax-ts/testcases/self-closing-child-strict.spec.ts +++ b/modules/xml/test/sax-ts/testcases/self-closing-child-strict.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/self-closing-child.spec.ts b/modules/xml/test/sax-ts/testcases/self-closing-child.spec.ts index 37617cf8fb..c04fb1055e 100644 --- a/modules/xml/test/sax-ts/testcases/self-closing-child.spec.ts +++ b/modules/xml/test/sax-ts/testcases/self-closing-child.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/self-closing-tag.spec.ts b/modules/xml/test/sax-ts/testcases/self-closing-tag.spec.ts index c5d6abea80..2e3a30757d 100644 --- a/modules/xml/test/sax-ts/testcases/self-closing-tag.spec.ts +++ b/modules/xml/test/sax-ts/testcases/self-closing-tag.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/stand-alone-comment.spec.ts b/modules/xml/test/sax-ts/testcases/stand-alone-comment.spec.ts index 9b20d6e542..16f1016b9a 100644 --- a/modules/xml/test/sax-ts/testcases/stand-alone-comment.spec.ts +++ b/modules/xml/test/sax-ts/testcases/stand-alone-comment.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/stray-ending.spec.ts b/modules/xml/test/sax-ts/testcases/stray-ending.spec.ts index 51064ad240..43ffbcb347 100644 --- a/modules/xml/test/sax-ts/testcases/stray-ending.spec.ts +++ b/modules/xml/test/sax-ts/testcases/stray-ending.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/trailing-attribute-no-value.spec.ts b/modules/xml/test/sax-ts/testcases/trailing-attribute-no-value.spec.ts index 446721bdb7..3d40fdaccc 100644 --- a/modules/xml/test/sax-ts/testcases/trailing-attribute-no-value.spec.ts +++ b/modules/xml/test/sax-ts/testcases/trailing-attribute-no-value.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/trailing-non-whitespace.spec.ts b/modules/xml/test/sax-ts/testcases/trailing-non-whitespace.spec.ts index c64cff62d1..db3788466a 100644 --- a/modules/xml/test/sax-ts/testcases/trailing-non-whitespace.spec.ts +++ b/modules/xml/test/sax-ts/testcases/trailing-non-whitespace.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/unclosed-root.spec.ts b/modules/xml/test/sax-ts/testcases/unclosed-root.spec.ts index 20f07e6c11..ed531fb7d5 100644 --- a/modules/xml/test/sax-ts/testcases/unclosed-root.spec.ts +++ b/modules/xml/test/sax-ts/testcases/unclosed-root.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/unquoted.spec.ts b/modules/xml/test/sax-ts/testcases/unquoted.spec.ts index bc9a0e8910..4a8adc3432 100644 --- a/modules/xml/test/sax-ts/testcases/unquoted.spec.ts +++ b/modules/xml/test/sax-ts/testcases/unquoted.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license // unquoted attributes should be ok in non-strict mode diff --git a/modules/xml/test/sax-ts/testcases/utf8-split.spec.ts b/modules/xml/test/sax-ts/testcases/utf8-split.spec.ts index 1e06411a3a..8fc4fdd0d3 100644 --- a/modules/xml/test/sax-ts/testcases/utf8-split.spec.ts +++ b/modules/xml/test/sax-ts/testcases/utf8-split.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license // import {testSax} from '../utils/test-utils'; diff --git a/modules/xml/test/sax-ts/testcases/xml-internal-entities.spec.ts b/modules/xml/test/sax-ts/testcases/xml-internal-entities.spec.ts index d0fdd8615b..186efb79b4 100644 --- a/modules/xml/test/sax-ts/testcases/xml-internal-entities.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xml-internal-entities.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license /* eslint-disable camelcase */ diff --git a/modules/xml/test/sax-ts/testcases/xml_entities.spec.ts b/modules/xml/test/sax-ts/testcases/xml_entities.spec.ts index 8cbec9388f..36a4929176 100644 --- a/modules/xml/test/sax-ts/testcases/xml_entities.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xml_entities.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/xmlns-as-tag-name.spec.ts b/modules/xml/test/sax-ts/testcases/xmlns-as-tag-name.spec.ts index ad381e653d..8330fe424e 100644 --- a/modules/xml/test/sax-ts/testcases/xmlns-as-tag-name.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xmlns-as-tag-name.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/xmlns-issue-41.spec.ts b/modules/xml/test/sax-ts/testcases/xmlns-issue-41.spec.ts index 84bc28fdd4..73f7801698 100644 --- a/modules/xml/test/sax-ts/testcases/xmlns-issue-41.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xmlns-issue-41.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license /* TODO - NOT PORTED TO LOADERS.GL diff --git a/modules/xml/test/sax-ts/testcases/xmlns-rebinding.spec.ts b/modules/xml/test/sax-ts/testcases/xmlns-rebinding.spec.ts index c8b7a061ef..815ac96f3e 100644 --- a/modules/xml/test/sax-ts/testcases/xmlns-rebinding.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xmlns-rebinding.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/xmlns-strict.spec.ts b/modules/xml/test/sax-ts/testcases/xmlns-strict.spec.ts index a7b0763d4f..726ddd9407 100644 --- a/modules/xml/test/sax-ts/testcases/xmlns-strict.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xmlns-strict.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/xmlns-unbound-element.spec.ts b/modules/xml/test/sax-ts/testcases/xmlns-unbound-element.spec.ts index 3f56d9f1cd..6cdc1ef5be 100644 --- a/modules/xml/test/sax-ts/testcases/xmlns-unbound-element.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xmlns-unbound-element.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/xmlns-unbound.spec.ts b/modules/xml/test/sax-ts/testcases/xmlns-unbound.spec.ts index 24011fe1e3..d993abf136 100644 --- a/modules/xml/test/sax-ts/testcases/xmlns-unbound.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xmlns-unbound.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/xmlns-xml-default-ns.spec.ts b/modules/xml/test/sax-ts/testcases/xmlns-xml-default-ns.spec.ts index 88c6595c84..799a70606e 100644 --- a/modules/xml/test/sax-ts/testcases/xmlns-xml-default-ns.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xmlns-xml-default-ns.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/xmlns-xml-default-prefix-attribute.spec.ts b/modules/xml/test/sax-ts/testcases/xmlns-xml-default-prefix-attribute.spec.ts index 9435a748db..89133a1e72 100644 --- a/modules/xml/test/sax-ts/testcases/xmlns-xml-default-prefix-attribute.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xmlns-xml-default-prefix-attribute.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/xmlns-xml-default-prefix.spec.ts b/modules/xml/test/sax-ts/testcases/xmlns-xml-default-prefix.spec.ts index d2ffb731b0..c187e0d371 100644 --- a/modules/xml/test/sax-ts/testcases/xmlns-xml-default-prefix.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xmlns-xml-default-prefix.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/testcases/xmlns-xml-default-redefine.spec.ts b/modules/xml/test/sax-ts/testcases/xmlns-xml-default-redefine.spec.ts index 02a14faaab..db396221d9 100644 --- a/modules/xml/test/sax-ts/testcases/xmlns-xml-default-redefine.spec.ts +++ b/modules/xml/test/sax-ts/testcases/xmlns-xml-default-redefine.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import test from 'tape-promise/tape'; diff --git a/modules/xml/test/sax-ts/utils/test-utils.ts b/modules/xml/test/sax-ts/utils/test-utils.ts index 080ac900ca..0935f17504 100644 --- a/modules/xml/test/sax-ts/utils/test-utils.ts +++ b/modules/xml/test/sax-ts/utils/test-utils.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Forked from sax-ts & sax under ISC license import type {Test} from 'tape-promise/tape'; diff --git a/modules/xml/test/xml-loader.spec.ts b/modules/xml/test/xml-loader.spec.ts index 0352d0159c..d79ad6c779 100644 --- a/modules/xml/test/xml-loader.spec.ts +++ b/modules/xml/test/xml-loader.spec.ts @@ -1,4 +1,5 @@ // loaders.gl, MIT license +// Copyright (c) vis.gl contributors import test from 'tape-promise/tape'; // import {validateLoader} from 'test/common/conformance'; diff --git a/modules/zarr/package.json b/modules/zarr/package.json index 2d9168e5bd..15d9277739 100644 --- a/modules/zarr/package.json +++ b/modules/zarr/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/zarr", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Framework-independent loaders for Zarr", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -17,8 +18,15 @@ "zarr" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -27,8 +35,8 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { "zarr": "^0.5.0" diff --git a/modules/zarr/src/bundle.ts b/modules/zarr/src/bundle.ts deleted file mode 100644 index fcff1012c9..0000000000 --- a/modules/zarr/src/bundle.ts +++ /dev/null @@ -1,6 +0,0 @@ -import * as moduleExports from './index'; -const _global = typeof window === 'undefined' ? global : window; -// @ts-ignore -_global.loaders = _global.loaders || {}; -// @ts-ignore -module.exports = Object.assign(_global.loaders, moduleExports); diff --git a/modules/zarr/src/types.ts b/modules/zarr/src/types.ts index 9fec38b2c6..f52b59a9be 100644 --- a/modules/zarr/src/types.ts +++ b/modules/zarr/src/types.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import {DTYPE_LOOKUP} from './lib/zarr-pixel-source'; -export type SupportedDtype = typeof DTYPE_LOOKUP[keyof typeof DTYPE_LOOKUP]; -export type SupportedTypedArray = InstanceType; +export type SupportedDtype = (typeof DTYPE_LOOKUP)[keyof typeof DTYPE_LOOKUP]; +export type SupportedTypedArray = InstanceType<(typeof globalThis)[`${SupportedDtype}Array`]>; interface Multiscale { datasets: {path: string}[]; diff --git a/modules/zip/package.json b/modules/zip/package.json index a27507e2b0..a6c7c5c4e4 100644 --- a/modules/zip/package.json +++ b/modules/zip/package.json @@ -1,8 +1,9 @@ { "name": "@loaders.gl/zip", - "version": "4.0.0-alpha.13", + "version": "4.0.3", "description": "Zip Archive Loader", "license": "MIT", + "type": "module", "publishConfig": { "access": "public", "registry": "https://npm.pkg.github.com" @@ -18,8 +19,15 @@ "ZIP" ], "types": "dist/index.d.ts", - "main": "dist/es5/index.js", - "module": "dist/esm/index.js", + "main": "dist/index.cjs", + "module": "dist/index.js", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./dist/index.d.ts" + } + }, "sideEffects": false, "files": [ "src", @@ -27,11 +35,15 @@ "README.md" ], "scripts": { - "pre-build": "npm run build-bundle", - "build-bundle": "esbuild src/bundle.ts --bundle --outfile=dist/dist.min.js" + "pre-build": "npm run build-bundle && npm run build-bundle -- --env=dev", + "build-bundle": "ocular-bundle ./src/index.ts" }, "dependencies": { - "jszip": "^3.1.5" + "@loaders.gl/compression": "4.0.3", + "@loaders.gl/crypto": "4.0.3", + "@loaders.gl/loader-utils": "4.0.3", + "jszip": "^3.1.5", + "md5": "^2.3.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/modules/zip/src/bundle.ts b/modules/zip/src/bundle.ts deleted file mode 100644 index 0db0c48b55..0000000000 --- a/modules/zip/src/bundle.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -const moduleExports = require('./index'); -globalThis.loaders = globalThis.loaders || {}; -module.exports = Object.assign(globalThis.loaders, moduleExports); diff --git a/modules/zip/src/filesystems/zip-filesystem.ts b/modules/zip/src/filesystems/zip-filesystem.ts new file mode 100644 index 0000000000..b600ec2a8e --- /dev/null +++ b/modules/zip/src/filesystems/zip-filesystem.ts @@ -0,0 +1,138 @@ +import {FileSystem, isBrowser} from '@loaders.gl/loader-utils'; +import {FileProvider, isFileProvider} from '@loaders.gl/loader-utils'; +import {FileHandleFile} from '@loaders.gl/loader-utils'; +import {ZipCDFileHeader, makeZipCDHeaderIterator} from '../parse-zip/cd-file-header'; +import {parseZipLocalFileHeader} from '../parse-zip/local-file-header'; +import {DeflateCompression} from '@loaders.gl/compression'; + +type CompressionHandler = (compressedFile: ArrayBuffer) => Promise; +/** Handling different compression types in zip */ +const COMPRESSION_METHODS: {[key: number]: CompressionHandler} = { + /** No compression */ + 0: async (compressedFile) => compressedFile, + /** Deflation */ + 8: async (compressedFile) => { + const compression = new DeflateCompression({raw: true}); + const decompressedData = await compression.decompress(compressedFile); + return decompressedData; + } +}; + +/** + * FileSystem adapter for a ZIP file + * Holds FileProvider object that provides random access to archived files + */ +export class ZipFileSystem implements FileSystem { + /** FileProvider instance promise */ + protected fileProvider: FileProvider | null = null; + public fileName?: string; + + /** + * Constructor + * @param file - instance of FileProvider or file path string + */ + constructor(file: FileProvider | string) { + // Try to open file in NodeJS + if (typeof file === 'string') { + this.fileName = file; + if (!isBrowser) { + this.fileProvider = new FileHandleFile(file); + } else { + throw new Error('Cannot open file for random access in a WEB browser'); + } + } else if (isFileProvider(file)) { + this.fileProvider = file; + } + } + + /** Clean up resources */ + async destroy() { + if (this.fileProvider) { + await this.fileProvider.destroy(); + } + } + + /** + * Get file names list from zip archive + * @returns array of file names + */ + async readdir(): Promise { + if (!this.fileProvider) { + throw new Error('No data detected in the zip archive'); + } + const fileNames: string[] = []; + const zipCDIterator = makeZipCDHeaderIterator(this.fileProvider); + for await (const cdHeader of zipCDIterator) { + fileNames.push(cdHeader.fileName); + } + return fileNames; + } + + /** + * Get file metadata + * @param filename - name of a file + * @returns central directory data + */ + async stat(filename: string): Promise { + const cdFileHeader = await this.getCDFileHeader(filename); + return {...cdFileHeader, size: Number(cdFileHeader.uncompressedSize)}; + } + + /** + * Implementation of fetch against this file system + * @param filename - name of a file + * @returns - Response with file data + */ + async fetch(filename: string): Promise { + if (!this.fileProvider) { + throw new Error('No data detected in the zip archive'); + } + const cdFileHeader = await this.getCDFileHeader(filename); + const localFileHeader = await parseZipLocalFileHeader( + cdFileHeader.localHeaderOffset, + this.fileProvider + ); + if (!localFileHeader) { + throw new Error('Local file header has not been found in the zip archive`'); + } + + const compressionHandler = COMPRESSION_METHODS[localFileHeader.compressionMethod.toString()]; + if (!compressionHandler) { + throw Error('Only Deflation compression is supported'); + } + + const compressedFile = await this.fileProvider.slice( + localFileHeader.fileDataOffset, + localFileHeader.fileDataOffset + localFileHeader.compressedSize + ); + + const uncompressedFile = await compressionHandler(compressedFile); + + const response = new Response(uncompressedFile); + Object.defineProperty(response, 'url', {value: `${this.fileName || ''}/${filename}`}); + return response; + } + + /** + * Get central directory file header + * @param filename - name of a file + * @returns central directory file header + */ + private async getCDFileHeader(filename: string): Promise { + if (!this.fileProvider) { + throw new Error('No data detected in the zip archive'); + } + const zipCDIterator = makeZipCDHeaderIterator(this.fileProvider); + let result: ZipCDFileHeader | null = null; + for await (const cdHeader of zipCDIterator) { + if (cdHeader.fileName === filename) { + result = cdHeader; + break; + } + } + if (!result) { + throw new Error('File has not been found in the zip archive'); + } + return result; + } +} diff --git a/modules/zip/src/hash-file-utility.ts b/modules/zip/src/hash-file-utility.ts new file mode 100644 index 0000000000..4970c82248 --- /dev/null +++ b/modules/zip/src/hash-file-utility.ts @@ -0,0 +1,53 @@ +import {MD5Hash} from '@loaders.gl/crypto'; +import {FileProvider} from '@loaders.gl/loader-utils'; +import {makeZipCDHeaderIterator} from './parse-zip/cd-file-header'; + +/** + * Reads hash file from buffer and returns it in ready-to-use form + * @param arrayBuffer - buffer containing hash file + * @returns Map containing hash and offset + */ +export function parseHashTable(arrayBuffer: ArrayBuffer): Record { + const dataView = new DataView(arrayBuffer); + + const hashMap: Record = {}; + + for (let i = 0; i < arrayBuffer.byteLength; i = i + 24) { + const offset = dataView.getBigUint64(i + 16, true); + const hash = bufferToHex(arrayBuffer, i, 16); + hashMap[hash] = offset; + } + + return hashMap; +} + +function bufferToHex(buffer: ArrayBuffer, start: number, length: number): string { + // buffer is an ArrayBuffer + return [...new Uint8Array(buffer, start, length)] + .map((x) => x.toString(16).padStart(2, '0')) + .join(''); +} + +/** + * generates hash info from zip files "central directory" + * @param fileProvider - provider of the archive + * @returns ready to use hash info + */ +export async function makeHashTableFromZipHeaders( + fileProvider: FileProvider +): Promise> { + const zipCDIterator = makeZipCDHeaderIterator(fileProvider); + const md5Hash = new MD5Hash(); + const textEncoder = new TextEncoder(); + + const hashTable: Record = {}; + + for await (const cdHeader of zipCDIterator) { + const filename = cdHeader.fileName.split('\\').join('/').toLocaleLowerCase(); + const arrayBuffer = textEncoder.encode(filename).buffer; + const md5 = await md5Hash.hash(arrayBuffer, 'hex'); + hashTable[md5] = cdHeader.localHeaderOffset; + } + + return hashTable; +} diff --git a/modules/zip/src/index.ts b/modules/zip/src/index.ts index 0a4df78cc0..a7954a0a67 100644 --- a/modules/zip/src/index.ts +++ b/modules/zip/src/index.ts @@ -1,3 +1,23 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + export {ZipLoader} from './zip-loader'; export {ZipWriter} from './zip-writer'; -export {default as TarBuilder} from './tar-builder'; +export {TarBuilder} from './tar-builder'; + +export { + parseZipCDFileHeader, + makeZipCDHeaderIterator, + signature as cdSignature +} from './parse-zip/cd-file-header'; +export { + parseZipLocalFileHeader, + signature as localHeaderSignature +} from './parse-zip/local-file-header'; +export {parseEoCDRecord} from './parse-zip/end-of-central-directory'; +export {searchFromTheEnd} from './parse-zip/search-from-the-end'; + +// export type {HashElement} from './hash-file-utility'; +export {parseHashTable, makeHashTableFromZipHeaders} from './hash-file-utility'; + +export {ZipFileSystem} from './filesystems/zip-filesystem'; diff --git a/modules/zip/src/parse-zip/cd-file-header.ts b/modules/zip/src/parse-zip/cd-file-header.ts new file mode 100644 index 0000000000..409ee1e8d2 --- /dev/null +++ b/modules/zip/src/parse-zip/cd-file-header.ts @@ -0,0 +1,187 @@ +import {FileProvider, compareArrayBuffers} from '@loaders.gl/loader-utils'; +import {parseEoCDRecord} from './end-of-central-directory'; +import {ZipSignature} from './search-from-the-end'; + +/** + * zip central directory file header info + * according to https://en.wikipedia.org/wiki/ZIP_(file_format) + */ +export type ZipCDFileHeader = { + /** Compressed size */ + compressedSize: bigint; + /** Uncompressed size */ + uncompressedSize: bigint; + /** Extra field size */ + extraFieldLength: number; + /** File name length */ + fileNameLength: number; + /** File name */ + fileName: string; + /** Extra field offset */ + extraOffset: bigint; + /** Relative offset of local file header */ + localHeaderOffset: bigint; +}; + +/** + * Data that might be in Zip64 notation inside extra data + */ +type Zip64Data = { + /** Uncompressed size */ + uncompressedSize: bigint; + /** Compressed size */ + compressedSize: bigint; + /** Relative offset of local file header */ + localHeaderOffset: bigint; + /** Start disk */ + startDisk: bigint; +}; + +// offsets accroding to https://en.wikipedia.org/wiki/ZIP_(file_format) +const CD_COMPRESSED_SIZE_OFFSET = 20n; +const CD_UNCOMPRESSED_SIZE_OFFSET = 24n; +const CD_FILE_NAME_LENGTH_OFFSET = 28n; +const CD_EXTRA_FIELD_LENGTH_OFFSET = 30n; +const CD_START_DISK_OFFSET = 32n; +const CD_LOCAL_HEADER_OFFSET_OFFSET = 42n; +const CD_FILE_NAME_OFFSET = 46n; + +export const signature: ZipSignature = new Uint8Array([0x50, 0x4b, 0x01, 0x02]); + +/** + * Parses central directory file header of zip file + * @param headerOffset - offset in the archive where header starts + * @param buffer - buffer containing whole array + * @returns Info from the header + */ +export const parseZipCDFileHeader = async ( + headerOffset: bigint, + file: FileProvider +): Promise => { + const magicBytes = await file.slice(headerOffset, headerOffset + 4n); + if (!compareArrayBuffers(magicBytes, signature.buffer)) { + return null; + } + + const compressedSize = BigInt(await file.getUint32(headerOffset + CD_COMPRESSED_SIZE_OFFSET)); + const uncompressedSize = BigInt(await file.getUint32(headerOffset + CD_UNCOMPRESSED_SIZE_OFFSET)); + const extraFieldLength = await file.getUint16(headerOffset + CD_EXTRA_FIELD_LENGTH_OFFSET); + const startDisk = BigInt(await file.getUint16(headerOffset + CD_START_DISK_OFFSET)); + const fileNameLength = await file.getUint16(headerOffset + CD_FILE_NAME_LENGTH_OFFSET); + const filenameBytes = await file.slice( + headerOffset + CD_FILE_NAME_OFFSET, + headerOffset + CD_FILE_NAME_OFFSET + BigInt(fileNameLength) + ); + const fileName = new TextDecoder().decode(filenameBytes); + + const extraOffset = headerOffset + CD_FILE_NAME_OFFSET + BigInt(fileNameLength); + const oldFormatOffset = await file.getUint32(headerOffset + CD_LOCAL_HEADER_OFFSET_OFFSET); + + const localHeaderOffset = BigInt(oldFormatOffset); + const extraField = new DataView( + await file.slice(extraOffset, extraOffset + BigInt(extraFieldLength)) + ); + // looking for info that might be also be in zip64 extra field + + const zip64data: Zip64Data = { + uncompressedSize, + compressedSize, + localHeaderOffset, + startDisk + }; + + const res = findZip64DataInExtra(zip64data, extraField); + + return { + ...zip64data, + ...res, + extraFieldLength, + fileNameLength, + fileName, + extraOffset + }; +}; + +/** + * Create iterator over files of zip archive + * @param fileProvider - file provider that provider random access to the file + */ +export async function* makeZipCDHeaderIterator( + fileProvider: FileProvider +): AsyncIterable { + const {cdStartOffset} = await parseEoCDRecord(fileProvider); + let cdHeader = await parseZipCDFileHeader(cdStartOffset, fileProvider); + while (cdHeader) { + yield cdHeader; + cdHeader = await parseZipCDFileHeader( + cdHeader.extraOffset + BigInt(cdHeader.extraFieldLength), + fileProvider + ); + } +} +/** + * returns the number written in the provided bytes + * @param bytes two bytes containing the number + * @returns the number written in the provided bytes + */ +const getUint16 = (...bytes: [number, number]) => { + return bytes[0] + bytes[1] * 16; +}; + +/** + * reads all nesessary data from zip64 record in the extra data + * @param zip64data values that might be in zip64 record + * @param extraField full extra data + * @returns data read from zip64 + */ + +const findZip64DataInExtra = (zip64data: Zip64Data, extraField: DataView): Partial => { + const zip64dataList = findExpectedData(zip64data); + + const zip64DataRes: Partial = {}; + if (zip64dataList.length > 0) { + // total length of data in zip64 notation in bytes + const zip64chunkSize = zip64dataList.reduce((sum, curr) => sum + curr.length, 0); + // we're looking for the zip64 nontation header (0x0001) + // and a size field with a correct value next to it + const offsetInExtraData = new Uint8Array(extraField.buffer).findIndex( + (_val, i, arr) => + getUint16(arr[i], arr[i + 1]) === 0x0001 && + getUint16(arr[i + 2], arr[i + 3]) === zip64chunkSize + ); + // then we read all the nesessary fields from the zip64 data + let bytesRead = 0; + for (const note of zip64dataList) { + const offset = bytesRead; + zip64DataRes[note.name] = extraField.getBigUint64(offsetInExtraData + 4 + offset, true); + bytesRead = offset + note.length; + } + } + + return zip64DataRes; +}; + +/** + * frind data that's expected to be in zip64 + * @param zip64data values that might be in zip64 record + * @returns zip64 data description + */ + +const findExpectedData = (zip64data: Zip64Data): {length: number; name: string}[] => { + // We define fields that should be in zip64 data + const zip64dataList: {length: number; name: string}[] = []; + if (zip64data.uncompressedSize === BigInt(0xffffffff)) { + zip64dataList.push({name: 'uncompressedSize', length: 8}); + } + if (zip64data.compressedSize === BigInt(0xffffffff)) { + zip64dataList.push({name: 'compressedSize', length: 8}); + } + if (zip64data.localHeaderOffset === BigInt(0xffffffff)) { + zip64dataList.push({name: 'localHeaderOffset', length: 8}); + } + if (zip64data.startDisk === BigInt(0xffffffff)) { + zip64dataList.push({name: 'startDisk', length: 4}); + } + + return zip64dataList; +}; diff --git a/modules/zip/src/parse-zip/end-of-central-directory.ts b/modules/zip/src/parse-zip/end-of-central-directory.ts new file mode 100644 index 0000000000..ddb5e7f331 --- /dev/null +++ b/modules/zip/src/parse-zip/end-of-central-directory.ts @@ -0,0 +1,61 @@ +import {FileProvider, compareArrayBuffers} from '@loaders.gl/loader-utils'; +import {ZipSignature, searchFromTheEnd} from './search-from-the-end'; + +/** + * End of central directory info + * according to https://en.wikipedia.org/wiki/ZIP_(file_format) + */ +export type ZipEoCDRecord = { + /** Relative offset of local file header */ + cdStartOffset: bigint; + /** Relative offset of local file header */ + cdRecordsNumber: bigint; +}; + +const eoCDSignature: ZipSignature = new Uint8Array([0x50, 0x4b, 0x05, 0x06]); +const zip64EoCDLocatorSignature = new Uint8Array([0x50, 0x4b, 0x06, 0x07]); +const zip64EoCDSignature = new Uint8Array([0x50, 0x4b, 0x06, 0x06]); + +// offsets accroding to https://en.wikipedia.org/wiki/ZIP_(file_format) +const CD_RECORDS_NUMBER_OFFSET = 8n; +const CD_START_OFFSET_OFFSET = 16n; +const ZIP64_EOCD_START_OFFSET_OFFSET = 8n; +const ZIP64_CD_RECORDS_NUMBER_OFFSET = 24n; +const ZIP64_CD_START_OFFSET_OFFSET = 48n; + +/** + * Parses end of central directory record of zip file + * @param file - FileProvider instance + * @returns Info from the header + */ +export const parseEoCDRecord = async (file: FileProvider): Promise => { + const zipEoCDOffset = await searchFromTheEnd(file, eoCDSignature); + + let cdRecordsNumber = BigInt(await file.getUint16(zipEoCDOffset + CD_RECORDS_NUMBER_OFFSET)); + let cdStartOffset = BigInt(await file.getUint32(zipEoCDOffset + CD_START_OFFSET_OFFSET)); + + if (cdStartOffset === BigInt(0xffffffff) || cdRecordsNumber === BigInt(0xffffffff)) { + const zip64EoCDLocatorOffset = zipEoCDOffset - 20n; + + const magicBytes = await file.slice(zip64EoCDLocatorOffset, zip64EoCDLocatorOffset + 4n); + if (!compareArrayBuffers(magicBytes, zip64EoCDLocatorSignature)) { + throw new Error('zip64 EoCD locator not found'); + } + const zip64EoCDOffset = await file.getBigUint64( + zip64EoCDLocatorOffset + ZIP64_EOCD_START_OFFSET_OFFSET + ); + + const endOfCDMagicBytes = await file.slice(zip64EoCDOffset, zip64EoCDOffset + 4n); + if (!compareArrayBuffers(endOfCDMagicBytes, zip64EoCDSignature.buffer)) { + throw new Error('zip64 EoCD not found'); + } + + cdRecordsNumber = await file.getBigUint64(zip64EoCDOffset + ZIP64_CD_RECORDS_NUMBER_OFFSET); + cdStartOffset = await file.getBigUint64(zip64EoCDOffset + ZIP64_CD_START_OFFSET_OFFSET); + } + + return { + cdRecordsNumber, + cdStartOffset + }; +}; diff --git a/modules/zip/src/parse-zip/local-file-header.ts b/modules/zip/src/parse-zip/local-file-header.ts new file mode 100644 index 0000000000..1bfa7a2bc1 --- /dev/null +++ b/modules/zip/src/parse-zip/local-file-header.ts @@ -0,0 +1,93 @@ +import {FileProvider, compareArrayBuffers} from '@loaders.gl/loader-utils'; +import {ZipSignature} from './search-from-the-end'; + +/** + * zip local file header info + * according to https://en.wikipedia.org/wiki/ZIP_(file_format) + */ +export type ZipLocalFileHeader = { + /** File name length */ + fileNameLength: number; + /** File name */ + fileName: string; + /** Extra field length */ + extraFieldLength: number; + /** Offset of the file data */ + fileDataOffset: bigint; + /** Compressed size */ + compressedSize: bigint; + /** Compression method */ + compressionMethod: number; +}; + +// offsets accroding to https://en.wikipedia.org/wiki/ZIP_(file_format) +const COMPRESSION_METHOD_OFFSET = 8n; +const COMPRESSED_SIZE_OFFSET = 18n; +const UNCOMPRESSED_SIZE_OFFSET = 22n; +const FILE_NAME_LENGTH_OFFSET = 26n; +const EXTRA_FIELD_LENGTH_OFFSET = 28n; +const FILE_NAME_OFFSET = 30n; + +export const signature: ZipSignature = new Uint8Array([0x50, 0x4b, 0x03, 0x04]); + +/** + * Parses local file header of zip file + * @param headerOffset - offset in the archive where header starts + * @param buffer - buffer containing whole array + * @returns Info from the header + */ +export const parseZipLocalFileHeader = async ( + headerOffset: bigint, + buffer: FileProvider +): Promise => { + const magicBytes = await buffer.slice(headerOffset, headerOffset + 4n); + if (!compareArrayBuffers(magicBytes, signature)) { + return null; + } + + const fileNameLength = await buffer.getUint16(headerOffset + FILE_NAME_LENGTH_OFFSET); + + const fileName = new TextDecoder() + .decode( + await buffer.slice( + headerOffset + FILE_NAME_OFFSET, + headerOffset + FILE_NAME_OFFSET + BigInt(fileNameLength) + ) + ) + .split('\\') + .join('/'); + const extraFieldLength = await buffer.getUint16(headerOffset + EXTRA_FIELD_LENGTH_OFFSET); + + let fileDataOffset = headerOffset + FILE_NAME_OFFSET + BigInt(fileNameLength + extraFieldLength); + + const compressionMethod = await buffer.getUint16(headerOffset + COMPRESSION_METHOD_OFFSET); + + let compressedSize = BigInt(await buffer.getUint32(headerOffset + COMPRESSED_SIZE_OFFSET)); // add zip 64 logic + + let uncompressedSize = BigInt(await buffer.getUint32(headerOffset + UNCOMPRESSED_SIZE_OFFSET)); // add zip 64 logic + + const extraOffset = headerOffset + FILE_NAME_OFFSET + BigInt(fileNameLength); + + let offsetInZip64Data = 4n; + // looking for info that might be also be in zip64 extra field + if (uncompressedSize === BigInt(0xffffffff)) { + uncompressedSize = await buffer.getBigUint64(extraOffset + offsetInZip64Data); + offsetInZip64Data += 8n; + } + if (compressedSize === BigInt(0xffffffff)) { + compressedSize = await buffer.getBigUint64(extraOffset + offsetInZip64Data); + offsetInZip64Data += 8n; + } + if (fileDataOffset === BigInt(0xffffffff)) { + fileDataOffset = await buffer.getBigUint64(extraOffset + offsetInZip64Data); // setting it to the one from zip64 + } + + return { + fileNameLength, + fileName, + extraFieldLength, + fileDataOffset, + compressedSize, + compressionMethod + }; +}; diff --git a/modules/zip/src/parse-zip/search-from-the-end.ts b/modules/zip/src/parse-zip/search-from-the-end.ts new file mode 100644 index 0000000000..5630ee0f5b --- /dev/null +++ b/modules/zip/src/parse-zip/search-from-the-end.ts @@ -0,0 +1,38 @@ +import {FileProvider} from '@loaders.gl/loader-utils'; + +/** Description of zip signature type */ +export type ZipSignature = Uint8Array; + +/** + * looking for the last occurrence of the provided + * @param file + * @param target + * @returns + */ +export const searchFromTheEnd = async ( + file: FileProvider, + target: ZipSignature +): Promise => { + const searchWindow = [ + await file.getUint8(file.length - 1n), + await file.getUint8(file.length - 2n), + await file.getUint8(file.length - 3n), + undefined + ]; + + let targetOffset = 0n; + + // looking for the last record in the central directory + for (let i = file.length - 4n; i > -1; i--) { + searchWindow[3] = searchWindow[2]; + searchWindow[2] = searchWindow[1]; + searchWindow[1] = searchWindow[0]; + searchWindow[0] = await file.getUint8(i); + if (searchWindow.every((val, index) => val === target[index])) { + targetOffset = i; + break; + } + } + + return targetOffset; +}; diff --git a/modules/zip/src/tar-builder.ts b/modules/zip/src/tar-builder.ts index e1ed6d8cc3..a882ff9d00 100644 --- a/modules/zip/src/tar-builder.ts +++ b/modules/zip/src/tar-builder.ts @@ -11,14 +11,14 @@ type TarBuilderOptions = { /** * Build a tar file by adding files */ -export default class TARBuilder { +export class TarBuilder { static get properties() { return { id: 'tar', name: 'TAR', extensions: ['tar'], mimeTypes: ['application/x-tar'], - builder: TARBuilder, + builder: TarBuilder, options: TAR_BUILDER_OPTIONS }; } diff --git a/modules/zip/src/zip-loader.ts b/modules/zip/src/zip-loader.ts index 49e571d124..baafca5945 100644 --- a/modules/zip/src/zip-loader.ts +++ b/modules/zip/src/zip-loader.ts @@ -1,4 +1,6 @@ -// Zip loader +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils'; import JSZip from 'jszip'; @@ -64,5 +66,3 @@ async function loadZipEntry(jsZip: any, subFilename: string, options: any = {}) return error; } } - -export const _typecheckZipLoader: LoaderWithParser = ZipLoader; diff --git a/modules/zip/src/zip-writer.ts b/modules/zip/src/zip-writer.ts index ca2681cc4a..8889f34897 100644 --- a/modules/zip/src/zip-writer.ts +++ b/modules/zip/src/zip-writer.ts @@ -1,10 +1,21 @@ -import type {Writer} from '@loaders.gl/loader-utils'; -import JSZip from 'jszip'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import type {Writer, WriterOptions} from '@loaders.gl/loader-utils'; +import JSZip, {JSZipGeneratorOptions} from 'jszip'; + +export type ZipWriterOptions = WriterOptions & { + zip?: { + onUpdate?: (metadata: {percent: number}) => void; + }; + /** Passthrough options to jszip */ + jszip?: JSZipGeneratorOptions; +}; /** * Zip exporter */ -export const ZipWriter: Writer = { +export const ZipWriter: Writer = { name: 'Zip Archive', extensions: ['zip'], category: 'archive', @@ -13,7 +24,10 @@ export const ZipWriter: Writer = { encode: encodeZipAsync }; -async function encodeZipAsync(fileMap: any, options: any = {}) { +async function encodeZipAsync( + fileMap: Record, + options: ZipWriterOptions = {} +) { const jsZip = new JSZip(); // add files to the zip for (const subFileName in fileMap) { @@ -21,17 +35,17 @@ async function encodeZipAsync(fileMap: any, options: any = {}) { // jszip supports both arraybuffer and string data (the main loaders.gl types) // https://stuk.github.io/jszip/documentation/api_zipobject/async.html - jsZip.file(subFileName, subFileData, options); + jsZip.file(subFileName, subFileData, options?.jszip || {}); } // always generate the full zip as an arraybuffer - options = Object.assign({}, options, { - type: 'arraybuffer' - }); + const jszipOptions: JSZipGeneratorOptions = {...options?.jszip, type: 'arraybuffer'}; const {onUpdate = () => {}} = options; - return jsZip.generateAsync(options, onUpdate).catch((error) => { + try { + return await jsZip.generateAsync(jszipOptions, onUpdate); + } catch (error) { options.log.error(`Unable to write zip archive: ${error}`); throw error; - }); + } } diff --git a/modules/zip/test/data/test-store.zip b/modules/zip/test/data/test-store.zip new file mode 100644 index 0000000000..194f68b646 Binary files /dev/null and b/modules/zip/test/data/test-store.zip differ diff --git a/modules/zip/test/filesystems/zip-filesystem.spec.ts b/modules/zip/test/filesystems/zip-filesystem.spec.ts new file mode 100644 index 0000000000..114ae4f2a1 --- /dev/null +++ b/modules/zip/test/filesystems/zip-filesystem.spec.ts @@ -0,0 +1,88 @@ +import test from 'tape-promise/tape'; + +import {fetchFile, isBrowser} from '@loaders.gl/core'; +import {FileHandleFile} from '@loaders.gl/loader-utils'; +import {DataViewFile} from '@loaders.gl/loader-utils'; +import {FileProvider} from '@loaders.gl/loader-utils'; +import {ZipFileSystem} from '../../src/filesystems/zip-filesystem'; + +const ZIP_FILE_PATH = '@loaders.gl/zip/test/data/test-store.zip'; + +test('zip#ZipFileSystem - initialize from existing fileHandler', async (t) => { + const fileProvider = await getFileProvider(ZIP_FILE_PATH); + const fileSystem = new ZipFileSystem(fileProvider); + const files = await fileSystem.readdir(); + await fileSystem.destroy(); + t.ok(fileSystem); + t.deepEqual(files, ['test-file.txt']); + t.end(); +}); + +test('zip#ZipFileSystem - initialize with zip file path', async (t) => { + if (isBrowser) { + t.throws(() => new ZipFileSystem(ZIP_FILE_PATH)); + } else { + const fileSystem = new ZipFileSystem(ZIP_FILE_PATH); + const files = await fileSystem.readdir(); + await fileSystem.destroy(); + t.ok(fileSystem); + t.deepEqual(files, ['test-file.txt']); + } + t.end(); +}); + +test('zip#ZipFileSystem - get stat for the first file', async (t) => { + const fileProvider = await getFileProvider(ZIP_FILE_PATH); + const fileSystem = new ZipFileSystem(fileProvider); + const files = await fileSystem.readdir(); + const stats = await fileSystem.stat(files[0]); + await fileSystem.destroy(); + t.ok(stats); + t.equal(stats.compressedSize, 15n); + t.equal(stats.uncompressedSize, 15n); + t.equal(stats.fileName, 'test-file.txt'); + t.equal(stats.fileNameLength, 13); + t.equal(stats.extraFieldLength, 24); + t.equal(stats.extraOffset, 145n); + t.equal(stats.localHeaderOffset, 0n); + t.equal(stats.size, 15); + t.end(); +}); + +test('zip#ZipFileSystem - get stat should fail', async (t) => { + const fileProvider = await getFileProvider(ZIP_FILE_PATH); + const fileSystem = new ZipFileSystem(fileProvider); + t.rejects(() => fileSystem.stat('not-existing-file.xyz')); + await fileSystem.destroy(); + t.end(); +}); + +test('zip#ZipFileSystem - fetch the file', async (t) => { + const fileProvider = await getFileProvider(ZIP_FILE_PATH); + const fileSystem = new ZipFileSystem(fileProvider); + const fileResponse = await fileSystem.fetch('test-file.txt'); + const text = await fileResponse.text(); + await fileSystem.destroy(); + t.equal(text, 'test file data\n'); + t.end(); +}); + +test('zip#ZipFileSystem - fetch should fail', async (t) => { + const fileProvider = await getFileProvider(ZIP_FILE_PATH); + const fileSystem = new ZipFileSystem(fileProvider); + t.rejects(() => fileSystem.fetch('not-existing-file.xyz')); + await fileSystem.destroy(); + t.end(); +}); + +const getFileProvider = async (fileName: string) => { + let fileProvider: FileProvider; + if (isBrowser) { + const fileResponse = await fetchFile(ZIP_FILE_PATH); + const file = await fileResponse.arrayBuffer(); + fileProvider = new DataViewFile(new DataView(file)); + } else { + fileProvider = new FileHandleFile(ZIP_FILE_PATH); + } + return fileProvider; +}; diff --git a/modules/zip/test/index.js b/modules/zip/test/index.js deleted file mode 100644 index 59938710e7..0000000000 --- a/modules/zip/test/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import './zip-writer-loader.spec'; -import './tar-builder.spec'; diff --git a/modules/zip/test/index.ts b/modules/zip/test/index.ts new file mode 100644 index 0000000000..659f6601de --- /dev/null +++ b/modules/zip/test/index.ts @@ -0,0 +1,9 @@ +import './zip-writer-loader.spec'; +import './tar-builder.spec'; + +import './filesystems/zip-filesystem.spec'; + +import './zip-utils/cd-file-header.spec'; +import './zip-utils/end-of-central-directory.spec'; +import './zip-utils/local-file-header.spec'; +import './zip-utils/search-from-the-end.spec'; diff --git a/modules/zip/test/tar-builder.spec.js b/modules/zip/test/tar-builder.spec.ts similarity index 100% rename from modules/zip/test/tar-builder.spec.js rename to modules/zip/test/tar-builder.spec.ts diff --git a/modules/zip/test/zip-utils/cd-file-header.spec.ts b/modules/zip/test/zip-utils/cd-file-header.spec.ts new file mode 100644 index 0000000000..ff0ba0db96 --- /dev/null +++ b/modules/zip/test/zip-utils/cd-file-header.spec.ts @@ -0,0 +1,17 @@ +import test from 'tape-promise/tape'; +import {DATA_ARRAY} from '@loaders.gl/i3s/test/data/test.zip'; + +import {DataViewFile} from '@loaders.gl/loader-utils'; +import {parseZipCDFileHeader} from '../../src//parse-zip/cd-file-header'; + +test('SLPKLoader#central directory file header parse', async (t) => { + const cdFileHeader = await parseZipCDFileHeader( + 78n, + new DataViewFile(new DataView(DATA_ARRAY.buffer)) + ); + t.deepEqual(cdFileHeader?.compressedSize, 39n); + t.deepEqual(cdFileHeader?.fileNameLength, 9); + t.deepEqual(cdFileHeader?.fileName, 'test.json'); + t.deepEqual(cdFileHeader?.localHeaderOffset, 0n); + t.end(); +}); diff --git a/modules/zip/test/zip-utils/end-of-central-directory.spec.ts b/modules/zip/test/zip-utils/end-of-central-directory.spec.ts new file mode 100644 index 0000000000..50ba9357dd --- /dev/null +++ b/modules/zip/test/zip-utils/end-of-central-directory.spec.ts @@ -0,0 +1,12 @@ +import test from 'tape-promise/tape'; +import {DATA_ARRAY} from '@loaders.gl/i3s/test/data/test.zip'; +import {parseEoCDRecord} from '../../src/parse-zip/end-of-central-directory'; +import {DataViewFile} from '@loaders.gl/loader-utils'; +import {parseZipCDFileHeader} from '../../src/parse-zip/cd-file-header'; + +test('SLPKLoader#eon of central directory record parse', async (t) => { + const provider = new DataViewFile(new DataView(DATA_ARRAY.buffer)); + const localFileHeader = await parseEoCDRecord(provider); + t.ok(parseZipCDFileHeader(localFileHeader?.cdStartOffset, provider)); + t.end(); +}); diff --git a/modules/zip/test/zip-utils/local-file-header.spec.ts b/modules/zip/test/zip-utils/local-file-header.spec.ts new file mode 100644 index 0000000000..94986483a7 --- /dev/null +++ b/modules/zip/test/zip-utils/local-file-header.spec.ts @@ -0,0 +1,15 @@ +import test from 'tape-promise/tape'; +import {DATA_ARRAY} from '@loaders.gl/i3s/test/data/test.zip'; + +import {DataViewFile} from '@loaders.gl/loader-utils'; +import {parseZipLocalFileHeader} from '../../src/parse-zip/local-file-header'; + +test('SLPKLoader#local file header parse', async (t) => { + const localFileHeader = await parseZipLocalFileHeader( + 0n, + new DataViewFile(new DataView(DATA_ARRAY.buffer)) + ); + t.deepEqual(localFileHeader?.compressedSize, 39n); + t.deepEqual(localFileHeader?.fileNameLength, 9); + t.end(); +}); diff --git a/modules/zip/test/zip-utils/search-from-the-end.spec.ts b/modules/zip/test/zip-utils/search-from-the-end.spec.ts new file mode 100644 index 0000000000..1332c536c4 --- /dev/null +++ b/modules/zip/test/zip-utils/search-from-the-end.spec.ts @@ -0,0 +1,16 @@ +import test from 'tape-promise/tape'; +import {DATA_ARRAY} from '@loaders.gl/i3s/test/data/test.zip'; + +import {DataViewFile} from '@loaders.gl/loader-utils'; +import {searchFromTheEnd} from '../../src/parse-zip/search-from-the-end'; + +test('SLPKLoader#searchFromTheEnd', async (t) => { + t.equals( + await searchFromTheEnd( + new DataViewFile(new DataView(DATA_ARRAY.buffer)), + new Uint8Array([0x50, 0x4b, 0x03, 0x04]) + ), + 0n + ); + t.end(); +}); diff --git a/modules/zip/test/zip-writer-loader.spec.js b/modules/zip/test/zip-writer-loader.spec.ts similarity index 53% rename from modules/zip/test/zip-writer-loader.spec.js rename to modules/zip/test/zip-writer-loader.spec.ts index eb1e73bcd3..e2f9080f70 100644 --- a/modules/zip/test/zip-writer-loader.spec.js +++ b/modules/zip/test/zip-writer-loader.spec.ts @@ -17,18 +17,12 @@ test('Zip#loader/writer conformance', (t) => { t.end(); }); -test('Zip#encode/decode', (t) => { - encode(FILE_MAP, ZipWriter) - .then((arrayBuffer) => parse(arrayBuffer, ZipLoader)) - .then((fileMap) => { - for (const key in FILE_MAP) { - const text = new TextDecoder().decode(fileMap[key]); - t.equal(text, FILE_MAP[key], `Subfile ${key} encoded/decoded correctly`); - } - t.end(); - }) - .catch((error) => { - t.fail(error.message); - t.end(); - }); +test('Zip#encode/decode', async (t) => { + const arrayBuffer = await encode(FILE_MAP, ZipWriter); + const fileMap = await parse(arrayBuffer, ZipLoader); + for (const key in FILE_MAP) { + const text = new TextDecoder().decode(fileMap[key]); + t.equal(text, FILE_MAP[key], `Subfile ${key} encoded/decoded correctly`); + } + t.end(); }); diff --git a/modules/zip/tsconfig.json b/modules/zip/tsconfig.json index 6632898456..1580d14c14 100644 --- a/modules/zip/tsconfig.json +++ b/modules/zip/tsconfig.json @@ -8,6 +8,9 @@ "outDir": "dist" }, "references": [ - {"path": "../loader-utils"} + {"path": "../loader-utils"}, + {"path": "../core"}, + {"path": "../compression"}, + {"path": "../crypto"} ] } diff --git a/package.json b/package.json index 7067137f57..521f59ed6e 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "loaders.gl", "description": "Framework-independent loaders for visualization, 3D graphics and geospatial formats", "license": "MIT", + "type": "module", "private": true, "repository": { "type": "git", @@ -30,10 +31,9 @@ "install-fast": "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true yarn", "start": "echo 'Please see loaders.gl website for how to run examples' && open https://loaders.gl/docs", "clean": "ocular-clean && npm run tsclean", - "build": "npm run clean && npm run tsbuild && ocular-build --dist es5,esm && lerna run pre-build", + "build": "npm run clean && ocular-build && lerna run pre-build", "build-workers": "lerna run pre-build", "tsclean": "find . -name tsconfig.tsbuildinfo -exec rm {} \\;", - "tsbuild": "tsc -b tsconfig.build.json && scripts/add-js-to-imports.sh", "cover": "ocular-test cover", "lint": "tsc && ocular-lint", "bump": "ocular-bump", @@ -45,35 +45,30 @@ "pre-push": "yarn lint" }, "devDependencies": { - "@babel/plugin-transform-runtime": "^7.14.5", "@probe.gl/bench": "^4.0.2", "@probe.gl/test-utils": "^4.0.2", "@types/tape-promise": "^4.0.1", "base64-inline-loader": "^1.1.1", "base64-loader": "^1.0.0", - "coveralls": "^3.0.3", - "esbuild": "^0.13.13", - "ocular-dev-tools": "1.0.0-alpha.7", + "ocular-dev-tools": "2.0.0-alpha.18", "pre-commit": "^1.2.2", - "pre-push": "^0.1.1", - "ts-node": "^10.4.0" + "pre-push": "^0.1.1" }, "pre-commit": "pre-commit", "pre-push": "pre-push", "dependencies": {}, "resolutions_notes": [ - "Something triggers double luma.gl versions after deck.gl 8.9 upgrade", - "Note: tape 4.12 and higher no longer compares 0 and -0 equally...", - "Note: newer prettier breaks on typescript import type" + "Ensure we use recent typescript", + "Note: prettier 3 required for JSON import assertions" ], "resolutions": { - "@luma.gl/core": "8.5.20", - "tape": "4.11.0", - "prettier": "2.3.1", - "typescript": "^5.0.4" + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser" : "^6.0.0", + "prettier": "3.0.3", + "typescript": "^5.2.2" }, "volta": { - "node": "16.19.1", + "node": "18.18.2", "yarn": "1.22.19" } } diff --git a/scripts/webpack/bundle.js b/scripts/webpack/bundle.js deleted file mode 100644 index 231cd968ad..0000000000 --- a/scripts/webpack/bundle.js +++ /dev/null @@ -1,142 +0,0 @@ -const {resolve} = require('path'); -const webpack = require('webpack'); - -const {getOcularConfig} = require('ocular-dev-tools'); -const {getExternals} = require('./helpers'); - -const ocularConfig = getOcularConfig({ - aliasMode: 'src', - root: resolve(__dirname, '../..') -}); - -const ALIASES = ocularConfig.aliases; - -const PACKAGE_ROOT = resolve('.'); -const DIST_PATH = resolve('dist'); -const PACKAGE_INFO = require(resolve(PACKAGE_ROOT, 'package.json')); - -const NODE = { - Buffer: false, - fs: 'empty', - http: 'empty', - https: 'empty', - path: 'empty', - crypto: 'empty', - tls: 'empty', - net: 'empty', - child_process: 'empty' -}; - -const ES5_BABEL_CONFIG = { - presets: ['@babel/preset-typescript', ['@babel/preset-env', {forceAllTransforms: true}]], - plugins: [ - // webpack 4 cannot parse the most recent JS syntax - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-nullish-coalescing-operator', - // typescript supports class properties - '@babel/plugin-proposal-class-properties', - // inject __VERSION__ from package.json - 'version-inline', - ['@babel/plugin-transform-modules-commonjs', {allowTopLevelThis: true}] - ] -}; - -const ES6_BABEL_CONFIG = { - presets: ['@babel/preset-typescript'], - plugins: [ - // webpack 4 cannot parse the most recent JS syntax - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-nullish-coalescing-operator', - // typescript supports class properties - '@babel/plugin-proposal-class-properties', - // inject __VERSION__ from package.json - 'version-inline' - ] -}; - -const config = { - name: 'production', - mode: 'production', - - devtool: 'source-map', - - stats: 'errors-only', - - entry: { - main: resolve('./src/bundle.ts') - }, - - output: { - libraryTarget: 'umd', - path: DIST_PATH, - filename: 'dist.min.js' - }, - - node: NODE, - - resolve: { - extensions: ['.js', '.mjs', '.jsx', '.ts', '.tsx', '.json'], - alias: ALIASES - }, - - module: { - rules: [ - { - test: /\.(js|ts)$/, - loader: 'babel-loader', - include: /src/, - options: ES6_BABEL_CONFIG - }, - { - test: /\.mjs$/, - include: /node_modules/, - type: 'javascript/auto' - } - ] - }, - - externals: getExternals(PACKAGE_INFO), - - plugins: [ - // This is used to define the __VERSION__ constant in core/lib/init.js - // babel-plugin-version-inline uses the package version from the working directory - // Therefore we need to manually import the correct version from the core - // This is called in prepublishOnly, after lerna bumps the package versions - new webpack.DefinePlugin({ - __VERSION__: JSON.stringify(PACKAGE_INFO.version) - }) - ] -}; - -// const developmentConfig = { -// ...config, -// name: 'development', -// mode: 'development', -// output: { -// filename: 'dist.dev.js' -// } -// }; - -// @ts-ignore -const es5Config = { - ...config, - name: 'es5', - output: { - filename: 'dist.es5.min.js' - }, - module: { - rules: [ - { - // Compile ES2015 using babel - test: /\.(js|ts)$/, - loader: 'babel-loader', - include: /src/, - options: ES5_BABEL_CONFIG - } - ] - } -}; - -// console.error(JSON.stringify(config, null, 2)) - -module.exports = [config]; // , es5Config] - DROP ES5 in 3.1 diff --git a/scripts/webpack/helpers.js b/scripts/webpack/helpers.js deleted file mode 100644 index 83944d4841..0000000000 --- a/scripts/webpack/helpers.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * peerDependencies are excluded using `externals` - * https://webpack.js.org/configuration/externals/ - * e.g. @deck.gl/core is not bundled with @deck.gl/geolayers - * @param {{peerDependencies: any, browser: any}} packageInfo - */ -module.exports.getExternals = function getExternals(packageInfo) { - /** @type {Record} */ - const externals = { - // Hard coded externals - }; - - const {peerDependencies = {}, browser} = packageInfo; - - Object.assign(externals, browser); - - for (const depName in peerDependencies) { - if (depName.startsWith('@loaders.gl')) { - // Instead of bundling the dependency, import from the global `deck` object - externals[depName] = 'loaders'; - } - } - - return externals; -} diff --git a/scripts/webpack/worker.js b/scripts/webpack/worker.js deleted file mode 100644 index 4691f91a78..0000000000 --- a/scripts/webpack/worker.js +++ /dev/null @@ -1,124 +0,0 @@ -// Config for bundling workers -const {resolve} = require('path'); -const {getOcularConfig} = require('ocular-dev-tools'); - -const ALIASES = getOcularConfig({ - aliasMode: 'src', - root: resolve(__dirname, '../..') -}).aliases; - -const BABEL_CONFIG = { - presets: [ - '@babel/typescript', - // We must transpile to es6 to enable tree shaking - ['@babel/preset-env', {modules: false}] - ], - plugins: [ - '@babel/plugin-transform-runtime', - // webpack 4 cannot parse the most recent JS syntax - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-nullish-coalescing-operator', - // inject __VERSION__ from package.json - 'version-inline' - ] -}; - -const CONFIG = { - mode: 'production', - - devtool: 'source-map', - - stats: 'errors-only', - - resolve: { - extensions: ['.js', '.mjs', '.jsx', '.ts', '.tsx'], - alias: ALIASES - }, - - module: { - rules: [ - { - // Compile - test: /\.(js|ts)$/, - exclude: /node_modules|libs/, - use: [ - { - loader: 'babel-loader', - options: BABEL_CONFIG - } - ] - }, - { - test: /\.mjs$/, - include: /node_modules/, - type: "javascript/auto" - }, - { - // LIBS: Already compiled, just process with babel - e.g. copy to dist - test: /libs\.*\.js$/, - exclude: /node_modules|/, - use: [ - { - loader: 'babel-loader', - options: { - compact: false - } - } - ] - } - ] - }, - - node: { - fs: 'empty', - Buffer: false - } -}; - -// For dev workers: -// Minimize transpilation, disable regenerator transforms, and use non-transpiled vis.gl dependencies. -// TODO - duplicates local example setup. Generalize and move to ocular-dev-tools -function addESNextSettings(config) { - // Add 'esnext' to make sure vis.gl frameworks are imported with minimal transpilation - config.resolve = config.resolve || {}; - config.resolve.mainFields = config.resolve.mainFields || ['browser', 'module', 'main']; - config.resolve.mainFields.shift('esnext'); - - // Look for babel plugin - config.module = config.module || {}; - config.module.rules = config.module.rules || []; - const babelRuleWrapper = config.module.rules.find(rule => { - // console.log('rule', JSON.stringify(rule.use, null, 2)); - return rule.use && (rule.use[0].loader === 'babel-loader'); - }); - - const babelRule = babelRuleWrapper && babelRuleWrapper.use[0]; - - // If found, inject excludes in @babel/present-env to prevent transpile - if (babelRule && babelRule.options && babelRule.options.presets) { - babelRule.options.presets = babelRule.options.presets.map(preset => { - if (preset === '@babel/preset-env' || preset[0] === '@babel/preset-env') { - return [ - '@babel/preset-env', - { - targets: {chrome: 80}, - exclude: [/transform-async-to-generator/, /transform-regenerator/] - } - ]; - } - return preset; - }); - } - return config; -} - -module.exports = (env = {}) => { - let config = CONFIG; - - if (env.dev) { - config.mode = 'development'; - config = addESNextSettings(config); - } - // console.log(JSON.stringify(config, null, 2)); - return config; -}; diff --git a/test/aliases.js b/test/aliases.js deleted file mode 100644 index e0eaba7f38..0000000000 --- a/test/aliases.js +++ /dev/null @@ -1,54 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ - -import {path} from '@loaders.gl/loader-utils'; -const {resolve} = path; - -// NOTE - Replace with a transform of ocular-dev-tools aliases? -function makeAliases(basename = __dirname) { - return { - test: resolve(basename, '../test'), - '@loaders.gl/3d-tiles/test': resolve(basename, '../modules/3d-tiles/test'), - '@loaders.gl/arrow/test': resolve(basename, '../modules/arrow/test'), - '@loaders.gl/bson/test': resolve(basename, '../modules/bson/test'), - '@loaders.gl/compression/test': resolve(basename, '../modules/compression/test'), - '@loaders.gl/crypto/test': resolve(basename, '../modules/crypto/test'), - '@loaders.gl/core/test': resolve(basename, '../modules/core/test'), - '@loaders.gl/csv/test': resolve(basename, '../modules/csv/test'), - '@loaders.gl/draco/test': resolve(basename, '../modules/draco/test'), - '@loaders.gl/excel/test': resolve(basename, '../modules/excel/test'), - '@loaders.gl/flatgeobuf/test': resolve(basename, '../modules/flatgeobuf/test'), - '@loaders.gl/geopackage/test': resolve(basename, '../modules/geopackage/test'), - '@loaders.gl/geotiff/test': resolve(basename, '../modules/geotiff/test'), - '@loaders.gl/gis/test': resolve(basename, '../modules/gis/test'), - '@loaders.gl/gltf/test': resolve(basename, '../modules/gltf/test'), - '@loaders.gl/i3s/test': resolve(basename, '../modules/i3s/test'), - '@loaders.gl/images/test': resolve(basename, '../modules/images/test'), - '@loaders.gl/json/test': resolve(basename, '../modules/json/test'), - '@loaders.gl/kml/test': resolve(basename, '../modules/kml/test'), - '@loaders.gl/las/test': resolve(basename, '../modules/las/test'), - '@loaders.gl/mvt/test': resolve(basename, '../modules/mvt/test'), - '@loaders.gl/netcdf/test': resolve(basename, '../modules/netcdf/test'), - '@loaders.gl/obj/test': resolve(basename, '../modules/obj/test'), - '@loaders.gl/parquet/test': resolve(basename, '../modules/parquet/test'), - '@loaders.gl/pcd/test': resolve(basename, '../modules/pcd/test'), - '@loaders.gl/ply/test': resolve(basename, '../modules/ply/test'), - '@loaders.gl/polyfills/test': resolve(basename, '../modules/polyfills/test'), - '@loaders.gl/potree/test': resolve(basename, '../modules/potree/test'), - '@loaders.gl/shapefile/test': resolve(basename, '../modules/shapefile/test'), - '@loaders.gl/schema/test': resolve(basename, '../modules/schema/test'), - '@loaders.gl/terrain/test': resolve(basename, '../modules/terrain/test'), - '@loaders.gl/textures/test': resolve(basename, '../modules/textures/test'), - '@loaders.gl/tile-converter/test': resolve(basename, '../modules/tile-converter/test'), - '@loaders.gl/tiles/test': resolve(basename, '../modules/tiles/test'), - '@loaders.gl/video/test': resolve(basename, '../modules/video/test'), - '@loaders.gl/wkt/test': resolve(basename, '../modules/wkt/test'), - '@loaders.gl/wms/test': resolve(basename, '../modules/wms/test'), - '@loaders.gl/worker-utils/test': resolve(basename, '../modules/worker-utils/test'), - '@loaders.gl/xml/test': resolve(basename, '../modules/xml/test'), - '@loaders.gl/zarr/test': resolve(basename, '../modules/zarr/test'), - '@loaders.gl/zip/test': resolve(basename, '../modules/zip/test'), - 'node_modules': resolve(basename, '../node_modules') - }; -} - -export default makeAliases(); diff --git a/test/aliases.ts b/test/aliases.ts new file mode 100644 index 0000000000..77d089b39f --- /dev/null +++ b/test/aliases.ts @@ -0,0 +1,56 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ + +import {path} from '@loaders.gl/loader-utils'; +const {resolve} = path; + +// NOTE - Replace with a transform of ocular-dev-tools aliases? +function makeAliases() { + return { + test: resolve('./test'), + '@loaders.gl/3d-tiles/test': resolve('./modules/3d-tiles/test'), + '@loaders.gl/arrow/test': resolve('./modules/arrow/test'), + '@loaders.gl/bson/test': resolve('./modules/bson/test'), + '@loaders.gl/compression/test': resolve('./modules/compression/test'), + '@loaders.gl/crypto/test': resolve('./modules/crypto/test'), + '@loaders.gl/core/test': resolve('./modules/core/test'), + '@loaders.gl/csv/test': resolve('./modules/csv/test'), + '@loaders.gl/draco/test': resolve('./modules/draco/test'), + '@loaders.gl/excel/test': resolve('./modules/excel/test'), + '@loaders.gl/flatgeobuf/test': resolve('./modules/flatgeobuf/test'), + '@loaders.gl/geopackage/test': resolve('./modules/geopackage/test'), + '@loaders.gl/geotiff/test': resolve('./modules/geotiff/test'), + '@loaders.gl/gis/test': resolve('./modules/gis/test'), + '@loaders.gl/gltf/test': resolve('./modules/gltf/test'), + '@loaders.gl/i3s/test': resolve('./modules/i3s/test'), + '@loaders.gl/images/test': resolve('./modules/images/test'), + '@loaders.gl/json/test': resolve('./modules/json/test'), + '@loaders.gl/kml/test': resolve('./modules/kml/test'), + '@loaders.gl/las/test': resolve('./modules/las/test'), + '@loaders.gl/lerc/test': resolve('./modules/lerc/test'), + '@loaders.gl/mvt/test': resolve('./modules/mvt/test'), + '@loaders.gl/netcdf/test': resolve('./modules/netcdf/test'), + '@loaders.gl/obj/test': resolve('./modules/obj/test'), + '@loaders.gl/parquet/test': resolve('./modules/parquet/test'), + '@loaders.gl/pcd/test': resolve('./modules/pcd/test'), + '@loaders.gl/ply/test': resolve('./modules/ply/test'), + '@loaders.gl/pmtiles/test': resolve('./modules/pmtiles/test'), + '@loaders.gl/polyfills/test': resolve('./modules/polyfills/test'), + '@loaders.gl/potree/test': resolve('./modules/potree/test'), + '@loaders.gl/shapefile/test': resolve('./modules/shapefile/test'), + '@loaders.gl/schema/test': resolve('./modules/schema/test'), + '@loaders.gl/terrain/test': resolve('./modules/terrain/test'), + '@loaders.gl/textures/test': resolve('./modules/textures/test'), + '@loaders.gl/tile-converter/test': resolve('./modules/tile-converter/test'), + '@loaders.gl/tiles/test': resolve('./modules/tiles/test'), + '@loaders.gl/video/test': resolve('./modules/video/test'), + '@loaders.gl/wkt/test': resolve('./modules/wkt/test'), + '@loaders.gl/wms/test': resolve('./modules/wms/test'), + '@loaders.gl/worker-utils/test': resolve('./modules/worker-utils/test'), + '@loaders.gl/xml/test': resolve('./modules/xml/test'), + '@loaders.gl/zarr/test': resolve('./modules/zarr/test'), + '@loaders.gl/zip/test': resolve('./modules/zip/test'), + 'node_modules': resolve('./node_modules') + }; +} + +export default makeAliases(); diff --git a/test/apps/typescript-test/package.json b/test/apps/typescript-test/package.json index 2507ff72ea..03cbaedc52 100644 --- a/test/apps/typescript-test/package.json +++ b/test/apps/typescript-test/package.json @@ -31,7 +31,7 @@ "typescript": "^5.0.4" }, "dependencies": { - "@loaders.gl/shapefile": "^4.0.0-alpha.8" + "@loaders.gl/shapefile": "^4.0.0" }, "gitHead": "c95a4ff72512668a93d9041ce8636bac09333fd5" } diff --git a/test/apps/typescript-test/yarn.lock b/test/apps/typescript-test/yarn.lock index 23632837d7..36392917ee 100644 --- a/test/apps/typescript-test/yarn.lock +++ b/test/apps/typescript-test/yarn.lock @@ -43,47 +43,47 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== -"@loaders.gl/gis@3.3.0-alpha.12": - version "3.3.0-alpha.12" - resolved "https://registry.yarnpkg.com/@loaders.gl/gis/-/gis-3.3.0-alpha.12.tgz#ba6080df9148cefed9e77da8e0753d46053e00d7" - integrity sha512-yNvr90e6b27pmxcIXdwfgEIqv0KSLpKsDu7etkLTgYBeNpQnclf/YHG9tzlP9kPY9HEidm+K10XmSwpUGIknDw== +"@loaders.gl/gis@4.0.0-alpha.14": + version "4.0.0-alpha.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/gis/-/gis-4.0.0-alpha.14.tgz#c9a8e38e6e2b74261dec82026d228604a4e5379a" + integrity sha512-g0X2/7JpCbJCe/xoHedJnYoEsBpKLvaw2jKTd66UA6ZNFCfAy51VmhRCG9A6HhcG25NOd3uunMrsmebSSBsgPg== dependencies: - "@loaders.gl/loader-utils" "3.3.0-alpha.12" - "@loaders.gl/schema" "3.3.0-alpha.12" + "@loaders.gl/loader-utils" "4.0.0-alpha.14" + "@loaders.gl/schema" "4.0.0-alpha.14" "@mapbox/vector-tile" "^1.3.1" "@math.gl/polygon" "^3.5.1" pbf "^3.2.1" -"@loaders.gl/loader-utils@3.3.0-alpha.12": - version "3.3.0-alpha.12" - resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-3.3.0-alpha.12.tgz#55ce0de14422f64ddf72727716f5ebfa3ddf29f2" - integrity sha512-pBlHaXCbEaAwFe+XbuzoEjwbV4OPLiJ9DS+P3Cpe/ML2R9CT9UUxNDQMJOqM9cW/+hIG+kyUhatP3f67R5SOfQ== +"@loaders.gl/loader-utils@4.0.0-alpha.14": + version "4.0.0-alpha.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-4.0.0-alpha.14.tgz#d09d8867c7ef6036e23520e4c1c75690964a58a9" + integrity sha512-T4PCHX5r1ZP3BLyl9VAc9xdtYUgvJzVXdrbchRdCpyeEhkT8FVvOzW2PqYTii8uC7cJkAQZk7lJ13GrMsaMaYA== dependencies: "@babel/runtime" "^7.3.1" - "@loaders.gl/worker-utils" "3.3.0-alpha.12" - "@probe.gl/stats" "^3.5.0" + "@loaders.gl/worker-utils" "4.0.0-alpha.14" + "@probe.gl/stats" "^4.0.2" -"@loaders.gl/schema@3.3.0-alpha.12": - version "3.3.0-alpha.12" - resolved "https://registry.yarnpkg.com/@loaders.gl/schema/-/schema-3.3.0-alpha.12.tgz#bd78d48ae3c7c527bd55afedbc9dc6cb6e0e2afc" - integrity sha512-Kv7Auj2fEHU52kvegjFpK/Hjn83MWni3lVm2EpFEDc+Aw27dDb7U5sH7ow606qq7axiX+lC9JXaEDGYIBALn0Q== +"@loaders.gl/schema@4.0.0-alpha.14": + version "4.0.0-alpha.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/schema/-/schema-4.0.0-alpha.14.tgz#4cc04b834d7a7267cd7748b5a070a40021e813df" + integrity sha512-365OyfUF+Z3gi7XG4y4FYSpsefI+SW+qSMJIVwzE53z0bWNxgncPI5cuGHZPQFd4KYrFg0914z3O/FIj9ISaqA== dependencies: "@types/geojson" "^7946.0.7" -"@loaders.gl/shapefile@3.3.0-alpha.12": - version "3.3.0-alpha.12" - resolved "https://registry.yarnpkg.com/@loaders.gl/shapefile/-/shapefile-3.3.0-alpha.12.tgz#3d5096f74d8c5ebb269d971951a2eb21395792b1" - integrity sha512-2z0OOq+HbTEsH/nIr6Gi5v7fvx5Pdgqyrdk6VohmCgXKsTPupP5q2tX5Wmx8OBA4TfU+Q8XGydUFbLsmBBJS5Q== +"@loaders.gl/shapefile@^4.0.0-alpha.8": + version "4.0.0-alpha.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/shapefile/-/shapefile-4.0.0-alpha.14.tgz#6b9166eb1567c7d2348a6666eb5935e514abd4a0" + integrity sha512-Q2YS6K3lJfdImcYwz3srv+20CcWdX0HOnc9/Qi/jJAPyB9QAGAvAxCufqwE9YVQIET+O40gXYw6S1fSevKDxJQ== dependencies: - "@loaders.gl/gis" "3.3.0-alpha.12" - "@loaders.gl/loader-utils" "3.3.0-alpha.12" - "@loaders.gl/schema" "3.3.0-alpha.12" + "@loaders.gl/gis" "4.0.0-alpha.14" + "@loaders.gl/loader-utils" "4.0.0-alpha.14" + "@loaders.gl/schema" "4.0.0-alpha.14" "@math.gl/proj4" "^3.5.1" -"@loaders.gl/worker-utils@3.3.0-alpha.12": - version "3.3.0-alpha.12" - resolved "https://registry.yarnpkg.com/@loaders.gl/worker-utils/-/worker-utils-3.3.0-alpha.12.tgz#3c5f56be03b1359491e79674c6d5d71b3161e998" - integrity sha512-OOptTemcEvH0K0P/WJi13jXF2eerdcoxIDRVX0Q8sHHahkhD3+oGBwhilFj+oxTy1VQUiE/wienJRqZioQ22dQ== +"@loaders.gl/worker-utils@4.0.0-alpha.14": + version "4.0.0-alpha.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/worker-utils/-/worker-utils-4.0.0-alpha.14.tgz#ac8d6da13f64e0aa2fb089543092cc26fa7307f8" + integrity sha512-ntnJgfPEt4ym9mxXpl1PLOjsGLTlNI/8ivfpzHR0Dp0KvaXjFsL+Vle8g/+2BV2dmbihHhnARygNw9VmYbgsKw== dependencies: "@babel/runtime" "^7.3.1" @@ -145,10 +145,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@probe.gl/stats@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@probe.gl/stats/-/stats-3.5.0.tgz#774495772f06e898aae28c1d315c9edac07f3425" - integrity sha512-IH2M+F3c8HR1DTroBARePUFG7wIewumtKA0UFqx51Z7S4hKrD60wFbpMmg0AcF4FvHAXMBoC+kYi1UKW9XbAOw== +"@probe.gl/stats@^4.0.2": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@probe.gl/stats/-/stats-4.0.4.tgz#b33a47bf192951d0789dfd2044b295c3709386bd" + integrity sha512-SDuSY/D4yDL6LQDa69l/GCcnZLRiGYdyvYkxWb0CgnzTPdPrcdrzGkzkvpC3zsA4fEFw2smlDje370QGHwlisg== dependencies: "@babel/runtime" "^7.0.0" @@ -1445,19 +1445,19 @@ semver-diff@^3.1.1: semver "^6.3.0" semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.3.4, semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" @@ -1607,10 +1607,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.0.3: - version "4.4.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" - integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== +typescript@^5.0.4: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== undefsafe@^2.0.3: version "2.0.5" @@ -1683,9 +1683,9 @@ wkt-parser@^1.2.4: integrity sha512-XK5qV+Y5gsygQfHx2/cS5a7Zxsgleaw8iX5UPC5eOXPc0TgJAu1JB9lr0iYYX3zAnN3p0aNiaN5c+1Bdblxwrg== word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + version "1.2.4" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" + integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== wrap-ansi@^7.0.0: version "7.0.0" diff --git a/test/bench/browser.js b/test/bench/browser.js index b3b5257dc6..21c41d3f50 100644 --- a/test/bench/browser.js +++ b/test/bench/browser.js @@ -1,22 +1,5 @@ -// Copyright (c) 2015 - 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors import '@loaders.gl/polyfills'; diff --git a/test/bench/modules.js b/test/bench/modules.js index bea203f5f6..899f5d7131 100644 --- a/test/bench/modules.js +++ b/test/bench/modules.js @@ -1,22 +1,5 @@ -// Copyright (c) 2015 - 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors // Override aliases to point to publicly accessible github // TODO maybe setPathPrefix is enough? diff --git a/test/browser.js b/test/browser.js deleted file mode 100644 index 8f5b707b42..0000000000 --- a/test/browser.js +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2015 - 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -/* eslint-disable @typescript-eslint/no-var-requires */ - -require('@probe.gl/test-utils/polyfill'); -const test = require('tape'); - -// @ts-ignore TS2339: Property does not exist on type 'Window & typeof globalThis'. -test.onFinish(window.browserTestDriver_finish); -// @ts-ignore TS2339: Property does not exist on type 'Window & typeof globalThis'. -test.onFailure(window.browserTestDriver_fail); - -// This constant will be inlined by babel plugin. -// To test source without transpilation, set a fallback here. -// @ts-ignore TS2339: Property does not exist on type 'Global' -window.__VERSION__ = require('../lerna.json').version; - -test('Browser tests', t => { - require('./modules'); - // Render tests too flaky - disable for now - // require('./render'); - t.end(); -}); diff --git a/test/browser.ts b/test/browser.ts new file mode 100644 index 0000000000..5b5865c017 --- /dev/null +++ b/test/browser.ts @@ -0,0 +1,7 @@ +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors + +import './init-browser-test'; +import './modules'; +// Render tests too flaky - disable for now +// import './render'; diff --git a/test/common/conformance.js b/test/common/conformance.ts similarity index 97% rename from test/common/conformance.js rename to test/common/conformance.ts index 2decd2e87a..74035d4fed 100644 --- a/test/common/conformance.js +++ b/test/common/conformance.ts @@ -17,7 +17,7 @@ export function validateLoader(t, loader, name = '') { } else { t.equal(typeof loader.parse, 'function', `Loader ${name} has 'parse' function`); // Call parse just to ensure it returns a promise - const promise = loader.parse(null, {}).catch((_) => {}); + const promise = loader.parse(new ArrayBuffer(0), {}).catch((_) => {}); t.ok(promise.then, `Loader ${name} is async (returns a promise)`); } } @@ -68,7 +68,7 @@ export function validateMeshCategoryData(t, data) { t.equals(data.shape, 'columnar-table'); t.ok(data.data); let hasAttributes = false; - let attributesError = null; + let attributesError = false; for (const attributeName in data.data) { hasAttributes = true; const value = data.data[attributeName]; diff --git a/test/init-browser-test.ts b/test/init-browser-test.ts new file mode 100644 index 0000000000..333923aa1b --- /dev/null +++ b/test/init-browser-test.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import test from 'tape'; +import {version} from '../lerna.json'; + +// @ts-ignore TS2339: Property does not exist on type 'Window & typeof globalThis'. +test.onFinish(window.browserTestDriver_finish); +// @ts-ignore TS2339: Property does not exist on type 'Window & typeof globalThis'. +test.onFailure(window.browserTestDriver_fail); + +// This constant will be inlined by babel plugin. +// To test source without transpilation, set a fallback here. +// @ts-ignore TS2339: Property does not exist on type 'Global' +window.__VERSION__ = version; diff --git a/test/init-tests.ts b/test/init-tests.ts new file mode 100644 index 0000000000..4a59d621c1 --- /dev/null +++ b/test/init-tests.ts @@ -0,0 +1,4 @@ +import ALIASES from './aliases.js'; +import {_addAliases} from '@loaders.gl/loader-utils'; + +_addAliases(ALIASES); diff --git a/test/modules.js b/test/modules.ts similarity index 81% rename from test/modules.js rename to test/modules.ts index 63eaec83a3..2f5eefd1a7 100644 --- a/test/modules.js +++ b/test/modules.ts @@ -2,13 +2,7 @@ /* eslint-disable @typescript-eslint/no-var-requires */ -import ALIASES from './aliases.js'; -import {_addAliases} from '@loaders.gl/loader-utils'; -import {installFilePolyfills} from '@loaders.gl/polyfills'; - -_addAliases(ALIASES); -// Install polyfills (primarily for Node) -installFilePolyfills(); +import './init-tests'; // Utility modules import '@loaders.gl/polyfills/test'; @@ -18,10 +12,12 @@ import '@loaders.gl/math/test'; // Core import '@loaders.gl/loader-utils/test'; import '@loaders.gl/core/test'; +import '@loaders.gl/schema/test'; // Image Formats import '@loaders.gl/images/test'; import '@loaders.gl/textures/test'; +import '@loaders.gl/lerc/test'; // import '@loaders.gl/video/test'; // import '@loaders.gl/geotiff/test'; // import '@loaders.gl/zarr/test'; @@ -29,13 +25,13 @@ import '@loaders.gl/netcdf/test'; // Pointcloud/Mesh Formats import '@loaders.gl/draco/test'; -import '@loaders.gl/las/test'; +// import '@loaders.gl/las/test'; import '@loaders.gl/obj/test'; import '@loaders.gl/pcd/test'; import '@loaders.gl/ply/test'; import '@loaders.gl/terrain/test'; -// // Scenegraph Formats +// Scenegraph Formats import '@loaders.gl/gltf/test'; // 3D Tile Formats @@ -46,17 +42,20 @@ import '@loaders.gl/tiles/test'; // Geospatial Formats // TODO restore once we have upgraded to ES modules -// import '@loaders.gl/flatgeobuf/test'; +import '@loaders.gl/flatgeobuf/test'; import '@loaders.gl/geopackage/test'; import '@loaders.gl/gis/test'; import '@loaders.gl/kml/test'; -import '@loaders.gl/mvt/test'; import '@loaders.gl/shapefile/test'; import '@loaders.gl/wkt/test'; import '@loaders.gl/wms/test'; +import '@loaders.gl/mvt/test'; + +// Range request archive style formats +import '@loaders.gl/pmtiles/test'; + // Table Formats -import '@loaders.gl/schema/test'; import '@loaders.gl/arrow/test'; import '@loaders.gl/csv/test'; import '@loaders.gl/json/test'; @@ -72,6 +71,3 @@ import '@loaders.gl/xml/test'; import '@loaders.gl/compression/test'; import '@loaders.gl/crypto/test'; import '@loaders.gl/zip/test'; - -// Tile converter -import '@loaders.gl/tile-converter/test'; diff --git a/test/node.js b/test/node.js deleted file mode 100644 index 7052cc38cf..0000000000 --- a/test/node.js +++ /dev/null @@ -1,19 +0,0 @@ -require('@babel/register')({ - extensions: ['.js', '.jsx', '.ts', '.tsx'] -}); - -// Determine Node version -let version = 10; -if (typeof process !== 'undefined') { - const matches = process.version.match(/v([0-9]*)/); - version = (matches && parseFloat(matches[1])) || version; -} - -// Note: This constant will be inlined by babel plugin during transpilation -// But for node we read it from lerna.json -// @ts-ignore TS2339: Property does not exist on type 'Global' -global.__VERSION__ = require('../lerna.json').version; -// @ts-ignore TS2339: Property does not exist on type 'Global' -global.nodeVersion = version; - -require('./modules'); diff --git a/test/node.ts b/test/node.ts new file mode 100644 index 0000000000..fa1043ad81 --- /dev/null +++ b/test/node.ts @@ -0,0 +1,23 @@ +import {readFileSync} from 'fs'; + +const packageInfo = JSON.parse(readFileSync('./lerna.json', 'utf-8')); +// Note: This constant will be inlined by babel plugin during transpilation +// But for node we read it from lerna.json +// @ts-ignore TS2339: Property does not exist on type 'Global' +globalThis.__VERSION__ = packageInfo.version; + +// Determine Node version +let nodeVersion = 10; +if (typeof process !== 'undefined') { + const matches = process.version.match(/v([0-9]*)/); + nodeVersion = (matches && parseFloat(matches[1])) || nodeVersion; +} +// @ts-ignore TS2339: Property does not exist on type 'Global' +globalThis.nodeVersion = nodeVersion; + +import '@loaders.gl/polyfills'; + +import './modules'; + +// Tile converter +import '@loaders.gl/tile-converter/test'; diff --git a/test/render/index.js b/test/render/index.js index 38a5fa0771..901f707801 100644 --- a/test/render/index.js +++ b/test/render/index.js @@ -1,24 +1,7 @@ -// Copyright (c) 2015 - 2017 Uber Technologies, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -import test from 'tape'; +// loaders.gl, MIT license +// Copyright (c) vis.gl contributors +import test from 'tape'; import {SnapshotTestRunner} from '@luma.gl/test-utils'; import {TEST_CASES} from './test-cases'; diff --git a/test/render/test-utils/get-model.js b/test/render/test-utils/get-model.js index ab88100201..a73b2785f1 100644 --- a/test/render/test-utils/get-model.js +++ b/test/render/test-utils/get-model.js @@ -1,5 +1,4 @@ -import {Model, Geometry} from '@luma.gl/core'; -import * as mat4 from 'gl-matrix/mat4'; +import {Model, Geometry, mat4} from '@luma.gl/core'; import {normalizeAttributes} from './normalize-attributes'; diff --git a/test/size/import-nothing.js b/test/size/import-nothing.js index b68af94297..e85b85cfc2 100644 --- a/test/size/import-nothing.js +++ b/test/size/import-nothing.js @@ -1,3 +1,4 @@ -import {Schema, Field, Float32Vector, Float32} from '@loaders.gl/schema'; -console.log(Schema, Field); // eslint-disable-line +import {getTableLength} from '@loaders.gl/schema'; + +console.log(getTableLength); // eslint-disable-line diff --git a/test/utils/expect-assertions.js b/test/utils/expect-assertions.ts similarity index 84% rename from test/utils/expect-assertions.js rename to test/utils/expect-assertions.ts index b89c84c492..0516dac9fc 100644 --- a/test/utils/expect-assertions.js +++ b/test/utils/expect-assertions.ts @@ -1,4 +1,4 @@ -import test from 'tape'; +import test, {Test as TapeTest} from 'tape'; import {tapeEquals, tapeEqualsEpsilon} from './tape-assertions'; /** @@ -6,7 +6,10 @@ import {tapeEquals, tapeEqualsEpsilon} from './tape-assertions'; * @see https://jestjs.io/docs/expect */ class TestCase { - constructor(t, result) { + t: TapeTest; + result: any; + + constructor(t: TapeTest, result: any) { this.t = t; this.result = result; } @@ -47,11 +50,11 @@ class TestCase { } } -const descriptions = []; +const descriptions: string[] = []; let description = ''; -let currentTest; +let currentTest: TapeTest; -export function describe(string, func) { +export function describe(string: string, func: Function) { descriptions.push(string); description = descriptions.join('#'); func(); diff --git a/test/utils/tape-assertions.js b/test/utils/tape-assertions.ts similarity index 72% rename from test/utils/tape-assertions.js rename to test/utils/tape-assertions.ts index 7bba6d18bb..d63eb03d5a 100644 --- a/test/utils/tape-assertions.js +++ b/test/utils/tape-assertions.ts @@ -1,10 +1,11 @@ +import {Test as TapeTest} from 'tape'; import {equals, withEpsilon} from '@math.gl/core'; import './tape-deep-equal'; // FOR TAPE TESTING // Use tape assert to compares using a.equals(b) // Usage test(..., t => { tapeEquals(t, a, b, ...); }); -export function tapeEquals(t, a, b, msg, extra) { +export function tapeEquals(t: TapeTest, a: any, b: any, msg?: string, extra?: any) { /* eslint-disable no-invalid-this */ let valid = false; if (a.equals) { @@ -14,6 +15,7 @@ export function tapeEquals(t, a, b, msg, extra) { } else { valid = equals(a, b); } + // @ts-ignore t._assert(valid, { message: msg || 'should be equal', operator: 'equal', @@ -24,6 +26,6 @@ export function tapeEquals(t, a, b, msg, extra) { } // eslint-disable-next-line max-params -export function tapeEqualsEpsilon(t, a, b, epsilon, msg, extra) { +export function tapeEqualsEpsilon(t: TapeTest, a: any, b: any, epsilon: number, msg?: string, extra?: any) { return withEpsilon(epsilon, () => tapeEquals(t, a, b, msg, extra)); } diff --git a/test/utils/tape-deep-equal.js b/test/utils/tape-deep-equal.ts similarity index 86% rename from test/utils/tape-deep-equal.js rename to test/utils/tape-deep-equal.ts index 528aa69fda..ce4ca8e3af 100644 --- a/test/utils/tape-deep-equal.js +++ b/test/utils/tape-deep-equal.ts @@ -1,7 +1,7 @@ import Test from 'tape'; // Tape uses `deep-equal` module which throws exceptions, so replace... -function tapeDeepEqual(a, b, msg, extra) { +function tapeDeepEqual(a: any, b: any, msg: string, extra?: any) { // @ts-ignore const that = this; // eslint-disable-line no-invalid-this that._assert(deepEqual(a, b), { @@ -16,7 +16,7 @@ function tapeDeepEqual(a, b, msg, extra) { Test.prototype.deepEqual = Test.prototype.deepEquals = Test.prototype.isEquivalent = Test.prototype.same = tapeDeepEqual; // Compare two objects, partial deep equal -export function deepEqual(a, b) { +export function deepEqual(a: any, b: any): boolean { if (a === b) { return true; } diff --git a/tsconfig.build.json b/tsconfig.build.json index 59f2f9d71c..68c4fd0b03 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -33,6 +33,7 @@ {"path": "modules/parquet"}, {"path": "modules/pcd"}, {"path": "modules/ply"}, + {"path": "modules/pmtiles"}, {"path": "modules/polyfills"}, {"path": "modules/potree"}, {"path": "modules/schema"}, diff --git a/tsconfig.json b/tsconfig.json index 0f9883540b..1784cd0f13 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ { "compilerOptions": { "target": "es2020", + "module": "esnext", "jsx": "react", "strict": true, "noImplicitAny": false, @@ -59,14 +60,16 @@ "@loaders.gl/gltf/test": ["modules/gltf/test"], "@loaders.gl/images": ["modules/images/src"], "@loaders.gl/images/test": ["modules/images/test"], + "@loaders.gl/i3s": ["modules/i3s/src"], + "@loaders.gl/i3s/test": ["modules/i3s/test"], "@loaders.gl/json": ["modules/json/src"], "@loaders.gl/json/test": ["modules/json/test"], "@loaders.gl/kml": ["modules/kml/src"], "@loaders.gl/kml/test": ["modules/kml/test"], - "@loaders.gl/i3s": ["modules/i3s/src"], - "@loaders.gl/i3s/test": ["modules/i3s/test"], "@loaders.gl/las": ["modules/las/src"], "@loaders.gl/las/test": ["modules/las/test"], + "@loaders.gl/lerc": ["modules/lerc/src"], + "@loaders.gl/lerc/test": ["modules/lerc/test"], "@loaders.gl/mvt": ["modules/mvt/src"], "@loaders.gl/mvt/test": ["modules/mvt/test"], "@loaders.gl/netcdf": ["modules/netcdf/src"], @@ -78,6 +81,8 @@ "@loaders.gl/parquet/test": ["modules/parquet/test"], "@loaders.gl/pcd": ["modules/pcd/src"], "@loaders.gl/pcd/test": ["modules/pcd/test"], + "@loaders.gl/pmtiles": ["modules/pmtiles/src"], + "@loaders.gl/pmtiles/test": ["modules/pmtiles/test"], "@loaders.gl/ply": ["modules/ply/src"], "@loaders.gl/ply/test": ["modules/ply/test"], "@loaders.gl/potree": ["modules/potree/src"], @@ -109,6 +114,7 @@ }, "exclude":[ "modules/**/src/libs", + "modules/*/bin/**", "modules/*/dist/**", "modules/**/wip/**", "examples", diff --git a/tsconfig.module.json b/tsconfig.module.json index 0f61c96602..9bc28d89cd 100644 --- a/tsconfig.module.json +++ b/tsconfig.module.json @@ -3,13 +3,11 @@ "extends": "./tsconfig.json", "compilerOptions": { "build": true, - "target": "es2020", - "module": "commonjs", "esModuleInterop": true, "declaration": true, "declarationMap": true, "noEmit": false, - // "emitDeclarationOnly": true + "emitDeclarationOnly": true // Uncomment to debug // "listEmittedFiles": true } diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 24692d65c7..0000000000 --- a/webpack.config.js +++ /dev/null @@ -1,102 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -const {getWebpackConfig} = require('ocular-dev-tools'); - -module.exports = (env = {}) => { - let config = getWebpackConfig(env); - - config.devtool = 'source-map'; - - // Enable SharedArrayBuffer in wasm - // config.devServer = config.devServer || {}; - // config.devServer.headers = config.devServer.headers || {}; - // config.devServer.headers['Cross-Origin-Embedder-Policy'] = 'require-corp'; - // config.devServer.headers['Cross-Origin-Opener-Policy'] = 'same-origin'; - - config = addBabelSettings(config); - config.module.rules.push({ - // Load worker tests - test: /\.worker\.js$/, - loader: 'worker-loader' - }); - // config.module.rules.push({ - // test: /\.wasm$/, - // loaders: ['base64-loader'], - // type: 'javascript/auto' - // }); - // Look for babel plugin - config.module = config.module || {}; - config.module.rules = config.module.rules || []; - const babelRule = config.module.rules.find((rule) => rule.loader === 'babel-loader'); - - // If found, inject excludes in @babel/present-env to prevent transpile - if (babelRule && babelRule.options && babelRule.options.presets) { - babelRule.options.presets = babelRule.options.presets.map((preset) => { - if (preset === '@babel/preset-env') { - return [ - '@babel/preset-env', - { - exclude: [/transform-async-to-generator/, /transform-regenerator/] - } - ]; - } - return preset; - }); - } - - // Log regex - // eslint-disable-next-line - Object.defineProperty(RegExp.prototype, 'toJSON', { - value: RegExp.prototype.toString - }); - - // Uncomment to debug config - // console.warn(JSON.stringify(config, null, 2)); - - return [config]; -}; - -function makeBabelRule() { - return { - // Compile source using babel - test: /(\.js|\.ts|\.tsx)$/, - loader: 'babel-loader', - include: [/modules\/.*\/src/, /modules\/.*\/test/], - exclude: [/node_modules/], - options: { - presets: [ - '@babel/preset-react', - '@babel/preset-typescript', - [ - '@babel/preset-env', - { - exclude: [/transform-async-to-generator/, /transform-regenerator/] - } - ] - ], - plugins: ['@babel/plugin-proposal-class-properties'] - } - }; -} - -function addBabelSettings(config) { - return { - ...config, - module: { - ...config.module, - rules: [ - ...config.module.rules.filter((r) => r.loader !== 'babel-loader'), - makeBabelRule(), - // See https://github.com/apollographql/apollo-link-state/issues/302 - { - test: /\.mjs$/, - include: /node_modules/, - type: 'javascript/auto' - } - ] - }, - resolve: { - ...config.resolve, - extensions: ['.ts', '.tsx', '.js', '.json'] - } - }; -} diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index c7200d6c76..c142c3d542 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -41,60 +41,69 @@ const config = { ], plugins: [ + [ + 'docusaurus-node-polyfills', + { + excludeAliases: ['console'] + } + ], [ './ocular-docusaurus-plugin', { debug: true, resolve: { + fallback: {path: false, fs: false, buffer: true}, modules: [resolve('node_modules'), resolve('../node_modules')], alias: { examples: resolve('../examples'), - '@loaders.gl/3d': resolve('../modules/3d-tiles'), - '@loaders.gl/arrow': resolve('../modules/arrow'), - '@loaders.gl/bson': resolve('../modules/bson'), - '@loaders.gl/compression': resolve('../modules/compression'), - '@loaders.gl/core': resolve('../modules/core'), - '@loaders.gl/crypto': resolve('../modules/crypto'), - '@loaders.gl/csv': resolve('../modules/csv'), - '@loaders.gl/draco': resolve('../modules/draco'), - '@loaders.gl/excel': resolve('../modules/excel'), - '@loaders.gl/flatgeobuf': resolve('../modules/flatgeobuf'), - '@loaders.gl/geopackage': resolve('../modules/geopackage'), - '@loaders.gl/geotiff': resolve('../modules/geotiff'), - '@loaders.gl/gis': resolve('../modules/gis'), - '@loaders.gl/gltf': resolve('../modules/gltf'), - '@loaders.gl/i3s': resolve('../modules/i3s'), - '@loaders.gl/images': resolve('../modules/images'), - '@loaders.gl/json': resolve('../modules/json'), - '@loaders.gl/kml': resolve('../modules/kml'), - '@loaders.gl/las': resolve('../modules/las'), - '@loaders.gl/loader-utils': resolve('../modules/loader-utils'), - '@loaders.gl/math': resolve('../modules/math'), - '@loaders.gl/mvt': resolve('../modules/mvt'), - '@loaders.gl/netcdf': resolve('../modules/netcdf'), - '@loaders.gl/obj': resolve('../modules/obj'), - '@loaders.gl/parquet': resolve('../modules/parquet'), - '@loaders.gl/pcd': resolve('../modules/pcd'), - '@loaders.gl/ply': resolve('../modules/ply'), - '@loaders.gl/polyfills': resolve('../modules/polyfills'), - '@loaders.gl/potree': resolve('../modules/potree'), - '@loaders.gl/schema': resolve('../modules/schema'), - '@loaders.gl/shapefile': resolve('../modules/shapefile'), - '@loaders.gl/stac': resolve('../modules/stac'), - '@loaders.gl/terrain': resolve('../modules/terrain'), - '@loaders.gl/textures': resolve('../modules/textures'), - '@loaders.gl/tile': resolve('../modules/tile-converter'), - '@loaders.gl/tiles': resolve('../modules/tiles'), - '@loaders.gl/tiles-2d': resolve('../modules/tiles-2d'), - '@loaders.gl/type': resolve('../modules/type-analyzer'), - '@loaders.gl/video': resolve('../modules/video'), - '@loaders.gl/wkt': resolve('../modules/wkt'), - '@loaders.gl/wms': resolve('../modules/wms'), - '@loaders.gl/worker': resolve('../modules/worker-utils'), - '@loaders.gl/xml': resolve('../modules/xml'), - '@loaders.gl/zarr': resolve('../modules/zarr'), - '@loaders.gl/zip': resolve('../modules/zip') + '@loaders.gl/3d-tiles': resolve('../modules/3d-tiles/src'), + '@loaders.gl/arrow': resolve('../modules/arrow/src'), + '@loaders.gl/bson': resolve('../modules/bson/src'), + '@loaders.gl/compression': resolve('../modules/compression/src'), + '@loaders.gl/core': resolve('../modules/core/src'), + '@loaders.gl/crypto': resolve('../modules/crypto/src'), + '@loaders.gl/csv': resolve('../modules/csv/src'), + '@loaders.gl/draco': resolve('../modules/draco/src'), + '@loaders.gl/excel': resolve('../modules/excel/src'), + '@loaders.gl/flatgeobuf': resolve('../modules/flatgeobuf/src'), + '@loaders.gl/geopackage': resolve('../modules/geopackage/src'), + '@loaders.gl/geotiff': resolve('../modules/geotiff/src'), + '@loaders.gl/gis': resolve('../modules/gis/src'), + '@loaders.gl/gltf': resolve('../modules/gltf/src'), + '@loaders.gl/i3s': resolve('../modules/i3s/src'), + '@loaders.gl/images': resolve('../modules/images/src'), + '@loaders.gl/json': resolve('../modules/json/src'), + '@loaders.gl/kml': resolve('../modules/kml/src'), + '@loaders.gl/las': resolve('../modules/las/src'), + '@loaders.gl/loader-utils': resolve('../modules/loader-utils/src'), + '@loaders.gl/math': resolve('../modules/math/src'), + '@loaders.gl/mvt': resolve('../modules/mvt/src'), + '@loaders.gl/netcdf': resolve('../modules/netcdf/src'), + '@loaders.gl/obj': resolve('../modules/obj/src'), + '@loaders.gl/parquet': resolve('../modules/parquet/src'), + '@loaders.gl/pcd': resolve('../modules/pcd/src'), + '@loaders.gl/ply': resolve('../modules/ply/src'), + '@loaders.gl/pmtiles': resolve('../modules/pmtiles/src'), + '@loaders.gl/polyfills': resolve('../modules/polyfills/src'), + '@loaders.gl/potree': resolve('../modules/potree/src'), + '@loaders.gl/schema': resolve('../modules/schema/src'), + '@loaders.gl/shapefile': resolve('../modules/shapefile/src'), + '@loaders.gl/stac': resolve('../modules/stac/src'), + '@loaders.gl/terrain': resolve('../modules/terrain/src'), + '@loaders.gl/textures': resolve('../modules/textures/src'), + '@loaders.gl/tile-converter': resolve('../modules/tile/converter/src-'), + '@loaders.gl/tiles': resolve('../modules/tiles/src'), + '@loaders.gl/tiles-2d': resolve('../modules/tiles-2d/src'), + '@loaders.gl/type-analyzer': resolve('../modules/type-analyzer/src'), + '@loaders.gl/video': resolve('../modules/video/src'), + '@loaders.gl/wkt': resolve('../modules/wkt/src'), + '@loaders.gl/wms': resolve('../modules/wms/src'), + '@loaders.gl/worker-utils': resolve('../modules/worker-utils/src'), + '@loaders.gl/xml': resolve('../modules/xml/src'), + '@loaders.gl/zarr': resolve('../modules/zarr/src'), + '@loaders.gl/zip': resolve('../modules/zip/src'), + 'sql.js': resolve('../node_modules/sql.js/dist/sql-wasm.js'), // '@deck.gl/react': resolve() // '@deck.gl/layers' @@ -116,7 +125,7 @@ const config = { // new webpack.EnvironmentPlugin(['MapboxAccessToken', 'GoogleMapsAPIKey', 'GoogleMapsMapId']), // These modules break server side bundling new webpack.IgnorePlugin({ - resourceRegExp: /asciify-image/ + resourceRegExp: /sql/ }) ], module: { @@ -191,13 +200,18 @@ const config = { { to: '/showcase', position: 'left', - label: 'Showcase', + label: 'Showcases', }, { to: 'https://medium.com/vis-gl', label: 'Blog', position: 'left' }, + { + type: 'html', + position: 'right', + value: '' + }, { href: 'https://github.com/visgl/loaders.gl', label: 'GitHub', @@ -211,6 +225,10 @@ const config = { { title: 'Other vis.gl Libraries', items: [ + { + label: 'vis.gl', + href: 'https://vis.gl' + }, { label: 'deck.gl', href: 'https://deck.gl' @@ -222,16 +240,16 @@ const config = { { label: 'math.gl', href: 'https://math.gl' - }, - { - label: 'vis.gl', - href: 'https://vis.gl' } ] }, { title: 'More', items: [ + { + label: 'Open Visualization', + href: 'https://www.openvisualization.org/' + }, { label: 'deck.gl slack', href: 'https://join.slack.com/t/deckgl/shared_invite/zt-7oeoqie8-NQqzSp5SLTFMDeNSPxi7eg' diff --git a/website/package.json b/website/package.json index 2d04ea8dd5..a687f06a36 100644 --- a/website/package.json +++ b/website/package.json @@ -17,23 +17,24 @@ "dependencies": { "@algolia/autocomplete-js": "^1.8.3", "@arcgis/core": "^4.25.0", - "@deck.gl/arcgis": "^8.9.7", - "@deck.gl/core": "^8.9.7", - "@deck.gl/extensions": "^8.9.7", - "@deck.gl/geo-layers": "^8.9.7", - "@deck.gl/layers": "^8.9.7", - "@deck.gl/mesh-layers": "^8.9.7", - "@deck.gl/react": "^8.9.7", + "@deck.gl/arcgis": "^8.9.28", + "@deck.gl/core": "^8.9.28", + "@deck.gl/extensions": "^8.9.28", + "@deck.gl/geo-layers": "^8.9.28", + "@deck.gl/layers": "^8.9.28", + "@deck.gl/mesh-layers": "^8.9.28", + "@deck.gl/react": "^8.9.28", "@esri/react-arcgis": "^5.2.0", "@fortawesome/fontawesome-svg-core": "^1.2.34", "@fortawesome/free-solid-svg-icons": "^5.15.2", "@fortawesome/react-fontawesome": "^0.1.14", - "@loaders.gl/core": "^4.0.0-alpha.8", - "@loaders.gl/i3s": "^4.0.0-alpha.8", - "@loaders.gl/las": "^4.0.0-alpha.8", - "@loaders.gl/loader-utils": "^4.0.0-alpha.8", - "@loaders.gl/obj": "^4.0.0-alpha.8", - "@loaders.gl/ply": "^4.0.0-alpha.8", + "@loaders.gl/core": "^4.0.0", + "@loaders.gl/geopackage": "^4.0.0", + "@loaders.gl/i3s": "^4.0.0", + "@loaders.gl/las": "^4.0.0", + "@loaders.gl/loader-utils": "^4.0.0", + "@loaders.gl/obj": "^4.0.0", + "@loaders.gl/ply": "^4.0.0", "@luma.gl/experimental": "^8.5.0", "@material-ui/core": "^4.10.2", "@material-ui/icons": "^4.9.1", @@ -46,6 +47,7 @@ "d3-color": "^3.1.0", "d3-request": "^1.0.6", "d3-scale": "^3.2.1", + "docusaurus-node-polyfills": "^1.0.0", "expr-eval": "^2.0.2", "mapbox-gl": "npm:empty-npm-package@1.0.0", "maplibre-gl": "^2.4.0", @@ -54,25 +56,30 @@ "react-color": "^2.19.3", "react-dom": "^18.0.0", "react-map-gl": "^7.0.0", + "sql.js": "1.8.0", "styled-components": "^5.3.3" }, "devDependencies": { "@cmfcmf/docusaurus-search-local": "^1.0.0", - "@docusaurus/core": "^2.4.0", - "@docusaurus/plugin-client-redirects": "^2.4.0", - "@docusaurus/plugin-content-docs": "^2.4.0", - "@docusaurus/preset-classic": "^2.4.0", + "@docusaurus/core": "^2.4.3", + "@docusaurus/plugin-client-redirects": "^2.4.3", + "@docusaurus/plugin-content-docs": "^2.4.3", + "@docusaurus/preset-classic": "^2.4.3", "@mdx-js/react": "^1.6.22", "babel-plugin-styled-components": "^2.0.0", "prism-react-renderer": "^1.2.1", "ts-node": "~10.9.1" }, + "browser": { + "fs": false, + "path": false + }, "browserslist": [ ">0.2% and supports async-functions", "not dead" ], "volta": { - "node": "16.19.1", + "node": "18.18.2", "yarn": "1.22.19" } } diff --git a/website/src/components/home/index.jsx b/website/src/components/home/index.jsx index 641aeb62e7..f469e8da06 100644 --- a/website/src/components/home/index.jsx +++ b/website/src/components/home/index.jsx @@ -5,6 +5,7 @@ import { BannerContainer, HeroExampleContainer, ProjectName, + TagLine, GetStartedLink } from './styled'; @@ -18,7 +19,7 @@ export default function renderPage({HeroExample, children}) { {HeroExample && } {siteConfig.title} -

{siteConfig.tagline}

+ {siteConfig.tagline} GET STARTED diff --git a/website/src/components/home/styled.js b/website/src/components/home/styled.js index b3bfef606c..9a61d1347a 100644 --- a/website/src/components/home/styled.js +++ b/website/src/components/home/styled.js @@ -49,12 +49,17 @@ export const ProjectName = styled.h1` font-size: 5em; line-height: 1; text-transform: uppercase; + text-shadow: 1px 1px 1px black, 0 0 1em white, 0 0 0.2em white; letter-spacing: 4px; font-weight: 700; margin: 0; margin-bottom: 16px; `; +export const TagLine = styled.p` + text-shadow: 1px 1px 2px black, 0 0 1em white, 0 0 0.2em lightblue; +`; + export const GetStartedLink = styled.a` pointer-events: all; font-size: 12px; diff --git a/website/src/doc-demos/aggregation-layers.js b/website/src/doc-demos/aggregation-layers.js deleted file mode 100644 index cf0f74f899..0000000000 --- a/website/src/doc-demos/aggregation-layers.js +++ /dev/null @@ -1,97 +0,0 @@ -import { - ContourLayer, - CPUGridLayer, - HexagonLayer, - ScreenGridLayer, - GPUGridLayer, - GridLayer, - HeatmapLayer -} from '@deck.gl/aggregation-layers'; - -import {makeLayerDemo} from './demo-base'; -import {DATA_URI} from '../constants/defaults'; - -export const ContourLayerDemo = makeLayerDemo({ - Layer: ContourLayer, - getTooltip: '({object}) => object && `threshold: ${object.contour.threshold}`', - props: `{ - data: '${DATA_URI}/sf-bike-parking.json', - pickable: true, - cellSize: 200, - getPosition: d => d.COORDINATES, - contours: [ - {threshold: 1, color: [255, 0, 0], strokeWidth: 2, zIndex: 1}, - {threshold: [3, 10], color: [55, 0, 55], zIndex: 0}, - {threshold: 5, color: [0, 255, 0], strokeWidth: 6, zIndex: 2}, - {threshold: 15, color: [0, 0, 255], strokeWidth: 4, zIndex: 3} - ] - }` -}); - -const GRID_LAYER_INFO = { - getTooltip: '({object}) => object && `${object.position.join(\', \')}\nCount: ${object.count}`', - props: `{ - data: '${DATA_URI}/sf-bike-parking.json', - pickable: true, - extruded: true, - cellSize: 200, - elevationScale: 4, - getPosition: d => d.COORDINATES - }` -}; - -export const GPUGridLayerDemo = makeLayerDemo({ - Layer: GPUGridLayer, - ...GRID_LAYER_INFO -}); - -export const GridLayerDemo = makeLayerDemo({ - Layer: GridLayer, - ...GRID_LAYER_INFO -}); - -export const CPUGridLayerDemo = makeLayerDemo({ - Layer: CPUGridLayer, - ...GRID_LAYER_INFO -}); - -export const HexagonLayerDemo = makeLayerDemo({ - Layer: HexagonLayer, - getTooltip: '({object}) => object && `${object.position.join(\', \')}\nCount: ${object.points.length}`', - props: `{ - data: '${DATA_URI}/sf-bike-parking.json', - pickable: true, - extruded: true, - radius: 200, - elevationScale: 4, - getPosition: d => d.COORDINATES - }` -}); - -export const ScreenGridLayerDemo = makeLayerDemo({ - Layer: ScreenGridLayer, - props: `{ - data: '${DATA_URI}/sf-bike-parking.json', - opacity: 0.8, - cellSizePixels: 50, - colorRange: [ - [0, 25, 0, 25], - [0, 85, 0, 85], - [0, 127, 0, 127], - [0, 170, 0, 170], - [0, 190, 0, 190], - [0, 255, 0, 255] - ], - getPosition: d => d.COORDINATES, - getWeight: d => d.SPACES - }` -}); - -export const HeatmapLayerDemo = makeLayerDemo({ - Layer: HeatmapLayer, - props: `{ - data: '${DATA_URI}/sf-bike-parking.json', - getPosition: d => d.COORDINATES, - radiusPixels: 25 - }` -}); diff --git a/website/src/doc-demos/codepen-automation.js b/website/src/doc-demos/codepen-automation.js deleted file mode 100644 index 0f83bf1ff3..0000000000 --- a/website/src/doc-demos/codepen-automation.js +++ /dev/null @@ -1,254 +0,0 @@ - -const PROP_BLACK_LIST = ['dataComparator', 'fetch']; -const BASE_LAYER_PROP_WHITE_LIST = ['autoHighlight', 'coordinateOrigin', 'coordinateSystem', 'highlightColor', 'modelMatrix', 'opacity', 'pickable', 'visible', 'wrapLongitude']; -const PROP_OVERRIDES = { - loaders: [], - coordinateSystem: 'COORDINATE_SYSTEM.LNGLAT', - renderSubLayers: 'props => new GeoJsonLayer(props)', - characterSet: '" !\\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"' -}; - -/* - * Adjust indent to a block of code - * @param text (String) - source code - * @param spaces (Number) - positive: add x spaces to indent; negative: remove x spaces to indent - */ -function addIndent(text, spaces) { - if (!text.includes('\n') || spaces === 0) { - return text; - } - const lines = text.split('\n'); - if (spaces > 0) { - const indent = ''.padStart(spaces, ' '); - return lines.map((line, i) => { - return i > 0 ? indent + line : line; - }).join('\n'); - } else { - return lines.map((line, i) => { - return i > 0 ? line.slice(-spaces) : line; - }).join('\n'); - } -} - -/* - * Format an object for use in source code - */ -function formatValue(value) { - if (value === null || value === undefined) { - return 'null'; - } else if (typeof value === 'string') { - return `'${value}'`; - } else if (typeof value === 'function') { - return value.toString(); - } else if (Array.isArray(value)) { - return `[${value.map(formatValue).join(', ')}]` - } else if (value === Number.MAX_SAFE_INTEGER) { - return 'Number.MAX_SAFE_INTEGER'; - } - return addIndent(JSON.stringify(value, null, 2), 2); -} - -/* - * Convert the source code that describes a JS props object to a map of propName -> source - */ -function parsePropSource(source) { - const lines = source.split('\n'); - lines.pop(); - const result = {}; - let indent = Infinity; - let key; - for (const line of lines) { - const m = line.match(/^(\s*)(\w+):\s*(.*)/); - if (m && m[1].length <= indent) { - indent = m[1].length; - key = m[2]; - result[key] = m[3]; - } else if (key) { - result[key] += '\n' + line; - } - } - for (key in result) { - result[key] = addIndent(result[key].replace(/,\s*$/, ''), 2 - indent); - } - return result; -} - -/* - * Takes a layer instance and returns JS code that reproduces it - */ -function printLayerProps(layer, propsSource) { - const result = []; - const propNames = {}; - propsSource = parsePropSource(propsSource); - let Class = layer.constructor; - let isPrototype = false; - - const asyncOriginal = layer.props[Symbol.for('asyncPropOriginal')]; - const asyncResolved = layer.props[Symbol.for('asyncPropResolved')]; - - function addProp(propName, value, shouldComment, insertPosition) { - if (!propNames[propName] && !PROP_BLACK_LIST.includes(propName)) { - const line = `${shouldComment ? '// ' : ''}${propName}: ${value},`; - if (insertPosition === undefined) { - result.push(line); - } else { - result.splice(insertPosition, 0, line); - } - propNames[propName] = true; - } - } - - let ownPropscount = 1; - addProp('id', formatValue(layer.id)); - if (propsSource.data) { - addProp('data', propsSource.data, false, ownPropscount++); - } - - while (Class.layerName) { - if (Class.hasOwnProperty('defaultProps')) { - result.push('', `/* props ${isPrototype ? 'inherited from' : 'from'} ${Class.layerName} class */`, ''); - const keys = Object.keys(Class.defaultProps).sort(); - for (const key of keys) { - const propDef = Class.defaultProps[key]; - const shouldComment = !(key in asyncOriginal) && !(key in asyncResolved) && !Object.hasOwnProperty.call(layer.props, key); - let shouldSkip = false; - let value; - - if (key in propsSource) { - // this prop is explicitly specified, use source - value = propsSource[key]; - } else { - // Hide deprecated props - shouldSkip = propDef && propDef.deprecatedFor; - // Hide experimental props - shouldSkip = shouldSkip || key.startsWith('_'); - // Hide accessors that are not exposed, e.g. `getPolygon` in `H3HexagonLayer` - shouldSkip = shouldSkip || (propDef && propDef.type === 'accessor' && typeof propDef.value === 'function'); - // Hide some base layer props - shouldSkip = shouldSkip || (Class.layerName === 'Layer' && !BASE_LAYER_PROP_WHITE_LIST.includes(key)); - - value = asyncOriginal[key] || layer.props[key]; - if (typeof value === 'function') { - // Hide default callbacks, e.g. `onTileError` - value = null; - } else if (key in PROP_OVERRIDES) { - // Replace enums - value = PROP_OVERRIDES[key]; - } else { - value = formatValue(value); - } - } - - if (!shouldSkip) { - addProp(key, value, shouldComment); - } - } - } - Class = Object.getPrototypeOf(Class); - isPrototype = true; - } - - for (const key in propsSource) { - if (!(key in propNames)) { - addProp(key, propsSource[key], false, ownPropscount++); - } - } - - return result.join('\n '); -} - -/* - * Open a layer example in Codepen - */ -export function gotoLayerSource(config, layer) { - const {Layer, isExperimental, getTooltip, props, mapStyle = true, dependencies = [], imports, initialViewState} = config; - - const layerName = isExperimental ? `_${Layer.layerName}` : Layer.layerName; - - const symbols = ['DeckGL', layerName]; - const loaders = []; - if (imports) { - for (const key in imports) { - if (key[0] >= 'a') continue; - if (key.endsWith('Loader')) { - loaders.push(key); - } else { - symbols.push(key); - } - } - } - - const initialViewStateSerialized = addIndent( - JSON.stringify(initialViewState, null, 2).replace(/"/g, ''), // remove quotes - 2); - - // https://blog.codepen.io/documentation/prefill/ - const source = `\ -const {${symbols.join(', ')}} = deck; -${loaders.length ? `\ -const {${loaders.join(', ')}} = loaders; -` : ''} -const layer = new ${layerName}({ - ${printLayerProps(layer, props)} -}); - -new DeckGL({ - ${mapStyle ? `mapStyle: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',` : ''} - initialViewState: ${initialViewStateSerialized}, - controller: true, - ${getTooltip ? `getTooltip: ${getTooltip},` : ''} - layers: [layer] -}); - `; - - gotoSource({ - dependencies: dependencies.concat(['https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js']), - title: `deck.gl ${Layer.layerName}`, - source - }); -} - -function gotoSource({dependencies = [], title, source}) { - const formData = { - js_external: dependencies.concat([ - 'https://unpkg.com/deck.gl@latest/dist.min.js' - ]).join(';'), - title, - parent: 48721472, - tags: ['webgl', 'data visualization'], - editors: '001', - css: ` - body { - margin: 0; - width: 100vw; - height: 100vh; - overflow: hidden; - } - - .deck-tooltip { - font-size: 0.8em; - font-family: Helvetica, Arial, sans-serif; - } - `, - js: `\ -/* -* ${window.location.href} -*/ -${source}` - }; - - const form = document.createElement('form'); - form.action = 'https://codepen.io/pen/define/'; - form.method = 'POST'; - form.style.display = 'none'; - document.body.appendChild(form); - const input = document.createElement('input'); - input.type = 'text'; - input.name = 'data'; - input.value = JSON.stringify(formData); - form.appendChild(input); - window.open('', 'deck-example-codepen'); - form.target = 'deck-example-codepen'; - form.submit(); - form.remove(); -} diff --git a/website/src/doc-demos/demo-base.js b/website/src/doc-demos/demo-base.js deleted file mode 100644 index dd1b7a2601..0000000000 --- a/website/src/doc-demos/demo-base.js +++ /dev/null @@ -1,123 +0,0 @@ -import React from 'react'; -import DeckGL from '@deck.gl/react'; -import {Map} from 'react-map-gl'; -import styled from 'styled-components'; - -import {MAPBOX_STYLES} from '../constants/defaults'; -import {gotoLayerSource} from './codepen-automation'; - -const INITIAL_VIEW_STATE = { - longitude: -122.4, - latitude: 37.74, - zoom: 11, - maxZoom: 20, - pitch: 30, - bearing: 0 -}; - -const TOOLTIP_STYLE = { - padding: '4px', - background: 'rgba(0, 0, 0, 0.8)', - color: '#fff', - maxWidth: '300px', - fontSize: '10px', - zIndex: 9 -}; - -const DemoPlaceholder = styled.div` -height: 50vh; -min-height: 200px; -position: relative; -margin-bottom: 24px; - -@media screen and (max-width: 768px) { - height: 60vh; -} -`; - -const DemoContainer = styled.div` -height: 50vh; -min-height: 200px; -position: absolute; -width: 100%; -left: 0; -top: 0; -overflow: hidden; - -@media screen and (max-width: 768px) { - height: 60vh; -} -`; - -const DemoSourceLink = styled.div` -position: absolute; -top: 0; -right: 0; -padding: 8px; -background: #fff; -margin: 12px; -box-shadow: 0 2px 4px rgba(0,0,0,0.3); -cursor: pointer; -font-weight: bold; -font-size: 12px; - -&:hover { - color: var(--color-primary); -} - -svg { - width: 20px; - vertical-align: middle; - margin-right: 4px; -} -`; - -function evalObject(source, globals, output) { - return eval(`(function evalObject(globals){ - Object.assign(globalThis, globals); - ${ - output ? `${source} - return {${output.join(',')}};` : `return ${source};` - } - })`)(globals); -} - -export function makeLayerDemo(config) { - const {Layer, getTooltip, props, mapStyle = MAPBOX_STYLES.LIGHT, initialViewState = INITIAL_VIEW_STATE, imports} = config; - config.initialViewState = initialViewState; - - function Demo() { - const _getTooltip = getTooltip && eval(getTooltip); - const styledGetTooltip = pickingInfo => { - const text = _getTooltip && _getTooltip(pickingInfo); - return text && { - text, - style: TOOLTIP_STYLE - }; - }; - - const layerProps = evalObject(props, imports); - const layer = new Layer(layerProps); - - return ( - - - - {mapStyle && } - - - gotoLayerSource(config, layer)}> - - Edit on Codepen - - - ); - } - return React.memo(Demo); -} diff --git a/website/src/doc-demos/geo-layers.js b/website/src/doc-demos/geo-layers.js deleted file mode 100644 index b82c4bd302..0000000000 --- a/website/src/doc-demos/geo-layers.js +++ /dev/null @@ -1,233 +0,0 @@ -import {BitmapLayer} from '@deck.gl/layers'; - -import { - GreatCircleLayer, - S2Layer, - TileLayer, - TripsLayer, - TerrainLayer, - MVTLayer, - _WMSLayer as WMSLayer, - H3HexagonLayer, - H3ClusterLayer, - QuadkeyLayer, - GeohashLayer -} from '@deck.gl/geo-layers'; - -import {makeLayerDemo} from './demo-base'; -import {DATA_URI} from '../constants/defaults'; - -export const GreatCircleLayerDemo = makeLayerDemo({ - Layer: GreatCircleLayer, - getTooltip: '({object}) => object && `${object.from.name} to ${object.to.name}`', - props: `{ - data: '${DATA_URI}/flights.json', - getSourcePosition: d => d.from.coordinates, - getTargetPosition: d => d.to.coordinates, - getSourceColor: [64, 255, 0], - getTargetColor: [0, 128, 200], - widthMinPixels: 5, - pickable: true - }` -}); - - -export const GeohashLayerDemo = makeLayerDemo({ - Layer: GeohashLayer, - getTooltip: '({object}) => object && `${object.geohash} value: ${object.value}`', - props: `{ - data: '${DATA_URI}/sf.geohashes.json', - pickable: true, - wireframe: false, - filled: true, - extruded: true, - elevationScale: 1000, - getGeohash: d => d.geohash, - getFillColor: d => [d.value * 255, (1 - d.value) * 128, (1 - d.value) * 255], - getElevation: d => d.value - }` -}); - -export const QuadkeyLayerDemo = makeLayerDemo({ - Layer: QuadkeyLayer, - getTooltip: '({object}) => object && `${object.quadkey} value: ${object.value}`', - props: `{ - data: '${DATA_URI}/sf.quadkeys.json', - pickable: true, - wireframe: false, - filled: true, - extruded: true, - elevationScale: 1000, - getQuadkey: d => d.quadkey, - getFillColor: d => [d.value * 128, (1 - d.value) * 255, (1 - d.value) * 255, 180], - getElevation: d => d.value - }` -}); - -export const S2LayerDemo = makeLayerDemo({ - Layer: S2Layer, - getTooltip: '({object}) => object && `${object.token} value: ${object.value}`', - props: `{ - data: '${DATA_URI}/sf.s2cells.json', - pickable: true, - wireframe: false, - filled: true, - extruded: true, - elevationScale: 1000, - getS2Token: d => d.token, - getFillColor: d => [d.value * 255, (1 - d.value) * 255, (1 - d.value) * 128], - getElevation: d => d.value - }` -}); - -export const H3HexagonLayerDemo = makeLayerDemo({ - Layer: H3HexagonLayer, - dependencies: ['https://unpkg.com/h3-js@3.7.2'], - getTooltip: '({object}) => object && `${object.hex} count: ${object.count}`', - props: `{ - data: '${DATA_URI}/sf.h3cells.json', - pickable: true, - wireframe: false, - filled: true, - extruded: true, - elevationScale: 20, - getHexagon: d => d.hex, - getFillColor: d => [255, (1 - d.count / 500) * 255, 0], - getElevation: d => d.count - }` -}); - -export const H3ClusterLayerDemo = makeLayerDemo({ - Layer: H3ClusterLayer, - dependencies: ['https://unpkg.com/h3-js@3.7.2'], - getTooltip: '({object}) => object && `density: ${object.mean}`', - props: `{ - data: '${DATA_URI}/sf.h3clusters.json', - pickable: true, - stroked: true, - filled: true, - extruded: false, - getHexagons: d => d.hexIds, - getFillColor: d => [255, (1 - d.mean / 500) * 255, 0], - getLineColor: [255, 255, 255], - lineWidthMinPixels: 2 - }` -}); - -export const TileLayerDemo = makeLayerDemo({ - Layer: TileLayer, - getTooltip: '({tile}) => tile && `x:${tile.x}, y:${tile.y}, z:${tile.z}`', - imports: {BitmapLayer}, - mapStyle: null, - props: `{ - data: 'https://c.tile.openstreetmap.org/{z}/{x}/{y}.png', - pickable: true, - minZoom: 0, - maxZoom: 19, - - renderSubLayers: props => { - const { - bbox: {west, south, east, north} - } = props.tile; - - return new BitmapLayer(props, { - data: null, - image: props.data, - bounds: [west, south, east, north] - }); - } - }` -}); - -export const TripsLayerDemo = makeLayerDemo({ - Layer: TripsLayer, - props: `{ - data: '${DATA_URI}/sf.trips.json', - getPath: d => d.waypoints.map(p => p.coordinates), - getTimestamps: d => d.waypoints.map(p => p.timestamp - 1554772579000), - getColor: [253, 128, 93], - opacity: 0.8, - widthMinPixels: 8, - rounded: true, - trailLength: 600, - currentTime: 500 - }` -}); - -export const TerrainLayerDemo = makeLayerDemo({ - Layer: TerrainLayer, - props: `{ - elevationDecoder: { - rScaler: 2, - gScaler: 0, - bScaler: 0, - offset: 0 - }, - elevationData: '${DATA_URI}/terrain.png', - texture: '${DATA_URI}/terrain-mask.png', - bounds: [-122.5233, 37.6493, -122.3566, 37.8159], - material: { - diffuse: 1 - } - }` -}); - -export const MVTLayerDemo = makeLayerDemo({ - Layer: MVTLayer, - mapStyle: null, - props: `{ - data: [ - 'https://tiles-a.basemaps.cartocdn.com/vectortiles/carto.streets/v1/{z}/{x}/{y}.mvt' - ], - - minZoom: 0, - maxZoom: 14, - stroked: false, - getLineColor: [192, 192, 192], - getFillColor: f => { - switch (f.properties.layerName) { - case 'poi': - return [255, 0, 0]; - case 'water': - return [120, 150, 180]; - case 'building': - return [218, 218, 218]; - default: - return [240, 240, 240]; - } - }, - getPointRadius: 2, - pointRadiusUnits: 'pixels', - getLineWidth: f => { - switch (f.properties.class) { - case 'street': - return 6; - case 'motorway': - return 10; - default: - return 1; - } - }, - lineWidthMinPixels: 1 - }` -}); - -export const WMSLayerDemo = makeLayerDemo({ - Layer: WMSLayer, - isExperimental: true, - getTooltip: '({bitmap}) => bitmap && `x:${bitmap.x}, y:${bitmap.y}`', - mapStyle: null, - initialViewState: { - longitude: -122.4, - latitude: 37.74, - zoom: 9, - minZoom: 1, - maxZoom: 20 - }, - props: `{ - data: 'https://ows.terrestris.de/osm/service', - serviceType: 'wms', - layers: ['OSM-WMS'], - pickable: true - }` -}); diff --git a/website/src/doc-demos/layers.js b/website/src/doc-demos/layers.js deleted file mode 100644 index 7ca267e853..0000000000 --- a/website/src/doc-demos/layers.js +++ /dev/null @@ -1,228 +0,0 @@ -import {COORDINATE_SYSTEM} from '@deck.gl/core'; -import { - ArcLayer, - BitmapLayer, - ColumnLayer, - GeoJsonLayer, - GridCellLayer, - IconLayer, - LineLayer, - PathLayer, - PointCloudLayer, - PolygonLayer, - ScatterplotLayer, - TextLayer -} from '@deck.gl/layers'; - -import {makeLayerDemo} from './demo-base'; -import {DATA_URI} from '../constants/defaults'; - -export const ArcLayerDemo = makeLayerDemo({ - Layer: ArcLayer, - getTooltip: '({object}) => object && `${object.from.name} to ${object.to.name}`', - props: `{ - data: '${DATA_URI}/bart-segments.json', - pickable: true, - getWidth: 12, - getSourcePosition: d => d.from.coordinates, - getTargetPosition: d => d.to.coordinates, - getSourceColor: d => [Math.sqrt(d.inbound), 140, 0], - getTargetColor: d => [Math.sqrt(d.outbound), 140, 0] - }` -}); - -export const BitmapLayerDemo = makeLayerDemo({ - Layer: BitmapLayer, - props: `{ - bounds: [-122.519, 37.7045, -122.355, 37.829], - image: '${DATA_URI}/sf-districts.png' - }` -}); - -export const ColumnLayerDemo = makeLayerDemo({ - Layer: ColumnLayer, - getTooltip: '({object}) => object && `height: ${object.value * 5000}m`', - props: `{ - data: '${DATA_URI}/hexagons.json', - diskResolution: 12, - radius: 250, - extruded: true, - pickable: true, - elevationScale: 100, - getPosition: d => d.centroid, - getFillColor: d => [48, 128, d.value * 255, 255], - getLineColor: [0, 0, 0], - getLineWidth: 20, - getElevation: d => d.value * 50 - }` -}); - -export const GeoJsonLayerDemo = makeLayerDemo({ - Layer: GeoJsonLayer, - getTooltip: '({object}) => object && (object.properties.name || object.properties.station)', - props: `{ - data: '${DATA_URI}/bart.geo.json', - pickable: true, - stroked: false, - filled: true, - extruded: true, - lineWidthMinPixels: 2, - pointType: 'circle+text', - pointRadiusUnits: 'pixels', - getText: f => f.properties.name, - getTextSize: 12, - getFillColor: [160, 160, 180, 200], - getLineColor: f => { - const hex = f.properties.color; - // convert to RGB - return hex ? hex.match(/[0-9a-f]{2}/g).map(x => parseInt(x, 16)) : [0, 0, 0]; - }, - getPointRadius: 4, - getLineWidth: 20, - getElevation: 30 - }` -}); - -export const GridCellLayerDemo = makeLayerDemo({ - Layer: GridCellLayer, - getTooltip: '({object}) => object && `height: ${object.value * 5000}m`', - props: `{ - data: '${DATA_URI}/hexagons.json', - pickable: true, - extruded: true, - cellSize: 200, - elevationScale: 5000, - getPosition: d => d.centroid, - getFillColor: d => [48, 128, d.value * 255, 255], - getElevation: d => d.value - }` -}); - -export const IconLayerDemo = makeLayerDemo({ - Layer: IconLayer, - getTooltip: '({object}) => object && `${object.name}\n${object.address}`', - props: `{ - data: '${DATA_URI}/bart-stations.json', - pickable: true, - iconAtlas: '${DATA_URI}/icon-atlas.png', - iconMapping: { - marker: { - x: 0, - y: 0, - width: 128, - height: 128, - anchorY: 128, - mask: true - } - }, - sizeScale: 8, - getPosition: d => d.coordinates, - getIcon: d => 'marker', - getSize: d => 5, - getColor: d => [Math.sqrt(d.exits), 140, 0] - }` -}); - -export const LineLayerDemo = makeLayerDemo({ - Layer: LineLayer, - getTooltip: '({object}) => object && `${object.from.name} to ${object.to.name}`', - props: `{ - data: '${DATA_URI}/bart-segments.json', - pickable: true, - getWidth: 12, - getSourcePosition: d => d.from.coordinates, - getTargetPosition: d => d.to.coordinates, - getColor: d => [Math.sqrt(d.inbound + d.outbound), 140, 0] - }` -}); - -export const PathLayerDemo = makeLayerDemo({ - Layer: PathLayer, - getTooltip: '({object}) => object && object.name', - props: `{ - data: '${DATA_URI}/bart-lines.json', - parameters: { - depthMask: false - }, - pickable: true, - widthScale: 20, - widthMinPixels: 2, - getPath: d => d.path, - getColor: d => { - const hex = d.color; - // convert to RGB - return hex.match(/[0-9a-f]{2}/g).map(x => parseInt(x, 16)); - }, - getWidth: d => 5 - }` -}); - -export const PointCloudLayerDemo = makeLayerDemo({ - Layer: PointCloudLayer, - getTooltip: `({object}) => object && object.position.join(', ')`, - imports: {COORDINATE_SYSTEM}, - props: `{ - data: '${DATA_URI}/pointcloud.json', - pickable: false, - coordinateSystem: COORDINATE_SYSTEM.METER_OFFSETS, - coordinateOrigin: [-122.4, 37.74], - pointSize: 2, - getPosition: d => d.position, - getNormal: d => d.normal, - getColor: d => d.color - }` -}); - -export const PolygonLayerDemo = makeLayerDemo({ - Layer: PolygonLayer, - getTooltip: '({object}) => object && `${object.zipcode}\nPopulation: ${object.population}`', - props: `{ - data: '${DATA_URI}/sf-zipcodes.json', - pickable: true, - stroked: true, - filled: true, - extruded: true, - wireframe: true, - lineWidthMinPixels: 1, - getPolygon: d => d.contour, - getElevation: d => d.population / d.area / 10, - getFillColor: d => [d.population / d.area / 60, 140, 0], - getLineColor: [80, 80, 80], - getLineWidth: d => 1 - }` -}); - -export const ScatterplotLayerDemo = makeLayerDemo({ - Layer: ScatterplotLayer, - getTooltip: '({object}) => object && `${object.name}\n${object.address}`', - props: `{ - data: '${DATA_URI}/bart-stations.json', - pickable: true, - opacity: 0.8, - stroked: true, - radiusScale: 6, - radiusMinPixels: 1, - radiusMaxPixels: 100, - lineWidthMinPixels: 1, - getPosition: d => d.coordinates, - getRadius: d => Math.sqrt(d.exits), - getFillColor: [255, 140, 0], - getLineColor: [0, 0, 0] - }` -}); - -export const TextLayerDemo = makeLayerDemo({ - Layer: TextLayer, - getTooltip: '({object}) => object && `${object.name}\n${object.address}`', - props: `{ - data: '${DATA_URI}/bart-stations.json', - pickable: true, - sizeScale: 1, - getPosition: d => d.coordinates, - getText: d => d.name, - getSize: 16, - getAngle: 0, - getTextAnchor: 'middle', - getAlignmentBaseline: 'center' - }` -}); diff --git a/website/src/doc-demos/mesh-layers.js b/website/src/doc-demos/mesh-layers.js deleted file mode 100644 index 33d4239085..0000000000 --- a/website/src/doc-demos/mesh-layers.js +++ /dev/null @@ -1,41 +0,0 @@ -import { - ScenegraphLayer, SimpleMeshLayer -} from '@deck.gl/mesh-layers'; -import {OBJLoader} from '@loaders.gl/obj'; - -import {makeLayerDemo} from './demo-base'; -import {DATA_URI} from '../constants/defaults'; - -export const ScenegraphLayerDemo = makeLayerDemo({ - Layer: ScenegraphLayer, - getTooltip: '({object}) => object && `${object.name}\n${object.address}`', - props: `{ - data: '${DATA_URI}/bart-stations.json', - pickable: true, - scenegraph: 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoxAnimated/glTF-Binary/BoxAnimated.glb', - getPosition: d => d.coordinates, - getOrientation: d => [0, Math.random() * 180, 90], - _animations: { - '*': {speed: 5} - }, - sizeScale: 500, - _lighting: 'pbr' - }` -}); - -export const SimpleMeshLayerDemo = makeLayerDemo({ - Layer: SimpleMeshLayer, - dependencies: ['https://unpkg.com/@loaders.gl/obj@beta/dist/dist.min.js'], - imports: {OBJLoader}, - getTooltip: '({object}) => object && `${object.name}\n${object.address}`', - props: `{ - data: '${DATA_URI}/bart-stations.json', - pickable: true, - mesh: '${DATA_URI}/humanoid_quad.obj', - getPosition: d => d.coordinates, - getColor: d => [Math.sqrt(d.exits), 140, 0], - getOrientation: d => [0, Math.random() * 180, 0], - sizeScale: 30, - loaders: [OBJLoader] - }` -}); diff --git a/website/src/examples-sidebar.js b/website/src/examples-sidebar.js index 4158a56ad1..50f71d0c8e 100644 --- a/website/src/examples-sidebar.js +++ b/website/src/examples-sidebar.js @@ -17,25 +17,40 @@ }, { type: 'category', - label: 'Loaders', + label: 'Geospatial Loaders', items: [ - 'textures', - // 'gltf', - 'geospatial', - 'wms', - 'pointcloud' + 'geoparquet', + // 'geopackage', sql.js bundling issue... + 'flatgeobuf', + 'geojson' ], }, { type: 'category', - label: 'Tiled Loaders', + label: 'Geospatial Tile Loaders', + items: [ + 'pmtiles', + 'wms' + ] + }, + { + type: 'category', + label: '3D Tile Loaders', items: [ 'i3s', - 'i3s-debug', - 'i3s-arcgis', + // 'i3s-arcgis', '3d-tiles' ] }, + { + type: 'category', + label: 'General Loaders', + items: [ + 'textures', + // 'gltf', + 'pointcloud' + ], + } // { // type: 'category', // label: 'Benchmarks', diff --git a/website/src/examples/flatgeobuf.mdx b/website/src/examples/flatgeobuf.mdx new file mode 100644 index 0000000000..af9741eb21 --- /dev/null +++ b/website/src/examples/flatgeobuf.mdx @@ -0,0 +1,8 @@ + +# FlatGeobuf + +import Demo from 'examples/website/geospatial/app'; + +
+ +
diff --git a/website/src/examples/geospatial.mdx b/website/src/examples/geojson.mdx similarity index 71% rename from website/src/examples/geospatial.mdx rename to website/src/examples/geojson.mdx index 8f84f84ea0..58851eef3f 100644 --- a/website/src/examples/geospatial.mdx +++ b/website/src/examples/geojson.mdx @@ -1,7 +1,7 @@ -# Geospatial +# GeoJSON import Demo from 'examples/website/geospatial/app';
- +
diff --git a/website/src/examples/geopackage.mdx b/website/src/examples/geopackage.mdx new file mode 100644 index 0000000000..492aa23008 --- /dev/null +++ b/website/src/examples/geopackage.mdx @@ -0,0 +1,7 @@ +# GeoPackage + +import Demo from 'examples/website/geospatial/app'; + +
+ +
diff --git a/website/src/examples/geoparquet.mdx b/website/src/examples/geoparquet.mdx new file mode 100644 index 0000000000..72fd8534a3 --- /dev/null +++ b/website/src/examples/geoparquet.mdx @@ -0,0 +1,7 @@ +# GeoParquet + +import Demo from 'examples/website/geospatial/app'; + +
+ +
diff --git a/website/src/examples/i3s-arcgis.mdx b/website/src/examples/i3s-arcgis.mdx index 06f2a8ceba..8874ff5469 100644 --- a/website/src/examples/i3s-arcgis.mdx +++ b/website/src/examples/i3s-arcgis.mdx @@ -1,4 +1,4 @@ -# I3S ArcGIS +# I3S import Demo from './i3s-arcgis'; diff --git a/website/src/examples/i3s-debug.mdx b/website/src/examples/i3s-debug.mdx deleted file mode 100644 index c0ee488969..0000000000 --- a/website/src/examples/i3s-debug.mdx +++ /dev/null @@ -1,7 +0,0 @@ -# I3S Debug - -import Demo from 'examples/website/i3s/src/app-debug'; - -
- -
\ No newline at end of file diff --git a/website/src/examples/pmtiles.mdx b/website/src/examples/pmtiles.mdx new file mode 100644 index 0000000000..7ef312e666 --- /dev/null +++ b/website/src/examples/pmtiles.mdx @@ -0,0 +1,7 @@ +# PMTiles + +import Demo from 'examples/website/tiles/app'; + +
+ +
diff --git a/website/static/images/examples/flatgeobuf.jpg b/website/static/images/examples/flatgeobuf.jpg new file mode 100644 index 0000000000..7bc34d041e Binary files /dev/null and b/website/static/images/examples/flatgeobuf.jpg differ diff --git a/website/static/images/examples/geojson.jpg b/website/static/images/examples/geojson.jpg new file mode 100644 index 0000000000..ae79906471 Binary files /dev/null and b/website/static/images/examples/geojson.jpg differ diff --git a/website/static/images/examples/geopackage.jpg b/website/static/images/examples/geopackage.jpg new file mode 100644 index 0000000000..ae79906471 Binary files /dev/null and b/website/static/images/examples/geopackage.jpg differ diff --git a/website/static/images/examples/geoparquet.jpg b/website/static/images/examples/geoparquet.jpg new file mode 100644 index 0000000000..bd9953a6d1 Binary files /dev/null and b/website/static/images/examples/geoparquet.jpg differ diff --git a/website/static/images/examples/pmtiles.jpg b/website/static/images/examples/pmtiles.jpg new file mode 100644 index 0000000000..516accdf6f Binary files /dev/null and b/website/static/images/examples/pmtiles.jpg differ diff --git a/website/static/images/openjs-foundation.svg b/website/static/images/openjs-foundation.svg new file mode 100644 index 0000000000..7726a45c17 --- /dev/null +++ b/website/static/images/openjs-foundation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/yarn.lock b/website/yarn.lock index 875a24c811..e9d0098c67 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -1262,23 +1262,25 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@deck.gl/arcgis@^8.9.7": - version "8.9.18" - resolved "https://registry.yarnpkg.com/@deck.gl/arcgis/-/arcgis-8.9.18.tgz#c1c4b70b457b12a48ca28d9c2c074463962d51ff" - integrity sha512-Eckp8GY0ybeNGErpRmjNN8/vLdhtLqwpHEUWwiXwTxy+GrYbQ78A4eQ5l0roDndtv8G+mUIuEETwRnHx312vHA== +"@deck.gl/arcgis@^8.9.28": + version "8.9.28" + resolved "https://registry.yarnpkg.com/@deck.gl/arcgis/-/arcgis-8.9.28.tgz#35655445a3467c0d7a39fd4c57eb41ff48f4cf00" + integrity sha512-y+wa2mRFbjdGZ30QPBD9kVdwj8d54iXHxpiC1fOZND9sf8/vXq1aSGVitq8tHTRMrpROS0LbYOtvVPalkEOOyQ== dependencies: + "@babel/runtime" "^7.0.0" esri-loader "^3.3.0" -"@deck.gl/core@^8.9.7": - version "8.9.18" - resolved "https://registry.yarnpkg.com/@deck.gl/core/-/core-8.9.18.tgz#4a09defbc0f226a85c49864e2bbe324e2f9e43d5" - integrity sha512-zliaY4ZHebPCfMVEN6lxWuKDN+iNRL3XQwCLRX9N6ZqzzNFUXTLs//Vasdj+x8v4qEAQCJBEngzFPa0BemtvOQ== +"@deck.gl/core@^8.9.28": + version "8.9.28" + resolved "https://registry.yarnpkg.com/@deck.gl/core/-/core-8.9.28.tgz#fb857c417fb3ba2fae375b548f43358843a00b96" + integrity sha512-Tpji8XykZAjaDIJwXR9RalulemgF6AGpCq43XIInK+0JdumJYuYVyGObPkujNW7zeWM26rAb3TiqyOLwW/3yOQ== dependencies: - "@loaders.gl/core" "^3.4.2" - "@loaders.gl/images" "^3.4.2" - "@luma.gl/constants" "^8.5.20" - "@luma.gl/core" "^8.5.20" - "@luma.gl/webgl" "^8.5.20" + "@babel/runtime" "^7.0.0" + "@loaders.gl/core" "^3.4.13" + "@loaders.gl/images" "^3.4.13" + "@luma.gl/constants" "^8.5.21" + "@luma.gl/core" "^8.5.21" + "@luma.gl/webgl" "^8.5.21" "@math.gl/core" "^3.6.2" "@math.gl/sun" "^3.6.2" "@math.gl/web-mercator" "^3.6.2" @@ -1289,28 +1291,30 @@ math.gl "^3.6.2" mjolnir.js "^2.7.0" -"@deck.gl/extensions@^8.9.7": - version "8.9.18" - resolved "https://registry.yarnpkg.com/@deck.gl/extensions/-/extensions-8.9.18.tgz#899136199d9e14af052e29e3940dd01de49f0e1f" - integrity sha512-Tyrea4UiOm7KQQGjaEcXvtT1OHDjQErhZEt9GJcZx4MIbF6g2RZkR/gsXcNPYq4/sPg7evRlspe43TUMKUjYZw== - dependencies: - "@luma.gl/shadertools" "^8.5.20" - -"@deck.gl/geo-layers@^8.9.7": - version "8.9.18" - resolved "https://registry.yarnpkg.com/@deck.gl/geo-layers/-/geo-layers-8.9.18.tgz#d4b2cec11a5491081d9a383c3db87bd92d2c0cb7" - integrity sha512-vjOuMXK331JYvzdPUbMQK7DiPfYGXMuoFWtIAvmCihRg2Cw5nQspRTwIx7X2JxCQFYiKDmkq/4CRyE9Oz0X9Nw== - dependencies: - "@loaders.gl/3d-tiles" "^3.4.2" - "@loaders.gl/gis" "^3.4.2" - "@loaders.gl/loader-utils" "^3.4.2" - "@loaders.gl/mvt" "^3.4.2" - "@loaders.gl/schema" "^3.4.2" - "@loaders.gl/terrain" "^3.4.2" - "@loaders.gl/tiles" "^3.4.2" - "@loaders.gl/wms" "^3.4.2" - "@luma.gl/constants" "^8.5.20" - "@luma.gl/experimental" "^8.5.20" +"@deck.gl/extensions@^8.9.28": + version "8.9.28" + resolved "https://registry.yarnpkg.com/@deck.gl/extensions/-/extensions-8.9.28.tgz#cb37918e1081f28a1f584ac8061856a41ba7c67e" + integrity sha512-4j1k9a2epu+ZOOhrXm2OKwjsn3fS9l6X1K+iTPXHuRGCpf6r9ArrmF8YnI3sdr6u1Tr4JIjgOvV0r6+Ntqagng== + dependencies: + "@babel/runtime" "^7.0.0" + "@luma.gl/shadertools" "^8.5.21" + +"@deck.gl/geo-layers@^8.9.28": + version "8.9.28" + resolved "https://registry.yarnpkg.com/@deck.gl/geo-layers/-/geo-layers-8.9.28.tgz#3612b02438ab442b6a47accdaf9049c73f5b33e1" + integrity sha512-0pTsDEc+Gx2j9QPJ+CXa6lpZxZKM4HgHyATmLCRetPVm+1hbCs28xuiyLMWIRfXD+izmVm5/o9OoBuC04Q6kqg== + dependencies: + "@babel/runtime" "^7.0.0" + "@loaders.gl/3d-tiles" "^3.4.13" + "@loaders.gl/gis" "^3.4.13" + "@loaders.gl/loader-utils" "^3.4.13" + "@loaders.gl/mvt" "^3.4.13" + "@loaders.gl/schema" "^3.4.13" + "@loaders.gl/terrain" "^3.4.13" + "@loaders.gl/tiles" "^3.4.13" + "@loaders.gl/wms" "^3.4.13" + "@luma.gl/constants" "^8.5.21" + "@luma.gl/experimental" "^8.5.21" "@math.gl/core" "^3.6.2" "@math.gl/culling" "^3.6.2" "@math.gl/web-mercator" "^3.6.2" @@ -1318,34 +1322,38 @@ h3-js "^3.7.0" long "^3.2.0" -"@deck.gl/layers@^8.9.7": - version "8.9.18" - resolved "https://registry.yarnpkg.com/@deck.gl/layers/-/layers-8.9.18.tgz#1222f2ec3a5e4151c76e5d7655906c2d86258a18" - integrity sha512-O6X8ueTBp0r//47UeXCHXvr+TNKJPjdfmwQVzGSogFejOif1IVpU2hycIOJ5pMl/PdYs8uZK8VbZj7qYibsrMQ== +"@deck.gl/layers@^8.9.28": + version "8.9.28" + resolved "https://registry.yarnpkg.com/@deck.gl/layers/-/layers-8.9.28.tgz#dc55442e78f186d919d690e5283bfa95706cf743" + integrity sha512-2ty8zrrq7TNahkgzly/1NBH90I9VrgIDdbcSUBldptkjeYdtsSErAMuKycQGuQdzJb/KFriZAwOxPjSnMEDJ2w== dependencies: - "@loaders.gl/images" "^3.4.2" - "@loaders.gl/schema" "^3.4.2" - "@luma.gl/constants" "^8.5.20" + "@babel/runtime" "^7.0.0" + "@loaders.gl/images" "^3.4.13" + "@loaders.gl/schema" "^3.4.13" + "@luma.gl/constants" "^8.5.21" "@mapbox/tiny-sdf" "^2.0.5" "@math.gl/core" "^3.6.2" "@math.gl/polygon" "^3.6.2" "@math.gl/web-mercator" "^3.6.2" earcut "^2.2.4" -"@deck.gl/mesh-layers@^8.9.7": - version "8.9.18" - resolved "https://registry.yarnpkg.com/@deck.gl/mesh-layers/-/mesh-layers-8.9.18.tgz#c57262210773161d0ea0511625b4372892ca915c" - integrity sha512-1/kH8T7q2UbpUzxrbTiDGA1g75W99Oiz3vvNpPz18W+qEyWMyrxIv80yIdT1s4DKe9uPIfiktNjdYPuhD6q9xw== +"@deck.gl/mesh-layers@^8.9.28": + version "8.9.28" + resolved "https://registry.yarnpkg.com/@deck.gl/mesh-layers/-/mesh-layers-8.9.28.tgz#e485e90f0aee2acc8aca14a51ebc3d7b55b300b7" + integrity sha512-n4m9NfVzPy00G21WA9nkl6T0dlVIWhLcyFAicMMQBX4CqK/iXWz090tT8GtZ6PUzzohuTofUQvvGWgwxhEJM0w== dependencies: - "@loaders.gl/gltf" "^3.4.2" - "@luma.gl/constants" "^8.5.20" - "@luma.gl/experimental" "^8.5.20" - "@luma.gl/shadertools" "^8.5.20" + "@babel/runtime" "^7.0.0" + "@loaders.gl/gltf" "^3.4.13" + "@luma.gl/constants" "^8.5.21" + "@luma.gl/experimental" "^8.5.21" + "@luma.gl/shadertools" "^8.5.21" -"@deck.gl/react@^8.9.7": - version "8.9.18" - resolved "https://registry.yarnpkg.com/@deck.gl/react/-/react-8.9.18.tgz#404b6be119766da872a0511eeb12d1a78d3bfa20" - integrity sha512-HpkZptmTGOKKoRmf3JQEuV2TvxtjeeKclRwQYe7c0mWfxVbVnfBKcKBYMC8cIyXF6ONgLjKXp3PN0iIoftu9gw== +"@deck.gl/react@^8.9.28": + version "8.9.28" + resolved "https://registry.yarnpkg.com/@deck.gl/react/-/react-8.9.28.tgz#b4bd4053d1ec15895613ab2a2c2c13919d652317" + integrity sha512-OZYeawC69WJKVb8BJZuTWiq1wGaJPchSy2bgVHuDlydDpz+Efv2TyJv9EcyimP7pFCfRUh3Fvobbz5aP1dYwBQ== + dependencies: + "@babel/runtime" "^7.0.0" "@discoveryjs/json-ext@0.5.7": version "0.5.7" @@ -1367,10 +1375,10 @@ "@docsearch/css" "3.3.3" algoliasearch "^4.0.0" -"@docusaurus/core@2.4.0", "@docusaurus/core@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.4.0.tgz#a12c175cb2e5a7e4582e65876a50813f6168913d" - integrity sha512-J55/WEoIpRcLf3afO5POHPguVZosKmJEQWKBL+K7TAnfuE7i+Y0NPLlkKtnWCehagGsgTqClfQEexH/UT4kELA== +"@docusaurus/core@2.4.3", "@docusaurus/core@^2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.4.3.tgz#d86624901386fd8164ce4bff9cc7f16fde57f523" + integrity sha512-dWH5P7cgeNSIg9ufReX6gaCl/TmrGKD38Orbwuz05WPhAQtFXHd5B8Qym1TiXfvUNvwoYKkAJOJuGe8ou0Z7PA== dependencies: "@babel/core" "^7.18.6" "@babel/generator" "^7.18.7" @@ -1382,13 +1390,13 @@ "@babel/runtime" "^7.18.6" "@babel/runtime-corejs3" "^7.18.6" "@babel/traverse" "^7.18.8" - "@docusaurus/cssnano-preset" "2.4.0" - "@docusaurus/logger" "2.4.0" - "@docusaurus/mdx-loader" "2.4.0" + "@docusaurus/cssnano-preset" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/mdx-loader" "2.4.3" "@docusaurus/react-loadable" "5.5.2" - "@docusaurus/utils" "2.4.0" - "@docusaurus/utils-common" "2.4.0" - "@docusaurus/utils-validation" "2.4.0" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" "@slorber/static-site-generator-webpack-plugin" "^4.0.7" "@svgr/webpack" "^6.2.1" autoprefixer "^10.4.7" @@ -1444,33 +1452,33 @@ webpack-merge "^5.8.0" webpackbar "^5.0.2" -"@docusaurus/cssnano-preset@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.0.tgz#9213586358e0cce517f614af041eb7d184f8add6" - integrity sha512-RmdiA3IpsLgZGXRzqnmTbGv43W4OD44PCo+6Q/aYjEM2V57vKCVqNzuafE94jv0z/PjHoXUrjr69SaRymBKYYw== +"@docusaurus/cssnano-preset@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.3.tgz#1d7e833c41ce240fcc2812a2ac27f7b862f32de0" + integrity sha512-ZvGSRCi7z9wLnZrXNPG6DmVPHdKGd8dIn9pYbEOFiYihfv4uDR3UtxogmKf+rT8ZlKFf5Lqne8E8nt08zNM8CA== dependencies: cssnano-preset-advanced "^5.3.8" postcss "^8.4.14" postcss-sort-media-queries "^4.2.1" tslib "^2.4.0" -"@docusaurus/logger@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.4.0.tgz#393d91ad9ecdb9a8f80167dd6a34d4b45219b835" - integrity sha512-T8+qR4APN+MjcC9yL2Es+xPJ2923S9hpzDmMtdsOcUGLqpCGBbU1vp3AAqDwXtVgFkq+NsEk7sHdVsfLWR/AXw== +"@docusaurus/logger@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.4.3.tgz#518bbc965fb4ebe8f1d0b14e5f4161607552d34c" + integrity sha512-Zxws7r3yLufk9xM1zq9ged0YHs65mlRmtsobnFkdZTxWXdTYlWWLWdKyNKAsVC+D7zg+pv2fGbyabdOnyZOM3w== dependencies: chalk "^4.1.2" tslib "^2.4.0" -"@docusaurus/mdx-loader@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.4.0.tgz#c6310342904af2f203e7df86a9df623f86840f2d" - integrity sha512-GWoH4izZKOmFoC+gbI2/y8deH/xKLvzz/T5BsEexBye8EHQlwsA7FMrVa48N063bJBH4FUOiRRXxk5rq9cC36g== +"@docusaurus/mdx-loader@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.4.3.tgz#e8ff37f30a060eaa97b8121c135f74cb531a4a3e" + integrity sha512-b1+fDnWtl3GiqkL0BRjYtc94FZrcDDBV1j8446+4tptB9BAOlePwG2p/pK6vGvfL53lkOsszXMghr2g67M0vCw== dependencies: "@babel/parser" "^7.18.8" "@babel/traverse" "^7.18.8" - "@docusaurus/logger" "2.4.0" - "@docusaurus/utils" "2.4.0" + "@docusaurus/logger" "2.4.3" + "@docusaurus/utils" "2.4.3" "@mdx-js/mdx" "^1.6.22" escape-html "^1.0.3" file-loader "^6.2.0" @@ -1485,13 +1493,13 @@ url-loader "^4.1.1" webpack "^5.73.0" -"@docusaurus/module-type-aliases@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.0.tgz#6961605d20cd46f86163ed8c2d83d438b02b4028" - integrity sha512-YEQO2D3UXs72qCn8Cr+RlycSQXVGN9iEUyuHwTuK4/uL/HFomB2FHSU0vSDM23oLd+X/KibQ3Ez6nGjQLqXcHg== +"@docusaurus/module-type-aliases@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.3.tgz#d08ef67e4151e02f352a2836bcf9ecde3b9c56ac" + integrity sha512-cwkBkt1UCiduuvEAo7XZY01dJfRn7UR/75mBgOdb1hKknhrabJZ8YH+7savd/y9kLExPyrhe0QwdS9GuzsRRIA== dependencies: "@docusaurus/react-loadable" "5.5.2" - "@docusaurus/types" "2.4.0" + "@docusaurus/types" "2.4.3" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" @@ -1499,33 +1507,33 @@ react-helmet-async "*" react-loadable "npm:@docusaurus/react-loadable@5.5.2" -"@docusaurus/plugin-client-redirects@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-2.4.0.tgz#53117d112ac9cc191deda053af4335e0381b4125" - integrity sha512-HsS+Dc2ZLWhfpjYJ5LIrOB/XfXZcElcC7o1iA4yIVtiFz+LHhwP863fhqbwSJ1c6tNDOYBH3HwbskHrc/PIn7Q== - dependencies: - "@docusaurus/core" "2.4.0" - "@docusaurus/logger" "2.4.0" - "@docusaurus/utils" "2.4.0" - "@docusaurus/utils-common" "2.4.0" - "@docusaurus/utils-validation" "2.4.0" +"@docusaurus/plugin-client-redirects@^2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-2.4.3.tgz#0da7e6facadbca3bd7cb8d0453f21bea7f4f1721" + integrity sha512-iCwc/zH8X6eNtLYdyUJFY6+GbsbRgMgvAC/TmSmCYTmwnoN5Y1Bc5OwUkdtoch0XKizotJMRAmGIAhP8sAetdQ== + dependencies: + "@docusaurus/core" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" eta "^2.0.0" fs-extra "^10.1.0" lodash "^4.17.21" tslib "^2.4.0" -"@docusaurus/plugin-content-blog@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.0.tgz#50dbfbc7b51f152ae660385fd8b34076713374c3" - integrity sha512-YwkAkVUxtxoBAIj/MCb4ohN0SCtHBs4AS75jMhPpf67qf3j+U/4n33cELq7567hwyZ6fMz2GPJcVmctzlGGThQ== - dependencies: - "@docusaurus/core" "2.4.0" - "@docusaurus/logger" "2.4.0" - "@docusaurus/mdx-loader" "2.4.0" - "@docusaurus/types" "2.4.0" - "@docusaurus/utils" "2.4.0" - "@docusaurus/utils-common" "2.4.0" - "@docusaurus/utils-validation" "2.4.0" +"@docusaurus/plugin-content-blog@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.3.tgz#6473b974acab98e967414d8bbb0d37e0cedcea14" + integrity sha512-PVhypqaA0t98zVDpOeTqWUTvRqCEjJubtfFUQ7zJNYdbYTbS/E/ytq6zbLVsN/dImvemtO/5JQgjLxsh8XLo8Q== + dependencies: + "@docusaurus/core" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/mdx-loader" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" cheerio "^1.0.0-rc.12" feed "^4.2.2" fs-extra "^10.1.0" @@ -1536,18 +1544,18 @@ utility-types "^3.10.0" webpack "^5.73.0" -"@docusaurus/plugin-content-docs@2.4.0", "@docusaurus/plugin-content-docs@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.0.tgz#36e235adf902325735b873b4f535205884363728" - integrity sha512-ic/Z/ZN5Rk/RQo+Io6rUGpToOtNbtPloMR2JcGwC1xT2riMu6zzfSwmBi9tHJgdXH6CB5jG+0dOZZO8QS5tmDg== - dependencies: - "@docusaurus/core" "2.4.0" - "@docusaurus/logger" "2.4.0" - "@docusaurus/mdx-loader" "2.4.0" - "@docusaurus/module-type-aliases" "2.4.0" - "@docusaurus/types" "2.4.0" - "@docusaurus/utils" "2.4.0" - "@docusaurus/utils-validation" "2.4.0" +"@docusaurus/plugin-content-docs@2.4.3", "@docusaurus/plugin-content-docs@^2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.3.tgz#aa224c0512351e81807adf778ca59fd9cd136973" + integrity sha512-N7Po2LSH6UejQhzTCsvuX5NOzlC+HiXOVvofnEPj0WhMu1etpLEXE6a4aTxrtg95lQ5kf0xUIdjX9sh3d3G76A== + dependencies: + "@docusaurus/core" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/mdx-loader" "2.4.3" + "@docusaurus/module-type-aliases" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" "@types/react-router-config" "^5.0.6" combine-promises "^1.1.0" fs-extra "^10.1.0" @@ -1558,95 +1566,95 @@ utility-types "^3.10.0" webpack "^5.73.0" -"@docusaurus/plugin-content-pages@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.0.tgz#6169909a486e1eae0ddffff0b1717ce4332db4d4" - integrity sha512-Pk2pOeOxk8MeU3mrTU0XLIgP9NZixbdcJmJ7RUFrZp1Aj42nd0RhIT14BGvXXyqb8yTQlk4DmYGAzqOfBsFyGw== - dependencies: - "@docusaurus/core" "2.4.0" - "@docusaurus/mdx-loader" "2.4.0" - "@docusaurus/types" "2.4.0" - "@docusaurus/utils" "2.4.0" - "@docusaurus/utils-validation" "2.4.0" +"@docusaurus/plugin-content-pages@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.3.tgz#7f285e718b53da8c8d0101e70840c75b9c0a1ac0" + integrity sha512-txtDVz7y3zGk67q0HjG0gRttVPodkHqE0bpJ+7dOaTH40CQFLSh7+aBeGnPOTl+oCPG+hxkim4SndqPqXjQ8Bg== + dependencies: + "@docusaurus/core" "2.4.3" + "@docusaurus/mdx-loader" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" fs-extra "^10.1.0" tslib "^2.4.0" webpack "^5.73.0" -"@docusaurus/plugin-debug@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.4.0.tgz#1ad513fe9bcaf017deccf62df8b8843faeeb7d37" - integrity sha512-KC56DdYjYT7Txyux71vXHXGYZuP6yYtqwClvYpjKreWIHWus5Zt6VNi23rMZv3/QKhOCrN64zplUbdfQMvddBQ== +"@docusaurus/plugin-debug@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.4.3.tgz#2f90eb0c9286a9f225444e3a88315676fe02c245" + integrity sha512-LkUbuq3zCmINlFb+gAd4ZvYr+bPAzMC0hwND4F7V9bZ852dCX8YoWyovVUBKq4er1XsOwSQaHmNGtObtn8Av8Q== dependencies: - "@docusaurus/core" "2.4.0" - "@docusaurus/types" "2.4.0" - "@docusaurus/utils" "2.4.0" + "@docusaurus/core" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" fs-extra "^10.1.0" react-json-view "^1.21.3" tslib "^2.4.0" -"@docusaurus/plugin-google-analytics@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.0.tgz#8062d7a09d366329dfd3ce4e8a619da8624b6cc3" - integrity sha512-uGUzX67DOAIglygdNrmMOvEp8qG03X20jMWadeqVQktS6nADvozpSLGx4J0xbkblhJkUzN21WiilsP9iVP+zkw== +"@docusaurus/plugin-google-analytics@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.3.tgz#0d19993136ade6f7a7741251b4f617400d92ab45" + integrity sha512-KzBV3k8lDkWOhg/oYGxlK5o9bOwX7KpPc/FTWoB+SfKhlHfhq7qcQdMi1elAaVEIop8tgK6gD1E58Q+XC6otSQ== dependencies: - "@docusaurus/core" "2.4.0" - "@docusaurus/types" "2.4.0" - "@docusaurus/utils-validation" "2.4.0" + "@docusaurus/core" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" tslib "^2.4.0" -"@docusaurus/plugin-google-gtag@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.0.tgz#a8efda476f971410dfb3aab1cfe1f0f7d269adc5" - integrity sha512-adj/70DANaQs2+TF/nRdMezDXFAV/O/pjAbUgmKBlyOTq5qoMe0Tk4muvQIwWUmiUQxFJe+sKlZGM771ownyOg== +"@docusaurus/plugin-google-gtag@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.3.tgz#e1a80b0696771b488562e5b60eff21c9932d9e1c" + integrity sha512-5FMg0rT7sDy4i9AGsvJC71MQrqQZwgLNdDetLEGDHLfSHLvJhQbTCUGbGXknUgWXQJckcV/AILYeJy+HhxeIFA== dependencies: - "@docusaurus/core" "2.4.0" - "@docusaurus/types" "2.4.0" - "@docusaurus/utils-validation" "2.4.0" + "@docusaurus/core" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" tslib "^2.4.0" -"@docusaurus/plugin-google-tag-manager@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.0.tgz#9a94324ac496835fc34e233cc60441df4e04dfdd" - integrity sha512-E66uGcYs4l7yitmp/8kMEVQftFPwV9iC62ORh47Veqzs6ExwnhzBkJmwDnwIysHBF1vlxnzET0Fl2LfL5fRR3A== +"@docusaurus/plugin-google-tag-manager@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.3.tgz#e41fbf79b0ffc2de1cc4013eb77798cff0ad98e3" + integrity sha512-1jTzp71yDGuQiX9Bi0pVp3alArV0LSnHXempvQTxwCGAEzUWWaBg4d8pocAlTpbP9aULQQqhgzrs8hgTRPOM0A== dependencies: - "@docusaurus/core" "2.4.0" - "@docusaurus/types" "2.4.0" - "@docusaurus/utils-validation" "2.4.0" + "@docusaurus/core" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" tslib "^2.4.0" -"@docusaurus/plugin-sitemap@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.0.tgz#ba0eb43565039fe011bdd874b5c5d7252b19d709" - integrity sha512-pZxh+ygfnI657sN8a/FkYVIAmVv0CGk71QMKqJBOfMmDHNN1FeDeFkBjWP49ejBqpqAhjufkv5UWq3UOu2soCw== - dependencies: - "@docusaurus/core" "2.4.0" - "@docusaurus/logger" "2.4.0" - "@docusaurus/types" "2.4.0" - "@docusaurus/utils" "2.4.0" - "@docusaurus/utils-common" "2.4.0" - "@docusaurus/utils-validation" "2.4.0" +"@docusaurus/plugin-sitemap@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.3.tgz#1b3930900a8f89670ce7e8f83fb4730cd3298c32" + integrity sha512-LRQYrK1oH1rNfr4YvWBmRzTL0LN9UAPxBbghgeFRBm5yloF6P+zv1tm2pe2hQTX/QP5bSKdnajCvfnScgKXMZQ== + dependencies: + "@docusaurus/core" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" fs-extra "^10.1.0" sitemap "^7.1.1" tslib "^2.4.0" -"@docusaurus/preset-classic@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.4.0.tgz#92fdcfab35d8d0ffb8c38bcbf439e4e1cb0566a3" - integrity sha512-/5z5o/9bc6+P5ool2y01PbJhoGddEGsC0ej1MF6mCoazk8A+kW4feoUd68l7Bnv01rCnG3xy7kHUQP97Y0grUA== - dependencies: - "@docusaurus/core" "2.4.0" - "@docusaurus/plugin-content-blog" "2.4.0" - "@docusaurus/plugin-content-docs" "2.4.0" - "@docusaurus/plugin-content-pages" "2.4.0" - "@docusaurus/plugin-debug" "2.4.0" - "@docusaurus/plugin-google-analytics" "2.4.0" - "@docusaurus/plugin-google-gtag" "2.4.0" - "@docusaurus/plugin-google-tag-manager" "2.4.0" - "@docusaurus/plugin-sitemap" "2.4.0" - "@docusaurus/theme-classic" "2.4.0" - "@docusaurus/theme-common" "2.4.0" - "@docusaurus/theme-search-algolia" "2.4.0" - "@docusaurus/types" "2.4.0" +"@docusaurus/preset-classic@^2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.4.3.tgz#074c57ebf29fa43d23bd1c8ce691226f542bc262" + integrity sha512-tRyMliepY11Ym6hB1rAFSNGwQDpmszvWYJvlK1E+md4SW8i6ylNHtpZjaYFff9Mdk3i/Pg8ItQq9P0daOJAvQw== + dependencies: + "@docusaurus/core" "2.4.3" + "@docusaurus/plugin-content-blog" "2.4.3" + "@docusaurus/plugin-content-docs" "2.4.3" + "@docusaurus/plugin-content-pages" "2.4.3" + "@docusaurus/plugin-debug" "2.4.3" + "@docusaurus/plugin-google-analytics" "2.4.3" + "@docusaurus/plugin-google-gtag" "2.4.3" + "@docusaurus/plugin-google-tag-manager" "2.4.3" + "@docusaurus/plugin-sitemap" "2.4.3" + "@docusaurus/theme-classic" "2.4.3" + "@docusaurus/theme-common" "2.4.3" + "@docusaurus/theme-search-algolia" "2.4.3" + "@docusaurus/types" "2.4.3" "@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": version "5.5.2" @@ -1656,23 +1664,23 @@ "@types/react" "*" prop-types "^15.6.2" -"@docusaurus/theme-classic@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.4.0.tgz#a5404967b00adec3472efca4c3b3f6a5e2021c78" - integrity sha512-GMDX5WU6Z0OC65eQFgl3iNNEbI9IMJz9f6KnOyuMxNUR6q0qVLsKCNopFUDfFNJ55UU50o7P7o21yVhkwpfJ9w== - dependencies: - "@docusaurus/core" "2.4.0" - "@docusaurus/mdx-loader" "2.4.0" - "@docusaurus/module-type-aliases" "2.4.0" - "@docusaurus/plugin-content-blog" "2.4.0" - "@docusaurus/plugin-content-docs" "2.4.0" - "@docusaurus/plugin-content-pages" "2.4.0" - "@docusaurus/theme-common" "2.4.0" - "@docusaurus/theme-translations" "2.4.0" - "@docusaurus/types" "2.4.0" - "@docusaurus/utils" "2.4.0" - "@docusaurus/utils-common" "2.4.0" - "@docusaurus/utils-validation" "2.4.0" +"@docusaurus/theme-classic@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.4.3.tgz#29360f2eb03a0e1686eb19668633ef313970ee8f" + integrity sha512-QKRAJPSGPfDY2yCiPMIVyr+MqwZCIV2lxNzqbyUW0YkrlmdzzP3WuQJPMGLCjWgQp/5c9kpWMvMxjhpZx1R32Q== + dependencies: + "@docusaurus/core" "2.4.3" + "@docusaurus/mdx-loader" "2.4.3" + "@docusaurus/module-type-aliases" "2.4.3" + "@docusaurus/plugin-content-blog" "2.4.3" + "@docusaurus/plugin-content-docs" "2.4.3" + "@docusaurus/plugin-content-pages" "2.4.3" + "@docusaurus/theme-common" "2.4.3" + "@docusaurus/theme-translations" "2.4.3" + "@docusaurus/types" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" "@mdx-js/react" "^1.6.22" clsx "^1.2.1" copy-text-to-clipboard "^3.0.1" @@ -1687,18 +1695,18 @@ tslib "^2.4.0" utility-types "^3.10.0" -"@docusaurus/theme-common@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.4.0.tgz#626096fe9552d240a2115b492c7e12099070cf2d" - integrity sha512-IkG/l5f/FLY6cBIxtPmFnxpuPzc5TupuqlOx+XDN+035MdQcAh8wHXXZJAkTeYDeZ3anIUSUIvWa7/nRKoQEfg== - dependencies: - "@docusaurus/mdx-loader" "2.4.0" - "@docusaurus/module-type-aliases" "2.4.0" - "@docusaurus/plugin-content-blog" "2.4.0" - "@docusaurus/plugin-content-docs" "2.4.0" - "@docusaurus/plugin-content-pages" "2.4.0" - "@docusaurus/utils" "2.4.0" - "@docusaurus/utils-common" "2.4.0" +"@docusaurus/theme-common@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.4.3.tgz#bb31d70b6b67d0bdef9baa343192dcec49946a2e" + integrity sha512-7KaDJBXKBVGXw5WOVt84FtN8czGWhM0lbyWEZXGp8AFfL6sZQfRTluFp4QriR97qwzSyOfQb+nzcDZZU4tezUw== + dependencies: + "@docusaurus/mdx-loader" "2.4.3" + "@docusaurus/module-type-aliases" "2.4.3" + "@docusaurus/plugin-content-blog" "2.4.3" + "@docusaurus/plugin-content-docs" "2.4.3" + "@docusaurus/plugin-content-pages" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-common" "2.4.3" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" @@ -1709,19 +1717,19 @@ use-sync-external-store "^1.2.0" utility-types "^3.10.0" -"@docusaurus/theme-search-algolia@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.0.tgz#07d297d50c44446d6bc5a37be39afb8f014084e1" - integrity sha512-pPCJSCL1Qt4pu/Z0uxBAuke0yEBbxh0s4fOvimna7TEcBLPq0x06/K78AaABXrTVQM6S0vdocFl9EoNgU17hqA== +"@docusaurus/theme-search-algolia@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.3.tgz#32d4cbefc3deba4112068fbdb0bde11ac51ece53" + integrity sha512-jziq4f6YVUB5hZOB85ELATwnxBz/RmSLD3ksGQOLDPKVzat4pmI8tddNWtriPpxR04BNT+ZfpPUMFkNFetSW1Q== dependencies: "@docsearch/react" "^3.1.1" - "@docusaurus/core" "2.4.0" - "@docusaurus/logger" "2.4.0" - "@docusaurus/plugin-content-docs" "2.4.0" - "@docusaurus/theme-common" "2.4.0" - "@docusaurus/theme-translations" "2.4.0" - "@docusaurus/utils" "2.4.0" - "@docusaurus/utils-validation" "2.4.0" + "@docusaurus/core" "2.4.3" + "@docusaurus/logger" "2.4.3" + "@docusaurus/plugin-content-docs" "2.4.3" + "@docusaurus/theme-common" "2.4.3" + "@docusaurus/theme-translations" "2.4.3" + "@docusaurus/utils" "2.4.3" + "@docusaurus/utils-validation" "2.4.3" algoliasearch "^4.13.1" algoliasearch-helper "^3.10.0" clsx "^1.2.1" @@ -1731,18 +1739,18 @@ tslib "^2.4.0" utility-types "^3.10.0" -"@docusaurus/theme-translations@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.4.0.tgz#62dacb7997322f4c5a828b3ab66177ec6769eb33" - integrity sha512-kEoITnPXzDPUMBHk3+fzEzbopxLD3fR5sDoayNH0vXkpUukA88/aDL1bqkhxWZHA3LOfJ3f0vJbOwmnXW5v85Q== +"@docusaurus/theme-translations@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.4.3.tgz#91ac73fc49b8c652b7a54e88b679af57d6ac6102" + integrity sha512-H4D+lbZbjbKNS/Zw1Lel64PioUAIT3cLYYJLUf3KkuO/oc9e0QCVhIYVtUI2SfBCF2NNdlyhBDQEEMygsCedIg== dependencies: fs-extra "^10.1.0" tslib "^2.4.0" -"@docusaurus/types@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.4.0.tgz#f94f89a0253778b617c5d40ac6f16b17ec55ce41" - integrity sha512-xaBXr+KIPDkIaef06c+i2HeTqVNixB7yFut5fBXPGI2f1rrmEV2vLMznNGsFwvZ5XmA3Quuefd4OGRkdo97Dhw== +"@docusaurus/types@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.4.3.tgz#4aead281ca09f721b3c0a9b926818450cfa3db31" + integrity sha512-W6zNLGQqfrp/EoPD0bhb9n7OobP+RHpmvVzpA+Z/IuU3Q63njJM24hmT0GYboovWcDtFmnIJC9wcyx4RVPQscw== dependencies: "@types/history" "^4.7.11" "@types/react" "*" @@ -1753,30 +1761,30 @@ webpack "^5.73.0" webpack-merge "^5.8.0" -"@docusaurus/utils-common@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.4.0.tgz#eb2913871860ed32e73858b4c7787dd820c5558d" - integrity sha512-zIMf10xuKxddYfLg5cS19x44zud/E9I7lj3+0bv8UIs0aahpErfNrGhijEfJpAfikhQ8tL3m35nH3hJ3sOG82A== +"@docusaurus/utils-common@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.4.3.tgz#30656c39ef1ce7e002af7ba39ea08330f58efcfb" + integrity sha512-/jascp4GbLQCPVmcGkPzEQjNaAk3ADVfMtudk49Ggb+131B1WDD6HqlSmDf8MxGdy7Dja2gc+StHf01kiWoTDQ== dependencies: tslib "^2.4.0" -"@docusaurus/utils-validation@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.4.0.tgz#1ed92bfab5da321c4a4d99cad28a15627091aa90" - integrity sha512-IrBsBbbAp6y7mZdJx4S4pIA7dUyWSA0GNosPk6ZJ0fX3uYIEQgcQSGIgTeSC+8xPEx3c16o03en1jSDpgQgz/w== +"@docusaurus/utils-validation@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.4.3.tgz#8122c394feef3e96c73f6433987837ec206a63fb" + integrity sha512-G2+Vt3WR5E/9drAobP+hhZQMaswRwDlp6qOMi7o7ZypB+VO7N//DZWhZEwhcRGepMDJGQEwtPv7UxtYwPL9PBw== dependencies: - "@docusaurus/logger" "2.4.0" - "@docusaurus/utils" "2.4.0" + "@docusaurus/logger" "2.4.3" + "@docusaurus/utils" "2.4.3" joi "^17.6.0" js-yaml "^4.1.0" tslib "^2.4.0" -"@docusaurus/utils@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.4.0.tgz#fdf0c3545819e48bb57eafc5057495fd4d50e900" - integrity sha512-89hLYkvtRX92j+C+ERYTuSUK6nF9bGM32QThcHPg2EDDHVw6FzYQXmX6/p+pU5SDyyx5nBlE4qXR92RxCAOqfg== +"@docusaurus/utils@2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.4.3.tgz#52b000d989380a2125831b84e3a7327bef471e89" + integrity sha512-fKcXsjrD86Smxv8Pt0TBFqYieZZCPh4cbf9oszUq/AMhZn3ujwpKaVYZACPX8mmjtYx0JOgNx52CREBfiGQB4A== dependencies: - "@docusaurus/logger" "2.4.0" + "@docusaurus/logger" "2.4.3" "@svgr/webpack" "^6.2.1" escape-string-regexp "^4.0.0" file-loader "^6.2.0" @@ -1993,207 +2001,398 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@loaders.gl/3d-tiles@^3.4.2": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/3d-tiles/-/3d-tiles-3.4.4.tgz#e80f930fd7c318add0ef9df0699c623611c6fec6" - integrity sha512-o6z8h5541OYTQT546p1FJlMjiqFvTu29C6W9F9X3rPIUdnBirTpCubgpHcAw53AIDOrvlIxBKH/KDkqoxFIylQ== +"@loaders.gl/3d-tiles@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/3d-tiles/-/3d-tiles-3.4.14.tgz#c04781acf377e13c16b41eeaa14e46dfc96c90ca" + integrity sha512-cxStTSLIJgRZnkTBYTcp9FPVBQWQlJMzW1LRlaKWiwAHkOKBElszzApIIEvRvZGSrs8k8TUi6BJ1Y41iiANF7w== dependencies: - "@loaders.gl/draco" "3.4.4" - "@loaders.gl/gltf" "3.4.4" - "@loaders.gl/loader-utils" "3.4.4" - "@loaders.gl/math" "3.4.4" - "@loaders.gl/tiles" "3.4.4" + "@loaders.gl/draco" "3.4.14" + "@loaders.gl/gltf" "3.4.14" + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/math" "3.4.14" + "@loaders.gl/tiles" "3.4.14" "@math.gl/core" "^3.5.1" "@math.gl/geospatial" "^3.5.1" long "^5.2.1" -"@loaders.gl/core@^3.4.2": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/core/-/core-3.4.4.tgz#65c182fcf9e7e6536ac7d27d87635a8721843a95" - integrity sha512-uutqjvf91WJZx7WbSmJy75AHFNCPDnnweFnVmdAEflF6ohc+uAdjltqz6tGD3PxbT8LjNLTOk60kxyC/QwDBqQ== +"@loaders.gl/compression@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/compression/-/compression-4.0.2.tgz#b029e3ff2b039eccd694d6e05244fbbbd09ef842" + integrity sha512-tfkv//P9BzgFy66073oyTxNinkGaPRaYcrAs4+Ac8qH2NC64Y/MA/gJ1R9CmZLyd+oK59LlgCU7FRC2uCU7znA== dependencies: "@babel/runtime" "^7.3.1" - "@loaders.gl/loader-utils" "3.4.4" - "@loaders.gl/worker-utils" "3.4.4" + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/worker-utils" "4.0.2" + "@types/brotli" "^1.3.0" + "@types/pako" "^1.0.1" + fflate "0.7.4" + lzo-wasm "^0.0.4" + pako "1.0.11" + snappyjs "^0.6.1" + optionalDependencies: + brotli "^1.3.2" + lz4js "^0.2.0" + zstd-codec "^0.1" + +"@loaders.gl/core@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/core/-/core-3.4.14.tgz#79e5c54112f5bfe398da1718dc4fb661ffa213fd" + integrity sha512-5PFcjv7xC8AYL17juDMrvo8n0Fcwg9s8F4BaM2YCNUsb9RCI2SmLuIFJMcx1GgHO5vL0WiTIKO+JT4n1FuNR6w== + dependencies: + "@babel/runtime" "^7.3.1" + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/worker-utils" "3.4.14" "@probe.gl/log" "^4.0.1" -"@loaders.gl/draco@3.4.4": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/draco/-/draco-3.4.4.tgz#cf2db51cf413a866363475da439382ef6878e579" - integrity sha512-VtJffpDbcdA0/uJzzJIET3B5j96cz6g5f93Wg2tlGtvnKZvJs4bjyojur4p7u5ElHJARm36F91N7Td4jGvMbYw== +"@loaders.gl/core@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/core/-/core-4.0.2.tgz#99fcda9a7a5236f3aad8f7cc92333f434bdeb901" + integrity sha512-itvzdiDg85lmF7O/QYNgE7TOhpMzHdUdMAVpt9P+5D0LZPxOqNIfWbxtuT0DDCssRmWkmm4tgXiOeONHnS293A== + dependencies: + "@babel/runtime" "^7.3.1" + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/worker-utils" "4.0.2" + "@probe.gl/log" "^4.0.2" + +"@loaders.gl/crypto@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/crypto/-/crypto-4.0.2.tgz#903c061dee1dc5ad59b7abbc19ccb3bc48ab525c" + integrity sha512-Z9rhJ1D8BfPc+I5R4GJrZhgNW6KzV/+VHiUmmIqzpBAcaVOafB0jQ3td7UIWzBcRBsCgWLOc+FCmu3FeHQc5vw== + dependencies: + "@babel/runtime" "^7.3.1" + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/worker-utils" "4.0.2" + "@types/crypto-js" "^4.0.2" + +"@loaders.gl/draco@3.4.14": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/draco/-/draco-3.4.14.tgz#8555d9179db334faaded00d2902c318478a93b07" + integrity sha512-HwNFFt+dKZqFtzI0uVGvRkudFEZXxybJ+ZRsNkBbzAWoMM5L1TpuLs6DPsqPQUIT9HXNHzov18cZI0gK5bTJpg== dependencies: "@babel/runtime" "^7.3.1" - "@loaders.gl/loader-utils" "3.4.4" - "@loaders.gl/schema" "3.4.4" - "@loaders.gl/worker-utils" "3.4.4" + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/schema" "3.4.14" + "@loaders.gl/worker-utils" "3.4.14" draco3d "1.5.5" -"@loaders.gl/gis@3.4.4", "@loaders.gl/gis@^3.4.2": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/gis/-/gis-3.4.4.tgz#74390f4271e26b0bcc83bbe5786947658c3093bd" - integrity sha512-QwGOdpaE/jb1KsgHEkkiUD7C+dHWSDJKfMKM5OStIMPABX0Cxd8MSqyQ8+BOFWM7kdqXdMvgRjB9912R6T4AHQ== +"@loaders.gl/draco@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/draco/-/draco-4.0.2.tgz#69c71143f8148da0d147e074b06f40cfb55f68a6" + integrity sha512-dsCmcRJHewETedmZyyFrdXzsBYHo8RxMvZclWgTewcl74PbQBvJNP2j2jKnzNw67UO+Oyuh/Sbfdgf69IItWbQ== dependencies: - "@loaders.gl/loader-utils" "3.4.4" - "@loaders.gl/schema" "3.4.4" + "@babel/runtime" "^7.3.1" + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/schema" "4.0.2" + "@loaders.gl/worker-utils" "4.0.2" + draco3d "1.5.5" + +"@loaders.gl/geopackage@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/geopackage/-/geopackage-4.0.2.tgz#af302989e085f5e0ae6f0d30f5e41c53181e1c68" + integrity sha512-HcPdUgfN41KHIyikh/erQaypxanQkBfafG8dossi6IKU443pQDcj2Juvpq5YpptxVp8Y6qHW+LSXmlMEywIrvg== + dependencies: + "@babel/runtime" "^7.3.1" + "@loaders.gl/gis" "4.0.2" + "@loaders.gl/schema" "4.0.2" + "@loaders.gl/wkt" "4.0.2" + "@math.gl/proj4" "^4.0.0" + "@types/sql.js" "^1.4.5" + fs "^0.0.1-security" + path "^0.12.7" + sql.js "1.8.0" + +"@loaders.gl/gis@3.4.14", "@loaders.gl/gis@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/gis/-/gis-3.4.14.tgz#a9b3eed45e2a4465a754e3404061222c51b1334a" + integrity sha512-5cmhIwioPpSkfNzFRM3PbFDecjpYIhtEOFbryu3rE37npKHLTD2tF4ocQxUPB+QVED6GLwWBdzJIs64UWGrqjw== + dependencies: + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/schema" "3.4.14" "@mapbox/vector-tile" "^1.3.1" "@math.gl/polygon" "^3.5.1" pbf "^3.2.1" -"@loaders.gl/gltf@3.4.4", "@loaders.gl/gltf@^3.4.2": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/gltf/-/gltf-3.4.4.tgz#9b68965432bebda85db05fd354cb348fab37f7a0" - integrity sha512-8dbyZChWXku+OoL64rccFa60uxBhbRLdDelfCZqopRxwI/JF8ZCAEGuoFAftw84sU97JmfJbnCtcMMc8bebv4w== +"@loaders.gl/gis@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/gis/-/gis-4.0.2.tgz#edd3f1e57a66c1a16571a5d9a801a516cf6575ce" + integrity sha512-uZgdvAhsPcWHim5whFiFpemyGO4BhPBuAlhBmmzdD+b8QsEJHwMIouAIleqIkgShRHbseE3xZ+rtRmFCXhiUNQ== dependencies: - "@loaders.gl/draco" "3.4.4" - "@loaders.gl/images" "3.4.4" - "@loaders.gl/loader-utils" "3.4.4" - "@loaders.gl/textures" "3.4.4" + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/schema" "4.0.2" + "@mapbox/vector-tile" "^1.3.1" + "@math.gl/polygon" "^4.0.0" + pbf "^3.2.1" + +"@loaders.gl/gltf@3.4.14", "@loaders.gl/gltf@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/gltf/-/gltf-3.4.14.tgz#8677d6793cf0827dee52711e55fbdc445e9f9bb8" + integrity sha512-jv+B5S/taiwzXAOu5D9nk1jjU9+JCCr/6/nGguCE2Ya3IX7CI1Nlnp20eKKhW8ZCEokZavMNT0bNbiJ5ahEFjA== + dependencies: + "@loaders.gl/draco" "3.4.14" + "@loaders.gl/images" "3.4.14" + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/textures" "3.4.14" "@math.gl/core" "^3.5.1" -"@loaders.gl/images@3.4.4", "@loaders.gl/images@^3.4.2": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/images/-/images-3.4.4.tgz#ce0d56caac49ebf63f76a6234f1df4a7b8c99441" - integrity sha512-ViMh58oZ2GLsKCoYBH4nYMvi5fHeVZXiLAABVP+AVU54Jrf+PZYm8y8KaC22zBmGEZ15hGhJF/dNeOpgqZ+V4w== +"@loaders.gl/i3s@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/i3s/-/i3s-4.0.2.tgz#fd807e164b795d51c8a156785c90e2b355f127a8" + integrity sha512-H4tOZRmAq06M8WsT1EhaT4bLI38B4YSnFjmJfQ5a+qvdaCISXSzQjrqJdbAPnk7Z4JBPufLRNlYYcLG1b34ylg== + dependencies: + "@loaders.gl/compression" "4.0.2" + "@loaders.gl/crypto" "4.0.2" + "@loaders.gl/draco" "4.0.2" + "@loaders.gl/images" "4.0.2" + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/schema" "4.0.2" + "@loaders.gl/textures" "4.0.2" + "@loaders.gl/tiles" "4.0.2" + "@math.gl/core" "^4.0.0" + "@math.gl/culling" "^4.0.0" + "@math.gl/geospatial" "^4.0.0" + +"@loaders.gl/images@3.4.14", "@loaders.gl/images@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/images/-/images-3.4.14.tgz#d7a4950f11b48d028cf3719cf6498945f4a05c14" + integrity sha512-tL447hTWhOKBOB87SE4hvlC8OkbRT0mEaW1a/wIS9f4HnYDa/ycRLMV+nvdvYMZur4isNPam44oiRqi7GcILkg== + dependencies: + "@loaders.gl/loader-utils" "3.4.14" + +"@loaders.gl/images@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/images/-/images-4.0.2.tgz#cefb6a9b812db93ce3e65e81447b8796cfeebc1a" + integrity sha512-mnBIH8mLrN9djvGHQNDsRrlxn+/fvDFwv8iMEf61a347GQsVSxd2OQSONSzHHKvCDfON37hY/EpkvVsEaJwn0Q== dependencies: - "@loaders.gl/loader-utils" "3.4.4" + "@loaders.gl/loader-utils" "4.0.2" -"@loaders.gl/loader-utils@3.4.4", "@loaders.gl/loader-utils@^3.4.2": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-3.4.4.tgz#06a9185e6b9a6457aaaf4f5526dca25c9021ff25" - integrity sha512-EFY/YBniNyfZk0ojnBitl+xRL3Du8tinOwdFnWD0rVIf61+bFifFI0fJys8/tgrlF6sfiKdYbupow8G/a3xF2g== +"@loaders.gl/las@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/las/-/las-4.0.2.tgz#01ad70b3ca7028db482e9e7e3b81033fcf37111b" + integrity sha512-3EElZLz2VqN6iCaX2uHUU0kzm1kpXxbyq+6vbkRFqygs3ZMIRdNn5NxnuIg11r3smPIqudXikeNAR/L/xLALJg== dependencies: "@babel/runtime" "^7.3.1" - "@loaders.gl/worker-utils" "3.4.4" + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/schema" "4.0.2" + laz-perf "^0.0.6" + +"@loaders.gl/loader-utils@3.4.14", "@loaders.gl/loader-utils@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-3.4.14.tgz#d94decc279fd2304b8762c87d8d9626058d91f21" + integrity sha512-HCTY2/F83RLbZWcTvWLVJ1vke3dl6Bye20HU1AqkA37J2vzHwOZ8kj6eee8eeSkIkf7VIFwjyhVJxe0flQE/Bw== + dependencies: + "@babel/runtime" "^7.3.1" + "@loaders.gl/worker-utils" "3.4.14" "@probe.gl/stats" "^4.0.1" +"@loaders.gl/loader-utils@4.0.2", "@loaders.gl/loader-utils@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-4.0.2.tgz#076b9830f3bda7690e7f939aa6bdf097bc40cfb3" + integrity sha512-Obb62xjbe5Hul3IM17bVQB+gIz5XoFKaqXKX1KDErlbOYJ8SiBFTHPfNcAK9bhmYYVgf6jWU9Vn94l241lcJLw== + dependencies: + "@babel/runtime" "^7.3.1" + "@loaders.gl/worker-utils" "4.0.2" + "@probe.gl/stats" "^4.0.2" -"@loaders.gl/math@3.4.4": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/math/-/math-3.4.4.tgz#4e427fdd1bd954b4667303df71ae21cbe1dacad3" - integrity sha512-l5ZGV7gAznj0nFjfiKIP9qIrSKLLiaRvGC2pmbM4J+2A674Sj59WwoZiASYNevOlByjScIwyZWe62wcneuyIWw== +"@loaders.gl/math@3.4.14": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/math/-/math-3.4.14.tgz#c27993f0dbe5a88f3ffa07e5240ce27ea5e92392" + integrity sha512-OBEVX6Q5pMipbCAiZyX2+q1zRd0nw8M2dclpny05on8700OaKMwfs47wEUnbfCU3iyHad3sgsAxN3EIh+kuo9Q== dependencies: - "@loaders.gl/images" "3.4.4" - "@loaders.gl/loader-utils" "3.4.4" + "@loaders.gl/images" "3.4.14" + "@loaders.gl/loader-utils" "3.4.14" "@math.gl/core" "^3.5.1" -"@loaders.gl/mvt@^3.4.2": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/mvt/-/mvt-3.4.4.tgz#7c7559912ed3ef6e995e0baae20cc7c6b090375b" - integrity sha512-qxGe+EmuaDlXBs/EeBFzIKipgv+YrAm2BlHzyxLOsdBVmay9q31OYCqdigVw3Fc5h30D65hOfBC6k1lKo4OUyw== +"@loaders.gl/math@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/math/-/math-4.0.2.tgz#c225b92c30979648ecba4c48c2a0958e21c0a33e" + integrity sha512-3Lu/url3/y8BWBef78xlBhR8tSokOWozV2PV1ixPazNWiE+clDm/JXp+ZibswiLHkCySVrYpMTV9bGSxGPHDpg== + dependencies: + "@loaders.gl/images" "4.0.2" + "@loaders.gl/loader-utils" "4.0.2" + "@math.gl/core" "^4.0.0" + +"@loaders.gl/mvt@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/mvt/-/mvt-3.4.14.tgz#fee16db321301e94b329c6be9db9420db0be17c8" + integrity sha512-tozGmWvsJacjaLavjX4S/5yNDV9S4wJb7+vPG/nXWX2gTtgZ1mxcFQAtAJjokqpy37d1ZhLt+TXh0HrLoTmRgw== dependencies: - "@loaders.gl/gis" "3.4.4" - "@loaders.gl/loader-utils" "3.4.4" - "@loaders.gl/schema" "3.4.4" + "@loaders.gl/gis" "3.4.14" + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/schema" "3.4.14" "@math.gl/polygon" "^3.5.1" pbf "^3.2.1" +"@loaders.gl/obj@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/obj/-/obj-4.0.2.tgz#8d4182efe48479ad4e3e70830e5f4ca7f436163a" + integrity sha512-Msz9yK/r56JjjKSsXXH7PyzWKeIx7fjeT2D9zMIE33HKme8CQSXm0imhoJm5bQBkjH55FWDhfbhjkaIEw21O0Q== + dependencies: + "@babel/runtime" "^7.3.1" + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/schema" "4.0.2" + +"@loaders.gl/ply@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/ply/-/ply-4.0.2.tgz#30073d31b90a15ec319748e05fdf04a6362e6f12" + integrity sha512-uOZ0v2sZp/0Y3nhACPMs72+c6ZEZag9MT3jGlAt4hUIMChLmxbYXGLMZaevXdC1+wWBpr842hO+iDPR3BiVklg== + dependencies: + "@babel/runtime" "^7.3.1" + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/schema" "4.0.2" -"@loaders.gl/schema@3.4.4", "@loaders.gl/schema@^3.4.2": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/schema/-/schema-3.4.4.tgz#551c74a03a6d0f82913125bf2aca3254de67e0cb" - integrity sha512-+lESS+cUSgXst9kxaW2LTxWMVMrT96cv0TWfsSryA11EVsxr50aSPWC+K0BHe7k60+80pQWEt4iyMRgVHM+6tg== +"@loaders.gl/schema@3.4.14", "@loaders.gl/schema@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/schema/-/schema-3.4.14.tgz#6f145065a2abaf402aa419cfa25ec7f1fdeed487" + integrity sha512-r6BEDfUvbvzgUnh/MtkR5RzrkIwo1x1jtPFRTSJVsIZO7arXXlu3blffuv5ppEkKpNZ1Xzd9WtHp/JIkuctsmw== dependencies: "@types/geojson" "^7946.0.7" -"@loaders.gl/terrain@^3.4.2": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/terrain/-/terrain-3.4.4.tgz#4af56000d7b0722a421282621ad3c625fc7142c4" - integrity sha512-IXX9uBlhRaehKMkFBmIclbexygTkRtDXTGg1r5p+SOITTnt1QYCM2J2q49Fntpi19reyVl9n+DzA81Pb8YeNLg== +"@loaders.gl/schema@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/schema/-/schema-4.0.2.tgz#700dcb4166e406a478ef5d7ed05319378453e908" + integrity sha512-R7SI7u4WNrTVbKpOMtpCVfCB/s5OjVBjvO/NtF8IItR4cWMVaGXS5TjYXa0BheLEoJcsIIPcY9FLtMnOXCeGLg== + dependencies: + "@types/geojson" "^7946.0.7" + +"@loaders.gl/terrain@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/terrain/-/terrain-3.4.14.tgz#8cd469b356b94c0d31ffc987c9166ccd33fe1d86" + integrity sha512-vhchEVkPaWXnqd2ofujG2AEnBsk4hEw6LWSaFY7E3VMzNhI9l2EHvyU3+Hs03jYbXM4oLlQPGqd/T7x+5IMtig== dependencies: "@babel/runtime" "^7.3.1" - "@loaders.gl/images" "3.4.4" - "@loaders.gl/loader-utils" "3.4.4" - "@loaders.gl/schema" "3.4.4" + "@loaders.gl/images" "3.4.14" + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/schema" "3.4.14" "@mapbox/martini" "^0.2.0" -"@loaders.gl/textures@3.4.4": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/textures/-/textures-3.4.4.tgz#9d5199ad49d4c2e30f21b170a717844dd143ffc0" - integrity sha512-CD1CPKvXJy3TzzCq42xpwrpYdjimJ7bKf5GSwDs2+qx/fZDnmJBk9z/762VzXyUwTpgGFk3XzbEwkP6H4vkESg== +"@loaders.gl/textures@3.4.14": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/textures/-/textures-3.4.14.tgz#a229ff70592b7a90af96fdd361cda48b9c95bec9" + integrity sha512-iKDHL2ZlOUud4/e3g0p0SyvkukznopYy6La3O6I9vDfKp8peuKMRRcTfFfd/zH0OqQC0hIhCXNz46vRLu7h6ng== + dependencies: + "@loaders.gl/images" "3.4.14" + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/schema" "3.4.14" + "@loaders.gl/worker-utils" "3.4.14" + ktx-parse "^0.0.4" + texture-compressor "^1.0.2" + +"@loaders.gl/textures@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/textures/-/textures-4.0.2.tgz#577402857c994812657a982552f6d37705d0e1a7" + integrity sha512-LLf0oWykS/15KglT7Y9MahjcP0O+ySiZAKcOofty8MWzLzBHDds4CfaarwppvaTp/KFh/GPG8PwVmZ+MyBDZbw== dependencies: - "@loaders.gl/images" "3.4.4" - "@loaders.gl/loader-utils" "3.4.4" - "@loaders.gl/schema" "3.4.4" - "@loaders.gl/worker-utils" "3.4.4" + "@loaders.gl/images" "4.0.2" + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/schema" "4.0.2" + "@loaders.gl/worker-utils" "4.0.2" ktx-parse "^0.0.4" texture-compressor "^1.0.2" -"@loaders.gl/tiles@3.4.4", "@loaders.gl/tiles@^3.4.2": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/tiles/-/tiles-3.4.4.tgz#8e0597a7cda501e4a1a6a35d6434f27407a487d3" - integrity sha512-Z2doHX4+9RTDpQZJ2EHqxcwXxvqWkJoD8i4wh/DvSZgp9Ccot4L7wb907gHyYvDZ3lzQc0mx7LVcC6BBNvKS8w== +"@loaders.gl/tiles@3.4.14", "@loaders.gl/tiles@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/tiles/-/tiles-3.4.14.tgz#8513426ae4965a9c6200f9d61902ce7af4f276cf" + integrity sha512-an3scxl65r74LW4WoIGgluBmQpMY9eb381y9mZmREphTP6bWEj96fL/tiR+G6TiE6HJqTv8O3PH6xwI9OQmEJg== dependencies: - "@loaders.gl/loader-utils" "3.4.4" - "@loaders.gl/math" "3.4.4" + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/math" "3.4.14" "@math.gl/core" "^3.5.1" "@math.gl/culling" "^3.5.1" "@math.gl/geospatial" "^3.5.1" "@math.gl/web-mercator" "^3.5.1" "@probe.gl/stats" "^4.0.1" -"@loaders.gl/wms@^3.4.2": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/wms/-/wms-3.4.4.tgz#447124ee599d3c45ca668f3aa36776f449b47108" - integrity sha512-CVYldmVQq9rADw6ex5rLc0mrhqvq5LogUGQHAdRgU2HfpdLEVYjHOJuPzmZfDVQHMNw6a9QQBo54thjSQn5BbA== +"@loaders.gl/tiles@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/tiles/-/tiles-4.0.2.tgz#2e42701be4f0f3536464607e30388f4feb98debc" + integrity sha512-F2EvmqyzWlHfeB05RP33777Hxz6RYH8Hmc78zD6TBiilpSVexhEnPGV5Lu7MfApmMk77Y9SahKC7Lka0Ep3qTQ== + dependencies: + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/math" "4.0.2" + "@math.gl/core" "^4.0.0" + "@math.gl/culling" "^4.0.0" + "@math.gl/geospatial" "^4.0.0" + "@math.gl/web-mercator" "^4.0.0" + "@probe.gl/stats" "^4.0.2" + +"@loaders.gl/wkt@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/wkt/-/wkt-4.0.2.tgz#7cdc97c0330f1fe7841c6d4084e4ff547bfab742" + integrity sha512-Rw1qoKvSRSR6ZKWOaf92HXejJNpE4JxjhrBsEFweJtXwUU/MeRxyiRvr7fbPJR8H3jrIDv5duMGqn6Z7H8YofQ== + dependencies: + "@loaders.gl/loader-utils" "4.0.2" + "@loaders.gl/schema" "4.0.2" + +"@loaders.gl/wms@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/wms/-/wms-3.4.14.tgz#1bfde56078409fb41a749c1ef9e22a870fa59e3e" + integrity sha512-D1pObPSUj885zGPyHIb7GtcwpHQNk0T8nK/4EHb0SHLe0y1b4qwqSOswdS9geXT9Q61hyhl/L0zqyTgwjiMStg== dependencies: "@babel/runtime" "^7.3.1" - "@loaders.gl/images" "3.4.4" - "@loaders.gl/loader-utils" "3.4.4" - "@loaders.gl/schema" "3.4.4" - "@loaders.gl/xml" "3.4.4" + "@loaders.gl/images" "3.4.14" + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/schema" "3.4.14" + "@loaders.gl/xml" "3.4.14" "@turf/rewind" "^5.1.5" deep-strict-equal "^0.2.0" lerc "^4.0.1" -"@loaders.gl/worker-utils@3.4.4": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/worker-utils/-/worker-utils-3.4.4.tgz#4447814e4746aa206771307429fd1174e720131e" - integrity sha512-ltqMd+BsAk3QGPLycZODukL1wNyBEb04X6wpI3rC5NWByzwSippwWTW4g4QnS3Q9zgMFV4jR/YV6CRp/GiVzvQ== +"@loaders.gl/worker-utils@3.4.14": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/worker-utils/-/worker-utils-3.4.14.tgz#5391a416a3d60e03b9edcedb285af44312d40d2e" + integrity sha512-PUSwxoAYbskisXd0KfYEQ902b0igBA2UAWdP6PzPvY+tJmobfh74dTNwrrBQ1rGXQxxmGx6zc6/ksX6mlIzIrg== + dependencies: + "@babel/runtime" "^7.3.1" + +"@loaders.gl/worker-utils@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@loaders.gl/worker-utils/-/worker-utils-4.0.2.tgz#9808d8003d4b4eed6f08bbf83d422b39641967c3" + integrity sha512-ZEbOSA3ryX2julRKA7nzPAqOWUc8RV/u/ysIIdHYuASck0N+VRljowZIzLAZ70hK4LWn5eIHvmoCDauznEoxeA== dependencies: "@babel/runtime" "^7.3.1" -"@loaders.gl/xml@3.4.4": - version "3.4.4" - resolved "https://registry.yarnpkg.com/@loaders.gl/xml/-/xml-3.4.4.tgz#e15b3bc25c2f60776ab2557752cb0981b98725ec" - integrity sha512-y8idCKtyjVsIIpDDxZ8K53nLNJpzEffBadPNWfuhWWXCggOuQZhrnBWKnNrBu9GeO1ShYwrN8ea7GKdALl4fhA== +"@loaders.gl/xml@3.4.14": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/xml/-/xml-3.4.14.tgz#8bfdbed0440cabdc891f152c80b05128cb019d24" + integrity sha512-SNMGOHz4p8Cw+M6kxXhFEjXdNddJPOZY1rzNmRq7NYdGQlQYYeJdqV5HWzHx9BkoQYyrDXkrweGN0mY9QxCfeA== dependencies: "@babel/runtime" "^7.3.1" - "@loaders.gl/loader-utils" "3.4.4" - "@loaders.gl/schema" "3.4.4" - fast-xml-parser "^4.1.3" + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/schema" "3.4.14" + fast-xml-parser "^4.2.5" -"@luma.gl/constants@8.5.19", "@luma.gl/constants@^8.5.4": +"@luma.gl/constants@8.5.19": version "8.5.19" resolved "https://registry.yarnpkg.com/@luma.gl/constants/-/constants-8.5.19.tgz#eac22b36017770058a31aab2c1cb4afe4f7db8cd" integrity sha512-TNbONy1CQXCZ5+VOAiLh6G9wvvSwMgZxJJtbubhCgkHeR7Up+iTql6gaOF5qIX0SuQbltp7jvB5U5uEml2zUKg== -"@luma.gl/constants@8.5.20", "@luma.gl/constants@^8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/constants/-/constants-8.5.20.tgz#91de116f68110fb28a000b59747d34d54bc06ab2" - integrity sha512-5yG+ybkUZ4j6kLPWMZjN4Hun2yLB0MyEpNCRKAUN9/yS9UIWA7unyVxjSf2vnE7k/7dywtxlbXegASNFgNVGxw== +"@luma.gl/constants@8.5.21", "@luma.gl/constants@^8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/constants/-/constants-8.5.21.tgz#81825e9bd9bdf4a9449bcface8b504389f65f634" + integrity sha512-aJxayGxTT+IRd1vfpcgD/cKSCiVJjBNiuiChS96VulrmCvkzUOLvYXr42y5qKB4RyR7vOIda5uQprNzoHrhQAA== -"@luma.gl/core@^8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/core/-/core-8.5.20.tgz#8b6cea7b5d7230e8b2848c310fc092af2c652571" - integrity sha512-xJr96G6vhYcznYHC84fbeOG3fgNM4lFwj9bd0VPcg/Kfe8otUeN1Hl0AKHCCtNn48PiMSg3LKbaiRfNUMhaffQ== +"@luma.gl/core@^8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/core/-/core-8.5.21.tgz#dc630f1ea18900287ac8da60724d0d8f783b31b1" + integrity sha512-11jQJQEMoR/IN2oIsd4zFxiQJk6FE+xgVIMUcsCTBuzafTtQZ8Po9df8mt+MVewpDyBlTVs6g8nxHRH4np1ukA== dependencies: "@babel/runtime" "^7.0.0" - "@luma.gl/constants" "8.5.20" - "@luma.gl/engine" "8.5.20" - "@luma.gl/gltools" "8.5.20" - "@luma.gl/shadertools" "8.5.20" - "@luma.gl/webgl" "8.5.20" + "@luma.gl/constants" "8.5.21" + "@luma.gl/engine" "8.5.21" + "@luma.gl/gltools" "8.5.21" + "@luma.gl/shadertools" "8.5.21" + "@luma.gl/webgl" "8.5.21" -"@luma.gl/engine@8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/engine/-/engine-8.5.20.tgz#5ef3fa7b69a3bcfeda5991ed9f1d75445cbfbf13" - integrity sha512-+0ryJ/4gL1pWaEgZimY21jUPt1LYiO6Cqte8TNUprCfAHoAStsuzD7jwgEqnM6jJOUEdIxQ3w0z3Dzw/0KIE+w== +"@luma.gl/engine@8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/engine/-/engine-8.5.21.tgz#bc8e55371fb95e33fec195c08abf35598c55da42" + integrity sha512-IG3WQSKXFNUEs8QG7ZjHtGiOtsakUu+BAxtJ6997A6/F06yynZ44tPe5NU70jG9Yfu3kV0LykPZg7hO3vXZDiA== dependencies: "@babel/runtime" "^7.0.0" - "@luma.gl/constants" "8.5.20" - "@luma.gl/gltools" "8.5.20" - "@luma.gl/shadertools" "8.5.20" - "@luma.gl/webgl" "8.5.20" + "@luma.gl/constants" "8.5.21" + "@luma.gl/gltools" "8.5.21" + "@luma.gl/shadertools" "8.5.21" + "@luma.gl/webgl" "8.5.21" "@math.gl/core" "^3.5.0" "@probe.gl/env" "^3.5.0" "@probe.gl/stats" "^3.5.0" @@ -2208,42 +2407,42 @@ "@math.gl/core" "^3.5.0" earcut "^2.0.6" -"@luma.gl/experimental@^8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/experimental/-/experimental-8.5.20.tgz#1bb4c458eaadc81f357aead7b7d3067f7c665c94" - integrity sha512-V1Jp68rYMPtwMdf+50r3NSYsGV3srjwZ+lcK2ew4DshjedDbYwLqTGMWcOyBhY3K3aCl2LH3Fhn0hAY+3NTLGA== +"@luma.gl/experimental@^8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/experimental/-/experimental-8.5.21.tgz#1ab5ad084202ae3c05e16b7e4b430c791f86a50d" + integrity sha512-uFKPChGofyihOKxtqJy78QCQCDFnuMTK4QHrUX/qiTnvFSO8BgtTUevKvWGN9lBvq+uDD0lSieeF9yBzhQfAzw== dependencies: - "@luma.gl/constants" "8.5.20" + "@luma.gl/constants" "8.5.21" "@math.gl/core" "^3.5.0" earcut "^2.0.6" -"@luma.gl/gltools@8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/gltools/-/gltools-8.5.20.tgz#eb35b44b185a83e2e46f1a4f61b51661b95aaf32" - integrity sha512-5pP6ph9FSX5gHiVWQM1DmYRUnriklzKUG9yaqlQsKEqCFsOcKB0EfK3MfBVXIfsOdP/1bJZ9Dlz/zV19soWVhg== +"@luma.gl/gltools@8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/gltools/-/gltools-8.5.21.tgz#1077305a30712f20cd904c2e4cbe5b9263b7d138" + integrity sha512-6qZ0LaT2Mxa4AJT5F44TFoaziokYiHUwO45vnM/NYUOIu9xevcmS6VtToawytMEACGL6PDeDyVqP3Y80SDzq5g== dependencies: "@babel/runtime" "^7.0.0" - "@luma.gl/constants" "8.5.20" + "@luma.gl/constants" "8.5.21" "@probe.gl/env" "^3.5.0" "@probe.gl/log" "^3.5.0" "@types/offscreencanvas" "^2019.7.0" -"@luma.gl/shadertools@8.5.20", "@luma.gl/shadertools@^8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/shadertools/-/shadertools-8.5.20.tgz#ee09f3880acdd1599619dead3ca5fbe4116bb8b6" - integrity sha512-q1lrCZy1ncIFb4mMjsYgISLzNP6eMnhLUY+Oltj/qjAMcPEssCeHN2+XGfP/CVtU+O7sC+5JY2bQGaTs6HQ/Qw== +"@luma.gl/shadertools@8.5.21", "@luma.gl/shadertools@^8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/shadertools/-/shadertools-8.5.21.tgz#9a8e087e39e34f055f9fdda9fac527c04f637b4e" + integrity sha512-WQah7yFDJ8cNCLPYpIm3r0wSlXLvjoA279fcknmATvvkW3/i8PcCJ/nYEBJO3hHEwwMQxD16+YZu/uwGiifLMg== dependencies: "@babel/runtime" "^7.0.0" "@math.gl/core" "^3.5.0" -"@luma.gl/webgl@8.5.20", "@luma.gl/webgl@^8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/webgl/-/webgl-8.5.20.tgz#7eef0d695a2a62d0bbe57227c239930a4acf179f" - integrity sha512-p/kt9KztywH4l+09XHoZ4cPFOoE7xlZXIBMT8rxRVgfe1w0lvi7QYh4tOG7gk+iixQ34EyDQacoHCsabdpmqQg== +"@luma.gl/webgl@8.5.21", "@luma.gl/webgl@^8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/webgl/-/webgl-8.5.21.tgz#fe67bf19a41231840ca677ae702969c7a9a5d7a0" + integrity sha512-ZVLO4W5UuaOlzZIwmFWhnmZ1gYoU97a+heMqxLrSSmCUAsSu3ZETUex9gOmzdM1WWxcdWaa3M68rvKCNEgwz0Q== dependencies: "@babel/runtime" "^7.0.0" - "@luma.gl/constants" "8.5.20" - "@luma.gl/gltools" "8.5.20" + "@luma.gl/constants" "8.5.21" + "@luma.gl/gltools" "8.5.21" "@probe.gl/env" "^3.5.0" "@probe.gl/stats" "^3.5.0" @@ -2388,6 +2587,14 @@ "@math.gl/types" "3.6.3" gl-matrix "^3.4.0" +"@math.gl/core@4.0.0", "@math.gl/core@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/core/-/core-4.0.0.tgz#db64af16743ee4def7c1e294b3d1247621d2dabc" + integrity sha512-qGbP4R8G0dsh5OUO+eWKX5NJwZitkV8CdVEolRFSoPteE0lrWxsg01FwAjegKv4jCm975VJ4HxDcb4L6KAiGGw== + dependencies: + "@babel/runtime" "^7.12.0" + "@math.gl/types" "4.0.0" + "@math.gl/culling@^3.5.1", "@math.gl/culling@^3.6.2": version "3.6.3" resolved "https://registry.yarnpkg.com/@math.gl/culling/-/culling-3.6.3.tgz#91cdfa496748e8873a2e6261415a27a27b6af5f2" @@ -2397,6 +2604,14 @@ "@math.gl/core" "3.6.3" gl-matrix "^3.4.0" +"@math.gl/culling@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/culling/-/culling-4.0.0.tgz#5bb96ec38645944088176911a6f4aab43b265cb9" + integrity sha512-8+btkQZtirG64kGbWs/UBoLnXPLw83D1g0sKgZQG32bQLm2dnArMynfqkPs/Mkj5Cm3MvwkTSdz6vN4a3FF6UA== + dependencies: + "@babel/runtime" "^7.12.0" + "@math.gl/core" "4.0.0" + "@math.gl/geospatial@^3.5.1": version "3.6.3" resolved "https://registry.yarnpkg.com/@math.gl/geospatial/-/geospatial-3.6.3.tgz#011ebbe8d1660ff79020a81ed218886c353a62e1" @@ -2406,6 +2621,14 @@ "@math.gl/core" "3.6.3" gl-matrix "^3.4.0" +"@math.gl/geospatial@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/geospatial/-/geospatial-4.0.0.tgz#fdaf8546dc6e2459134a0b2e2bc20a0cc6baac9d" + integrity sha512-bfUqDbu9ZftmiMERMkM1b1N01RVrFUT0d6VuiMRER0d8R5GrWuRccZxROPoS52lyo692nJa0Z4Or97WJxLUYYw== + dependencies: + "@babel/runtime" "^7.12.0" + "@math.gl/core" "4.0.0" + "@math.gl/polygon@^3.5.1", "@math.gl/polygon@^3.6.2": version "3.6.3" resolved "https://registry.yarnpkg.com/@math.gl/polygon/-/polygon-3.6.3.tgz#0c19c0b059cedde1cd760cc3796e9180f75bcbde" @@ -2413,6 +2636,23 @@ dependencies: "@math.gl/core" "3.6.3" +"@math.gl/polygon@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/polygon/-/polygon-4.0.0.tgz#17d2b1c7569d5a7fd1cde67e885d77e5742c23ec" + integrity sha512-BsseetloYtSZkphH5Fqn02uCL9UWsD26DNLfGhvd2farhU9BaJnn0JGuZnRWT/rf+glZZcDJkyqHq5pDnSX/BQ== + dependencies: + "@math.gl/core" "4.0.0" + +"@math.gl/proj4@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/proj4/-/proj4-4.0.0.tgz#5073be8eedfb61decca9cb20d07116bee37748f4" + integrity sha512-gMVTlhzb1Mbq1F32jDph2nJivkipcGNU+Nx+jovFT9shplXwXeEdZxObjngYCtQiZEM3HqFFTbTnA1YhhZsG7A== + dependencies: + "@babel/runtime" "^7.12.0" + "@math.gl/core" "4.0.0" + "@types/proj4" "^2.5.0" + proj4 "2.6.2" + "@math.gl/sun@^3.6.2": version "3.6.3" resolved "https://registry.yarnpkg.com/@math.gl/sun/-/sun-3.6.3.tgz#30c15612313b56349c568f21f39c0e0f0e77b2df" @@ -2425,6 +2665,11 @@ resolved "https://registry.yarnpkg.com/@math.gl/types/-/types-3.6.3.tgz#9fa9866feabcbb76de107d78ff3a89c0243ac374" integrity sha512-3uWLVXHY3jQxsXCr/UCNPSc2BG0hNUljhmOBt9l+lNFDp7zHgm0cK2Tw4kj2XfkJy4TgwZTBGwRDQgWEbLbdTA== +"@math.gl/types@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/types/-/types-4.0.0.tgz#20c649dcef8459d9dd1f83a708d7410fe06a3309" + integrity sha512-ZqU7o0LFaWQK/0wYobCwQKrKhRHaihps8oE74CLnWAdTTjXkM2vA8dU7vdx238QfXkNkz4Mv+KYklHpXMQJ8Hw== + "@math.gl/web-mercator@^3.5.1", "@math.gl/web-mercator@^3.6.2": version "3.6.3" resolved "https://registry.yarnpkg.com/@math.gl/web-mercator/-/web-mercator-3.6.3.tgz#ef91168e030eecffc788618d686e8a6c1d7a0bf8" @@ -2433,6 +2678,13 @@ "@babel/runtime" "^7.12.0" gl-matrix "^3.4.0" +"@math.gl/web-mercator@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/web-mercator/-/web-mercator-4.0.0.tgz#2df70ec478b464337991b9fcdc3acaacf41311cc" + integrity sha512-dtzjaJPckyAEsCT0eHayPoZ8RrHg7XkQq9fZAHAn8CPiyLX0J0ZdvpH1x4a3qe7Ct7CPo6ChnqSk0DwItA4aNQ== + dependencies: + "@babel/runtime" "^7.12.0" + "@mdx-js/mdx@^1.6.22": version "1.6.22" resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" @@ -3958,6 +4210,13 @@ dependencies: "@types/node" "*" +"@types/brotli@^1.3.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/brotli/-/brotli-1.3.1.tgz#65dc6c69bb9f4159677032f60e81ffc09faf1fce" + integrity sha512-mGwX0BBQqmpHoX8+b8Oez0X+ZEYnl2gbDL2n0HxYT4imqhTChhj1AAgAKVWNZSuPvXGZXqVoOtBS0071tN6Tkw== + dependencies: + "@types/node" "*" + "@types/color-convert@*": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-2.0.0.tgz#8f5ee6b9e863dcbee5703f5a517ffb13d3ea4e22" @@ -3977,16 +4236,6 @@ dependencies: "@types/color-convert" "*" -"@types/command-line-args@5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@types/command-line-args/-/command-line-args-5.2.0.tgz#adbb77980a1cc376bb208e3f4142e907410430f6" - integrity sha512-UuKzKpJJ/Ief6ufIaIzr3A/0XnluX7RvFgwkV89Yzvm77wCh1kFaFmqN8XEnGcN62EuHdedQjEMb8mYxFLGPyA== - -"@types/command-line-usage@5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@types/command-line-usage/-/command-line-usage-5.0.2.tgz#ba5e3f6ae5a2009d466679cc431b50635bf1a064" - integrity sha512-n7RlEEJ+4x4TS7ZQddTmNSxP+zziEG0TNsMfiRIxcIVXt71ENJ9ojeXmGO3wPoTdn7pJcU2xc3CJYMktNT6DPg== - "@types/connect-history-api-fallback@^1.3.5": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" @@ -4002,6 +4251,16 @@ dependencies: "@types/node" "*" +"@types/crypto-js@^4.0.2": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.1.3.tgz#7f2fa22857ae2b5d3221edcba9644f67f8ea984c" + integrity sha512-YP1sYYayLe7Eg5oXyLLvOLfxBfZ5Fgpz6sVWkpB18wDMywCLPWmqzRz+9gyuOoLF0fzDTTFwlyNbx7koONUwqA== + +"@types/emscripten@*": + version "1.39.9" + resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.39.9.tgz#cbe73a8d153fc714a2e3177fbda2d7332d45efa7" + integrity sha512-ILdWj4XYtNOqxJaW22NEQx2gJsLfV5ncxYhhGX1a1H1lXl2Ta0gUz7QOnOoF1xQbJwWDjImi8gXN9mKdIf6n9g== + "@types/eslint-scope@^3.7.3": version "3.7.4" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" @@ -4042,11 +4301,6 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/flatbuffers@*": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@types/flatbuffers/-/flatbuffers-1.10.0.tgz#aa74e30ffdc86445f2f060e1808fc9d56b5603ba" - integrity sha512-7btbphLrKvo5yl/5CC2OCxUSMx1wV1wvGT1qDXkSt7yi00/YW7E8k6qzXqJHsp+WU0eoG7r6MTQQXI9lIvd0qA== - "@types/geojson@*", "@types/geojson@^7946.0.10", "@types/geojson@^7946.0.7", "@types/geojson@^7946.0.8": version "7946.0.10" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" @@ -4148,7 +4402,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.12.tgz#833756634e78c829e1254db006468dadbb0c696b" integrity sha512-Wha1UwsB3CYdqUm2PPzh/1gujGCNtWVUYF0mB00fJFoR4gTyWTDPjSm+zBF787Ahw8vSGgBja90MkgFwvB86Dg== -"@types/node@^17.0.36", "@types/node@^17.0.5": +"@types/node@^17.0.5": version "17.0.45" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== @@ -4158,10 +4412,10 @@ resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz#e4a932069db47bb3eabeb0b305502d01586fa90d" integrity sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg== -"@types/pad-left@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/pad-left/-/pad-left-2.1.1.tgz#17d906fc75804e1cc722da73623f1d978f16a137" - integrity sha512-Xd22WCRBydkGSApl5Bw0PhAOHKSVjNL3E3AwzKaps96IMraPqy5BvZIsBVK6JLwdybUzjHnuWVwpDd0JjTfHXA== +"@types/pako@^1.0.1": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/pako/-/pako-1.0.4.tgz#b4262aef92680a9331fcdb8420c69cf3dd98d3f3" + integrity sha512-Z+5bJSm28EXBSUJEgx29ioWeEEHUh6TiMkZHDhLwjc9wVFH+ressbkmX6waUZc5R3Gobn4Qu5llGxaoflZ+yhA== "@types/parse-json@^4.0.0": version "4.0.0" @@ -4178,6 +4432,11 @@ resolved "https://registry.yarnpkg.com/@types/pbf/-/pbf-3.0.2.tgz#8d291ad68b4b8c533e96c174a2e3e6399a59ed61" integrity sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ== +"@types/proj4@^2.5.0": + version "2.5.4" + resolved "https://registry.yarnpkg.com/@types/proj4/-/proj4-2.5.4.tgz#ca6e77fcd05c31238a3c4ecc9eb2de3d1d20613b" + integrity sha512-AAqZQKgOGuEmhlfLgues7anx0vcZg+0xKE8UggfVnWJSKk4xdVm+Rg1q2q7YmqoZS51BJ5jTcCyYQqIGdxa8FQ== + "@types/prop-types@*": version "15.7.5" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" @@ -4281,6 +4540,14 @@ dependencies: "@types/node" "*" +"@types/sql.js@^1.4.5": + version "1.4.7" + resolved "https://registry.yarnpkg.com/@types/sql.js/-/sql.js-1.4.7.tgz#991a07279e0564d114cac72d008e64793cc9ecfd" + integrity sha512-6NsC/6ECtyVdkEAw1jJcY44uFagQtL9dj+RxN8WceGcbON/E53IP/+/7zah2NnfzNzLnjAViiGLccWuAuKotgw== + dependencies: + "@types/emscripten" "*" + "@types/node" "*" + "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" @@ -4592,23 +4859,6 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -apache-arrow@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/apache-arrow/-/apache-arrow-9.0.0.tgz#c2308e5b9723aef75f7a56d9339c2523736a31f6" - integrity sha512-Myt0vtm7eRtg4dYQp1hb2A77jbPhCYZcPeNp1F0u7te3rWQtcDI3EOSLxToFywdLQ1hfPzhzdLfDL0tPQObJjw== - dependencies: - "@types/command-line-args" "5.2.0" - "@types/command-line-usage" "5.0.2" - "@types/flatbuffers" "*" - "@types/node" "^17.0.36" - "@types/pad-left" "2.1.1" - command-line-args "5.2.1" - command-line-usage "6.1.3" - flatbuffers "2.0.4" - json-bignum "^0.0.3" - pad-left "^2.1.0" - tslib "^2.4.0" - arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -4631,16 +4881,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -array-back@^3.0.1, array-back@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" - integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== - -array-back@^4.0.1, array-back@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" - integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -4661,6 +4901,27 @@ asap@~2.0.3: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +assert@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" + integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== + dependencies: + call-bind "^1.0.2" + is-nan "^1.3.2" + object-is "^1.1.5" + object.assign "^4.1.4" + util "^0.12.5" + at-least-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" @@ -4678,6 +4939,11 @@ autoprefixer@^10.4.12, autoprefixer@^10.4.7: picocolors "^1.0.0" postcss-value-parser "^4.2.0" +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + axios@^0.25.0: version "0.25.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" @@ -4772,6 +5038,11 @@ base16@^1.0.0: resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70" integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ== +base64-js@^1.1.2, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -4787,6 +5058,16 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + body-parser@1.20.1: version "1.20.1" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" @@ -4863,6 +5144,79 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +brotli@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/brotli/-/brotli-1.3.3.tgz#7365d8cc00f12cf765d2b2c898716bcf4b604d48" + integrity sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg== + dependencies: + base64-js "^1.1.2" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.2.tgz#e78d4b69816d6e3dd1c747e64e9947f9ad79bc7e" + integrity sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg== + dependencies: + bn.js "^5.2.1" + browserify-rsa "^4.1.0" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.4" + inherits "^2.0.4" + parse-asn1 "^5.1.6" + readable-stream "^3.6.2" + safe-buffer "^5.2.1" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" @@ -4883,6 +5237,24 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -4914,6 +5286,15 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-bind@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -4967,7 +5348,7 @@ ccount@^1.0.0: resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== -chalk@^2.0.0, chalk@^2.4.2: +chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -5054,6 +5435,14 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + classnames@^2.2.5: version "2.3.2" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" @@ -5176,26 +5565,6 @@ comma-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== -command-line-args@5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" - integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== - dependencies: - array-back "^3.1.0" - find-replace "^3.0.0" - lodash.camelcase "^4.3.0" - typical "^4.0.0" - -command-line-usage@6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.3.tgz#428fa5acde6a838779dfa30e44686f4b6761d957" - integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== - dependencies: - array-back "^4.0.2" - chalk "^2.4.2" - table-layout "^1.0.2" - typical "^5.2.0" - commander@2, commander@^2.20.0, commander@^2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -5278,6 +5647,16 @@ consola@^2.15.3: resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== +console-browserify@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== + content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" @@ -5394,6 +5773,37 @@ cosmiconfig@^8.1.3: parse-json "^5.0.0" path-type "^4.0.0" +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -5415,6 +5825,23 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-browserify@^3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -5751,7 +6178,7 @@ deep-equal@1.x, deep-equal@^1.0.0: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-extend@^0.6.0, deep-extend@~0.6.0: +deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== @@ -5780,6 +6207,15 @@ defer-to-connect@^1.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -5822,6 +6258,14 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== +des.js@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" + integrity sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" @@ -5860,6 +6304,15 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -5879,6 +6332,15 @@ dns-packet@^5.2.2: dependencies: "@leichtgewicht/ip-codec" "^2.0.1" +docusaurus-node-polyfills@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/docusaurus-node-polyfills/-/docusaurus-node-polyfills-1.0.0.tgz#b19c1bda29978e149c17fcf0707d6d02fad9c0a0" + integrity sha512-TUX/smcS0NcoiBKThM3hNlAx7Z8jJr/F5UKR+FiMlsJJbt1KYWgbj3blgUTk/ad0+hfe2vaytJZX4r0GeK6oRQ== + dependencies: + node-polyfill-webpack-plugin "^1.1.2" + os-browserify "^0.3.0" + process "^0.11.10" + dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -5912,6 +6374,11 @@ dom-serializer@^2.0.0: domhandler "^5.0.2" entities "^4.2.0" +domain-browser@^4.19.0: + version "4.23.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.23.0.tgz#427ebb91efcb070f05cffdfb8a4e9a6c25f8c94b" + integrity sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA== + domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" @@ -5999,6 +6466,19 @@ electron-to-chromium@^1.4.284: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz#75901f97d3e23da2e66feb1e61fbb8e70ac96430" integrity sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw== +elliptic@^6.5.3, elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -6149,11 +6629,19 @@ eventemitter3@^4.0.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@^3.2.0: +events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -6251,10 +6739,10 @@ fast-url-parser@1.1.3: dependencies: punycode "^1.3.2" -fast-xml-parser@^4.1.3: - version "4.2.5" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz#a6747a09296a6cb34f2ae634019bf1738f3b421f" - integrity sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g== +fast-xml-parser@^4.2.5: + version "4.2.7" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.7.tgz#871f2ca299dc4334b29f8da3658c164e68395167" + integrity sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig== dependencies: strnum "^1.0.5" @@ -6304,6 +6792,11 @@ feed@^4.2.2: dependencies: xml-js "^1.6.11" +fflate@0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.4.tgz#61587e5d958fdabb5a9368a302c25363f4f69f50" + integrity sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw== + file-loader@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" @@ -6324,6 +6817,11 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +filter-obj@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-2.0.2.tgz#fff662368e505d69826abb113f0f6a98f56e9d5f" + integrity sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg== + finalhandler@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" @@ -6346,13 +6844,6 @@ find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-replace@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" - integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== - dependencies: - array-back "^3.0.1" - find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -6376,11 +6867,6 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -flatbuffers@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/flatbuffers/-/flatbuffers-2.0.4.tgz#034456e29ec480de48bad34f7fc18c03f20c9768" - integrity sha512-4rUFVDPjSoP0tOII34oQf+72NKU7E088U5oX7kwICahft0UB2kOQ9wUzzCp+OHxByERIfxRDCgX5mP8Pjkfl0g== - flux@^4.0.1: version "4.0.4" resolved "https://registry.yarnpkg.com/flux/-/flux-4.0.4.tgz#9661182ea81d161ee1a6a6af10d20485ef2ac572" @@ -6401,6 +6887,13 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.7: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + fork-ts-checker-webpack-plugin@^6.5.0: version "6.5.3" resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz#eda2eff6e22476a2688d10661688c47f611b37f3" @@ -6469,6 +6962,11 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fs@^0.0.1-security: + version "0.0.1-security" + resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4" + integrity sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w== + fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" @@ -6479,6 +6977,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -6521,6 +7024,16 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.3" +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -6637,6 +7150,13 @@ globby@^13.1.1: merge2 "^1.4.1" slash "^4.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -6708,6 +7228,11 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" @@ -6732,6 +7257,30 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + hast-to-hyperscript@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d" @@ -6817,6 +7366,15 @@ history@^4.9.0: tiny-warning "^1.0.0" value-equal "^1.0.1" +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -6954,6 +7512,11 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -6988,7 +7551,7 @@ icss-utils@^5.0.0, icss-utils@^5.1.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -ieee754@^1.1.12: +ieee754@^1.1.12, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -7051,7 +7614,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -7146,6 +7709,11 @@ is-buffer@^2.0.0: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -7197,6 +7765,13 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -7222,6 +7797,14 @@ is-installed-globally@^0.4.0: global-dirs "^3.0.0" is-path-inside "^3.0.2" +is-nan@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + is-npm@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" @@ -7292,6 +7875,13 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-typed-array@^1.1.3: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -7411,11 +8001,6 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== -json-bignum@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/json-bignum/-/json-bignum-0.0.3.tgz#41163b50436c773d82424dbc20ed70db7604b8d7" - integrity sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg== - json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" @@ -7574,6 +8159,11 @@ launch-editor@^2.6.0: picocolors "^1.0.0" shell-quote "^1.7.3" +laz-perf@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/laz-perf/-/laz-perf-0.0.6.tgz#a8bfad45f7f02695af631ca374d080528da4ba51" + integrity sha512-ZBqC+BBlofznDIY3SfjXDBVdIhYfz7bq8HAHztlw4XOnu++nHiWtCGPgzpdeAhPkByc68DaKNy3E3rY4XrdRtQ== + lerc@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lerc/-/lerc-4.0.1.tgz#a7cc8d4d8e16320d3888fdc743320bdf29c54f2b" @@ -7649,11 +8239,6 @@ lodash-es@4.17.21, lodash-es@^4.17.15: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== - lodash.curry@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" @@ -7742,6 +8327,16 @@ luxon@~3.2.1: resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.2.1.tgz#14f1af209188ad61212578ea7e3d518d18cee45f" integrity sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg== +lz4js@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/lz4js/-/lz4js-0.2.0.tgz#09f1a397cb2158f675146c3351dde85058cb322f" + integrity sha512-gY2Ia9Lm7Ep8qMiuGRhvUq0Q7qUereeldZPP1PMEJxPtEWHJLqw9pgX68oHajBH0nzJK4MaZEA/YNV3jT8u8Bg== + +lzo-wasm@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/lzo-wasm/-/lzo-wasm-0.0.4.tgz#49152521a0b67a1da3a3e113dd38f150f303230a" + integrity sha512-VKlnoJRFrB8SdJhlVKvW5vI1gGwcZ+mvChEXcSX6r2xDNc/Q2FD9esfBmGCuPZdrJ1feO+YcVFd2PTk0c137Gw== + make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -7816,6 +8411,15 @@ math.gl@^3.6.2: dependencies: "@math.gl/core" "3.6.3" +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + mdast-squeeze-paragraphs@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" @@ -7891,6 +8495,11 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== +mgrs@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mgrs/-/mgrs-1.0.0.tgz#fb91588e78c90025672395cb40b25f7cd6ad1829" + integrity sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA== + micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -7899,6 +8508,14 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -7945,11 +8562,16 @@ mini-css-extract-plugin@^2.6.1: dependencies: schema-utils "^4.0.0" -minimalistic-assert@^1.0.0: +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -8045,6 +8667,36 @@ node-forge@^1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== +node-polyfill-webpack-plugin@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-1.1.4.tgz#56bfa4d16e17addb9d6b1ef3d04e790c401f5f1d" + integrity sha512-Z0XTKj1wRWO8o/Vjobsw5iOJCN+Sua3EZEUc2Ziy9CyVvmHKu6o+t4gUH9GOE0czyPR94LI6ZCV/PpcM8b5yow== + dependencies: + assert "^2.0.0" + browserify-zlib "^0.2.0" + buffer "^6.0.3" + console-browserify "^1.2.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.12.0" + domain-browser "^4.19.0" + events "^3.3.0" + filter-obj "^2.0.2" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "^1.0.1" + process "^0.11.10" + punycode "^2.1.1" + querystring-es3 "^0.2.1" + readable-stream "^3.6.0" + stream-browserify "^3.0.0" + stream-http "^3.2.0" + string_decoder "^1.3.0" + timers-browserify "^2.0.12" + tty-browserify "^0.0.1" + url "^0.11.0" + util "^0.12.4" + vm-browserify "^1.1.2" + node-releases@^2.0.8: version "2.0.10" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" @@ -8099,7 +8751,7 @@ object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== -object-is@^1.0.1: +object-is@^1.0.1, object-is@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== @@ -8112,7 +8764,7 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.0: +object.assign@^4.1.0, object.assign@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== @@ -8167,6 +8819,11 @@ opener@^1.5.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -8237,12 +8894,10 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" -pad-left@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pad-left/-/pad-left-2.1.0.tgz#16e6a3b2d44a8e138cb0838cc7cb403a4fc9e994" - integrity sha512-HJxs9K9AztdIQIAIa/OIazRAUW/L6B9hbQDxO4X07roW3eo9XqZc2ur9bn1StH9CnbbI9EgvejHQX7CBpCF1QA== - dependencies: - repeat-string "^1.5.4" +pako@1.0.11, pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== param-case@^3.0.4: version "3.0.4" @@ -8259,6 +8914,17 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-asn1@^5.0.0, parse-asn1@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + parse-entities@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" @@ -8319,6 +8985,11 @@ pascal-case@^3.1.2: no-case "^3.0.4" tslib "^2.0.3" +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -8371,6 +9042,14 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path@^0.12.7: + version "0.12.7" + resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" + integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== + dependencies: + process "^0.11.1" + util "^0.10.3" + pbf@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.2.1.tgz#b4c1b9e72af966cd82c6531691115cc0409ffe2a" @@ -8379,6 +9058,17 @@ pbf@^3.2.1: ieee754 "^1.1.12" resolve-protobuf-schema "^2.1.0" +pbkdf2@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + picocolors@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" @@ -8804,6 +9494,19 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process@^0.11.1, process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +proj4@2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/proj4/-/proj4-2.6.2.tgz#4665d7cbc30fd356375007c2fed53b07dbda1d67" + integrity sha512-Pn0+HZtXb4JzuN8RR0VM7yyseegiYHbXkF+2FOdGpzRojcZ1BTjWxOh7qfp2vH0EyLu8pvcrhLxidwzgyUy/Gw== + dependencies: + mgrs "1.0.0" + wkt-parser "^1.2.4" + promise@^7.1.1: version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" @@ -8848,6 +9551,18 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -8856,7 +9571,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^1.3.2: +punycode@^1.3.2, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== @@ -8866,6 +9581,11 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +punycode@^2.1.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + pupa@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" @@ -8885,6 +9605,18 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@^6.11.2: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + +querystring-es3@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -8907,13 +9639,21 @@ quickselect@^2.0.0: resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018" integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw== -randombytes@^2.1.0: +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + range-parser@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" @@ -9169,7 +9909,7 @@ readable-stream@^2.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6: +readable-stream@^3.0.6, readable-stream@^3.5.0, readable-stream@^3.6.0, readable-stream@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -9204,11 +9944,6 @@ recursive-readdir@^2.2.2: dependencies: minimatch "^3.0.5" -reduce-flatten@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" - integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== - regenerate-unicode-properties@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" @@ -9418,6 +10153,14 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + robust-predicates@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-2.0.4.tgz#0a2367a93abd99676d075981707f29cfb402248b" @@ -9462,12 +10205,12 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -9558,19 +10301,19 @@ semver-diff@^3.1.1: semver "^6.3.0" semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.3.2, semver@^7.3.4, semver@^7.3.7, semver@^7.3.8: - version "7.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" - integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" @@ -9637,7 +10380,17 @@ serve-static@1.15.0: parseurl "~1.3.3" send "0.18.0" -setimmediate@^1.0.5: +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +setimmediate@^1.0.4, setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== @@ -9652,6 +10405,14 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -9750,6 +10511,11 @@ slash@^4.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +snappyjs@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/snappyjs/-/snappyjs-0.6.1.tgz#9bca9ff8c54b133a9cc84a71d22779e97fc51878" + integrity sha512-YIK6I2lsH072UE0aOFxxY1dPDCS43I5ktqHpeAsuLNYWkE5pGxRGWfDM4/vSUfNzXjC1Ivzt3qx31PCLmc9yqg== + sockjs@^0.3.24: version "0.3.24" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" @@ -9830,6 +10596,11 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +sql.js@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/sql.js/-/sql.js-1.8.0.tgz#cb45d957e17a2239662fe2f614c9b678990867a6" + integrity sha512-3HD8pSkZL+5YvYUI8nlvNILs61ALqq34xgmF+BHpqxe68yZIJ1H+sIVIODvni25+CcxHUxDyrTJUL0lE/m7afw== + stable@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" @@ -9860,6 +10631,24 @@ std-env@^3.0.1: resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.2.tgz#af27343b001616015534292178327b202b9ee955" integrity sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA== +stream-browserify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== + dependencies: + inherits "~2.0.4" + readable-stream "^3.5.0" + +stream-http@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5" + integrity sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.4" + readable-stream "^3.6.0" + xtend "^4.0.2" + string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -9878,7 +10667,7 @@ string-width@^5.0.1: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string_decoder@^1.1.1: +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -10035,16 +10824,6 @@ tabbable@^6.0.1: resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.1.1.tgz#40cfead5ed11be49043f04436ef924c8890186a0" integrity sha512-4kl5w+nCB44EVRdO0g/UGoOp3vlwgycUVtkk/7DPyeLZUCuNFFKCFG6/t/DgHLrUPHjrZg6s5tNm+56Q2B0xyg== -table-layout@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" - integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== - dependencies: - array-back "^4.0.1" - deep-extend "~0.6.0" - typical "^5.2.0" - wordwrapjs "^4.0.0" - tapable@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -10094,6 +10873,13 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +timers-browserify@^2.0.12: + version "2.0.12" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" + integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== + dependencies: + setimmediate "^1.0.4" + tiny-invariant@^1.0.2: version "1.3.1" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" @@ -10199,6 +10985,11 @@ tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== +tty-browserify@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" + integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== + turf-jsts@*: version "1.2.3" resolved "https://registry.yarnpkg.com/turf-jsts/-/turf-jsts-1.2.3.tgz#59757f542afbff9a577bbf411f183b8f48d38aa4" @@ -10229,16 +11020,6 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typical@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" - integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== - -typical@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" - integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== - ua-parser-js@^0.7.30: version "0.7.35" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.35.tgz#8bda4827be4f0b1dda91699a29499575a1f1d307" @@ -10425,6 +11206,14 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" +url@^0.11.0: + version "0.11.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.3.tgz#6f495f4b935de40ce4a0a52faee8954244f3d3ad" + integrity sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw== + dependencies: + punycode "^1.4.1" + qs "^6.11.2" + use-composed-ref@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.3.0.tgz#3d8104db34b7b264030a9d916c5e94fbe280dbda" @@ -10452,6 +11241,24 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== + dependencies: + inherits "2.0.3" + +util@^0.12.4, util@^0.12.5: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + utila@~0.4: version "0.4.0" resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" @@ -10510,6 +11317,11 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" +vm-browserify@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + vt-pbf@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac" @@ -10693,6 +11505,17 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +which-typed-array@^1.1.11, which-typed-array@^1.1.2: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.4" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -10726,13 +11549,10 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== -wordwrapjs@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" - integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== - dependencies: - reduce-flatten "^2.0.0" - typical "^5.2.0" +wkt-parser@^1.2.4: + version "1.3.3" + resolved "https://registry.yarnpkg.com/wkt-parser/-/wkt-parser-1.3.3.tgz#46b4e3032dd9c86907f7e630b57e3c6ea2bb772b" + integrity sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw== wrap-ansi@^7.0.0: version "7.0.0" @@ -10802,7 +11622,7 @@ xss@1.0.13: commander "^2.20.3" cssfilter "0.0.10" -xtend@^4.0.0, xtend@^4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -10832,6 +11652,11 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +zstd-codec@^0.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/zstd-codec/-/zstd-codec-0.1.4.tgz#6abb311b63cfacbd06e72797ee6c6e1c7c65248c" + integrity sha512-KYnWoFWgGtWyQEKNnUcb3u8ZtKO8dn5d8u+oGpxPlopqsPyv60U8suDyfk7Z7UtAO6Sk5i1aVcAs9RbaB1n36A== + zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" diff --git a/yarn.lock b/yarn.lock index 1c741c82b5..b98616995e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@75lb/deep-merge@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@75lb/deep-merge/-/deep-merge-1.1.1.tgz#3b06155b90d34f5f8cc2107d796f1853ba02fd6d" + integrity sha512-xvgv6pkMGBA6GwdyJbNAnDmfAIR/DfWhrj9jgWh3TY7gRm3KO46x/GPjRg6wJ0nOepwqrNxFfojebh0Df4h4Tw== + dependencies: + lodash.assignwith "^4.2.0" + typical "^7.1.1" + "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -45,7 +53,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.7.tgz#61caffb60776e49a57ba61a88f02bedd8714f6bc" integrity sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA== -"@babel/core@^7.12.3", "@babel/core@^7.14.5", "@babel/core@^7.7.5": +"@babel/core@^7.12.3", "@babel/core@^7.14.5": version "7.21.8" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== @@ -323,7 +331,7 @@ "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-class-properties@^7.14.5", "@babel/plugin-proposal-class-properties@^7.18.6": +"@babel/plugin-proposal-class-properties@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== @@ -372,7 +380,7 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.14.2", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": +"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== @@ -407,7 +415,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.14.2", "@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0": +"@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== @@ -990,17 +998,6 @@ "@babel/plugin-transform-modules-commonjs" "^7.21.5" "@babel/plugin-transform-typescript" "^7.21.3" -"@babel/register@^7.14.5": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.21.0.tgz#c97bf56c2472e063774f31d344c592ebdcefa132" - integrity sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw== - dependencies: - clone-deep "^4.0.1" - find-cache-dir "^2.0.0" - make-dir "^2.1.0" - pirates "^4.0.5" - source-map-support "^0.5.16" - "@babel/regjsgen@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" @@ -1054,6 +1051,11 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -1062,15 +1064,16 @@ "@jridgewell/trace-mapping" "0.3.9" "@deck.gl/core@^8.9.0": - version "8.9.15" - resolved "https://registry.yarnpkg.com/@deck.gl/core/-/core-8.9.15.tgz#ad25b5890a7bd5fcdb7de779910c33ab12433001" - integrity sha512-9fUnrRBITgaYHTEYgbSXUaK/x/gRwlKTF68YCy0Us3BoCbRqPqHZTXxOENiN7gHzdHmrDu6ArR71LcI4ZEGWoA== - dependencies: - "@loaders.gl/core" "^3.3.5" - "@loaders.gl/images" "^3.3.5" - "@luma.gl/constants" "^8.5.20" - "@luma.gl/core" "^8.5.20" - "@luma.gl/webgl" "^8.5.20" + version "8.9.28" + resolved "https://registry.yarnpkg.com/@deck.gl/core/-/core-8.9.28.tgz#fb857c417fb3ba2fae375b548f43358843a00b96" + integrity sha512-Tpji8XykZAjaDIJwXR9RalulemgF6AGpCq43XIInK+0JdumJYuYVyGObPkujNW7zeWM26rAb3TiqyOLwW/3yOQ== + dependencies: + "@babel/runtime" "^7.0.0" + "@loaders.gl/core" "^3.4.13" + "@loaders.gl/images" "^3.4.13" + "@luma.gl/constants" "^8.5.21" + "@luma.gl/core" "^8.5.21" + "@luma.gl/webgl" "^8.5.21" "@math.gl/core" "^3.6.2" "@math.gl/sun" "^3.6.2" "@math.gl/web-mercator" "^3.6.2" @@ -1081,6 +1084,251 @@ math.gl "^3.6.2" mjolnir.js "^2.7.0" +"@esbuild-plugins/node-globals-polyfill@^0.2.0": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz#0e4497a2b53c9e9485e149bc92ddb228438d6bcf" + integrity sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw== + +"@esbuild-plugins/node-modules-polyfill@^0.2.0": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.2.2.tgz#cefa3dc0bd1c16277a8338b52833420c94987327" + integrity sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA== + dependencies: + escape-string-regexp "^4.0.0" + rollup-plugin-node-polyfills "^0.2.1" + +"@esbuild/android-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" + integrity sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg== + +"@esbuild/android-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" + integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== + +"@esbuild/android-arm@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz#025b6246d3f68b7bbaa97069144fb5fb70f2fff2" + integrity sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw== + +"@esbuild/android-arm@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" + integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== + +"@esbuild/android-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.17.tgz#c820e0fef982f99a85c4b8bfdd582835f04cd96e" + integrity sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ== + +"@esbuild/android-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" + integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== + +"@esbuild/darwin-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz#edef4487af6b21afabba7be5132c26d22379b220" + integrity sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w== + +"@esbuild/darwin-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" + integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== + +"@esbuild/darwin-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz#42829168730071c41ef0d028d8319eea0e2904b4" + integrity sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg== + +"@esbuild/darwin-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" + integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== + +"@esbuild/freebsd-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz#1f4af488bfc7e9ced04207034d398e793b570a27" + integrity sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw== + +"@esbuild/freebsd-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" + integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== + +"@esbuild/freebsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz#636306f19e9bc981e06aa1d777302dad8fddaf72" + integrity sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug== + +"@esbuild/freebsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" + integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== + +"@esbuild/linux-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz#a003f7ff237c501e095d4f3a09e58fc7b25a4aca" + integrity sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g== + +"@esbuild/linux-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" + integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== + +"@esbuild/linux-arm@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz#b591e6a59d9c4fe0eeadd4874b157ab78cf5f196" + integrity sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ== + +"@esbuild/linux-arm@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" + integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== + +"@esbuild/linux-ia32@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz#24333a11027ef46a18f57019450a5188918e2a54" + integrity sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg== + +"@esbuild/linux-ia32@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" + integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== + +"@esbuild/linux-loong64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz#d5ad459d41ed42bbd4d005256b31882ec52227d8" + integrity sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ== + +"@esbuild/linux-loong64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" + integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== + +"@esbuild/linux-mips64el@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz#4e5967a665c38360b0a8205594377d4dcf9c3726" + integrity sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw== + +"@esbuild/linux-mips64el@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" + integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== + +"@esbuild/linux-ppc64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz#206443a02eb568f9fdf0b438fbd47d26e735afc8" + integrity sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g== + +"@esbuild/linux-ppc64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" + integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== + +"@esbuild/linux-riscv64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz#c351e433d009bf256e798ad048152c8d76da2fc9" + integrity sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw== + +"@esbuild/linux-riscv64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" + integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== + +"@esbuild/linux-s390x@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz#661f271e5d59615b84b6801d1c2123ad13d9bd87" + integrity sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w== + +"@esbuild/linux-s390x@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" + integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== + +"@esbuild/linux-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz#e4ba18e8b149a89c982351443a377c723762b85f" + integrity sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw== + +"@esbuild/linux-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" + integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== + +"@esbuild/netbsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz#7d4f4041e30c5c07dd24ffa295c73f06038ec775" + integrity sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA== + +"@esbuild/netbsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" + integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== + +"@esbuild/openbsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz#970fa7f8470681f3e6b1db0cc421a4af8060ec35" + integrity sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg== + +"@esbuild/openbsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" + integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== + +"@esbuild/sunos-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz#abc60e7c4abf8b89fb7a4fe69a1484132238022c" + integrity sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw== + +"@esbuild/sunos-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" + integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== + +"@esbuild/win32-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz#7b0ff9e8c3265537a7a7b1fd9a24e7bd39fcd87a" + integrity sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw== + +"@esbuild/win32-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" + integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== + +"@esbuild/win32-ia32@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz#e90fe5267d71a7b7567afdc403dfd198c292eb09" + integrity sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig== + +"@esbuild/win32-ia32@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" + integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== + +"@esbuild/win32-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz#c5a1a4bfe1b57f0c3e61b29883525c6da3e5c091" + integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q== + +"@esbuild/win32-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" + integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== + +"@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.9.1.tgz#449dfa81a57a1d755b09aa58d826c1262e4283b4" + integrity sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA== + "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" @@ -1195,7 +1443,7 @@ js-yaml "^3.13.1" resolve-from "^5.0.0" -"@istanbuljs/schema@^0.1.2": +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== @@ -1214,7 +1462,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== @@ -1224,12 +1472,20 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/sourcemap-codec@1.4.14": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -1242,6 +1498,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.12": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.18" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" @@ -1935,98 +2199,98 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" -"@loaders.gl/core@^3.3.5": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@loaders.gl/core/-/core-3.4.1.tgz#ccdbd9c057b68d000445be06ce5b5acaf1c9594b" - integrity sha512-bj2h19AVoaRAcwt51wQpzvPI1e9iBUyVbJ3mIb+RrKBDYypzfKGdNS8YgoFltR5ijM5y0UG0NlZVMQYIhzmF5g== +"@loaders.gl/core@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/core/-/core-3.4.14.tgz#79e5c54112f5bfe398da1718dc4fb661ffa213fd" + integrity sha512-5PFcjv7xC8AYL17juDMrvo8n0Fcwg9s8F4BaM2YCNUsb9RCI2SmLuIFJMcx1GgHO5vL0WiTIKO+JT4n1FuNR6w== dependencies: "@babel/runtime" "^7.3.1" - "@loaders.gl/loader-utils" "3.4.1" - "@loaders.gl/worker-utils" "3.4.1" + "@loaders.gl/loader-utils" "3.4.14" + "@loaders.gl/worker-utils" "3.4.14" "@probe.gl/log" "^4.0.1" -"@loaders.gl/images@^3.3.5": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@loaders.gl/images/-/images-3.4.1.tgz#64b120ed3f85f23ad604bebad5ac9370339a3743" - integrity sha512-1GyAEvpXKvOq8QXuG+xtiyOTXSxcue27b3bvXE9nQS9M4mCDlEEkO4Bau+l01LU3ll8JsK8NiZl2PtHSMv1+iQ== +"@loaders.gl/images@^3.4.13": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/images/-/images-3.4.14.tgz#d7a4950f11b48d028cf3719cf6498945f4a05c14" + integrity sha512-tL447hTWhOKBOB87SE4hvlC8OkbRT0mEaW1a/wIS9f4HnYDa/ycRLMV+nvdvYMZur4isNPam44oiRqi7GcILkg== dependencies: - "@loaders.gl/loader-utils" "3.4.1" + "@loaders.gl/loader-utils" "3.4.14" -"@loaders.gl/loader-utils@3.4.1": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-3.4.1.tgz#65ed0d7c5e61c59ade00f24b505c3161e28d9493" - integrity sha512-N7XihvbAeB+XSsFgV9Vj5tslV3M1YbMqpyb0lEhsj1zRQXid7F86teVXLxDw+ZhhDPj/wrRSvFqq3QWF3QhBBQ== +"@loaders.gl/loader-utils@3.4.14": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-3.4.14.tgz#d94decc279fd2304b8762c87d8d9626058d91f21" + integrity sha512-HCTY2/F83RLbZWcTvWLVJ1vke3dl6Bye20HU1AqkA37J2vzHwOZ8kj6eee8eeSkIkf7VIFwjyhVJxe0flQE/Bw== dependencies: "@babel/runtime" "^7.3.1" - "@loaders.gl/worker-utils" "3.4.1" + "@loaders.gl/worker-utils" "3.4.14" "@probe.gl/stats" "^4.0.1" -"@loaders.gl/worker-utils@3.4.1": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@loaders.gl/worker-utils/-/worker-utils-3.4.1.tgz#c87ef376bb64dd09c5fcf2ae0403dfe0f1b2c6e6" - integrity sha512-ezlj+l1JXmhhXn5cDAkwA2nf0Bk2WvOHOYO+q7eqiKz0VfKzGd0pfEeHrncBkzq1sdAeQspgUbNnRyp6vOnnfg== +"@loaders.gl/worker-utils@3.4.14": + version "3.4.14" + resolved "https://registry.yarnpkg.com/@loaders.gl/worker-utils/-/worker-utils-3.4.14.tgz#5391a416a3d60e03b9edcedb285af44312d40d2e" + integrity sha512-PUSwxoAYbskisXd0KfYEQ902b0igBA2UAWdP6PzPvY+tJmobfh74dTNwrrBQ1rGXQxxmGx6zc6/ksX6mlIzIrg== dependencies: "@babel/runtime" "^7.3.1" -"@luma.gl/constants@8.5.20", "@luma.gl/constants@^8.5.20", "@luma.gl/constants@^8.5.4": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/constants/-/constants-8.5.20.tgz#91de116f68110fb28a000b59747d34d54bc06ab2" - integrity sha512-5yG+ybkUZ4j6kLPWMZjN4Hun2yLB0MyEpNCRKAUN9/yS9UIWA7unyVxjSf2vnE7k/7dywtxlbXegASNFgNVGxw== +"@luma.gl/constants@8.5.21", "@luma.gl/constants@^8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/constants/-/constants-8.5.21.tgz#81825e9bd9bdf4a9449bcface8b504389f65f634" + integrity sha512-aJxayGxTT+IRd1vfpcgD/cKSCiVJjBNiuiChS96VulrmCvkzUOLvYXr42y5qKB4RyR7vOIda5uQprNzoHrhQAA== -"@luma.gl/core@8.5.20", "@luma.gl/core@^8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/core/-/core-8.5.20.tgz#8b6cea7b5d7230e8b2848c310fc092af2c652571" - integrity sha512-xJr96G6vhYcznYHC84fbeOG3fgNM4lFwj9bd0VPcg/Kfe8otUeN1Hl0AKHCCtNn48PiMSg3LKbaiRfNUMhaffQ== +"@luma.gl/core@^8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/core/-/core-8.5.21.tgz#dc630f1ea18900287ac8da60724d0d8f783b31b1" + integrity sha512-11jQJQEMoR/IN2oIsd4zFxiQJk6FE+xgVIMUcsCTBuzafTtQZ8Po9df8mt+MVewpDyBlTVs6g8nxHRH4np1ukA== dependencies: "@babel/runtime" "^7.0.0" - "@luma.gl/constants" "8.5.20" - "@luma.gl/engine" "8.5.20" - "@luma.gl/gltools" "8.5.20" - "@luma.gl/shadertools" "8.5.20" - "@luma.gl/webgl" "8.5.20" + "@luma.gl/constants" "8.5.21" + "@luma.gl/engine" "8.5.21" + "@luma.gl/gltools" "8.5.21" + "@luma.gl/shadertools" "8.5.21" + "@luma.gl/webgl" "8.5.21" -"@luma.gl/engine@8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/engine/-/engine-8.5.20.tgz#5ef3fa7b69a3bcfeda5991ed9f1d75445cbfbf13" - integrity sha512-+0ryJ/4gL1pWaEgZimY21jUPt1LYiO6Cqte8TNUprCfAHoAStsuzD7jwgEqnM6jJOUEdIxQ3w0z3Dzw/0KIE+w== +"@luma.gl/engine@8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/engine/-/engine-8.5.21.tgz#bc8e55371fb95e33fec195c08abf35598c55da42" + integrity sha512-IG3WQSKXFNUEs8QG7ZjHtGiOtsakUu+BAxtJ6997A6/F06yynZ44tPe5NU70jG9Yfu3kV0LykPZg7hO3vXZDiA== dependencies: "@babel/runtime" "^7.0.0" - "@luma.gl/constants" "8.5.20" - "@luma.gl/gltools" "8.5.20" - "@luma.gl/shadertools" "8.5.20" - "@luma.gl/webgl" "8.5.20" + "@luma.gl/constants" "8.5.21" + "@luma.gl/gltools" "8.5.21" + "@luma.gl/shadertools" "8.5.21" + "@luma.gl/webgl" "8.5.21" "@math.gl/core" "^3.5.0" "@probe.gl/env" "^3.5.0" "@probe.gl/stats" "^3.5.0" "@types/offscreencanvas" "^2019.7.0" -"@luma.gl/gltools@8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/gltools/-/gltools-8.5.20.tgz#eb35b44b185a83e2e46f1a4f61b51661b95aaf32" - integrity sha512-5pP6ph9FSX5gHiVWQM1DmYRUnriklzKUG9yaqlQsKEqCFsOcKB0EfK3MfBVXIfsOdP/1bJZ9Dlz/zV19soWVhg== +"@luma.gl/gltools@8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/gltools/-/gltools-8.5.21.tgz#1077305a30712f20cd904c2e4cbe5b9263b7d138" + integrity sha512-6qZ0LaT2Mxa4AJT5F44TFoaziokYiHUwO45vnM/NYUOIu9xevcmS6VtToawytMEACGL6PDeDyVqP3Y80SDzq5g== dependencies: "@babel/runtime" "^7.0.0" - "@luma.gl/constants" "8.5.20" + "@luma.gl/constants" "8.5.21" "@probe.gl/env" "^3.5.0" "@probe.gl/log" "^3.5.0" "@types/offscreencanvas" "^2019.7.0" -"@luma.gl/shadertools@8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/shadertools/-/shadertools-8.5.20.tgz#ee09f3880acdd1599619dead3ca5fbe4116bb8b6" - integrity sha512-q1lrCZy1ncIFb4mMjsYgISLzNP6eMnhLUY+Oltj/qjAMcPEssCeHN2+XGfP/CVtU+O7sC+5JY2bQGaTs6HQ/Qw== +"@luma.gl/shadertools@8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/shadertools/-/shadertools-8.5.21.tgz#9a8e087e39e34f055f9fdda9fac527c04f637b4e" + integrity sha512-WQah7yFDJ8cNCLPYpIm3r0wSlXLvjoA279fcknmATvvkW3/i8PcCJ/nYEBJO3hHEwwMQxD16+YZu/uwGiifLMg== dependencies: "@babel/runtime" "^7.0.0" "@math.gl/core" "^3.5.0" -"@luma.gl/webgl@8.5.20", "@luma.gl/webgl@^8.5.20": - version "8.5.20" - resolved "https://registry.yarnpkg.com/@luma.gl/webgl/-/webgl-8.5.20.tgz#7eef0d695a2a62d0bbe57227c239930a4acf179f" - integrity sha512-p/kt9KztywH4l+09XHoZ4cPFOoE7xlZXIBMT8rxRVgfe1w0lvi7QYh4tOG7gk+iixQ34EyDQacoHCsabdpmqQg== +"@luma.gl/webgl@8.5.21", "@luma.gl/webgl@^8.5.21": + version "8.5.21" + resolved "https://registry.yarnpkg.com/@luma.gl/webgl/-/webgl-8.5.21.tgz#fe67bf19a41231840ca677ae702969c7a9a5d7a0" + integrity sha512-ZVLO4W5UuaOlzZIwmFWhnmZ1gYoU97a+heMqxLrSSmCUAsSu3ZETUex9gOmzdM1WWxcdWaa3M68rvKCNEgwz0Q== dependencies: "@babel/runtime" "^7.0.0" - "@luma.gl/constants" "8.5.20" - "@luma.gl/gltools" "8.5.20" + "@luma.gl/constants" "8.5.21" + "@luma.gl/gltools" "8.5.21" "@probe.gl/env" "^3.5.0" "@probe.gl/stats" "^3.5.0" @@ -2047,7 +2311,7 @@ dependencies: "@mapbox/point-geometry" "~0.1.0" -"@math.gl/core@3.6.3", "@math.gl/core@^3.5.0", "@math.gl/core@^3.5.1", "@math.gl/core@^3.6.2": +"@math.gl/core@3.6.3", "@math.gl/core@^3.5.0", "@math.gl/core@^3.6.2": version "3.6.3" resolved "https://registry.yarnpkg.com/@math.gl/core/-/core-3.6.3.tgz#a6bf796ed421093099749d609de8d99a3ac20a53" integrity sha512-jBABmDkj5uuuE0dTDmwwss7Cup5ZwQ6Qb7h1pgvtkEutTrhkcv8SuItQNXmF45494yIHeoGue08NlyeY6wxq2A== @@ -2056,46 +2320,52 @@ "@math.gl/types" "3.6.3" gl-matrix "^3.4.0" -"@math.gl/culling@^3.5.1": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@math.gl/culling/-/culling-3.6.3.tgz#91cdfa496748e8873a2e6261415a27a27b6af5f2" - integrity sha512-3UERXHbaPlM6pnTk2MI7LeQ5CoelDZzDzghTTcv+HdQCZsT/EOEuEdYimETHtSxiyiOmsX2Un65UBLYT/rbKZg== +"@math.gl/core@4.0.0", "@math.gl/core@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/core/-/core-4.0.0.tgz#db64af16743ee4def7c1e294b3d1247621d2dabc" + integrity sha512-qGbP4R8G0dsh5OUO+eWKX5NJwZitkV8CdVEolRFSoPteE0lrWxsg01FwAjegKv4jCm975VJ4HxDcb4L6KAiGGw== dependencies: "@babel/runtime" "^7.12.0" - "@math.gl/core" "3.6.3" - gl-matrix "^3.4.0" + "@math.gl/types" "4.0.0" -"@math.gl/geoid@^3.5.1": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@math.gl/geoid/-/geoid-3.6.3.tgz#fd35e6473cbf91f77a9550831b738ad6d3862c59" - integrity sha512-cZooAG6WHrCtXl4uF+x76Mz0+Fnu7UCx1iqkKlzCxBjA9FpAOVu3vAJlnkgK/Z9NuGfB+i+dJ2rdThDbPXNc1Q== +"@math.gl/culling@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/culling/-/culling-4.0.0.tgz#5bb96ec38645944088176911a6f4aab43b265cb9" + integrity sha512-8+btkQZtirG64kGbWs/UBoLnXPLw83D1g0sKgZQG32bQLm2dnArMynfqkPs/Mkj5Cm3MvwkTSdz6vN4a3FF6UA== dependencies: "@babel/runtime" "^7.12.0" - "@math.gl/core" "3.6.3" + "@math.gl/core" "4.0.0" -"@math.gl/geospatial@^3.5.1": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@math.gl/geospatial/-/geospatial-3.6.3.tgz#011ebbe8d1660ff79020a81ed218886c353a62e1" - integrity sha512-6xf657lJnaecSarSzn02t0cnsCSkWb+39m4+im96v20dZTrLCWZ2glDQVzfuL91meDnDXjH4oyvynp12Mj5MFg== +"@math.gl/geoid@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/geoid/-/geoid-4.0.0.tgz#485bffbe84af1daddd2433ad1091b5ee47f56c0c" + integrity sha512-VqrHkeG+ORYo/FDM1ymFUyvDppyKrqpcRId7RVAsk+/glHp4EL1PgnLc30RmL3MK/wzZmJSzukDtn0PU4/ieBw== dependencies: "@babel/runtime" "^7.12.0" - "@math.gl/core" "3.6.3" - gl-matrix "^3.4.0" + "@math.gl/core" "4.0.0" -"@math.gl/polygon@^3.5.1": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@math.gl/polygon/-/polygon-3.6.3.tgz#0c19c0b059cedde1cd760cc3796e9180f75bcbde" - integrity sha512-FivQ1ZnYcAss1wVifOkHP/ZnlfQy1IL/769uzNtiHxwUbW0kZG3yyOZ9I7fwyzR5Hvqt3ErJKHjSYZr0uVlz5g== +"@math.gl/geospatial@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/geospatial/-/geospatial-4.0.0.tgz#fdaf8546dc6e2459134a0b2e2bc20a0cc6baac9d" + integrity sha512-bfUqDbu9ZftmiMERMkM1b1N01RVrFUT0d6VuiMRER0d8R5GrWuRccZxROPoS52lyo692nJa0Z4Or97WJxLUYYw== dependencies: - "@math.gl/core" "3.6.3" + "@babel/runtime" "^7.12.0" + "@math.gl/core" "4.0.0" -"@math.gl/proj4@^3.3.1", "@math.gl/proj4@^3.5.1": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@math.gl/proj4/-/proj4-3.6.3.tgz#beb66a0a786f39a9d204892949d414558bc37e44" - integrity sha512-8VC3noTBiLD45VzsOVBb9nniKC+e27n5NWdzwKNOBUiXgl5HtRwNessoUYY0GccABz0OP41wbyfSzukLVAyphw== +"@math.gl/polygon@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/polygon/-/polygon-4.0.0.tgz#17d2b1c7569d5a7fd1cde67e885d77e5742c23ec" + integrity sha512-BsseetloYtSZkphH5Fqn02uCL9UWsD26DNLfGhvd2farhU9BaJnn0JGuZnRWT/rf+glZZcDJkyqHq5pDnSX/BQ== + dependencies: + "@math.gl/core" "4.0.0" + +"@math.gl/proj4@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/proj4/-/proj4-4.0.0.tgz#5073be8eedfb61decca9cb20d07116bee37748f4" + integrity sha512-gMVTlhzb1Mbq1F32jDph2nJivkipcGNU+Nx+jovFT9shplXwXeEdZxObjngYCtQiZEM3HqFFTbTnA1YhhZsG7A== dependencies: "@babel/runtime" "^7.12.0" - "@math.gl/core" "3.6.3" + "@math.gl/core" "4.0.0" "@types/proj4" "^2.5.0" proj4 "2.6.2" @@ -2111,7 +2381,12 @@ resolved "https://registry.yarnpkg.com/@math.gl/types/-/types-3.6.3.tgz#9fa9866feabcbb76de107d78ff3a89c0243ac374" integrity sha512-3uWLVXHY3jQxsXCr/UCNPSc2BG0hNUljhmOBt9l+lNFDp7zHgm0cK2Tw4kj2XfkJy4TgwZTBGwRDQgWEbLbdTA== -"@math.gl/web-mercator@^3.5.1", "@math.gl/web-mercator@^3.6.2": +"@math.gl/types@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/types/-/types-4.0.0.tgz#20c649dcef8459d9dd1f83a708d7410fe06a3309" + integrity sha512-ZqU7o0LFaWQK/0wYobCwQKrKhRHaihps8oE74CLnWAdTTjXkM2vA8dU7vdx238QfXkNkz4Mv+KYklHpXMQJ8Hw== + +"@math.gl/web-mercator@^3.6.2": version "3.6.3" resolved "https://registry.yarnpkg.com/@math.gl/web-mercator/-/web-mercator-3.6.3.tgz#ef91168e030eecffc788618d686e8a6c1d7a0bf8" integrity sha512-UVrkSOs02YLehKaehrxhAejYMurehIHPfFQvPFZmdJHglHOU4V2cCUApTVEwOksvCp161ypEqVp+9H6mGhTTcw== @@ -2119,6 +2394,13 @@ "@babel/runtime" "^7.12.0" gl-matrix "^3.4.0" +"@math.gl/web-mercator@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@math.gl/web-mercator/-/web-mercator-4.0.0.tgz#2df70ec478b464337991b9fcdc3acaacf41311cc" + integrity sha512-dtzjaJPckyAEsCT0eHayPoZ8RrHg7XkQq9fZAHAn8CPiyLX0J0ZdvpH1x4a3qe7Ct7CPo6ChnqSk0DwItA4aNQ== + dependencies: + "@babel/runtime" "^7.12.0" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -2311,14 +2593,6 @@ dependencies: "@babel/runtime" "^7.0.0" -"@probe.gl/log@3.6.0", "@probe.gl/log@^3.5.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@probe.gl/log/-/log-3.6.0.tgz#c645bfd22b4769dc65161caa17f13bd2b231e413" - integrity sha512-hjpyenpEvOdowgZ1qMeCJxfRD4JkKdlXz0RC14m42Un62NtOT+GpWyKA4LssT0+xyLULCByRAtG2fzZorpIAcA== - dependencies: - "@babel/runtime" "^7.0.0" - "@probe.gl/env" "3.6.0" - "@probe.gl/log@4.0.3", "@probe.gl/log@^4.0.1", "@probe.gl/log@^4.0.2": version "4.0.3" resolved "https://registry.yarnpkg.com/@probe.gl/log/-/log-4.0.3.tgz#69c57aac4394f21e7786285028403fa7504bc4f4" @@ -2327,7 +2601,7 @@ "@babel/runtime" "^7.0.0" "@probe.gl/env" "4.0.3" -"@probe.gl/log@^4.0.4": +"@probe.gl/log@4.0.4", "@probe.gl/log@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@probe.gl/log/-/log-4.0.4.tgz#e42d1d0e22981c4010521c350cad2305bce02976" integrity sha512-WpmXl6njlBMwrm8HBh/b4kSp/xnY1VVmeT4PWUKF+RkVbFuKQbsU11dA1IxoMd7gSY+5DGIwxGfAv1H5OMzA4A== @@ -2335,6 +2609,14 @@ "@babel/runtime" "^7.0.0" "@probe.gl/env" "4.0.4" +"@probe.gl/log@^3.5.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@probe.gl/log/-/log-3.6.0.tgz#c645bfd22b4769dc65161caa17f13bd2b231e413" + integrity sha512-hjpyenpEvOdowgZ1qMeCJxfRD4JkKdlXz0RC14m42Un62NtOT+GpWyKA4LssT0+xyLULCByRAtG2fzZorpIAcA== + dependencies: + "@babel/runtime" "^7.0.0" + "@probe.gl/env" "3.6.0" + "@probe.gl/stats@^3.5.0": version "3.6.0" resolved "https://registry.yarnpkg.com/@probe.gl/stats/-/stats-3.6.0.tgz#a1bb12860fa6f40b9c028f9eb575d7ada0b4dbdd" @@ -2349,16 +2631,6 @@ dependencies: "@babel/runtime" "^7.0.0" -"@probe.gl/test-utils@^3.0.2": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@probe.gl/test-utils/-/test-utils-3.6.0.tgz#4c3dc04061896456935dc2ecf6ed79ef8de9c33a" - integrity sha512-BK6NkeNVN9CVC9Dbp7+Unvu8bAwWOLm3TV/WcyLd1ysI23xkrnPgUeN/PGNOqIqHkq32ijZwew7gX4Euc3Du3g== - dependencies: - "@babel/runtime" "^7.0.0" - "@probe.gl/log" "3.6.0" - pixelmatch "^4.0.2" - puppeteer "*" - "@probe.gl/test-utils@^4.0.2": version "4.0.3" resolved "https://registry.yarnpkg.com/@probe.gl/test-utils/-/test-utils-4.0.3.tgz#1bddd2543f89d5dcc928784bbf9a9d3b7a22c756" @@ -2370,6 +2642,17 @@ pixelmatch "^4.0.2" puppeteer "*" +"@probe.gl/test-utils@^4.0.3": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@probe.gl/test-utils/-/test-utils-4.0.4.tgz#86533d0a6ea3db03d3d41356830e4de8b0d84c8d" + integrity sha512-VxcFQ0yxgvbdL/4HQnrSFhaSRoUYGm6UB7xlah+DNUhBOJNF+30Cba7ekZYkq6u1ggcS+bRIf7Susgb4UXuI3g== + dependencies: + "@babel/runtime" "^7.0.0" + "@probe.gl/log" "4.0.4" + "@types/pngjs" "^6.0.1" + pixelmatch "^4.0.2" + puppeteer "*" + "@puppeteer/browsers@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.0.0.tgz#89de56a718c922857b1d802aac473ebbe1f54d99" @@ -2384,6 +2667,19 @@ unbzip2-stream "1.4.3" yargs "17.7.1" +"@repeaterjs/repeater@3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca" + integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA== + +"@rollup/pluginutils@^4.2.0": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" + integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + "@tmcw/togeojson@^4.5.0": version "4.7.0" resolved "https://registry.yarnpkg.com/@tmcw/togeojson/-/togeojson-4.7.0.tgz#071d6d6d01f0aa86299cc98e3cdb102f1119241d" @@ -2454,6 +2750,14 @@ "@turf/invariant" "^5.1.5" "@turf/meta" "^5.1.5" +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/brotli@^1.3.0": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/brotli/-/brotli-1.3.1.tgz#65dc6c69bb9f4159677032f60e81ffc09faf1fce" @@ -2478,6 +2782,13 @@ resolved "https://registry.yarnpkg.com/@types/command-line-usage/-/command-line-usage-5.0.2.tgz#ba5e3f6ae5a2009d466679cc431b50635bf1a064" integrity sha512-n7RlEEJ+4x4TS7ZQddTmNSxP+zziEG0TNsMfiRIxcIVXt71ENJ9ojeXmGO3wPoTdn7pJcU2xc3CJYMktNT6DPg== +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + "@types/crypto-js@^4.0.2": version "4.1.1" resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.1.1.tgz#602859584cecc91894eb23a4892f38cfa927890d" @@ -2488,10 +2799,25 @@ resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.39.6.tgz#698b90fe60d44acf93c31064218fbea93fbfd85a" integrity sha512-H90aoynNhhkQP6DRweEjJp5vfUVdIj7tdPLsu7pq89vODD/lcugKfZOsfgwpvM6XUewEp2N5dCg1Uf3Qe55Dcg== -"@types/flatbuffers@*": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@types/flatbuffers/-/flatbuffers-1.10.0.tgz#aa74e30ffdc86445f2f060e1808fc9d56b5603ba" - integrity sha512-7btbphLrKvo5yl/5CC2OCxUSMx1wV1wvGT1qDXkSt7yi00/YW7E8k6qzXqJHsp+WU0eoG7r6MTQQXI9lIvd0qA== +"@types/express-serve-static-core@^4.17.33": + version "4.17.35" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz#c95dd4424f0d32e525d23812aa8ab8e4d3906c4f" + integrity sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@^4.17.17": + version "4.17.17" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" + integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" "@types/geojson@^7946.0.7": version "7946.0.10" @@ -2519,7 +2845,22 @@ resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.41.tgz#f6ecf57d1b12d2befcce00e928a6a097c22980aa" integrity sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA== -"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7": +"@types/http-errors@*": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.1.tgz#20172f9578b225f6c7da63446f56d4ce108d5a65" + integrity sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ== + +"@types/istanbul-lib-coverage@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/json-schema@^7.0.12": + version "7.0.14" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.14.tgz#74a97a5573980802f32c8e47b663530ab3b6b7d1" + integrity sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw== + +"@types/json-schema@^7.0.5": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== @@ -2536,6 +2877,16 @@ dependencies: "@types/unist" "*" +"@types/mime@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + "@types/minimatch@*": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" @@ -2563,15 +2914,20 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.3.tgz#6bda7819aae6ea0b386ebc5b24bdf602f1b42b01" integrity sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q== +"@types/node@20.3.0": + version "20.3.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.0.tgz#719498898d5defab83c3560f45d8498f58d11938" + integrity sha512-cumHmIAf6On83X7yP+LrsEyUOf/YlociZelmpRYaGFydoaPdxdt80MAbu6vWerQT2COCp2nPvHdsbD7tHn/YlQ== + "@types/node@^10.14.15": version "10.17.60" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== -"@types/node@^17.0.36": - version "17.0.45" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" - integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== +"@types/node@^20.4.2": + version "20.4.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.2.tgz#129cc9ae69f93824f92fac653eebfb4812ab4af9" + integrity sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2615,10 +2971,42 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== -"@types/sql.js@^1.4.2": - version "1.4.4" - resolved "https://registry.yarnpkg.com/@types/sql.js/-/sql.js-1.4.4.tgz#1c4df5045f08a518d48cda6bbb4f5dacc44c503c" - integrity sha512-6EWU2wfiBtzgTy18WQoXZAGTreBjhZcBCfD8CDvyI1Nj0a4KNDDt41IYeAZ40cRUdfqWHb7VGx7t6nK0yBOI5A== +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/semver@^7.5.0": + version "7.5.4" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.4.tgz#0a41252ad431c473158b22f9bfb9a63df7541cff" + integrity sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ== + +"@types/send@*": + version "0.17.1" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.1.tgz#ed4932b8a2a805f1fe362a70f4e62d0ac994e301" + integrity sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.2.tgz#3e5419ecd1e40e7405d34093f10befb43f63381a" + integrity sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw== + dependencies: + "@types/http-errors" "*" + "@types/mime" "*" + "@types/node" "*" + +"@types/sql.js@^1.4.5": + version "1.4.5" + resolved "https://registry.yarnpkg.com/@types/sql.js/-/sql.js-1.4.5.tgz#e69d1a1ca3515ca6dea209f1cb85b402839c568f" + integrity sha512-FxaKd4TLmXC2gGsV0Pe1fX0GJkfVP3fIFJCXOjNMOYHoTLCi0+FLsURQmCtJLwlhabryvN67RMzRxpAErpEBWw== dependencies: "@types/emscripten" "*" "@types/node" "*" @@ -2673,235 +3061,95 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^4.26.1": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" - integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== - dependencies: - "@typescript-eslint/experimental-utils" "4.33.0" - "@typescript-eslint/scope-manager" "4.33.0" - debug "^4.3.1" - functional-red-black-tree "^1.0.1" - ignore "^5.1.8" - regexpp "^3.1.0" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/experimental-utils@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" - integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== - dependencies: - "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - -"@typescript-eslint/parser@^4.26.1": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" - integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== - dependencies: - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" - debug "^4.3.1" - -"@typescript-eslint/scope-manager@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" - integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== - dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - -"@typescript-eslint/types@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" - integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== - -"@typescript-eslint/typescript-estree@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" - integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== - dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/visitor-keys@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" - integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== - dependencies: - "@typescript-eslint/types" "4.33.0" - eslint-visitor-keys "^2.0.0" - -"@webassemblyjs/ast@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" - integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== - dependencies: - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - -"@webassemblyjs/floating-point-hex-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" - integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== - -"@webassemblyjs/helper-api-error@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" - integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== - -"@webassemblyjs/helper-buffer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" - integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== - -"@webassemblyjs/helper-code-frame@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" - integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/helper-fsm@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" - integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== - -"@webassemblyjs/helper-module-context@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" - integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== - dependencies: - "@webassemblyjs/ast" "1.9.0" - -"@webassemblyjs/helper-wasm-bytecode@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" - integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== - -"@webassemblyjs/helper-wasm-section@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" - integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - -"@webassemblyjs/ieee754@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" - integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" - integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" - integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== - -"@webassemblyjs/wasm-edit@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" - integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/helper-wasm-section" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-opt" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/wasm-gen@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" - integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wasm-opt@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" - integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - -"@webassemblyjs/wasm-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" - integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wast-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" - integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/floating-point-hex-parser" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-code-frame" "1.9.0" - "@webassemblyjs/helper-fsm" "1.9.0" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" - integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - "@xtuc/long" "4.2.2" - -"@xmldom/xmldom@^0.7.5": - version "0.7.10" - resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.10.tgz#b1f4a7dc63ac35b2750847644d5dacf5b4ead12f" - integrity sha512-hb9QhOg5MGmpVkFcoZ9XJMe1em5gd0e2eqqjK87O1dwULedXsnY/Zg/Ju6lcohA+t6jVkmKpe7I1etqhvdRdrQ== - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +"@typescript-eslint/eslint-plugin@^4.26.1", "@typescript-eslint/eslint-plugin@^6.0.0": + version "6.8.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz#06abe4265e7c82f20ade2dcc0e3403c32d4f148b" + integrity sha512-GosF4238Tkes2SHPQ1i8f6rMtG6zlKwMEB0abqSJ3Npvos+doIlc/ATG+vX1G9coDF3Ex78zM3heXHLyWEwLUw== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.8.0" + "@typescript-eslint/type-utils" "6.8.0" + "@typescript-eslint/utils" "6.8.0" + "@typescript-eslint/visitor-keys" "6.8.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/parser@^4.26.1", "@typescript-eslint/parser@^6.0.0": + version "6.8.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.8.0.tgz#bb2a969d583db242f1ee64467542f8b05c2e28cb" + integrity sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg== + dependencies: + "@typescript-eslint/scope-manager" "6.8.0" + "@typescript-eslint/types" "6.8.0" + "@typescript-eslint/typescript-estree" "6.8.0" + "@typescript-eslint/visitor-keys" "6.8.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@6.8.0": + version "6.8.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.8.0.tgz#5cac7977385cde068ab30686889dd59879811efd" + integrity sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g== + dependencies: + "@typescript-eslint/types" "6.8.0" + "@typescript-eslint/visitor-keys" "6.8.0" + +"@typescript-eslint/type-utils@6.8.0": + version "6.8.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.8.0.tgz#50365e44918ca0fd159844b5d6ea96789731e11f" + integrity sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g== + dependencies: + "@typescript-eslint/typescript-estree" "6.8.0" + "@typescript-eslint/utils" "6.8.0" + debug "^4.3.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/types@6.8.0": + version "6.8.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.8.0.tgz#1ab5d4fe1d613e3f65f6684026ade6b94f7e3ded" + integrity sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ== + +"@typescript-eslint/typescript-estree@6.8.0": + version "6.8.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.8.0.tgz#9565f15e0cd12f55cf5aa0dfb130a6cb0d436ba1" + integrity sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg== + dependencies: + "@typescript-eslint/types" "6.8.0" + "@typescript-eslint/visitor-keys" "6.8.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@6.8.0": + version "6.8.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.8.0.tgz#d42939c2074c6b59844d0982ce26a51d136c4029" + integrity sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.8.0" + "@typescript-eslint/types" "6.8.0" + "@typescript-eslint/typescript-estree" "6.8.0" + semver "^7.5.4" + +"@typescript-eslint/visitor-keys@6.8.0": + version "6.8.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.8.0.tgz#cffebed56ae99c45eba901c378a6447b06be58b8" + integrity sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg== + dependencies: + "@typescript-eslint/types" "6.8.0" + eslint-visitor-keys "^3.4.1" + +"@xmldom/xmldom@^0.7.13": + version "0.7.13" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.13.tgz#ff34942667a4e19a9f4a0996a76814daac364cf3" + integrity sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g== "@zkochan/cmd-shim@^3.1.0": version "3.1.0" @@ -2925,7 +3173,7 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: +accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -2938,22 +3186,12 @@ acorn-jsx@^5.3.1: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^6.4.1: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - -acorn@^7.1.1, acorn@^7.4.0: +acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -2963,13 +3201,10 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== -adler-32@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/adler-32/-/adler-32-1.2.0.tgz#6a3e6bf0a63900ba15652808cb15c6813d1a5f25" - integrity sha512-/vUqU/UY4MVeFsg+SsK6c+/05RZXIHZMGJA+PX5JyWI0ZRcBpupnRuPLU/NXXoFwMYCPCoxIfElM2eS+DUXCqQ== - dependencies: - exit-on-epipe "~1.0.1" - printj "~1.1.0" +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== adler-32@~1.3.0: version "1.3.1" @@ -3004,25 +3239,12 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ajv-errors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" - integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== - -ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: +ajv-keywords@^3.1.0, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -3042,11 +3264,6 @@ ajv@^8.0.1: require-from-string "^2.0.2" uri-js "^4.2.2" -ansi-colors@^3.0.0: - version "3.2.4" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" - integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== - ansi-colors@^4.1.1: version "4.1.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" @@ -3057,10 +3274,12 @@ ansi-escapes@^3.2.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== -ansi-html-community@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" - integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" ansi-regex@^2.0.0: version "2.1.1" @@ -3101,14 +3320,6 @@ any-promise@^1.0.0: resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -3117,29 +3328,21 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -apache-arrow@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/apache-arrow/-/apache-arrow-9.0.0.tgz#c2308e5b9723aef75f7a56d9339c2523736a31f6" - integrity sha512-Myt0vtm7eRtg4dYQp1hb2A77jbPhCYZcPeNp1F0u7te3rWQtcDI3EOSLxToFywdLQ1hfPzhzdLfDL0tPQObJjw== +apache-arrow@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/apache-arrow/-/apache-arrow-13.0.0.tgz#8c8c69ca8ec53b804b4e10fe6b57483bda46dfbd" + integrity sha512-3gvCX0GDawWz6KFNC28p65U+zGh/LZ6ZNKWNu74N6CQlKzxeoWHpi4CgEQsgRSEMuyrIIXi1Ea2syja7dwcHvw== dependencies: "@types/command-line-args" "5.2.0" "@types/command-line-usage" "5.0.2" - "@types/flatbuffers" "*" - "@types/node" "^17.0.36" + "@types/node" "20.3.0" "@types/pad-left" "2.1.1" command-line-args "5.2.1" - command-line-usage "6.1.3" - flatbuffers "2.0.4" + command-line-usage "7.0.1" + flatbuffers "23.5.26" json-bignum "^0.0.3" pad-left "^2.1.0" - tslib "^2.4.0" - -append-transform@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" - integrity sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg== - dependencies: - default-require-extensions "^3.0.0" + tslib "^2.5.3" aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" @@ -3180,11 +3383,6 @@ archiver@^5.0.0: tar-stream "^2.2.0" zip-stream "^4.1.0" -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== - are-we-there-yet@~1.1.2: version "1.1.7" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" @@ -3237,10 +3435,10 @@ array-back@^3.0.1, array-back@^3.1.0: resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== -array-back@^4.0.1, array-back@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" - integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== +array-back@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-6.2.2.tgz#f567d99e9af88a6d3d2f9dfcc21db6f9ba9fd157" + integrity sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw== array-buffer-byte-length@^1.0.0: version "1.0.0" @@ -3265,11 +3463,6 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== -array-flatten@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" @@ -3286,7 +3479,7 @@ array-includes@^3.1.5, array-includes@^3.1.6: get-intrinsic "^1.1.3" is-string "^1.0.7" -array-union@^1.0.1, array-union@^1.0.2: +array-union@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== @@ -3360,6 +3553,19 @@ array.prototype.tosorted@^1.1.1: es-shim-unscopables "^1.0.0" get-intrinsic "^1.1.3" +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -3370,16 +3576,6 @@ asap@^2.0.0: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== -asn1.js@^5.2.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" - asn1@~0.2.3: version "0.2.6" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" @@ -3392,14 +3588,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== -assert@^1.1.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== - dependencies: - object-assign "^4.1.1" - util "0.10.3" - assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -3415,11 +3603,6 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-each@^1.0.1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.6.tgz#52f1d9403818c179b7561e11a5d1b77eb2160e77" - integrity sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg== - async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" @@ -3432,13 +3615,6 @@ async-mutex@^0.2.2: dependencies: tslib "^2.0.0" -async@^2.5.0, async@^2.6.4: - version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== - dependencies: - lodash "^4.17.14" - async@^3.2.3: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" @@ -3496,6 +3672,13 @@ babel-loader@8.2.2: make-dir "^3.1.0" schema-utils "^2.6.5" +babel-plugin-add-import-extension@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-add-import-extension/-/babel-plugin-add-import-extension-1.6.0.tgz#807ce65b38d4763797c1616cb4e8372da167cdd1" + integrity sha512-JVSQPMzNzN/S4wPRoKQ7+u8PlkV//BPUMnfWVbr63zcE+6yHdU2Mblz10Vf7qe+6Rmu4svF5jG7JxdcPi9VvKg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + babel-plugin-istanbul@^6.0.0: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -3550,7 +3733,7 @@ base64-inline-loader@^1.1.1: loader-utils "^1.1.0" mime-types "^2.1.18" -base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.3.1: +base64-js@^1.1.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -3580,11 +3763,6 @@ basic-auth@~2.0.0: dependencies: safe-buffer "5.1.2" -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== - bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -3597,44 +3775,17 @@ before-after-hook@^2.0.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== -bfj@^6.1.1: - version "6.1.2" - resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" - integrity sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw== - dependencies: - bluebird "^3.5.5" - check-types "^8.0.3" - hoopy "^0.1.4" - tryer "^1.0.1" - -big.js@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" - integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - -bl@^4.0.3: +bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== @@ -3648,36 +3799,10 @@ bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - -body-parser@1.19.2: - version "1.19.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" - integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.8.1" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.9.7" - raw-body "2.4.3" - type-is "~1.6.18" - -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== dependencies: bytes "3.1.2" content-type "~1.0.4" @@ -3692,18 +3817,6 @@ body-parser@1.20.1: type-is "~1.6.18" unpipe "1.0.0" -bonjour@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" - integrity sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg== - dependencies: - array-flatten "^2.1.0" - deep-equal "^1.0.1" - dns-equal "^1.0.0" - dns-txt "^2.0.2" - multicast-dns "^6.0.1" - multicast-dns-service-types "^1.1.0" - boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -3724,7 +3837,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^2.3.1, braces@^2.3.2: +braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== @@ -3747,11 +3860,6 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" -brorand@^1.0.1, brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== - brotli@^1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/brotli/-/brotli-1.3.3.tgz#7365d8cc00f12cf765d2b2c898716bcf4b604d48" @@ -3764,67 +3872,6 @@ browser-or-node@^1.2.1: resolved "https://registry.yarnpkg.com/browser-or-node/-/browser-or-node-1.3.0.tgz#f2a4e8568f60263050a6714b2cc236bb976647a7" integrity sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg== -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" - integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== - dependencies: - bn.js "^5.0.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== - dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - elliptic "^6.5.3" - inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" - integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== - dependencies: - pako "~1.0.5" - browserslist@^4.21.3, browserslist@^4.21.5: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" @@ -3867,25 +3914,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer-indexof@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" - integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== - -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== - -buffer@^4.3.0: - version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -3902,11 +3930,6 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== - builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" @@ -3922,17 +3945,30 @@ byte-size@^5.0.1: resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-5.0.1.tgz#4b651039a5ecd96767e71a3d7ed380e48bed4191" integrity sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw== -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3: +c8@^7.12.0: + version "7.14.0" + resolved "https://registry.yarnpkg.com/c8/-/c8-7.14.0.tgz#f368184c73b125a80565e9ab2396ff0be4d732f3" + integrity sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@istanbuljs/schema" "^0.1.3" + find-up "^5.0.0" + foreground-child "^2.0.0" + istanbul-lib-coverage "^3.2.0" + istanbul-lib-report "^3.0.0" + istanbul-reports "^3.1.4" + rimraf "^3.0.2" + test-exclude "^6.0.0" + v8-to-istanbul "^9.0.0" + yargs "^16.2.0" + yargs-parser "^20.2.9" + +cacache@^12.0.0, cacache@^12.0.3: version "12.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== @@ -3968,17 +4004,7 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -caching-transform@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" - integrity sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA== - dependencies: - hasha "^5.0.0" - make-dir "^3.0.0" - package-hash "^4.0.0" - write-file-atomic "^3.0.0" - -call-bind@^1.0.0, call-bind@^1.0.2: +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== @@ -4015,13 +4041,13 @@ callsites@^3.0.0, callsites@^3.1.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camel-case@3.0.x: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" - integrity sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w== +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== dependencies: - no-case "^2.2.0" - upper-case "^1.1.1" + pascal-case "^3.1.2" + tslib "^2.0.3" camelcase-keys@^2.0.0: version "2.1.0" @@ -4065,16 +4091,16 @@ camelcase@^5.0.0, camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== caniuse-lite@^1.0.30001449: - version "1.0.30001515" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001515.tgz" - integrity sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA== + version "1.0.30001546" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz" + integrity sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw== caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== -cfb@^1.1.4: +cfb@~1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/cfb/-/cfb-1.2.2.tgz#94e687628c700e5155436dac05f74e08df23bc44" integrity sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA== @@ -4082,7 +4108,14 @@ cfb@^1.1.4: adler-32 "~1.3.0" crc-32 "~1.2.0" -chalk@^2.0.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: +chalk-template@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/chalk-template/-/chalk-template-0.4.0.tgz#692c034d0ed62436b9062c1707fadcd0f753204b" + integrity sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg== + dependencies: + chalk "^4.1.2" + +chalk@^2.0.0, chalk@^2.3.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -4091,7 +4124,7 @@ chalk@^2.0.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -4124,31 +4157,7 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== -check-types@^8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" - integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== - -chokidar@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chokidar@^3.4.0, chokidar@^3.4.1: +chokidar@^3.4.0: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -4168,11 +4177,6 @@ chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.4: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== - chromium-bidi@0.4.7: version "0.4.7" resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.7.tgz#4c022c2b0fb1d1c9b571fadf373042160e71d236" @@ -4185,14 +4189,6 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -4203,18 +4199,13 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -clean-css@4.2.x: - version "4.2.4" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" - integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A== +clean-css@^5.2.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.2.tgz#70ecc7d4d4114921f5d298349ff86a31a9975224" + integrity sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww== dependencies: source-map "~0.6.0" -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -4222,11 +4213,28 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.0.tgz#5881d0ad96381e117bbe07ad91f2008fe6ffd8db" + integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g== + cli-width@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -4236,14 +4244,14 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" + wrap-ansi "^7.0.0" cliui@^8.0.1: version "8.0.1" @@ -4310,6 +4318,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colorette@^2.0.16: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + columnify@^1.5.4: version "1.6.0" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" @@ -4325,7 +4338,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -command-line-args@5.2.1: +command-line-args@5.2.1, command-line-args@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== @@ -4335,35 +4348,30 @@ command-line-args@5.2.1: lodash.camelcase "^4.3.0" typical "^4.0.0" -command-line-usage@6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.3.tgz#428fa5acde6a838779dfa30e44686f4b6761d957" - integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== +command-line-usage@7.0.1, command-line-usage@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-7.0.1.tgz#e540afef4a4f3bc501b124ffde33956309100655" + integrity sha512-NCyznE//MuTjwi3y84QVUGEOT+P5oto1e1Pk/jFPVdPPfsG03qpTIl3yw6etR+v73d0lXsoojRpvbru2sqePxQ== dependencies: - array-back "^4.0.2" - chalk "^2.4.2" - table-layout "^1.0.2" - typical "^5.2.0" + array-back "^6.2.2" + chalk-template "^0.4.0" + table-layout "^3.0.0" + typical "^7.1.1" -commander@2, commander@^2.18.0, commander@^2.20.0: +commander@2, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@2.17.x: - version "2.17.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" - integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== - commander@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -commander@~2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== commondir@^1.0.1: version "1.0.1" @@ -4393,26 +4401,6 @@ compress-commons@^4.1.0: normalize-path "^3.0.0" readable-stream "^3.6.0" -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -4446,31 +4434,21 @@ config-chain@^1.1.11: ini "^1.3.4" proto-list "~1.2.1" -confusing-browser-globals@^1.0.10: - version "1.0.11" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" - integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== - connect-history-api-fallback@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== -console-browserify@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" - integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== +consola@^2.15.3: + version "2.15.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== - content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -4572,7 +4550,7 @@ conventional-recommended-bump@^5.0.0: meow "^4.0.0" q "^1.5.1" -convert-source-map@^1.1.0, convert-source-map@^1.7.0: +convert-source-map@^1.1.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== @@ -4582,11 +4560,6 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== - cookie@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" @@ -4624,11 +4597,6 @@ core-js-compat@^3.25.1: dependencies: browserslist "^4.21.5" -core-js@^3.2.1: - version "3.30.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.30.1.tgz#fc9c5adcc541d8e9fa3e381179433cbf795628ba" - integrity sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ== - core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -4678,7 +4646,7 @@ coveralls@^3.0.3: minimist "^1.2.5" request "^2.88.2" -crc-32@^1.2.0, crc-32@~1.2.0: +crc-32@^1.2.0, crc-32@~1.2.0, crc-32@~1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== @@ -4691,37 +4659,6 @@ crc32-stream@^4.0.2: crc-32 "^1.2.0" readable-stream "^3.4.0" -create-ecdh@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" - integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== - dependencies: - bn.js "^4.1.0" - elliptic "^6.5.3" - -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -4743,7 +4680,7 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -4754,7 +4691,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4768,29 +4705,12 @@ crypt@0.0.2, crypt@^0.0.2: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== -crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - "crypto-js@^3.0.0 || ^4.0.0": version "4.1.1" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== -css-select@^4.1.3: +css-select@^4.2.1: version "4.3.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== @@ -4863,7 +4783,7 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@~2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -4877,7 +4797,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1: +debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.4, debug@~4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4919,18 +4839,6 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== -deep-equal@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" - integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== - dependencies: - is-arguments "^1.0.4" - is-date-object "^1.0.1" - is-regex "^1.0.4" - object-is "^1.0.1" - object-keys "^1.1.1" - regexp.prototype.flags "^1.2.0" - deep-equal@^2.0.5: version "2.2.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.1.tgz#c72ab22f3a7d3503a4ca87dde976fe9978816739" @@ -4955,15 +4863,17 @@ deep-equal@^2.0.5: which-collection "^1.0.1" which-typed-array "^1.1.9" -deep-equal@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw== - -deep-extend@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +deep-equal@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" deep-is@^0.1.3: version "0.1.4" @@ -4982,21 +4892,6 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -default-gateway@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" - integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== - dependencies: - execa "^1.0.0" - ip-regex "^2.1.0" - -default-require-extensions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.1.tgz#bfae00feeaeada68c2ae256c62540f60b80625bd" - integrity sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw== - dependencies: - strip-bom "^4.0.0" - defaults@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" @@ -5004,7 +4899,16 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: +define-data-property@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.0.tgz#0db13540704e1d8d479a0656cf781267531b9451" + integrity sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== @@ -5039,19 +4943,6 @@ defined@~1.0.0: resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf" integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== -del@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" - integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== - dependencies: - "@types/glob" "^7.1.1" - globby "^6.1.0" - is-path-cwd "^2.0.0" - is-path-in-cwd "^2.0.0" - p-map "^2.0.0" - pify "^4.0.1" - rimraf "^2.6.3" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -5077,39 +4968,16 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg== - -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - integrity sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q== - detect-indent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g== -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== - devtools-protocol@0.0.1107588: version "0.0.1107588" resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1107588.tgz#f8cac707840b97cc30b029359341bcbbb0ad8ffa" @@ -5128,15 +4996,6 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -5151,26 +5010,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== - -dns-packet@^1.3.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" - integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== - dependencies: - ip "^1.1.0" - safe-buffer "^5.0.1" - -dns-txt@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" - integrity sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ== - dependencies: - buffer-indexof "^1.0.0" - doctrine@^1.2.2: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -5193,13 +5032,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-converter@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" - integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== - dependencies: - utila "~0.4" - dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" @@ -5209,24 +5041,19 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== -domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: +domhandler@^4.2.0, domhandler@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== dependencies: domelementtype "^2.2.0" -domutils@^2.5.2, domutils@^2.8.0: +domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== @@ -5235,6 +5062,14 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + dot-prop@^4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4" @@ -5249,6 +5084,23 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" +dotenv-expand@^8.0.2: + version "8.0.3" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-8.0.3.tgz#29016757455bcc748469c83a19b36aaf2b83dd6e" + integrity sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg== + +dotenv@^16.0.0: + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + +dotignore@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905" + integrity sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw== + dependencies: + minimatch "^3.0.4" + draco3d@1.5.5: version "1.5.5" resolved "https://registry.yarnpkg.com/draco3d/-/draco3d-1.5.5.tgz#6bf4bbdd65950e6153e991cb0dcb8a10323f610e" @@ -5282,29 +5134,18 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -ejs@^2.6.1: - version "2.7.4" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" - integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== +ejs@^3.1.6: + version "3.1.9" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" + integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== + dependencies: + jake "^10.8.5" electron-to-chromium@^1.4.284: version "1.4.379" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.379.tgz#c9b597e090ce738e7a76db84e5678f27817bd644" integrity sha512-eRMq6Cf4PhjB14R9U6QcXM/VRQ54Gc3OL9LKnFugUIh2AXm3KJlOizlSfVIgjH76bII4zHGK4t0PVTE5qq8dZg== -elliptic@^6.5.3: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -5320,11 +5161,6 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - integrity sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng== - emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" @@ -5349,15 +5185,6 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^4.1.1, enhanced-resolve@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" - integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.5.0" - tapable "^1.0.0" - enquirer@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -5385,13 +5212,6 @@ err-code@^1.0.0: resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" integrity sha512-CJAN+O0/yA1CKfRn9SXOGctSpEM7DCon/r/5r2eXFMY2zCCJBasFhcM5I+1kh3Ap11FsQCX+vGHceNPvpWKhoA== -errno@^0.1.3, errno@~0.1.7: - version "0.1.8" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" - integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== - dependencies: - prr "~1.0.1" - error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -5399,7 +5219,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2, es-abstract@^1.5.0: +es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: version "1.21.2" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== @@ -5439,6 +5259,51 @@ es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2, es-abstract@^1.5. unbox-primitive "^1.0.2" which-typed-array "^1.1.9" +es-abstract@^1.22.1: + version "1.22.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.2.tgz#90f7282d91d0ad577f505e423e52d4c1d93c1b8a" + integrity sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.1" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.11" + es-array-method-boxes-properly@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" @@ -5484,11 +5349,6 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es6-error@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" - integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== - es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -5501,113 +5361,70 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -esbuild-android-arm64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz#3fc3ff0bab76fe35dd237476b5d2b32bb20a3d44" - integrity sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg== - -esbuild-darwin-64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz#8e9169c16baf444eacec60d09b24d11b255a8e72" - integrity sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ== - -esbuild-darwin-arm64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz#1b07f893b632114f805e188ddfca41b2b778229a" - integrity sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ== - -esbuild-freebsd-64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz#0b8b7eca1690c8ec94c75680c38c07269c1f4a85" - integrity sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA== - -esbuild-freebsd-arm64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz#2e1a6c696bfdcd20a99578b76350b41db1934e52" - integrity sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ== - -esbuild-linux-32@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz#6fd39f36fc66dd45b6b5f515728c7bbebc342a69" - integrity sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g== - -esbuild-linux-64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz#9cb8e4bcd7574e67946e4ee5f1f1e12386bb6dd3" - integrity sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA== - -esbuild-linux-arm64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz#3891aa3704ec579a1b92d2a586122e5b6a2bfba1" - integrity sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA== - -esbuild-linux-arm@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz#8a00e99e6a0c6c9a6b7f334841364d8a2b4aecfe" - integrity sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA== - -esbuild-linux-mips64le@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz#36b07cc47c3d21e48db3bb1f4d9ef8f46aead4f7" - integrity sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg== - -esbuild-linux-ppc64le@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz#f7e6bba40b9a11eb9dcae5b01550ea04670edad2" - integrity sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ== - -esbuild-netbsd-64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz#a2fedc549c2b629d580a732d840712b08d440038" - integrity sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w== - -esbuild-openbsd-64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz#b22c0e5806d3a1fbf0325872037f885306b05cd7" - integrity sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g== - -esbuild-sunos-64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz#d0b6454a88375ee8d3964daeff55c85c91c7cef4" - integrity sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw== - -esbuild-windows-32@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz#c96d0b9bbb52f3303322582ef8e4847c5ad375a7" - integrity sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw== - -esbuild-windows-64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz#1f79cb9b1e1bb02fb25cd414cb90d4ea2892c294" - integrity sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ== - -esbuild-windows-arm64@0.13.15: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz#482173070810df22a752c686509c370c3be3b3c3" - integrity sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA== - -esbuild@^0.13.13: - version "0.13.15" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.15.tgz#db56a88166ee373f87dbb2d8798ff449e0450cdf" - integrity sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw== +"esbuild-plugin-babel@git+https://github.com/Pessimistress/esbuild-plugin-babel.git#patch-1": + version "0.2.3" + resolved "git+https://github.com/Pessimistress/esbuild-plugin-babel.git#0c081a5436ae0e703d97eff28fe77edfc1e30e4a" + +esbuild-plugin-external-global@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esbuild-plugin-external-global/-/esbuild-plugin-external-global-1.0.1.tgz#e3bba0e3a561f61b395bec0984a90ed0de06c4ce" + integrity sha512-NDzYHRoShpvLqNcrgV8ZQh61sMIFAry5KLTQV83BPG5iTXCCu7h72SCfJ97bW0GqtuqDD/1aqLbKinI/rNgUsg== + +esbuild@^0.16.7: + version "0.16.17" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259" + integrity sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg== + optionalDependencies: + "@esbuild/android-arm" "0.16.17" + "@esbuild/android-arm64" "0.16.17" + "@esbuild/android-x64" "0.16.17" + "@esbuild/darwin-arm64" "0.16.17" + "@esbuild/darwin-x64" "0.16.17" + "@esbuild/freebsd-arm64" "0.16.17" + "@esbuild/freebsd-x64" "0.16.17" + "@esbuild/linux-arm" "0.16.17" + "@esbuild/linux-arm64" "0.16.17" + "@esbuild/linux-ia32" "0.16.17" + "@esbuild/linux-loong64" "0.16.17" + "@esbuild/linux-mips64el" "0.16.17" + "@esbuild/linux-ppc64" "0.16.17" + "@esbuild/linux-riscv64" "0.16.17" + "@esbuild/linux-s390x" "0.16.17" + "@esbuild/linux-x64" "0.16.17" + "@esbuild/netbsd-x64" "0.16.17" + "@esbuild/openbsd-x64" "0.16.17" + "@esbuild/sunos-x64" "0.16.17" + "@esbuild/win32-arm64" "0.16.17" + "@esbuild/win32-ia32" "0.16.17" + "@esbuild/win32-x64" "0.16.17" + +esbuild@^0.18.10: + version "0.18.20" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6" + integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA== optionalDependencies: - esbuild-android-arm64 "0.13.15" - esbuild-darwin-64 "0.13.15" - esbuild-darwin-arm64 "0.13.15" - esbuild-freebsd-64 "0.13.15" - esbuild-freebsd-arm64 "0.13.15" - esbuild-linux-32 "0.13.15" - esbuild-linux-64 "0.13.15" - esbuild-linux-arm "0.13.15" - esbuild-linux-arm64 "0.13.15" - esbuild-linux-mips64le "0.13.15" - esbuild-linux-ppc64le "0.13.15" - esbuild-netbsd-64 "0.13.15" - esbuild-openbsd-64 "0.13.15" - esbuild-sunos-64 "0.13.15" - esbuild-windows-32 "0.13.15" - esbuild-windows-64 "0.13.15" - esbuild-windows-arm64 "0.13.15" + "@esbuild/android-arm" "0.18.20" + "@esbuild/android-arm64" "0.18.20" + "@esbuild/android-x64" "0.18.20" + "@esbuild/darwin-arm64" "0.18.20" + "@esbuild/darwin-x64" "0.18.20" + "@esbuild/freebsd-arm64" "0.18.20" + "@esbuild/freebsd-x64" "0.18.20" + "@esbuild/linux-arm" "0.18.20" + "@esbuild/linux-arm64" "0.18.20" + "@esbuild/linux-ia32" "0.18.20" + "@esbuild/linux-loong64" "0.18.20" + "@esbuild/linux-mips64el" "0.18.20" + "@esbuild/linux-ppc64" "0.18.20" + "@esbuild/linux-riscv64" "0.18.20" + "@esbuild/linux-s390x" "0.18.20" + "@esbuild/linux-x64" "0.18.20" + "@esbuild/netbsd-x64" "0.18.20" + "@esbuild/openbsd-x64" "0.18.20" + "@esbuild/sunos-x64" "0.18.20" + "@esbuild/win32-arm64" "0.18.20" + "@esbuild/win32-ia32" "0.18.20" + "@esbuild/win32-x64" "0.18.20" escalade@^3.1.1: version "3.1.1" @@ -5629,24 +5446,6 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-airbnb-base@^14.2.1: - version "14.2.1" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz#8a2eb38455dc5a312550193b319cdaeef042cd1e" - integrity sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA== - dependencies: - confusing-browser-globals "^1.0.10" - object.assign "^4.1.2" - object.entries "^1.1.2" - -eslint-config-airbnb@^18.0.1: - version "18.2.1" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz#b7fe2b42f9f8173e825b73c8014b592e449c98d9" - integrity sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg== - dependencies: - eslint-config-airbnb-base "^14.2.1" - object.assign "^4.1.2" - object.entries "^1.1.2" - eslint-config-prettier@^6.7.0: version "6.15.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9" @@ -5797,14 +5596,6 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" @@ -5812,13 +5603,6 @@ eslint-utils@^2.1.0: dependencies: eslint-visitor-keys "^1.1.0" -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" @@ -5829,7 +5613,12 @@ eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@^7.28.0: +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^7.32.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== @@ -5901,7 +5690,7 @@ esquery@^1.4.0: dependencies: estraverse "^5.1.0" -esrecurse@^4.1.0, esrecurse@^4.3.0: +esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== @@ -5918,6 +5707,16 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +estree-walker@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== + +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -5933,29 +5732,11 @@ eventemitter3@^3.1.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== -eventemitter3@^4.0.0, eventemitter3@^4.0.7: +eventemitter3@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -eventsource@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" - integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - execa@^0.6.0: version "0.6.3" resolved "https://registry.yarnpkg.com/execa/-/execa-0.6.3.tgz#57b69a594f081759c69e5370f0d17b9cb11658fe" @@ -5982,11 +5763,6 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -exit-on-epipe@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" - integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== - expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -6000,14 +5776,7 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw== - dependencies: - homedir-polyfill "^1.0.1" - -express@^4.16.3, express@^4.17.1: +express@~4.18.2: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== @@ -6044,42 +5813,6 @@ express@^4.16.3, express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" -express@~4.17.3: - version "4.17.3" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" - integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.19.2" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.4.2" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.9.7" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.17.2" - serve-static "1.14.2" - setprototypeof "1.2.0" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -6161,6 +5894,17 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" +fast-glob@^3.2.11: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" @@ -6196,13 +5940,6 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -faye-websocket@^0.11.3, faye-websocket@^0.11.4: - version "0.11.4" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== - dependencies: - websocket-driver ">=0.5.1" - fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -6215,6 +5952,11 @@ fflate@0.7.4: resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.4.tgz#61587e5d958fdabb5a9368a302c25363f4f69f50" integrity sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw== +fflate@^0.8.0: + version "0.8.1" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.1.tgz#1ed92270674d2ad3c73f077cd0acf26486dae6c9" + integrity sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ== + figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" @@ -6227,6 +5969,13 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -6242,15 +5991,12 @@ file-loader@^1.1.11: loader-utils "^1.0.2" schema-utils "^0.4.5" -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -filesize@^3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" - integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" fill-range@^4.0.0: version "4.0.0" @@ -6287,29 +6033,7 @@ finalhandler@1.2.0: statuses "2.0.1" unpipe "~1.0.0" -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - -find-cache-dir@^3.2.0, find-cache-dir@^3.3.1: +find-cache-dir@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== @@ -6355,15 +6079,13 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" + locate-path "^6.0.0" + path-exists "^4.0.0" flat-cache@^3.0.4: version "3.0.4" @@ -6373,24 +6095,19 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" -flatbuffers@1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/flatbuffers/-/flatbuffers-1.12.0.tgz#72e87d1726cb1b216e839ef02658aa87dcef68aa" - integrity sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ== - -flatbuffers@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/flatbuffers/-/flatbuffers-2.0.4.tgz#034456e29ec480de48bad34f7fc18c03f20c9768" - integrity sha512-4rUFVDPjSoP0tOII34oQf+72NKU7E088U5oX7kwICahft0UB2kOQ9wUzzCp+OHxByERIfxRDCgX5mP8Pjkfl0g== +flatbuffers@23.5.26: + version "23.5.26" + resolved "https://registry.yarnpkg.com/flatbuffers/-/flatbuffers-23.5.26.tgz#01358e272a61239f0faf3bfbe4e014f3ace9d746" + integrity sha512-vE+SI9vrJDwi1oETtTIFldC/o9GsVKRM+s6EL0nQgxXlYV1Vc4Tk30hj4xGICftInKQKj1F3up2n8UbIVobISQ== -flatgeobuf@3.6.5: - version "3.6.5" - resolved "https://registry.yarnpkg.com/flatgeobuf/-/flatgeobuf-3.6.5.tgz#756b9c0c2beb4acea4ea0a1ee1caada98463320b" - integrity sha512-qROJAh5iC0ueDBB4Q7WRL11mTmueinjAfMjgirxV8JI5JK4QV2Uht1naJVY3AP5+QSHhyxSnVuZl6MLapLiXzw== +flatgeobuf@3.27.0: + version "3.27.0" + resolved "https://registry.yarnpkg.com/flatgeobuf/-/flatgeobuf-3.27.0.tgz#1c1834661c5a6ecfbae78ff8369bc244a65d12b0" + integrity sha512-UcCmsxBZD/mBjWryLfFjaPkeIpvjJbEr1wGVaWeYfF76NGsZ53KlEZ5hQP9NRt615vuymp/OKBlTqvAU/Zx19w== dependencies: - flatbuffers "1.12.0" + "@repeaterjs/repeater" "3.0.4" + flatbuffers "23.5.26" slice-source "0.4.1" - stream-buffers "3.0.2" flatted@^3.1.0: version "3.2.7" @@ -6405,11 +6122,6 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -follow-redirects@^1.0.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== - for-each@^0.3.3, for-each@~0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -6474,16 +6186,20 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" -fromentries@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" - integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== - fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@^10.0.1: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + 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" @@ -6520,20 +6236,17 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" +fs@^0.0.1-security: + version "0.0.1-security" + resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4" + integrity sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w== fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -function-bind@^1.0.2, function-bind@^1.1.1, function-bind@~1.1.1: +function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== @@ -6548,6 +6261,16 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -6618,12 +6341,22 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has "^1.0.3" has-symbols "^1.0.3" +get-intrinsic@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-pixels@^3.3.2: +get-pixels@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/get-pixels/-/get-pixels-3.3.3.tgz#71e2dfd4befb810b5478a61c6354800976ce01c7" integrity sha512-5kyGBn90i9tSMUVHTqkgCHsoWoR+/lGbl4yC83Gefyr0HLIhgSWEx/2F/3YgsZ7UpYNuM6pDhDK7zebrUJ5nXg== @@ -6791,7 +6524,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig== -glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: +glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.2.0, glob@~7.2.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -6803,54 +6536,6 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@~7.1.4: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-modules@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg== - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== - dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -6870,7 +6555,7 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globby@^11.0.3: +globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -6882,17 +6567,6 @@ globby@^11.0.3: merge2 "^1.4.1" slash "^3.0.0" -globby@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" - integrity sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw== - dependencies: - array-union "^1.0.1" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" @@ -6919,25 +6593,17 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -gzip-size@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" - integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== - dependencies: - duplexer "^0.1.1" - pify "^4.0.1" +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== hammerjs@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" integrity sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ== -handle-thing@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" - integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== - -handlebars@^4.1.2, handlebars@^4.7.6: +handlebars@^4.7.6: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== @@ -7049,57 +6715,11 @@ has@^1.0.1, has@^1.0.3, has@~1.0.3: dependencies: function-bind "^1.1.1" -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -hasha@^5.0.0: - version "5.2.2" - resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" - integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ== - dependencies: - is-stream "^2.0.0" - type-fest "^0.8.0" - -he@1.2.x: +he@1.2.0, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - -hoopy@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" - integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== - hosted-git-info@^2.1.4, hosted-git-info@^2.7.1: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -7112,83 +6732,29 @@ hosted-git-info@^4.0.1: dependencies: lru-cache "^6.0.0" -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== - dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" - -html-entities@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc" - integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA== - html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -html-minifier@^3.2.3: - version "3.5.21" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" - integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== - dependencies: - camel-case "3.0.x" - clean-css "4.2.x" - commander "2.17.x" - he "1.2.x" - param-case "2.1.x" - relateurl "0.2.x" - uglify-js "3.4.x" - -html-webpack-plugin@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b" - integrity sha512-Br4ifmjQojUP4EmHnRBoUIYcZ9J7M4bTMcm7u6xoIAIuq2Nte4TzXX0533owvkQKQD1WeMTTTyD4Ni4QKxS0Bg== - dependencies: - html-minifier "^3.2.3" - loader-utils "^0.2.16" - lodash "^4.17.3" - pretty-error "^2.0.2" - tapable "^1.0.0" - toposort "^1.0.0" - util.promisify "1.0.0" - -htmlparser2@^6.1.0: +html-minifier-terser@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" - integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.5.2" - entities "^2.0.0" + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" http-cache-semantics@^3.8.1: version "3.8.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== - -http-errors@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" - integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.1" - http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -7200,21 +6766,6 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-parser-js@>=0.5.1: - version "0.5.8" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" - integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== - http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" @@ -7223,25 +6774,6 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -http-proxy-middleware@0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" - integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== - dependencies: - http-proxy "^1.17.0" - is-glob "^4.0.0" - lodash "^4.17.11" - micromatch "^3.1.10" - -http-proxy@^1.17.0: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -7251,11 +6783,6 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== - https-proxy-agent@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -7293,7 +6820,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.1.12, ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: +ieee754@^1.1.12, ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -7315,7 +6842,7 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.8, ignore@^5.2.0: +ignore@^5.2.0, ignore@^5.2.4: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== @@ -7394,17 +6921,12 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA== - inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== -ini@^1.3.2, ini@^1.3.4, ini@^1.3.5: +ini@^1.3.2, ini@^1.3.4: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -7442,19 +6964,32 @@ inquirer@^6.2.0: strip-ansi "^5.1.0" through "^2.3.6" +inquirer@^8.0.0: + version "8.2.6" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" + integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + wrap-ansi "^6.0.1" + int53@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/int53/-/int53-0.2.4.tgz#5ed8d7aad6c5c6567cae69aa7ffc4a109ee80f86" integrity sha512-a5jlKftS7HUOhkUyYD7j2sJ/ZnvWiNlZS1ldR+g1ifQ+/UuZXIE+YTc/lK1qGj/GwAU5F8Z0e1eVq2t1J5Ob2g== -internal-ip@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" - integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== - dependencies: - default-gateway "^4.2.0" - ipaddr.js "^1.9.0" - internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" @@ -7464,41 +6999,21 @@ internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5: has "^1.0.3" side-channel "^1.0.4" -interpret@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - iota-array@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/iota-array/-/iota-array-1.0.0.tgz#81ef57fe5d05814cd58c2483632a99c30a0e8087" integrity sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA== -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw== - ip@1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha512-rBtCAQAJm8A110nbwn6YdveUnuZH3WrC36IwkRXxDnq53JvXA2NVQvB7IHyKomxK1MJ4VDNw3UtFDdXQ+AvLYA== -ip@^1.1.0, ip@^1.1.5: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" - integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== - -ipaddr.js@1.9.1, ipaddr.js@^1.9.0: +ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -is-absolute-url@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" - integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== - is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -7555,13 +7070,6 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q== - dependencies: - binary-extensions "^1.0.0" - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -7601,6 +7109,13 @@ is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.9.0: dependencies: has "^1.0.3" +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -7694,6 +7209,13 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -7713,6 +7235,11 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-map@^2.0.1, is-map@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" @@ -7757,25 +7284,6 @@ is-observable@^2.1.0: resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-2.1.0.tgz#5c8d733a0b201c80dff7bb7c0df58c6a255c7c69" integrity sha512-DailKdLb0WU+xX8K5w7VsJhapwHLZ9jjmazqCJq4X12CTgqq73TKnbRcnSLuXYPOoLQgV5IrD7ePiX/h1vnkBw== -is-path-cwd@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== - -is-path-in-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" - integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== - dependencies: - is-path-inside "^2.1.0" - -is-path-inside@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" - integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== - dependencies: - path-is-inside "^1.0.2" - is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -7798,7 +7306,7 @@ is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== -is-regex@^1.0.4, is-regex@^1.1.4: +is-regex@^1.0.4, is-regex@^1.1.4, is-regex@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== @@ -7830,11 +7338,6 @@ is-stream@^1.1.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -7867,11 +7370,23 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" -is-typedarray@^1.0.0, is-typedarray@~1.0.0: +is-typed-array@^1.1.12, is-typed-array@^1.1.3: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + +is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -7897,16 +7412,11 @@ is-weakset@^2.0.1: call-bind "^1.0.2" get-intrinsic "^1.1.1" -is-windows@^1.0.0, is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.0, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -7954,23 +7464,6 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-hook@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz#8f84c9434888cc6b1d0a9d7092a76d239ebf0cc6" - integrity sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ== - dependencies: - append-transform "^2.0.0" - -istanbul-lib-instrument@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== - dependencies: - "@babel/core" "^7.7.5" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - istanbul-lib-instrument@^5.0.4: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" @@ -7982,18 +7475,6 @@ istanbul-lib-instrument@^5.0.4: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" -istanbul-lib-processinfo@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz#366d454cd0dcb7eb6e0e419378e60072c8626169" - integrity sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg== - dependencies: - archy "^1.0.0" - cross-spawn "^7.0.3" - istanbul-lib-coverage "^3.2.0" - p-map "^3.0.0" - rimraf "^3.0.0" - uuid "^8.3.2" - istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -8003,23 +7484,24 @@ istanbul-lib-report@^3.0.0: make-dir "^3.0.0" supports-color "^7.1.0" -istanbul-lib-source-maps@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.0.2: - version "3.1.5" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" - integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== +istanbul-reports@^3.1.4: + version "3.1.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" + integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jake@^10.8.5: + version "10.8.7" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" + integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + jpeg-js@^0.4.1, jpeg-js@^0.4.3: version "0.4.4" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa" @@ -8070,7 +7552,7 @@ json-map-transform@^1.2.6: resolved "https://registry.yarnpkg.com/json-map-transform/-/json-map-transform-1.2.6.tgz#275295cdf0fa09d9d9536542854ef5e74cdd59ea" integrity sha512-QyIKkQSqxo8lmwoU6fjcH9Ygp44XlSpnMiNa5qKkwsVggzqS3LfOOoICEYNWsin9R/3wGeFB/GIO7MLLNWzV7Q== -json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: +json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== @@ -8105,11 +7587,6 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== -json5@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - integrity sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw== - json5@^1.0.1, json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" @@ -8129,6 +7606,15 @@ jsonfile@^4.0.0: 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" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -8167,11 +7653,6 @@ jszip@^3.1.5, jszip@^3.5.0: readable-stream "~2.3.6" setimmediate "^1.0.5" -killable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" - integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== - kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -8213,6 +7694,11 @@ language-tags@=1.0.5: dependencies: language-subtag-registry "~0.3.2" +laz-perf@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/laz-perf/-/laz-perf-0.0.6.tgz#a8bfad45f7f02695af631ca374d080528da4ba51" + integrity sha512-ZBqC+BBlofznDIY3SfjXDBVdIhYfz7bq8HAHztlw4XOnu++nHiWtCGPgzpdeAhPkByc68DaKNy3E3rY4XrdRtQ== + lazystream@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" @@ -8311,22 +7797,7 @@ load-json-file@^5.3.0: strip-bom "^3.0.0" type-fest "^0.3.0" -loader-runner@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@^0.2.16: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" - integrity sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug== - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - -loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: +loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.4.0: version "1.4.2" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== @@ -8358,11 +7829,23 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA== +lodash.assignwith@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz#127a97f02adc41751a954d24b0de17e100e038eb" + integrity sha512-ZznplvbvtjK2gMvnQ1BR/zqPFZmS6jbK4p+6Up4xcRYA7yMIwxHCfbTcrYxXKzzqLsQ05eJPVznEW3tuwV7k1g== + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -8393,11 +7876,6 @@ lodash.flatten@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== -lodash.flattendeep@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" - integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== - lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -8458,7 +7936,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.2.1: +lodash@^4.17.12, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.2.1: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8468,10 +7946,13 @@ log-driver@^1.2.7: resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== -loglevel@^1.6.8: - version "1.8.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" - integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" long@^5.2.1: version "5.2.3" @@ -8493,10 +7974,12 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lower-case@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" - integrity sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA== +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" lru-cache@^4.0.1: version "4.1.5" @@ -8535,6 +8018,13 @@ macos-release@^2.2.0: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.1.tgz#bccac4a8f7b93163a8d163b8ebf385b3c5f55bf9" integrity sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A== +magic-string@^0.25.3: + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" + make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -8542,7 +8032,7 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-dir@^2.0.0, make-dir@^2.1.0: +make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== @@ -8613,15 +8103,6 @@ math.gl@^3.6.2: dependencies: "@math.gl/core" "3.6.3" -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - md5@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" @@ -8652,22 +8133,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== -memory-fs@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" - integrity sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -memory-fs@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" - integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -8744,7 +8209,7 @@ micromark@~2.11.0: debug "^4.0.0" parse-entities "^2.0.0" -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.10: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -8771,20 +8236,12 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": +mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.0.1, mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.0.1, mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -8796,31 +8253,21 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.4.4: - version "2.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== - minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -8828,7 +8275,7 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^5.1.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" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== @@ -8852,7 +8299,7 @@ minimist-options@^3.0.1: arrify "^1.0.1" is-plain-obj "^1.1.0" -minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.0: +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -8926,7 +8373,7 @@ mkdirp@*: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@^0.5.6: +mkdirp@^0.5.1, mkdirp@^0.5.5: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -8938,11 +8385,6 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -module-alias@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0" - integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q== - morgan@~1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.1.tgz#0a8d16734a1d9afbc824b99df87e738e58e2da59" @@ -8981,19 +8423,6 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multicast-dns-service-types@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" - integrity sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ== - -multicast-dns@^6.0.1: - version "6.2.3" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" - integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== - dependencies: - dns-packet "^1.3.1" - thunky "^1.0.2" - multimatch@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-3.0.0.tgz#0e2534cc6bc238d9ab67e1b9cd5fcd85a6dbf70b" @@ -9009,7 +8438,7 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ== -mute-stream@~0.0.4: +mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== @@ -9023,10 +8452,10 @@ mz@^2.5.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.12.1: - version "2.17.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" - integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== nanomatch@^1.2.9: version "1.2.13" @@ -9065,7 +8494,7 @@ ndarray-pack@^1.1.1: cwise-compiler "^1.1.2" ndarray "^1.0.13" -ndarray@^1.0.13, ndarray@^1.0.18: +ndarray@^1.0.13, ndarray@^1.0.18, ndarray@^1.0.19: version "1.0.19" resolved "https://registry.yarnpkg.com/ndarray/-/ndarray-1.0.19.tgz#6785b5f5dfa58b83e31ae5b2a058cfd1ab3f694e" integrity sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ== @@ -9078,7 +8507,7 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: +neo-async@^2.6.0: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -9088,12 +8517,13 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -no-case@^2.2.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" - integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== dependencies: - lower-case "^1.1.1" + lower-case "^2.0.2" + tslib "^2.0.3" node-bitmap@0.0.1: version "0.0.1" @@ -9123,11 +8553,6 @@ node-fetch@^2.5.0, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -node-forge@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" - integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== - node-gyp@^5.0.2: version "5.1.1" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" @@ -9145,47 +8570,19 @@ node-gyp@^5.0.2: tar "^4.4.12" which "^1.3.1" +node-html-parser@^5.3.3: + version "5.4.2" + resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-5.4.2.tgz#93e004038c17af80226c942336990a0eaed8136a" + integrity sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw== + dependencies: + css-select "^4.2.1" + he "1.2.0" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-libs-browser@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - -node-preload@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" - integrity sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ== - dependencies: - process-on-spawn "^1.0.0" - node-releases@^2.0.8: version "2.0.10" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" @@ -9219,13 +8616,6 @@ normalize-package-data@^3.0.0: semver "^7.3.4" validate-npm-package-license "^3.0.1" -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== - dependencies: - remove-trailing-separator "^1.0.1" - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -9324,39 +8714,6 @@ numcodecs@^0.2.2: resolved "https://registry.yarnpkg.com/numcodecs/-/numcodecs-0.2.2.tgz#33d8b72e7223f3c80d67b9007720b9b54c62c240" integrity sha512-Y5K8mv80yb4MgVpcElBkUeMZqeE4TrovxRit/dTZvoRl6YkB6WEjY+fiUjGCblITnt3T3fmrDg8yRWu0gOLjhQ== -nyc@^15.0.0: - version "15.1.0" - resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02" - integrity sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A== - dependencies: - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - caching-transform "^4.0.0" - convert-source-map "^1.7.0" - decamelize "^1.2.0" - find-cache-dir "^3.2.0" - find-up "^4.1.0" - foreground-child "^2.0.0" - get-package-type "^0.1.0" - glob "^7.1.6" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-hook "^3.0.0" - istanbul-lib-instrument "^4.0.0" - istanbul-lib-processinfo "^2.0.2" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - make-dir "^3.0.0" - node-preload "^0.2.1" - p-map "^3.0.0" - process-on-spawn "^1.0.0" - resolve-from "^5.0.0" - rimraf "^3.0.0" - signal-exit "^3.0.2" - spawn-wrap "^2.0.0" - test-exclude "^6.0.0" - yargs "^15.0.2" - oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -9376,16 +8733,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.12.3, object-inspect@^1.9.0: +object-inspect@^1.12.3, object-inspect@^1.9.0, object-inspect@~1.12.2: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== -object-inspect@~1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" - integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== - object-is@^1.0.1, object-is@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" @@ -9411,7 +8763,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.0.4, object.assign@^4.1.2, object.assign@^4.1.3, object.assign@^4.1.4: +object.assign@^4.0.4, object.assign@^4.1.3, object.assign@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== @@ -9421,7 +8773,7 @@ object.assign@^4.0.4, object.assign@^4.1.2, object.assign@^4.1.3, object.assign@ has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.2, object.entries@^1.1.6: +object.entries@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== @@ -9479,44 +8831,40 @@ observable-fns@^0.6.1: resolved "https://registry.yarnpkg.com/observable-fns/-/observable-fns-0.6.1.tgz#636eae4fdd1132e88c0faf38d33658cc79d87e37" integrity sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg== -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== - octokit-pagination-methods@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" integrity sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ== -ocular-dev-tools@1.0.0-alpha.7: - version "1.0.0-alpha.7" - resolved "https://registry.yarnpkg.com/ocular-dev-tools/-/ocular-dev-tools-1.0.0-alpha.7.tgz#1defc262b67bb564821c96ced89af369127ca7a0" - integrity sha512-UDCNcDjbpiI7jDTodSasdrW0D2YveeDNEVS2ktEIEYq30Ao8Wr5m7DpVWcrkzHA/nF1qbetax1kqFiJVhtr5fA== +ocular-dev-tools@2.0.0-alpha.18: + version "2.0.0-alpha.18" + resolved "https://registry.yarnpkg.com/ocular-dev-tools/-/ocular-dev-tools-2.0.0-alpha.18.tgz#6483afe6a54cb0293eba5487db798daed942e810" + integrity sha512-szLVQ/ViYU7RF0TvJ1NNCI3IxAvKavxZw/iltULUJsPLcJPxhcIEbx0e/SQTc97JbgGkxWcWmdWmpzthxuPSHg== dependencies: "@babel/cli" "^7.14.5" "@babel/core" "^7.14.5" "@babel/eslint-parser" "^7.14.5" - "@babel/plugin-proposal-class-properties" "^7.14.5" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.2" - "@babel/plugin-proposal-optional-chaining" "^7.14.2" "@babel/plugin-transform-runtime" "^7.14.5" "@babel/preset-env" "^7.14.5" "@babel/preset-react" "^7.14.5" "@babel/preset-typescript" "^7.14.5" - "@babel/register" "^7.14.5" "@babel/runtime" "7.14.5" - "@probe.gl/test-utils" "^3.0.2" + "@esbuild-plugins/node-globals-polyfill" "^0.2.0" + "@esbuild-plugins/node-modules-polyfill" "^0.2.0" + "@probe.gl/test-utils" "^4.0.3" "@typescript-eslint/eslint-plugin" "^4.26.1" "@typescript-eslint/parser" "^4.26.1" babel-loader "8.2.2" + babel-plugin-add-import-extension "^1.6.0" babel-plugin-istanbul "^6.0.0" babel-plugin-version-inline "^1.0.0" - core-js "^3.2.1" + c8 "^7.12.0" coveralls "^3.0.3" deepmerge "^4.2.2" - eslint "^7.28.0" - eslint-config-airbnb "^18.0.1" + esbuild "^0.16.7" + esbuild-plugin-babel "git+https://github.com/Pessimistress/esbuild-plugin-babel.git#patch-1" + esbuild-plugin-external-global "^1.0.1" + eslint "^7.32.0" eslint-config-prettier "^6.7.0" eslint-config-uber-es2015 "^3.0.0" eslint-config-uber-jsx "^3.3.3" @@ -9527,24 +8875,17 @@ ocular-dev-tools@1.0.0-alpha.7: eslint-plugin-react "^7.22.0" eslint-plugin-react-hooks "^4.0.0" glob "^7.1.4" - handlebars "^4.1.2" - html-webpack-plugin "^3.2.0" lerna "^3.14.1" - lodash "^4.17.13" - lodash.template "^4.5.0" - module-alias "^2.0.0" - nyc "^15.0.0" - prettier "2.3.1" + prettier "2.4.1" prettier-check "2.0.0" - source-map-loader "^0.2.3" - source-map-support "^0.5.12" - tape "4.11.0" + tape "^4.11.0" tape-promise "^4.0.0" - typescript "^4.3.4" - webpack "^4.28.4" - webpack-bundle-analyzer "^3.0.3" - webpack-cli "^3.2.1" - webpack-dev-server "^3.1.14" + ts-node "~10.9.0" + tsconfig-paths "^4.1.1" + typescript "~4.6.0" + url "^0.11.0" + vite "^4.0.1" + vite-plugin-html "^3.2.0" omggif@^1.0.5: version "1.0.10" @@ -9565,7 +8906,7 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -on-headers@~1.0.1, on-headers@~1.0.2: +on-headers@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== @@ -9584,17 +8925,12 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -opener@^1.5.1: - version "1.5.2" - resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" - integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== - -opn@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" - integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: - is-wsl "^1.1.0" + mimic-fn "^2.1.0" optionator@^0.9.1: version "0.9.1" @@ -9608,10 +8944,20 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" os-homedir@^1.0.0: version "1.0.2" @@ -9663,6 +9009,13 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -9684,6 +9037,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map-series@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" @@ -9691,18 +9051,11 @@ p-map-series@^1.0.0: dependencies: p-reduce "^1.0.0" -p-map@^2.0.0, p-map@^2.1.0: +p-map@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== -p-map@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" - integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== - dependencies: - aggregate-error "^3.0.0" - p-pipe@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" @@ -9728,13 +9081,6 @@ p-reduce@^1.0.0: resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" integrity sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ== -p-retry@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" - integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== - dependencies: - retry "^0.12.0" - p-timeout@^5.0.2: version "5.1.0" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-5.1.0.tgz#b3c691cf4415138ce2d9cfe071dba11f0fee085b" @@ -9757,16 +9103,6 @@ p-waterfall@^1.0.0: dependencies: p-reduce "^1.0.0" -package-hash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" - integrity sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ== - dependencies: - graceful-fs "^4.1.15" - hasha "^5.0.0" - lodash.flattendeep "^4.4.0" - release-zalgo "^1.0.0" - pad-left@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/pad-left/-/pad-left-2.1.0.tgz#16e6a3b2d44a8e138cb0838cc7cb403a4fc9e994" @@ -9774,7 +9110,7 @@ pad-left@^2.1.0: dependencies: repeat-string "^1.5.4" -pako@1.0.11, pako@~1.0.2, pako@~1.0.5: +pako@1.0.11, pako@~1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== @@ -9793,12 +9129,13 @@ parallel-transform@^1.1.0: inherits "^2.0.3" readable-stream "^2.1.5" -param-case@2.1.x: - version "2.1.1" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" - integrity sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w== +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== dependencies: - no-case "^2.2.0" + dot-case "^3.0.4" + tslib "^2.0.3" parent-module@^1.0.0: version "1.0.1" @@ -9812,17 +9149,6 @@ parquet-wasm@^0.3.1: resolved "https://registry.yarnpkg.com/parquet-wasm/-/parquet-wasm-0.3.1.tgz#4c7306cac00e1bfacb00ee9e395a9ef8a4c8623f" integrity sha512-8OMYwh7N+IcQBeKTuDgpvJR7xh/YKPgnQcs29CQW0Q4ziwroRu7N6tQmmsjiCaQ/W7enBNppquj6VAe2fA1yYQ== -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.6" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" - integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== - dependencies: - asn1.js "^5.2.0" - browserify-aes "^1.0.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - parse-data-uri@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/parse-data-uri/-/parse-data-uri-0.2.0.tgz#bf04d851dd5c87b0ab238e5d01ace494b604b4c9" @@ -9877,11 +9203,6 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== - parse-path@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea" @@ -9902,21 +9223,24 @@ parse-url@^6.0.0: parse-path "^4.0.0" protocols "^1.4.0" -parseurl@~1.3.2, parseurl@~1.3.3: +parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -9944,11 +9268,6 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== - path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -9959,7 +9278,7 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6, path-parse@^1.0.7: +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -9990,6 +9309,19 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path@^0.12.7: + version "0.12.7" + resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" + integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== + dependencies: + process "^0.11.1" + util "^0.10.3" + +pathe@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-0.2.0.tgz#30fd7bbe0a0d91f0e60bae621f5d19e9e225c339" + integrity sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw== + pbf@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.2.1.tgz#b4c1b9e72af966cd82c6531691115cc0409ffe2a" @@ -9998,17 +9330,6 @@ pbf@^3.2.1: ieee754 "^1.1.12" resolve-protobuf-schema "^2.1.0" -pbkdf2@^3.0.3: - version "3.1.2" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" - integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -10024,7 +9345,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -10056,11 +9377,6 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== -pirates@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== - pixelmatch@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-4.0.2.tgz#8f47dcec5011b477b67db03c243bc1f3085e8854" @@ -10082,6 +9398,13 @@ pkg-dir@^4.1.0: dependencies: find-up "^4.0.0" +pmtiles@^2.7.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/pmtiles/-/pmtiles-2.10.0.tgz#894e2954723924add7dd9637c89c0329ffe62501" + integrity sha512-X+s6JyperpcAkKwv55MKx72ckOUB0ZjcfK4929iM0SS0MkLydEi2FSW1E8YTE1E2XaZ2TVk/MIUrbsZuXV7K2g== + dependencies: + fflate "^0.8.0" + pngjs-nozlib@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/pngjs-nozlib/-/pngjs-nozlib-1.0.0.tgz#9e64d602cfe9cce4d9d5997d0687429a73f0b7d7" @@ -10092,20 +9415,20 @@ pngjs@^3.0.0, pngjs@^3.3.3: resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== -portfinder@^1.0.26: - version "1.0.32" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81" - integrity sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg== - dependencies: - async "^2.6.4" - debug "^3.2.7" - mkdirp "^0.5.6" - posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== +postcss@^8.4.27: + version "8.4.31" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + pre-commit@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/pre-commit/-/pre-commit-1.2.2.tgz#dbcee0ee9de7235e57f79c56d7ce94641a69eec6" @@ -10136,37 +9459,17 @@ prettier-check@2.0.0: dependencies: execa "^0.6.0" -prettier@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" - integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA== - -pretty-error@^2.0.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6" - integrity sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw== - dependencies: - lodash "^4.17.20" - renderkid "^2.0.4" - -printj@~1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" - integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== +prettier@2.4.1, prettier@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643" + integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg== process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process-on-spawn@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" - integrity sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg== - dependencies: - fromentries "^1.2.0" - -process@^0.11.10: +process@^0.11.1: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== @@ -10253,11 +9556,6 @@ proxy-from-env@1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" - integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== - pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -10268,18 +9566,6 @@ psl@^1.1.28: resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - pump@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" @@ -10310,11 +9596,6 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== -punycode@^1.2.4: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== - punycode@^2.1.0, punycode@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" @@ -10361,11 +9642,6 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" -qs@6.9.7: - version "6.9.7" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" - integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== - qs@^6.9.4: version "6.11.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" @@ -10388,21 +9664,11 @@ query-string@^6.13.8: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" -querystring-es3@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== - querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -10423,36 +9689,11 @@ random-js@1.0.2: resolved "https://registry.yarnpkg.com/random-js/-/random-js-1.0.2.tgz#a0104882faa6da43922fefda658330c49d232ce7" integrity sha512-IIsnMV4J4+K0CY0bDRKJTTlGwy9KUEqiqDJekfBq5iwUSa1l2C5ssml5fmP8YVS17Pry7VJbsHBebDWjcIdheQ== -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@^1.2.1, range-parser@~1.2.1: +range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" - integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== - dependencies: - bytes "3.1.2" - http-errors "1.8.1" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-body@2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" @@ -10554,7 +9795,7 @@ read@1, read@~1.0.1: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -10567,7 +9808,7 @@ read@1, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -10613,15 +9854,6 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -10653,11 +9885,6 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -reduce-flatten@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" - integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== - regenerate-unicode-properties@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" @@ -10690,7 +9917,16 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" + +regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== @@ -10723,34 +9959,11 @@ regjsparser@^0.9.1: dependencies: jsesc "~0.5.0" -relateurl@0.2.x: +relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== -release-zalgo@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" - integrity sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA== - dependencies: - es6-error "^4.0.1" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== - -renderkid@^2.0.4: - version "2.0.7" - resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.7.tgz#464f276a6bdcee606f4a15993f9b29fc74ca8609" - integrity sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ== - dependencies: - css-select "^4.1.3" - dom-converter "^0.2.0" - htmlparser2 "^6.1.0" - lodash "^4.17.21" - strip-ansi "^3.0.1" - repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" @@ -10809,11 +10022,6 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -10821,14 +10029,6 @@ resolve-cwd@^2.0.0: dependencies: resolve-from "^3.0.0" -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg== - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -10874,12 +10074,14 @@ resolve@^2.0.0-next.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@~1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" - integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== +resolve@~1.22.1: + version "1.22.6" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" + integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== dependencies: - path-parse "^1.0.6" + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" restore-cursor@^2.0.0: version "2.0.0" @@ -10889,6 +10091,14 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + resumer@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" @@ -10906,11 +10116,6 @@ retry@^0.10.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" integrity sha512-ZXUSQYTHdl3uS7IuCehYfMzKyIDBNoAuUblvy5oGO5UJSUTmStUUVPXbA9Qxd173Bgre53yCQczQuHgRWAdvJQ== -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -10923,22 +10128,44 @@ rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.0, rimraf@^3.0.2: +rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== +rollup-plugin-inject@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz#e4233855bfba6c0c12a312fd6649dff9a13ee9f4" + integrity sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w== dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" + estree-walker "^0.6.1" + magic-string "^0.25.3" + rollup-pluginutils "^2.8.1" + +rollup-plugin-node-polyfills@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz#53092a2744837164d5b8a28812ba5f3ff61109fd" + integrity sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA== + dependencies: + rollup-plugin-inject "^3.0.0" + +rollup-pluginutils@^2.8.1: + version "2.8.2" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== + dependencies: + estree-walker "^0.6.1" -run-async@^2.2.0: +rollup@^3.27.1: + version "3.29.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.4.tgz#4d70c0f9834146df8705bfb69a9a19c9e1109981" + integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw== + optionalDependencies: + fsevents "~2.3.2" + +run-async@^2.2.0, run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== @@ -10969,6 +10196,13 @@ rxjs@^6.4.0: dependencies: tslib "^1.9.0" +rxjs@^7.5.5: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-array-concat@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" @@ -10979,12 +10213,22 @@ safe-array-concat@^1.0.0: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" +safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -11010,7 +10254,7 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -save-pixels@^2.3.2: +save-pixels@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/save-pixels/-/save-pixels-2.3.6.tgz#d9f40a2f6b374f9c61d14f1b7def2591db7fb780" integrity sha512-/ayfEWBxt0tFpf5lxSU1S0+/TBn7EiaTZD+6GL+mwizHm3BKCBysnzT6Js7BusDUVcNVLkeJJKLZcBgdpM2leQ== @@ -11031,15 +10275,6 @@ schema-utils@^0.4.5: ajv "^6.1.0" ajv-keywords "^3.1.0" -schema-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" - integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== - dependencies: - ajv "^6.1.0" - ajv-errors "^1.0.0" - ajv-keywords "^3.1.0" - schema-utils@^2.6.5: version "2.7.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" @@ -11049,54 +10284,23 @@ schema-utils@^2.6.5: ajv "^6.12.4" ajv-keywords "^3.5.2" -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== - -selfsigned@^1.10.8: - version "1.10.14" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.14.tgz#ee51d84d9dcecc61e07e4aba34f229ab525c1574" - integrity sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA== - dependencies: - node-forge "^0.10.0" - "semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.2.1, semver@^7.3.4, semver@^7.3.5: - version "7.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" - integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== +semver@^7.2.1, semver@^7.3.4, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" -send@0.17.2: - version "0.17.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" - integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "1.8.1" - mime "1.6.0" - ms "2.1.3" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -11116,36 +10320,6 @@ send@0.18.0: range-parser "~1.2.1" statuses "2.0.1" -serialize-javascript@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== - dependencies: - randombytes "^2.1.0" - -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== - dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" - -serve-static@1.14.2: - version "1.14.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" - integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.2" - serve-static@1.15.0: version "1.15.0" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" @@ -11161,6 +10335,15 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -11171,29 +10354,16 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -setimmediate@^1.0.4, setimmediate@^1.0.5: +setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -11308,26 +10478,6 @@ snappyjs@^0.6.0, snappyjs@^0.6.1: resolved "https://registry.yarnpkg.com/snappyjs/-/snappyjs-0.6.1.tgz#9bca9ff8c54b133a9cc84a71d22779e97fc51878" integrity sha512-YIK6I2lsH072UE0aOFxxY1dPDCS43I5ktqHpeAsuLNYWkE5pGxRGWfDM4/vSUfNzXjC1Ivzt3qx31PCLmc9yqg== -sockjs-client@^1.5.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.6.1.tgz#350b8eda42d6d52ddc030c39943364c11dcad806" - integrity sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw== - dependencies: - debug "^3.2.7" - eventsource "^2.0.2" - faye-websocket "^0.11.4" - inherits "^2.0.4" - url-parse "^1.5.10" - -sockjs@^0.3.21: - version "0.3.24" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" - integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== - dependencies: - faye-websocket "^0.11.3" - uuid "^8.3.2" - websocket-driver "^0.7.4" - socks-proxy-agent@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" @@ -11351,18 +10501,10 @@ sort-keys@^2.0.0: dependencies: is-plain-obj "^1.0.0" -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - -source-map-loader@^0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.4.tgz#c18b0dc6e23bf66f6792437557c569a11e072271" - integrity sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ== - dependencies: - async "^2.5.0" - loader-utils "^1.1.0" +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== source-map-resolve@^0.5.0: version "0.5.3" @@ -11375,7 +10517,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.12, source-map-support@^0.5.16, source-map-support@~0.5.12: +source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -11393,11 +10535,16 @@ source-map@^0.5.6: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + spawn-sync@^1.0.15: version "1.0.15" resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" @@ -11406,18 +10553,6 @@ spawn-sync@^1.0.15: concat-stream "^1.4.7" os-shim "^0.1.2" -spawn-wrap@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" - integrity sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg== - dependencies: - foreground-child "^2.0.0" - is-windows "^1.0.2" - make-dir "^3.0.0" - rimraf "^3.0.0" - signal-exit "^3.0.2" - which "^2.0.1" - spdx-correct@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" @@ -11444,29 +10579,6 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== - dependencies: - debug "^4.1.0" - detect-node "^2.0.4" - hpack.js "^2.1.6" - obuf "^1.1.2" - readable-stream "^3.0.6" - wbuf "^1.7.3" - -spdy@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" - integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== - dependencies: - debug "^4.1.0" - handle-thing "^2.0.0" - http-deceiver "^1.2.7" - select-hose "^2.0.0" - spdy-transport "^3.0.0" - split-on-first@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" @@ -11505,10 +10617,10 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -sql.js@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/sql.js/-/sql.js-1.5.0.tgz#6625c3e41115194f6bfab80a5e53647f1378c3fb" - integrity sha512-Qqr6HgX/hCDpLFWdN0BNoNpYQ2c1tOl1c3HGI0cshjaFSAWszKICuLZ9CyFUvRFPpEGW8RzHzwuXWWvXVGTKBg== +sql.js@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/sql.js/-/sql.js-1.8.0.tgz#cb45d957e17a2239662fe2f614c9b678990867a6" + integrity sha512-3HD8pSkZL+5YvYUI8nlvNILs61ALqq34xgmF+BHpqxe68yZIJ1H+sIVIODvni25+CcxHUxDyrTJUL0lE/m7afw== ssf@~0.11.2: version "0.11.2" @@ -11552,11 +10664,6 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - stop-iteration-iterator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" @@ -11564,19 +10671,6 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-buffers@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" - integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ== - stream-each@^1.1.0: version "1.2.3" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" @@ -11585,26 +10679,20 @@ stream-each@^1.1.0: end-of-stream "^1.1.0" stream-shift "^1.0.0" -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" +stream-read-all@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/stream-read-all/-/stream-read-all-3.0.1.tgz#60762ae45e61d93ba0978cda7f3913790052ad96" + integrity sha512-EWZT9XOceBPlVJRrYcykW8jyRSZYbkb/0ZK36uLEmoWVO5gxBOnntNTseNzfREsqxqdfEGQrD8SXQ3QWbBmq8A== stream-shift@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -stream-to-async-iterator@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/stream-to-async-iterator/-/stream-to-async-iterator-0.2.0.tgz#bef5c885e9524f98b2fa5effecc357bd58483780" - integrity sha512-ACcmP5IdGSq9cIENmcLl+Xe7g3fXIDoxT8p4RwsEakMLG5NZXTg/v+aO9Lu288lFXrou3ubYW+hNGO54HE4t2w== +stream-to-async-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-to-async-iterator/-/stream-to-async-iterator-1.0.0.tgz#d7af183a1fc28655741a1b1810fd008d4b1cd566" + integrity sha512-y7IQUStB2pOmq36KaOnLhaxIXjEYkKqzIxRW7grC3ByVKW7yDf88vXw9kS1wxdX5BrJvw/uh5N52NZ8COFy8tA== strict-uri-encode@^2.0.0: version "2.0.0" @@ -11669,14 +10757,14 @@ string.prototype.trim@^1.2.7: define-properties "^1.1.4" es-abstract "^1.20.4" -string.prototype.trim@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" - integrity sha512-IlEfUereZQqIcv/LJFNPUygFkq0HJCQMnaDr5i+zyRXpeYvF4F8J8u4UFxXICLMY+O3SEfJeaye5AO5miS6a9g== +string.prototype.trim@^1.2.8, string.prototype.trim@~1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.0" - function-bind "^1.0.2" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" string.prototype.trimend@^1.0.6: version "1.0.6" @@ -11687,6 +10775,15 @@ string.prototype.trimend@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string.prototype.trimstart@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" @@ -11696,7 +10793,16 @@ string.prototype.trimstart@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" -string_decoder@^1.0.0, string_decoder@^1.1.1: +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -11755,11 +10861,6 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -11810,13 +10911,6 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -11829,15 +10923,18 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -table-layout@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" - integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== +table-layout@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-3.0.2.tgz#69c2be44388a5139b48c59cf21e73b488021769a" + integrity sha512-rpyNZYRw+/C+dYkcQ3Pr+rLxW4CfHpXjPDnG7lYhdRoUcZTUt+KEsX+94RGp/aVp/MQU35JCITv2T/beY4m+hw== dependencies: - array-back "^4.0.1" - deep-extend "~0.6.0" - typical "^5.2.0" - wordwrapjs "^4.0.0" + "@75lb/deep-merge" "^1.1.1" + array-back "^6.2.2" + command-line-args "^5.2.1" + command-line-usage "^7.0.0" + stream-read-all "^3.0.1" + typical "^7.1.1" + wordwrapjs "^5.1.0" table@^6.0.9: version "6.8.1" @@ -11850,11 +10947,6 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" -tapable@^1.0.0, tapable@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== - tape-promise@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tape-promise/-/tape-promise-4.0.0.tgz#c1f3553959b2e9d64b1546e7276b8a017c616897" @@ -11863,23 +10955,25 @@ tape-promise@^4.0.0: is-promise "^2.1.0" onetime "^2.0.0" -tape@4.11.0: - version "4.11.0" - resolved "https://registry.yarnpkg.com/tape/-/tape-4.11.0.tgz#63d41accd95e45a23a874473051c57fdbc58edc1" - integrity sha512-yixvDMX7q7JIs/omJSzSZrqulOV51EC9dK8dM0TzImTIkHWfe2/kFyL5v+d9C+SrCMaICk59ujsqFAVidDqDaA== +tape@^4.11.0: + version "4.16.1" + resolved "https://registry.yarnpkg.com/tape/-/tape-4.16.1.tgz#8d511b3a0be1a30441885972047c1dac822fd9be" + integrity sha512-U4DWOikL5gBYUrlzx+J0oaRedm2vKLFbtA/+BRAXboGWpXO7bMP8ddxlq3Cse2bvXFQ0jZMOj6kk3546mvCdFg== dependencies: - deep-equal "~1.0.1" + call-bind "~1.0.2" + deep-equal "~1.1.1" defined "~1.0.0" + dotignore "~0.1.2" for-each "~0.3.3" - function-bind "~1.1.1" - glob "~7.1.4" + glob "~7.2.3" has "~1.0.3" inherits "~2.0.4" - minimist "~1.2.0" - object-inspect "~1.6.0" - resolve "~1.11.1" + is-regex "~1.1.4" + minimist "~1.2.6" + object-inspect "~1.12.2" + resolve "~1.22.1" resumer "~0.0.0" - string.prototype.trim "~1.1.2" + string.prototype.trim "~1.2.6" through "~2.3.8" tar-fs@2.1.1: @@ -11933,29 +11027,15 @@ temp-write@^3.4.0: temp-dir "^1.0.0" uuid "^3.0.1" -terser-webpack-plugin@^1.4.3: - version "1.4.5" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" - integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== - dependencies: - cacache "^12.0.2" - find-cache-dir "^2.1.0" - is-wsl "^1.1.0" - schema-utils "^1.0.0" - serialize-javascript "^4.0.0" - source-map "^0.6.1" - terser "^4.1.2" - webpack-sources "^1.4.0" - worker-farm "^1.7.0" - -terser@^4.1.2: - version "4.8.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f" - integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw== +terser@^5.10.0: + version "5.20.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.20.0.tgz#ea42aea62578703e33def47d5c5b93c49772423e" + integrity sha512-e56ETryaQDyebBwJIWYB2TT6f2EZ0fL0sW/JRXNMN26zZdKi2u/E/5my5lG6jNxym6qsrVXfFRmOdV42zlAgLQ== dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" + source-map-support "~0.5.20" test-exclude@^6.0.0: version "6.0.0" @@ -12010,16 +11090,16 @@ threads@^1.7.0: optionalDependencies: tiny-worker ">= 2" -thrift@^0.14.2: - version "0.14.2" - resolved "https://registry.yarnpkg.com/thrift/-/thrift-0.14.2.tgz#723c38a27da2d235ee744b5850ea61d29f3f988e" - integrity sha512-bW8EaE6iw3hSt4HB2HpBdHW86Xpb9IUJfqufx4NwEu7OGuIpS0ISj+Yy1Z1Wvhfno6SPNhKRJ1qFXea84HcrOQ== +thrift@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/thrift/-/thrift-0.19.0.tgz#340545555ea74cd129457963aa300967afcb038c" + integrity sha512-FfAeToex47DYF5UiqFiLXc0dTOQ1Dt94hdT/p1WEM8HQGOvI32jGs235QUeOvYwb1bApsTfFCa+ACDyF0fVtrg== dependencies: browser-or-node "^1.2.1" isomorphic-ws "^4.0.1" node-int64 "^0.4.0" q "^1.5.0" - ws "^5.2.2" + ws "^5.2.3" through2@^2.0.0, through2@^2.0.2: version "2.0.5" @@ -12049,18 +11129,6 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== - -timers-browserify@^2.0.4: - version "2.0.12" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" - integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== - dependencies: - setimmediate "^1.0.4" - "tiny-worker@>= 2": version "2.3.0" resolved "https://registry.yarnpkg.com/tiny-worker/-/tiny-worker-2.3.0.tgz#715ae34304c757a9af573ae9a8e3967177e6011e" @@ -12075,11 +11143,6 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - integrity sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA== - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -12122,11 +11185,6 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -toposort@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" - integrity sha512-FclLrw8b9bMWf4QlCJuHBEVhSRsqDj6u3nIjAzPeJvgl//1hBlffdlk0MALceL14+koWEdU4ofRAXofbODxQzg== - tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -12167,12 +11225,12 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -tryer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" - integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== +ts-api-utils@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== -ts-node@^10.4.0: +ts-node@~10.9.0: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== @@ -12201,27 +11259,29 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1, tslib@^1.9.0: +tsconfig-paths@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.4.0: +tslib@^2.0.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - integrity sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw== +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.5.3: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== tunnel-agent@^0.6.0: version "0.6.0" @@ -12252,6 +11312,11 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + type-fest@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" @@ -12262,7 +11327,7 @@ type-fest@^0.6.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== -type-fest@^0.8.0, type-fest@^0.8.1: +type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== @@ -12275,6 +11340,36 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -12284,40 +11379,25 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@^4.3.4, typescript@^5.0.4: - version "5.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" - integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== +typescript@^5.2.2, typescript@~4.6.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== typical@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== -typical@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" - integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== - -uglify-js@3.4.x: - version "3.4.10" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" - integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== - dependencies: - commander "~2.19.0" - source-map "~0.6.1" +typical@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/typical/-/typical-7.1.1.tgz#ba177ab7ab103b78534463ffa4c0c9754523ac1f" + integrity sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA== uglify-js@^3.1.4: version "3.17.4" @@ -12428,6 +11508,11 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -12441,7 +11526,7 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -upath@^1.1.1, upath@^1.2.0: +upath@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== @@ -12454,11 +11539,6 @@ update-browserslist-db@^1.0.10: escalade "^3.1.1" picocolors "^1.0.0" -upper-case@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" - integrity sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA== - uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -12471,14 +11551,6 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== -url-parse@^1.5.10: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -12504,32 +11576,23 @@ util-promisify@^2.1.0: dependencies: object.getownpropertydescriptors "^2.0.3" -util.promisify@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" - integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" - -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - integrity sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ== - dependencies: - inherits "2.0.1" - -util@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== dependencies: inherits "2.0.3" -utila@~0.4: - version "0.4.0" - resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" - integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== +util@^0.12.5: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" utils-merge@1.0.1: version "1.0.1" @@ -12541,21 +11604,30 @@ uuid@^3.0.1, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.1.0, uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.1: +v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== +v8-to-istanbul@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -12590,44 +11662,43 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vm-browserify@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" - integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== - -watchpack-chokidar2@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" - integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== - dependencies: - chokidar "^2.1.8" - -watchpack@^1.7.4: - version "1.7.5" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" - integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== +vite-plugin-html@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vite-plugin-html/-/vite-plugin-html-3.2.0.tgz#0d4df9900642a321a139f1c25c05195ba9d0ec79" + integrity sha512-2VLCeDiHmV/BqqNn5h2V+4280KRgQzCFN47cst3WiNK848klESPQnzuC3okH5XHtgwHH/6s1Ho/YV6yIO0pgoQ== dependencies: - graceful-fs "^4.1.2" - neo-async "^2.5.0" + "@rollup/pluginutils" "^4.2.0" + colorette "^2.0.16" + connect-history-api-fallback "^1.6.0" + consola "^2.15.3" + dotenv "^16.0.0" + dotenv-expand "^8.0.2" + ejs "^3.1.6" + fast-glob "^3.2.11" + fs-extra "^10.0.1" + html-minifier-terser "^6.1.0" + node-html-parser "^5.3.3" + pathe "^0.2.0" + +vite@^4.0.1: + version "4.4.9" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.9.tgz#1402423f1a2f8d66fd8d15e351127c7236d29d3d" + integrity sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA== + dependencies: + esbuild "^0.18.10" + postcss "^8.4.27" + rollup "^3.27.1" optionalDependencies: - chokidar "^3.4.1" - watchpack-chokidar2 "^2.0.1" - -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== - dependencies: - minimalistic-assert "^1.0.0" + fsevents "~2.3.2" -wcwidth@^1.0.0: +wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== dependencies: defaults "^1.0.3" -web-streams-polyfill@^3.0.0: +web-streams-polyfill@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== @@ -12642,151 +11713,6 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -webpack-bundle-analyzer@^3.0.3: - version "3.9.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz#f6f94db108fb574e415ad313de41a2707d33ef3c" - integrity sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - bfj "^6.1.1" - chalk "^2.4.1" - commander "^2.18.0" - ejs "^2.6.1" - express "^4.16.3" - filesize "^3.6.1" - gzip-size "^5.0.0" - lodash "^4.17.19" - mkdirp "^0.5.1" - opener "^1.5.1" - ws "^6.0.0" - -webpack-cli@^3.2.1: - version "3.3.12" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a" - integrity sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag== - dependencies: - chalk "^2.4.2" - cross-spawn "^6.0.5" - enhanced-resolve "^4.1.1" - findup-sync "^3.0.0" - global-modules "^2.0.0" - import-local "^2.0.0" - interpret "^1.4.0" - loader-utils "^1.4.0" - supports-color "^6.1.0" - v8-compile-cache "^2.1.1" - yargs "^13.3.2" - -webpack-dev-middleware@^3.7.2: - version "3.7.3" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" - integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ== - dependencies: - memory-fs "^0.4.1" - mime "^2.4.4" - mkdirp "^0.5.1" - range-parser "^1.2.1" - webpack-log "^2.0.0" - -webpack-dev-server@^3.1.14: - version "3.11.3" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz#8c86b9d2812bf135d3c9bce6f07b718e30f7c3d3" - integrity sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA== - dependencies: - ansi-html-community "0.0.8" - bonjour "^3.5.0" - chokidar "^2.1.8" - compression "^1.7.4" - connect-history-api-fallback "^1.6.0" - debug "^4.1.1" - del "^4.1.1" - express "^4.17.1" - html-entities "^1.3.1" - http-proxy-middleware "0.19.1" - import-local "^2.0.0" - internal-ip "^4.3.0" - ip "^1.1.5" - is-absolute-url "^3.0.3" - killable "^1.0.1" - loglevel "^1.6.8" - opn "^5.5.0" - p-retry "^3.0.1" - portfinder "^1.0.26" - schema-utils "^1.0.0" - selfsigned "^1.10.8" - semver "^6.3.0" - serve-index "^1.9.1" - sockjs "^0.3.21" - sockjs-client "^1.5.0" - spdy "^4.0.2" - strip-ansi "^3.0.1" - supports-color "^6.1.0" - url "^0.11.0" - webpack-dev-middleware "^3.7.2" - webpack-log "^2.0.0" - ws "^6.2.1" - yargs "^13.3.2" - -webpack-log@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" - integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== - dependencies: - ansi-colors "^3.0.0" - uuid "^3.3.2" - -webpack-sources@^1.4.0, webpack-sources@^1.4.1: - version "1.4.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack@^4.28.4: - version "4.46.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" - integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/wasm-edit" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.4.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" - chrome-trace-event "^1.0.2" - enhanced-resolve "^4.5.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.3" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.7.4" - webpack-sources "^1.4.1" - -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -12830,6 +11756,17 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== +which-typed-array@^1.1.11, which-typed-array@^1.1.2: + version "1.1.11" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" + integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + which-typed-array@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" @@ -12849,7 +11786,7 @@ which@1.2.x: dependencies: isexe "^2.0.0" -which@^1.2.14, which@^1.2.9, which@^1.3.1: +which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -12902,20 +11839,10 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -wordwrapjs@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" - integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== - dependencies: - reduce-flatten "^2.0.0" - typical "^5.2.0" - -worker-farm@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" - integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== - dependencies: - errno "~0.1.7" +wordwrapjs@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-5.1.0.tgz#4c4d20446dcc670b14fa115ef4f8fd9947af2b3a" + integrity sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg== wrap-ansi@^5.1.0: version "5.1.0" @@ -12926,7 +11853,7 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" -wrap-ansi@^6.2.0: +wrap-ansi@^6.0.1: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== @@ -12958,16 +11885,6 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: imurmurhash "^0.1.4" signal-exit "^3.0.2" -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== - dependencies: - imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" - write-json-file@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-2.3.0.tgz#2b64c8a33004d54b8698c76d585a77ceb61da32f" @@ -13005,29 +11922,22 @@ ws@8.13.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== -ws@^5.2.2: +ws@^5.2.3: version "5.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d" integrity sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA== dependencies: async-limiter "~1.0.0" -ws@^6.0.0, ws@^6.2.1: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== +xlsx@^0.18.5: + version "0.18.5" + resolved "https://registry.yarnpkg.com/xlsx/-/xlsx-0.18.5.tgz#16711b9113c848076b8a177022799ad356eba7d0" + integrity sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ== dependencies: - async-limiter "~1.0.0" - -xlsx@^0.17.0: - version "0.17.5" - resolved "https://registry.yarnpkg.com/xlsx/-/xlsx-0.17.5.tgz#78b788fcfc0773d126cdcd7ea069cb7527c1ce81" - integrity sha512-lXNU0TuYsvElzvtI6O7WIVb9Zar1XYw7Xb3VAx2wn8N/n0whBYrCnHMxtFyIiUU1Wjf09WzmLALDfBO5PqTb1g== - dependencies: - adler-32 "~1.2.0" - cfb "^1.1.4" + adler-32 "~1.3.0" + cfb "~1.2.1" codepage "~1.15.0" - crc-32 "~1.2.0" + crc-32 "~1.2.1" ssf "~0.11.2" wmf "~1.0.1" word "~0.3.0" @@ -13037,21 +11947,16 @@ xml-utils@^1.0.2: resolved "https://registry.yarnpkg.com/xml-utils/-/xml-utils-1.7.0.tgz#333ce391d8918f872aaf98d2cf90f9ef9b82bd0f" integrity sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw== -xmldom@0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.6.0.tgz#43a96ecb8beece991cef382c08397d82d4d0c46f" - integrity sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg== - -xtend@^4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - xtend@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.2.0.tgz#eef6b1f198c1c8deafad8b1765a04dad4a01c5a9" integrity sha512-SLt5uylT+4aoXxXuwtQp5ZnMMzhDb1Xkg4pEqc00WUJCQifPfV9Ub1VrNhp9kXkrjZD2I2Hl8WnjP37jzZLPZw== +xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -13077,14 +11982,6 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yargs-parser@^13.1.2: - version "13.1.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" - integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs-parser@^15.0.1: version "15.0.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.3.tgz#316e263d5febe8b38eef61ac092b33dfcc9b1115" @@ -13093,15 +11990,7 @@ yargs-parser@^15.0.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^20.2.3: +yargs-parser@^20.2.2, yargs-parser@^20.2.3, yargs-parser@^20.2.9: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== @@ -13124,22 +12013,6 @@ yargs@17.7.1: y18n "^5.0.5" yargs-parser "^21.1.1" -yargs@^13.3.2: - version "13.3.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" - integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.2" - yargs@^14.2.2: version "14.2.3" resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" @@ -13157,22 +12030,18 @@ yargs@^14.2.2: y18n "^4.0.0" yargs-parser "^15.0.1" -yargs@^15.0.2: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" + y18n "^5.0.5" + yargs-parser "^20.2.2" yauzl@^2.10.0: version "2.10.0" @@ -13187,6 +12056,11 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + zarr@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/zarr/-/zarr-0.5.2.tgz#d19aad64df388460e1c1038709fb7d344c2cdded"