Compare commits
30 Commits
41c9ac73d6
...
v3.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
| eb8bee65d9 | |||
| fc4c8745a6 | |||
| 0474973588 | |||
| 7f2cb2c413 | |||
| 3c81b5225f | |||
| d10dabf604 | |||
| 26886f8a7d | |||
| 46dcf65de6 | |||
| d160ad87f9 | |||
| fc74e933d6 | |||
| ad355fe15e | |||
| 4dd8bd1108 | |||
| e2f4433042 | |||
| 7775abbbca | |||
| f340c57ffd | |||
| 6c6a603cd8 | |||
| 35167f1853 | |||
| 815ae473db | |||
| ff3052f3b0 | |||
| 0985e40ead | |||
| 23afe71752 | |||
| a77d661797 | |||
| bf46067a0c | |||
| ed4bbf2327 | |||
| 16bb7d3510 | |||
| edca2698a7 | |||
| 4fd95189ce | |||
| 4f13309cdc | |||
| 4059082416 | |||
| 35bfa52531 |
37
.gitea/workflows/publish.yaml
Normal file
37
.gitea/workflows/publish.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Publish Package
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
runs-on: ubuntu-22.04
|
||||
container:
|
||||
image: ghcr.io/catthehacker/ubuntu:act-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
registry-url: 'https://git.fjla.uk/api/packages/owlboard/npm'
|
||||
scope: '@OwlBoard'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Publish to Gitea Repo
|
||||
run: |
|
||||
VERSION=${GITHUB_REF_NAME#v}
|
||||
npm version $VERSION --no-git-tag-version
|
||||
npm config set //git.fjla.uk/api/packages/owlboard/npm/:_authToken ${{ secrets.REPO_TOKEN }}
|
||||
npm config set //git.fjla.uk/api/packages/OwlBoard/npm/:_authToken ${{ secrets.REPO_TOKEN }}
|
||||
npm publish --registry=https://git.fjla.uk
|
||||
1
.npmrc
Normal file
1
.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
@owlboard:registry=https://git.fjla.uk/api/packages/OwlBoard/npm/
|
||||
803
package-lock.json
generated
Normal file
803
package-lock.json
generated
Normal file
@@ -0,0 +1,803 @@
|
||||
{
|
||||
"name": "@owlboard/owlboard-ts",
|
||||
"version": "3.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@owlboard/owlboard-ts",
|
||||
"version": "3.0.0",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@owlboard/api-schema-types": "^3.0.3-alpha1",
|
||||
"latlon-geohash": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/latlon-geohash": "^2.0.4",
|
||||
"@types/node": "^25.3.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
|
||||
"integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
|
||||
"integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
|
||||
"integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
|
||||
"integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
|
||||
"integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
|
||||
"integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
|
||||
"integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
|
||||
"integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
|
||||
"integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
|
||||
"integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@owlboard/api-schema-types": {
|
||||
"version": "3.0.3-alpha1",
|
||||
"resolved": "https://git.fjla.uk/api/packages/OwlBoard/npm/%40owlboard%2Fapi-schema-types/-/3.0.3-alpha1/api-schema-types-3.0.3-alpha1.tgz",
|
||||
"integrity": "sha512-UWe2nbJWb2B/LuZW1UXHJ2lpqOGwugiXTa4G6X5xLiaws3ISEdciweorX8kr2/JAz5+iFYIe1xXRFAWsFtpn/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
|
||||
"integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/latlon-geohash": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/latlon-geohash/-/latlon-geohash-2.0.4.tgz",
|
||||
"integrity": "sha512-R/wb/V8lhhI0hyRGDTkT6F4C3lynkF/D29iMSN5B4bYswaa4R+wajgcTv9z73BotGArf4Q8BnDNtmLeI+WiKkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz",
|
||||
"integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.5",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",
|
||||
"integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
|
||||
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
|
||||
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.27.3",
|
||||
"@esbuild/android-arm": "0.27.3",
|
||||
"@esbuild/android-arm64": "0.27.3",
|
||||
"@esbuild/android-x64": "0.27.3",
|
||||
"@esbuild/darwin-arm64": "0.27.3",
|
||||
"@esbuild/darwin-x64": "0.27.3",
|
||||
"@esbuild/freebsd-arm64": "0.27.3",
|
||||
"@esbuild/freebsd-x64": "0.27.3",
|
||||
"@esbuild/linux-arm": "0.27.3",
|
||||
"@esbuild/linux-arm64": "0.27.3",
|
||||
"@esbuild/linux-ia32": "0.27.3",
|
||||
"@esbuild/linux-loong64": "0.27.3",
|
||||
"@esbuild/linux-mips64el": "0.27.3",
|
||||
"@esbuild/linux-ppc64": "0.27.3",
|
||||
"@esbuild/linux-riscv64": "0.27.3",
|
||||
"@esbuild/linux-s390x": "0.27.3",
|
||||
"@esbuild/linux-x64": "0.27.3",
|
||||
"@esbuild/netbsd-arm64": "0.27.3",
|
||||
"@esbuild/netbsd-x64": "0.27.3",
|
||||
"@esbuild/openbsd-arm64": "0.27.3",
|
||||
"@esbuild/openbsd-x64": "0.27.3",
|
||||
"@esbuild/openharmony-arm64": "0.27.3",
|
||||
"@esbuild/sunos-x64": "0.27.3",
|
||||
"@esbuild/win32-arm64": "0.27.3",
|
||||
"@esbuild/win32-ia32": "0.27.3",
|
||||
"@esbuild/win32-x64": "0.27.3"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.13.6",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
|
||||
"integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/latlon-geohash": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/latlon-geohash/-/latlon-geohash-2.0.0.tgz",
|
||||
"integrity": "sha512-OKBswTwrvTdtenV+9C9euBmvgGuqyjJNAzpQCarRz1m8/pYD2nz9fKkXmLs2S3jeXaLi3Ry76twQplKKUlgS/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
"@tsconfig/node12": "^1.0.7",
|
||||
"@tsconfig/node14": "^1.0.0",
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-walk": "^8.1.1",
|
||||
"arg": "^4.1.0",
|
||||
"create-require": "^1.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"v8-compile-cache-lib": "^3.0.1",
|
||||
"yn": "3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"ts-node": "dist/bin.js",
|
||||
"ts-node-cwd": "dist/bin-cwd.js",
|
||||
"ts-node-esm": "dist/bin-esm.js",
|
||||
"ts-node-script": "dist/bin-script.js",
|
||||
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||
"ts-script": "dist/bin-script-deprecated.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/core": ">=1.2.50",
|
||||
"@swc/wasm": ">=1.2.50",
|
||||
"@types/node": "*",
|
||||
"typescript": ">=2.7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@swc/wasm": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
|
||||
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.27.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.18.2",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
package.json
Normal file
43
package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@owlboard/owlboard-ts",
|
||||
"version": "3.0.0",
|
||||
"description": "TypeScript API Library to interact with the OwlBoard API (> v3.0.0)",
|
||||
"main": "./dist/index.js",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "tsc",
|
||||
"test-pis": "NODE_TLS_REJECT_UNAUTHORIZED=0 tsx tests/manual/pis.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://svc_gitea@git.fjla.uk:2222/OwlBoard/OwlBoard-TS.git"
|
||||
},
|
||||
"keywords": [
|
||||
"owlboard"
|
||||
],
|
||||
"author": "Frederick Boniface",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@owlboard/api-schema-types": "^3.0.3-alpha1",
|
||||
"latlon-geohash": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/latlon-geohash": "^2.0.4",
|
||||
"@types/node": "^25.3.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
11
src/app.d.ts
vendored
Normal file
11
src/app.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { OwlBoardClient } from "./lib/client";
|
||||
|
||||
declare global {
|
||||
namespace App {
|
||||
interface Locals {
|
||||
api: OwlBoardClient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
11
src/index.ts
Normal file
11
src/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export { OwlBoardClient } from './lib/client.js';
|
||||
export { ValidationError, ApiError } from './lib/errors.js';
|
||||
|
||||
// Re-export API Schema types
|
||||
export type * from '@owlboard/api-schema-types'
|
||||
|
||||
// Useful exports for Type Hinting
|
||||
export { PisModule } from './modules/pis.js';
|
||||
export { LocationFilterModule } from './modules/locationFilter.js';
|
||||
export { StationDataModule } from './modules/stationData.js';
|
||||
export { TrainsModule } from './modules/trains.js';
|
||||
66
src/lib/base.ts
Normal file
66
src/lib/base.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { ApiError } from "./errors.js";
|
||||
import type { ApiErrorCode } from "./errors.js";
|
||||
import type { ApiEnvelope } from "@owlboard/api-schema-types";
|
||||
|
||||
export interface ApiResult<T> {
|
||||
data: T;
|
||||
producedAt: Date;
|
||||
}
|
||||
|
||||
export class BaseClient {
|
||||
protected readonly baseUrl: string;
|
||||
|
||||
constructor(
|
||||
baseUrl: string,
|
||||
protected readonly apiKey?: string
|
||||
) {
|
||||
const root = baseUrl.replace(/\/$/, '');
|
||||
this.baseUrl = root.endsWith('/api/v3')
|
||||
? root
|
||||
: `${root}/api/v3`;
|
||||
}
|
||||
|
||||
public async ping(): Promise<boolean> {
|
||||
try {
|
||||
await fetch(this.baseUrl, {
|
||||
method: 'HEAD',
|
||||
signal: AbortSignal.timeout(3000)
|
||||
});
|
||||
return true
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the Envelope logic
|
||||
*/
|
||||
public async request<T>(path: string, options: RequestInit = {}): Promise<ApiResult<T>> {
|
||||
const url = `${this.baseUrl}${path}`;
|
||||
console.debug(`[API DEBUG] Calling: ${url}`);
|
||||
|
||||
const headers = new Headers(options.headers);
|
||||
headers.set('Content-Type', 'application/json');
|
||||
if (this.apiKey) headers.set('X-OWL-KEY', this.apiKey);
|
||||
|
||||
const response = await fetch(url, { ...options, headers });
|
||||
|
||||
if (!response.ok && !response.headers.get('content-type')?.includes('application/json')) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
// Parse raw envelope
|
||||
const envelope = (await response.json()) as ApiEnvelope.Envelope;
|
||||
|
||||
if (envelope.e !== undefined && envelope.e !== null) {
|
||||
throw new ApiError(envelope.e || {}, response.status);
|
||||
}
|
||||
|
||||
return {
|
||||
data: envelope.d as T,
|
||||
|
||||
// Convert seconds to milliseconds before parsing date
|
||||
producedAt: new Date(Number(envelope.t) * 1000)
|
||||
};
|
||||
}
|
||||
}
|
||||
21
src/lib/client.ts
Normal file
21
src/lib/client.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { BaseClient } from "./base.js";
|
||||
import { PisModule } from "../modules/pis.js";
|
||||
import { LocationFilterModule } from "../modules/locationFilter.js";
|
||||
import { StationDataModule } from "../modules/stationData.js";
|
||||
import { TrainsModule } from "../modules/trains.js";
|
||||
|
||||
export class OwlBoardClient extends BaseClient {
|
||||
public readonly pis: PisModule;
|
||||
public readonly locationFilter: LocationFilterModule;
|
||||
public readonly stationData: StationDataModule;
|
||||
public readonly trains: TrainsModule;
|
||||
|
||||
constructor(baseUrl: string, apiKey?: string) {
|
||||
super(baseUrl, apiKey);
|
||||
|
||||
this.pis = new PisModule(this);
|
||||
this.locationFilter = new LocationFilterModule(this);
|
||||
this.stationData = new StationDataModule(this);
|
||||
this.trains = new TrainsModule(this);
|
||||
}
|
||||
}
|
||||
24
src/lib/errors.ts
Normal file
24
src/lib/errors.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { ApiEnvelope } from "@owlboard/api-schema-types";
|
||||
|
||||
type RawErrorObject = NonNullable<ApiEnvelope.Envelope["e"]>;
|
||||
export type ApiErrorCode = NonNullable<RawErrorObject['code']>
|
||||
|
||||
export class ApiError extends Error {
|
||||
public readonly code: ApiErrorCode;
|
||||
public readonly status: number;
|
||||
|
||||
constructor(errorObj: RawErrorObject, status: number) {
|
||||
super(errorObj.msg || 'An unknown error occurred');
|
||||
this.name = 'ApiError';
|
||||
this.status = status;
|
||||
// cast fallback to ensure it satisfies union
|
||||
this.code = errorObj.code || ("SERVER" as ApiErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
export class ValidationError extends Error {
|
||||
constructor(public field: string, public reason: string) {
|
||||
super(`validation failed for ${field}: ${reason}`);
|
||||
this.name = "VALIDATIONERROR";
|
||||
}
|
||||
}
|
||||
20
src/lib/helpers.ts
Normal file
20
src/lib/helpers.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Normalises input to YYYY-MM-DD string
|
||||
* - Strings are passed through as is
|
||||
* - Date objects are converted to UK time
|
||||
*/
|
||||
export const ensureDateString = (input: Date | string): string => {
|
||||
if (typeof input === 'string') return input.trim();
|
||||
|
||||
const d = new Intl.DateTimeFormat('en-GB', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
timeZone: 'Europe/London'
|
||||
}).formatToParts(input);
|
||||
|
||||
const part = (type: string) => d.find(p => p.type === type)?.value;
|
||||
|
||||
return `${part('year')}-${part('month')}-${part('day')}`
|
||||
|
||||
}
|
||||
125
src/lib/validation.ts
Normal file
125
src/lib/validation.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Checks if a character code is a digit
|
||||
*/
|
||||
const isDigit = (c: number) => c >= 48 && c <= 57;
|
||||
|
||||
/**
|
||||
* Checks if a character code is an upper case letter
|
||||
*/
|
||||
const isUpper = (c: number) => c >= 65 && c <= 90;
|
||||
|
||||
/**
|
||||
* Checks if a string is a valid CRS (Syntactically Only)
|
||||
* using byte level checking for max performance
|
||||
* @param CRS The CRS Code to be validated
|
||||
*/
|
||||
export const IsValidCrs = (CRS: string): boolean => {
|
||||
if (CRS.length !== 3) return false;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const char = CRS.charCodeAt(i);
|
||||
if (!(
|
||||
(char >= 65 && char <=90) ||
|
||||
(char >= 97 && char <=122)
|
||||
)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is a valid TIPLOC (Syntactically Only)
|
||||
* using byte level checking for max performance
|
||||
* @param TIPLOC The TIPLOC to be validated
|
||||
*/
|
||||
export const IsValidTiploc = (TIPLOC: string): boolean => {
|
||||
const l = TIPLOC.length;
|
||||
if (l === 0 || l > 7) return false;
|
||||
for (let i = 0; i > l; i++) {
|
||||
const char = TIPLOC.charCodeAt(i);
|
||||
if (!(
|
||||
(char >= 48 && char <= 57) ||
|
||||
(char >= 65 && char <=90) ||
|
||||
(char >= 97 && char <=122)
|
||||
)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is a valid PIS Code (Syntactically Only)
|
||||
* using byte level checking for max performance
|
||||
* ONLY GWR (4-digit) CODES SUPPORTED
|
||||
* @param PIS The PIS Code to be validated
|
||||
*/
|
||||
export const IsValidPis = (PIS: string): boolean => {
|
||||
if (PIS.length !== 4) return false;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const char = PIS.charCodeAt(i);
|
||||
if (!(char >= 48 && char <= 57)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is the correct format for a Headcode
|
||||
* USes byte level checking for max performance
|
||||
* @param Headcode The headcode to be validated
|
||||
*/
|
||||
export const IsValidHeadcode = (Headcode: string): boolean => {
|
||||
if (Headcode.length !== 4) return false;
|
||||
const c0 = Headcode.charCodeAt(0);
|
||||
const c1 = Headcode.charCodeAt(1);
|
||||
const c2 = Headcode.charCodeAt(2);
|
||||
const c3 = Headcode.charCodeAt(3);
|
||||
|
||||
return isDigit(c0) && isUpper(c1) && isDigit(c2) && isDigit(c3)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is the correct format for a TOC Code
|
||||
*/
|
||||
export const IsValidToc = (toc: string): boolean => {
|
||||
if (toc.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const c0 = toc.charCodeAt(0);
|
||||
const c1 = toc.charCodeAt(1);
|
||||
return isUpper(c0) && isUpper(c1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is the correct format, and is a valid date
|
||||
*/
|
||||
export const IsValidDateStr = (s: string): boolean => {
|
||||
if (s.length !== 10) return false;
|
||||
if (s[4] !== '-' || s[7] !== '-') return false;
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
if (i === 4 || i === 7) continue;
|
||||
const c = s.charCodeAt(i);
|
||||
if (!isDigit(c)) return false;
|
||||
}
|
||||
|
||||
const y = parseInt(s.substring(0, 4), 10);
|
||||
const m = parseInt(s.substring(5, 7), 10) - 1; // JS months are 0-11
|
||||
const d = parseInt(s.substring(8, 10), 10);
|
||||
|
||||
const date = new Date(y, m, d);
|
||||
|
||||
return (
|
||||
date.getFullYear() === y &&
|
||||
date.getMonth() === m &&
|
||||
date.getDate() === d
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates Geohash string against standard b32 alphabet
|
||||
* (Syntactically Only validation)
|
||||
* @param hash The geohash to be validated
|
||||
* @param maxLen Defaults to 6 - which is enforced by the server, should not need changing
|
||||
*/
|
||||
export const IsValidGeoHash = (hash: string, maxLen: number = 6): boolean => {
|
||||
const geoHashRegex = new RegExp(`^[0-9bcdefghjkmnpqrstuvwxyz]{1,${maxLen}}$`, 'i');
|
||||
return geoHashRegex.test(hash);
|
||||
}
|
||||
15
src/modules/locationFilter.ts
Normal file
15
src/modules/locationFilter.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ApiLocationFilter } from "@owlboard/api-schema-types";
|
||||
import type { BaseClient, ApiResult } from '../lib/base.js';
|
||||
|
||||
export class LocationFilterModule {
|
||||
constructor(private client: BaseClient) {}
|
||||
|
||||
async getLocationFilterData(): Promise<ApiResult<ApiLocationFilter.LocationFilterObject[]>> {
|
||||
const path = '/locationFilter/data';
|
||||
|
||||
return this.client.request<ApiLocationFilter.LocationFilterObject[]>(
|
||||
path,
|
||||
{method: "GET"}
|
||||
);
|
||||
}
|
||||
}
|
||||
35
src/modules/pis.ts
Normal file
35
src/modules/pis.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { ApiPisObject } from '@owlboard/api-schema-types';
|
||||
import type { BaseClient, ApiResult } from '../lib/base.js';
|
||||
import { IsValidCrs, IsValidPis } from '../lib/validation.js';
|
||||
import { ValidationError } from '../lib/errors.js';
|
||||
|
||||
export class PisModule {
|
||||
constructor(private client: BaseClient) {}
|
||||
|
||||
async getByStartEndCrs(startCrs: string, endCrs: string): Promise<ApiResult<ApiPisObject.PisObjects[]>> {
|
||||
if (!IsValidCrs(startCrs)) {
|
||||
throw new ValidationError("startCrs", "Invalid CRS Format")
|
||||
}
|
||||
if (!IsValidCrs(endCrs)) {
|
||||
throw new ValidationError("endCrs", "Invalid CRS Format")
|
||||
}
|
||||
|
||||
const path = `/pis/route/${encodeURIComponent(startCrs.toUpperCase())}/${encodeURIComponent(endCrs.toUpperCase())}`;
|
||||
|
||||
return this.client.request<ApiPisObject.PisObjects[]>(path, {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
async getByCode(code: string): Promise<ApiResult<ApiPisObject.PisObjects[]>> {
|
||||
if (!IsValidPis(code)) {
|
||||
throw new ValidationError("code", "Invalid PIS Code Format")
|
||||
}
|
||||
|
||||
const path = `/pis/code/${encodeURIComponent(code)}`;
|
||||
|
||||
return this.client.request<ApiPisObject.PisObjects[]>(path, {
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
}
|
||||
37
src/modules/stationData.ts
Normal file
37
src/modules/stationData.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { ApiStationsNearestStations } from '@owlboard/api-schema-types';
|
||||
import type { BaseClient, ApiResult } from '../lib/base.js';
|
||||
import { IsValidGeoHash } from '../lib/validation.js';
|
||||
import { ValidationError } from '../lib/errors.js';
|
||||
import Geohash from 'latlon-geohash';
|
||||
|
||||
export class StationDataModule {
|
||||
constructor(private client: BaseClient) {}
|
||||
|
||||
/**
|
||||
* Generates a 6-char Geohash for the given co-ordinates
|
||||
* @param lt Latitude
|
||||
* @param ln Longitude
|
||||
* @returns Geohash as string
|
||||
*/
|
||||
generateGeohash(lt: number, ln: number): string {
|
||||
return Geohash.encode(lt, ln, 6);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param geohash Geohash as string (up to six characters), generate using this.generateGeohash()
|
||||
* @returns Nearest Stations API Response (CRS, Name)
|
||||
*/
|
||||
async getNearestStations(geohash: string): Promise<ApiResult<ApiStationsNearestStations.StationsNearestStations[]>> {
|
||||
if (!IsValidGeoHash(geohash)) {
|
||||
throw new ValidationError("hash", "Invalid Geohash requested");
|
||||
}
|
||||
|
||||
const path = `/stationData/nearest/${geohash}`;
|
||||
return this.client.request<ApiStationsNearestStations.StationsNearestStations[]>(path, {
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
// getStationSate(crs: string){}
|
||||
}
|
||||
34
src/modules/trains.ts
Normal file
34
src/modules/trains.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { ApiTrainsTrainByHeadcode } from '@owlboard/api-schema-types';
|
||||
import type { BaseClient, ApiResult } from '../lib/base.js';
|
||||
import { IsValidHeadcode, IsValidToc, IsValidDateStr } from '../lib/validation.js';
|
||||
import { ensureDateString } from '../lib/helpers.js';
|
||||
import { ValidationError } from '../lib/errors.js';
|
||||
|
||||
export class TrainsModule {
|
||||
constructor(private client: BaseClient) { }
|
||||
|
||||
async getByHeadcode(headcode: string, date: string | Date = new Date, toc: string = ""): Promise<ApiResult<ApiTrainsTrainByHeadcode.TrainByHeadcodeResponse[]>> {
|
||||
if (!IsValidHeadcode(headcode)) {
|
||||
throw new ValidationError("headcode", "Invalid headcode format")
|
||||
}
|
||||
if (toc !== "" && !IsValidToc(toc)) {
|
||||
throw new ValidationError("TOC", "Invalid TOC Format, needs to be two characters");
|
||||
}
|
||||
|
||||
const dateStr = ensureDateString(date);
|
||||
if (!IsValidDateStr(dateStr)) {
|
||||
throw new ValidationError("Date", "Invalid date format, need YYYY-MM-DD");
|
||||
}
|
||||
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.append('d', dateStr);
|
||||
searchParams.append('h', headcode);
|
||||
if (toc) searchParams.append('t', toc);
|
||||
|
||||
const path = `/trains?${searchParams.toString()}`;
|
||||
|
||||
return this.client.request<ApiTrainsTrainByHeadcode.TrainByHeadcodeResponse[]>(path, {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
}
|
||||
80
tests/manual/pis.ts
Normal file
80
tests/manual/pis.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { OwlBoardClient } from '../../src/lib/client';
|
||||
import { ApiError, ValidationError } from '../../src/lib/errors';
|
||||
|
||||
console.log("Manual PIS Tests are running")
|
||||
|
||||
const TEST_SERVER: string = "https://owlboard.rke2-gw.svc.fjla.net"
|
||||
console.log(`Using TEST_SERVER: ${TEST_SERVER}`)
|
||||
|
||||
const api = new OwlBoardClient(TEST_SERVER);
|
||||
|
||||
async function main() {
|
||||
// Test server reachability
|
||||
console.log("Testing server reachability")
|
||||
if (!(await api.ping())) {
|
||||
console.error('Server not reachable. Check it is running and is reachable at the provided address')
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("Server is reachable. Running tests...")
|
||||
|
||||
// Run code here.
|
||||
|
||||
{
|
||||
// Test PIS Route
|
||||
const start = 'bri';
|
||||
const end = 'svb';
|
||||
|
||||
console.log(`Querying PIS Route: ${start} > ${end}...`);
|
||||
try {
|
||||
const {data, producedAt} = await api.pis.getByStartEndCrs(start, end);
|
||||
console.log('\n---Response Success---');
|
||||
console.log(`Data Produced At: ${producedAt.toLocaleTimeString()}`);
|
||||
console.log(`Number of entries in response: ${data.length}`);
|
||||
console.log(JSON.stringify(data));
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
console.error('\n--- API Error ---');
|
||||
console.error(`Code: ${err.code}`);
|
||||
console.error(`Msg: ${err.message}`);
|
||||
console.error(`Status: ${err.status}`);
|
||||
} else if (err instanceof ValidationError) {
|
||||
console.error('\n--- Local Validation Error ---');
|
||||
console.error(`Error Field: ${err.field}`);
|
||||
console.error(`Message: ${err.reason}`);
|
||||
console.error(`Request not sent`);
|
||||
} else {
|
||||
console.error(`Unknown Error: ${err}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test PIS Code
|
||||
{
|
||||
const code = 5001;
|
||||
console.log(`Querying PIS Code: ${code}`);
|
||||
try {
|
||||
const {data, producedAt} = await api.pis.getByCode(code.toString());
|
||||
console.log("\n--- Response Success ---");
|
||||
console.log(`Data produced at: ${producedAt.toLocaleString()}`);
|
||||
console.log(`Number of results: ${data.length}`);
|
||||
console.log(JSON.stringify(data));
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
console.error('\n--- API Error ---');
|
||||
console.error(`Code: ${err.code}`);
|
||||
console.error(`Msg: ${err.message}`);
|
||||
console.error(`Status: ${err.status}`);
|
||||
} else if (err instanceof ValidationError) {
|
||||
console.error('\n--- Local Validation Error ---');
|
||||
console.error(`Error Field: ${err.field}`);
|
||||
console.error(`Message: ${err.reason}`);
|
||||
console.error(`Request not sent`);
|
||||
} else {
|
||||
console.error(`Unknown Error: ${err}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
main();
|
||||
48
tsconfig.json
Normal file
48
tsconfig.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
// Visit https://aka.ms/tsconfig to read more about this file
|
||||
"compilerOptions": {
|
||||
// File Layout
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
|
||||
// Environment Settings
|
||||
// See also https://aka.ms/tsconfig/module
|
||||
"module": "nodenext",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "nodenext",
|
||||
"baseUrl": ".",
|
||||
"types": [],
|
||||
// For nodejs:
|
||||
// "lib": ["esnext"],
|
||||
// "types": ["node"],
|
||||
// and npm install -D @types/node
|
||||
|
||||
// Other Outputs
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"composite": true,
|
||||
|
||||
// Stricter Typechecking Options
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
|
||||
// Style Options
|
||||
// "noImplicitReturns": true,
|
||||
// "noImplicitOverride": true,
|
||||
// "noUnusedLocals": true,
|
||||
// "noUnusedParameters": true,
|
||||
// "noFallthroughCasesInSwitch": true,
|
||||
// "noPropertyAccessFromIndexSignature": true,
|
||||
|
||||
// Recommended Options
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"moduleDetection": "force",
|
||||
"skipLibCheck": true,
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user