diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 1716b55..0000000
--- a/.editorconfig
+++ /dev/null
@@ -1,6 +0,0 @@
-[*]
-end_of_line = lf
-insert_final_newline = true
-
-indent_style = space
-indent_size = 2
diff --git a/.github/renovate.json b/.github/renovate.json
new file mode 100644
index 0000000..6b9fc3a
--- /dev/null
+++ b/.github/renovate.json
@@ -0,0 +1,6 @@
+{
+    "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+    "extends": [
+        "local>myrotvorets/.github:renovate-config"
+    ]
+}
diff --git a/.github/workflows/audit-signatures.yml b/.github/workflows/audit-signatures.yml
new file mode 100644
index 0000000..6f7feee
--- /dev/null
+++ b/.github/workflows/audit-signatures.yml
@@ -0,0 +1,47 @@
+name: Audit Signatures
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+jobs:
+  audit:
+    name: Verify Signatures and Provenance Statements
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+    steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        with:
+          disable-sudo: true
+          allowed-endpoints: >
+            api.github.com:443
+            github.com:443
+            objects.githubusercontent.com:443
+            nodejs.org:443
+            registry.npmjs.org:443
+            tuf-repo-cdn.sigstore.dev:443
+
+      - name: Checkout
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+      - name: Setup Node.js environment
+        uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
+        with:
+          node-version: lts/*
+  
+      - name: Install latest npm
+        run: npm install -g npm@latest
+
+      - name: Install dependencies
+        run: npm ci
+
+      - name: Run audit
+        run: npm audit signatures
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..b3d0e51
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,40 @@
+# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
+name: Build and Test
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+jobs:
+  build:
+    name: Build and test (Node ${{ matrix.node.name }})
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node:
+          - { name: Current,      version: current }
+          - { name: LTS,          version: lts/* }
+          - { name: Previous LTS, version: lts/-1 }
+    steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        with:
+          disable-sudo: true
+          egress-policy: block
+          allowed-endpoints: >
+            api.github.com:443
+            github.com:443
+            objects.githubusercontent.com:443
+            nodejs.org:443
+            registry.npmjs.org:443
+
+      - name: Build and test
+        uses: myrotvorets/composite-actions/build-test-nodejs@27ab3af18ca12ee3e44e50e015ee74acd199d0dc
+        with:
+          node-version: ${{ matrix.node.version }}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..37d7927
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,54 @@
+name: CodeQL Analysis
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - master
+  schedule:
+    - cron: '4 5 * * 3'
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+jobs:
+  analyze:
+    name: Static Code Analysis with CodeQL
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        language:
+          - javascript
+    permissions:
+      actions: read
+      contents: read
+      security-events: write
+    steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        with:
+          disable-sudo: true
+          egress-policy: block
+          allowed-endpoints: >
+            api.github.com:443
+            github.com:443
+            uploads.github.com:443
+            objects.githubusercontent.com:443
+
+      - name: Checkout repository
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+      - name: Initialize CodeQL
+        uses: github/codeql-action/init@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
+        with:
+          languages: ${{ matrix.language }}
+          queries: +security-and-quality
+
+      - name: Perform CodeQL Analysis
+        uses: github/codeql-action/analyze@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
+        with:
+          category: "/language:${{ matrix.language }}"
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
new file mode 100644
index 0000000..2d4ce01
--- /dev/null
+++ b/.github/workflows/dependency-review.yml
@@ -0,0 +1,34 @@
+name: Dependency Review
+
+on:
+  pull_request:
+
+permissions:
+  contents: read
+
+jobs:
+  dependency-review:
+    runs-on: ubuntu-latest
+    name: Review Dependencies
+    permissions:
+      contents: read
+      pull-requests: write
+    steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        with:
+          disable-sudo: true
+          egress-policy: block
+          allowed-endpoints: >
+            api.deps.dev:443
+            api.github.com:443
+            api.securityscorecards.dev:443
+            github.com:443
+
+      - name: Check out the source code
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+      - name: Review dependencies
+        uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
+        with:
+          comment-summary-in-pr: true
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..e8bc055
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,35 @@
+name: Linting
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+jobs:
+  lint:
+    name: ESLint Check
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+    steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        with:
+          disable-sudo: true
+          egress-policy: block
+          allowed-endpoints: >
+            api.github.com:443
+            github.com:443
+            objects.githubusercontent.com:443
+            nodejs.org:443
+            registry.npmjs.org:443
+
+      - name: Run code style check
+        uses: myrotvorets/composite-actions/node-run-script@27ab3af18ca12ee3e44e50e015ee74acd199d0dc
+        with:
+          script: lint
diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml
new file mode 100644
index 0000000..71ad3ce
--- /dev/null
+++ b/.github/workflows/npm-publish.yml
@@ -0,0 +1,86 @@
+name: Publish Package
+
+on:
+  release:
+    types:
+      - released
+  workflow_dispatch:
+    inputs:
+      npm:
+        default: "yes"
+        description: Publish to NPM?
+        required: true
+      gpr:
+        default: "yes"
+        description: Publish to GPR?
+        required: true
+
+permissions:
+  contents: read
+
+jobs:
+  prepare:
+    name: Prepare source code
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+    if: github.event_name == 'release' || github.event.inputs.npm == 'yes' || github.event.inputs.gpr == 'yes'
+    steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        with:
+          disable-sudo: true
+          egress-policy: block
+          allowed-endpoints: >
+            api.github.com:443
+            github.com:443
+            objects.githubusercontent.com:443
+            nodejs.org:443
+            registry.npmjs.org:443
+
+      - name: Prepare source
+        uses: myrotvorets/composite-actions/node-prepublish@27ab3af18ca12ee3e44e50e015ee74acd199d0dc
+
+  publish:
+    name: Publish package (${{ matrix.registry }})
+    runs-on: ubuntu-latest
+    needs: prepare
+    permissions:
+      contents: read
+      packages: write
+      statuses: write
+      id-token: write
+    strategy:
+      matrix:
+        registry:
+          - npm
+          - gpr
+        include:
+          - registry: npm
+            secret: NPM_TOKEN
+            registry_url: https://registry.npmjs.org/
+          - registry: gpr
+            secret: GITHUB_TOKEN
+            registry_url: https://npm.pkg.github.com/
+    steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        with:
+          disable-sudo: true
+          egress-policy: block
+          allowed-endpoints: >
+            api.github.com:443
+            github.com:443
+            objects.githubusercontent.com:443
+            nodejs.org:443
+            fulcio.sigstore.dev:443
+            registry.npmjs.org:443
+            rekor.sigstore.dev:443
+            npm.pkg.github.com:443
+
+      - name: Publish package
+        uses: myrotvorets/composite-actions/node-publish@27ab3af18ca12ee3e44e50e015ee74acd199d0dc
+        with:
+          node-auth-token: ${{ secrets[matrix.secret] }}
+          registry-url: ${{ matrix.registry_url }}
+        if: github.event.inputs[matrix.registry] == 'yes' || github.event_name == 'release'
diff --git a/.github/workflows/package-audit.yml b/.github/workflows/package-audit.yml
new file mode 100644
index 0000000..376e2ae
--- /dev/null
+++ b/.github/workflows/package-audit.yml
@@ -0,0 +1,33 @@
+name: Package Audit
+
+on:
+  push:
+    branches:
+      - '**'
+    paths:
+      - package.json
+      - package-lock.json
+      - .github/workflows/package-audit.yml
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+jobs:
+  audit-npm:
+    name: NPM Audit
+    runs-on: ubuntu-latest
+    steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        with:
+          disable-sudo: true
+          allowed-endpoints:
+            api.github.com:443
+            github.com:443
+            objects.githubusercontent.com:443
+            nodejs.org:443
+            registry.npmjs.org:443
+
+      - name: Audit with NPM
+        uses: myrotvorets/composite-actions/node-package-audit@27ab3af18ca12ee3e44e50e015ee74acd199d0dc
diff --git a/.github/workflows/push-tag.yml b/.github/workflows/push-tag.yml
new file mode 100644
index 0000000..df14335
--- /dev/null
+++ b/.github/workflows/push-tag.yml
@@ -0,0 +1,57 @@
+name: Pre-release Testing
+
+on:
+  push:
+    tags:
+      - "**"
+
+permissions:
+  contents: read
+
+jobs:
+  build:
+    name: Build and test
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+    steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        with:
+          disable-sudo: true
+          egress-policy: block
+          allowed-endpoints: >
+            api.github.com:443
+            github.com:443
+            objects.githubusercontent.com:443
+            nodejs.org:443
+            registry.npmjs.org:443
+
+      - name: Build and test
+        uses: myrotvorets/composite-actions/build-test-nodejs@27ab3af18ca12ee3e44e50e015ee74acd199d0dc
+
+  release:
+    name: Prepare the release
+    runs-on: ubuntu-latest
+    needs: build
+    permissions:
+      contents: write
+    steps:
+      - name: Harden Runner
+        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        with:
+          disable-sudo: true
+          egress-policy: block
+          allowed-endpoints: >
+            api.github.com:443
+            github.com:443
+
+      - name: Checkout
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+      - name: Create a release
+        uses: softprops/action-gh-release@7b4da11513bf3f43f9999e90eabced41ab8bb048 # v2.2.0
+        with:
+          generate_release_notes: true
+        env:
+          GITHUB_TOKEN: ${{ secrets.REPOSITORY_ACCESS_TOKEN }}
diff --git a/package.json b/package.json
index 3bb77e3..73d6744 100644
--- a/package.json
+++ b/package.json
@@ -51,5 +51,9 @@
   },
   "directories": {
     "test": "test"
+  },
+  "publishConfig": {
+    "access": "public",
+    "provenance": true
   }
 }