summaryrefslogtreecommitdiff
path: root/nodejs
diff options
context:
space:
mode:
authorJulien Dessaux2024-03-27 15:20:14 +0100
committerJulien Dessaux2024-03-27 15:21:37 +0100
commita1d6b03ec98abbc073b5b73b631da6ea3eae4eb9 (patch)
tree1566c60bf1155e62821d9561ba1cc998b04b8ea5 /nodejs
parent[all] fixed erroneous contracts index (diff)
downloadspacetraders-a1d6b03ec98abbc073b5b73b631da6ea3eae4eb9.tar.gz
spacetraders-a1d6b03ec98abbc073b5b73b631da6ea3eae4eb9.tar.bz2
spacetraders-a1d6b03ec98abbc073b5b73b631da6ea3eae4eb9.zip
[node] finished the great typescript rewrite
Diffstat (limited to 'nodejs')
-rw-r--r--nodejs/automation/contracting.ts16
-rw-r--r--nodejs/automation/exploration.js11
-rw-r--r--nodejs/automation/exploration.ts9
-rw-r--r--nodejs/automation/init.ts (renamed from nodejs/automation/init.js)24
-rw-r--r--nodejs/automation/mining.js33
-rw-r--r--nodejs/automation/mining.ts29
-rw-r--r--nodejs/automation/selling.ts (renamed from nodejs/automation/selling.js)40
-rw-r--r--nodejs/database/agents.ts8
-rw-r--r--nodejs/database/contracts.ts8
-rw-r--r--nodejs/database/db.ts21
-rw-r--r--nodejs/database/markets.js34
-rw-r--r--nodejs/database/markets.ts26
-rw-r--r--nodejs/database/ships.ts16
-rw-r--r--nodejs/database/surveys.js20
-rw-r--r--nodejs/database/surveys.ts20
-rw-r--r--nodejs/database/systems.ts (renamed from nodejs/database/systems.js)47
-rw-r--r--nodejs/database/tokens.ts6
-rw-r--r--nodejs/lib/api.ts54
-rw-r--r--nodejs/lib/contracts.ts36
-rw-r--r--nodejs/lib/ships.ts58
-rw-r--r--nodejs/lib/systems.ts118
-rwxr-xr-xnodejs/main.ts8
-rw-r--r--nodejs/model/api.ts3
-rw-r--r--nodejs/model/common.ts5
-rw-r--r--nodejs/model/errors.ts8
-rw-r--r--nodejs/model/market.ts19
-rw-r--r--nodejs/model/ship.ts8
-rw-r--r--nodejs/model/system.ts30
-rw-r--r--nodejs/package-lock.json641
-rw-r--r--nodejs/package.json10
30 files changed, 1013 insertions, 353 deletions
diff --git a/nodejs/automation/contracting.ts b/nodejs/automation/contracting.ts
index 2857778..aa4cd95 100644
--- a/nodejs/automation/contracting.ts
+++ b/nodejs/automation/contracting.ts
@@ -34,7 +34,7 @@ async function runProcurement(contract: Contract, ships: Array<Ship>) {
// TODO check if contract is fulfilled!
const wantedCargo = contract.terms.deliver[0].tradeSymbol;
const deliveryPoint = contract.terms.deliver[0].destinationSymbol;
- const asteroids = await systems.type({symbol: ships[0].nav.systemSymbol, type: 'ENGINEERED_ASTEROID'});
+ const asteroids = await systems.type(ships[0].nav.systemSymbol, 'ENGINEERED_ASTEROID');
const asteroidSymbol = asteroids[0].symbol;
ships.forEach(async function(ship) {
while (!contract.fulfilled) {
@@ -46,12 +46,8 @@ async function runProcurement(contract: Contract, ships: Array<Ship>) {
// Then it depends on where we are
switch (ship.nav.waypointSymbol) {
case asteroidSymbol:
- await mining.mineUntilFullOf({
- asteroidSymbol: asteroidSymbol,
- good: wantedCargo,
- symbol: ship.symbol
- });
- await libShips.navigate(ship, deliveryPoint);
+ ship = await mining.mineUntilFullOf(wantedCargo, ship, asteroidSymbol);
+ ship = await libShips.navigate(ship, deliveryPoint);
break;
case deliveryPoint:
if (goodCargo !== undefined) { // we could be here if a client restart happens right after selling before we navigate away
@@ -59,12 +55,12 @@ async function runProcurement(contract: Contract, ships: Array<Ship>) {
contract = await contracts.deliver(contract, ship);
if (contract.fulfilled) break;
}
- await libShips.navigate(ship, asteroidSymbol);
+ ship = await libShips.navigate(ship, asteroidSymbol);
break;
default:
// we were either selling or started contracting
- await selling.sell(ship, wantedCargo);
- await libShips.navigate(ship, asteroidSymbol);
+ ship = await selling.sell(ship, wantedCargo);
+ ship = await libShips.navigate(ship, asteroidSymbol);
}
}
// TODO repurpose the ship
diff --git a/nodejs/automation/exploration.js b/nodejs/automation/exploration.js
deleted file mode 100644
index e4f24f1..0000000
--- a/nodejs/automation/exploration.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import db from '../database/db.js';
-import * as dbSystems from '../database/systems.js';
-import * as api from '../lib/api.js';
-
-export async function init() {
- const response = await api.send({endpoint: `/systems`, page: Math.max(1, Math.floor(dbSystems.getSystemsCount()/20)), priority: 100});
- if (response.error !== undefined) {
- throw response;
- }
- db.transaction(() => response.forEach(function(system) { try { dbSystems.addSystem(system); } catch {} }))();
-}
diff --git a/nodejs/automation/exploration.ts b/nodejs/automation/exploration.ts
new file mode 100644
index 0000000..67fbfe4
--- /dev/null
+++ b/nodejs/automation/exploration.ts
@@ -0,0 +1,9 @@
+import db from '../database/db.ts';
+import * as dbSystems from '../database/systems.ts';
+import { System } from '../model/system.ts';
+import * as api from '../lib/api.ts';
+
+export async function init(): Promise<void> {
+ const response = await api.sendPaginated<System>({endpoint: `/systems`, page: Math.max(1, Math.floor(dbSystems.getSystemsCount()/20)), priority: 100});
+ db.transaction(() => response.forEach(function(system) { try { dbSystems.addSystem(system); } catch {} }))();
+}
diff --git a/nodejs/automation/init.js b/nodejs/automation/init.ts
index 93e5a70..b3c7a40 100644
--- a/nodejs/automation/init.js
+++ b/nodejs/automation/init.ts
@@ -1,15 +1,19 @@
-import * as dbAgents from '../database/agents.js';
-import * as db from '../database/db.js';
-import * as dbContracts from '../database/contracts.js';
-import * as dbShips from '../database/ships.js';
-import * as dbTokens from '../database/tokens.js';
-import * as api from '../lib/api.js';
-import * as ships from '../lib/ships.js';
+import * as dbAgents from '../database/agents.ts';
+import * as db from '../database/db.ts';
+import * as dbContracts from '../database/contracts.ts';
+import * as dbShips from '../database/ships.ts';
+import * as dbTokens from '../database/tokens.ts';
+import { Agent } from '../model/agent.ts';
+import { Response } from '../model/api.ts';
+import { Contract } from '../model/contract.ts';
+import { Ship } from '../model/ship.ts';
+import * as api from '../lib/api.ts';
+import * as ships from '../lib/ships.ts';
-const symbol = process.env.NODE_ENV === 'test' ? 'ADYXAX-0' : 'ADYXAX-TS';
+const symbol = process.env.NODE_ENV === 'test' ? 'ADYXAX-0' : 'ADYXAX-JS';
// This function registers then inits the database
-export async function init() {
+export async function init(): Promise<void> {
const response = await fetch('https://api.spacetraders.io/v2/register', {
method: 'POST',
headers: {
@@ -20,7 +24,7 @@ export async function init() {
faction: "COSMIC",
}),
});
- const json = await response.json();
+ const json = await response.json() as Response<{agent: Agent, contract: Contract, ship: Ship, token: string}>;
if (json.error !== undefined) {
switch(json.error?.code) {
case 4111: // 4111 means the agent symbol has already been claimed so no server reset happened
diff --git a/nodejs/automation/mining.js b/nodejs/automation/mining.js
deleted file mode 100644
index 79a784a..0000000
--- a/nodejs/automation/mining.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import * as selling from './selling.js';
-import * as dbShips from '../database/ships.js';
-import * as api from '../lib/api.js';
-import * as libShips from '../lib/ships.js';
-import * as utils from '../lib/utils.js';
-
-// example ctx { asteroidSymbol: XXXXX, good: 'SILVER_ORE', symbol: 'ADYXAX-2' }
-// returns the number of units of the good the ship holds
-export async function mineUntilFullOf(ctx) {
- while(true) {
- const ship = dbShips.getShip(ctx.symbol);
- const cargo = utils.categorizeCargo(await mineUntilFull({symbol: ctx.symbol}), ctx.good);
- const wantedUnits = Object.values(cargo.wanted).reduce((acc, e) => acc += e, 0);
- // > 90% full of the valuable goods ?
- if (wantedUnits >= ship.cargo.capacity * 0.9) return;
- // we are full but need to sell junk
- await selling.sell(ship, ctx.good);
- await libShips.navigate({symbol: ship.symbol, waypoint: ctx.asteroidSymbol});
- }
-}
-
-// example ctx { symbol: 'ADYXAX-2' }
-// extract the ship's cargo contents when more than 80% full then returns the ships cargo object
-async function mineUntilFull(ctx) {
- while(true) {
- const ship = dbShips.getShip(ctx.symbol);
- if (ship.cargo.units >= ship.cargo.capacity * 0.9) return ship.cargo;
- if (await libShips.extract({symbol: ctx.symbol}) === null)
- await ship(ctx); // refresh the ships status from the server just in case
- }
-}
-
-// TODO surveying the asteroid field
diff --git a/nodejs/automation/mining.ts b/nodejs/automation/mining.ts
new file mode 100644
index 0000000..5e36de5
--- /dev/null
+++ b/nodejs/automation/mining.ts
@@ -0,0 +1,29 @@
+import * as selling from './selling.js';
+import * as dbShips from '../database/ships.js';
+import * as api from '../lib/api.js';
+import * as libShips from '../lib/ships.js';
+import * as utils from '../lib/utils.js';
+import { Ship } from '../model/ship.ts';
+
+export async function mineUntilFullOf(good: string, ship: Ship, asteroidSymbol: string): Promise<Ship> {
+ // TODO find a good asteroid
+ while(true) {
+ ship = await mineUntilFull(ship);
+ const cargo = utils.categorizeCargo(ship.cargo, good);
+ const wantedUnits = Object.values(cargo.wanted).reduce((acc, e) => acc += e, 0);
+ // > 90% full of the valuable goods ?
+ if (wantedUnits >= ship.cargo.capacity * 0.9) return ship;
+ // we are full but need to sell junk
+ await selling.sell(ship, good);
+ await libShips.navigate(ship, asteroidSymbol);
+ }
+}
+
+// example ctx { symbol: 'ADYXAX-2' }
+// extract the ship's cargo contents when more than 80% full then returns the ships cargo object
+async function mineUntilFull(ship: Ship): Promise<Ship> {
+ for (;ship.cargo.units <= ship.cargo.capacity * 0.9; ship = await libShips.extract(ship)) {}
+ return ship;
+}
+
+// TODO surveying the asteroid field
diff --git a/nodejs/automation/selling.js b/nodejs/automation/selling.ts
index 3d444af..dbdacfe 100644
--- a/nodejs/automation/selling.js
+++ b/nodejs/automation/selling.ts
@@ -1,37 +1,34 @@
-import * as dbMarkets from '../database/markets.js';
-import * as dbShips from '../database/ships.js';
-import * as api from '../lib/api.js';
-import * as libShips from '../lib/ships.js';
-import * as libSystems from '../lib/systems.js';
-import * as utils from '../lib/utils.js';
+import * as dbMarkets from '../database/markets.ts';
+import * as dbShips from '../database/ships.ts';
+import * as api from '../lib/api.ts';
+import * as libShips from '../lib/ships.ts';
+import * as libSystems from '../lib/systems.ts';
+import * as utils from '../lib/utils.ts';
+import { CargoManifest } from '../model/cargo.ts';
+import { CommonThing } from '../model/common.ts';
+import { Ship } from '../model/ship.ts';
// example ctx { ship: {XXX}, keep: 'SILVER_ORE' }
-export async function sell(ship, keep) {
+export async function sell(ship: Ship, good: string): Promise<Ship> {
outer: while(true) {
// first lets see what we want to sell
- let cargo = utils.categorizeCargo(ship.cargo, keep);
+ let cargo = utils.categorizeCargo(ship.cargo, good);
// get the marketdata from our location
const market = await libSystems.market(ship.nav.waypointSymbol);
// can we sell anything here?
const goods = whatCanBeTradedAt(cargo.goods, market.imports.concat(market.exchange));
for (let i = 0; i < goods.length; i++) {
const symbol = goods[i].symbol;
- await libShips.sell({
- good: symbol,
- symbol: ship.symbol,
- units: cargo.goods[symbol],
- });
- delete cargo.goods[symbol];
+ ship = await libShips.sell(ship, good);
};
// are we done selling everything we can?
- ship = dbShips.getShip(ship.symbol);
- cargo = utils.categorizeCargo(ship.cargo, keep);
+ cargo = utils.categorizeCargo(ship.cargo, good);
if (Object.keys(cargo.goods).length === 0) {
- return;
+ return ship;
}
// we need to move somewhere else to sell our remaining goods
// first we look into markets in our system
- const rawMarkets = await libSystems.trait({symbol: ship.nav.systemSymbol, trait: 'MARKETPLACE'});
+ const rawMarkets = await libSystems.trait(ship.nav.systemSymbol, 'MARKETPLACE');
// sorted by distance from where we are
const markets = rawMarkets.map(function (m) { return {
data: m,
@@ -52,7 +49,7 @@ export async function sell(ship, keep) {
// if we have no data on the market we need to go there and see
// and if we have data and can sell there we need to go too
if (market === null || whatCanBeTradedAt(cargo.goods, market.imports).length > 0) {
- await libShips.navigate({symbol: ship.symbol, waypoint: waypointSymbol});
+ ship = await libShips.navigate(ship, waypointSymbol);
continue outer;
}
}
@@ -62,7 +59,7 @@ export async function sell(ship, keep) {
const market = await libSystems.market(waypointSymbol);
// if we can sell there we need to go
if (whatCanBeTradedAt(cargo.goods, market.exchange).length > 0) {
- await libShips.navigate({symbol: ship.symbol, waypoint: waypointSymbol});
+ ship = await libShips.navigate(ship, waypointSymbol);
continue outer;
}
}
@@ -70,7 +67,6 @@ export async function sell(ship, keep) {
}
}
-function whatCanBeTradedAt(cargo, goods) {
- if (goods === undefined) return [];
+function whatCanBeTradedAt(cargo: CargoManifest, goods: Array<CommonThing>): Array<CommonThing> {
return goods.filter(g => cargo[g.symbol] !== undefined );
}
diff --git a/nodejs/database/agents.ts b/nodejs/database/agents.ts
index 41b956a..3fc4182 100644
--- a/nodejs/database/agents.ts
+++ b/nodejs/database/agents.ts
@@ -1,20 +1,20 @@
import { Agent } from '../model/agent.ts';
-import db from './db.js';
+import { DbData, db } from './db.ts';
const addAgentStatement = db.prepare(`INSERT INTO agents(data) VALUES (json(?));`);
const getAgentStatement = db.prepare(`SELECT data FROM agents;`);
const setAgentStatement = db.prepare(`UPDATE agents SET data = json(?);`);
-export function addAgent(agent: Agent) {
+export function addAgent(agent: Agent): void {
addAgentStatement.run(JSON.stringify(agent));
}
export function getAgent(): Agent|null {
- const data = getAgentStatement.get() as {data: string}|undefined;
+ const data = getAgentStatement.get() as DbData|undefined;
if (!data) return null;
return JSON.parse(data.data);
}
-export function setAgent(agent: Agent) {
+export function setAgent(agent: Agent): void {
setAgentStatement.run(JSON.stringify(agent));
}
diff --git a/nodejs/database/contracts.ts b/nodejs/database/contracts.ts
index 576f8dd..ff24524 100644
--- a/nodejs/database/contracts.ts
+++ b/nodejs/database/contracts.ts
@@ -1,5 +1,5 @@
import { Contract } from '../model/contract.ts';
-import db from './db.ts';
+import { DbData, db } from './db.ts';
const addContractStatement = db.prepare(`INSERT INTO contracts(data) VALUES (json(?));`);
const getContractStatement = db.prepare(`SELECT data FROM contracts WHERE data->>'id' = ?;`);
@@ -7,17 +7,17 @@ const getContractsStatement = db.prepare(`SELECT data FROM contracts WHERE data-
const updateContractStatement = db.prepare(`UPDATE contracts SET data = json(:data) WHERE data->>'id' = :id;`);
export function getContract(id: string): Contract|null {
- const data = getContractStatement.get(id) as {data: string}|undefined;
+ const data = getContractStatement.get(id) as DbData|undefined;
if (!data) return null;
return JSON.parse(data.data);
}
export function getContracts(): Array<Contract> {
- const data = getContractsStatement.all() as Array<{data: string}>;
+ const data = getContractsStatement.all() as Array<DbData>;
return data.map(contractData => JSON.parse(contractData.data));
}
-export function setContract(data: Contract) {
+export function setContract(data: Contract): void {
if (getContract(data.id) === null) {
addContractStatement.run(JSON.stringify(data));
} else {
diff --git a/nodejs/database/db.ts b/nodejs/database/db.ts
index 247ee5a..115d6a3 100644
--- a/nodejs/database/db.ts
+++ b/nodejs/database/db.ts
@@ -1,22 +1,23 @@
import fs from 'fs';
+import path from 'path';
import Database from 'better-sqlite3';
-const allMigrations = [
- 'database/000_init.sql',
- 'database/001_systems.sql',
- 'database/002_ships.sql',
- 'database/003_surveys.sql',
- 'database/004_markets.sql',
-];
+let allMigrations: Array<string> = [];
+fs.readdir('./database/', function(err, files) {
+ if (err) throw err;
+ allMigrations = files.filter(e => e.match(/\.sql$/)).map(e => path.join('./database', e));
+});
-const db = new Database(
+export type DbData = {data: string};
+
+export const db = new Database(
process.env.NODE_ENV === 'test' ? 'test.db' : 'spacetraders.db',
process.env.NODE_ENV === 'development' ? { verbose: console.log } : undefined
);
db.pragma('foreign_keys = ON');
db.pragma('journal_mode = WAL');
-function init() {
+function init(): void {
db.transaction(function migrate() {
let version;
try {
@@ -34,7 +35,7 @@ function init() {
})();
}
-export function reset() {
+export function reset(): void {
const indices = db.prepare(`SELECT name FROM sqlite_master WHERE type = 'index';`).all() as Array<{name: string}>;
const tables = db.prepare(`SELECT name FROM sqlite_master WHERE type = 'table';`).all() as Array<{name: string}>;
const triggers = db.prepare(`SELECT name FROM sqlite_master WHERE type = 'trigger';`).all() as Array<{name: string}>;
diff --git a/nodejs/database/markets.js b/nodejs/database/markets.js
deleted file mode 100644
index 522f57f..0000000
--- a/nodejs/database/markets.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import db from './db.js';
-import * as utils from '../lib/utils.js';
-
-const addMarketStatement = db.prepare(`INSERT INTO markets(system, data) VALUES (?, json(?));`);
-const getMarketAtWaypointStatement = db.prepare(`SELECT data FROM markets WHERE data->>'symbol' = ?;`);
-const getMarketsInSystemStatement = db.prepare(`SELECT data FROM markets WHERE system = ?;`);
-const updateMarketStatement = db.prepare(`UPDATE markets SET data = json(:data) WHERE data->>'symbol' = :symbol;`);
-
-export function getMarketAtWaypoint(symbol) {
- const data = getMarketAtWaypointStatement.get(symbol);
- if (data === undefined) {
- return null;
- }
- return JSON.parse(data.data);
-}
-
-export function getMarketsInSystem(symbol) {
- const data = getMarketsInSystemStatement.get(symbol);
- if (data === undefined) {
- return null;
- }
- return JSON.parse(data.data);
-}
-
-export function setMarket(data) {
- if (getMarketAtWaypoint(data.symbol) === null) {
- const system = utils.systemFromWaypoint(data.symbol);
- return addMarketStatement.run(system, JSON.stringify(data)).lastInsertRowid;
- }
- return updateMarketStatement.run({
- data: JSON.stringify(data),
- symbol: data.symbol,
- }).changes;
-}
diff --git a/nodejs/database/markets.ts b/nodejs/database/markets.ts
new file mode 100644
index 0000000..7ae5ca2
--- /dev/null
+++ b/nodejs/database/markets.ts
@@ -0,0 +1,26 @@
+import { DbData, db } from './db.ts';
+import { Market } from '../model/market.ts';
+import { systemFromWaypoint } from '../lib/utils.ts';
+
+const addMarketStatement = db.prepare(`INSERT INTO markets(system, data) VALUES (?, json(?));`);
+const getMarketAtWaypointStatement = db.prepare(`SELECT data FROM markets WHERE data->>'symbol' = ?;`);
+//const getMarketsInSystemStatement = db.prepare(`SELECT data FROM markets WHERE system = ?;`);
+const updateMarketStatement = db.prepare(`UPDATE markets SET data = json(:data) WHERE data->>'symbol' = :symbol;`);
+
+export function getMarketAtWaypoint(symbol: string): Market|null {
+ const data = getMarketAtWaypointStatement.get(symbol) as DbData;
+ if (!data) return null;
+ return JSON.parse(data.data);
+}
+
+export function setMarket(data: Market): void {
+ if (getMarketAtWaypoint(data.symbol) === null) {
+ const system = systemFromWaypoint(data.symbol);
+ addMarketStatement.run(system, JSON.stringify(data));
+ } else {
+ updateMarketStatement.run({
+ data: JSON.stringify(data),
+ symbol: data.symbol,
+ });
+ }
+}
diff --git a/nodejs/database/ships.ts b/nodejs/database/ships.ts
index 58c8abf..414a059 100644
--- a/nodejs/database/ships.ts
+++ b/nodejs/database/ships.ts
@@ -1,4 +1,4 @@
-import db from './db.ts';
+import { DbData, db } from './db.ts';
import { Cargo } from '../model/cargo.ts';
import { Fuel, Nav, Ship } from '../model/ship.ts';
@@ -11,18 +11,18 @@ const setShipNavStatement = db.prepare(`UPDATE ships SET data = (SELECT json_set
const updateShipStatement = db.prepare(`UPDATE ships SET data = json(:data) WHERE data->>'symbol' = :symbol;`);
export function getShip(symbol: string): Ship|null {
- const data = getShipStatement.get(symbol) as {data: string}|undefined;
+ const data = getShipStatement.get(symbol) as DbData|undefined;
if (!data) return null;
return JSON.parse(data.data);
}
-export function getShipsAt(symbol: string) {
- const data = getShipsAtStatement.all(symbol) as Array<{data: string}>;
+export function getShipsAt(symbol: string): Array<Ship> {
+ const data = getShipsAtStatement.all(symbol) as Array<DbData>;
return data.map(elt => JSON.parse(elt.data));
}
-export function setShip(data: Ship) {
+export function setShip(data: Ship): void {
if (getShip(data.symbol) === null) {
addShipStatement.run(JSON.stringify(data));
} else {
@@ -33,21 +33,21 @@ export function setShip(data: Ship) {
}
}
-export function setShipCargo(symbol: string, cargo: Cargo) {
+export function setShipCargo(symbol: string, cargo: Cargo): void {
setShipCargoStatement.run({
cargo: JSON.stringify(cargo),
symbol: symbol,
});
}
-export function setShipFuel(symbol: string, fuel: Fuel) {
+export function setShipFuel(symbol: string, fuel: Fuel): void {
setShipFuelStatement.run({
fuel: JSON.stringify(fuel),
symbol: symbol,
});
}
-export function setShipNav(symbol: string, nav: Nav) {
+export function setShipNav(symbol: string, nav: Nav): void {
setShipNavStatement.run({
nav: JSON.stringify(nav),
symbol: symbol,
diff --git a/nodejs/database/surveys.js b/nodejs/database/surveys.js
deleted file mode 100644
index 3397374..0000000
--- a/nodejs/database/surveys.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import db from './db.js';
-
-const deleteExpiredSurveysStatement = db.prepare(`DELETE FROM surveys WHERE data->>'expiration' < ?;`);
-const getSurveysStatement = db.prepare(`SELECT data FROM surveys WHERE data->>'symbol' = ?;`);
-const setSurveysStatement = db.prepare(`INSERT INTO surveys(data) VALUES (json(?));`);
-
-export function deleteExpired() {
- return deleteExpiredSurveysStatement.run(new Date().toISOString()).changes;
-}
-
-export function get(symbol) {
- deleteExpired();
- const data = getSurveysStatement.all(symbol);
- return data.map(elt => JSON.parse(elt.data));
-}
-
-export function set(survey) {
- deleteExpired();
- return setSurveysStatement.run(JSON.stringify(survey));
-}
diff --git a/nodejs/database/surveys.ts b/nodejs/database/surveys.ts
new file mode 100644
index 0000000..6492805
--- /dev/null
+++ b/nodejs/database/surveys.ts
@@ -0,0 +1,20 @@
+import { DbData, db } from './db.ts';
+
+//const deleteExpiredSurveysStatement = db.prepare(`DELETE FROM surveys WHERE data->>'expiration' < ?;`);
+//const getSurveysStatement = db.prepare(`SELECT data FROM surveys WHERE data->>'symbol' = ?;`);
+//const setSurveysStatement = db.prepare(`INSERT INTO surveys(data) VALUES (json(?));`);
+//
+//export function deleteExpired() {
+// return deleteExpiredSurveysStatement.run(new Date().toISOString()).changes;
+//}
+//
+//export function get(symbol) {
+// deleteExpired();
+// const data = getSurveysStatement.all(symbol);
+// return data.map(elt => JSON.parse(elt.data));
+//}
+//
+//export function set(survey) {
+// deleteExpired();
+// return setSurveysStatement.run(JSON.stringify(survey));
+//}
diff --git a/nodejs/database/systems.js b/nodejs/database/systems.ts
index f989218..d6d6cf1 100644
--- a/nodejs/database/systems.js
+++ b/nodejs/database/systems.ts
@@ -1,4 +1,5 @@
-import db from './db.js';
+import { DbData, db } from './db.ts';
+import { System, Waypoint } from '../model/system.ts';
const addSystemStatement = db.prepare(`INSERT INTO systems(data) VALUES (json(?));`);
const getSystemStatement = db.prepare(`SELECT data FROM systems WHERE data->>'symbol' = ?;`);
@@ -7,50 +8,44 @@ const getSystemsCountStatement = db.prepare(`SELECT COUNT(data) as data FROM sys
const setSystemStatement = db.prepare(`UPDATE systems SET data = json(:data), updated = :date WHERE data->>'symbol' = :symbol;`);
const setSystemWaypointsStatement = db.prepare(`UPDATE systems SET data = (SELECT json_set(data, '$.waypoints', json(:waypoints)) FROM systems WHERE data->>'symbol' = :symbol), updated = :date WHERE data->>'symbol' = :symbol;`);
-export function addSystem(data) {
- return addSystemStatement.run(JSON.stringify(data)).lastInsertRowid;
+export function addSystem(data: System): void {
+ addSystemStatement.run(JSON.stringify(data)).lastInsertRowid;
}
-export function getSystem(symbol) {
- const data = getSystemStatement.get(symbol);
- if (data === undefined) {
- return null;
- }
+export function getSystem(symbol: string): System|null {
+ const data = getSystemStatement.get(symbol) as DbData|undefined;
+ if (!data) return null;
return JSON.parse(data.data);
}
-export function getSystemsCount() {
- const data = getSystemsCountStatement.get();
- if (data === undefined) {
- return null;
- }
- return data.data;
+export function getSystemsCount(): number {
+ const data = getSystemsCountStatement.get() as number|undefined;
+ if (!data) return 0;
+ return data;
}
-export function getSystemUpdated(symbol) {
- const updated = getSystemUpdatedStatement.get(symbol);
- if (updated === undefined) {
- return null;
- }
- return updated.updated;
+export function getSystemUpdated(symbol: string): Date|null {
+ const data = getSystemUpdatedStatement.get(symbol) as {updated: Date}|undefined;
+ if (!data) return null;
+ return data.updated;
}
-export function setSystem(data) {
+export function setSystem(data: System): void {
if (getSystem(data.symbol) === null) {
addSystem(data);
} else {
- return setSystemStatement.run({
+ setSystemStatement.run({
data: JSON.stringify(data),
date: new Date().toISOString(),
symbol: data.symbol,
- }).changes;
+ });
}
}
-export function setSystemWaypoints(symbol, waypoints) {
- return setSystemWaypointsStatement.run({
+export function setSystemWaypoints(symbol: string, waypoints: Array<Waypoint>): void {
+ setSystemWaypointsStatement.run({
date: new Date().toISOString(),
symbol: symbol,
waypoints: JSON.stringify(waypoints),
- }).changes;
+ });
}
diff --git a/nodejs/database/tokens.ts b/nodejs/database/tokens.ts
index 4495a65..4d67ded 100644
--- a/nodejs/database/tokens.ts
+++ b/nodejs/database/tokens.ts
@@ -1,14 +1,14 @@
-import db from './db.ts';
+import { DbData, db } from './db.ts';
const addTokenStatement = db.prepare(`INSERT INTO tokens(data) VALUES (?);`);
const getTokenStatement = db.prepare(`SELECT data FROM tokens;`);
-export function addToken(token: string) {
+export function addToken(token: string): void {
addTokenStatement.run(token);
}
export function getToken(): string|null {
- const data = getTokenStatement.get() as {data: string}|undefined;
+ const data = getTokenStatement.get() as DbData|undefined;
if (data === undefined) return null;
return data.data;
}
diff --git a/nodejs/lib/api.ts b/nodejs/lib/api.ts
index 08c6c71..f023ccf 100644
--- a/nodejs/lib/api.ts
+++ b/nodejs/lib/api.ts
@@ -1,9 +1,10 @@
-import * as fs from 'fs';
-import * as events from 'events';
+import fs from 'fs';
+import events from 'events';
-import { APIError, Request, RequestPromise, Response } from '../model/api.ts';
-import { getToken } from '../database/tokens.ts';
import { PriorityQueue } from './priority_queue.ts';
+import { getToken } from '../database/tokens.ts';
+import { APIError, Request, RequestPromise, Response } from '../model/api.ts';
+import { RateLimitError } from '../model/errors.ts';
// queue processor module variables
const bus = new events.EventEmitter(); // a bus to notify the queue processor to start processing messages
@@ -14,7 +15,7 @@ let running = false;
let headers: {[key:string]:string}|null = null; // a file scoped variable so that we only evaluate these once.
let queue = new PriorityQueue(); // a priority queue to hold api calls we want to send, allows for throttling.
-async function queue_processor() {
+async function queue_processor(): Promise<void> {
if (running) {
throw 'refusing to start a second queue processor';
}
@@ -45,19 +46,29 @@ async function queue_processor() {
}
queue_processor();
-export async function send<T>(request: Request): Promise<T|APIError> {
- const response = await send_one<T>(request);
- if (response.error) return response.error;
- return response.data;
+export async function send<T>(request: Request): Promise<Response<T>> {
+ return new Promise((resolve, reject) => {
+ const data: RequestPromise<T> = {
+ reject: reject,
+ request: request,
+ resolve: resolve,
+ };
+ queue.enqueue(data, request.priority ? request.priority : 10);
+ if (!busy) {
+ bus.emit('send'); // the queue was previously empty, let's wake up the queue_processor
+ }
+ });
}
-export async function sendPaginated<T>(request: Request): Promise<Array<T>|APIError> {
+export async function sendPaginated<T>(request: Request): Promise<Array<T>> {
if (request.page === undefined) request.page = 1;
let ret: Array<T> = [];
while (true) {
- const response = await send_one<T>(request);
+ const response = await send<T>(request);
if (response.meta === undefined) {
throw {"message": "paginated request did not return a meta block", "request": request, "response": response};
+ } else if (response.error) {
+ throw {"message": "paginated request returned an error", "request": request, "response": response};
}
ret = ret.concat(response.data);
if (response.meta.limit * response.meta.page >= response.meta.total) {
@@ -67,22 +78,8 @@ export async function sendPaginated<T>(request: Request): Promise<Array<T>|APIEr
}
}
-function send_one<T>(request: Request): Promise<Response<T>> {
- return new Promise((resolve, reject) => {
- const data: RequestPromise<T> = {
- reject: reject,
- request: request,
- resolve: resolve,
- };
- queue.enqueue(data, request.priority ? request.priority : 10);
- if (!busy) {
- bus.emit('send'); // the queue was previously empty, let's wake up the queue_processor
- }
- });
-}
-
// send_this take a data object as argument built in the send function above
-async function send_this(data: RequestPromise<unknown>) {
+async function send_this(data: RequestPromise<unknown>): Promise<void> {
if (headers === null) {
const token = getToken();
if (token === null) {
@@ -119,7 +116,8 @@ async function send_this(data: RequestPromise<unknown>) {
// spawnSync?
// break;
case 429: // 429 means rate limited, let's hold back as instructed
- backoffSeconds = json.error.data.retryAfter;
+ const errorData = json.error.data as RateLimitError;
+ backoffSeconds = errorData.retryAfter;
queue.enqueue(data, 1);
break;
case 503: // 503 means maintenance mode, let's hold back for 1 minute
@@ -150,7 +148,7 @@ async function send_this(data: RequestPromise<unknown>) {
}
}
-export function debugLog(ctx: any) {
+export function debugLog(ctx: any): void {
console.log(`--- ${Date()} -----------------------------------------------------------------------------`);
console.log(JSON.stringify(ctx, null, 2));
}
diff --git a/nodejs/lib/contracts.ts b/nodejs/lib/contracts.ts
index fb62813..0adb5f6 100644
--- a/nodejs/lib/contracts.ts
+++ b/nodejs/lib/contracts.ts
@@ -12,21 +12,17 @@ import * as libShips from '../lib/ships.ts';
export async function accept(contract: Contract): Promise<Contract> {
if (contract.accepted) return contract;
const response = await api.send<{agent: Agent, contract: Contract, type: ''}>({endpoint: `/my/contracts/${contract.id}/accept`, method: 'POST'});
- if ('apiError' in response) {
+ if (response.error) {
api.debugLog(response);
throw response;
}
- dbAgents.setAgent(response.agent);
- dbContracts.setContract(response.contract);
- return response.contract;
+ dbAgents.setAgent(response.data.agent);
+ dbContracts.setContract(response.data.contract);
+ return response.data.contract;
}
export async function contracts(): Promise<Array<Contract>> {
- const response = await api.sendPaginated<Contract>({endpoint: '/my/contracts', page: 1});
- if ('apiError' in response) {
- api.debugLog(response);
- throw response;
- }
+ const response = await api.sendPaginated<Contract>({endpoint: '/my/contracts'});
response.forEach(contract => dbContracts.setContract(contract));
return response;
}
@@ -44,8 +40,8 @@ export async function deliver(contract: Contract, ship: Ship): Promise<Contract>
tradeSymbol: tradeSymbol,
units: units,
}});
- if ('apiError' in response) {
- switch(response.code) {
+ if (response.error) {
+ switch(response.error.code) {
case 4509: // contract delivery terms have been met
return await fulfill(contract);
default: // yet unhandled error
@@ -53,22 +49,22 @@ export async function deliver(contract: Contract, ship: Ship): Promise<Contract>
throw response;
}
}
- dbContracts.setContract(response.contract);
- dbShips.setShipCargo(ship.symbol, response.cargo);
- if(response.contract.terms.deliver[0].unitsRequired >= response.contract.terms.deliver[0].unitsFulfilled) {
- return await fulfill(contract);
+ dbContracts.setContract(response.data.contract);
+ dbShips.setShipCargo(ship.symbol, response.data.cargo);
+ if(response.data.contract.terms.deliver[0].unitsRequired >= response.data.contract.terms.deliver[0].unitsFulfilled) {
+ return await fulfill(response.data.contract);
}
- return response.contract;
+ return response.data.contract;
}
export async function fulfill(contract: Contract): Promise<Contract> {
if (contract.fulfilled) return contract;
const response = await api.send<{agent: Agent, contract: Contract}>({ endpoint: `/my/contracts/${contract.id}/fulfill`, method: 'POST'});
- if ('apiError' in response) {
+ if (response.error) {
api.debugLog(response);
throw response;
}
- dbAgents.setAgent(response.agent);
- dbContracts.setContract(response.contract);
- return response.contract;
+ dbAgents.setAgent(response.data.agent);
+ dbContracts.setContract(response.data.contract);
+ return response.data.contract;
}
diff --git a/nodejs/lib/ships.ts b/nodejs/lib/ships.ts
index 2e04dd4..955824e 100644
--- a/nodejs/lib/ships.ts
+++ b/nodejs/lib/ships.ts
@@ -2,19 +2,20 @@ import { Response } from '../model/api.ts';
import { Agent } from '../model/agent.ts';
import { Cargo } from '../model/cargo.ts';
import { Cooldown, Fuel, Nav, Ship } from '../model/ship.ts';
-import * as api from './api.js';
+import * as api from './api.ts';
import * as dbAgents from '../database/agents.ts';
-import * as dbShips from '../database/ships.js';
-import * as dbSurveys from '../database/surveys.js';
-import * as systems from '../lib/systems.js';
+import * as dbShips from '../database/ships.ts';
+//import * as dbSurveys from '../database/surveys.ts';
+import * as systems from '../lib/systems.ts';
export async function dock(ship: Ship): Promise<Ship> {
if (ship.nav.status === 'DOCKED') return ship;
- const response = await api.send({endpoint: `/my/ships/${ship.symbol}/dock`, method: 'POST'}) as Response<{nav: Nav}>;
- if (response.error !== undefined) {
+ const response = await api.send<{nav: Nav}>({endpoint: `/my/ships/${ship.symbol}/dock`, method: 'POST'});
+ if (response.error) {
switch(response.error.code) {
case 4214: // ship is in transit
- await api.sleep(response.error.data.secondsToArrival * 1000);
+ const errorData = response.error.data as { secondsToArrival: number};
+ await api.sleep(errorData.secondsToArrival * 1000);
return await dock(ship);
default: // yet unhandled error
api.debugLog(response);
@@ -33,11 +34,12 @@ export async function extract(ship: Ship): Promise<Ship> {
//await navigate({symbol: ctx.symbol, waypoint: asteroidFields[0].symbol});
ship = await orbit(ship);
// TODO handle surveying?
- const response = await api.send({endpoint: `/my/ships/${ship.symbol}/extract`, method: 'POST'}) as Response<{cooldown: Cooldown, cargo: Cargo}>; // TODO extraction and events api response fields cf https://spacetraders.stoplight.io/docs/spacetraders/b3931d097608d-extract-resources
- if (response.error !== undefined) {
+ const response = await api.send<{cooldown: Cooldown, cargo: Cargo}>({endpoint: `/my/ships/${ship.symbol}/extract`, method: 'POST'}); // TODO extraction and events api response fields cf https://spacetraders.stoplight.io/docs/spacetraders/b3931d097608d-extract-resources
+ if (response.error) {
switch(response.error.code) {
case 4000: // ship is on cooldown
- await api.sleep(response.error.data.cooldown.remainingSeconds * 1000);
+ const errorData = response.error.data as {cooldown: Cooldown};
+ await api.sleep(errorData.cooldown.remainingSeconds * 1000);
return await extract(ship);
case 4228: // ship is full
return ship;
@@ -69,11 +71,12 @@ export async function navigate(ship: Ship, waypoint: string): Promise<Ship> {
if (ship.nav.waypointSymbol === waypoint) return ship;
ship = await orbit(ship);
// TODO if we do not have enough fuel, make a stop to refuel along the way or drift to the destination
- const response = await api.send({endpoint: `/my/ships/${ship.symbol}/navigate`, method: 'POST', payload: { waypointSymbol: waypoint }}) as Response<{fuel: Fuel, nav: Nav}>; // TODO events field
- if (response.error !== undefined) {
+ const response = await api.send<{fuel: Fuel, nav: Nav}>({endpoint: `/my/ships/${ship.symbol}/navigate`, method: 'POST', payload: { waypointSymbol: waypoint }}); // TODO events field
+ if (response.error) {
switch(response.error.code) {
case 4214: // ship is in transit
- await api.sleep(response.error.data.secondsToArrival * 1000);
+ const errorData = response.error.data as { secondsToArrival: number};
+ await api.sleep(errorData.secondsToArrival * 1000);
return await navigate(ship, waypoint);
default: // yet unhandled error
api.debugLog(response);
@@ -89,8 +92,8 @@ export async function navigate(ship: Ship, waypoint: string): Promise<Ship> {
response.data.nav.status = 'IN_ORBIT'; // we arrive in orbit
dbShips.setShipNav(ship.symbol, response.data.nav);
ship.nav = response.data.nav
- ship = await refuel(ship);
- return ship;
+ // TODO only refuel at the start of a journey, if we do not have enough OR if the destination does not sell fuel?
+ return await refuel(ship);
}
//export async function negotiate(ctx) {
@@ -100,11 +103,12 @@ export async function navigate(ship: Ship, waypoint: string): Promise<Ship> {
export async function orbit(ship: Ship): Promise<Ship> {
if (ship.nav.status === 'IN_ORBIT') return ship;
- const response = await api.send({endpoint: `/my/ships/${ship.symbol}/orbit`, method: 'POST'}) as Response<{nav: Nav}>;
- if (response.error !== undefined) {
+ const response = await api.send<{nav: Nav}>({endpoint: `/my/ships/${ship.symbol}/orbit`, method: 'POST'});
+ if (response.error) {
switch(response.error.code) {
case 4214: // ship is in transit
- await api.sleep(response.error.data.secondsToArrival * 1000);
+ const errorData = response.error.data as { secondsToArrival: number};
+ await api.sleep(errorData.secondsToArrival * 1000);
return await orbit(ship);
default: // yet unhandled error
throw response;
@@ -131,8 +135,8 @@ export async function refuel(ship: Ship): Promise<Ship> {
if (ship.fuel.current >= ship.fuel.capacity * 0.9) return ship;
// TODO check if our current waypoint has a marketplace (and sells fuel)?
ship = await dock(ship);
- const response = await api.send({endpoint: `/my/ships/${ship.symbol}/refuel`, method: 'POST'}) as Response<{agent: Agent, fuel: Fuel}>; // TODO transaction field
- if (response.error !== undefined) {
+ const response = await api.send<{agent: Agent, fuel: Fuel}>({endpoint: `/my/ships/${ship.symbol}/refuel`, method: 'POST'}); // TODO transaction field
+ if (response.error) {
api.debugLog(response);
throw response;
}
@@ -143,12 +147,12 @@ export async function refuel(ship: Ship): Promise<Ship> {
}
export async function sell(ship: Ship, tradeSymbol: string): Promise<Ship> {
- // TODO check if our current waypoint has a marketplace?
+ // TODO check if our current waypoint has a marketplace and buys tradeSymbol?
ship = await dock(ship);
let units = 0;
ship.cargo.inventory.forEach(i => {if (i.symbol === tradeSymbol) units = i.units; });
- const response = await api.send({endpoint: `/my/ships/${ship.symbol}/sell`, method: 'POST', payload: { symbol: tradeSymbol, units: units }}) as Response<{agent: Agent, cargo: Cargo}>; // TODO transaction field
- if (response.error !== undefined) {
+ const response = await api.send<{agent: Agent, cargo: Cargo}>({endpoint: `/my/ships/${ship.symbol}/sell`, method: 'POST', payload: { symbol: tradeSymbol, units: units }}); // TODO transaction field
+ if (response.error) {
api.debugLog(response);
throw response;
}
@@ -159,8 +163,8 @@ export async function sell(ship: Ship, tradeSymbol: string): Promise<Ship> {
}
export async function ships(): Promise<Array<Ship>> {
- const response = await api.send({endpoint: `/my/ships`, page: 1}) as Response<Array<Ship>>;
- if (response.error !== undefined) {
+ const response = await api.send<Array<Ship>>({endpoint: `/my/ships`, page: 1});
+ if (response.error) {
api.debugLog(response);
throw response;
}
@@ -169,8 +173,8 @@ export async function ships(): Promise<Array<Ship>> {
}
export async function ship(ship: Ship): Promise<Ship> {
- const response = await api.send({endpoint: `/my/ships/${ship.symbol}`}) as Response<Ship>;
- if (response.error !== undefined) {
+ const response = await api.send<Ship>({endpoint: `/my/ships/${ship.symbol}`});
+ if (response.error) {
api.debugLog(response);
throw response;
}
diff --git a/nodejs/lib/systems.ts b/nodejs/lib/systems.ts
index 94ebab7..dc938bb 100644
--- a/nodejs/lib/systems.ts
+++ b/nodejs/lib/systems.ts
@@ -1,86 +1,60 @@
-import * as api from './api.js';
-import * as dbMarkets from '../database/markets.js';
-import * as dbShips from '../database/ships.js';
-import * as dbSystems from '../database/systems.js';
-import * as utils from './utils.js';
+import * as api from './api.ts';
+import * as dbMarkets from '../database/markets.ts';
+import * as dbShips from '../database/ships.ts';
+import * as dbSystems from '../database/systems.ts';
+import { Market } from '../model/market.ts'
+import { System, Waypoint } from '../model/system.ts'
+import * as utils from './utils.ts';
-// Retrieves a marketplace's market data for waypointSymbol
-export async function market(waypointSymbol: string) {
+export async function market(waypointSymbol: string): Promise<Market> {
const data = dbMarkets.getMarketAtWaypoint(waypointSymbol);
- if (data === null) {
- if (dbShips.getShipsAt(waypointSymbol) === null) {
- return null;
- }
- const systemSymbol = utils.systemFromWaypoint(waypointSymbol);
- let d = await api.send({endpoint: `/systems/${systemSymbol}/waypoints/${waypointSymbol}/market`});
- delete d.data.transactions;
- dbMarkets.setMarket(d.data);
- return d;
- }
- return data;
+ if (data) { return data; }
+ const systemSymbol = utils.systemFromWaypoint(waypointSymbol);
+ let response = await api.send<Market>({endpoint: `/systems/${systemSymbol}/waypoints/${waypointSymbol}/market`});
+ if (response.error) {
+ api.debugLog(response);
+ throw response;
+ }
+ dbMarkets.setMarket(response.data);
+ return response.data;
}
-// Retrieves a shipyard's information for ctx.symbol
-export async function shipyard(ctx) {
- const systemSymbol = utils.systemFromWaypoint(ctx.symbol);
- return await api.send({endpoint: `/systems/${systemSymbol}/waypoints/${ctx.symbol}/shipyard`});
-}
+//export async function shipyard(waypoint: string): Promise<unknown> {
+// // TODO database caching
+// const systemSymbol = utils.systemFromWaypoint(waypoint);
+// return await api.send({endpoint: `/systems/${systemSymbol}/waypoints/${waypoint}/shipyard`});
+//}
-// Retrieves the system's information for ctx.symbol and caches it in the database
-export async function system(ctx) {
- let s = dbSystems.getSystem(ctx.symbol);
- if (s === null) {
- const response = await api.send({endpoint: `/systems/${ctx.symbol}`});
- if (response.error !== undefined) {
- switch(response.error.code) {
- case 404:
- throw `Error retrieving info for system ${ctx.symbol}: ${response.error.message}`;
- default: // yet unhandled error
- throw response;
- }
- }
- s = response.data;
- dbSystems.setSystem(s);
+export async function system(symbol: string): Promise<System> {
+ let data = dbSystems.getSystem(symbol);
+ if (data) { return data; }
+ const response = await api.send<System>({endpoint: `/systems/${symbol}`});
+ if (response.error) {
+ api.debugLog(response);
+ throw response;
}
- return s;
+ dbSystems.setSystem(response.data);
+ return response.data;
}
-// Retrieves a list of waypoints that have a specific ctx.trait like a SHIPYARD or a MARKETPLACE in the system ctx.symbol
-export async function trait(ctx) {
- const w = await waypoints(ctx);
- return w.filter(s => s.traits.some(t => t.symbol === ctx.trait));
+// Retrieves a list of waypoints that have a specific trait like a SHIPYARD or a MARKETPLACE
+export async function trait(system: string, trait: string): Promise<Array<Waypoint>> {
+ const ws = await waypoints(system);
+ return ws.filter(w => w.traits.some(t => t.symbol === trait));
}
-// Retrieves a list of waypoints that have a specific ctx.type like ASTEROID_FIELD in the system ctx.symbol
-export async function type(ctx, response) {
- const w = await waypoints(ctx);
- return w.filter(s => s.type === ctx.type);
+// Retrieves a list of waypoints that have a specific type like ASTEROID_FIELD
+export async function type(system: string, typeSymbol: string): Promise<Array<Waypoint>> {
+ const ws = await waypoints(system);
+ return ws.filter(s => s.type === typeSymbol);
}
-// Retrieves the system's information for ctx.symbol and caches it in the database
-export async function waypoints(ctx) {
- await system(ctx);
- let updated = dbSystems.getSystemUpdated(ctx.symbol);
+export async function waypoints(systemSymbol: string): Promise<Array<Waypoint>> {
+ const s = await system(systemSymbol);
+ const updated = dbSystems.getSystemUpdated(systemSymbol);
// TODO handle uncharted systems
- if (updated === null) {
- let waypoints = [];
- for (let page=1; true; ++page) {
- const response = await api.send({endpoint: `/systems/${ctx.symbol}/waypoints?limit=20&page=${page}`, priority: 98});
- if (response.error !== undefined) {
- switch(response.error.code) {
- case 404:
- throw `Error retrieving waypoints for system ${ctx.symbol}: ${response.error.message}`;
- default: // yet unhandled error
- throw response;
- }
- }
- waypoints = waypoints.concat(response.data);
- if (response.meta.total <= response.meta.limit * page) {
- break;
- }
- }
- dbSystems.setSystemWaypoints(ctx.symbol, waypoints);
- return waypoints;
- }
- return dbSystems.getSystem(ctx.symbol).waypoints;
+ if (updated) return s.waypoints;
+ const waypoints = await api.sendPaginated<Waypoint>({endpoint: `/systems/${systemSymbol}/waypoints`});
+ dbSystems.setSystemWaypoints(systemSymbol, waypoints);
+ return waypoints;
}
diff --git a/nodejs/main.ts b/nodejs/main.ts
index 51be650..ae10ffc 100755
--- a/nodejs/main.ts
+++ b/nodejs/main.ts
@@ -1,8 +1,8 @@
import * as autoContracting from './automation/contracting.ts';
-//import * as autoExploring from './automation/exploration.js';
-import * as autoInit from './automation/init.js';
-//import * as api from './lib/api.js';
-//import * as contracts from './lib/contracts.js';
+//import * as autoExploring from './automation/exploration.ts';
+import * as autoInit from './automation/init.ts';
+//import * as api from './lib/api.ts';
+//import * as contracts from './lib/contracts.ts';
await autoInit.init();
autoContracting.init();
diff --git a/nodejs/model/api.ts b/nodejs/model/api.ts
index 5092157..69c7ee1 100644
--- a/nodejs/model/api.ts
+++ b/nodejs/model/api.ts
@@ -1,8 +1,7 @@
export type APIError = {
- apiError: 'APIError';
error: string;
code: number;
- data: any; // TODO
+ data: unknown;
};
export type Meta = {
diff --git a/nodejs/model/common.ts b/nodejs/model/common.ts
new file mode 100644
index 0000000..1712ad8
--- /dev/null
+++ b/nodejs/model/common.ts
@@ -0,0 +1,5 @@
+export type CommonThing = {
+ description: string;
+ name: string;
+ symbol: string;
+};
diff --git a/nodejs/model/errors.ts b/nodejs/model/errors.ts
new file mode 100644
index 0000000..6002ae6
--- /dev/null
+++ b/nodejs/model/errors.ts
@@ -0,0 +1,8 @@
+export type RateLimitError = {
+ type: string;
+ retryAfter: number;
+ limitBurst: number;
+ limitPerSecond: number;
+ remaining: number;
+ reset: Date;
+};
diff --git a/nodejs/model/market.ts b/nodejs/model/market.ts
new file mode 100644
index 0000000..1ba70c1
--- /dev/null
+++ b/nodejs/model/market.ts
@@ -0,0 +1,19 @@
+import { CommonThing } from 'common.ts';
+
+export type TradeGood = CommonThing & {
+ activity: string;
+ purchasePrice: number;
+ sellPrice: number;
+ supply: string;
+ tradeVolume: number;
+ type: string;
+};
+
+export type Market = {
+ symbol: string;
+ exchange: Array<CommonThing>;
+ exports: Array<CommonThing>;
+ imports: Array<CommonThing>;
+ //transactions: Array<Transaction>;
+ tradeGoods: Array<TradeGood>;
+};
diff --git a/nodejs/model/ship.ts b/nodejs/model/ship.ts
index 9aaf601..bf40767 100644
--- a/nodejs/model/ship.ts
+++ b/nodejs/model/ship.ts
@@ -25,6 +25,12 @@ export type Nav = {
waypointSymbol: string;
};
+export type Registration = {
+ factionSymbol: string;
+ name: string;
+ role: string;
+};
+
export type Route = {
arrival: Date;
departureTime: Date;
@@ -51,6 +57,6 @@ export type Ship = {
// mounts
nav: Nav;
// reactor
- // registration
+ registration: Registration;
symbol: string;
};
diff --git a/nodejs/model/system.ts b/nodejs/model/system.ts
new file mode 100644
index 0000000..b90560f
--- /dev/null
+++ b/nodejs/model/system.ts
@@ -0,0 +1,30 @@
+import { CommonThing } from 'common.ts';
+
+export type Chart = {
+ waypointSymbol: string;
+ submittedBy: string;
+ submittedOn: Date;
+};
+
+export type System = {
+ symbol: string;
+ sectorSymbol: string;
+ type: string;
+ x: number;
+ y: number;
+ waypoints: Array<Waypoint>;
+};
+
+export type Waypoint = {
+ chart: Chart;
+ factions: Array<{symbol: string;}>;
+ isUnderConstruction: boolean;
+ modifiers: Array<CommonThing>;
+ orbitals: Array<{symbol: string;}>;
+ orbits: string;
+ symbol: string;
+ traits: Array<CommonThing>;
+ type: string;
+ x: number;
+ y: number;
+};
diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json
index 0764b8a..cc5da1d 100644
--- a/nodejs/package-lock.json
+++ b/nodejs/package-lock.json
@@ -5,10 +5,402 @@
"packages": {
"": {
"dependencies": {
- "better-sqlite3": "^9.3.0"
+ "@types/better-sqlite3": "^7.6.9",
+ "better-sqlite3": "^9.4.3"
},
+ "devDependencies": {
+ "esrun": "^3.2.26",
+ "typescript": "^5.4.3"
+ },
+ "engines": {
+ "node": ">=21.6.2"
+ }
+ },
+ "node_modules/@digitak/grubber": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/@digitak/grubber/-/grubber-3.1.4.tgz",
+ "integrity": "sha512-pqsnp2BUYlDoTXWG34HWgEJse/Eo1okRgNex8IG84wHrJp8h3SakeR5WhB4VxSA2+/D+frNYJ0ch3yXzsfNDoA==",
+ "dev": true
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
+ "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
+ "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
+ "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
+ "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
+ "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
+ "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
+ "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
+ "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=18.10.0"
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
+ "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
+ "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
+ "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
+ "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
+ "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
+ "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
+ "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
+ "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
+ "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
+ "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
+ "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
+ "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
+ "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
+ "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@types/better-sqlite3": {
+ "version": "7.6.9",
+ "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.9.tgz",
+ "integrity": "sha512-FvktcujPDj9XKMJQWFcl2vVl7OdRIqsSRX9b0acWwTmwLK9CF2eqo/FRcmMLNpugKoX/avA6pb7TorDLmpgTnQ==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.11.30",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
+ "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
}
},
"node_modules/base64-js": {
@@ -40,6 +432,18 @@
"prebuild-install": "^7.1.1"
}
},
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@@ -58,6 +462,18 @@
"readable-stream": "^3.4.0"
}
},
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -81,6 +497,30 @@
"ieee754": "^1.1.13"
}
},
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
"node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
@@ -124,6 +564,60 @@
"once": "^1.4.0"
}
},
+ "node_modules/esbuild": {
+ "version": "0.17.19",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
+ "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.17.19",
+ "@esbuild/android-arm64": "0.17.19",
+ "@esbuild/android-x64": "0.17.19",
+ "@esbuild/darwin-arm64": "0.17.19",
+ "@esbuild/darwin-x64": "0.17.19",
+ "@esbuild/freebsd-arm64": "0.17.19",
+ "@esbuild/freebsd-x64": "0.17.19",
+ "@esbuild/linux-arm": "0.17.19",
+ "@esbuild/linux-arm64": "0.17.19",
+ "@esbuild/linux-ia32": "0.17.19",
+ "@esbuild/linux-loong64": "0.17.19",
+ "@esbuild/linux-mips64el": "0.17.19",
+ "@esbuild/linux-ppc64": "0.17.19",
+ "@esbuild/linux-riscv64": "0.17.19",
+ "@esbuild/linux-s390x": "0.17.19",
+ "@esbuild/linux-x64": "0.17.19",
+ "@esbuild/netbsd-x64": "0.17.19",
+ "@esbuild/openbsd-x64": "0.17.19",
+ "@esbuild/sunos-x64": "0.17.19",
+ "@esbuild/win32-arm64": "0.17.19",
+ "@esbuild/win32-ia32": "0.17.19",
+ "@esbuild/win32-x64": "0.17.19"
+ }
+ },
+ "node_modules/esrun": {
+ "version": "3.2.26",
+ "resolved": "https://registry.npmjs.org/esrun/-/esrun-3.2.26.tgz",
+ "integrity": "sha512-gDjP87qj4RW0BryZXPY3/L161hPo9uG6luBTjLsuHG3cKnhSMrzB7eNzSzvDyBLg7OgugyvzSgB2ov7mZ/oa7Q==",
+ "dev": true,
+ "dependencies": {
+ "@digitak/grubber": "^3.1.4",
+ "chokidar": "^3.5.1",
+ "esbuild": "^0.17.4"
+ },
+ "bin": {
+ "esrun": "bin.js"
+ },
+ "engines": {
+ "node": ">=14.0"
+ }
+ },
"node_modules/expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
@@ -137,16 +631,54 @@
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
+ "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,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
},
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -176,6 +708,48 @@
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -227,6 +801,15 @@
"node": ">=10"
}
},
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -235,6 +818,18 @@
"wrappy": "1"
}
},
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/prebuild-install": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz",
@@ -296,6 +891,18 @@
"node": ">= 6"
}
},
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -414,6 +1021,18 @@
"node": ">=6"
}
},
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -425,6 +1044,24 @@
"node": "*"
}
},
+ "node_modules/typescript": {
+ "version": "5.4.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
+ "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
diff --git a/nodejs/package.json b/nodejs/package.json
index bd9d5a5..ac17019 100644
--- a/nodejs/package.json
+++ b/nodejs/package.json
@@ -1,9 +1,15 @@
{
+ "module": "nodenext",
"type": "module",
"engines": {
- "node": ">=18.10.0"
+ "node": ">=21.6.2"
+ },
+ "devDependencies": {
+ "esrun": "^3.2.26",
+ "typescript": "^5.4.3"
},
"dependencies": {
- "better-sqlite3": "^9.3.0"
+ "@types/better-sqlite3": "^7.6.9",
+ "better-sqlite3": "^9.4.3"
}
}