1
0
Fork 0

[node] finished the great typescript rewrite

This commit is contained in:
Julien Dessaux 2024-03-27 15:20:14 +01:00
parent 8107afbd90
commit a1d6b03ec9
Signed by: adyxax
GPG key ID: F92E51B86E07177E
30 changed files with 1019 additions and 359 deletions

View file

@ -34,7 +34,7 @@ async function runProcurement(contract: Contract, ships: Array<Ship>) {
// TODO check if contract is fulfilled! // TODO check if contract is fulfilled!
const wantedCargo = contract.terms.deliver[0].tradeSymbol; const wantedCargo = contract.terms.deliver[0].tradeSymbol;
const deliveryPoint = contract.terms.deliver[0].destinationSymbol; 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; const asteroidSymbol = asteroids[0].symbol;
ships.forEach(async function(ship) { ships.forEach(async function(ship) {
while (!contract.fulfilled) { while (!contract.fulfilled) {
@ -46,12 +46,8 @@ async function runProcurement(contract: Contract, ships: Array<Ship>) {
// Then it depends on where we are // Then it depends on where we are
switch (ship.nav.waypointSymbol) { switch (ship.nav.waypointSymbol) {
case asteroidSymbol: case asteroidSymbol:
await mining.mineUntilFullOf({ ship = await mining.mineUntilFullOf(wantedCargo, ship, asteroidSymbol);
asteroidSymbol: asteroidSymbol, ship = await libShips.navigate(ship, deliveryPoint);
good: wantedCargo,
symbol: ship.symbol
});
await libShips.navigate(ship, deliveryPoint);
break; break;
case deliveryPoint: case deliveryPoint:
if (goodCargo !== undefined) { // we could be here if a client restart happens right after selling before we navigate away 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); contract = await contracts.deliver(contract, ship);
if (contract.fulfilled) break; if (contract.fulfilled) break;
} }
await libShips.navigate(ship, asteroidSymbol); ship = await libShips.navigate(ship, asteroidSymbol);
break; break;
default: default:
// we were either selling or started contracting // we were either selling or started contracting
await selling.sell(ship, wantedCargo); ship = await selling.sell(ship, wantedCargo);
await libShips.navigate(ship, asteroidSymbol); ship = await libShips.navigate(ship, asteroidSymbol);
} }
} }
// TODO repurpose the ship // TODO repurpose the ship

View file

@ -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 {} }))();
}

View file

@ -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 {} }))();
}

View file

