diff --git a/pnpm-config/.npmrc b/pnpm-config/.npmrc
new file mode 100644
index 0000000000000000000000000000000000000000..9f4565c7b16742d5df7b79912ca40867d97cf272
--- /dev/null
+++ b/pnpm-config/.npmrc
@@ -0,0 +1,5 @@
+# https://gitlab.syncad.com/hive group specification, offering aggregated package view: https://gitlab.syncad.com/groups/hive/-/packages
+@hiveio:registry=https://gitlab.syncad.com/api/v4/groups/136/-/packages/npm/
+
+# Enforce strict engine-versions from package.json
+engine-strict=true
diff --git a/pnpm-config/pnpm-workspace.yaml b/pnpm-config/pnpm-workspace.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ebf88f11620eb297aaa1f2d2328bf16f562b9b65
--- /dev/null
+++ b/pnpm-config/pnpm-workspace.yaml
@@ -0,0 +1,42 @@
+packages:
+  - ./* # maybe we can constrain it to some common subdirs
+
+catalogs:
+  # Can be referenced through "catalog:rollup-toolset"
+  rollup-toolset:
+    rollup: ^4.22.4
+    rollup-plugin-copy: ^3.5.0
+    rollup-plugin-dts: ^6.1.1
+    "@rollup/plugin-commonjs": ^27.0.0
+    "@rollup/plugin-node-resolve": ^15.3.0
+    "@rollup/plugin-replace": ^6.0.1
+
+  typedoc-toolset:
+    "typedoc": "0.27.3"
+    "typedoc-gitlab-wiki-theme": "^2.1.0"
+    "typedoc-plugin-markdown": "4.3.1"
+
+  typescript-toolset:
+    typescript: 5.7.3
+    tslib: ^2.8.1
+    tsx: ^4.19.2
+
+  proto-toolset:
+    protobufjs: ^7.2.5
+    ts-proto: ^1.172.0
+
+  playwright-toolset:
+    "@playwright/test": ^1.49.1
+    playwright: 1.49.1
+    http-server: ^14.1.1
+
+  husky:
+    husky: 8.0.3
+
+  terser:
+    terser: ^5.39.0
+
+  size-limit-toolset:
+    size-limit: ^11.1.6
+    "@size-limit/file": ^11.1.6
+
diff --git a/scripts/bash/npm-helpers/npm_pack_package.sh b/scripts/bash/npm-helpers/npm_pack_package.sh
index 54d271805e71475503484c7db50e95c327e3a31f..04f1c4a75ff35a00d389cbe3ee2c46c8e78ebbb0 100755
--- a/scripts/bash/npm-helpers/npm_pack_package.sh
+++ b/scripts/bash/npm-helpers/npm_pack_package.sh
@@ -15,11 +15,11 @@ pushd "${SOURCE_DIR}" # move to the project directory (where package.json file i
 
 "${SCRIPTPATH}/npm_generate_version.sh" "${SOURCE_DIR}" "${REGISTRY_URL}" "${SCOPE}" "${PROJECT_NAME}" "${COMMIT_REF_PROTECTED}" "${COMMIT_TAG}"
 
-npm pack --pack-destination "${OUTPUT_DIR}" --json > "${OUTPUT_DIR}/built_package_info.json"
-BUILT_PACKAGE_NAME=$(jq -r .[].filename "${OUTPUT_DIR}/built_package_info.json")
+pnpm pack --pack-destination "${OUTPUT_DIR}" --json | tail -n +5 > "${OUTPUT_DIR}/built_package_info.json"
+BUILT_PACKAGE_NAME=$(jq -r .filename "${OUTPUT_DIR}/built_package_info.json")
 {
   echo PACKAGE_SOURCE_DIR="${SOURCE_DIR}"
-  echo BUILT_PACKAGE_PATH="${OUTPUT_DIR}/${BUILT_PACKAGE_NAME}"
+  echo BUILT_PACKAGE_PATH="${BUILT_PACKAGE_NAME}"
 } > "${SOURCE_DIR}/built_package_info.env"
 
 echo "built_package_info.env file contents:"
diff --git a/scripts/bash/npm-helpers/npm_publish.sh b/scripts/bash/npm-helpers/npm_publish.sh
index d48e3fb1e38fc31f199e5d6e01d4dbdc6090b8f8..e1a8bef26865dd45e8db70a9a0c80375589876e8 100755
--- a/scripts/bash/npm-helpers/npm_publish.sh
+++ b/scripts/bash/npm-helpers/npm_publish.sh
@@ -40,7 +40,7 @@ else
   set -e
   echo "Publishing ${NAME}@${VERSION} to tag ${PACKAGE_DIST_TAG}"
   # We are going to repack the tarball as there are registry-dependent data in each job for package.json
-  npm publish --access=public --tag "${PACKAGE_DIST_TAG}"
+  pnpm publish --access=public --tag "${PACKAGE_DIST_TAG}"
 fi
 
 popd
diff --git a/ts-common/terser.ts b/ts-common/terser.ts
new file mode 100644
index 0000000000000000000000000000000000000000..275a18d3fa38948c773a3e09c323108b04230905
--- /dev/null
+++ b/ts-common/terser.ts
@@ -0,0 +1,66 @@
+import type { MinifyOptions } from "terser";
+import { minify } from "terser";
+import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
+import { resolve, join, dirname } from "node:path";
+import { fileURLToPath } from 'node:url';
+import { existsSync } from "node:fs";
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+
+const packageJsonLocation = join(process.cwd(), "package.json");
+const { files } = JSON.parse(readFileSync(packageJsonLocation, { encoding: "utf-8" }));
+
+// =============== #1 ANALYZE "files" in package.json and collect all .js sources ===============
+const jsSources: string[] = [];
+const analyzePath = (path: string) => {
+  if (!existsSync(path)) {
+    console.warn(`The path ${path} does not exist`);
+
+    return;
+  }
+
+  if (statSync(path).isFile()) {
+    if (path.endsWith(".js"))
+      jsSources.push(resolve(__dirname, path));
+
+    return;
+  }
+
+  for(const file of readdirSync(path, { withFileTypes: true }))
+    analyzePath(join(file.parentPath, file.name));
+}
+
+for(const file of files)
+  analyzePath(resolve(file));
+
+// =============== #2 Define terser minification configuration ===============
+
+const minifyOptions: MinifyOptions = {
+  compress: true,
+  mangle: false,
+  ecma: 2020,
+  ie8: false,
+  keep_classnames: true,
+  keep_fnames: true,
+  sourceMap: false,
+  format: {
+    inline_script: false,
+    comments: false,
+    max_line_len: 100
+  }
+};
+
+// =============== #3 Minify the files ===============
+
+for(const file of jsSources) {
+  const inputCode = readFileSync(file, { encoding: "utf-8" });
+
+  void minify(inputCode, minifyOptions).then(({ code }) => {
+    if (!code)
+      throw new Error(`Failed to minify the file ${file}`);
+
+    writeFileSync(file, code, { encoding: "utf-8" });
+
+    console.log(`Minified ${file}`);
+  });
+}
diff --git a/ts-common/tsconfig.base.json b/ts-common/tsconfig.base.json
new file mode 100644
index 0000000000000000000000000000000000000000..621da5c97acfbd54e4c56ff9f0751766b9f29da7
--- /dev/null
+++ b/ts-common/tsconfig.base.json
@@ -0,0 +1,52 @@
+{
+  "compilerOptions": {
+    "emitDecoratorMetadata": true,
+    "resolveJsonModule": true,
+    "experimentalDecorators": true,
+
+    "incremental": false,
+
+    "target": "ES2020",
+    "lib": [
+      "DOM",
+      "ES2020"
+    ],
+    "module": "ES2020",
+    "moduleResolution": "bundler",
+
+    "noEmit": false,
+
+    "declaration": true,
+    "declarationMap": false,
+    "sourceMap": false,
+
+    "noImplicitAny": false,
+    "noLib": false,
+
+    "allowUnreachableCode": false,
+    "allowSyntheticDefaultImports": true,
+    "allowUnusedLabels": false,
+    "strictNullChecks": true,
+    "strictPropertyInitialization": true,
+    "noFallthroughCasesInSwitch": false,
+    "noImplicitReturns": true,
+    "noImplicitThis": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+
+    "removeComments": false,
+    "checkJs": false,
+    "strict": true,
+    "alwaysStrict": true,
+
+    "allowJs": false,
+
+    "esModuleInterop": true,
+
+    "noErrorTruncation": true,
+  },
+  "buildOptions": {
+    "force": true,
+    "verbose": true
+  }
+}
\ No newline at end of file