diff --git a/clouds/bigquery/Makefile b/clouds/bigquery/Makefile
index 5310196a7..03d43e064 100644
--- a/clouds/bigquery/Makefile
+++ b/clouds/bigquery/Makefile
@@ -4,6 +4,7 @@ ROOT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
 
 DIST_DIR ?= $(ROOT_DIR)/dist
 BUILD_DIR ?= $(ROOT_DIR)/build
+MODULES_DIRS ?= $(ROOT_DIR)/modules
 ESLINTRC_DIR ?= $(ROOT_DIR)/../..
 COMMON_DIR = $(ROOT_DIR)/common
 PACKAGE_VERSION ?= $(shell cat $(ROOT_DIR)/version)
@@ -49,7 +50,10 @@ build:
 build-libraries:
 	mkdir -p $(BUILD_DIR)/libs
 	$(MAKE) -C libraries/javascript build
-	cp libraries/javascript/build/index.js $(BUILD_DIR)/libs/$(BQ_LIBRARY_DEFAULT).js
+	$(COMMON_DIR)/list_libraries.js $(MODULES_DIRS) --diff="$(diff)" --modules=$(modules) --functions=$(functions) --nodeps=$(nodeps) --makelib=$(MAKE_LIB) 1>/dev/null # Check errors
+	for f in `$(COMMON_DIR)/list_libraries.js $(MODULES_DIRS) --diff="$(diff)" --modules=$(modules) --functions=$(functions) --nodeps=$(nodeps) --makelib=$(MAKE_LIB)`; do \
+		cp libraries/javascript/build/$${f}.js $(BUILD_DIR)/libs/$(BQ_LIBRARY_DEFAULT)_$${f}.js; \
+	done
 ifdef MAKE_LIB
 	cp libraries/javascript/build/index_$(MAKE_LIB).js $(BUILD_DIR)/libs/$(BQ_LIBRARY_DEFAULT)_$(MAKE_LIB).js
 endif
diff --git a/clouds/bigquery/common/Makefile b/clouds/bigquery/common/Makefile
index 57e536739..6cd71dfe4 100644
--- a/clouds/bigquery/common/Makefile
+++ b/clouds/bigquery/common/Makefile
@@ -10,11 +10,11 @@ BQ_DATASET_DEFAULT = carto
 BQ_LIBRARY_DEFAULT ?= carto_analytics_toolbox_core
 
 ifeq ($(production),1)
-export BQ_LIBRARY_BUCKET = $(BQ_BUCKET)/$(BQ_DATASET_DEFAULT)/libs/$(BQ_LIBRARY_DEFAULT).js
-export BQ_LIBRARY_TILER_BUCKET = $(BQ_BUCKET)/$(BQ_DATASET_DEFAULT)/libs/$(BQ_LIBRARY_DEFAULT)_tiler.js
+export BQ_LIBRARY_BUCKET = $(BQ_BUCKET)/$(BQ_DATASET_DEFAULT)/libs/$(BQ_LIBRARY_DEFAULT)
+export BQ_LIBRARY_TILER_BUCKET = $(BQ_LIBRARY_BUCKET)_tiler.js
 else
-export BQ_LIBRARY_BUCKET = $(BQ_BUCKET)/$(BQ_PREFIX)$(BQ_DATASET_DEFAULT)/libs/$(BQ_LIBRARY_DEFAULT).js
-export BQ_LIBRARY_TILER_BUCKET = $(BQ_BUCKET)/$(BQ_PREFIX)$(BQ_DATASET_DEFAULT)/libs/$(BQ_LIBRARY_DEFAULT)_tiler.js
+export BQ_LIBRARY_BUCKET = $(BQ_BUCKET)/$(BQ_PREFIX)$(BQ_DATASET_DEFAULT)/libs/$(BQ_LIBRARY_DEFAULT)
+export BQ_LIBRARY_TILER_BUCKET = $(BQ_LIBRARY_BUCKET)_tiler.js
 
 endif
 
diff --git a/clouds/bigquery/common/build_modules.js b/clouds/bigquery/common/build_modules.js
index e98af1770..41f6c3bc7 100755
--- a/clouds/bigquery/common/build_modules.js
+++ b/clouds/bigquery/common/build_modules.js
@@ -14,8 +14,11 @@ const argv = require('minimist')(process.argv.slice(2));
 
 const inputDirs = argv._[0] && argv._[0].split(',');
 const outputDir = argv.output || 'build';
+const libsBuildDir = argv.libs_build_dir || '../libraries/javascript/build';
 const diff = argv.diff || [];
 const nodeps = argv.nodeps;
+const libraryBucket = argv.librarybucket;
+const makelib = argv.makelib;
 let modulesFilter = (argv.modules && argv.modules.split(',')) || [];
 let functionsFilter = (argv.functions && argv.functions.split(',')) || [];
 let all = !(diff.length || modulesFilter.length || functionsFilter.length);