@ -1,15 +1,19 @@
import * as dbAgents from '../database/agents.js'; import * as dbAgents from '../database/agents.ts';
import * as db from '../database/db.js'; import * as db from '../database/db.ts';
import * as dbContracts from '../database/contracts.js'; import * as dbContracts from '../database/contracts.ts';
import * as dbShips from '../database/ships.js'; import * as dbShips from '../database/ships.ts';
import * as dbTokens from '../database/tokens.js'; import * as dbTokens from '../database/tokens.ts';
import * as api from '../lib/api.js'; import { Agent } from '../model/agent.ts';
import * as ships from '../lib/ships.js'; 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 // 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', { const response = await fetch('https://api.spacetraders.io/v2/register', {
method: 'POST', method: 'POST',
headers: { headers: {
@ -20,7 +24,7 @@ export async function init() {
faction: "COSMIC", 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) { if (json.error !== undefined) {
switch(json.error?.code) { switch(json.error?.code) {
case 4111: // 4111 means the agent symbol has already been claimed so no server reset happened case 4111: // 4111 means the agent symbol has already been claimed so no server reset happened

View file

@ -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

View file

@ -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

View file

@ -1,37 +1,34 @@
import * as dbMarkets from '../database/markets.js'; import * as dbMarkets from '../database/markets.ts';
import * as dbShips from '../database/ships.js'; import * as dbShips from '../database/ships.ts';
import * as api from '../lib/api.js'; import * as api from '../lib/api.ts';
import * as libShips from '../lib/ships.js'; import * as libShips from '../lib/ships.ts';
import * as libSystems from '../lib/systems.js'; import * as libSystems from '../lib/systems.ts';
import * as utils from '../lib/utils.js'; 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' } // 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) { outer: while(true) {
// first lets see what we want to sell // 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 // get the marketdata from our location
const market = await libSystems.market(ship.nav.waypointSymbol); const market = await libSystems.market(ship.nav.waypointSymbol);
// can we sell anything here? // can we sell anything here?
const goods = whatCanBeTradedAt(cargo.goods, market.imports.concat(market.exchange)); const goods = whatCanBeTradedAt(cargo.goods, market.imports.concat(market.exchange));
for (let i = 0; i < goods.length; i++) { for (let i = 0; i < goods.length; i++) {
const symbol = goods[i].symbol; const symbol = goods[i].symbol;
await libShips.sell({ ship = await libShips.sell(ship, good);
good: symbol,
symbol: ship.symbol,
units: cargo.goods[symbol],
});
delete cargo.goods[symbol];
}; };
// are we done selling everything we can? // are we done selling everything we can?
ship = dbShips.getShip(ship.symbol); cargo = utils.categorizeCargo(ship.cargo, good);
cargo = utils.categorizeCargo(ship.cargo, keep);
if (Object.keys(cargo.goods).length === 0) { if (Object.keys(cargo.goods).length === 0) {
return; return ship;
} }
// we need to move somewhere else to sell our remaining goods // we need to move somewhere else to sell our remaining goods
// first we look into markets in our system // 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 // sorted by distance from where we are
const markets = rawMarkets.map(function (m) { return { const markets = rawMarkets.map(function (m) { return {
data: m, 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 // 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 // and if we have data and can sell there we need to go too
if (market === null || whatCanBeTradedAt(cargo.goods, market.imports).length > 0) { 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; continue outer;
} }
} }
@ -62,7 +59,7 @@ export async function sell(ship, keep) {
const market = await libSystems.market(waypointSymbol); const market = await libSystems.market(waypointSymbol);
// if we can sell there we need to go // if we can sell there we need to go
if (whatCanBeTradedAt(cargo.goods, market.exchange).length > 0) { if (whatCanBeTradedAt(cargo.goods, market.exchange).length > 0) {
await libShips.navigate({symbol: ship.symbol, waypoint: waypointSymbol}); ship = await libShips.navigate(ship, waypointSymbol);
continue outer; continue outer;
} }
} }
@ -70,7 +67,6 @@ export async function sell(ship, keep) {
} }
} }
function whatCanBeTradedAt(cargo, goods) { function whatCanBeTradedAt(cargo: CargoManifest, goods: Array<CommonThing>): Array<CommonThing> {
if (goods === undefined) return [];
return goods.filter(g => cargo[g.symbol] !== undefined ); return goods.filter(g => cargo[g.symbol] !== undefined );
} }

View file

@ -1,20 +1,20 @@
import { Agent } from '../model/agent.ts'; 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 addAgentStatement = db.prepare(`INSERT INTO agents(data) VALUES (json(?));`);
const getAgentStatement = db.prepare(`SELECT data FROM agents;`); const getAgentStatement = db.prepare(`SELECT data FROM agents;`);
const setAgentStatement = db.prepare(`UPDATE agents SET data = json(?);`); 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)); addAgentStatement.run(JSON.stringify(agent));
} }
export function getAgent(): Agent|null { export function getAgent(): Agent|null {
const data = getAgentStatement.get() as {data: string}|undefined; const data = getAgentStatement.get() as DbData|undefined;
if (!data) return null; if (!data) return null;
return JSON.parse(data.data); return JSON.parse(data.data);
} }
export function setAgent(agent: Agent) { export function setAgent(agent: Agent): void {
setAgentStatement.run(JSON.stringify(agent)); setAgentStatement.run(JSON.stringify(agent));
} }

View file

@ -1,5 +1,5 @@
import { Contract } from '../model/contract.ts'; 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 addContractStatement = db.prepare(`INSERT INTO contracts(data) VALUES (json(?));`);
const getContractStatement = db.prepare(`SELECT data FROM contracts WHERE data->>'id' = ?;`); 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;`); const updateContractStatement = db.prepare(`UPDATE contracts SET data = json(:data) WHERE data->>'id' = :id;`);
export function getContract(id: string): Contract|null { 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; if (!data) return null;
return JSON.parse(data.data); return JSON.parse(data.data);
} }
export function getContracts(): Array<Contract> { 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)); return data.map(contractData => JSON.parse(contractData.data));
} }
export function setContract(data: Contract) { export function setContract(data: Contract): void {
if (getContract(data.id) === null) { if (getContract(data.id) === null) {
addContractStatement.run(JSON.stringify(data)); addContractStatement.run(JSON.stringify(data));
} else { } else {

View file

@ -1,22 +1,23 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path';
import Database from 'better-sqlite3'; import Database from 'better-sqlite3';
const allMigrations = [ let allMigrations: Array<string> = [];
'database/000_init.sql', fs.readdir('./database/', function(err, files) {
'database/001_systems.sql', if (err) throw err;
'database/002_ships.sql', allMigrations = files.filter(e => e.match(/\.sql$/)).map(e => path.join('./database', e));
'database/003_surveys.sql', });
'database/004_markets.sql',
];
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 === 'test' ? 'test.db' : 'spacetraders.db',
process.env.NODE_ENV === 'development' ? { verbose: console.log } : undefined process.env.NODE_ENV === 'development' ? { verbose: console.log } : undefined
); );
db.pragma('foreign_keys = ON'); db.pragma('foreign_keys = ON');
db.pragma('journal_mode = WAL'); db.pragma('journal_mode = WAL');
function init() { function init(): void {
db.transaction(function migrate() { db.transaction(function migrate() {
let version; let version;
try { 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 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 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}>; const triggers = db.prepare(`SELECT name FROM sqlite_master WHERE type = 'trigger';`).all() as Array<{name: string}>;

View file

@ -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;
}

View file

@ -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,
});
}
}

View file

@ -1,4 +1,4 @@
import db from './db.ts'; import { DbData, db } from './db.ts';
import { Cargo } from '../model/cargo.ts'; import { Cargo } from '../model/cargo.ts';
import { Fuel, Nav, Ship } from '../model/ship.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;`); const updateShipStatement = db.prepare(`UPDATE ships SET data = json(:data) WHERE data->>'symbol' = :symbol;`);
export function getShip(symbol: string): Ship|null { 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; if (!data) return null;
return JSON.parse(data.data); return JSON.parse(data.data);
} }
export function getShipsAt(symbol: string) { export function getShipsAt(symbol: string): Array<Ship> {
const data = getShipsAtStatement.all(symbol) as Array<{data: string}>; const data = getShipsAtStatement.all(symbol) as Array<DbData>;
return data.map(elt => JSON.parse(elt.data)); return data.map(elt => JSON.parse(elt.data));
} }
export function setShip(data: Ship) { export function setShip(data: Ship): void {
if (getShip(data.symbol) === null) { if (getShip(data.symbol) === null) {
addShipStatement.run(JSON.stringify(data)); addShipStatement.run(JSON.stringify(data));
} else { } 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({ setShipCargoStatement.run({
cargo: JSON.stringify(cargo), cargo: JSON.stringify(cargo),
symbol: symbol, symbol: symbol,
}); });
} }
export function setShipFuel(symbol: string, fuel: Fuel) { export function setShipFuel(symbol: string, fuel: Fuel): void {
setShipFuelStatement.run({ setShipFuelStatement.run({
fuel: JSON.stringify(fuel), fuel: JSON.stringify(fuel),
symbol: symbol, symbol: symbol,
}); });
} }
export function setShipNav(symbol: string, nav: Nav) { export function setShipNav(symbol: string, nav: Nav): void {
setShipNavStatement.run({ setShipNavStatement.run({
nav: JSON.stringify(nav), nav: JSON.stringify(nav),
symbol: symbol, symbol: symbol,

View file

@ -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));
}

View file

@ -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));
//}

View file

@ -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 addSystemStatement = db.prepare(`INSERT INTO systems(data) VALUES (json(?));`);
const getSystemStatement = db.prepare(`SELECT data FROM systems WHERE data->>'symbol' = ?;`); 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 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;`); 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) { export function addSystem(data: System): void {
return addSystemStatement.run(JSON.stringify(data)).lastInsertRowid; addSystemStatement.run(JSON.stringify(data)).lastInsertRowid;
} }
export function getSystem(symbol) { export function getSystem(symbol: string): System|null {
const data = getSystemStatement.get(symbol); const data = getSystemStatement.get(symbol) as DbData|undefined;
if (data === undefined) { if (!data) return null;
return null;
}
return JSON.parse(data.data); return JSON.parse(data.data);
} }
export function getSystemsCount() { export function getSystemsCount(): number {
const data = getSystemsCountStatement.get(); const data = getSystemsCountStatement.get() as number|undefined;
if (data === undefined) { if (!data) return 0;
return null; return data;
}
return data.data;
} }
export function getSystemUpdated(symbol) { export function getSystemUpdated(symbol: string): Date|null {
const updated = getSystemUpdatedStatement.get(symbol); const data = getSystemUpdatedStatement.get(symbol) as {updated: Date}|undefined;
if (updated === undefined) { if (!data) return null;
return null; return data.updated;
}
return updated.updated;
} }
export function setSystem(data) { export function setSystem(data: System): void {
if (getSystem(data.symbol) === null) { if (getSystem(data.symbol) === null) {
addSystem(data); addSystem(data);
} else { } else {
return setSystemStatement.run({ setSystemStatement.run({
data: JSON.stringify(data), data: JSON.stringify(data),
date: new Date().toISOString(), date: new Date().toISOString(),
symbol: data.symbol, symbol: data.symbol,
}).changes; });
} }
} }
export function setSystemWaypoints(symbol, waypoints) { export function setSystemWaypoints(symbol: string, waypoints: Array<Waypoint>): void {
return setSystemWaypointsStatement.run({ setSystemWaypointsStatement.run({
date: new Date().toISOString(), date: new Date().toISOString(),
symbol: symbol, symbol: symbol,
waypoints: JSON.stringify(waypoints), waypoints: JSON.stringify(waypoints),
}).changes; });
} }

View file

@ -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 addTokenStatement = db.prepare(`INSERT INTO tokens(data) VALUES (?);`);
const getTokenStatement = db.prepare(`SELECT data FROM tokens;`); const getTokenStatement = db.prepare(`SELECT data FROM tokens;`);
export function addToken(token: string) { export function addToken(token: string): void {
addTokenStatement.run(token); addTokenStatement.run(token);
} }
export function getToken(): string|null { 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; if (data === undefined) return null;
return data.data; return data.data;
} }

View file

@ -1,9 +1,10 @@
import * as fs from 'fs'; import fs from 'fs';
import * as events from 'events'; 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 { 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 // queue processor module variables
const bus = new events.EventEmitter(); // a bus to notify the queue processor to start processing messages 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 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. 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) { if (running) {
throw 'refusing to start a second queue processor'; throw 'refusing to start a second queue processor';
} }
@ -45,29 +46,7 @@ async function queue_processor() {
} }
queue_processor(); queue_processor();
export async function send<T>(request: Request): Promise<T|APIError> { export async function send<T>(request: Request): Promise<Response<T>> {
const response = await send_one<T>(request);
if (response.error) return response.error;
return response.data;
}
export async function sendPaginated<T>(request: Request): Promise<Array<T>|APIError> {
if (request.page === undefined) request.page = 1;
let ret: Array<T> = [];
while (true) {
const response = await send_one<T>(request);
if (response.meta === undefined) {
throw {"message": "paginated request did not return a meta block", "request": request, "response": response};
}
ret = ret.concat(response.data);
if (response.meta.limit * response.meta.page >= response.meta.total) {
return ret;
}
request.page++;
}
}
function send_one<T>(request: Request): Promise<Response<T>> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const data: RequestPromise<T> = { const data: RequestPromise<T> = {
reject: reject, reject: reject,
@ -81,8 +60,26 @@ function send_one<T>(request: Request): Promise<Response<T>> {
}); });
} }
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<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) {
return ret;
}
request.page++;
}
}
// send_this take a data object as argument built in the send function above // 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) { if (headers === null) {
const token = getToken(); const token = getToken();
if (token === null) { if (token === null) {
@ -119,7 +116,8 @@ async function send_this(data: RequestPromise<unknown>) {
// spawnSync? // spawnSync?
// break; // break;
case 429: // 429 means rate limited, let's hold back as instructed 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); queue.enqueue(data, 1);
break; break;
case 503: // 503 means maintenance mode, let's hold back for 1 minute 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(`--- ${Date()} -----------------------------------------------------------------------------`);
console.log(JSON.stringify(ctx, null, 2)); console.log(JSON.stringify(ctx, null, 2));
} }

View file

@ -12,21 +12,17 @@ import * as libShips from '../lib/ships.ts';
export async function accept(contract: Contract): Promise<Contract> { export async function accept(contract: Contract): Promise<Contract> {
if (contract.accepted) return contract; if (contract.accepted) return contract;
const response = await api.send<{agent: Agent, contract: Contract, type: ''}>({endpoint: `/my/contracts/${contract.id}/accept`, method: 'POST'}); 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); api.debugLog(response);
throw response; throw response;
} }
dbAgents.setAgent(response.agent); dbAgents.setAgent(response.data.agent);
dbContracts.setContract(response.contract); dbContracts.setContract(response.data.contract);
return response.contract; return response.data.contract;
} }
export async function contracts(): Promise<Array<Contract>> { export async function contracts(): Promise<Array<Contract>> {
const response = await api.sendPaginated<Contract>({endpoint: '/my/contracts', page: 1}); const response = await api.sendPaginated<Contract>({endpoint: '/my/contracts'});
if ('apiError' in response) {
api.debugLog(response);
throw response;
}
response.forEach(contract => dbContracts.setContract(contract)); response.forEach(contract => dbContracts.setContract(contract));
return response; return response;
} }
@ -44,8 +40,8 @@ export async function deliver(contract: Contract, ship: Ship): Promise<Contract>
tradeSymbol: tradeSymbol, tradeSymbol: tradeSymbol,
units: units, units: units,
}}); }});
if ('apiError' in response) { if (response.error) {
switch(response.code) { switch(response.error.code) {
case 4509: // contract delivery terms have been met case 4509: // contract delivery terms have been met
return await fulfill(contract); return await fulfill(contract);
default: // yet unhandled error default: // yet unhandled error
@ -53,22 +49,22 @@ export async function deliver(contract: Contract, ship: Ship): Promise<Contract>
throw response; throw response;
} }
} }
dbContracts.setContract(response.contract); dbContracts.setContract(response.data.contract);
dbShips.setShipCargo(ship.symbol, response.cargo); dbShips.setShipCargo(ship.symbol, response.data.cargo);
if(response.contract.terms.deliver[0].unitsRequired >= response.contract.terms.deliver[0].unitsFulfilled) { if(response.data.contract.terms.deliver[0].unitsRequired >= response.data.contract.terms.deliver[0].unitsFulfilled) {
return await fulfill(contract); return await fulfill(response.data.contract);
} }
return response.contract; return response.data.contract;
} }
export async function fulfill(contract: Contract): Promise<Contract> { export async function fulfill(contract: Contract): Promise<Contract> {
if (contract.fulfilled) return contract; if (contract.fulfilled) return contract;
const response = await api.send<{agent: Agent, contract: Contract}>({ endpoint: `/my/contracts/${contract.id}/fulfill`, method: 'POST'}); 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); api.debugLog(response);
throw response; throw response;
} }
dbAgents.setAgent(response.agent); dbAgents.setAgent(response.data.agent);
dbContracts.setContract(response.contract); dbContracts.setContract(response.data.contract);
return response.contract; return response.data.contract;
} }

View file

@ -2,19 +2,20 @@ import { Response } from '../model/api.ts';
import { Agent } from '../model/agent.ts'; import { Agent } from '../model/agent.ts';
import { Cargo } from '../model/cargo.ts'; import { Cargo } from '../model/cargo.ts';
import { Cooldown, Fuel, Nav, Ship } from '../model/ship.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 dbAgents from '../database/agents.ts';
import * as dbShips from '../database/ships.js'; import * as dbShips from '../database/ships.ts';
import * as dbSurveys from '../database/surveys.js'; //import * as dbSurveys from '../database/surveys.ts';
import * as systems from '../lib/systems.js'; import * as systems from '../lib/systems.ts';
export async function dock(ship: Ship): Promise<Ship> { export async function dock(ship: Ship): Promise<Ship> {
if (ship.nav.status === 'DOCKED') return 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}>; const response = await api.send<{nav: Nav}>({endpoint: `/my/ships/${ship.symbol}/dock`, method: 'POST'});
if (response.error !== undefined) { if (response.error) {
switch(response.error.code) { switch(response.error.code) {
case 4214: // ship is in transit 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); return await dock(ship);
default: // yet unhandled error default: // yet unhandled error
api.debugLog(response); api.debugLog(response);
@ -33,11 +34,12 @@ export async function extract(ship: Ship): Promise<Ship> {
//await navigate({symbol: ctx.symbol, waypoint: asteroidFields[0].symbol}); //await navigate({symbol: ctx.symbol, waypoint: asteroidFields[0].symbol});
ship = await orbit(ship); ship = await orbit(ship);
// TODO handle surveying? // 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 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 !== undefined) { if (response.error) {
switch(response.error.code) { switch(response.error.code) {
case 4000: // ship is on cooldown 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); return await extract(ship);
case 4228: // ship is full case 4228: // ship is full
return ship; return ship;
@ -69,11 +71,12 @@ export async function navigate(ship: Ship, waypoint: string): Promise<Ship> {
if (ship.nav.waypointSymbol === waypoint) return ship; if (ship.nav.waypointSymbol === waypoint) return ship;
ship = await orbit(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 // 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 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 !== undefined) { if (response.error) {
switch(response.error.code) { switch(response.error.code) {
case 4214: // ship is in transit 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); return await navigate(ship, waypoint);
default: // yet unhandled error default: // yet unhandled error
api.debugLog(response); 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 response.data.nav.status = 'IN_ORBIT'; // we arrive in orbit
dbShips.setShipNav(ship.symbol, response.data.nav); dbShips.setShipNav(ship.symbol, response.data.nav);
ship.nav = response.data.nav ship.nav = response.data.nav
ship = await refuel(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 ship; return await refuel(ship);
} }
//export async function negotiate(ctx) { //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> { export async function orbit(ship: Ship): Promise<Ship> {
if (ship.nav.status === 'IN_ORBIT') return 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}>; const response = await api.send<{nav: Nav}>({endpoint: `/my/ships/${ship.symbol}/orbit`, method: 'POST'});
if (response.error !== undefined) { if (response.error) {
switch(response.error.code) { switch(response.error.code) {
case 4214: // ship is in transit 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); return await orbit(ship);
default: // yet unhandled error default: // yet unhandled error
throw response; 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; if (ship.fuel.current >= ship.fuel.capacity * 0.9) return ship;
// TODO check if our current waypoint has a marketplace (and sells fuel)? // TODO check if our current waypoint has a marketplace (and sells fuel)?
ship = await dock(ship); 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 const response = await api.send<{agent: Agent, fuel: Fuel}>({endpoint: `/my/ships/${ship.symbol}/refuel`, method: 'POST'}); // TODO transaction field
if (response.error !== undefined) { if (response.error) {
api.debugLog(response); api.debugLog(response);
throw 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> { 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); ship = await dock(ship);
let units = 0; let units = 0;
ship.cargo.inventory.forEach(i => {if (i.symbol === tradeSymbol) units = i.units; }); 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 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 !== undefined) { if (response.error) {
api.debugLog(response); api.debugLog(response);
throw response; throw response;
} }
@ -159,8 +163,8 @@ export async function sell(ship: Ship, tradeSymbol: string): Promise<Ship> {
} }
export async function ships(): Promise<Array<Ship>> { export async function ships(): Promise<Array<Ship>> {
const response = await api.send({endpoint: `/my/ships`, page: 1}) as Response<Array<Ship>>; const response = await api.send<Array<Ship>>({endpoint: `/my/ships`, page: 1});
if (response.error !== undefined) { if (response.error) {
api.debugLog(response); api.debugLog(response);
throw response; throw response;
} }
@ -169,8 +173,8 @@ export async function ships(): Promise<Array<Ship>> {
} }
export async function ship(ship: Ship): Promise<Ship> { export async function ship(ship: Ship): Promise<Ship> {
const response = await api.send({endpoint: `/my/ships/${ship.symbol}`}) as Response<Ship>; const response = await api.send<Ship>({endpoint: `/my/ships/${ship.symbol}`});
if (response.error !== undefined) { if (response.error) {
api.debugLog(response); api.debugLog(response);
throw response; throw response;
} }

View file

@ -1,86 +1,60 @@
import * as api from './api.js'; import * as api from './api.ts';
import * as dbMarkets from '../database/markets.js'; import * as dbMarkets from '../database/markets.ts';
import * as dbShips from '../database/ships.js'; import * as dbShips from '../database/ships.ts';
import * as dbSystems from '../database/systems.js'; import * as dbSystems from '../database/systems.ts';
import * as utils from './utils.js'; 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): Promise<Market> {
export async function market(waypointSymbol: string) {
const data = dbMarkets.getMarketAtWaypoint(waypointSymbol); const data = dbMarkets.getMarketAtWaypoint(waypointSymbol);
if (data === null) { if (data) { return data; }
if (dbShips.getShipsAt(waypointSymbol) === null) { const systemSymbol = utils.systemFromWaypoint(waypointSymbol);
return null; let response = await api.send<Market>({endpoint: `/systems/${systemSymbol}/waypoints/${waypointSymbol}/market`});
} if (response.error) {
const systemSymbol = utils.systemFromWaypoint(waypointSymbol); api.debugLog(response);
let d = await api.send({endpoint: `/systems/${systemSymbol}/waypoints/${waypointSymbol}/market`}); throw response;
delete d.data.transactions;
dbMarkets.setMarket(d.data);
return d;
}
return 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`});
}
// 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);
} }
return s; dbMarkets.setMarket(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 shipyard(waypoint: string): Promise<unknown> {
export async function trait(ctx) { // // TODO database caching
const w = await waypoints(ctx); // const systemSymbol = utils.systemFromWaypoint(waypoint);
return w.filter(s => s.traits.some(t => t.symbol === ctx.trait)); // return await api.send({endpoint: `/systems/${systemSymbol}/waypoints/${waypoint}/shipyard`});
//}
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;
}
dbSystems.setSystem(response.data);
return response.data;
} }
// Retrieves a list of waypoints that have a specific ctx.type like ASTEROID_FIELD in the system ctx.symbol // Retrieves a list of waypoints that have a specific trait like a SHIPYARD or a MARKETPLACE
export async function type(ctx, response) { export async function trait(system: string, trait: string): Promise<Array<Waypoint>> {
const w = await waypoints(ctx); const ws = await waypoints(system);
return w.filter(s => s.type === ctx.type); return ws.filter(w => w.traits.some(t => t.symbol === trait));
} }
// Retrieves the system's information for ctx.symbol and caches it in the database // Retrieves a list of waypoints that have a specific type like ASTEROID_FIELD
export async function waypoints(ctx) { export async function type(system: string, typeSymbol: string): Promise<Array<Waypoint>> {
await system(ctx); const ws = await waypoints(system);
let updated = dbSystems.getSystemUpdated(ctx.symbol); return ws.filter(s => s.type === typeSymbol);
}
export async function waypoints(systemSymbol: string): Promise<Array<Waypoint>> {
const s = await system(systemSymbol);
const updated = dbSystems.getSystemUpdated(systemSymbol);
// TODO handle uncharted systems // TODO handle uncharted systems
if (updated === null) { if (updated) return s.waypoints;
let waypoints = []; const waypoints = await api.sendPaginated<Waypoint>({endpoint: `/systems/${systemSymbol}/waypoints`});
for (let page=1; true; ++page) { dbSystems.setSystemWaypoints(systemSymbol, waypoints);
const response = await api.send({endpoint: `/systems/${ctx.symbol}/waypoints?limit=20&page=${page}`, priority: 98}); return waypoints;
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;
} }

View file

@ -1,8 +1,8 @@
import * as autoContracting from './automation/contracting.ts'; import * as autoContracting from './automation/contracting.ts';
//import * as autoExploring from './automation/exploration.js'; //import * as autoExploring from './automation/exploration.ts';
import * as autoInit from './automation/init.js'; import * as autoInit from './automation/init.ts';
//import * as api from './lib/api.js'; //import * as api from './lib/api.ts';
//import * as contracts from './lib/contracts.js'; //import * as contracts from './lib/contracts.ts';
await autoInit.init(); await autoInit.init();
autoContracting.init(); autoContracting.init();

View file

@ -1,8 +1,7 @@
export type APIError = { export type APIError = {
apiError: 'APIError';
error: string; error: string;
code: number; code: number;
data: any; // TODO data: unknown;
}; };
export type Meta = { export type Meta = {

5
nodejs/model/common.ts Normal file
View file

@ -0,0 +1,5 @@
export type CommonThing = {
description: string;
name: string;
symbol: string;
};

8
nodejs/model/errors.ts Normal file
View file

@ -0,0 +1,8 @@
export type RateLimitError = {
type: string;
retryAfter: number;
limitBurst: number;
limitPerSecond: number;
remaining: number;
reset: Date;
};

19
nodejs/model/market.ts Normal file
View file

@ -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>;
};

View file

@ -25,6 +25,12 @@ export type Nav = {
waypointSymbol: string; waypointSymbol: string;
}; };
export type Registration = {
factionSymbol: string;
name: string;
role: string;
};
export type Route = { export type Route = {
arrival: Date; arrival: Date;
departureTime: Date; departureTime: Date;
@ -51,6 +57,6 @@ export type Ship = {
// mounts // mounts
nav: Nav; nav: Nav;
// reactor // reactor
// registration registration: Registration;
symbol: string; symbol: string;
}; };

30
nodejs/model/system.ts Normal file
View file

@ -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;
};

641
nodejs/package-lock.json generated
View file

@ -5,10 +5,402 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "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": { "engines": {
"node": ">=18.10.0" "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": ">=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": { "node_modules/base64-js": {
@ -40,6 +432,18 @@
"prebuild-install": "^7.1.1" "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": { "node_modules/bindings": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@ -58,6 +462,18 @@
"readable-stream": "^3.4.0" "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": { "node_modules/buffer": {
"version": "5.7.1", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@ -81,6 +497,30 @@
"ieee754": "^1.1.13" "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": { "node_modules/chownr": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
@ -124,6 +564,60 @@
"once": "^1.4.0" "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": { "node_modules/expand-template": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "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", "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==" "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": { "node_modules/fs-constants": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" "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": { "node_modules/github-from-package": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" "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": { "node_modules/ieee754": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "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", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" "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": { "node_modules/lru-cache": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@ -227,6 +801,15 @@
"node": ">=10" "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": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -235,6 +818,18 @@
"wrappy": "1" "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": { "node_modules/prebuild-install": {
"version": "7.1.2", "version": "7.1.2",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz",
@ -296,6 +891,18 @@
"node": ">= 6" "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": { "node_modules/safe-buffer": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@ -414,6 +1021,18 @@
"node": ">=6" "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": { "node_modules/tunnel-agent": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@ -425,6 +1044,24 @@
"node": "*" "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": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View file

@ -1,9 +1,15 @@
{ {
"module": "nodenext",
"type": "module", "type": "module",
"engines": { "engines": {
"node": ">=18.10.0" "node": ">=21.6.2"
},
"devDependencies": {
"esrun": "^3.2.26",
"typescript": "^5.4.3"
}, },
"dependencies": { "dependencies": {
"better-sqlite3": "^9.3.0" "@types/better-sqlite3": "^7.6.9",
"better-sqlite3": "^9.4.3"
} }
} }