Compare commits
7 Commits
v3.0.0-dev
...
v3.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 70e64e8d64 | |||
| 935dc271a2 | |||
| ed2f8527d6 | |||
| 50c2e5f427 | |||
| eb8bee65d9 | |||
| fc4c8745a6 | |||
| 0474973588 |
1784
package-lock.json
generated
1784
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -30,8 +30,10 @@
|
|||||||
"author": "Frederick Boniface",
|
"author": "Frederick Boniface",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@owlboard/api-schema-types": "^3.0.3-alpha1",
|
"@owlboard/api-schema-types": "^3.0.3-alpha10",
|
||||||
"latlon-geohash": "^2.0.0"
|
"install": "^0.13.0",
|
||||||
|
"latlon-geohash": "^2.0.0",
|
||||||
|
"npm": "^11.13.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/latlon-geohash": "^2.0.4",
|
"@types/latlon-geohash": "^2.0.4",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export { OwlBoardClient } from './lib/client.js';
|
export { OwlBoardClient } from './lib/client.js';
|
||||||
export { ValidationError, ApiError } from './lib/errors.js';
|
export { ValidationError, ApiError, type ApiErrorCode } from './lib/errors.js';
|
||||||
|
|
||||||
// Re-export API Schema types
|
// Re-export API Schema types
|
||||||
export type * from '@owlboard/api-schema-types'
|
export type * from '@owlboard/api-schema-types'
|
||||||
|
|||||||
@@ -34,8 +34,9 @@ export class BaseClient {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the Envelope logic
|
* Handles the Envelope logic
|
||||||
|
* @param fetcher - Optional custom fetch instance (e.g SvelteKit load fetch)
|
||||||
*/
|
*/
|
||||||
public async request<T>(path: string, options: RequestInit = {}): Promise<ApiResult<T>> {
|
public async request<T>(path: string, options: RequestInit = {}, fetcher: typeof fetch = fetch): Promise<ApiResult<T>> {
|
||||||
const url = `${this.baseUrl}${path}`;
|
const url = `${this.baseUrl}${path}`;
|
||||||
console.debug(`[API DEBUG] Calling: ${url}`);
|
console.debug(`[API DEBUG] Calling: ${url}`);
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ export class BaseClient {
|
|||||||
headers.set('Content-Type', 'application/json');
|
headers.set('Content-Type', 'application/json');
|
||||||
if (this.apiKey) headers.set('X-OWL-KEY', this.apiKey);
|
if (this.apiKey) headers.set('X-OWL-KEY', this.apiKey);
|
||||||
|
|
||||||
const response = await fetch(url, { ...options, headers });
|
const response = await fetcher(url, { ...options, headers });
|
||||||
|
|
||||||
if (!response.ok && !response.headers.get('content-type')?.includes('application/json')) {
|
if (!response.ok && !response.headers.get('content-type')?.includes('application/json')) {
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { BaseClient } from "./base.js";
|
|||||||
import { PisModule } from "../modules/pis.js";
|
import { PisModule } from "../modules/pis.js";
|
||||||
import { LocationFilterModule } from "../modules/locationFilter.js";
|
import { LocationFilterModule } from "../modules/locationFilter.js";
|
||||||
import { StationDataModule } from "../modules/stationData.js";
|
import { StationDataModule } from "../modules/stationData.js";
|
||||||
import { TrainsModule } from "src/modules/trains.js";
|
import { TrainsModule } from "../modules/trains.js";
|
||||||
|
|
||||||
export class OwlBoardClient extends BaseClient {
|
export class OwlBoardClient extends BaseClient {
|
||||||
public readonly pis: PisModule;
|
public readonly pis: PisModule;
|
||||||
|
|||||||
@@ -25,6 +25,24 @@ export const IsValidCrs = (CRS: string): boolean => {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a string is a valid RID, with 15-16 numeric characters
|
||||||
|
* @param rid The RID to validate
|
||||||
|
* @returns True/False - Whether valid or not
|
||||||
|
*/
|
||||||
|
export const IsValidRid = (rid: string): boolean => {
|
||||||
|
if (rid.length > 16 || rid.length < 15) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for (let i = 0; i < rid.length; i++) {
|
||||||
|
const char = rid.charCodeAt(i);
|
||||||
|
if (!isDigit(char)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a string is a valid TIPLOC (Syntactically Only)
|
* Checks if a string is a valid TIPLOC (Syntactically Only)
|
||||||
* using byte level checking for max performance
|
* using byte level checking for max performance
|
||||||
@@ -97,7 +115,7 @@ export const IsValidDateStr = (s: string): boolean => {
|
|||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
if (i === 4 || i === 7) continue;
|
if (i === 4 || i === 7) continue;
|
||||||
const c = s.charCodeAt(i);
|
const c = s.charCodeAt(i);
|
||||||
if (!isDigit(i)) return false;
|
if (!isDigit(c)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const y = parseInt(s.substring(0, 4), 10);
|
const y = parseInt(s.substring(0, 4), 10);
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import type { BaseClient, ApiResult } from '../lib/base.js';
|
|||||||
export class LocationFilterModule {
|
export class LocationFilterModule {
|
||||||
constructor(private client: BaseClient) {}
|
constructor(private client: BaseClient) {}
|
||||||
|
|
||||||
async getLocationFilterData(): Promise<ApiResult<ApiLocationFilter.LocationFilterObject[]>> {
|
async getLocationFilterData(customFetch?: typeof fetch): Promise<ApiResult<ApiLocationFilter.LocationFilterObject[]>> {
|
||||||
const path = '/locationFilter/data';
|
const path = '/locationFilter/data';
|
||||||
|
|
||||||
return this.client.request<ApiLocationFilter.LocationFilterObject[]>(
|
return this.client.request<ApiLocationFilter.LocationFilterObject[]>(
|
||||||
path,
|
path,
|
||||||
{method: "GET"}
|
{method: "GET"},
|
||||||
|
customFetch,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ import { ValidationError } from '../lib/errors.js';
|
|||||||
export class PisModule {
|
export class PisModule {
|
||||||
constructor(private client: BaseClient) {}
|
constructor(private client: BaseClient) {}
|
||||||
|
|
||||||
async getByStartEndCrs(startCrs: string, endCrs: string): Promise<ApiResult<ApiPisObject.PisObjects[]>> {
|
async getByStartEndCrs(startCrs: string, endCrs: string, customFetch?: typeof fetch): Promise<ApiResult<ApiPisObject.PisObjects[]>> {
|
||||||
if (!IsValidCrs(startCrs)) {
|
if (!IsValidCrs(startCrs)) {
|
||||||
throw new ValidationError("startCrs", "Invalid CRS Format")
|
throw new ValidationError("startCrs", "Invalid CRS Format")
|
||||||
}
|
}
|
||||||
@@ -18,10 +18,10 @@ export class PisModule {
|
|||||||
|
|
||||||
return this.client.request<ApiPisObject.PisObjects[]>(path, {
|
return this.client.request<ApiPisObject.PisObjects[]>(path, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
}, customFetch);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getByCode(code: string): Promise<ApiResult<ApiPisObject.PisObjects[]>> {
|
async getByCode(code: string, customFetch?: typeof fetch): Promise<ApiResult<ApiPisObject.PisObjects[]>> {
|
||||||
if (!IsValidPis(code)) {
|
if (!IsValidPis(code)) {
|
||||||
throw new ValidationError("code", "Invalid PIS Code Format")
|
throw new ValidationError("code", "Invalid PIS Code Format")
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,6 @@ export class PisModule {
|
|||||||
|
|
||||||
return this.client.request<ApiPisObject.PisObjects[]>(path, {
|
return this.client.request<ApiPisObject.PisObjects[]>(path, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
})
|
}, customFetch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ export class StationDataModule {
|
|||||||
* @param geohash Geohash as string (up to six characters), generate using this.generateGeohash()
|
* @param geohash Geohash as string (up to six characters), generate using this.generateGeohash()
|
||||||
* @returns Nearest Stations API Response (CRS, Name)
|
* @returns Nearest Stations API Response (CRS, Name)
|
||||||
*/
|
*/
|
||||||
async getNearestStations(geohash: string): Promise<ApiResult<ApiStationsNearestStations.StationsNearestStations[]>> {
|
async getNearestStations(geohash: string, customFetch?: typeof fetch): Promise<ApiResult<ApiStationsNearestStations.StationsNearestStations[]>> {
|
||||||
if (!IsValidGeoHash(geohash)) {
|
if (!IsValidGeoHash(geohash)) {
|
||||||
throw new ValidationError("hash", "Invalid Geohash requested");
|
throw new ValidationError("hash", "Invalid Geohash requested");
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ export class StationDataModule {
|
|||||||
const path = `/stationData/nearest/${geohash}`;
|
const path = `/stationData/nearest/${geohash}`;
|
||||||
return this.client.request<ApiStationsNearestStations.StationsNearestStations[]>(path, {
|
return this.client.request<ApiStationsNearestStations.StationsNearestStations[]>(path, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
})
|
}, customFetch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getStationSate(crs: string){}
|
// getStationSate(crs: string){}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { ApiTrainsTrainByHeadcode } from '@owlboard/api-schema-types';
|
import { ApiTrainsTrainByHeadcode, ApiTrainsTrainDetails } from '@owlboard/api-schema-types';
|
||||||
import type { BaseClient, ApiResult } from '../lib/base.js';
|
import type { BaseClient, ApiResult } from '../lib/base.js';
|
||||||
import { IsValidHeadcode, IsValidToc, IsValidDateStr } from '../lib/validation.js';
|
import { IsValidHeadcode, IsValidRid, IsValidToc, IsValidDateStr } from '../lib/validation.js';
|
||||||
import { ensureDateString } from '../lib/helpers.js';
|
import { ensureDateString } from '../lib/helpers.js';
|
||||||
import { ValidationError } from '../lib/errors.js';
|
import { ValidationError } from '../lib/errors.js';
|
||||||
|
|
||||||
export class TrainsModule {
|
export class TrainsModule {
|
||||||
constructor(private client: BaseClient) { }
|
constructor(private client: BaseClient) { }
|
||||||
|
|
||||||
async getByHeadcode(headcode: string, date: string | Date = new Date, toc: string = ""): Promise<ApiResult<ApiTrainsTrainByHeadcode.TrainByHeadcodeResponse[]>> {
|
async getByHeadcode(headcode: string, date: string | Date = new Date, toc: string = "", customFetch?: typeof fetch): Promise<ApiResult<ApiTrainsTrainByHeadcode.TrainByHeadcodeResponse[]>> {
|
||||||
if (!IsValidHeadcode(headcode)) {
|
if (!IsValidHeadcode(headcode)) {
|
||||||
throw new ValidationError("headcode", "Invalid headcode format")
|
throw new ValidationError("headcode", "Invalid headcode format")
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,17 @@ export class TrainsModule {
|
|||||||
|
|
||||||
return this.client.request<ApiTrainsTrainByHeadcode.TrainByHeadcodeResponse[]>(path, {
|
return this.client.request<ApiTrainsTrainByHeadcode.TrainByHeadcodeResponse[]>(path, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
}, customFetch);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getByRid(rid: string, customFetch?: typeof fetch): Promise<ApiResult<ApiTrainsTrainDetails.TrainDetailsResponse>> {
|
||||||
|
if (!IsValidRid(rid)) {
|
||||||
|
throw new ValidationError("RID", "Invalid RID format, must be 15-16 numerals")
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = `/train/${rid}`
|
||||||
|
return this.client.request<ApiTrainsTrainDetails.TrainDetailsResponse>(path, {
|
||||||
|
method: 'GET',
|
||||||
|
}, customFetch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
// Environment Settings
|
// Environment Settings
|
||||||
// See also https://aka.ms/tsconfig/module
|
// See also https://aka.ms/tsconfig/module
|
||||||
"module": "esnext",
|
"module": "nodenext",
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "nodenext",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"types": [],
|
"types": [],
|
||||||
// For nodejs:
|
// For nodejs:
|
||||||
|
|||||||
Reference in New Issue
Block a user