@@ -156,6 +159,23 @@ if (argv.production) {
 let content = output.map(f => f.content).join(separator);
 
 function apply_replacements (text) {
+    const libraries = [... new Set(text.match(new RegExp('@@BQ_LIBRARY_.*_BUCKET@@', 'g')))];
+    for (let library of libraries) {
+        let libraryName = library.replace('@@BQ_LIBRARY_', '').replace('_BUCKET@@', '').toLowerCase();
+        if (makelib == libraryName) {
+            continue;
+        }
+        libraryName += '.js';
+        const libraryPath = path.join(libsBuildDir, libraryName);
+        if (fs.existsSync(libraryPath)) {
+            const libraryBucketPath = libraryBucket + '_' + libraryName;
+            text = text.replace(new RegExp(library, 'g'), libraryBucketPath);
+        }
+        else {
+            console.log(`Warning: library "${libraryName}" does not exist. Run "make build-libraries" with the same filters.`);
+            process.exit(1);
+        }
+    }
     const replacements = process.env.REPLACEMENTS.split(' ');
     for (let replacement of replacements) {
         if (replacement) {
diff --git a/clouds/bigquery/common/list_libraries.js b/clouds/bigquery/common/list_libraries.js
new file mode 100755
index 000000000..878c35e17
--- /dev/null
+++ b/clouds/bigquery/common/list_libraries.js
@@ -0,0 +1,145 @@
+#!/usr/bin/env node
+
+// List the JavaScript libraries based on the input filters to the SQL functions
+
+
+// ./build_modules.js modules --output=build --diff="clouds/bigquery/modules/sql/quadbin/QUADBIN_TOZXY.sql"
+// ./build_modules.js modules --output=build --functions=ST_TILEENVELOPE
+// ./build_modules.js modules --output=build --modules=quadbin
+// ./build_modules.js modules --output=build --production --dropfirst
+
+const fs = require('fs');
+const path = require('path');
+const argv = require('minimist')(process.argv.slice(2));
+
+const inputDirs = argv._[0] && argv._[0].split(',');
+const diff = argv.diff || [];
+const nodeps = argv.nodeps;
+const makelib = argv.makelib;
+let modulesFilter = (argv.modules && argv.modules.split(',')) || [];
+let functionsFilter = (argv.functions && argv.functions.split(',')) || [];
+let all = !(diff.length || modulesFilter.length || functionsFilter.length);
+
+// Convert diff to modules/functions
+if (diff.length) {
+    const patternsAll = [
+        /\.github\/workflows\/bigquery\.yml/,
+        /clouds\/bigquery\/common\/.+/,
+        /clouds\/bigquery\/libraries\/.+/,
+        /clouds\/bigquery\/.*Makefile/,
+        /clouds\/bigquery\/version/
+    ];
+    const patternModulesSql = /clouds\/bigquery\/modules\/sql\/([^\s]*?)\//g;
+    const patternModulesTest = /clouds\/bigquery\/modules\/test\/([^\s]*?)\//g;
+    const diffAll = patternsAll.some(p => diff.match(p));
+    if (diffAll) {
+        all = diffAll;
+    } else {
+        const modulesSql = [...diff.matchAll(patternModulesSql)].map(m => m[1]);
+        const modulesTest = [...diff.matchAll(patternModulesTest)].map(m => m[1]);
+        const diffModulesFilter = [...new Set(modulesSql.concat(modulesTest))];
+        if (diffModulesFilter) {
+            modulesFilter = diffModulesFilter;
+        }
+    }
+}
+
+// Extract functions
+const functions = [];
+for (let inputDir of inputDirs) {
+    const sqldir = path.join(inputDir, 'sql');
+    const modules = fs.readdirSync(sqldir);
+    modules.forEach(module => {
+        const moduledir = path.join(sqldir, module);
+        if (fs.statSync(moduledir).isDirectory()) {
+            const files = fs.readdirSync(moduledir);
+            files.forEach(file => {
+                if (file.endsWith('.sql')) {
+                    const name = path.parse(file).name;
+                    const content = fs.readFileSync(path.join(moduledir, file)).toString().replace(/--.*\n/g, '');
+                    functions.push({
+                        name,
+                        module,
+                        content,
+                        dependencies: []
+                    });
+                }
+            });
+        }
+    });
+}
+
+// Check filters
+modulesFilter.forEach(m => {
+    if (!functions.map(fn => fn.module).includes(m)) {
+        process.stderr.write(`ERROR: Module not found ${m}\n`);
+        process.exit(1);
+    }
+});
+functionsFilter.forEach(f => {
+    if (!functions.map(fn => fn.name).includes(f)) {
+        process.stderr.write(`ERROR: Function not found ${f}`);
+        process.exit(1);
+    }
+});
+
+// Extract function dependencies
+if (!nodeps) {
+    functions.forEach(mainFunction => {
+        functions.forEach(depFunction => {
+            if (mainFunction.name != depFunction.name) {
+                const depFunctionMatches = [];
+                depFunctionMatches.push(...depFunction.content.replace(/(\r\n|\n|\r)/gm,' ').matchAll(new RegExp('(?<=(?<!TEMP )FUNCTION)(.*?)(?=AS |RETURNS)','g')));
+                depFunctionMatches.push(...depFunction.content.replace(/(\r\n|\n|\r)/gm,' ').matchAll(new RegExp('(?<=PROCEDURE)(.*?)(?=BEGIN)','g')));
+                const depFunctionNames = [];
+                depFunctionMatches.forEach((depFunctionMatch) => {
+                    let qualifiedDepFunctName = depFunctionMatch[0].replace(/[ \p{Diacritic}]/gu, '').split('(')[0];
+                    qualifiedDepFunctName = qualifiedDepFunctName.split('.');
+                    depFunctionNames.push(qualifiedDepFunctName[qualifiedDepFunctName.length - 1]);
+                })
+                if (depFunctionNames.some((depFunctionName) => mainFunction.content.includes(`DATASET@@.${depFunctionName}\`(`))) {
+                    mainFunction.dependencies.push(depFunction.name);
+                }
+            }
+        });
+    });
+}
+
+// Check circular dependencies
+functions.forEach(mainFunction => {
+    functions.forEach(depFunction => {
+        if (mainFunction.dependencies.includes(depFunction.name) &&
+            depFunction.dependencies.includes(mainFunction.name)) {
+            process.stderr.write(`ERROR: Circular dependency between ${mainFunction.name} and ${depFunction.name}`);
+            process.exit(1);
+        }
+    });
+});
+
+
+// Filter functions
+const output = [];
+function add (f, include) {
+    include = include || all || functionsFilter.includes(f.name) || modulesFilter.includes(f.module);
+    for (const dependency of f.dependencies) {
+        add(functions.find(f => f.name === dependency), include);
+    }
+    if (!output.map(f => f.name).includes(f.name) && include) {
+        output.push({
+            name: f.name,
+            content: f.content
+        });
+    }
+}
+functions.forEach(f => add(f));
+
+const content = output.map(f => f.content).join('\n');
+let libraries = [... new Set(content.match(new RegExp('@@BQ_LIBRARY_.*_BUCKET@@', 'g')))]
+    .map(l => l.replace('@@BQ_LIBRARY_', '').replace('_BUCKET@@', '').toLowerCase());
+
+// Exclude libraries pointed by makelib as they are deployed separately
+if (makelib) {
+    libraries = libraries.filter(l => l !== makelib);
+}
+
+process.stdout.write(libraries.join(' '));
\ No newline at end of file
diff --git a/clouds/bigquery/common/rollup.config.js b/clouds/bigquery/common/rollup.config.js
index a0af9d73e..d23eb64df 100644
--- a/clouds/bigquery/common/rollup.config.js
+++ b/clouds/bigquery/common/rollup.config.js
@@ -18,6 +18,11 @@ for (let dir of dirs) {
     }
 }
 
+if (!input && filename) {
+    console.log(`Error: library "${filename}" does not exist. Add it or revisit the replacement "@@BQ_LIBRARY_${path.parse(filename).name.toUpperCase()}@@" in one of your sql files.`);
+    process.exit(1);
+}
+
 // Format library name to camel case
 const name = process.env.NAME.replace(/(_\w)/g, k => k[1].toUpperCase());
 
diff --git a/clouds/bigquery/libraries/javascript/Makefile b/clouds/bigquery/libraries/javascript/Makefile
index f39502fca..5dad8f37b 100644
--- a/clouds/bigquery/libraries/javascript/Makefile
+++ b/clouds/bigquery/libraries/javascript/Makefile
@@ -45,12 +45,27 @@ ifdef MAKE_LIB
 endif
 
 build-libs: build-install $(NODE_MODULES_DEV)
-	NAME=lib \
-	PATH="$(NODE_MODULES_DEV)/.bin/:$(PATH)" \
-	DIRS=$(LIBS_DIR) \
-	FILENAME=index.js \
-	OUTPUT=$(BUILD_DIR)/index.js \
-	rollup --config $(COMMON_DIR)/rollup.config.js $(BUILD_PARAMS); \
+ifdef UNIT_TEST
+	$(COMMON_DIR)/list_libraries.js $(MODULES_DIR) --all --makelib=$(MAKE_LIB) 1>/dev/null # Check errors
+	for f in `$(COMMON_DIR)/list_libraries.js $(MODULES_DIR) --all --makelib=$(MAKE_LIB)`; do \
+		NAME=$${f}Lib \
+		PATH="$(NODE_MODULES_DEV)/.bin/:$(PATH)" \
+		DIRS=$(LIBS_DIR) \
+		FILENAME=$${f}.js \
+		OUTPUT=$(BUILD_DIR)/$${f}.js \
+		rollup --config $(COMMON_DIR)/rollup.config.js $(BUILD_PARAMS); \
+	done
+else
+	$(COMMON_DIR)/list_libraries.js $(MODULES_DIRS) --diff="$(diff)" --modules=$(modules) --functions=$(functions) --nodeps=$(nodeps) --makelib=$(MAKE_LIB) 1>/dev/null # Check errors
+	for f in `$(COMMON_DIR)/list_libraries.js $(MODULES_DIRS) --diff="$(diff)" --modules=$(modules) --functions=$(functions) --nodeps=$(nodeps) --makelib=$(MAKE_LIB)`; do \
+		NAME=$${f}Lib \
+		PATH="$(NODE_MODULES_DEV)/.bin/:$(PATH)" \
+		DIRS=$(LIBS_DIRS) \
+		FILENAME=$${f}.js \
+		OUTPUT=$(BUILD_DIR)/$${f}.js \
+		rollup --config $(COMMON_DIR)/rollup.config.js $(BUILD_PARAMS); \
+	done
+endif
 
 build-install:
 	for d in $(shell echo $(LIBS_DIRS) | tr "," "\n"); do \
@@ -59,7 +74,10 @@ build-install:
 
 deploy: check build
 	echo "Deploying libraries..."
-	$(GSUTIL) cp -r $(BUILD_DIR)/index.js $(BQ_LIBRARY_BUCKET)
+	$(COMMON_DIR)/list_libraries.js $(MODULES_DIRS) --diff="$(diff)" --modules=$(modules) --functions=$(functions) --nodeps=$(nodeps) --makelib=$(MAKE_LIB) 1>/dev/null # Check errors
+	for f in `$(COMMON_DIR)/list_libraries.js $(MODULES_DIRS) --diff="$(diff)" --modules=$(modules) --functions=$(functions) --nodeps=$(nodeps) --makelib=$(MAKE_LIB)`; do \
+		$(GSUTIL) cp -r $(BUILD_DIR)/$${f}.js $(BQ_LIBRARY_BUCKET)_$${f}.js; \
+	done
 	# Provisional for WASM version of tiler
 ifdef MAKE_LIB
 	echo "Deploying tiler library..."
diff --git a/clouds/bigquery/libraries/javascript/src/accessors.js b/clouds/bigquery/libraries/javascript/libs/accessors.js
similarity index 100%
rename from clouds/bigquery/libraries/javascript/src/accessors.js
rename to clouds/bigquery/libraries/javascript/libs/accessors.js
diff --git a/clouds/bigquery/libraries/javascript/src/clustering.js b/clouds/bigquery/libraries/javascript/libs/clustering.js
similarity index 100%
rename from clouds/bigquery/libraries/javascript/src/clustering.js
rename to clouds/bigquery/libraries/javascript/libs/clustering.js
diff --git a/clouds/bigquery/libraries/javascript/src/constructors.js b/clouds/bigquery/libraries/javascript/libs/constructors.js
similarity index 100%
rename from clouds/bigquery/libraries/javascript/src/constructors.js
rename to clouds/bigquery/libraries/javascript/libs/constructors.js
diff --git a/clouds/bigquery/libraries/javascript/src/h3.js b/clouds/bigquery/libraries/javascript/libs/h3.js
similarity index 100%
rename from clouds/bigquery/libraries/javascript/src/h3.js
rename to clouds/bigquery/libraries/javascript/libs/h3.js
diff --git a/clouds/bigquery/libraries/javascript/libs/index.js b/clouds/bigquery/libraries/javascript/libs/index.js
deleted file mode 100644
index e96a72888..000000000
--- a/clouds/bigquery/libraries/javascript/libs/index.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import accessors from '../src/accessors';
-import constructors from '../src/constructors';
-import measurements from '../src/measurements';
-import quadkey from '../src/quadkey';
-import s2 from '../src/s2';
-import processing from '../src/processing';
-import transformations from '../src/transformations';
-import h3 from '../src/h3';
-import placekey from '../src/placekey';
-import clustering from '../src/clustering';
-import random from '../src/random';
-
-export default {
-    accessors,
-    constructors,
-    measurements,
-    quadkey,
-    s2,
-    processing,
-    transformations,
-    h3,
-    placekey,
-    clustering,
-    random
-};
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/src/measurements.js b/clouds/bigquery/libraries/javascript/libs/measurements.js
similarity index 100%
rename from clouds/bigquery/libraries/javascript/src/measurements.js
rename to clouds/bigquery/libraries/javascript/libs/measurements.js
diff --git a/clouds/bigquery/libraries/javascript/src/placekey.js b/clouds/bigquery/libraries/javascript/libs/placekey.js
similarity index 100%
rename from clouds/bigquery/libraries/javascript/src/placekey.js
rename to clouds/bigquery/libraries/javascript/libs/placekey.js
diff --git a/clouds/bigquery/libraries/javascript/src/processing.js b/clouds/bigquery/libraries/javascript/libs/processing.js
similarity index 100%
rename from clouds/bigquery/libraries/javascript/src/processing.js
rename to clouds/bigquery/libraries/javascript/libs/processing.js
diff --git a/clouds/bigquery/libraries/javascript/libs/quadkey.js b/clouds/bigquery/libraries/javascript/libs/quadkey.js
new file mode 100644
index 000000000..51eb55b18
--- /dev/null
+++ b/clouds/bigquery/libraries/javascript/libs/quadkey.js
@@ -0,0 +1,25 @@
+import {
+    bbox,
+    toParent,
+    toChildren,
+    quadkeyFromQuadint,
+    quadintFromQuadkey,
+    quadintFromLocation,
+    quadintToGeoJSON,
+    quadintFromZXY,
+    geojsonToQuadints,
+    ZXYFromQuadint
+} from '../src/quadkey';
+
+export default {
+    bbox,
+    toParent,
+    toChildren,
+    quadkeyFromQuadint,
+    quadintFromQuadkey,
+    quadintFromLocation,
+    quadintToGeoJSON,
+    quadintFromZXY,
+    geojsonToQuadints,
+    ZXYFromQuadint
+};
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/libs/random.js b/clouds/bigquery/libraries/javascript/libs/random.js
new file mode 100644
index 000000000..70dcd1cc6
--- /dev/null
+++ b/clouds/bigquery/libraries/javascript/libs/random.js
@@ -0,0 +1,8 @@
+import {
+    generateRandomPointsInPolygon
+
+} from '../src/random';
+
+export default {
+    generateRandomPointsInPolygon
+};
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/libs/s2.js b/clouds/bigquery/libraries/javascript/libs/s2.js
new file mode 100644
index 000000000..229bb13f1
--- /dev/null
+++ b/clouds/bigquery/libraries/javascript/libs/s2.js
@@ -0,0 +1,9 @@
+import { S2 } from '../src/s2';
+
+export default {
+    keyToId: S2.keyToId,
+    idToKey: S2.idToKey,
+    latLngToKey: S2.latLngToKey,
+    FromHilbertQuadKey: S2.S2Cell.FromHilbertQuadKey,
+    idToLatLng: S2.idToLatLng
+};
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/src/transformations.js b/clouds/bigquery/libraries/javascript/libs/transformations.js
similarity index 100%
rename from clouds/bigquery/libraries/javascript/src/transformations.js
rename to clouds/bigquery/libraries/javascript/libs/transformations.js
diff --git a/clouds/bigquery/libraries/javascript/src/quadkey.js b/clouds/bigquery/libraries/javascript/src/quadkey.js
index f74df5b5c..8ea5e5d7e 100644
--- a/clouds/bigquery/libraries/javascript/src/quadkey.js
+++ b/clouds/bigquery/libraries/javascript/src/quadkey.js
@@ -1,25 +1,310 @@
-import {
-    bbox,
-    toParent,
-    toChildren,
-    quadkeyFromQuadint,
-    quadintFromQuadkey,
-    quadintFromLocation,
-    quadintToGeoJSON,
-    quadintFromZXY,
-    geojsonToQuadints,
-    ZXYFromQuadint
-} from './quadkey/index';
-
-export default {
-    bbox,
-    toParent,
-    toChildren,
-    quadkeyFromQuadint,
-    quadintFromQuadkey,
-    quadintFromLocation,
-    quadintToGeoJSON,
-    quadintFromZXY,
-    geojsonToQuadints,
-    ZXYFromQuadint
-};
\ No newline at end of file
+// ----------------------------
+// -- Copyright (C) 2021 CARTO
+// ----------------------------
+
+import tilebelt from '@mapbox/tilebelt';
+import tilecover from '@mapbox/tile-cover';
+
+/**
+ * convert tile coordinates to quadint at specific zoom level
+ * @param  {zxycoord} zxy   zoom and tile coordinates
+ * @return {int}            quadint for input tile coordinates at input zoom level
+ */
+export function quadintFromZXY (z, x, y) {
+    if (z < 0 || z > 29) {
+        throw new Error('Wrong zoom');
+    }
+    const zI = z;
+    if (zI <= 13) {
+        let quadint = y;
+        quadint <<= zI;
+        quadint |= x;
+        quadint <<= 5;
+        quadint |= zI;
+        return quadint;
+    }
+    let quadint = BigInt(y);
+    quadint <<= BigInt(z);
+    quadint |= BigInt(x);
+    quadint <<= BigInt(5);
+    quadint |= BigInt(z);
+    return quadint;
+}
+
+/**
+ * convert quadint to tile coordinates and level of zoom
+ * @param  {int} quadint   quadint to be converted
+ * @return {zxycoord}      level of zoom and tile coordinates
+ */
+export function ZXYFromQuadint (quadint) {
+    const quadintBig = BigInt(quadint);
+    const z = quadintBig & BigInt(0x1F);
+    if (z <= 13n) {
+        const zNumber = Number(z);
+        const x = (quadint >> 5) & ((1 << zNumber) - 1);
+        const y = quadint >> (zNumber + 5);
+        return { z: zNumber, x: x, y: y };
+    }
+    const x = (quadintBig >> (5n)) & ((1n << z) - 1n);
+    const y = quadintBig >> (5n + z);
+    return { z: Number(z), x: Number(x), y: Number(y) };
+}
+
+/**
+ * get quadint for location at specific zoom level
+ * @param  {geocoord} location  location coordinates to convert to quadint
+ * @param  {number}   zoom      map zoom level of quadint to return
+ * @return {string}             quadint the input location resides in for the input zoom level
+ */
+export function quadintFromLocation (long, lat, zoom) {
+    if (zoom < 0 || zoom > 29) {
+        throw new Error('Wrong zoom');
+    }
+    lat = clipNumber(lat, -85.05, 85.05);
+    const tile = tilebelt.pointToTile(long, lat, zoom);
+    return quadintFromZXY(zoom, tile[0], tile[1]);
+}
+
+/**
+ * convert quadkey into a quadint
+ * @param  {string} quadkey     quadkey to be converted
+ * @return {int}                quadint
+ */
+export function quadintFromQuadkey (quadkey) {
+    const z = quadkey.length;
+    const tile = tilebelt.quadkeyToTile(quadkey);
+    return quadintFromZXY(z, tile[0], tile[1]);
+}
+
+/**
+ * convert quadint into a quadkey
+ * @param  {int} quadint    quadint to be converted
+ * @return {string}         quadkey
+ */
+export function quadkeyFromQuadint (quadint) {
+    const tile = ZXYFromQuadint(quadint);
+    return tilebelt.tileToQuadkey([tile.x, tile.y, tile.z]);
+}
+
+/**
+ * get the bounding box for a quadint in location coordinates
+ * @param  {int} quadint    quadint to get bounding box from
+ * @return {bbox}           bounding box for the input quadint
+ */
+export function bbox (quadint) {
+    const tile = ZXYFromQuadint(quadint);
+    return tilebelt.tileToBBOX([tile.x, tile.y, tile.z]);
+}
+
+/**
+ * get the GeoJSON with the bounding box for a quadint in location coordinates
+ * @param  {int} quadint    quadint to get bounding box from
+ * @return {GeoJSON}        GeoJSON with the bounding box for the input quadint
+ */
+export function quadintToGeoJSON (quadint) {
+    const tile = ZXYFromQuadint(quadint);
+    return tilebelt.tileToGeoJSON([tile.x, tile.y, tile.z]);
+}
+
+/**
+ * returns the sibling of the given quadint and will wrap
+ * @param  {int} quadint      key to get sibling of
+ * @param  {string} direction direction of sibling from key
+ * @return {int}              sibling key
+ */
+export function sibling (quadint, direction) {
+    direction = direction.toLowerCase();
+    if (direction !== 'left' && direction !== 'right' && direction !== 'up' && direction !== 'down') {
+        throw new Error('Wrong direction argument passed to sibling');
+    }
+    if (direction === 'left') {
+        return siblingLeft(quadint);
+    }
+    if (direction === 'right') {
+        return siblingRight(quadint);
+    }
+    if (direction === 'up') {
+        return siblingUp(quadint);
+    }
+    if (direction === 'down') {
+        return siblingDown(quadint);
+    }
+}
+
+/**
+ * returns the sibling of the given quadint and will wrap
+ * @param  {int} quadint      key to get sibling of
+ * @param  {string} direction direction of sibling from key
+ * @return {int}              sibling key
+ */
+export function siblingLeft (quadint) {
+    const tile = ZXYFromQuadint(quadint);
+    const tilesPerLevel = 2 << (tile.z - 1);
+    const x = tile.x > 0 ? tile.x - 1 : tilesPerLevel - 1;
+    return quadintFromZXY(tile.z, x, tile.y);
+}
+
+/**
+ * returns the sibling of the given quadint and will wrap
+ * @param  {int} quadint      key to get sibling of
+ * @param  {string} direction direction of sibling from key
+ * @return {int}              sibling key
+ */
+export function siblingRight (quadint) {
+    const tile = ZXYFromQuadint(quadint);
+    const tilesPerLevel = 2 << (tile.z - 1);
+    const x = tile.x < tilesPerLevel - 1 ? tile.x + 1 : 0;
+    return quadintFromZXY(tile.z, x, tile.y);
+}
+
+/**
+ * returns the sibling of the given quadint and will wrap
+ * @param  {int} quadint      key to get sibling of
+ * @param  {string} direction direction of sibling from key
+ * @return {int}              sibling key
+ */
+export function siblingUp (quadint) {
+    const tile = ZXYFromQuadint(quadint);
+    const tilesPerLevel = 2 << (tile.z - 1);
+    const y = tile.y > 0 ? tile.y - 1 : tilesPerLevel - 1;
+    return quadintFromZXY(tile.z, tile.x, y);
+}
+
+/**
+ * returns the sibling of the given quadint and will wrap
+ * @param  {int} quadint      key to get sibling of
+ * @param  {string} direction direction of sibling from key
+ * @return {int}              sibling key
+ */
+export function siblingDown (quadint) {
+    const tile = ZXYFromQuadint(quadint);
+    const tilesPerLevel = 2 << (tile.z - 1);
+    const y = tile.y < tilesPerLevel - 1 ? tile.y + 1 : 0;
+    return quadintFromZXY(tile.z, tile.x, y);
+}
+
+/**
+ * get all the children quadints of a quadint
+ * @param  {int} quadint    quadint to get the children of
+ * @param  {int} resolution resolution of the desired children
+ * @return {array}          array of quadints representing the children of the input quadint
+ */
+export function toChildren (quadint, resolution) {
+    const zxy = ZXYFromQuadint(quadint);
+    if (zxy.z < 0 || zxy.z > 28) {
+        throw new Error('Wrong quadint zoom');
+    }
+
+    if (resolution < 0 || resolution <= zxy.z) {
+        throw new Error('Wrong resolution');
+    }
+    const diffZ = resolution - zxy.z;
+    const mask = (1 << diffZ) - 1;
+    const minTileX = zxy.x << diffZ;
+    const maxTileX = minTileX | mask;
+    const minTileY = zxy.y << diffZ;
+    const maxTileY = minTileY | mask;
+    const children = [];
+    let x, y;
+    for (x = minTileX; x <= maxTileX; x++) {
+        for (y = minTileY; y <= maxTileY; y++) {
+            children.push(quadintFromZXY(resolution, x, y));
+        }
+    }
+    return children;
+}
+
+/**
+ * get the parent of a quadint
+ * @param  {int} quadint quadint to get the parent of
+ * @param  {int} resolution resolution of the desired parent
+ * @return {int}         parent of the input quadint
+ */
+export function toParent (quadint, resolution) {
+    const zxy = ZXYFromQuadint(quadint);
+    if (zxy.z < 1 || zxy.z > 29) {
+        throw new Error('Wrong quadint zoom');
+    }
+    if (resolution < 0 || resolution >= zxy.z) {
+        throw new Error('Wrong resolution');
+    }
+    return quadintFromZXY(resolution, zxy.x >> (zxy.z - resolution), zxy.y >> (zxy.z - resolution));
+}
+
+/**
+ * get the kring of a quadint
+ * @param  {int} origin quadint to get the kring of
+ * @param  {int} size in tiles of the desired kring
+ * @return {int}         kring of the input quadint
+ */
+export function kRing (origin, size) {
+    if (size === 0) {
+        return [origin.toString()];
+    }
+
+    let i, j;
+    let cornerQuadint = origin;
+    // Traverse to top left corner
+    for (i = 0; i < size; i++) {
+        cornerQuadint = siblingLeft(cornerQuadint);
+        cornerQuadint = siblingUp(cornerQuadint)
+    }
+
+    const neighbors = [];
+    let traversalQuadint;
+    for (j = 0; j < size * 2 + 1; j++) {
+        traversalQuadint = cornerQuadint;
+        for (i = 0; i < size * 2 + 1; i++) {
+            neighbors.push(traversalQuadint.toString());
+            traversalQuadint = siblingRight(traversalQuadint);
+        }
+        cornerQuadint = siblingDown(cornerQuadint)
+    }
+    return neighbors;
+}
+
+/**
+ * get the kring distances of a quadint
+ * @param  {int} origin quadint to get the kring of
+ * @param  {int} size in tiles of the desired kring
+ * @return {int}         kring distances of the input quadint
+ */
+export function kRingDistances (origin, size) {
+    if (size === 0) {
+        return [{ index: origin.toString(), distance: 0 }];
+    }
+
+    let cornerQuadint = origin;
+    // Traverse to top left corner
+    for (let i = 0; i < size; i++) {
+        cornerQuadint = siblingLeft(cornerQuadint);
+        cornerQuadint = siblingUp(cornerQuadint)
+    }
+
+    const neighbors = [];
+    let traversalQuadint;
+    for (let j = -size; j <= size; j++) {
+        traversalQuadint = cornerQuadint;
+        for (let i = -size; i <= size; i++) {
+            neighbors.push({
+                index: traversalQuadint.toString(),
+                distance: Math.max(Math.abs(i), Math.abs(j)) // Chebychev distance
+            });
+            traversalQuadint = siblingRight(traversalQuadint);
+        }
+        cornerQuadint = siblingDown(cornerQuadint)
+    }
+    return neighbors.sort((a, b) => (a['distance'] > b['distance']) ? 1 : -1);
+}
+
+/**
+ * get an array of quadints containing a geography for given zooms
+ * @param  {object} poly    geography we want to extract the quadints from
+ * @param  {struct} limits  struct containing the range of zooms
+ * @return {array}          array of quadints containing a geography
+ */
+export function geojsonToQuadints (poly, limits) {
+    return tilecover.tiles(poly, limits).map(tile => quadintFromZXY(tile[2], tile[0], tile[1]));
+}
+
+const clipNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/src/quadkey/index.js b/clouds/bigquery/libraries/javascript/src/quadkey/index.js
deleted file mode 100644
index 8ea5e5d7e..000000000
--- a/clouds/bigquery/libraries/javascript/src/quadkey/index.js
+++ /dev/null
@@ -1,310 +0,0 @@
-// ----------------------------
-// -- Copyright (C) 2021 CARTO
-// ----------------------------
-
-import tilebelt from '@mapbox/tilebelt';
-import tilecover from '@mapbox/tile-cover';
-
-/**
- * convert tile coordinates to quadint at specific zoom level
- * @param  {zxycoord} zxy   zoom and tile coordinates
- * @return {int}            quadint for input tile coordinates at input zoom level
- */
-export function quadintFromZXY (z, x, y) {
-    if (z < 0 || z > 29) {
-        throw new Error('Wrong zoom');
-    }
-    const zI = z;
-    if (zI <= 13) {
-        let quadint = y;
-        quadint <<= zI;
-        quadint |= x;
-        quadint <<= 5;
-        quadint |= zI;
-        return quadint;
-    }
-    let quadint = BigInt(y);
-    quadint <<= BigInt(z);
-    quadint |= BigInt(x);
-    quadint <<= BigInt(5);
-    quadint |= BigInt(z);
-    return quadint;
-}
-
-/**
- * convert quadint to tile coordinates and level of zoom
- * @param  {int} quadint   quadint to be converted
- * @return {zxycoord}      level of zoom and tile coordinates
- */
-export function ZXYFromQuadint (quadint) {
-    const quadintBig = BigInt(quadint);
-    const z = quadintBig & BigInt(0x1F);
-    if (z <= 13n) {
-        const zNumber = Number(z);
-        const x = (quadint >> 5) & ((1 << zNumber) - 1);
-        const y = quadint >> (zNumber + 5);
-        return { z: zNumber, x: x, y: y };
-    }
-    const x = (quadintBig >> (5n)) & ((1n << z) - 1n);
-    const y = quadintBig >> (5n + z);
-    return { z: Number(z), x: Number(x), y: Number(y) };
-}
-
-/**
- * get quadint for location at specific zoom level
- * @param  {geocoord} location  location coordinates to convert to quadint
- * @param  {number}   zoom      map zoom level of quadint to return
- * @return {string}             quadint the input location resides in for the input zoom level
- */
-export function quadintFromLocation (long, lat, zoom) {
-    if (zoom < 0 || zoom > 29) {
-        throw new Error('Wrong zoom');
-    }
-    lat = clipNumber(lat, -85.05, 85.05);
-    const tile = tilebelt.pointToTile(long, lat, zoom);
-    return quadintFromZXY(zoom, tile[0], tile[1]);
-}
-
-/**
- * convert quadkey into a quadint
- * @param  {string} quadkey     quadkey to be converted
- * @return {int}                quadint
- */
-export function quadintFromQuadkey (quadkey) {
-    const z = quadkey.length;
-    const tile = tilebelt.quadkeyToTile(quadkey);
-    return quadintFromZXY(z, tile[0], tile[1]);
-}
-
-/**
- * convert quadint into a quadkey
- * @param  {int} quadint    quadint to be converted
- * @return {string}         quadkey
- */
-export function quadkeyFromQuadint (quadint) {
-    const tile = ZXYFromQuadint(quadint);
-    return tilebelt.tileToQuadkey([tile.x, tile.y, tile.z]);
-}
-
-/**
- * get the bounding box for a quadint in location coordinates
- * @param  {int} quadint    quadint to get bounding box from
- * @return {bbox}           bounding box for the input quadint
- */
-export function bbox (quadint) {
-    const tile = ZXYFromQuadint(quadint);
-    return tilebelt.tileToBBOX([tile.x, tile.y, tile.z]);
-}
-
-/**
- * get the GeoJSON with the bounding box for a quadint in location coordinates
- * @param  {int} quadint    quadint to get bounding box from
- * @return {GeoJSON}        GeoJSON with the bounding box for the input quadint
- */
-export function quadintToGeoJSON (quadint) {
-    const tile = ZXYFromQuadint(quadint);
-    return tilebelt.tileToGeoJSON([tile.x, tile.y, tile.z]);
-}
-
-/**
- * returns the sibling of the given quadint and will wrap
- * @param  {int} quadint      key to get sibling of
- * @param  {string} direction direction of sibling from key
- * @return {int}              sibling key
- */
-export function sibling (quadint, direction) {
-    direction = direction.toLowerCase();
-    if (direction !== 'left' && direction !== 'right' && direction !== 'up' && direction !== 'down') {
-        throw new Error('Wrong direction argument passed to sibling');
-    }
-    if (direction === 'left') {
-        return siblingLeft(quadint);
-    }
-    if (direction === 'right') {
-        return siblingRight(quadint);
-    }
-    if (direction === 'up') {
-        return siblingUp(quadint);
-    }
-    if (direction === 'down') {
-        return siblingDown(quadint);
-    }
-}
-
-/**
- * returns the sibling of the given quadint and will wrap
- * @param  {int} quadint      key to get sibling of
- * @param  {string} direction direction of sibling from key
- * @return {int}              sibling key
- */
-export function siblingLeft (quadint) {
-    const tile = ZXYFromQuadint(quadint);
-    const tilesPerLevel = 2 << (tile.z - 1);
-    const x = tile.x > 0 ? tile.x - 1 : tilesPerLevel - 1;
-    return quadintFromZXY(tile.z, x, tile.y);
-}
-
-/**
- * returns the sibling of the given quadint and will wrap
- * @param  {int} quadint      key to get sibling of
- * @param  {string} direction direction of sibling from key
- * @return {int}              sibling key
- */
-export function siblingRight (quadint) {
-    const tile = ZXYFromQuadint(quadint);
-    const tilesPerLevel = 2 << (tile.z - 1);
-    const x = tile.x < tilesPerLevel - 1 ? tile.x + 1 : 0;
-    return quadintFromZXY(tile.z, x, tile.y);
-}
-
-/**
- * returns the sibling of the given quadint and will wrap
- * @param  {int} quadint      key to get sibling of
- * @param  {string} direction direction of sibling from key
- * @return {int}              sibling key
- */
-export function siblingUp (quadint) {
-    const tile = ZXYFromQuadint(quadint);
-    const tilesPerLevel = 2 << (tile.z - 1);
-    const y = tile.y > 0 ? tile.y - 1 : tilesPerLevel - 1;
-    return quadintFromZXY(tile.z, tile.x, y);
-}
-
-/**
- * returns the sibling of the given quadint and will wrap
- * @param  {int} quadint      key to get sibling of
- * @param  {string} direction direction of sibling from key
- * @return {int}              sibling key
- */
-export function siblingDown (quadint) {
-    const tile = ZXYFromQuadint(quadint);
-    const tilesPerLevel = 2 << (tile.z - 1);
-    const y = tile.y < tilesPerLevel - 1 ? tile.y + 1 : 0;
-    return quadintFromZXY(tile.z, tile.x, y);
-}
-
-/**
- * get all the children quadints of a quadint
- * @param  {int} quadint    quadint to get the children of
- * @param  {int} resolution resolution of the desired children
- * @return {array}          array of quadints representing the children of the input quadint
- */
-export function toChildren (quadint, resolution) {
-    const zxy = ZXYFromQuadint(quadint);
-    if (zxy.z < 0 || zxy.z > 28) {
-        throw new Error('Wrong quadint zoom');
-    }
-
-    if (resolution < 0 || resolution <= zxy.z) {
-        throw new Error('Wrong resolution');
-    }
-    const diffZ = resolution - zxy.z;
-    const mask = (1 << diffZ) - 1;
-    const minTileX = zxy.x << diffZ;
-    const maxTileX = minTileX | mask;
-    const minTileY = zxy.y << diffZ;
-    const maxTileY = minTileY | mask;
-    const children = [];
-    let x, y;
-    for (x = minTileX; x <= maxTileX; x++) {
-        for (y = minTileY; y <= maxTileY; y++) {
-            children.push(quadintFromZXY(resolution, x, y));
-        }
-    }
-    return children;
-}
-
-/**
- * get the parent of a quadint
- * @param  {int} quadint quadint to get the parent of
- * @param  {int} resolution resolution of the desired parent
- * @return {int}         parent of the input quadint
- */
-export function toParent (quadint, resolution) {
-    const zxy = ZXYFromQuadint(quadint);
-    if (zxy.z < 1 || zxy.z > 29) {
-        throw new Error('Wrong quadint zoom');
-    }
-    if (resolution < 0 || resolution >= zxy.z) {
-        throw new Error('Wrong resolution');
-    }
-    return quadintFromZXY(resolution, zxy.x >> (zxy.z - resolution), zxy.y >> (zxy.z - resolution));
-}
-
-/**
- * get the kring of a quadint
- * @param  {int} origin quadint to get the kring of
- * @param  {int} size in tiles of the desired kring
- * @return {int}         kring of the input quadint
- */
-export function kRing (origin, size) {
-    if (size === 0) {
-        return [origin.toString()];
-    }
-
-    let i, j;
-    let cornerQuadint = origin;
-    // Traverse to top left corner
-    for (i = 0; i < size; i++) {
-        cornerQuadint = siblingLeft(cornerQuadint);
-        cornerQuadint = siblingUp(cornerQuadint)
-    }
-
-    const neighbors = [];
-    let traversalQuadint;
-    for (j = 0; j < size * 2 + 1; j++) {
-        traversalQuadint = cornerQuadint;
-        for (i = 0; i < size * 2 + 1; i++) {
-            neighbors.push(traversalQuadint.toString());
-            traversalQuadint = siblingRight(traversalQuadint);
-        }
-        cornerQuadint = siblingDown(cornerQuadint)
-    }
-    return neighbors;
-}
-
-/**
- * get the kring distances of a quadint
- * @param  {int} origin quadint to get the kring of
- * @param  {int} size in tiles of the desired kring
- * @return {int}         kring distances of the input quadint
- */
-export function kRingDistances (origin, size) {
-    if (size === 0) {
-        return [{ index: origin.toString(), distance: 0 }];
-    }
-
-    let cornerQuadint = origin;
-    // Traverse to top left corner
-    for (let i = 0; i < size; i++) {
-        cornerQuadint = siblingLeft(cornerQuadint);
-        cornerQuadint = siblingUp(cornerQuadint)
-    }
-
-    const neighbors = [];
-    let traversalQuadint;
-    for (let j = -size; j <= size; j++) {
-        traversalQuadint = cornerQuadint;
-        for (let i = -size; i <= size; i++) {
-            neighbors.push({
-                index: traversalQuadint.toString(),
-                distance: Math.max(Math.abs(i), Math.abs(j)) // Chebychev distance
-            });
-            traversalQuadint = siblingRight(traversalQuadint);
-        }
-        cornerQuadint = siblingDown(cornerQuadint)
-    }
-    return neighbors.sort((a, b) => (a['distance'] > b['distance']) ? 1 : -1);
-}
-
-/**
- * get an array of quadints containing a geography for given zooms
- * @param  {object} poly    geography we want to extract the quadints from
- * @param  {struct} limits  struct containing the range of zooms
- * @return {array}          array of quadints containing a geography
- */
-export function geojsonToQuadints (poly, limits) {
-    return tilecover.tiles(poly, limits).map(tile => quadintFromZXY(tile[2], tile[0], tile[1]));
-}
-
-const clipNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/src/random.js b/clouds/bigquery/libraries/javascript/src/random.js
index fde5772e1..4326c343b 100644
--- a/clouds/bigquery/libraries/javascript/src/random.js
+++ b/clouds/bigquery/libraries/javascript/src/random.js
@@ -1,6 +1,6 @@
 import { bbox, booleanPointInPolygon, randomPoint } from '@turf/turf';
 
-function generateRandomPointsInPolygon (polygon, numPoints) {
+export function generateRandomPointsInPolygon (polygon, numPoints) {
     const randomPoints = [];
     while (randomPoints.length < numPoints) {
         const point = randomPoint(1, { bbox: bbox(polygon) }).features[0];
@@ -9,16 +9,4 @@ function generateRandomPointsInPolygon (polygon, numPoints) {
         }
     }
     return randomPoints;
-}
-
-function generateRandomPointInPolygon (polygon) {
-    let point
-    do  {
-        point = randomPoint(1, { bbox: bbox(polygon) }).features[0];
-    } while (!booleanPointInPolygon(point, polygon))
-    return JSON.stringify(point.geometry);
-}
-
-export default {
-    generateRandomPointsInPolygon
-};
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/src/s2.js b/clouds/bigquery/libraries/javascript/src/s2.js
index a4590d8eb..3b28bd5cb 100644
--- a/clouds/bigquery/libraries/javascript/src/s2.js
+++ b/clouds/bigquery/libraries/javascript/src/s2.js
@@ -1,9 +1,596 @@
-import { S2 } from './s2/index';
-
-export default {
-    keyToId: S2.keyToId,
-    idToKey: S2.idToKey,
-    latLngToKey: S2.latLngToKey,
-    FromHilbertQuadKey: S2.S2Cell.FromHilbertQuadKey,
-    idToLatLng: S2.idToLatLng
-};
\ No newline at end of file
+/* eslint-disable */
+/// S2 Geometry functions
+// the regional scoreboard is based on a level 6 S2 Cell
+// - https://docs.google.com/presentation/d/1Hl4KapfAENAOf4gv-pSngKwvS_jwNVHRPZTTDzXXn6Q/view?pli=1#slide=id.i22
+// at the time of writing there's no actual API for the intel map to retrieve scoreboard data,
+// but it's still useful to plot the score cells on the intel map
+
+
+// the S2 geometry is based on projecting the earth sphere onto a cube, with some scaling of face coordinates to
+// keep things close to approximate equal area for adjacent cells
+// to convert a lat,lng into a cell id:
+// - convert lat,lng to x,y,z
+// - convert x,y,z into face,u,v
+// - u,v scaled to s,t with quadratic formula
+// - s,t converted to integer i,j offsets
+// - i,j converted to a position along a Hubbert space-filling curve
+// - combine face,position to get the cell id
+
+//NOTE: compared to the google S2 geometry library, we vary from their code in the following ways
+// - cell IDs: they combine face and the hilbert curve position into a single 64 bit number. this gives efficient space
+//             and speed. javascript doesn't have appropriate data types, and speed is not cricical, so we use
+//             as [face,[bitpair,bitpair,...]] instead
+// - i,j: they always use 30 bits, adjusting as needed. we use 0 to (1<<level)-1 instead
+//        (so GetSizeIJ for a cell is always 1)
+
+(function (exports) {
+'use strict';
+
+var S2 = exports.S2 = { L: {} };
+
+S2.L.LatLng = function (/*Number*/ rawLat, /*Number*/ rawLng, /*Boolean*/ noWrap) {
+  var lat = parseFloat(rawLat, 10);
+  var lng = parseFloat(rawLng, 10);
+
+  if (isNaN(lat) || isNaN(lng)) {
+    throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')');
+  }
+
+  if (noWrap !== true) {
+    lat = Math.max(Math.min(lat, 90), -90);                 // clamp latitude into -90..90
+    lng = (lng + 180) % 360 + ((lng < -180 || lng === 180) ? 180 : -180);   // wrap longtitude into -180..180
+  }
+
+  return { lat: lat, lng: lng };
+};
+
+S2.L.LatLng.DEG_TO_RAD = Math.PI / 180;
+S2.L.LatLng.RAD_TO_DEG = 180 / Math.PI;
+
+/*
+S2.LatLngToXYZ = function(latLng) {
+  // http://stackoverflow.com/questions/8981943/lat-long-to-x-y-z-position-in-js-not-working
+  var lat = latLng.lat;
+  var lon = latLng.lng;
+  var DEG_TO_RAD = Math.PI / 180.0;
+
+  var phi = lat * DEG_TO_RAD;
+  var theta = lon * DEG_TO_RAD;
+
+  var cosLat = Math.cos(phi);
+  var sinLat = Math.sin(phi);
+  var cosLon = Math.cos(theta);
+  var sinLon = Math.sin(theta);
+  var rad = 500.0;
+
+  return [
+    rad * cosLat * cosLon
+  , rad * cosLat * sinLon
+  , rad * sinLat
+  ];
+};
+*/
+S2.LatLngToXYZ = function(latLng) {
+  var d2r = S2.L.LatLng.DEG_TO_RAD;
+
+  var phi = latLng.lat*d2r;
+  var theta = latLng.lng*d2r;
+
+  var cosphi = Math.cos(phi);
+
+  return [Math.cos(theta)*cosphi, Math.sin(theta)*cosphi, Math.sin(phi)];
+};
+
+S2.XYZToLatLng = function(xyz) {
+  var r2d = S2.L.LatLng.RAD_TO_DEG;
+
+  var lat = Math.atan2(xyz[2], Math.sqrt(xyz[0]*xyz[0]+xyz[1]*xyz[1]));
+  var lng = Math.atan2(xyz[1], xyz[0]);
+
+  return S2.L.LatLng(lat*r2d, lng*r2d);
+};
+
+var largestAbsComponent = function(xyz) {
+  var temp = [Math.abs(xyz[0]), Math.abs(xyz[1]), Math.abs(xyz[2])];
+
+  if (temp[0] > temp[1]) {
+    if (temp[0] > temp[2]) {
+      return 0;
+    } else {
+      return 2;
+    }
+  } else {
+    if (temp[1] > temp[2]) {
+      return 1;
+    } else {
+      return 2;
+    }
+  }
+
+};
+
+var faceXYZToUV = function(face,xyz) {
+  var u,v;
+
+  switch (face) {
+    case 0: u =  xyz[1]/xyz[0]; v =  xyz[2]/xyz[0]; break;
+    case 1: u = -xyz[0]/xyz[1]; v =  xyz[2]/xyz[1]; break;
+    case 2: u = -xyz[0]/xyz[2]; v = -xyz[1]/xyz[2]; break;
+    case 3: u =  xyz[2]/xyz[0]; v =  xyz[1]/xyz[0]; break;
+    case 4: u =  xyz[2]/xyz[1]; v = -xyz[0]/xyz[1]; break;
+    case 5: u = -xyz[1]/xyz[2]; v = -xyz[0]/xyz[2]; break;
+    default: throw {error: 'Invalid face'};
+  }
+
+  return [u,v];
+};
+
+
+
+
+S2.XYZToFaceUV = function(xyz) {
+  var face = largestAbsComponent(xyz);
+
+  if (xyz[face] < 0) {
+    face += 3;
+  }
+
+  var uv = faceXYZToUV (face,xyz);
+
+  return [face, uv];
+};
+
+S2.FaceUVToXYZ = function(face,uv) {
+  var u = uv[0];
+  var v = uv[1];
+
+  switch (face) {
+    case 0: return [ 1, u, v];
+    case 1: return [-u, 1, v];
+    case 2: return [-u,-v, 1];
+    case 3: return [-1,-v,-u];
+    case 4: return [ v,-1,-u];
+    case 5: return [ v, u,-1];
+    default: throw {error: 'Invalid face'};
+  }
+};
+
+var singleSTtoUV = function(st) {
+  if (st >= 0.5) {
+    return (1/3.0) * (4*st*st - 1);
+  } else {
+    return (1/3.0) * (1 - (4*(1-st)*(1-st)));
+  }
+};
+
+S2.STToUV = function(st) {
+  return [singleSTtoUV(st[0]), singleSTtoUV(st[1])];
+};
+
+
+var singleUVtoST = function(uv) {
+  if (uv >= 0) {
+    return 0.5 * Math.sqrt (1 + 3*uv);
+  } else {
+    return 1 - 0.5 * Math.sqrt (1 - 3*uv);
+  }
+};
+S2.UVToST = function(uv) {
+  return [singleUVtoST(uv[0]), singleUVtoST(uv[1])];
+};
+
+
+S2.STToIJ = function(st,order) {
+  var maxSize = (1<<order);
+
+  var singleSTtoIJ = function(st) {
+    var ij = Math.floor(st * maxSize);
+    return Math.max(0, Math.min(maxSize-1, ij));
+  };
+
+  return [singleSTtoIJ(st[0]), singleSTtoIJ(st[1])];
+};
+
+
+S2.IJToST = function(ij,order,offsets) {
+  var maxSize = (1<<order);
+
+  return [
+    (ij[0]+offsets[0])/maxSize,
+    (ij[1]+offsets[1])/maxSize
+  ];
+};
+
+
+
+var rotateAndFlipQuadrant = function(n, point, rx, ry)
+{
+	var newX, newY;
+	if(ry == 0)
+	{
+		if(rx == 1){
+			point.x = n - 1 - point.x;
+			point.y = n - 1 - point.y
+
+		}
+
+    var x = point.x;
+		point.x = point.y
+		point.y = x;
+	}
+
+}
+
+
+
+
+
+// hilbert space-filling curve
+// based on http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves
+// note: rather then calculating the final integer hilbert position, we just return the list of quads
+// this ensures no precision issues whth large orders (S3 cell IDs use up to 30), and is more
+// convenient for pulling out the individual bits as needed later
+var pointToHilbertQuadList = function(x,y,order,face) {
+  var hilbertMap = {
+    'a': [ [0,'d'], [1,'a'], [3,'b'], [2,'a'] ],
+    'b': [ [2,'b'], [1,'b'], [3,'a'], [0,'c'] ],
+    'c': [ [2,'c'], [3,'d'], [1,'c'], [0,'b'] ],
+    'd': [ [0,'a'], [3,'c'], [1,'d'], [2,'d'] ]
+  };
+
+  if ('number' !== typeof face) {
+    console.warn(new Error("called pointToHilbertQuadList without face value, defaulting to '0'").stack);
+  }
+  var currentSquare = (face % 2) ? 'd' : 'a';
+  var positions = [];
+
+  for (var i=order-1; i>=0; i--) {
+
+    var mask = 1<<i;
+
+    var quad_x = x&mask ? 1 : 0;
+    var quad_y = y&mask ? 1 : 0;
+
+    var t = hilbertMap[currentSquare][quad_x*2+quad_y];
+
+    positions.push(t[0]);
+
+    currentSquare = t[1];
+  }
+
+  return positions;
+};
+
+// S2Cell class
+
+S2.S2Cell = function(){};
+
+S2.S2Cell.FromHilbertQuadKey = function(hilbertQuadkey) {
+  var parts = hilbertQuadkey.split('/');
+  var face = parseInt(parts[0]);
+  var position = parts[1];
+  var maxLevel = position.length;
+  var point = {
+    x : 0,
+    y: 0
+  };
+  var i;
+  var level;
+  var bit;
+  var rx, ry;
+  var val;
+
+	for(i = maxLevel - 1; i >= 0; i--) {
+
+		level = maxLevel - i;
+		bit = position[i];
+		rx = 0;
+    ry = 0;
+		if (bit === '1') {
+			ry = 1;
+		}
+		else if (bit === '2') {
+			rx = 1;
+			ry = 1;
+		}
+		else if (bit === '3') {
+			rx = 1;
+		}
+
+		val = Math.pow(2, level - 1);
+		rotateAndFlipQuadrant(val, point, rx, ry);
+
+		point.x += val * rx;
+		point.y += val * ry;
+
+	}
+
+  if (face % 2 === 1) {
+    var t = point.x;
+    point.x = point.y;
+    point.y = t;
+  }
+
+
+  return S2.S2Cell.FromFaceIJ(parseInt(face), [point.x, point.y], level);
+};
+
+//static method to construct
+S2.S2Cell.FromLatLng = function(latLng, level) {
+  if ((!latLng.lat && latLng.lat !== 0) || (!latLng.lng && latLng.lng !== 0)) {
+    throw new Error("Pass { lat: lat, lng: lng } to S2.S2Cell.FromLatLng");
+  }
+  var xyz = S2.LatLngToXYZ(latLng);
+
+  var faceuv = S2.XYZToFaceUV(xyz);
+  var st = S2.UVToST(faceuv[1]);
+
+  var ij = S2.STToIJ(st,level);
+
+  return S2.S2Cell.FromFaceIJ (faceuv[0], ij, level);
+};
+
+/*
+S2.faceIjLevelToXyz = function (face, ij, level) {
+  var st = S2.IJToST(ij, level, [0.5, 0.5]);
+  var uv = S2.STToUV(st);
+  var xyz = S2.FaceUVToXYZ(face, uv);
+
+  return S2.XYZToLatLng(xyz);
+  return xyz;
+};
+*/
+
+S2.S2Cell.FromFaceIJ = function(face,ij,level) {
+  var cell = new S2.S2Cell();
+  cell.face = face;
+  cell.ij = ij;
+  cell.level = level;
+
+  return cell;
+};
+
+
+S2.S2Cell.prototype.toString = function() {
+  return 'F'+this.face+'ij['+this.ij[0]+','+this.ij[1]+']@'+this.level;
+};
+
+S2.S2Cell.prototype.getLatLng = function() {
+  var st = S2.IJToST(this.ij,this.level, [0.5,0.5]);
+  var uv = S2.STToUV(st);
+  var xyz = S2.FaceUVToXYZ(this.face, uv);
+
+  return S2.XYZToLatLng(xyz);
+};
+
+S2.S2Cell.prototype.getCornerLatLngs = function() {
+  var result = [];
+  var offsets = [
+    [ 0.0, 0.0 ],
+    [ 0.0, 1.0 ],
+    [ 1.0, 1.0 ],
+    [ 1.0, 0.0 ]
+  ];
+
+  for (var i=0; i<4; i++) {
+    var st = S2.IJToST(this.ij, this.level, offsets[i]);
+    var uv = S2.STToUV(st);
+    var xyz = S2.FaceUVToXYZ(this.face, uv);
+
+    result.push ( S2.XYZToLatLng(xyz) );
+  }
+  return result;
+};
+
+
+S2.S2Cell.prototype.getFaceAndQuads = function () {
+  var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level, this.face);
+
+  return [this.face,quads];
+};
+S2.S2Cell.prototype.toHilbertQuadkey = function () {
+  var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level, this.face);
+
+  return this.face.toString(10) + '/' + quads.join('');
+};
+
+S2.latLngToNeighborKeys = S2.S2Cell.latLngToNeighborKeys = function (lat, lng, level) {
+  return S2.S2Cell.FromLatLng({ lat: lat, lng: lng }, level).getNeighbors().map(function (cell) {
+    return cell.toHilbertQuadkey();
+  });
+};
+S2.S2Cell.prototype.getNeighbors = function() {
+
+  var fromFaceIJWrap = function(face,ij,level) {
+    var maxSize = (1<<level);
+    if (ij[0]>=0 && ij[1]>=0 && ij[0]<maxSize && ij[1]<maxSize) {
+      // no wrapping out of bounds
+      return S2.S2Cell.FromFaceIJ(face,ij,level);
+    } else {
+      // the new i,j are out of range.
+      // with the assumption that they're only a little past the borders we can just take the points as
+      // just beyond the cube face, project to XYZ, then re-create FaceUV from the XYZ vector
+
+      var st = S2.IJToST(ij,level,[0.5,0.5]);
+      var uv = S2.STToUV(st);
+      var xyz = S2.FaceUVToXYZ(face,uv);
+      var faceuv = S2.XYZToFaceUV(xyz);
+      face = faceuv[0];
+      uv = faceuv[1];
+      st = S2.UVToST(uv);
+      ij = S2.STToIJ(st,level);
+      return S2.S2Cell.FromFaceIJ (face, ij, level);
+    }
+  };
+
+  var face = this.face;
+  var i = this.ij[0];
+  var j = this.ij[1];
+  var level = this.level;
+
+
+  return [
+    fromFaceIJWrap(face, [i-1,j], level),
+    fromFaceIJWrap(face, [i,j-1], level),
+    fromFaceIJWrap(face, [i+1,j], level),
+    fromFaceIJWrap(face, [i,j+1], level)
+  ];
+
+};
+
+//
+// Functional Style
+//
+S2.FACE_BITS = 3;
+S2.MAX_LEVEL = 30;
+S2.POS_BITS = (2 * S2.MAX_LEVEL) + 1; // 61 (60 bits of data, 1 bit lsb marker)
+
+S2.facePosLevelToId = S2.S2Cell.facePosLevelToId = S2.fromFacePosLevel = function (faceN, posS, levelN) {
+  var Long = exports.dcodeIO && exports.dcodeIO.Long || require('long');
+  var faceB;
+  var posB;
+  var bin;
+
+  if (!levelN) {
+    levelN = posS.length;
+  }
+  if (posS.length > levelN) {
+    posS = posS.substr(0, levelN);
+  }
+
+  // 3-bit face value
+  faceB = Long.fromString(faceN.toString(10), true, 10).toString(2);
+  while (faceB.length < S2.FACE_BITS) {
+    faceB = '0' + faceB;
+  }
+
+  // 60-bit position value
+  posB = Long.fromString(posS, true, 4).toString(2);
+  while (posB.length < (2 * levelN)) {
+    posB = '0' + posB;
+  }
+
+  bin = faceB + posB;
+  // 1-bit lsb marker
+  bin += '1';
+  // n-bit padding to 64-bits
+  while (bin.length < (S2.FACE_BITS + S2.POS_BITS)) {
+    bin += '0';
+  }
+
+  return Long.fromString(bin, true, 2).toSigned().toString(10);
+};
+
+S2.keyToId = S2.S2Cell.keyToId
+= S2.toId = S2.toCellId = S2.fromKey
+= function (key) {
+  var parts = key.split('/');
+
+  return S2.fromFacePosLevel(parts[0], parts[1], parts[1].length);
+};
+
+S2.idToKey = S2.S2Cell.idToKey
+= S2.S2Cell.toKey = S2.toKey
+= S2.fromId = S2.fromCellId
+= S2.S2Cell.toHilbertQuadkey  = S2.toHilbertQuadkey
+= function (idS) {
+  var Long = exports.dcodeIO && exports.dcodeIO.Long || require('long');
+  var bin = Long.fromString(idS, true, 10).toString(2);
+
+  while (bin.length < (S2.FACE_BITS + S2.POS_BITS)) {
+    bin = '0' + bin;
+  }
+
+  // MUST come AFTER binstr has been left-padded with '0's
+  var lsbIndex = bin.lastIndexOf('1');
+  // substr(start, len)
+  // substring(start, end) // includes start, does not include end
+  var faceB = bin.substring(0, 3);
+  // posB will always be a multiple of 2 (or it's invalid)
+  var posB = bin.substring(3, lsbIndex);
+  var levelN = posB.length / 2;
+
+  var faceS = Long.fromString(faceB, true, 2).toString(10);
+  var posS = Long.fromString(posB, true, 2).toString(4);
+
+  while (posS.length < levelN) {
+    posS = '0' + posS;
+  }
+
+  return faceS + '/' + posS;
+};
+
+S2.keyToLatLng = S2.S2Cell.keyToLatLng = function (key) {
+  var cell2 = S2.S2Cell.FromHilbertQuadKey(key);
+  return cell2.getLatLng();
+};
+
+S2.idToLatLng = S2.S2Cell.idToLatLng = function (id) {
+  var key = S2.idToKey(id);
+  return S2.keyToLatLng(key);
+};
+
+S2.S2Cell.latLngToKey = S2.latLngToKey
+= S2.latLngToQuadkey = function (lat, lng, level) {
+  if (isNaN(level) || level < 1 || level > 30) {
+    throw new Error("'level' is not a number between 1 and 30 (but it should be)");
+  }
+  // TODO
+  //
+  // S2.idToLatLng(id)
+  // S2.keyToLatLng(key)
+  // S2.nextFace(key)     // prevent wrapping on nextKey
+  // S2.prevFace(key)     // prevent wrapping on prevKey
+  //
+  // .toKeyArray(id)  // face,quadtree
+  // .toKey(id)       // hilbert
+  // .toPoint(id)     // ij
+  // .toId(key)       // uint64 (as string)
+  // .toLong(key)     // long.js
+  // .toLatLng(id)    // object? or array?, or string (with comma)?
+  //
+  // maybe S2.HQ.x, S2.GPS.x, S2.CI.x?
+  return S2.S2Cell.FromLatLng({ lat: lat, lng: lng }, level).toHilbertQuadkey();
+};
+
+S2.stepKey = function (key, num) {
+  var Long = exports.dcodeIO && exports.dcodeIO.Long || require('long');
+  var parts = key.split('/');
+
+  var faceS = parts[0];
+  var posS = parts[1];
+  var level = parts[1].length;
+
+  var posL = Long.fromString(posS, true, 4);
+  // TODO handle wrapping (0 === pos + 1)
+  // (only on the 12 edges of the globe)
+  var otherL;
+  if (num > 0) {
+    otherL = posL.add(Math.abs(num));
+  }
+  else if (num < 0) {
+    otherL = posL.subtract(Math.abs(num));
+  }
+  var otherS = otherL.toString(4);
+
+  if ('0' === otherS) {
+    console.warning(new Error("face/position wrapping is not yet supported"));
+  }
+
+  while (otherS.length < level) {
+    otherS = '0' + otherS;
+  }
+
+  return faceS + '/' + otherS;
+};
+
+S2.S2Cell.prevKey = S2.prevKey = function (key) {
+  return S2.stepKey(key, -1);
+};
+
+S2.S2Cell.nextKey = S2.nextKey = function (key) {
+  return S2.stepKey(key, 1);
+};
+
+})('undefined' !== typeof module ? module.exports : window);
+/* eslint-enable */
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/src/s2/index.js b/clouds/bigquery/libraries/javascript/src/s2/index.js
deleted file mode 100644
index 3b28bd5cb..000000000
--- a/clouds/bigquery/libraries/javascript/src/s2/index.js
+++ /dev/null
@@ -1,596 +0,0 @@
-/* eslint-disable */
-/// S2 Geometry functions
-// the regional scoreboard is based on a level 6 S2 Cell
-// - https://docs.google.com/presentation/d/1Hl4KapfAENAOf4gv-pSngKwvS_jwNVHRPZTTDzXXn6Q/view?pli=1#slide=id.i22
-// at the time of writing there's no actual API for the intel map to retrieve scoreboard data,
-// but it's still useful to plot the score cells on the intel map
-
-
-// the S2 geometry is based on projecting the earth sphere onto a cube, with some scaling of face coordinates to
-// keep things close to approximate equal area for adjacent cells
-// to convert a lat,lng into a cell id:
-// - convert lat,lng to x,y,z
-// - convert x,y,z into face,u,v
-// - u,v scaled to s,t with quadratic formula
-// - s,t converted to integer i,j offsets
-// - i,j converted to a position along a Hubbert space-filling curve
-// - combine face,position to get the cell id
-
-//NOTE: compared to the google S2 geometry library, we vary from their code in the following ways
-// - cell IDs: they combine face and the hilbert curve position into a single 64 bit number. this gives efficient space
-//             and speed. javascript doesn't have appropriate data types, and speed is not cricical, so we use
-//             as [face,[bitpair,bitpair,...]] instead
-// - i,j: they always use 30 bits, adjusting as needed. we use 0 to (1<<level)-1 instead
-//        (so GetSizeIJ for a cell is always 1)
-
-(function (exports) {
-'use strict';
-
-var S2 = exports.S2 = { L: {} };
-
-S2.L.LatLng = function (/*Number*/ rawLat, /*Number*/ rawLng, /*Boolean*/ noWrap) {
-  var lat = parseFloat(rawLat, 10);
-  var lng = parseFloat(rawLng, 10);
-
-  if (isNaN(lat) || isNaN(lng)) {
-    throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')');
-  }
-
-  if (noWrap !== true) {
-    lat = Math.max(Math.min(lat, 90), -90);                 // clamp latitude into -90..90
-    lng = (lng + 180) % 360 + ((lng < -180 || lng === 180) ? 180 : -180);   // wrap longtitude into -180..180
-  }
-
-  return { lat: lat, lng: lng };
-};
-
-S2.L.LatLng.DEG_TO_RAD = Math.PI / 180;
-S2.L.LatLng.RAD_TO_DEG = 180 / Math.PI;
-
-/*
-S2.LatLngToXYZ = function(latLng) {
-  // http://stackoverflow.com/questions/8981943/lat-long-to-x-y-z-position-in-js-not-working
-  var lat = latLng.lat;
-  var lon = latLng.lng;
-  var DEG_TO_RAD = Math.PI / 180.0;
-
-  var phi = lat * DEG_TO_RAD;
-  var theta = lon * DEG_TO_RAD;
-
-  var cosLat = Math.cos(phi);
-  var sinLat = Math.sin(phi);
-  var cosLon = Math.cos(theta);
-  var sinLon = Math.sin(theta);
-  var rad = 500.0;
-
-  return [
-    rad * cosLat * cosLon
-  , rad * cosLat * sinLon
-  , rad * sinLat
-  ];
-};
-*/
-S2.LatLngToXYZ = function(latLng) {
-  var d2r = S2.L.LatLng.DEG_TO_RAD;
-
-  var phi = latLng.lat*d2r;
-  var theta = latLng.lng*d2r;
-
-  var cosphi = Math.cos(phi);
-
-  return [Math.cos(theta)*cosphi, Math.sin(theta)*cosphi, Math.sin(phi)];
-};
-
-S2.XYZToLatLng = function(xyz) {
-  var r2d = S2.L.LatLng.RAD_TO_DEG;
-
-  var lat = Math.atan2(xyz[2], Math.sqrt(xyz[0]*xyz[0]+xyz[1]*xyz[1]));
-  var lng = Math.atan2(xyz[1], xyz[0]);
-
-  return S2.L.LatLng(lat*r2d, lng*r2d);
-};
-
-var largestAbsComponent = function(xyz) {
-  var temp = [Math.abs(xyz[0]), Math.abs(xyz[1]), Math.abs(xyz[2])];
-
-  if (temp[0] > temp[1]) {
-    if (temp[0] > temp[2]) {
-      return 0;
-    } else {
-      return 2;
-    }
-  } else {
-    if (temp[1] > temp[2]) {
-      return 1;
-    } else {
-      return 2;
-    }
-  }
-
-};
-
-var faceXYZToUV = function(face,xyz) {
-  var u,v;
-
-  switch (face) {
-    case 0: u =  xyz[1]/xyz[0]; v =  xyz[2]/xyz[0]; break;
-    case 1: u = -xyz[0]/xyz[1]; v =  xyz[2]/xyz[1]; break;
-    case 2: u = -xyz[0]/xyz[2]; v = -xyz[1]/xyz[2]; break;
-    case 3: u =  xyz[2]/xyz[0]; v =  xyz[1]/xyz[0]; break;
-    case 4: u =  xyz[2]/xyz[1]; v = -xyz[0]/xyz[1]; break;
-    case 5: u = -xyz[1]/xyz[2]; v = -xyz[0]/xyz[2]; break;
-    default: throw {error: 'Invalid face'};
-  }
-
-  return [u,v];
-};
-
-
-
-
-S2.XYZToFaceUV = function(xyz) {
-  var face = largestAbsComponent(xyz);
-
-  if (xyz[face] < 0) {
-    face += 3;
-  }
-
-  var uv = faceXYZToUV (face,xyz);
-
-  return [face, uv];
-};
-
-S2.FaceUVToXYZ = function(face,uv) {
-  var u = uv[0];
-  var v = uv[1];
-
-  switch (face) {
-    case 0: return [ 1, u, v];
-    case 1: return [-u, 1, v];
-    case 2: return [-u,-v, 1];
-    case 3: return [-1,-v,-u];
-    case 4: return [ v,-1,-u];
-    case 5: return [ v, u,-1];
-    default: throw {error: 'Invalid face'};
-  }
-};
-
-var singleSTtoUV = function(st) {
-  if (st >= 0.5) {
-    return (1/3.0) * (4*st*st - 1);
-  } else {
-    return (1/3.0) * (1 - (4*(1-st)*(1-st)));
-  }
-};
-
-S2.STToUV = function(st) {
-  return [singleSTtoUV(st[0]), singleSTtoUV(st[1])];
-};
-
-
-var singleUVtoST = function(uv) {
-  if (uv >= 0) {
-    return 0.5 * Math.sqrt (1 + 3*uv);
-  } else {
-    return 1 - 0.5 * Math.sqrt (1 - 3*uv);
-  }
-};
-S2.UVToST = function(uv) {
-  return [singleUVtoST(uv[0]), singleUVtoST(uv[1])];
-};
-
-
-S2.STToIJ = function(st,order) {
-  var maxSize = (1<<order);
-
-  var singleSTtoIJ = function(st) {
-    var ij = Math.floor(st * maxSize);
-    return Math.max(0, Math.min(maxSize-1, ij));
-  };
-
-  return [singleSTtoIJ(st[0]), singleSTtoIJ(st[1])];
-};
-
-
-S2.IJToST = function(ij,order,offsets) {
-  var maxSize = (1<<order);
-
-  return [
-    (ij[0]+offsets[0])/maxSize,
-    (ij[1]+offsets[1])/maxSize
-  ];
-};
-
-
-
-var rotateAndFlipQuadrant = function(n, point, rx, ry)
-{
-	var newX, newY;
-	if(ry == 0)
-	{
-		if(rx == 1){
-			point.x = n - 1 - point.x;
-			point.y = n - 1 - point.y
-
-		}
-
-    var x = point.x;
-		point.x = point.y
-		point.y = x;
-	}
-
-}
-
-
-
-
-
-// hilbert space-filling curve
-// based on http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves
-// note: rather then calculating the final integer hilbert position, we just return the list of quads
-// this ensures no precision issues whth large orders (S3 cell IDs use up to 30), and is more
-// convenient for pulling out the individual bits as needed later
-var pointToHilbertQuadList = function(x,y,order,face) {
-  var hilbertMap = {
-    'a': [ [0,'d'], [1,'a'], [3,'b'], [2,'a'] ],
-    'b': [ [2,'b'], [1,'b'], [3,'a'], [0,'c'] ],
-    'c': [ [2,'c'], [3,'d'], [1,'c'], [0,'b'] ],
-    'd': [ [0,'a'], [3,'c'], [1,'d'], [2,'d'] ]
-  };
-
-  if ('number' !== typeof face) {
-    console.warn(new Error("called pointToHilbertQuadList without face value, defaulting to '0'").stack);
-  }
-  var currentSquare = (face % 2) ? 'd' : 'a';
-  var positions = [];
-
-  for (var i=order-1; i>=0; i--) {
-
-    var mask = 1<<i;
-
-    var quad_x = x&mask ? 1 : 0;
-    var quad_y = y&mask ? 1 : 0;
-
-    var t = hilbertMap[currentSquare][quad_x*2+quad_y];
-
-    positions.push(t[0]);
-
-    currentSquare = t[1];
-  }
-
-  return positions;
-};
-
-// S2Cell class
-
-S2.S2Cell = function(){};
-
-S2.S2Cell.FromHilbertQuadKey = function(hilbertQuadkey) {
-  var parts = hilbertQuadkey.split('/');
-  var face = parseInt(parts[0]);
-  var position = parts[1];
-  var maxLevel = position.length;
-  var point = {
-    x : 0,
-    y: 0
-  };
-  var i;
-  var level;
-  var bit;
-  var rx, ry;
-  var val;
-
-	for(i = maxLevel - 1; i >= 0; i--) {
-
-		level = maxLevel - i;
-		bit = position[i];
-		rx = 0;
-    ry = 0;
-		if (bit === '1') {
-			ry = 1;
-		}
-		else if (bit === '2') {
-			rx = 1;
-			ry = 1;
-		}
-		else if (bit === '3') {
-			rx = 1;
-		}
-
-		val = Math.pow(2, level - 1);
-		rotateAndFlipQuadrant(val, point, rx, ry);
-
-		point.x += val * rx;
-		point.y += val * ry;
-
-	}
-
-  if (face % 2 === 1) {
-    var t = point.x;
-    point.x = point.y;
-    point.y = t;
-  }
-
-
-  return S2.S2Cell.FromFaceIJ(parseInt(face), [point.x, point.y], level);
-};
-
-//static method to construct
-S2.S2Cell.FromLatLng = function(latLng, level) {
-  if ((!latLng.lat && latLng.lat !== 0) || (!latLng.lng && latLng.lng !== 0)) {
-    throw new Error("Pass { lat: lat, lng: lng } to S2.S2Cell.FromLatLng");
-  }
-  var xyz = S2.LatLngToXYZ(latLng);
-
-  var faceuv = S2.XYZToFaceUV(xyz);
-  var st = S2.UVToST(faceuv[1]);
-
-  var ij = S2.STToIJ(st,level);
-
-  return S2.S2Cell.FromFaceIJ (faceuv[0], ij, level);
-};
-
-/*
-S2.faceIjLevelToXyz = function (face, ij, level) {
-  var st = S2.IJToST(ij, level, [0.5, 0.5]);
-  var uv = S2.STToUV(st);
-  var xyz = S2.FaceUVToXYZ(face, uv);
-
-  return S2.XYZToLatLng(xyz);
-  return xyz;
-};
-*/
-
-S2.S2Cell.FromFaceIJ = function(face,ij,level) {
-  var cell = new S2.S2Cell();
-  cell.face = face;
-  cell.ij = ij;
-  cell.level = level;
-
-  return cell;
-};
-
-
-S2.S2Cell.prototype.toString = function() {
-  return 'F'+this.face+'ij['+this.ij[0]+','+this.ij[1]+']@'+this.level;
-};
-
-S2.S2Cell.prototype.getLatLng = function() {
-  var st = S2.IJToST(this.ij,this.level, [0.5,0.5]);
-  var uv = S2.STToUV(st);
-  var xyz = S2.FaceUVToXYZ(this.face, uv);
-
-  return S2.XYZToLatLng(xyz);
-};
-
-S2.S2Cell.prototype.getCornerLatLngs = function() {
-  var result = [];
-  var offsets = [
-    [ 0.0, 0.0 ],
-    [ 0.0, 1.0 ],
-    [ 1.0, 1.0 ],
-    [ 1.0, 0.0 ]
-  ];
-
-  for (var i=0; i<4; i++) {
-    var st = S2.IJToST(this.ij, this.level, offsets[i]);
-    var uv = S2.STToUV(st);
-    var xyz = S2.FaceUVToXYZ(this.face, uv);
-
-    result.push ( S2.XYZToLatLng(xyz) );
-  }
-  return result;
-};
-
-
-S2.S2Cell.prototype.getFaceAndQuads = function () {
-  var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level, this.face);
-
-  return [this.face,quads];
-};
-S2.S2Cell.prototype.toHilbertQuadkey = function () {
-  var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level, this.face);
-
-  return this.face.toString(10) + '/' + quads.join('');
-};
-
-S2.latLngToNeighborKeys = S2.S2Cell.latLngToNeighborKeys = function (lat, lng, level) {
-  return S2.S2Cell.FromLatLng({ lat: lat, lng: lng }, level).getNeighbors().map(function (cell) {
-    return cell.toHilbertQuadkey();
-  });
-};
-S2.S2Cell.prototype.getNeighbors = function() {
-
-  var fromFaceIJWrap = function(face,ij,level) {
-    var maxSize = (1<<level);
-    if (ij[0]>=0 && ij[1]>=0 && ij[0]<maxSize && ij[1]<maxSize) {
-      // no wrapping out of bounds
-      return S2.S2Cell.FromFaceIJ(face,ij,level);
-    } else {
-      // the new i,j are out of range.
-      // with the assumption that they're only a little past the borders we can just take the points as
-      // just beyond the cube face, project to XYZ, then re-create FaceUV from the XYZ vector
-
-      var st = S2.IJToST(ij,level,[0.5,0.5]);
-      var uv = S2.STToUV(st);
-      var xyz = S2.FaceUVToXYZ(face,uv);
-      var faceuv = S2.XYZToFaceUV(xyz);
-      face = faceuv[0];
-      uv = faceuv[1];
-      st = S2.UVToST(uv);
-      ij = S2.STToIJ(st,level);
-      return S2.S2Cell.FromFaceIJ (face, ij, level);
-    }
-  };
-
-  var face = this.face;
-  var i = this.ij[0];
-  var j = this.ij[1];
-  var level = this.level;
-
-
-  return [
-    fromFaceIJWrap(face, [i-1,j], level),
-    fromFaceIJWrap(face, [i,j-1], level),
-    fromFaceIJWrap(face, [i+1,j], level),
-    fromFaceIJWrap(face, [i,j+1], level)
-  ];
-
-};
-
-//
-// Functional Style
-//
-S2.FACE_BITS = 3;
-S2.MAX_LEVEL = 30;
-S2.POS_BITS = (2 * S2.MAX_LEVEL) + 1; // 61 (60 bits of data, 1 bit lsb marker)
-
-S2.facePosLevelToId = S2.S2Cell.facePosLevelToId = S2.fromFacePosLevel = function (faceN, posS, levelN) {
-  var Long = exports.dcodeIO && exports.dcodeIO.Long || require('long');
-  var faceB;
-  var posB;
-  var bin;
-
-  if (!levelN) {
-    levelN = posS.length;
-  }
-  if (posS.length > levelN) {
-    posS = posS.substr(0, levelN);
-  }
-
-  // 3-bit face value
-  faceB = Long.fromString(faceN.toString(10), true, 10).toString(2);
-  while (faceB.length < S2.FACE_BITS) {
-    faceB = '0' + faceB;
-  }
-
-  // 60-bit position value
-  posB = Long.fromString(posS, true, 4).toString(2);
-  while (posB.length < (2 * levelN)) {
-    posB = '0' + posB;
-  }
-
-  bin = faceB + posB;
-  // 1-bit lsb marker
-  bin += '1';
-  // n-bit padding to 64-bits
-  while (bin.length < (S2.FACE_BITS + S2.POS_BITS)) {
-    bin += '0';
-  }
-
-  return Long.fromString(bin, true, 2).toSigned().toString(10);
-};
-
-S2.keyToId = S2.S2Cell.keyToId
-= S2.toId = S2.toCellId = S2.fromKey
-= function (key) {
-  var parts = key.split('/');
-
-  return S2.fromFacePosLevel(parts[0], parts[1], parts[1].length);
-};
-
-S2.idToKey = S2.S2Cell.idToKey
-= S2.S2Cell.toKey = S2.toKey
-= S2.fromId = S2.fromCellId
-= S2.S2Cell.toHilbertQuadkey  = S2.toHilbertQuadkey
-= function (idS) {
-  var Long = exports.dcodeIO && exports.dcodeIO.Long || require('long');
-  var bin = Long.fromString(idS, true, 10).toString(2);
-
-  while (bin.length < (S2.FACE_BITS + S2.POS_BITS)) {
-    bin = '0' + bin;
-  }
-
-  // MUST come AFTER binstr has been left-padded with '0's
-  var lsbIndex = bin.lastIndexOf('1');
-  // substr(start, len)
-  // substring(start, end) // includes start, does not include end
-  var faceB = bin.substring(0, 3);
-  // posB will always be a multiple of 2 (or it's invalid)
-  var posB = bin.substring(3, lsbIndex);
-  var levelN = posB.length / 2;
-
-  var faceS = Long.fromString(faceB, true, 2).toString(10);
-  var posS = Long.fromString(posB, true, 2).toString(4);
-
-  while (posS.length < levelN) {
-    posS = '0' + posS;
-  }
-
-  return faceS + '/' + posS;
-};
-
-S2.keyToLatLng = S2.S2Cell.keyToLatLng = function (key) {
-  var cell2 = S2.S2Cell.FromHilbertQuadKey(key);
-  return cell2.getLatLng();
-};
-
-S2.idToLatLng = S2.S2Cell.idToLatLng = function (id) {
-  var key = S2.idToKey(id);
-  return S2.keyToLatLng(key);
-};
-
-S2.S2Cell.latLngToKey = S2.latLngToKey
-= S2.latLngToQuadkey = function (lat, lng, level) {
-  if (isNaN(level) || level < 1 || level > 30) {
-    throw new Error("'level' is not a number between 1 and 30 (but it should be)");
-  }
-  // TODO
-  //
-  // S2.idToLatLng(id)
-  // S2.keyToLatLng(key)
-  // S2.nextFace(key)     // prevent wrapping on nextKey
-  // S2.prevFace(key)     // prevent wrapping on prevKey
-  //
-  // .toKeyArray(id)  // face,quadtree
-  // .toKey(id)       // hilbert
-  // .toPoint(id)     // ij
-  // .toId(key)       // uint64 (as string)
-  // .toLong(key)     // long.js
-  // .toLatLng(id)    // object? or array?, or string (with comma)?
-  //
-  // maybe S2.HQ.x, S2.GPS.x, S2.CI.x?
-  return S2.S2Cell.FromLatLng({ lat: lat, lng: lng }, level).toHilbertQuadkey();
-};
-
-S2.stepKey = function (key, num) {
-  var Long = exports.dcodeIO && exports.dcodeIO.Long || require('long');
-  var parts = key.split('/');
-
-  var faceS = parts[0];
-  var posS = parts[1];
-  var level = parts[1].length;
-
-  var posL = Long.fromString(posS, true, 4);
-  // TODO handle wrapping (0 === pos + 1)
-  // (only on the 12 edges of the globe)
-  var otherL;
-  if (num > 0) {
-    otherL = posL.add(Math.abs(num));
-  }
-  else if (num < 0) {
-    otherL = posL.subtract(Math.abs(num));
-  }
-  var otherS = otherL.toString(4);
-
-  if ('0' === otherS) {
-    console.warning(new Error("face/position wrapping is not yet supported"));
-  }
-
-  while (otherS.length < level) {
-    otherS = '0' + otherS;
-  }
-
-  return faceS + '/' + otherS;
-};
-
-S2.S2Cell.prevKey = S2.prevKey = function (key) {
-  return S2.stepKey(key, -1);
-};
-
-S2.S2Cell.nextKey = S2.nextKey = function (key) {
-  return S2.stepKey(key, 1);
-};
-
-})('undefined' !== typeof module ? module.exports : window);
-/* eslint-enable */
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/test/accessors.test.js b/clouds/bigquery/libraries/javascript/test/accessors.test.js
new file mode 100644
index 000000000..c233f5ac3
--- /dev/null
+++ b/clouds/bigquery/libraries/javascript/test/accessors.test.js
@@ -0,0 +1,7 @@
+const accessorsLib = require('../build/accessors');
+
+test('accessors library defined', () => {
+    expect(accessorsLib.featureCollection).toBeDefined();
+    expect(accessorsLib.feature).toBeDefined();
+    expect(accessorsLib.envelope).toBeDefined();
+});
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/test/clustering.test.js b/clouds/bigquery/libraries/javascript/test/clustering.test.js
index 0441d1608..4a2166d60 100644
--- a/clouds/bigquery/libraries/javascript/test/clustering.test.js
+++ b/clouds/bigquery/libraries/javascript/test/clustering.test.js
@@ -1,8 +1,8 @@
-const lib = require('../build/index');
+const clusteringLib = require('../build/clustering');
 
 test('clustering library defined', () => {
-    expect(lib.clustering.featureCollection).toBeDefined();
-    expect(lib.clustering.feature).toBeDefined();
-    expect(lib.clustering.clustersKmeans).toBeDefined();
-    expect(lib.clustering.prioritizeDistinctSort).toBeDefined();
+    expect(clusteringLib.featureCollection).toBeDefined();
+    expect(clusteringLib.feature).toBeDefined();
+    expect(clusteringLib.clustersKmeans).toBeDefined();
+    expect(clusteringLib.prioritizeDistinctSort).toBeDefined();
 });
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/test/constructors.test.js b/clouds/bigquery/libraries/javascript/test/constructors.test.js
new file mode 100644
index 000000000..3efd5d989
--- /dev/null
+++ b/clouds/bigquery/libraries/javascript/test/constructors.test.js
@@ -0,0 +1,6 @@
+const constructorsLib = require('../build/constructors');
+
+test('constructors library defined', () => {
+    expect(constructorsLib.bezierSpline).toBeDefined();
+    expect(constructorsLib.ellipse).toBeDefined();
+});
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/test/h3.test.js b/clouds/bigquery/libraries/javascript/test/h3.test.js
new file mode 100644
index 000000000..80d1aedf3
--- /dev/null
+++ b/clouds/bigquery/libraries/javascript/test/h3.test.js
@@ -0,0 +1,17 @@
+const h3Lib = require('../build/h3');
+
+test('h3 library defined', () => {
+    expect(h3Lib.geoToH3).toBeDefined();
+    expect(h3Lib.compact).toBeDefined();
+    expect(h3Lib.h3Distance).toBeDefined();
+    expect(h3Lib.h3IsValid).toBeDefined();
+    expect(h3Lib.hexRing).toBeDefined();
+    expect(h3Lib.h3IsPentagon).toBeDefined();
+    expect(h3Lib.kRing).toBeDefined();
+    expect(h3Lib.kRingDistances).toBeDefined();
+    expect(h3Lib.polyfill).toBeDefined();
+    expect(h3Lib.h3ToGeoBoundary).toBeDefined();
+    expect(h3Lib.h3ToChildren).toBeDefined();
+    expect(h3Lib.h3ToParent).toBeDefined();
+    expect(h3Lib.uncompact).toBeDefined();
+});
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/test/index.test.js b/clouds/bigquery/libraries/javascript/test/index.test.js
deleted file mode 100644
index 23d66323c..000000000
--- a/clouds/bigquery/libraries/javascript/test/index.test.js
+++ /dev/null
@@ -1,81 +0,0 @@
-const lib = require('../build/index');
-
-test('accessors library defined', () => {
-    expect(lib.accessors.featureCollection).toBeDefined();
-    expect(lib.accessors.feature).toBeDefined();
-    expect(lib.accessors.envelope).toBeDefined();
-});
-
-test('constructors library defined', () => {
-    expect(lib.constructors.bezierSpline).toBeDefined();
-    expect(lib.constructors.ellipse).toBeDefined();
-});
-
-test('h3 library defined', () => {
-    expect(lib.h3.geoToH3).toBeDefined();
-    expect(lib.h3.compact).toBeDefined();
-    expect(lib.h3.h3Distance).toBeDefined();
-    expect(lib.h3.h3IsValid).toBeDefined();
-    expect(lib.h3.hexRing).toBeDefined();
-    expect(lib.h3.h3IsPentagon).toBeDefined();
-    expect(lib.h3.kRing).toBeDefined();
-    expect(lib.h3.kRingDistances).toBeDefined();
-    expect(lib.h3.polyfill).toBeDefined();
-    expect(lib.h3.h3ToGeoBoundary).toBeDefined();
-    expect(lib.h3.h3ToChildren).toBeDefined();
-    expect(lib.h3.h3ToParent).toBeDefined();
-    expect(lib.h3.uncompact).toBeDefined();
-});
-
-test('measurements library defined', () => {
-    expect(lib.measurements.angle).toBeDefined();
-    expect(lib.measurements.bearing).toBeDefined();
-    expect(lib.measurements.featureCollection).toBeDefined();
-    expect(lib.measurements.feature).toBeDefined();
-    expect(lib.measurements.distanceWeight).toBeDefined();
-});
-
-test('placekey library defined', () => {
-    expect(lib.placekey.placekeyIsValid).toBeDefined();
-    expect(lib.placekey.h3ToPlacekey).toBeDefined();
-    expect(lib.placekey.placekeyToH3).toBeDefined();
-});
-
-test('processing library defined', () => {
-    expect(lib.processing.featureCollection).toBeDefined();
-    expect(lib.processing.feature).toBeDefined();
-    expect(lib.processing.voronoi).toBeDefined();
-    expect(lib.processing.polygonToLine).toBeDefined();
-});
-
-test('quadkey library defined', () => {
-    expect(lib.quadkey.bbox).toBeDefined();
-    expect(lib.quadkey.toChildren).toBeDefined();
-    expect(lib.quadkey.quadkeyFromQuadint).toBeDefined();
-    expect(lib.quadkey.quadintFromQuadkey).toBeDefined();
-    expect(lib.quadkey.quadintFromLocation).toBeDefined();
-    expect(lib.quadkey.quadintToGeoJSON).toBeDefined();
-    expect(lib.quadkey.geojsonToQuadints).toBeDefined();
-    expect(lib.quadkey.ZXYFromQuadint).toBeDefined();
-});
-
-test('s2 library defined', () => {
-    expect(lib.s2.keyToId).toBeDefined();
-    expect(lib.s2.idToKey).toBeDefined();
-    expect(lib.s2.latLngToKey).toBeDefined();
-    expect(lib.s2.FromHilbertQuadKey).toBeDefined();
-    expect(lib.s2.idToLatLng).toBeDefined();
-});
-
-test('transformations library defined', () => {
-    expect(lib.transformations.featureCollection).toBeDefined();
-    expect(lib.transformations.feature).toBeDefined();
-    expect(lib.transformations.buffer).toBeDefined();
-    expect(lib.transformations.centerMean).toBeDefined();
-    expect(lib.transformations.centerMedian).toBeDefined();
-    expect(lib.transformations.centerOfMass).toBeDefined();
-    expect(lib.transformations.concave).toBeDefined();
-    expect(lib.transformations.destination).toBeDefined();
-    expect(lib.transformations.greatCircle).toBeDefined();
-    expect(lib.transformations.along).toBeDefined();
-});
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/test/measurements.test.js b/clouds/bigquery/libraries/javascript/test/measurements.test.js
new file mode 100644
index 000000000..e626382da
--- /dev/null
+++ b/clouds/bigquery/libraries/javascript/test/measurements.test.js
@@ -0,0 +1,9 @@
+const measurementsLib = require('../build/measurements');
+
+test('measurements library defined', () => {
+    expect(measurementsLib.angle).toBeDefined();
+    expect(measurementsLib.bearing).toBeDefined();
+    expect(measurementsLib.featureCollection).toBeDefined();
+    expect(measurementsLib.feature).toBeDefined();
+    expect(measurementsLib.distanceWeight).toBeDefined();
+});
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/test/placekey.test.js b/clouds/bigquery/libraries/javascript/test/placekey.test.js
new file mode 100644
index 000000000..11b71f57d
--- /dev/null
+++ b/clouds/bigquery/libraries/javascript/test/placekey.test.js
@@ -0,0 +1,7 @@
+const placekeyLib = require('../build/placekey');
+
+test('placekey library defined', () => {
+    expect(placekeyLib.placekeyIsValid).toBeDefined();
+    expect(placekeyLib.h3ToPlacekey).toBeDefined();
+    expect(placekeyLib.placekeyToH3).toBeDefined();
+});
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/test/processing.test.js b/clouds/bigquery/libraries/javascript/test/processing.test.js
new file mode 100644
index 000000000..07c5eb9ea
--- /dev/null
+++ b/clouds/bigquery/libraries/javascript/test/processing.test.js
@@ -0,0 +1,8 @@
+const processingLib = require('../build/processing');
+
+test('processing library defined', () => {
+    expect(processingLib.featureCollection).toBeDefined();
+    expect(processingLib.feature).toBeDefined();
+    expect(processingLib.voronoi).toBeDefined();
+    expect(processingLib.polygonToLine).toBeDefined();
+});
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/test/quadkey.test.js b/clouds/bigquery/libraries/javascript/test/quadkey.test.js
index 942731ce3..a97ab67df 100644
--- a/clouds/bigquery/libraries/javascript/test/quadkey.test.js
+++ b/clouds/bigquery/libraries/javascript/test/quadkey.test.js
@@ -1,12 +1,23 @@
-const lib = require('../build/index');
+const quadkeyLib = require('../build/quadkey');
 
 // TODO: refactor tests
 
