From a1d6b03ec98abbc073b5b73b631da6ea3eae4eb9 Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Wed, 27 Mar 2024 15:20:14 +0100 Subject: [node] finished the great typescript rewrite --- nodejs/automation/contracting.ts | 16 +- nodejs/automation/exploration.js | 11 - nodejs/automation/exploration.ts | 9 + nodejs/automation/init.js | 39 --- nodejs/automation/init.ts | 43 +++ nodejs/automation/mining.js | 33 -- nodejs/automation/mining.ts | 29 ++ nodejs/automation/selling.js | 76 ----- nodejs/automation/selling.ts | 72 +++++ nodejs/database/agents.ts | 8 +- nodejs/database/contracts.ts | 8 +- nodejs/database/db.ts | 21 +- nodejs/database/markets.js | 34 --- nodejs/database/markets.ts | 26 ++ nodejs/database/ships.ts | 16 +- nodejs/database/surveys.js | 20 -- nodejs/database/surveys.ts | 20 ++ nodejs/database/systems.js | 56 ---- nodejs/database/systems.ts | 51 ++++ nodejs/database/tokens.ts | 6 +- nodejs/lib/api.ts | 54 ++-- nodejs/lib/contracts.ts | 36 +-- nodejs/lib/ships.ts | 58 ++-- nodejs/lib/systems.ts | 118 +++---- nodejs/main.ts | 8 +- nodejs/model/api.ts | 3 +- nodejs/model/common.ts | 5 + nodejs/model/errors.ts | 8 + nodejs/model/market.ts | 19 ++ nodejs/model/ship.ts | 8 +- nodejs/model/system.ts | 30 ++ nodejs/package-lock.json | 641 ++++++++++++++++++++++++++++++++++++++- nodejs/package.json | 10 +- 33 files changed, 1126 insertions(+), 466 deletions(-) delete mode 100644 nodejs/automation/exploration.js create mode 100644 nodejs/automation/exploration.ts delete mode 100644 nodejs/automation/init.js create mode 100644 nodejs/automation/init.ts delete mode 100644 nodejs/automation/mining.js create mode 100644 nodejs/automation/mining.ts delete mode 100644 nodejs/automation/selling.js create mode 100644 nodejs/automation/selling.ts delete mode 100644 nodejs/database/markets.js create mode 100644 nodejs/database/markets.ts delete mode 100644 nodejs/database/surveys.js create mode 100644 nodejs/database/surveys.ts delete mode 100644 nodejs/database/systems.js create mode 100644 nodejs/database/systems.ts create mode 100644 nodejs/model/common.ts create mode 100644 nodejs/model/errors.ts create mode 100644 nodejs/model/market.ts create mode 100644 nodejs/model/system.ts 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) { // 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) { // 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) { 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 { + const response = await api.sendPaginated({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.js deleted file mode 100644 index 93e5a70..0000000 --- a/nodejs/automation/init.js +++ /dev/null @@ -1,39 +0,0 @@ -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'; - -const symbol = process.env.NODE_ENV === 'test' ? 'ADYXAX-0' : 'ADYXAX-TS'; - -// This function registers then inits the database -export async function init() { - const response = await fetch('https://api.spacetraders.io/v2/register', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - symbol: symbol, - faction: "COSMIC", - }), - }); - const json = await response.json(); - if (json.error !== undefined) { - switch(json.error?.code) { - case 4111: // 4111 means the agent symbol has already been claimed so no server reset happened - return; - default: - throw json; - } - } - db.reset(); - dbAgents.addAgent(json.data.agent); - dbContracts.setContract(json.data.contract); - dbShips.setShip(json.data.ship); - dbTokens.addToken(json.data.token); - // Temporary fix to fetch the data on the startup probe - ships.ships(); -} diff --git a/nodejs/automation/init.ts b/nodejs/automation/init.ts new file mode 100644 index 0000000..b3c7a40 --- /dev/null +++ b/nodejs/automation/init.ts @@ -0,0 +1,43 @@ +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-JS'; + +// This function registers then inits the database +export async function init(): Promise { + const response = await fetch('https://api.spacetraders.io/v2/register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + symbol: symbol, + faction: "COSMIC", + }), + }); + 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 + return; + default: + throw json; + } + } + db.reset(); + dbAgents.addAgent(json.data.agent); + dbContracts.setContract(json.data.contract); + dbShips.setShip(json.data.ship); + dbTokens.addToken(json.data.token); + // Temporary fix to fetch the data on the startup probe + ships.ships(); +} 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 { + // 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 { + 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.js deleted file mode 100644 index 3d444af..0000000 --- a/nodejs/automation/selling.js +++ /dev/null @@ -1,76 +0,0 @@ -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'; - -// example ctx { ship: {XXX}, keep: 'SILVER_ORE' } -export async function sell(ship, keep) { - outer: while(true) { - // first lets see what we want to sell - let cargo = utils.categorizeCargo(ship.cargo, keep); - // 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]; - }; - // are we done selling everything we can? - ship = dbShips.getShip(ship.symbol); - cargo = utils.categorizeCargo(ship.cargo, keep); - if (Object.keys(cargo.goods).length === 0) { - return; - } - // 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'}); - // sorted by distance from where we are - const markets = rawMarkets.map(function (m) { return { - data: m, - distance: (m.x - ship.nav.route.destination.x) ** 2 + (m.y - ship.nav.route.destination.y) ** 2, - }}); - markets.sort(function(a, b) { - if (a.distance < b.distance) { - return -1; - } else if (a.distance > b.distance) { - return 1; - } - return 0; - }); - // check from the closest one if they import what we need to sell - for (let i = 0; i < markets.length; i++) { - const waypointSymbol = markets[i].data.symbol; - const market = await libSystems.market(waypointSymbol); - // 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}); - continue outer; - } - } - // check from the closest one if they exchange what we need to sell - for (let i = 0; i < markets.length; i++) { - const waypointSymbol = markets[i].data.symbol; - 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}); - continue outer; - } - } - throw new Error(`Ship {ship.symbol} has found no importing or exchanging market for its cargo in the system`); - } -} - -function whatCanBeTradedAt(cargo, goods) { - if (goods === undefined) return []; - return goods.filter(g => cargo[g.symbol] !== undefined ); -} diff --git a/nodejs/automation/selling.ts b/nodejs/automation/selling.ts new file mode 100644 index 0000000..dbdacfe --- /dev/null +++ b/nodejs/automation/selling.ts @@ -0,0 +1,72 @@ +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: Ship, good: string): Promise { + outer: while(true) { + // first lets see what we want to sell + 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; + ship = await libShips.sell(ship, good); + }; + // are we done selling everything we can? + cargo = utils.categorizeCargo(ship.cargo, good); + if (Object.keys(cargo.goods).length === 0) { + 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(ship.nav.systemSymbol, 'MARKETPLACE'); + // sorted by distance from where we are + const markets = rawMarkets.map(function (m) { return { + data: m, + distance: (m.x - ship.nav.route.destination.x) ** 2 + (m.y - ship.nav.route.destination.y) ** 2, + }}); + markets.sort(function(a, b) { + if (a.distance < b.distance) { + return -1; + } else if (a.distance > b.distance) { + return 1; + } + return 0; + }); + // check from the closest one if they import what we need to sell + for (let i = 0; i < markets.length; i++) { + const waypointSymbol = markets[i].data.symbol; + const market = await libSystems.market(waypointSymbol); + // 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) { + ship = await libShips.navigate(ship, waypointSymbol); + continue outer; + } + } + // check from the closest one if they exchange what we need to sell + for (let i = 0; i < markets.length; i++) { + const waypointSymbol = markets[i].data.symbol; + const market = await libSystems.market(waypointSymbol); + // if we can sell there we need to go + if (whatCanBeTradedAt(cargo.goods, market.exchange).length > 0) { + ship = await libShips.navigate(ship, waypointSymbol); + continue outer; + } + } + throw new Error(`Ship {ship.symbol} has found no importing or exchanging market for its cargo in the system`); + } +} + +function whatCanBeTradedAt(cargo: CargoManifest, goods: Array): Array { + 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 { - const data = getContractsStatement.all() as Array<{data: string}>; + const data = getContractsStatement.all() as Array; 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 = []; +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 { + const data = getShipsAtStatement.all(symbol) as Array; 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.js deleted file mode 100644 index f989218..0000000 --- a/nodejs/database/systems.js +++ /dev/null @@ -1,56 +0,0 @@ -import db from './db.js'; - -const addSystemStatement = db.prepare(`INSERT INTO systems(data) VALUES (json(?));`); -const getSystemStatement = db.prepare(`SELECT data FROM systems WHERE data->>'symbol' = ?;`); -const getSystemUpdatedStatement = db.prepare(`SELECT updated FROM systems WHERE data->>'symbol' = ?;`); -const getSystemsCountStatement = db.prepare(`SELECT COUNT(data) as data FROM systems;`); -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 getSystem(symbol) { - const data = getSystemStatement.get(symbol); - if (data === undefined) { - return null; - } - return JSON.parse(data.data); -} - -export function getSystemsCount() { - const data = getSystemsCountStatement.get(); - if (data === undefined) { - return null; - } - return data.data; -} - -export function getSystemUpdated(symbol) { - const updated = getSystemUpdatedStatement.get(symbol); - if (updated === undefined) { - return null; - } - return updated.updated; -} - -export function setSystem(data) { - if (getSystem(data.symbol) === null) { - addSystem(data); - } else { - return setSystemStatement.run({ - data: JSON.stringify(data), - date: new Date().toISOString(), - symbol: data.symbol, - }).changes; - } -} - -export function setSystemWaypoints(symbol, waypoints) { - return setSystemWaypointsStatement.run({ - date: new Date().toISOString(), - symbol: symbol, - waypoints: JSON.stringify(waypoints), - }).changes; -} diff --git a/nodejs/database/systems.ts b/nodejs/database/systems.ts new file mode 100644 index 0000000..d6d6cf1 --- /dev/null +++ b/nodejs/database/systems.ts @@ -0,0 +1,51 @@ +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' = ?;`); +const getSystemUpdatedStatement = db.prepare(`SELECT updated FROM systems WHERE data->>'symbol' = ?;`); +const getSystemsCountStatement = db.prepare(`SELECT COUNT(data) as data FROM systems;`); +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: System): void { + addSystemStatement.run(JSON.stringify(data)).lastInsertRowid; +} + +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(): number { + const data = getSystemsCountStatement.get() as number|undefined; + if (!data) return 0; + return data; +} + +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: System): void { + if (getSystem(data.symbol) === null) { + addSystem(data); + } else { + setSystemStatement.run({ + data: JSON.stringify(data), + date: new Date().toISOString(), + symbol: data.symbol, + }); + } +} + +export function setSystemWaypoints(symbol: string, waypoints: Array): void { + setSystemWaypointsStatement.run({ + date: new Date().toISOString(), + symbol: symbol, + waypoints: JSON.stringify(waypoints), + }); +} 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 { if (running) { throw 'refusing to start a second queue processor'; } @@ -45,19 +46,29 @@ async function queue_processor() { } queue_processor(); -export async function send(request: Request): Promise { - const response = await send_one(request); - if (response.error) return response.error; - return response.data; +export async function send(request: Request): Promise> { + return new Promise((resolve, reject) => { + const data: RequestPromise = { + 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(request: Request): Promise|APIError> { +export async function sendPaginated(request: Request): Promise> { if (request.page === undefined) request.page = 1; let ret: Array = []; while (true) { - const response = await send_one(request); + const response = await send(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(request: Request): Promise|APIEr } } -function send_one(request: Request): Promise> { - return new Promise((resolve, reject) => { - const data: RequestPromise = { - 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) { +async function send_this(data: RequestPromise): Promise { if (headers === null) { const token = getToken(); if (token === null) { @@ -119,7 +116,8 @@ async function send_this(data: RequestPromise) { // 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) { } } -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 { 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> { - const response = await api.sendPaginated({endpoint: '/my/contracts', page: 1}); - if ('apiError' in response) { - api.debugLog(response); - throw response; - } + const response = await api.sendPaginated({endpoint: '/my/contracts'}); response.forEach(contract => dbContracts.setContract(contract)); return response; } @@ -44,8 +40,8 @@ export async function deliver(contract: Contract, ship: Ship): Promise 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 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 { 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 { 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 { //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 { 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 { 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 { export async function orbit(ship: Ship): Promise { 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 { 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 { } export async function sell(ship: Ship, tradeSymbol: string): Promise { - // 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 { } export async function ships(): Promise> { - const response = await api.send({endpoint: `/my/ships`, page: 1}) as Response>; - if (response.error !== undefined) { + const response = await api.send>({endpoint: `/my/ships`, page: 1}); + if (response.error) { api.debugLog(response); throw response; } @@ -169,8 +173,8 @@ export async function ships(): Promise> { } export async function ship(ship: Ship): Promise { - const response = await api.send({endpoint: `/my/ships/${ship.symbol}`}) as Response; - if (response.error !== undefined) { + const response = await api.send({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 { 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({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 { +// // 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 { + let data = dbSystems.getSystem(symbol); + if (data) { return data; } + const response = await api.send({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> { + 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> { + 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> { + 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({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; + exports: Array; + imports: Array; + //transactions: Array; + tradeGoods: Array; +}; 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; +}; + +export type Waypoint = { + chart: Chart; + factions: Array<{symbol: string;}>; + isUnderConstruction: boolean; + modifiers: Array; + orbitals: Array<{symbol: string;}>; + orbits: string; + symbol: string; + traits: Array; + 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" } } -- cgit v1.2.3