+test('quadkey library defined', () => {
+    expect(quadkeyLib.bbox).toBeDefined();
+    expect(quadkeyLib.toChildren).toBeDefined();
+    expect(quadkeyLib.quadkeyFromQuadint).toBeDefined();
+    expect(quadkeyLib.quadintFromQuadkey).toBeDefined();
+    expect(quadkeyLib.quadintFromLocation).toBeDefined();
+    expect(quadkeyLib.quadintToGeoJSON).toBeDefined();
+    expect(quadkeyLib.geojsonToQuadints).toBeDefined();
+    expect(quadkeyLib.ZXYFromQuadint).toBeDefined();
+});
+
 test('bbox should work', () => {
-    expect(lib.quadkey.bbox(162)).toEqual([-90, 0, 0, 66.51326044311186]);
-    expect(lib.quadkey.bbox(12070922)).toEqual([-45, 44.840290651397986, -44.6484375, 45.08903556483103]);
-    expect(lib.quadkey.bbox(791040491538)).toEqual([-45, 44.99976701918129, -44.998626708984375, 45.00073807829068]);
-    expect(lib.quadkey.bbox(12960460429066265n)).toEqual([-45, 44.999994612636684, -44.99998927116394, 45.00000219906962]);
+    expect(quadkeyLib.bbox(162)).toEqual([-90, 0, 0, 66.51326044311186]);
+    expect(quadkeyLib.bbox(12070922)).toEqual([-45, 44.840290651397986, -44.6484375, 45.08903556483103]);
+    expect(quadkeyLib.bbox(791040491538)).toEqual([-45, 44.99976701918129, -44.998626708984375, 45.00073807829068]);
+    expect(quadkeyLib.bbox(12960460429066265n)).toEqual([-45, 44.999994612636684, -44.99998927116394, 45.00000219906962]);
 });
 
 test('toParent should work at any level of zoom', () => {
@@ -14,27 +25,27 @@ test('toParent should work at any level of zoom', () => {
     for (z = 1; z < 30; ++z) {
         for (lat = -90; lat <= 90; lat = lat + 15) {
             for (lng = -180; lng <= 180; lng = lng + 15) {
-                const quadint = lib.quadkey.quadintFromLocation(lng, lat, z);
-                const currentParent = lib.quadkey.quadintFromLocation(lng, lat, z - 1);
-                expect(currentParent).toEqual(lib.quadkey.toParent(quadint, z - 1));
+                const quadint = quadkeyLib.quadintFromLocation(lng, lat, z);
+                const currentParent = quadkeyLib.quadintFromLocation(lng, lat, z - 1);
+                expect(currentParent).toEqual(quadkeyLib.toParent(quadint, z - 1));
             }
         }
     }
     for (z = 5; z < 30; ++z) {
         for (lat = -90; lat <= 90; lat = lat + 15) {
             for (lng = -180; lng <= 180; lng = lng + 15) {
-                const quadint = lib.quadkey.quadintFromLocation(lng, lat, z);
-                const currentParent = lib.quadkey.quadintFromLocation(lng, lat, z - 5);
-                expect(currentParent).toEqual(lib.quadkey.toParent(quadint, z - 5));
+                const quadint = quadkeyLib.quadintFromLocation(lng, lat, z);
+                const currentParent = quadkeyLib.quadintFromLocation(lng, lat, z - 5);
+                expect(currentParent).toEqual(quadkeyLib.toParent(quadint, z - 5));
             }
         }
     }
     for (z = 10; z < 30; ++z) {
         for (lat = -90; lat <= 90; lat = lat + 15) {
             for (lng = -180; lng <= 180; lng = lng + 15) {
-                const quadint = lib.quadkey.quadintFromLocation(lng, lat, z);
-                const currentParent = lib.quadkey.quadintFromLocation(lng, lat, z - 10);
-                expect(currentParent).toEqual(lib.quadkey.toParent(quadint, z - 10));
+                const quadint = quadkeyLib.quadintFromLocation(lng, lat, z);
+                const currentParent = quadkeyLib.quadintFromLocation(lng, lat, z - 10);
+                expect(currentParent).toEqual(quadkeyLib.toParent(quadint, z - 10));
             }
         }
     }
@@ -45,10 +56,10 @@ test('toChildren should work at any level of zoom', () => {
     for (z = 0; z < 29; ++z) {
         for (lat = 90; lat <= 90; lat = lat + 15) {
             for (lng = -180; lng <= 180; lng = lng + 15) {
-                const quadint = lib.quadkey.quadintFromLocation(lng, lat, z);
-                const childs = lib.quadkey.toChildren(quadint, z + 1);
+                const quadint = quadkeyLib.quadintFromLocation(lng, lat, z);
+                const childs = quadkeyLib.toChildren(quadint, z + 1);
                 childs.forEach((element) => {
-                    expect(lib.quadkey.toParent(element, z)).toEqual(quadint);
+                    expect(quadkeyLib.toParent(element, z)).toEqual(quadint);
                 });
             }
         }
@@ -57,10 +68,10 @@ test('toChildren should work at any level of zoom', () => {
     for (z = 0; z < 25; ++z) {
         for (lat = 90; lat <= 90; lat = lat + 15) {
             for (lng = -180; lng <= 180; lng = lng + 15) {
-                const quadint = lib.quadkey.quadintFromLocation(lng, lat, z);
-                const childs = lib.quadkey.toChildren(quadint, z + 5);
+                const quadint = quadkeyLib.quadintFromLocation(lng, lat, z);
+                const childs = quadkeyLib.toChildren(quadint, z + 5);
                 childs.forEach((element) => {
-                    expect(lib.quadkey.toParent(element, z)).toEqual(quadint);
+                    expect(quadkeyLib.toParent(element, z)).toEqual(quadint);
                 });
             }
         }
@@ -78,7 +89,7 @@ test('Should be able to encode/decode between quadint and quadkey at any level o
 
         x = 0;
         y = 0;
-        let zxyDecoded = lib.quadkey.ZXYFromQuadint(lib.quadkey.quadintFromQuadkey(lib.quadkey.quadkeyFromQuadint(lib.quadkey.quadintFromZXY(z, x, y))));
+        let zxyDecoded = quadkeyLib.ZXYFromQuadint(quadkeyLib.quadintFromQuadkey(quadkeyLib.quadkeyFromQuadint(quadkeyLib.quadintFromZXY(z, x, y))));
         zDecoded = zxyDecoded.z;
         xDecoded = zxyDecoded.x;
         yDecoded = zxyDecoded.y;
@@ -87,7 +98,7 @@ test('Should be able to encode/decode between quadint and quadkey at any level o
         if (z > 0) {
             x = tilesPerLevel / 2;
             y = tilesPerLevel / 2;
-            zxyDecoded = lib.quadkey.ZXYFromQuadint(lib.quadkey.quadintFromQuadkey(lib.quadkey.quadkeyFromQuadint(lib.quadkey.quadintFromZXY(z, x, y))));
+            zxyDecoded = quadkeyLib.ZXYFromQuadint(quadkeyLib.quadintFromQuadkey(quadkeyLib.quadkeyFromQuadint(quadkeyLib.quadintFromZXY(z, x, y))));
             zDecoded = zxyDecoded.z;
             xDecoded = zxyDecoded.x;
             yDecoded = zxyDecoded.y;
@@ -95,7 +106,7 @@ test('Should be able to encode/decode between quadint and quadkey at any level o
 
             x = tilesPerLevel - 1;
             y = tilesPerLevel - 1;
-            zxyDecoded = lib.quadkey.ZXYFromQuadint(lib.quadkey.quadintFromQuadkey(lib.quadkey.quadkeyFromQuadint(lib.quadkey.quadintFromZXY(z, x, y))));
+            zxyDecoded = quadkeyLib.ZXYFromQuadint(quadkeyLib.quadintFromQuadkey(quadkeyLib.quadkeyFromQuadint(quadkeyLib.quadintFromZXY(z, x, y))));
             zDecoded = zxyDecoded.z;
             xDecoded = zxyDecoded.x;
             yDecoded = zxyDecoded.y;
@@ -115,7 +126,7 @@ test('Should be able to encode/decode tiles at any level of zoom', async () => {
 
         x = 0;
         y = 0;
-        let zxyDecoded = lib.quadkey.ZXYFromQuadint(lib.quadkey.quadintFromZXY(z, x, y));
+        let zxyDecoded = quadkeyLib.ZXYFromQuadint(quadkeyLib.quadintFromZXY(z, x, y));
         zDecoded = zxyDecoded.z;
         xDecoded = zxyDecoded.x;
         yDecoded = zxyDecoded.y;
@@ -124,7 +135,7 @@ test('Should be able to encode/decode tiles at any level of zoom', async () => {
         if (z > 0) {
             x = tilesPerLevel / 2;
             y = tilesPerLevel / 2;
-            zxyDecoded = lib.quadkey.ZXYFromQuadint(lib.quadkey.quadintFromZXY(z, x, y));
+            zxyDecoded = quadkeyLib.ZXYFromQuadint(quadkeyLib.quadintFromZXY(z, x, y));
             zDecoded = zxyDecoded.z;
             xDecoded = zxyDecoded.x;
             yDecoded = zxyDecoded.y;
@@ -132,7 +143,7 @@ test('Should be able to encode/decode tiles at any level of zoom', async () => {
 
             x = tilesPerLevel - 1;
             y = tilesPerLevel - 1;
-            zxyDecoded = lib.quadkey.ZXYFromQuadint(lib.quadkey.quadintFromZXY(z, x, y));
+            zxyDecoded = quadkeyLib.ZXYFromQuadint(quadkeyLib.quadintFromZXY(z, x, y));
             zDecoded = zxyDecoded.z;
             xDecoded = zxyDecoded.x;
             yDecoded = zxyDecoded.y;
diff --git a/clouds/bigquery/libraries/javascript/test/random.test.js b/clouds/bigquery/libraries/javascript/test/random.test.js
index f21b4dd35..4a02d3648 100644
--- a/clouds/bigquery/libraries/javascript/test/random.test.js
+++ b/clouds/bigquery/libraries/javascript/test/random.test.js
@@ -1,5 +1,5 @@
-const lib = require('../build/index');
+const randomLib = require('../build/random');
 
 test('random library defined', () => {
-    expect(lib.random.generateRandomPointsInPolygon).toBeDefined();
+    expect(randomLib.generateRandomPointsInPolygon).toBeDefined();
 });
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/test/s2.test.js b/clouds/bigquery/libraries/javascript/test/s2.test.js
new file mode 100644
index 000000000..e5381e717
--- /dev/null
+++ b/clouds/bigquery/libraries/javascript/test/s2.test.js
@@ -0,0 +1,9 @@
+const s2Lib = require('../build/s2');
+
+test('s2 library defined', () => {
+    expect(s2Lib.keyToId).toBeDefined();
+    expect(s2Lib.idToKey).toBeDefined();
+    expect(s2Lib.latLngToKey).toBeDefined();
+    expect(s2Lib.FromHilbertQuadKey).toBeDefined();
+    expect(s2Lib.idToLatLng).toBeDefined();
+});
\ No newline at end of file
diff --git a/clouds/bigquery/libraries/javascript/test/transformations.test.js b/clouds/bigquery/libraries/javascript/test/transformations.test.js
new file mode 100644
index 000000000..7a9ee511e
--- /dev/null
+++ b/clouds/bigquery/libraries/javascript/test/transformations.test.js
@@ -0,0 +1,14 @@
+const transformationsLib = require('../build/transformations');
+
+test('transformations library defined', () => {
+    expect(transformationsLib.featureCollection).toBeDefined();
+    expect(transformationsLib.feature).toBeDefined();
+    expect(transformationsLib.buffer).toBeDefined();
+    expect(transformationsLib.centerMean).toBeDefined();
+    expect(transformationsLib.centerMedian).toBeDefined();
+    expect(transformationsLib.centerOfMass).toBeDefined();
+    expect(transformationsLib.concave).toBeDefined();
+    expect(transformationsLib.destination).toBeDefined();
+    expect(transformationsLib.greatCircle).toBeDefined();
+    expect(transformationsLib.along).toBeDefined();
+});
\ No newline at end of file
diff --git a/clouds/bigquery/modules/Makefile b/clouds/bigquery/modules/Makefile
index 61892d7b8..877caaa64 100644
--- a/clouds/bigquery/modules/Makefile
+++ b/clouds/bigquery/modules/Makefile
@@ -28,7 +28,7 @@ endif
 BQ_MODULE_LABEL ?= spatial_extension_module:core
 MODULE_PERMISSIONS_BASH ?= set_module_permissions.sh
 
-REPLACEMENTS = "BQ_DATASET BQ_VERSION_FUNCTION BQ_PACKAGE_VERSION BQ_LIBRARY_BUCKET BQ_PROJECT BQ_REGION"
+REPLACEMENTS = "BQ_DATASET BQ_VERSION_FUNCTION BQ_PACKAGE_VERSION BQ_PROJECT BQ_REGION"
 
 include $(COMMON_DIR)/Makefile
 
@@ -57,7 +57,7 @@ build: $(NODE_MODULES_DEV)
 	REPLACEMENTS=$(REPLACEMENTS)" "$(REPLACEMENTS_EXTRA) \
 	$(COMMON_DIR)/build_modules.js $(MODULES_DIRS) \
 		--output=$(BUILD_DIR) --libs_build_dir=$(LIBS_BUILD_DIR) --diff="$(diff)" \
-		--modules=$(modules) --functions=$(functions) --production=$(production) --nodeps=$(nodeps) --dropfirst=$(dropfirst)
+		--modules=$(modules) --functions=$(functions) --production=$(production) --nodeps=$(nodeps) --dropfirst=$(dropfirst) --librarybucket=$(BQ_LIBRARY_BUCKET) --makelib=$(MAKE_LIB)
 
 deploy: check build
 	echo "Deploying modules..."
diff --git a/clouds/bigquery/modules/sql/accessors/ST_ENVELOPE.sql b/clouds/bigquery/modules/sql/accessors/ST_ENVELOPE.sql
index 21426549e..149d5009b 100644
--- a/clouds/bigquery/modules/sql/accessors/ST_ENVELOPE.sql
+++ b/clouds/bigquery/modules/sql/accessors/ST_ENVELOPE.sql
@@ -8,15 +8,15 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_ACCESSORS_BUCKET@@"]
 )
 AS """
     if (!geojson) {
         return null;
     }
 
-    const featuresCollection = lib.accessors.featureCollection(geojson.map(x => lib.accessors.feature(JSON.parse(x))));
-    const enveloped = lib.accessors.envelope(featuresCollection);
+    const featuresCollection = accessorsLib.featureCollection(geojson.map(x => accessorsLib.feature(JSON.parse(x))));
+    const enveloped = accessorsLib.envelope(featuresCollection);
     return JSON.stringify(enveloped.geometry);
 """;
 
diff --git a/clouds/bigquery/modules/sql/clustering/ST_CLUSTERKMEANS.sql b/clouds/bigquery/modules/sql/clustering/ST_CLUSTERKMEANS.sql
index 3a84db951..109b67a51 100644
--- a/clouds/bigquery/modules/sql/clustering/ST_CLUSTERKMEANS.sql
+++ b/clouds/bigquery/modules/sql/clustering/ST_CLUSTERKMEANS.sql
@@ -7,7 +7,7 @@ CREATE OR REPLACE FUNCTION `@@BQ_DATASET@@.__CLUSTERKMEANS`
 RETURNS ARRAY<STRUCT<cluster INT64, geom STRING>>
 DETERMINISTIC
 LANGUAGE js
-OPTIONS (library = ["@@BQ_LIBRARY_BUCKET@@"])
+OPTIONS (library = ["@@BQ_LIBRARY_CLUSTERING_BUCKET@@"])
 AS """
     if (!geojson) {
         return null;
@@ -19,8 +19,8 @@ AS """
         options.numberOfClusters = parseInt(Math.sqrt(geojson.length/2))
     }
     options.mutate = true;
-    const featuresCollection = lib.clustering.featureCollection(lib.clustering.prioritizeDistinctSort(geojson).map(x => lib.clustering.feature(JSON.parse(x))));
-    lib.clustering.clustersKmeans(featuresCollection, options);
+    const featuresCollection = clusteringLib.featureCollection(clusteringLib.prioritizeDistinctSort(geojson).map(x => clusteringLib.feature(JSON.parse(x))));
+    clusteringLib.clustersKmeans(featuresCollection, options);
     const cluster = [];
     featuresCollection.features.forEach(function(item, index, array) {
         cluster.push({cluster: item.properties.cluster, geom: JSON.stringify(item.geometry)});
diff --git a/clouds/bigquery/modules/sql/constructors/ST_BEZIERSPLINE.sql b/clouds/bigquery/modules/sql/constructors/ST_BEZIERSPLINE.sql
index 91898fcce..f50e2ee45 100644
--- a/clouds/bigquery/modules/sql/constructors/ST_BEZIERSPLINE.sql
+++ b/clouds/bigquery/modules/sql/constructors/ST_BEZIERSPLINE.sql
@@ -8,7 +8,7 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_CONSTRUCTORS_BUCKET@@"]
 )
 AS """
     if (!geojson) {
@@ -21,7 +21,7 @@ AS """
     if (sharpness != null) {
         options.sharpness = Number(sharpness);
     }
-    const curved = lib.constructors.bezierSpline(JSON.parse(geojson), options);
+    const curved = constructorsLib.bezierSpline(JSON.parse(geojson), options);
     return JSON.stringify(curved.geometry);
 """;
 
diff --git a/clouds/bigquery/modules/sql/constructors/ST_MAKEELLIPSE.sql b/clouds/bigquery/modules/sql/constructors/ST_MAKEELLIPSE.sql
index b72e5d255..62bab28c6 100644
--- a/clouds/bigquery/modules/sql/constructors/ST_MAKEELLIPSE.sql
+++ b/clouds/bigquery/modules/sql/constructors/ST_MAKEELLIPSE.sql
@@ -15,7 +15,7 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_CONSTRUCTORS_BUCKET@@"]
 )
 AS """
     if (!geojson || xSemiAxis == null || ySemiAxis == null) {
@@ -31,7 +31,7 @@ AS """
     if (steps != null) {
         options.steps = Number(steps);
     }
-    const ellipse = lib.constructors.ellipse(JSON.parse(geojson), Number(xSemiAxis), Number(ySemiAxis), options);
+    const ellipse = constructorsLib.ellipse(JSON.parse(geojson), Number(xSemiAxis), Number(ySemiAxis), options);
     return JSON.stringify(ellipse.geometry);
 """;
 
diff --git a/clouds/bigquery/modules/sql/h3/H3_BOUNDARY.sql b/clouds/bigquery/modules/sql/h3/H3_BOUNDARY.sql
index 81d6c7841..7de740d5b 100644
--- a/clouds/bigquery/modules/sql/h3/H3_BOUNDARY.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_BOUNDARY.sql
@@ -8,18 +8,18 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     if (!index) {
         return null;
     }
 
-    if (!lib.h3.h3IsValid(index)) {
+    if (!h3Lib.h3IsValid(index)) {
         return null;
     }
 
-    const coords = lib.h3.h3ToGeoBoundary(index, true);
+    const coords = h3Lib.h3ToGeoBoundary(index, true);
     let output = `POLYGON((`;
     for (let i = 0; i < coords.length - 1; i++) {
         output += coords[i][0] + ` ` + coords[i][1] + `,`;
diff --git a/clouds/bigquery/modules/sql/h3/H3_CENTER.sql b/clouds/bigquery/modules/sql/h3/H3_CENTER.sql
index 7c6ec43c3..072de1509 100644
--- a/clouds/bigquery/modules/sql/h3/H3_CENTER.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_CENTER.sql
@@ -8,18 +8,18 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     if (!index) {
         return null;
     }
 
-    if (!lib.h3.h3IsValid(index)) {
+    if (!h3Lib.h3IsValid(index)) {
         return null;
     }
 
-    const center = lib.h3.h3ToGeo(index);
+    const center = h3Lib.h3ToGeo(index);
     return `POINT(`+center[1] + ` ` + center[0] + `)`;
 """;
 
diff --git a/clouds/bigquery/modules/sql/h3/H3_COMPACT.sql b/clouds/bigquery/modules/sql/h3/H3_COMPACT.sql
index f7e51887c..77c3755e3 100644
--- a/clouds/bigquery/modules/sql/h3/H3_COMPACT.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_COMPACT.sql
@@ -8,11 +8,11 @@ RETURNS ARRAY<STRING>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     if (h3Array === null) {
         return null;
     }
-    return lib.h3.compact(h3Array);
+    return h3Lib.compact(h3Array);
 """;
diff --git a/clouds/bigquery/modules/sql/h3/H3_DISTANCE.sql b/clouds/bigquery/modules/sql/h3/H3_DISTANCE.sql
index c775a3648..635d237b9 100644
--- a/clouds/bigquery/modules/sql/h3/H3_DISTANCE.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_DISTANCE.sql
@@ -8,13 +8,13 @@ RETURNS INT64
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     if (!index1 || !index2) {
         return null;
     }
-    let dist = lib.h3.h3Distance(index1, index2);
+    let dist = h3Lib.h3Distance(index1, index2);
     if (dist < 0) {
         dist = null;
     }
diff --git a/clouds/bigquery/modules/sql/h3/H3_FROMLONGLAT.sql b/clouds/bigquery/modules/sql/h3/H3_FROMLONGLAT.sql
index 66e1007fa..0e3ae58b9 100644
--- a/clouds/bigquery/modules/sql/h3/H3_FROMLONGLAT.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_FROMLONGLAT.sql
@@ -8,11 +8,11 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     if (longitude === null || latitude === null || resolution === null) {
         return null;
     }
-    return lib.h3.geoToH3(Number(latitude), Number(longitude), Number(resolution));
+    return h3Lib.geoToH3(Number(latitude), Number(longitude), Number(resolution));
 """;
diff --git a/clouds/bigquery/modules/sql/h3/H3_HEXRING.sql b/clouds/bigquery/modules/sql/h3/H3_HEXRING.sql
index fd3d0ea7b..85bf8a951 100644
--- a/clouds/bigquery/modules/sql/h3/H3_HEXRING.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_HEXRING.sql
@@ -8,14 +8,14 @@ RETURNS ARRAY<STRING>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
-    if (!lib.h3.h3IsValid(origin)) {
+    if (!h3Lib.h3IsValid(origin)) {
         throw new Error('Invalid input origin')
     }
     if (size == null || size < 0) {
         throw new Error('Invalid input size')
     }
-    return lib.h3.hexRing(origin, parseInt(size));
+    return h3Lib.hexRing(origin, parseInt(size));
 """;
diff --git a/clouds/bigquery/modules/sql/h3/H3_ISPENTAGON.sql b/clouds/bigquery/modules/sql/h3/H3_ISPENTAGON.sql
index 87a506799..e3eb67c82 100644
--- a/clouds/bigquery/modules/sql/h3/H3_ISPENTAGON.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_ISPENTAGON.sql
@@ -8,11 +8,11 @@ RETURNS BOOLEAN
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     if (!index) {
         return false;
     }   
-    return lib.h3.h3IsPentagon(index);
+    return h3Lib.h3IsPentagon(index);
 """;
diff --git a/clouds/bigquery/modules/sql/h3/H3_ISVALID.sql b/clouds/bigquery/modules/sql/h3/H3_ISVALID.sql
index f7a5feecf..596ccc659 100644
--- a/clouds/bigquery/modules/sql/h3/H3_ISVALID.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_ISVALID.sql
@@ -8,11 +8,11 @@ RETURNS BOOLEAN
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     if (!index) {
         return false;
     }
-    return lib.h3.h3IsValid(index);
+    return h3Lib.h3IsValid(index);
 """;
diff --git a/clouds/bigquery/modules/sql/h3/H3_KRING.sql b/clouds/bigquery/modules/sql/h3/H3_KRING.sql
index cc759286e..edf8a2f22 100644
--- a/clouds/bigquery/modules/sql/h3/H3_KRING.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_KRING.sql
@@ -8,14 +8,14 @@ RETURNS ARRAY<STRING>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
-    if (!lib.h3.h3IsValid(origin)) {
+    if (!h3Lib.h3IsValid(origin)) {
         throw new Error('Invalid input origin')
     }
     if (size == null || size < 0) {
         throw new Error('Invalid input size')
     }
-    return lib.h3.kRing(origin, parseInt(size));
+    return h3Lib.kRing(origin, parseInt(size));
 """;
diff --git a/clouds/bigquery/modules/sql/h3/H3_KRING_DISTANCES.sql b/clouds/bigquery/modules/sql/h3/H3_KRING_DISTANCES.sql
index 3ba661433..60c03a6ae 100644
--- a/clouds/bigquery/modules/sql/h3/H3_KRING_DISTANCES.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_KRING_DISTANCES.sql
@@ -8,16 +8,16 @@ RETURNS ARRAY<STRUCT<index STRING, distance INT64>>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
-    if (!lib.h3.h3IsValid(origin)) {
+    if (!h3Lib.h3IsValid(origin)) {
         throw new Error('Invalid input origin')
     }
     if (size == null || size < 0) {
         throw new Error('Invalid input size')
     }
-    const kringDistances = lib.h3.kRingDistances(origin, size);
+    const kringDistances = h3Lib.kRingDistances(origin, size);
     const output = [];
     for (let distance = 0; distance <= size; distance++) {
         const indexes = kringDistances[distance];
diff --git a/clouds/bigquery/modules/sql/h3/H3_POLYFILL.sql b/clouds/bigquery/modules/sql/h3/H3_POLYFILL.sql
index 581d39bc7..c37b2e6ee 100644
--- a/clouds/bigquery/modules/sql/h3/H3_POLYFILL.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_POLYFILL.sql
@@ -8,7 +8,7 @@ RETURNS ARRAY<STRING>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     const resolution = Number(_resolution)
@@ -21,28 +21,28 @@ AS """
         case 'GeometryCollection':
             featureGeometry.geometries.forEach(function (geom) {
                 if (geom.type === 'MultiPolygon') {
-                    var clippedGeometryA = lib.h3.bboxClip(geom, bboxA).geometry
+                    var clippedGeometryA = h3Lib.bboxClip(geom, bboxA).geometry
                     polygonCoordinatesA = polygonCoordinatesA.concat(clippedGeometryA.coordinates)
-                    var clippedGeometryB = lib.h3.bboxClip(geom, bboxB).geometry
+                    var clippedGeometryB = h3Lib.bboxClip(geom, bboxB).geometry
                     polygonCoordinatesB = polygonCoordinatesB.concat(clippedGeometryB.coordinates)
                 } else if (geom.type === 'Polygon') {
-                    var clippedGeometryA = lib.h3.bboxClip(geom, bboxA).geometry
+                    var clippedGeometryA = h3Lib.bboxClip(geom, bboxA).geometry
                     polygonCoordinatesA = polygonCoordinatesA.concat([clippedGeometryA.coordinates])
-                    var clippedGeometryB = lib.h3.bboxClip(geom, bboxB).geometry
+                    var clippedGeometryB = h3Lib.bboxClip(geom, bboxB).geometry
                     polygonCoordinatesB = polygonCoordinatesB.concat([clippedGeometryB.coordinates])
                 }
             })
         break
         case 'MultiPolygon':
-            var clippedGeometryA = lib.h3.bboxClip(featureGeometry, bboxA).geometry
+            var clippedGeometryA = h3Lib.bboxClip(featureGeometry, bboxA).geometry
             polygonCoordinatesA = clippedGeometryA.coordinates
-            var clippedGeometryB = lib.h3.bboxClip(featureGeometry, bboxB).geometry
+            var clippedGeometryB = h3Lib.bboxClip(featureGeometry, bboxB).geometry
             polygonCoordinatesB = clippedGeometryB.coordinates
         break
         case 'Polygon':
-            var clippedGeometryA = lib.h3.bboxClip(featureGeometry, bboxA).geometry
+            var clippedGeometryA = h3Lib.bboxClip(featureGeometry, bboxA).geometry
             polygonCoordinatesA = [clippedGeometryA.coordinates]
-            var clippedGeometryB = lib.h3.bboxClip(featureGeometry, bboxB).geometry
+            var clippedGeometryB = h3Lib.bboxClip(featureGeometry, bboxB).geometry
             polygonCoordinatesB = [clippedGeometryB.coordinates]
         break
         default:
@@ -54,11 +54,11 @@ AS """
     }
 
     let hexesA = polygonCoordinatesA.reduce(
-        (acc, coordinates) => acc.concat(lib.h3.polyfill(coordinates, resolution, true)),
+        (acc, coordinates) => acc.concat(h3Lib.polyfill(coordinates, resolution, true)),
         []
     ).filter(h => h != null)
     let hexesB = polygonCoordinatesB.reduce(
-        (acc, coordinates) => acc.concat(lib.h3.polyfill(coordinates, resolution, true)),
+        (acc, coordinates) => acc.concat(h3Lib.polyfill(coordinates, resolution, true)),
         []
     ).filter(h => h != null)
     hexes = [...hexesA, ...hexesB]
diff --git a/clouds/bigquery/modules/sql/h3/H3_RESOLUTION.sql b/clouds/bigquery/modules/sql/h3/H3_RESOLUTION.sql
index d296809c2..dd5ee3e8b 100644
--- a/clouds/bigquery/modules/sql/h3/H3_RESOLUTION.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_RESOLUTION.sql
@@ -8,16 +8,16 @@ RETURNS INT64
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     if (!index) {
         return null;
     }
 
-    if (!lib.h3.h3IsValid(index)) {
+    if (!h3Lib.h3IsValid(index)) {
         return null;
     }
 
-    return lib.h3.h3GetResolution(index);
+    return h3Lib.h3GetResolution(index);
 """;
diff --git a/clouds/bigquery/modules/sql/h3/H3_TOCHILDREN.sql b/clouds/bigquery/modules/sql/h3/H3_TOCHILDREN.sql
index 7ad375f91..ecc9a0956 100644
--- a/clouds/bigquery/modules/sql/h3/H3_TOCHILDREN.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_TOCHILDREN.sql
@@ -8,14 +8,14 @@ RETURNS ARRAY<STRING>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     if (!index) {
         return null;
     }
-    if (!lib.h3.h3IsValid(index)) {
+    if (!h3Lib.h3IsValid(index)) {
         return null;
     }
-    return lib.h3.h3ToChildren(index, Number(resolution));
+    return h3Lib.h3ToChildren(index, Number(resolution));
 """;
diff --git a/clouds/bigquery/modules/sql/h3/H3_TOPARENT.sql b/clouds/bigquery/modules/sql/h3/H3_TOPARENT.sql
index f1a0252c3..2ecb09379 100644
--- a/clouds/bigquery/modules/sql/h3/H3_TOPARENT.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_TOPARENT.sql
@@ -8,14 +8,14 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     if (!index) {
         return null;
     }
-    if (!lib.h3.h3IsValid(index)) {
+    if (!h3Lib.h3IsValid(index)) {
         return null;
     }
-    return lib.h3.h3ToParent(index, Number(resolution));
+    return h3Lib.h3ToParent(index, Number(resolution));
 """;
diff --git a/clouds/bigquery/modules/sql/h3/H3_UNCOMPACT.sql b/clouds/bigquery/modules/sql/h3/H3_UNCOMPACT.sql
index 6ef711b99..e560dd468 100644
--- a/clouds/bigquery/modules/sql/h3/H3_UNCOMPACT.sql
+++ b/clouds/bigquery/modules/sql/h3/H3_UNCOMPACT.sql
@@ -8,11 +8,11 @@ RETURNS ARRAY<STRING>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_H3_BUCKET@@"]
 )
 AS """
     if (h3Array === null || resolution === null || resolution < 0 || resolution > 15) {
         return null;
     }
-    return lib.h3.uncompact(h3Array, Number(resolution));
+    return h3Lib.uncompact(h3Array, Number(resolution));
 """;
diff --git a/clouds/bigquery/modules/sql/measurements/ST_ANGLE.sql b/clouds/bigquery/modules/sql/measurements/ST_ANGLE.sql
index 97fd17e8a..e4059b1a0 100644
--- a/clouds/bigquery/modules/sql/measurements/ST_ANGLE.sql
+++ b/clouds/bigquery/modules/sql/measurements/ST_ANGLE.sql
@@ -8,7 +8,7 @@ RETURNS FLOAT64
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_MEASUREMENTS_BUCKET@@"]
 )
 AS """
     if (!geojsonStart || !geojsonMid || !geojsonEnd) {
@@ -18,7 +18,7 @@ AS """
     if(mercator != null) {
         options.mercator = mercator;
     }
-    return lib.measurements.angle(JSON.parse(geojsonStart), JSON.parse(geojsonMid), JSON.parse(geojsonEnd), options);
+    return measurementsLib.angle(JSON.parse(geojsonStart), JSON.parse(geojsonMid), JSON.parse(geojsonEnd), options);
 """;
 
 CREATE OR REPLACE FUNCTION `@@BQ_DATASET@@.ST_ANGLE`
diff --git a/clouds/bigquery/modules/sql/measurements/ST_MINKOWSKIDISTANCE.sql b/clouds/bigquery/modules/sql/measurements/ST_MINKOWSKIDISTANCE.sql
index 5a0c7d826..2e448e14e 100644
--- a/clouds/bigquery/modules/sql/measurements/ST_MINKOWSKIDISTANCE.sql
+++ b/clouds/bigquery/modules/sql/measurements/ST_MINKOWSKIDISTANCE.sql
@@ -8,7 +8,7 @@ RETURNS ARRAY<STRING>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_MEASUREMENTS_BUCKET@@"]
 )
 AS """
     if (!geojson) {
@@ -18,8 +18,8 @@ AS """
     if(p != null) {
         options.p = Number(p);
     }
-    const features = lib.measurements.featureCollection(geojson.map(x => lib.measurements.feature(JSON.parse(x))));
-    const distance = lib.measurements.distanceWeight(features, options);
+    const features = measurementsLib.featureCollection(geojson.map(x => measurementsLib.feature(JSON.parse(x))));
+    const distance = measurementsLib.distanceWeight(features, options);
     return distance;
 """;
 
diff --git a/clouds/bigquery/modules/sql/placekey/PLACEKEY_FROMH3.sql b/clouds/bigquery/modules/sql/placekey/PLACEKEY_FROMH3.sql
index ea1857052..913af2afb 100644
--- a/clouds/bigquery/modules/sql/placekey/PLACEKEY_FROMH3.sql
+++ b/clouds/bigquery/modules/sql/placekey/PLACEKEY_FROMH3.sql
@@ -9,10 +9,10 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_PLACEKEY_BUCKET@@"]
 )
 AS """
-    return lib.placekey.h3ToPlacekey(h3Index);
+    return placekeyLib.h3ToPlacekey(h3Index);
 """;
 
 CREATE OR REPLACE FUNCTION `@@BQ_DATASET@@.PLACEKEY_FROMH3`
diff --git a/clouds/bigquery/modules/sql/placekey/PLACEKEY_ISVALID.sql b/clouds/bigquery/modules/sql/placekey/PLACEKEY_ISVALID.sql
index d6f59e7ae..4af181cf3 100644
--- a/clouds/bigquery/modules/sql/placekey/PLACEKEY_ISVALID.sql
+++ b/clouds/bigquery/modules/sql/placekey/PLACEKEY_ISVALID.sql
@@ -8,8 +8,8 @@ RETURNS BOOLEAN
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_PLACEKEY_BUCKET@@"]
 )
 AS """
-    return lib.placekey.placekeyIsValid(placekey);
+    return placekeyLib.placekeyIsValid(placekey);
 """;
diff --git a/clouds/bigquery/modules/sql/placekey/PLACEKEY_TOH3.sql b/clouds/bigquery/modules/sql/placekey/PLACEKEY_TOH3.sql
index de43d1d69..b333246ad 100644
--- a/clouds/bigquery/modules/sql/placekey/PLACEKEY_TOH3.sql
+++ b/clouds/bigquery/modules/sql/placekey/PLACEKEY_TOH3.sql
@@ -8,11 +8,11 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_PLACEKEY_BUCKET@@"]
 )
 AS """
-    if (!lib.placekey.placekeyIsValid(placekey))  {
+    if (!placekeyLib.placekeyIsValid(placekey))  {
         return null;
     }
-    return lib.placekey.placekeyToH3(placekey);
+    return placekeyLib.placekeyToH3(placekey);
 """;
diff --git a/clouds/bigquery/modules/sql/processing/__VORONOIHELPER.sql b/clouds/bigquery/modules/sql/processing/__VORONOIHELPER.sql
index 92c4dc72f..9d672f1ba 100644
--- a/clouds/bigquery/modules/sql/processing/__VORONOIHELPER.sql
+++ b/clouds/bigquery/modules/sql/processing/__VORONOIHELPER.sql
@@ -8,7 +8,7 @@ RETURNS ARRAY<STRING>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_PROCESSING_BUCKET@@"]
 )
 AS """
     if (!geojson) {
@@ -26,8 +26,8 @@ AS """
         options.bbox = bbox;
     }
 
-    const featuresCollection = lib.processing.featureCollection(geojson.map(x => lib.processing.feature(JSON.parse(x))));
-    const voronoiPolygons = lib.processing.voronoi(featuresCollection, options);
+    const featuresCollection = processingLib.featureCollection(geojson.map(x => processingLib.feature(JSON.parse(x))));
+    const voronoiPolygons = processingLib.voronoi(featuresCollection, options);
 
     const returnArray = [];
 
@@ -39,7 +39,7 @@ AS """
 
     if (typeOfVoronoi === 'lines') {
         voronoiPolygons.features.forEach( function(item) {
-            let lineFeature = lib.processing.polygonToLine(item.geometry);
+            let lineFeature = processingLib.polygonToLine(item.geometry);
             returnArray.push(JSON.stringify(lineFeature.geometry));
         });
     }
diff --git a/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMLONGLAT.sql b/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMLONGLAT.sql
index 4974b734c..8a880405b 100644
--- a/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMLONGLAT.sql
+++ b/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMLONGLAT.sql
@@ -8,11 +8,11 @@ RETURNS INT64
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_QUADKEY_BUCKET@@"]
 )
 AS """
     if (longitude == null || latitude == null || resolution == null) {
         throw new Error('NULL argument passed to UDF');
     }
-    return lib.quadkey.quadintFromLocation(Number(longitude), Number(latitude), Number(resolution)).toString();
+    return quadkeyLib.quadintFromLocation(Number(longitude), Number(latitude), Number(resolution)).toString();
 """;
diff --git a/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMLONGLAT_ZOOMRANGE.sql b/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMLONGLAT_ZOOMRANGE.sql
index aec3b762d..f47398494 100644
--- a/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMLONGLAT_ZOOMRANGE.sql
+++ b/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMLONGLAT_ZOOMRANGE.sql
@@ -15,7 +15,7 @@ RETURNS ARRAY<STRUCT<id INT64, z INT64, x INT64, y INT64>>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_QUADKEY_BUCKET@@"]
 )
 AS """
     if (longitude === undefined || longitude === null || latitude === undefined || latitude === null) {
@@ -28,8 +28,8 @@ AS """
 
     const qintIdx = [];
     for (let i = zoomMin; i <= zoomMax; i += zoomStep) {
-        const key = lib.quadkey.quadintFromLocation(longitude, latitude, i + intResolution);
-        const zxy = lib.quadkey.ZXYFromQuadint(key);
+        const key = quadkeyLib.quadintFromLocation(longitude, latitude, i + intResolution);
+        const zxy = quadkeyLib.ZXYFromQuadint(key);
         qintIdx.push({ id : key.toString(), z : i, x : zxy.x  >>> intResolution, y : zxy.y  >>> intResolution});
     }
     return qintIdx;
diff --git a/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMQUADKEY.sql b/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMQUADKEY.sql
index 0b1d1d113..092889d5d 100644
--- a/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMQUADKEY.sql
+++ b/clouds/bigquery/modules/sql/quadkey/QUADINT_FROMQUADKEY.sql
@@ -8,8 +8,8 @@ RETURNS INT64
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_QUADKEY_BUCKET@@"]
 )
 AS """
-    return lib.quadkey.quadintFromQuadkey(quadkey).toString();
+    return quadkeyLib.quadintFromQuadkey(quadkey).toString();
 """;
diff --git a/clouds/bigquery/modules/sql/quadkey/QUADINT_POLYFILL.sql b/clouds/bigquery/modules/sql/quadkey/QUADINT_POLYFILL.sql
index 2c91f7f95..aeee59ea6 100644
--- a/clouds/bigquery/modules/sql/quadkey/QUADINT_POLYFILL.sql
+++ b/clouds/bigquery/modules/sql/quadkey/QUADINT_POLYFILL.sql
@@ -8,7 +8,7 @@ RETURNS ARRAY<INT64>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_QUADKEY_BUCKET@@"]
 )
 AS """
     if (!geojson || resolution == null) {
@@ -18,13 +18,13 @@ AS """
     let quadints = [];
     if (pol.type == 'GeometryCollection') {
         pol.geometries.forEach(function (geom) {
-            quadints = quadints.concat(lib.quadkey.geojsonToQuadints(geom, {min_zoom: Number(resolution), max_zoom: Number(resolution)}));
+            quadints = quadints.concat(quadkeyLib.geojsonToQuadints(geom, {min_zoom: Number(resolution), max_zoom: Number(resolution)}));
         });
         quadints = Array.from(new Set(quadints));
     }
     else
     {
-        quadints = lib.quadkey.geojsonToQuadints(pol, {min_zoom: Number(resolution), max_zoom: Number(resolution)});
+        quadints = quadkeyLib.geojsonToQuadints(pol, {min_zoom: Number(resolution), max_zoom: Number(resolution)});
     }
     return quadints.map(String);
 """;
diff --git a/clouds/bigquery/modules/sql/quadkey/QUADINT_TOCHILDREN.sql b/clouds/bigquery/modules/sql/quadkey/QUADINT_TOCHILDREN.sql
index c710f92f8..45f1459b6 100644
--- a/clouds/bigquery/modules/sql/quadkey/QUADINT_TOCHILDREN.sql
+++ b/clouds/bigquery/modules/sql/quadkey/QUADINT_TOCHILDREN.sql
@@ -8,12 +8,12 @@ RETURNS ARRAY<INT64>
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_QUADKEY_BUCKET@@"]
 )
 AS """
     if (quadint == null || resolution == null) {
         throw new Error('NULL argument passed to UDF');
     }
-    const quadints = lib.quadkey.toChildren(quadint, Number(resolution));
+    const quadints = quadkeyLib.toChildren(quadint, Number(resolution));
     return quadints.map(String);
 """;
diff --git a/clouds/bigquery/modules/sql/quadkey/QUADINT_TOQUADKEY.sql b/clouds/bigquery/modules/sql/quadkey/QUADINT_TOQUADKEY.sql
index ff8e599d1..df2589eb3 100644
--- a/clouds/bigquery/modules/sql/quadkey/QUADINT_TOQUADKEY.sql
+++ b/clouds/bigquery/modules/sql/quadkey/QUADINT_TOQUADKEY.sql
@@ -8,11 +8,11 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_QUADKEY_BUCKET@@"]
 )
 AS """
     if (quadint == null) {
         throw new Error('NULL argument passed to UDF');
     }
-    return lib.quadkey.quadkeyFromQuadint(quadint);
+    return quadkeyLib.quadkeyFromQuadint(quadint);
 """;
diff --git a/clouds/bigquery/modules/sql/random/ST_GENERATEPOINTS.sql b/clouds/bigquery/modules/sql/random/ST_GENERATEPOINTS.sql
index e63946a21..adff4a6de 100644
--- a/clouds/bigquery/modules/sql/random/ST_GENERATEPOINTS.sql
+++ b/clouds/bigquery/modules/sql/random/ST_GENERATEPOINTS.sql
@@ -7,9 +7,9 @@ CREATE OR REPLACE FUNCTION `@@BQ_DATASET@@.__ST_GENERATEPOINTS`
 RETURNS ARRAY<STRING>
 DETERMINISTIC
 LANGUAGE js
-OPTIONS (library = ["@@BQ_LIBRARY_BUCKET@@"])
+OPTIONS (library = ["@@BQ_LIBRARY_RANDOM_BUCKET@@"])
 AS """
-    return lib.random.generateRandomPointsInPolygon(JSON.parse(geojson), npoints);
+    return randomLib.generateRandomPointsInPolygon(JSON.parse(geojson), npoints);
 """;
 
 CREATE OR REPLACE FUNCTION `@@BQ_DATASET@@.ST_GENERATEPOINTS`
diff --git a/clouds/bigquery/modules/sql/s2/S2_BOUNDARY.sql b/clouds/bigquery/modules/sql/s2/S2_BOUNDARY.sql
index 2c362fc7d..4b6d0b65e 100644
--- a/clouds/bigquery/modules/sql/s2/S2_BOUNDARY.sql
+++ b/clouds/bigquery/modules/sql/s2/S2_BOUNDARY.sql
@@ -8,14 +8,14 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_S2_BUCKET@@"]
 )
 AS """
     if (id == null) {
         throw new Error('NULL argument passed to UDF');
     }
     
-    const cornerLongLat = lib.s2.FromHilbertQuadKey(lib.s2.idToKey(id)).getCornerLatLngs();
+    const cornerLongLat = s2Lib.FromHilbertQuadKey(s2Lib.idToKey(id)).getCornerLatLngs();
 
     const wkt = `POLYGON((` +
         cornerLongLat[0]['lng'] + ` ` + cornerLongLat[0]['lat'] + `, ` +
diff --git a/clouds/bigquery/modules/sql/s2/S2_CENTER.sql b/clouds/bigquery/modules/sql/s2/S2_CENTER.sql
index 74faab5eb..779be266d 100644
--- a/clouds/bigquery/modules/sql/s2/S2_CENTER.sql
+++ b/clouds/bigquery/modules/sql/s2/S2_CENTER.sql
@@ -6,12 +6,12 @@ CREATE OR REPLACE FUNCTION `@@BQ_DATASET@@.__S2_CENTER`(id INT64)
 RETURNS STRUCT<lng FLOAT64, lat FLOAT64>
 DETERMINISTIC
 LANGUAGE js
-OPTIONS (library = ["@@BQ_LIBRARY_BUCKET@@"])
+OPTIONS (library = ["@@BQ_LIBRARY_S2_BUCKET@@"])
 AS """
     if (id == null) {
         throw new Error('NULL argument passed to UDF');
     }
-    return lib.s2.idToLatLng(String(id));
+    return s2Lib.idToLatLng(String(id));
 """;
 
 CREATE OR REPLACE FUNCTION `@@BQ_DATASET@@.S2_CENTER`
diff --git a/clouds/bigquery/modules/sql/s2/S2_FROMHILBERTQUADKEY.sql b/clouds/bigquery/modules/sql/s2/S2_FROMHILBERTQUADKEY.sql
index 1ed1734ef..073f621f9 100644
--- a/clouds/bigquery/modules/sql/s2/S2_FROMHILBERTQUADKEY.sql
+++ b/clouds/bigquery/modules/sql/s2/S2_FROMHILBERTQUADKEY.sql
@@ -8,11 +8,11 @@ RETURNS INT64
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_S2_BUCKET@@"]
 )
 AS """
     if (!quadkey) {
         throw new Error('NULL argument passed to UDF');
     }
-    return lib.s2.keyToId(quadkey);
+    return s2Lib.keyToId(quadkey);
 """;
diff --git a/clouds/bigquery/modules/sql/s2/S2_FROMLONGLAT.sql b/clouds/bigquery/modules/sql/s2/S2_FROMLONGLAT.sql
index ca9f9ef4f..b1d8108d2 100644
--- a/clouds/bigquery/modules/sql/s2/S2_FROMLONGLAT.sql
+++ b/clouds/bigquery/modules/sql/s2/S2_FROMLONGLAT.sql
@@ -8,12 +8,12 @@ RETURNS INT64
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_S2_BUCKET@@"]
 )
 AS """
     if (latitude == null || longitude == null || resolution == null) {
         throw new Error('NULL argument passed to UDF');
     }
-    const key = lib.s2.latLngToKey(Number(latitude), Number(longitude), Number(resolution));
-    return lib.s2.keyToId(key);
+    const key = s2Lib.latLngToKey(Number(latitude), Number(longitude), Number(resolution));
+    return s2Lib.keyToId(key);
 """;
diff --git a/clouds/bigquery/modules/sql/s2/S2_TOHILBERTQUADKEY.sql b/clouds/bigquery/modules/sql/s2/S2_TOHILBERTQUADKEY.sql
index a9b7824c4..cb60f3776 100644
--- a/clouds/bigquery/modules/sql/s2/S2_TOHILBERTQUADKEY.sql
+++ b/clouds/bigquery/modules/sql/s2/S2_TOHILBERTQUADKEY.sql
@@ -8,11 +8,11 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_S2_BUCKET@@"]
 )
 AS """
     if (id == null) {
         throw new Error('NULL argument passed to UDF');
     }
-    return lib.s2.idToKey(id);
+    return s2Lib.idToKey(id);
 """;
diff --git a/clouds/bigquery/modules/sql/transformations/ST_BUFFER.sql b/clouds/bigquery/modules/sql/transformations/ST_BUFFER.sql
index 14227cc33..c5d8b89a1 100644
--- a/clouds/bigquery/modules/sql/transformations/ST_BUFFER.sql
+++ b/clouds/bigquery/modules/sql/transformations/ST_BUFFER.sql
@@ -8,7 +8,7 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_TRANSFORMATIONS_BUCKET@@"]
 )
 AS """
     if (!geojson || radius == null) {
@@ -21,7 +21,7 @@ AS """
     if (steps != null) {
         options.steps = Number(steps);
     }
-    const buffer = lib.transformations.buffer(JSON.parse(geojson), Number(radius), options);
+    const buffer = transformationsLib.buffer(JSON.parse(geojson), Number(radius), options);
     return JSON.stringify(buffer.geometry);
 """;
 
diff --git a/clouds/bigquery/modules/sql/transformations/ST_CENTERMEAN.sql b/clouds/bigquery/modules/sql/transformations/ST_CENTERMEAN.sql
index 40b0db2b2..bc0780ae2 100644
--- a/clouds/bigquery/modules/sql/transformations/ST_CENTERMEAN.sql
+++ b/clouds/bigquery/modules/sql/transformations/ST_CENTERMEAN.sql
@@ -8,13 +8,13 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_TRANSFORMATIONS_BUCKET@@"]
 )
 AS """
     if (!geojson) {
         return null;
     }
-    const center = lib.transformations.centerMean(JSON.parse(geojson));
+    const center = transformationsLib.centerMean(JSON.parse(geojson));
     return JSON.stringify(center.geometry);
 """;
 
diff --git a/clouds/bigquery/modules/sql/transformations/ST_CENTERMEDIAN.sql b/clouds/bigquery/modules/sql/transformations/ST_CENTERMEDIAN.sql
index 413ca93d6..aba6acad3 100644
--- a/clouds/bigquery/modules/sql/transformations/ST_CENTERMEDIAN.sql
+++ b/clouds/bigquery/modules/sql/transformations/ST_CENTERMEDIAN.sql
@@ -8,13 +8,13 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_TRANSFORMATIONS_BUCKET@@"]
 )
 AS """
     if (!geojson) {
         return null;
     }
-    const medianCenter = lib.transformations.centerMedian(lib.transformations.feature(JSON.parse(geojson)));
+    const medianCenter = transformationsLib.centerMedian(transformationsLib.feature(JSON.parse(geojson)));
     return JSON.stringify(medianCenter.geometry);
 """;
 
diff --git a/clouds/bigquery/modules/sql/transformations/ST_CENTEROFMASS.sql b/clouds/bigquery/modules/sql/transformations/ST_CENTEROFMASS.sql
index 2f811310e..f7c362409 100644
--- a/clouds/bigquery/modules/sql/transformations/ST_CENTEROFMASS.sql
+++ b/clouds/bigquery/modules/sql/transformations/ST_CENTEROFMASS.sql
@@ -8,13 +8,13 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_TRANSFORMATIONS_BUCKET@@"]
 )
 AS """
     if (!geojson) {
         return null;
     }
-    const center = lib.transformations.centerOfMass(JSON.parse(geojson));
+    const center = transformationsLib.centerOfMass(JSON.parse(geojson));
     return JSON.stringify(center.geometry);
 """;
 
diff --git a/clouds/bigquery/modules/sql/transformations/ST_CONCAVEHULL.sql b/clouds/bigquery/modules/sql/transformations/ST_CONCAVEHULL.sql
index 3792b2223..d639b2148 100644
--- a/clouds/bigquery/modules/sql/transformations/ST_CONCAVEHULL.sql
+++ b/clouds/bigquery/modules/sql/transformations/ST_CONCAVEHULL.sql
@@ -8,7 +8,7 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_TRANSFORMATIONS_BUCKET@@"]
 )
 AS """
     if (!geojson) {
@@ -22,25 +22,25 @@ AS """
         options.units = units;
     }
 
-    const multiPoints = lib.transformations.multiPoint(geojson.map(x => JSON.parse(x).coordinates));
-    const nonDuplicates = lib.transformations.cleanCoords(multiPoints).geometry;
+    const multiPoints = transformationsLib.multiPoint(geojson.map(x => JSON.parse(x).coordinates));
+    const nonDuplicates = transformationsLib.cleanCoords(multiPoints).geometry;
     const arrayCoordinates = nonDuplicates.coordinates;
 
     // Point
     if (arrayCoordinates.length == 1) {
-        return JSON.stringify(lib.transformations.point(arrayCoordinates[0]).geometry);
+        return JSON.stringify(transformationsLib.point(arrayCoordinates[0]).geometry);
     } 
 
     // Segment
     if (arrayCoordinates.length == 2) {
         const start = arrayCoordinates[0];
         const end = arrayCoordinates[1];
-        const lineString = lib.transformations.lineString([start, end]);
+        const lineString = transformationsLib.lineString([start, end]);
         return JSON.stringify(lineString.geometry);
     }
 
-    const featuresCollection = lib.transformations.featureCollection(arrayCoordinates.map(x => lib.transformations.point(x)));
-    const hull = lib.transformations.concave(featuresCollection, options);
+    const featuresCollection = transformationsLib.featureCollection(arrayCoordinates.map(x => transformationsLib.point(x)));
+    const hull = transformationsLib.concave(featuresCollection, options);
     return JSON.stringify(hull.geometry);
 """;
 
diff --git a/clouds/bigquery/modules/sql/transformations/ST_DESTINATION.sql b/clouds/bigquery/modules/sql/transformations/ST_DESTINATION.sql
index 7ec9fa75a..1da7e427e 100644
--- a/clouds/bigquery/modules/sql/transformations/ST_DESTINATION.sql
+++ b/clouds/bigquery/modules/sql/transformations/ST_DESTINATION.sql
@@ -8,7 +8,7 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_TRANSFORMATIONS_BUCKET@@"]
 )
 AS """
     if (!geojsonStart || distance == null || bearing == null) {
@@ -18,7 +18,7 @@ AS """
     if (units) {
         options.units = units;
     }
-    const destination = lib.transformations.destination(JSON.parse(geojsonStart), Number(distance), Number(bearing), options);
+    const destination = transformationsLib.destination(JSON.parse(geojsonStart), Number(distance), Number(bearing), options);
     return JSON.stringify(destination.geometry);
 """;
 
diff --git a/clouds/bigquery/modules/sql/transformations/ST_GREATCIRCLE.sql b/clouds/bigquery/modules/sql/transformations/ST_GREATCIRCLE.sql
index 9605e2a79..fbf9f0ce9 100644
--- a/clouds/bigquery/modules/sql/transformations/ST_GREATCIRCLE.sql
+++ b/clouds/bigquery/modules/sql/transformations/ST_GREATCIRCLE.sql
@@ -8,7 +8,7 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_TRANSFORMATIONS_BUCKET@@"]
 )
 AS """
     if (!geojsonStart || !geojsonEnd || geojsonEnd === geojsonStart) {
@@ -18,7 +18,7 @@ AS """
     if (npoints != null) {
         options.npoints = Number(npoints);
     }
-    const greatCircle = lib.transformations.greatCircle(JSON.parse(geojsonStart), JSON.parse(geojsonEnd), options);
+    const greatCircle = transformationsLib.greatCircle(JSON.parse(geojsonStart), JSON.parse(geojsonEnd), options);
     return JSON.stringify(greatCircle.geometry);
 """;
 
diff --git a/clouds/bigquery/modules/sql/transformations/ST_LINE_INTERPOLATE_POINT.sql b/clouds/bigquery/modules/sql/transformations/ST_LINE_INTERPOLATE_POINT.sql
index fac984957..6c2c85b1a 100644
--- a/clouds/bigquery/modules/sql/transformations/ST_LINE_INTERPOLATE_POINT.sql
+++ b/clouds/bigquery/modules/sql/transformations/ST_LINE_INTERPOLATE_POINT.sql
@@ -8,7 +8,7 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_TRANSFORMATIONS_BUCKET@@"]
 )
 AS """
     if (!geojson || distance == null) {
@@ -18,7 +18,7 @@ AS """
     if (units) {
         options.units = units;
     }
-    const along = lib.transformations.along(JSON.parse(geojson), distance, options);
+    const along = transformationsLib.along(JSON.parse(geojson), distance, options);
     return JSON.stringify(along.geometry);
 """;
 
diff --git a/clouds/bigquery/modules/sql/transformations/ST_POINTONSURFACE.sql b/clouds/bigquery/modules/sql/transformations/ST_POINTONSURFACE.sql
index 9fcce0575..361545e95 100644
--- a/clouds/bigquery/modules/sql/transformations/ST_POINTONSURFACE.sql
+++ b/clouds/bigquery/modules/sql/transformations/ST_POINTONSURFACE.sql
@@ -8,13 +8,13 @@ RETURNS STRING
 DETERMINISTIC
 LANGUAGE js
 OPTIONS (
-    library = ["@@BQ_LIBRARY_BUCKET@@"]
+    library = ["@@BQ_LIBRARY_TRANSFORMATIONS_BUCKET@@"]
 )
 AS """
     if (!geojson) {
         return null;
     }
-    const center = lib.transformations.pointOnFeature(JSON.parse(geojson));
+    const center = transformationsLib.pointOnFeature(JSON.parse(geojson));
     return JSON.stringify(center.geometry);
 """;