New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

@neaps/api

Package Overview
Dependencies
Maintainers
2
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@neaps/api - npm Package Compare versions

Comparing version
0.1.0
to
0.2.0
+6
-6
dist/index.d.mts

@@ -1027,3 +1027,3 @@ /// <reference types="node" />

readonly paths: {
readonly "/extremes": {
readonly "/tides/extremes": {
readonly get: {

@@ -1069,3 +1069,3 @@ readonly summary: "Get extremes prediction for a location";

};
readonly "/timeline": {
readonly "/tides/timeline": {
readonly get: {

@@ -1111,3 +1111,3 @@ readonly summary: "Get timeline prediction for a location";

};
readonly "/stations": {
readonly "/tides/stations": {
readonly get: {

@@ -1197,3 +1197,3 @@ readonly summary: "Find stations";

};
readonly "/stations/{id}/extremes": {
readonly "/tides/stations/{id}/extremes": {
readonly get: {

@@ -1246,3 +1246,3 @@ readonly summary: "Get extremes prediction for a specific station";

};
readonly "/stations/{id}/timeline": {
readonly "/tides/stations/{id}/timeline": {
readonly get: {

@@ -1295,3 +1295,3 @@ readonly summary: "Get timeline prediction for a specific station";

};
readonly "/openapi.json": {
readonly "/tides/openapi.json": {
readonly get: {

@@ -1298,0 +1298,0 @@ readonly summary: "Get OpenAPI specification";

@@ -6,3 +6,3 @@ import express, { Router, json } from "express";

//#region package.json
var version = "0.1.0";
var version = "0.2.0";

@@ -20,3 +20,3 @@ //#endregion

paths: {
"/extremes": { get: {
"/tides/extremes": { get: {
summary: "Get extremes prediction for a location",

@@ -43,3 +43,3 @@ description: "Returns high and low tide predictions for the nearest station to the given coordinates",

} },
"/timeline": { get: {
"/tides/timeline": { get: {
summary: "Get timeline prediction for a location",

@@ -66,3 +66,3 @@ description: "Returns water level predictions at regular intervals for the nearest station",

} },
"/stations": { get: {
"/tides/stations": { get: {
summary: "Find stations",

@@ -131,3 +131,3 @@ description: "Find stations by ID or near a location",

} },
"/stations/{id}/extremes": { get: {
"/tides/stations/{id}/extremes": { get: {
summary: "Get extremes prediction for a specific station",

@@ -156,3 +156,3 @@ parameters: [

} },
"/stations/{id}/timeline": { get: {
"/tides/stations/{id}/timeline": { get: {
summary: "Get timeline prediction for a specific station",

@@ -181,3 +181,3 @@ parameters: [

} },
"/openapi.json": { get: {
"/tides/openapi.json": { get: {
summary: "Get OpenAPI specification",

@@ -408,6 +408,6 @@ responses: { "200": {

}));
router.get("/openapi.json", (req, res) => {
router.get("/tides/openapi.json", (req, res) => {
res.json(openapi_default);
});
router.get("/extremes", (req, res) => {
router.get("/tides/extremes", (req, res) => {
res.json(getExtremesPrediction({

@@ -418,3 +418,3 @@ ...positionOptions(req),

});
router.get("/timeline", (req, res) => {
router.get("/tides/timeline", (req, res) => {
try {

@@ -429,3 +429,3 @@ res.json(getTimelinePrediction({

});
router.get("/stations", (req, res) => {
router.get("/tides/stations", (req, res) => {
if (req.query.id) try {

@@ -445,3 +445,3 @@ return res.json(findStation(req.query.id));

});
router.get("/stations/:id/extremes", (req, res) => {
router.get("/tides/stations/:id/extremes", (req, res) => {
let station;

@@ -455,3 +455,3 @@ try {

});
router.get("/stations/:id/timeline", (req, res) => {
router.get("/tides/stations/:id/timeline", (req, res) => {
try {

@@ -458,0 +458,0 @@ const station = findStation(req.params.id);

@@ -1,1 +0,1 @@

{"version":3,"file":"index.mjs","names":["pkg.version","openapiValidator","openapi","routes"],"sources":["../package.json","../src/openapi.ts","../src/routes.ts","../src/index.ts"],"sourcesContent":["","import pkg from \"../package.json\" with { type: \"json\" };\n\nexport default {\n openapi: \"3.0.3\",\n info: {\n title: \"Neaps Tide Prediction API\",\n version: pkg.version,\n description: \"HTTP JSON API for tide predictions using harmonic constituents\",\n license: {\n name: \"MIT\",\n },\n },\n paths: {\n \"/extremes\": {\n get: {\n summary: \"Get extremes prediction for a location\",\n description:\n \"Returns high and low tide predictions for the nearest station to the given coordinates\",\n parameters: [\n { $ref: \"#/components/parameters/latitude\" },\n { $ref: \"#/components/parameters/longitude\" },\n { $ref: \"#/components/parameters/start\" },\n { $ref: \"#/components/parameters/end\" },\n { $ref: \"#/components/parameters/datum\" },\n { $ref: \"#/components/parameters/units\" },\n ],\n responses: {\n \"200\": {\n description: \"Successful prediction\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/ExtremesResponse\",\n },\n },\n },\n },\n \"400\": {\n description: \"Invalid parameters\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n },\n },\n },\n \"/timeline\": {\n get: {\n summary: \"Get timeline prediction for a location\",\n description: \"Returns water level predictions at regular intervals for the nearest station\",\n parameters: [\n { $ref: \"#/components/parameters/latitude\" },\n { $ref: \"#/components/parameters/longitude\" },\n { $ref: \"#/components/parameters/start\" },\n { $ref: \"#/components/parameters/end\" },\n { $ref: \"#/components/parameters/datum\" },\n { $ref: \"#/components/parameters/units\" },\n ],\n responses: {\n \"200\": {\n description: \"Successful prediction\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/TimelineResponse\",\n },\n },\n },\n },\n \"400\": {\n description: \"Invalid parameters\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n },\n },\n },\n \"/stations\": {\n get: {\n summary: \"Find stations\",\n description: \"Find stations by ID or near a location\",\n parameters: [\n {\n name: \"id\",\n in: \"query\",\n description: \"Station ID or source ID\",\n required: false,\n schema: {\n type: \"string\",\n },\n },\n {\n name: \"latitude\",\n in: \"query\",\n description: \"Latitude for proximity search\",\n required: false,\n schema: {\n type: \"number\",\n minimum: -90,\n maximum: 90,\n },\n },\n {\n name: \"longitude\",\n in: \"query\",\n description: \"Longitude for proximity search\",\n required: false,\n schema: {\n type: \"number\",\n minimum: -180,\n maximum: 180,\n },\n },\n {\n name: \"limit\",\n in: \"query\",\n description: \"Maximum number of stations to return (for proximity search)\",\n required: false,\n schema: {\n type: \"integer\",\n minimum: 1,\n maximum: 100,\n default: 10,\n },\n },\n ],\n responses: {\n \"200\": {\n description: \"Stations found\",\n content: {\n \"application/json\": {\n schema: {\n oneOf: [\n {\n $ref: \"#/components/schemas/Station\",\n },\n {\n type: \"array\",\n items: {\n $ref: \"#/components/schemas/Station\",\n },\n },\n ],\n },\n },\n },\n },\n \"400\": {\n description: \"Invalid parameters\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n \"404\": {\n description: \"Station not found\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n },\n },\n },\n \"/stations/{id}/extremes\": {\n get: {\n summary: \"Get extremes prediction for a specific station\",\n parameters: [\n { $ref: \"#/components/parameters/stationId\" },\n { $ref: \"#/components/parameters/start\" },\n { $ref: \"#/components/parameters/end\" },\n { $ref: \"#/components/parameters/datum\" },\n { $ref: \"#/components/parameters/units\" },\n ],\n responses: {\n \"200\": {\n description: \"Successful prediction\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/ExtremesResponse\",\n },\n },\n },\n },\n \"400\": {\n description: \"Invalid parameters\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n \"404\": {\n description: \"Station not found\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n },\n },\n },\n \"/stations/{id}/timeline\": {\n get: {\n summary: \"Get timeline prediction for a specific station\",\n parameters: [\n { $ref: \"#/components/parameters/stationId\" },\n { $ref: \"#/components/parameters/start\" },\n { $ref: \"#/components/parameters/end\" },\n { $ref: \"#/components/parameters/datum\" },\n { $ref: \"#/components/parameters/units\" },\n ],\n responses: {\n \"200\": {\n description: \"Successful prediction\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/TimelineResponse\",\n },\n },\n },\n },\n \"400\": {\n description: \"Invalid parameters\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n \"404\": {\n description: \"Station not found\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n },\n },\n },\n \"/openapi.json\": {\n get: {\n summary: \"Get OpenAPI specification\",\n responses: {\n \"200\": {\n description: \"OpenAPI specification\",\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n },\n },\n },\n },\n },\n },\n },\n },\n components: {\n parameters: {\n latitude: {\n name: \"latitude\",\n in: \"query\",\n description: \"Latitude\",\n required: true,\n schema: {\n type: \"number\",\n minimum: -90,\n maximum: 90,\n },\n },\n longitude: {\n name: \"longitude\",\n in: \"query\",\n description: \"Longitude\",\n required: true,\n schema: {\n type: \"number\",\n minimum: -180,\n maximum: 180,\n },\n },\n start: {\n name: \"start\",\n in: \"query\",\n required: false,\n description: \"Start date/time (ISO 8601 format, defaults to now)\",\n schema: {\n type: \"string\",\n format: \"date-time\",\n },\n },\n end: {\n name: \"end\",\n in: \"query\",\n required: false,\n description: \"End date/time (ISO 8601 format, defaults to 7 days from start)\",\n schema: {\n type: \"string\",\n format: \"date-time\",\n },\n },\n datum: {\n name: \"datum\",\n in: \"query\",\n required: false,\n description: \"Vertical datum (defaults to MLLW if available)\",\n schema: {\n type: \"string\",\n enum: [\"MLLW\", \"MLW\", \"MTL\", \"MSL\", \"MHW\", \"MHHW\"],\n },\n },\n units: {\n name: \"units\",\n in: \"query\",\n required: false,\n description: \"Units for water levels (defaults to meters)\",\n schema: {\n type: \"string\",\n enum: [\"meters\", \"feet\"],\n default: \"meters\",\n },\n },\n stationId: {\n name: \"id\",\n in: \"path\",\n required: true,\n description: \"Station ID or source ID\",\n schema: {\n type: \"string\",\n },\n },\n },\n schemas: {\n Station: {\n type: \"object\",\n properties: {\n id: {\n type: \"string\",\n },\n name: {\n type: \"string\",\n },\n latitude: {\n type: \"number\",\n },\n longitude: {\n type: \"number\",\n },\n region: {\n type: \"string\",\n },\n country: {\n type: \"string\",\n },\n continent: {\n type: \"string\",\n },\n timezone: {\n type: \"string\",\n },\n type: {\n type: \"string\",\n enum: [\"reference\", \"subordinate\"],\n },\n source: {\n type: \"object\",\n additionalProperties: true,\n },\n license: {\n type: \"object\",\n additionalProperties: true,\n },\n disclaimers: {\n type: \"string\",\n },\n distance: {\n type: \"number\",\n description: \"Distance from query point in meters (only for proximity searches)\",\n },\n datums: {\n type: \"object\",\n additionalProperties: {\n type: \"number\",\n },\n },\n harmonic_constituents: {\n type: \"array\",\n items: {\n type: \"object\",\n additionalProperties: true,\n },\n },\n defaultDatum: {\n type: \"string\",\n },\n offsets: {\n type: \"object\",\n additionalProperties: true,\n },\n },\n additionalProperties: true,\n },\n Extreme: {\n type: \"object\",\n properties: {\n time: {\n type: \"string\",\n format: \"date-time\",\n },\n level: {\n type: \"number\",\n },\n high: {\n type: \"boolean\",\n },\n low: {\n type: \"boolean\",\n },\n label: {\n type: \"string\",\n },\n },\n required: [\"time\", \"level\", \"high\", \"low\", \"label\"],\n },\n ExtremesResponse: {\n type: \"object\",\n properties: {\n datum: {\n type: \"string\",\n },\n units: {\n type: \"string\",\n enum: [\"meters\", \"feet\"],\n },\n station: {\n $ref: \"#/components/schemas/Station\",\n },\n distance: {\n type: \"number\",\n },\n extremes: {\n type: \"array\",\n items: {\n $ref: \"#/components/schemas/Extreme\",\n },\n },\n },\n },\n TimelineEntry: {\n type: \"object\",\n properties: {\n time: {\n type: \"string\",\n format: \"date-time\",\n },\n level: {\n type: \"number\",\n },\n },\n required: [\"time\", \"level\"],\n },\n TimelineResponse: {\n type: \"object\",\n properties: {\n datum: {\n type: \"string\",\n },\n units: {\n type: \"string\",\n enum: [\"meters\", \"feet\"],\n },\n station: {\n $ref: \"#/components/schemas/Station\",\n },\n distance: {\n type: \"number\",\n },\n timeline: {\n type: \"array\",\n items: {\n $ref: \"#/components/schemas/TimelineEntry\",\n },\n },\n },\n },\n Error: {\n type: \"object\",\n properties: {\n message: {\n type: \"string\",\n },\n errors: {\n type: \"array\",\n items: {\n type: \"object\",\n additionalProperties: true,\n },\n },\n },\n required: [\"message\"],\n },\n },\n },\n} as const;\n","import { json, Router, Request, Response, type ErrorRequestHandler } from \"express\";\nimport { getExtremesPrediction, getTimelinePrediction, findStation, stationsNear } from \"neaps\";\nimport { middleware as openapiValidator } from \"express-openapi-validator\";\nimport openapi from \"./openapi.js\";\n\nconst router = Router();\n\nrouter.use(json());\n\nrouter.use(\n openapiValidator({\n apiSpec: openapi,\n validateRequests: {\n coerceTypes: true,\n },\n validateResponses: import.meta.env?.VITEST,\n }),\n);\n\nrouter.get(\"/openapi.json\", (req, res) => {\n res.json(openapi);\n});\n\nrouter.get(\"/extremes\", (req: Request, res: Response) => {\n res.json(\n getExtremesPrediction({\n ...positionOptions(req),\n ...predictionOptions(req),\n }),\n );\n});\n\nrouter.get(\"/timeline\", (req: Request, res: Response) => {\n try {\n res.json(\n getTimelinePrediction({\n ...positionOptions(req),\n ...predictionOptions(req),\n }),\n );\n } catch (error) {\n res.status(400).json({ message: (error as Error).message });\n }\n});\n\nrouter.get(\"/stations\", (req: Request, res: Response) => {\n if (req.query.id) {\n try {\n return res.json(findStation(req.query.id as string));\n } catch (error) {\n return res.status(404).json({ message: (error as Error).message });\n }\n }\n\n const { latitude, longitude } = positionOptions(req);\n\n if (latitude === undefined || longitude === undefined) {\n return res.status(400).json({\n message: \"Either 'id' or coordinates (latitude and longitude) required\",\n });\n }\n\n const limit = req.query.limit ? parseInt(req.query.limit as string, 10) : 10;\n\n const stations = stationsNear({ latitude, longitude }, limit);\n res.json(stations);\n});\n\nrouter.get(\"/stations/:id/extremes\", (req: Request, res: Response) => {\n let station: ReturnType<typeof findStation>;\n\n try {\n station = findStation(req.params.id);\n } catch (error) {\n return res.status(404).json({ message: (error as Error).message });\n }\n\n res.json(station.getExtremesPrediction(predictionOptions(req)));\n});\n\nrouter.get(\"/stations/:id/timeline\", (req: Request, res: Response) => {\n try {\n const station = findStation(req.params.id);\n res.json(station.getTimelinePrediction(predictionOptions(req)));\n } catch (error) {\n if ((error as Error).message.includes(\"not found\")) {\n return res.status(404).json({ message: (error as Error).message });\n }\n // Subordinate station errors and other application errors\n return res.status(400).json({ message: (error as Error).message });\n }\n});\n\nrouter.use(((err, _req, res, next) => {\n if (!err) return next();\n\n const status = err.status ?? 500;\n const message = err.message ?? \"Unknown error\";\n\n res.status(status).json({ message, errors: err.errors });\n}) satisfies ErrorRequestHandler);\n\nfunction positionOptions(req: Request) {\n return {\n latitude: req.query.latitude as unknown as number,\n longitude: req.query.longitude as unknown as number,\n };\n}\n\nfunction predictionOptions(req: Request) {\n return {\n start: req.query.start ? new Date(req.query.start as string) : new Date(),\n end: req.query.end\n ? new Date(req.query.end as string)\n : new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),\n ...(req.query.datum && { datum: req.query.datum as string }),\n ...(req.query.units && { units: req.query.units as \"meters\" | \"feet\" }),\n };\n}\n\nexport default router;\n","import express from \"express\";\nimport routes from \"./routes.js\";\nimport openapi from \"./openapi.js\";\n\nexport function createApp() {\n return express().use(\"/\", routes);\n}\n\nexport { routes, openapi };\n"],"mappings":";;;;;;;;;ACEA,sBAAe;CACb,SAAS;CACT,MAAM;EACJ,OAAO;EACEA;EACT,aAAa;EACb,SAAS,EACP,MAAM,OACP;EACF;CACD,OAAO;EACL,aAAa,EACX,KAAK;GACH,SAAS;GACT,aACE;GACF,YAAY;IACV,EAAE,MAAM,oCAAoC;IAC5C,EAAE,MAAM,qCAAqC;IAC7C,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,+BAA+B;IACvC,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,iCAAiC;IAC1C;GACD,WAAW;IACT,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,yCACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACF;GACF,EACF;EACD,aAAa,EACX,KAAK;GACH,SAAS;GACT,aAAa;GACb,YAAY;IACV,EAAE,MAAM,oCAAoC;IAC5C,EAAE,MAAM,qCAAqC;IAC7C,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,+BAA+B;IACvC,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,iCAAiC;IAC1C;GACD,WAAW;IACT,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,yCACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACF;GACF,EACF;EACD,aAAa,EACX,KAAK;GACH,SAAS;GACT,aAAa;GACb,YAAY;IACV;KACE,MAAM;KACN,IAAI;KACJ,aAAa;KACb,UAAU;KACV,QAAQ,EACN,MAAM,UACP;KACF;IACD;KACE,MAAM;KACN,IAAI;KACJ,aAAa;KACb,UAAU;KACV,QAAQ;MACN,MAAM;MACN,SAAS;MACT,SAAS;MACV;KACF;IACD;KACE,MAAM;KACN,IAAI;KACJ,aAAa;KACb,UAAU;KACV,QAAQ;MACN,MAAM;MACN,SAAS;MACT,SAAS;MACV;KACF;IACD;KACE,MAAM;KACN,IAAI;KACJ,aAAa;KACb,UAAU;KACV,QAAQ;MACN,MAAM;MACN,SAAS;MACT,SAAS;MACT,SAAS;MACV;KACF;IACF;GACD,WAAW;IACT,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,OAAO,CACL,EACE,MAAM,gCACP,EACD;MACE,MAAM;MACN,OAAO,EACL,MAAM,gCACP;MACF,CACF,EACF,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACF;GACF,EACF;EACD,2BAA2B,EACzB,KAAK;GACH,SAAS;GACT,YAAY;IACV,EAAE,MAAM,qCAAqC;IAC7C,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,+BAA+B;IACvC,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,iCAAiC;IAC1C;GACD,WAAW;IACT,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,yCACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACF;GACF,EACF;EACD,2BAA2B,EACzB,KAAK;GACH,SAAS;GACT,YAAY;IACV,EAAE,MAAM,qCAAqC;IAC7C,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,+BAA+B;IACvC,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,iCAAiC;IAC1C;GACD,WAAW;IACT,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,yCACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACF;GACF,EACF;EACD,iBAAiB,EACf,KAAK;GACH,SAAS;GACT,WAAW,EACT,OAAO;IACL,aAAa;IACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,UACP,EACF,EACF;IACF,EACF;GACF,EACF;EACF;CACD,YAAY;EACV,YAAY;GACV,UAAU;IACR,MAAM;IACN,IAAI;IACJ,aAAa;IACb,UAAU;IACV,QAAQ;KACN,MAAM;KACN,SAAS;KACT,SAAS;KACV;IACF;GACD,WAAW;IACT,MAAM;IACN,IAAI;IACJ,aAAa;IACb,UAAU;IACV,QAAQ;KACN,MAAM;KACN,SAAS;KACT,SAAS;KACV;IACF;GACD,OAAO;IACL,MAAM;IACN,IAAI;IACJ,UAAU;IACV,aAAa;IACb,QAAQ;KACN,MAAM;KACN,QAAQ;KACT;IACF;GACD,KAAK;IACH,MAAM;IACN,IAAI;IACJ,UAAU;IACV,aAAa;IACb,QAAQ;KACN,MAAM;KACN,QAAQ;KACT;IACF;GACD,OAAO;IACL,MAAM;IACN,IAAI;IACJ,UAAU;IACV,aAAa;IACb,QAAQ;KACN,MAAM;KACN,MAAM;MAAC;MAAQ;MAAO;MAAO;MAAO;MAAO;MAAO;KACnD;IACF;GACD,OAAO;IACL,MAAM;IACN,IAAI;IACJ,UAAU;IACV,aAAa;IACb,QAAQ;KACN,MAAM;KACN,MAAM,CAAC,UAAU,OAAO;KACxB,SAAS;KACV;IACF;GACD,WAAW;IACT,MAAM;IACN,IAAI;IACJ,UAAU;IACV,aAAa;IACb,QAAQ,EACN,MAAM,UACP;IACF;GACF;EACD,SAAS;GACP,SAAS;IACP,MAAM;IACN,YAAY;KACV,IAAI,EACF,MAAM,UACP;KACD,MAAM,EACJ,MAAM,UACP;KACD,UAAU,EACR,MAAM,UACP;KACD,WAAW,EACT,MAAM,UACP;KACD,QAAQ,EACN,MAAM,UACP;KACD,SAAS,EACP,MAAM,UACP;KACD,WAAW,EACT,MAAM,UACP;KACD,UAAU,EACR,MAAM,UACP;KACD,MAAM;MACJ,MAAM;MACN,MAAM,CAAC,aAAa,cAAc;MACnC;KACD,QAAQ;MACN,MAAM;MACN,sBAAsB;MACvB;KACD,SAAS;MACP,MAAM;MACN,sBAAsB;MACvB;KACD,aAAa,EACX,MAAM,UACP;KACD,UAAU;MACR,MAAM;MACN,aAAa;MACd;KACD,QAAQ;MACN,MAAM;MACN,sBAAsB,EACpB,MAAM,UACP;MACF;KACD,uBAAuB;MACrB,MAAM;MACN,OAAO;OACL,MAAM;OACN,sBAAsB;OACvB;MACF;KACD,cAAc,EACZ,MAAM,UACP;KACD,SAAS;MACP,MAAM;MACN,sBAAsB;MACvB;KACF;IACD,sBAAsB;IACvB;GACD,SAAS;IACP,MAAM;IACN,YAAY;KACV,MAAM;MACJ,MAAM;MACN,QAAQ;MACT;KACD,OAAO,EACL,MAAM,UACP;KACD,MAAM,EACJ,MAAM,WACP;KACD,KAAK,EACH,MAAM,WACP;KACD,OAAO,EACL,MAAM,UACP;KACF;IACD,UAAU;KAAC;KAAQ;KAAS;KAAQ;KAAO;KAAQ;IACpD;GACD,kBAAkB;IAChB,MAAM;IACN,YAAY;KACV,OAAO,EACL,MAAM,UACP;KACD,OAAO;MACL,MAAM;MACN,MAAM,CAAC,UAAU,OAAO;MACzB;KACD,SAAS,EACP,MAAM,gCACP;KACD,UAAU,EACR,MAAM,UACP;KACD,UAAU;MACR,MAAM;MACN,OAAO,EACL,MAAM,gCACP;MACF;KACF;IACF;GACD,eAAe;IACb,MAAM;IACN,YAAY;KACV,MAAM;MACJ,MAAM;MACN,QAAQ;MACT;KACD,OAAO,EACL,MAAM,UACP;KACF;IACD,UAAU,CAAC,QAAQ,QAAQ;IAC5B;GACD,kBAAkB;IAChB,MAAM;IACN,YAAY;KACV,OAAO,EACL,MAAM,UACP;KACD,OAAO;MACL,MAAM;MACN,MAAM,CAAC,UAAU,OAAO;MACzB;KACD,SAAS,EACP,MAAM,gCACP;KACD,UAAU,EACR,MAAM,UACP;KACD,UAAU;MACR,MAAM;MACN,OAAO,EACL,MAAM,sC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"}
{"version":3,"file":"index.mjs","names":["pkg.version","openapiValidator","openapi","routes"],"sources":["../package.json","../src/openapi.ts","../src/routes.ts","../src/index.ts"],"sourcesContent":["","import pkg from \"../package.json\" with { type: \"json\" };\n\nexport default {\n openapi: \"3.0.3\",\n info: {\n title: \"Neaps Tide Prediction API\",\n version: pkg.version,\n description: \"HTTP JSON API for tide predictions using harmonic constituents\",\n license: {\n name: \"MIT\",\n },\n },\n paths: {\n \"/tides/extremes\": {\n get: {\n summary: \"Get extremes prediction for a location\",\n description:\n \"Returns high and low tide predictions for the nearest station to the given coordinates\",\n parameters: [\n { $ref: \"#/components/parameters/latitude\" },\n { $ref: \"#/components/parameters/longitude\" },\n { $ref: \"#/components/parameters/start\" },\n { $ref: \"#/components/parameters/end\" },\n { $ref: \"#/components/parameters/datum\" },\n { $ref: \"#/components/parameters/units\" },\n ],\n responses: {\n \"200\": {\n description: \"Successful prediction\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/ExtremesResponse\",\n },\n },\n },\n },\n \"400\": {\n description: \"Invalid parameters\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n },\n },\n },\n \"/tides/timeline\": {\n get: {\n summary: \"Get timeline prediction for a location\",\n description: \"Returns water level predictions at regular intervals for the nearest station\",\n parameters: [\n { $ref: \"#/components/parameters/latitude\" },\n { $ref: \"#/components/parameters/longitude\" },\n { $ref: \"#/components/parameters/start\" },\n { $ref: \"#/components/parameters/end\" },\n { $ref: \"#/components/parameters/datum\" },\n { $ref: \"#/components/parameters/units\" },\n ],\n responses: {\n \"200\": {\n description: \"Successful prediction\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/TimelineResponse\",\n },\n },\n },\n },\n \"400\": {\n description: \"Invalid parameters\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n },\n },\n },\n \"/tides/stations\": {\n get: {\n summary: \"Find stations\",\n description: \"Find stations by ID or near a location\",\n parameters: [\n {\n name: \"id\",\n in: \"query\",\n description: \"Station ID or source ID\",\n required: false,\n schema: {\n type: \"string\",\n },\n },\n {\n name: \"latitude\",\n in: \"query\",\n description: \"Latitude for proximity search\",\n required: false,\n schema: {\n type: \"number\",\n minimum: -90,\n maximum: 90,\n },\n },\n {\n name: \"longitude\",\n in: \"query\",\n description: \"Longitude for proximity search\",\n required: false,\n schema: {\n type: \"number\",\n minimum: -180,\n maximum: 180,\n },\n },\n {\n name: \"limit\",\n in: \"query\",\n description: \"Maximum number of stations to return (for proximity search)\",\n required: false,\n schema: {\n type: \"integer\",\n minimum: 1,\n maximum: 100,\n default: 10,\n },\n },\n ],\n responses: {\n \"200\": {\n description: \"Stations found\",\n content: {\n \"application/json\": {\n schema: {\n oneOf: [\n {\n $ref: \"#/components/schemas/Station\",\n },\n {\n type: \"array\",\n items: {\n $ref: \"#/components/schemas/Station\",\n },\n },\n ],\n },\n },\n },\n },\n \"400\": {\n description: \"Invalid parameters\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n \"404\": {\n description: \"Station not found\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n },\n },\n },\n \"/tides/stations/{id}/extremes\": {\n get: {\n summary: \"Get extremes prediction for a specific station\",\n parameters: [\n { $ref: \"#/components/parameters/stationId\" },\n { $ref: \"#/components/parameters/start\" },\n { $ref: \"#/components/parameters/end\" },\n { $ref: \"#/components/parameters/datum\" },\n { $ref: \"#/components/parameters/units\" },\n ],\n responses: {\n \"200\": {\n description: \"Successful prediction\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/ExtremesResponse\",\n },\n },\n },\n },\n \"400\": {\n description: \"Invalid parameters\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n \"404\": {\n description: \"Station not found\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n },\n },\n },\n \"/tides/stations/{id}/timeline\": {\n get: {\n summary: \"Get timeline prediction for a specific station\",\n parameters: [\n { $ref: \"#/components/parameters/stationId\" },\n { $ref: \"#/components/parameters/start\" },\n { $ref: \"#/components/parameters/end\" },\n { $ref: \"#/components/parameters/datum\" },\n { $ref: \"#/components/parameters/units\" },\n ],\n responses: {\n \"200\": {\n description: \"Successful prediction\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/TimelineResponse\",\n },\n },\n },\n },\n \"400\": {\n description: \"Invalid parameters\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n \"404\": {\n description: \"Station not found\",\n content: {\n \"application/json\": {\n schema: {\n $ref: \"#/components/schemas/Error\",\n },\n },\n },\n },\n },\n },\n },\n \"/tides/openapi.json\": {\n get: {\n summary: \"Get OpenAPI specification\",\n responses: {\n \"200\": {\n description: \"OpenAPI specification\",\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n },\n },\n },\n },\n },\n },\n },\n },\n components: {\n parameters: {\n latitude: {\n name: \"latitude\",\n in: \"query\",\n description: \"Latitude\",\n required: true,\n schema: {\n type: \"number\",\n minimum: -90,\n maximum: 90,\n },\n },\n longitude: {\n name: \"longitude\",\n in: \"query\",\n description: \"Longitude\",\n required: true,\n schema: {\n type: \"number\",\n minimum: -180,\n maximum: 180,\n },\n },\n start: {\n name: \"start\",\n in: \"query\",\n required: false,\n description: \"Start date/time (ISO 8601 format, defaults to now)\",\n schema: {\n type: \"string\",\n format: \"date-time\",\n },\n },\n end: {\n name: \"end\",\n in: \"query\",\n required: false,\n description: \"End date/time (ISO 8601 format, defaults to 7 days from start)\",\n schema: {\n type: \"string\",\n format: \"date-time\",\n },\n },\n datum: {\n name: \"datum\",\n in: \"query\",\n required: false,\n description: \"Vertical datum (defaults to MLLW if available)\",\n schema: {\n type: \"string\",\n enum: [\"MLLW\", \"MLW\", \"MTL\", \"MSL\", \"MHW\", \"MHHW\"],\n },\n },\n units: {\n name: \"units\",\n in: \"query\",\n required: false,\n description: \"Units for water levels (defaults to meters)\",\n schema: {\n type: \"string\",\n enum: [\"meters\", \"feet\"],\n default: \"meters\",\n },\n },\n stationId: {\n name: \"id\",\n in: \"path\",\n required: true,\n description: \"Station ID or source ID\",\n schema: {\n type: \"string\",\n },\n },\n },\n schemas: {\n Station: {\n type: \"object\",\n properties: {\n id: {\n type: \"string\",\n },\n name: {\n type: \"string\",\n },\n latitude: {\n type: \"number\",\n },\n longitude: {\n type: \"number\",\n },\n region: {\n type: \"string\",\n },\n country: {\n type: \"string\",\n },\n continent: {\n type: \"string\",\n },\n timezone: {\n type: \"string\",\n },\n type: {\n type: \"string\",\n enum: [\"reference\", \"subordinate\"],\n },\n source: {\n type: \"object\",\n additionalProperties: true,\n },\n license: {\n type: \"object\",\n additionalProperties: true,\n },\n disclaimers: {\n type: \"string\",\n },\n distance: {\n type: \"number\",\n description: \"Distance from query point in meters (only for proximity searches)\",\n },\n datums: {\n type: \"object\",\n additionalProperties: {\n type: \"number\",\n },\n },\n harmonic_constituents: {\n type: \"array\",\n items: {\n type: \"object\",\n additionalProperties: true,\n },\n },\n defaultDatum: {\n type: \"string\",\n },\n offsets: {\n type: \"object\",\n additionalProperties: true,\n },\n },\n additionalProperties: true,\n },\n Extreme: {\n type: \"object\",\n properties: {\n time: {\n type: \"string\",\n format: \"date-time\",\n },\n level: {\n type: \"number\",\n },\n high: {\n type: \"boolean\",\n },\n low: {\n type: \"boolean\",\n },\n label: {\n type: \"string\",\n },\n },\n required: [\"time\", \"level\", \"high\", \"low\", \"label\"],\n },\n ExtremesResponse: {\n type: \"object\",\n properties: {\n datum: {\n type: \"string\",\n },\n units: {\n type: \"string\",\n enum: [\"meters\", \"feet\"],\n },\n station: {\n $ref: \"#/components/schemas/Station\",\n },\n distance: {\n type: \"number\",\n },\n extremes: {\n type: \"array\",\n items: {\n $ref: \"#/components/schemas/Extreme\",\n },\n },\n },\n },\n TimelineEntry: {\n type: \"object\",\n properties: {\n time: {\n type: \"string\",\n format: \"date-time\",\n },\n level: {\n type: \"number\",\n },\n },\n required: [\"time\", \"level\"],\n },\n TimelineResponse: {\n type: \"object\",\n properties: {\n datum: {\n type: \"string\",\n },\n units: {\n type: \"string\",\n enum: [\"meters\", \"feet\"],\n },\n station: {\n $ref: \"#/components/schemas/Station\",\n },\n distance: {\n type: \"number\",\n },\n timeline: {\n type: \"array\",\n items: {\n $ref: \"#/components/schemas/TimelineEntry\",\n },\n },\n },\n },\n Error: {\n type: \"object\",\n properties: {\n message: {\n type: \"string\",\n },\n errors: {\n type: \"array\",\n items: {\n type: \"object\",\n additionalProperties: true,\n },\n },\n },\n required: [\"message\"],\n },\n },\n },\n} as const;\n","import { json, Router, Request, Response, type ErrorRequestHandler } from \"express\";\nimport { getExtremesPrediction, getTimelinePrediction, findStation, stationsNear } from \"neaps\";\nimport { middleware as openapiValidator } from \"express-openapi-validator\";\nimport openapi from \"./openapi.js\";\n\nconst router = Router();\n\nrouter.use(json());\n\nrouter.use(\n openapiValidator({\n apiSpec: openapi,\n validateRequests: {\n coerceTypes: true,\n },\n validateResponses: import.meta.env?.VITEST,\n }),\n);\n\nrouter.get(\"/tides/openapi.json\", (req, res) => {\n res.json(openapi);\n});\n\nrouter.get(\"/tides/extremes\", (req: Request, res: Response) => {\n res.json(\n getExtremesPrediction({\n ...positionOptions(req),\n ...predictionOptions(req),\n }),\n );\n});\n\nrouter.get(\"/tides/timeline\", (req: Request, res: Response) => {\n try {\n res.json(\n getTimelinePrediction({\n ...positionOptions(req),\n ...predictionOptions(req),\n }),\n );\n } catch (error) {\n res.status(400).json({ message: (error as Error).message });\n }\n});\n\nrouter.get(\"/tides/stations\", (req: Request, res: Response) => {\n if (req.query.id) {\n try {\n return res.json(findStation(req.query.id as string));\n } catch (error) {\n return res.status(404).json({ message: (error as Error).message });\n }\n }\n\n const { latitude, longitude } = positionOptions(req);\n\n if (latitude === undefined || longitude === undefined) {\n return res.status(400).json({\n message: \"Either 'id' or coordinates (latitude and longitude) required\",\n });\n }\n\n const limit = req.query.limit ? parseInt(req.query.limit as string, 10) : 10;\n\n const stations = stationsNear({ latitude, longitude }, limit);\n res.json(stations);\n});\n\nrouter.get(\"/tides/stations/:id/extremes\", (req: Request, res: Response) => {\n let station: ReturnType<typeof findStation>;\n\n try {\n station = findStation(req.params.id);\n } catch (error) {\n return res.status(404).json({ message: (error as Error).message });\n }\n\n res.json(station.getExtremesPrediction(predictionOptions(req)));\n});\n\nrouter.get(\"/tides/stations/:id/timeline\", (req: Request, res: Response) => {\n try {\n const station = findStation(req.params.id);\n res.json(station.getTimelinePrediction(predictionOptions(req)));\n } catch (error) {\n if ((error as Error).message.includes(\"not found\")) {\n return res.status(404).json({ message: (error as Error).message });\n }\n // Subordinate station errors and other application errors\n return res.status(400).json({ message: (error as Error).message });\n }\n});\n\nrouter.use(((err, _req, res, next) => {\n if (!err) return next();\n\n const status = err.status ?? 500;\n const message = err.message ?? \"Unknown error\";\n\n res.status(status).json({ message, errors: err.errors });\n}) satisfies ErrorRequestHandler);\n\nfunction positionOptions(req: Request) {\n return {\n latitude: req.query.latitude as unknown as number,\n longitude: req.query.longitude as unknown as number,\n };\n}\n\nfunction predictionOptions(req: Request) {\n return {\n start: req.query.start ? new Date(req.query.start as string) : new Date(),\n end: req.query.end\n ? new Date(req.query.end as string)\n : new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),\n ...(req.query.datum && { datum: req.query.datum as string }),\n ...(req.query.units && { units: req.query.units as \"meters\" | \"feet\" }),\n };\n}\n\nexport default router;\n","import express from \"express\";\nimport routes from \"./routes.js\";\nimport openapi from \"./openapi.js\";\n\nexport function createApp() {\n return express().use(\"/\", routes);\n}\n\nexport { routes, openapi };\n"],"mappings":";;;;;;;;;ACEA,sBAAe;CACb,SAAS;CACT,MAAM;EACJ,OAAO;EACEA;EACT,aAAa;EACb,SAAS,EACP,MAAM,OACP;EACF;CACD,OAAO;EACL,mBAAmB,EACjB,KAAK;GACH,SAAS;GACT,aACE;GACF,YAAY;IACV,EAAE,MAAM,oCAAoC;IAC5C,EAAE,MAAM,qCAAqC;IAC7C,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,+BAA+B;IACvC,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,iCAAiC;IAC1C;GACD,WAAW;IACT,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,yCACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACF;GACF,EACF;EACD,mBAAmB,EACjB,KAAK;GACH,SAAS;GACT,aAAa;GACb,YAAY;IACV,EAAE,MAAM,oCAAoC;IAC5C,EAAE,MAAM,qCAAqC;IAC7C,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,+BAA+B;IACvC,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,iCAAiC;IAC1C;GACD,WAAW;IACT,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,yCACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACF;GACF,EACF;EACD,mBAAmB,EACjB,KAAK;GACH,SAAS;GACT,aAAa;GACb,YAAY;IACV;KACE,MAAM;KACN,IAAI;KACJ,aAAa;KACb,UAAU;KACV,QAAQ,EACN,MAAM,UACP;KACF;IACD;KACE,MAAM;KACN,IAAI;KACJ,aAAa;KACb,UAAU;KACV,QAAQ;MACN,MAAM;MACN,SAAS;MACT,SAAS;MACV;KACF;IACD;KACE,MAAM;KACN,IAAI;KACJ,aAAa;KACb,UAAU;KACV,QAAQ;MACN,MAAM;MACN,SAAS;MACT,SAAS;MACV;KACF;IACD;KACE,MAAM;KACN,IAAI;KACJ,aAAa;KACb,UAAU;KACV,QAAQ;MACN,MAAM;MACN,SAAS;MACT,SAAS;MACT,SAAS;MACV;KACF;IACF;GACD,WAAW;IACT,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,OAAO,CACL,EACE,MAAM,gCACP,EACD;MACE,MAAM;MACN,OAAO,EACL,MAAM,gCACP;MACF,CACF,EACF,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACF;GACF,EACF;EACD,iCAAiC,EAC/B,KAAK;GACH,SAAS;GACT,YAAY;IACV,EAAE,MAAM,qCAAqC;IAC7C,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,+BAA+B;IACvC,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,iCAAiC;IAC1C;GACD,WAAW;IACT,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,yCACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACF;GACF,EACF;EACD,iCAAiC,EAC/B,KAAK;GACH,SAAS;GACT,YAAY;IACV,EAAE,MAAM,qCAAqC;IAC7C,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,+BAA+B;IACvC,EAAE,MAAM,iCAAiC;IACzC,EAAE,MAAM,iCAAiC;IAC1C;GACD,WAAW;IACT,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,yCACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACD,OAAO;KACL,aAAa;KACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,8BACP,EACF,EACF;KACF;IACF;GACF,EACF;EACD,uBAAuB,EACrB,KAAK;GACH,SAAS;GACT,WAAW,EACT,OAAO;IACL,aAAa;IACb,SAAS,EACP,oBAAoB,EAClB,QAAQ,EACN,MAAM,UACP,EACF,EACF;IACF,EACF;GACF,EACF;EACF;CACD,YAAY;EACV,YAAY;GACV,UAAU;IACR,MAAM;IACN,IAAI;IACJ,aAAa;IACb,UAAU;IACV,QAAQ;KACN,MAAM;KACN,SAAS;KACT,SAAS;KACV;IACF;GACD,WAAW;IACT,MAAM;IACN,IAAI;IACJ,aAAa;IACb,UAAU;IACV,QAAQ;KACN,MAAM;KACN,SAAS;KACT,SAAS;KACV;IACF;GACD,OAAO;IACL,MAAM;IACN,IAAI;IACJ,UAAU;IACV,aAAa;IACb,QAAQ;KACN,MAAM;KACN,QAAQ;KACT;IACF;GACD,KAAK;IACH,MAAM;IACN,IAAI;IACJ,UAAU;IACV,aAAa;IACb,QAAQ;KACN,MAAM;KACN,QAAQ;KACT;IACF;GACD,OAAO;IACL,MAAM;IACN,IAAI;IACJ,UAAU;IACV,aAAa;IACb,QAAQ;KACN,MAAM;KACN,MAAM;MAAC;MAAQ;MAAO;MAAO;MAAO;MAAO;MAAO;KACnD;IACF;GACD,OAAO;IACL,MAAM;IACN,IAAI;IACJ,UAAU;IACV,aAAa;IACb,QAAQ;KACN,MAAM;KACN,MAAM,CAAC,UAAU,OAAO;KACxB,SAAS;KACV;IACF;GACD,WAAW;IACT,MAAM;IACN,IAAI;IACJ,UAAU;IACV,aAAa;IACb,QAAQ,EACN,MAAM,UACP;IACF;GACF;EACD,SAAS;GACP,SAAS;IACP,MAAM;IACN,YAAY;KACV,IAAI,EACF,MAAM,UACP;KACD,MAAM,EACJ,MAAM,UACP;KACD,UAAU,EACR,MAAM,UACP;KACD,WAAW,EACT,MAAM,UACP;KACD,QAAQ,EACN,MAAM,UACP;KACD,SAAS,EACP,MAAM,UACP;KACD,WAAW,EACT,MAAM,UACP;KACD,UAAU,EACR,MAAM,UACP;KACD,MAAM;MACJ,MAAM;MACN,MAAM,CAAC,aAAa,cAAc;MACnC;KACD,QAAQ;MACN,MAAM;MACN,sBAAsB;MACvB;KACD,SAAS;MACP,MAAM;MACN,sBAAsB;MACvB;KACD,aAAa,EACX,MAAM,UACP;KACD,UAAU;MACR,MAAM;MACN,aAAa;MACd;KACD,QAAQ;MACN,MAAM;MACN,sBAAsB,EACpB,MAAM,UACP;MACF;KACD,uBAAuB;MACrB,MAAM;MACN,OAAO;OACL,MAAM;OACN,sBAAsB;OACvB;MACF;KACD,cAAc,EACZ,MAAM,UACP;KACD,SAAS;MACP,MAAM;MACN,sBAAsB;MACvB;KACF;IACD,sBAAsB;IACvB;GACD,SAAS;IACP,MAAM;IACN,YAAY;KACV,MAAM;MACJ,MAAM;MACN,QAAQ;MACT;KACD,OAAO,EACL,MAAM,UACP;KACD,MAAM,EACJ,MAAM,WACP;KACD,KAAK,EACH,MAAM,WACP;KACD,OAAO,EACL,MAAM,UACP;KACF;IACD,UAAU;KAAC;KAAQ;KAAS;KAAQ;KAAO;KAAQ;IACpD;GACD,kBAAkB;IAChB,MAAM;IACN,YAAY;KACV,OAAO,EACL,MAAM,UACP;KACD,OAAO;MACL,MAAM;MACN,MAAM,CAAC,UAAU,OAAO;MACzB;KACD,SAAS,EACP,MAAM,gCACP;KACD,UAAU,EACR,MAAM,UACP;KACD,UAAU;MACR,MAAM;MACN,OAAO,EACL,MAAM,gCACP;MACF;KACF;IACF;GACD,eAAe;IACb,MAAM;IACN,YAAY;KACV,MAAM;MACJ,MAAM;MACN,QAAQ;MACT;KACD,OAAO,EACL,MAAM,UACP;KACF;IACD,UAAU,CAAC,QAAQ,QAAQ;IAC5B;GACD,kBAAkB;IAChB,MAAM;IACN,YAAY;KACV,OAAO,EACL,MAAM,UACP;KACD,OAAO;MACL,MAAM;MACN,MAAM,CAAC,UAAU,OAAO;MACzB;KACD,SAAS,EACP,MAAM,gCACP;KACD,UAAU,EACR,MAAM,UACP;KACD,UAAU;MACR,MAAM;MACN,OAAO,EACL,MAAM,sCACP;MACF;KACF;IACF;GACD,OAAO;IACL,MAAM;IACN,YAAY;KACV,SAAS,EACP,MAAM,UACP;KACD,QAAQ;MACN,MAAM;MACN,OAAO;OACL,MAAM;OACN,sBAAsB;OACvB;MACF;KACF;IACD,UAAU,CAAC,UAAU;IACtB;GACF;EACF;CACF;;;;AC9gBD,MAAM,SAAS,QAAQ;AAEvB,OAAO,IAAI,MAAM,CAAC;AAElB,OAAO,IACLC,WAAiB;CACf,SAASC;CACT,kBAAkB,EAChB,aAAa,MACd;CACD,mBAAmB,OAAO,KAAK,KAAK;CACrC,CAAC,CACH;AAED,OAAO,IAAI,wBAAwB,KAAK,QAAQ;AAC9C,KAAI,KAAKA,gBAAQ;EACjB;AAEF,OAAO,IAAI,oBAAoB,KAAc,QAAkB;AAC7D,KAAI,KACF,sBAAsB;EACpB,GAAG,gBAAgB,IAAI;EACvB,GAAG,kBAAkB,IAAI;EAC1B,CAAC,CACH;EACD;AAEF,OAAO,IAAI,oBAAoB,KAAc,QAAkB;AAC7D,KAAI;AACF,MAAI,KACF,sBAAsB;GACpB,GAAG,gBAAgB,IAAI;GACvB,GAAG,kBAAkB,IAAI;GAC1B,CAAC,CACH;UACM,OAAO;AACd,MAAI,OAAO,IAAI,CAAC,KAAK,EAAE,SAAU,MAAgB,SAAS,CAAC;;EAE7D;AAEF,OAAO,IAAI,oBAAoB,KAAc,QAAkB;AAC7D,KAAI,IAAI,MAAM,GACZ,KAAI;AACF,SAAO,IAAI,KAAK,YAAY,IAAI,MAAM,GAAa,CAAC;UAC7C,OAAO;AACd,SAAO,IAAI,OAAO,IAAI,CAAC,KAAK,EAAE,SAAU,MAAgB,SAAS,CAAC;;CAItE,MAAM,EAAE,UAAU,cAAc,gBAAgB,IAAI;AAEpD,KAAI,aAAa,UAAa,cAAc,OAC1C,QAAO,IAAI,OAAO,IAAI,CAAC,KAAK,EAC1B,SAAS,gEACV,CAAC;CAGJ,MAAM,QAAQ,IAAI,MAAM,QAAQ,SAAS,IAAI,MAAM,OAAiB,GAAG,GAAG;CAE1E,MAAM,WAAW,aAAa;EAAE;EAAU;EAAW,EAAE,MAAM;AAC7D,KAAI,KAAK,SAAS;EAClB;AAEF,OAAO,IAAI,iCAAiC,KAAc,QAAkB;CAC1E,IAAI;AAEJ,KAAI;AACF,YAAU,YAAY,IAAI,OAAO,GAAG;UAC7B,OAAO;AACd,SAAO,IAAI,OAAO,IAAI,CAAC,KAAK,EAAE,SAAU,MAAgB,SAAS,CAAC;;AAGpE,KAAI,KAAK,QAAQ,sBAAsB,kBAAkB,IAAI,CAAC,CAAC;EAC/D;AAEF,OAAO,IAAI,iCAAiC,KAAc,QAAkB;AAC1E,KAAI;EACF,MAAM,UAAU,YAAY,IAAI,OAAO,GAAG;AAC1C,MAAI,KAAK,QAAQ,sBAAsB,kBAAkB,IAAI,CAAC,CAAC;UACxD,OAAO;AACd,MAAK,MAAgB,QAAQ,SAAS,YAAY,CAChD,QAAO,IAAI,OAAO,IAAI,CAAC,KAAK,EAAE,SAAU,MAAgB,SAAS,CAAC;AAGpE,SAAO,IAAI,OAAO,IAAI,CAAC,KAAK,EAAE,SAAU,MAAgB,SAAS,CAAC;;EAEpE;AAEF,OAAO,MAAM,KAAK,MAAM,KAAK,SAAS;AACpC,KAAI,CAAC,IAAK,QAAO,MAAM;CAEvB,MAAM,SAAS,IAAI,UAAU;CAC7B,MAAM,UAAU,IAAI,WAAW;AAE/B,KAAI,OAAO,OAAO,CAAC,KAAK;EAAE;EAAS,QAAQ,IAAI;EAAQ,CAAC;GACzB;AAEjC,SAAS,gBAAgB,KAAc;AACrC,QAAO;EACL,UAAU,IAAI,MAAM;EACpB,WAAW,IAAI,MAAM;EACtB;;AAGH,SAAS,kBAAkB,KAAc;AACvC,QAAO;EACL,OAAO,IAAI,MAAM,QAAQ,IAAI,KAAK,IAAI,MAAM,MAAgB,mBAAG,IAAI,MAAM;EACzE,KAAK,IAAI,MAAM,MACX,IAAI,KAAK,IAAI,MAAM,IAAc,GACjC,IAAI,KAAK,KAAK,KAAK,GAAG,QAAc,KAAK,IAAK;EAClD,GAAI,IAAI,MAAM,SAAS,EAAE,OAAO,IAAI,MAAM,OAAiB;EAC3D,GAAI,IAAI,MAAM,SAAS,EAAE,OAAO,IAAI,MAAM,OAA4B;EACvE;;AAGH,qBAAe;;;;ACpHf,SAAgB,YAAY;AAC1B,QAAO,SAAS,CAAC,IAAI,KAAKC,eAAO"}
{
"name": "@neaps/api",
"version": "0.1.0",
"version": "0.2.0",
"description": "HTTP JSON API for tide predictions",

@@ -5,0 +5,0 @@ "repository": {

+17
-17
# @neaps/api
HTTP JSON API for tide predictions using NOAA harmonic constituents.
HTTP JSON API for tide predictions using [neaps](https://github.com/neaps/neaps).

@@ -8,3 +8,3 @@ ## Installation

```bash
npm install @neaps/api express
npm install @neaps/api
```

@@ -44,3 +44,3 @@

### GET /extremes
### GET /tides/extremes

@@ -51,4 +51,4 @@ Get high and low tide predictions for the nearest station to given coordinates.

- `lat` or `latitude` (required): Latitude (-90 to 90)
- `lon`, `lng`, or `longitude` (required): Longitude (-180 to 180)
- `latitude` (required): Latitude (-90 to 90)
- `longitude` (required): Longitude (-180 to 180)
- `start` (required): Start date/time in ISO 8601 format

@@ -62,6 +62,6 @@ - `end` (required): End date/time in ISO 8601 format

```bash
curl "http://localhost:3000/extremes?lat=26.772&lon=-80.05&start=2025-12-17T00:00:00Z&end=2025-12-18T00:00:00Z&datum=MLLW&units=feet"
curl "http://localhost:3000/tides/extremes?latitude=26.772&longitude=-80.05&start=2025-12-17T00:00:00Z&end=2025-12-18T00:00:00Z&datum=MLLW&units=feet"
```
### GET /timeline
### GET /tides/timeline

@@ -75,6 +75,6 @@ Get water level predictions at regular intervals for the nearest station.

```bash
curl "http://localhost:3000/timeline?lat=26.772&lon=-80.05&start=2025-12-17T00:00:00Z&end=2025-12-18T00:00:00Z"
curl "http://localhost:3000/tides/timeline?latitude=26.772&longitude=-80.05&start=2025-12-17T00:00:00Z&end=2025-12-18T00:00:00Z"
```
### GET /stations
### GET /tides/stations

@@ -86,4 +86,4 @@ Find stations by ID or near a location.

- `id` (optional): Station ID or source ID
- `lat` or `latitude` (optional): Latitude for proximity search
- `lon`, `lng`, or `longitude` (optional): Longitude for proximity search
- `latitude` (optional): Latitude for proximity search
- `longitude` (optional): Longitude for proximity search
- `limit` (optional): Maximum number of stations to return (1-100, defaults to 10)

@@ -95,9 +95,9 @@

# Find a specific station
curl "http://localhost:3000/stations?id=noaa/8722588"
curl "http://localhost:3000/tides/stations?id=noaa/8722588"
# Find stations near coordinates
curl "http://localhost:3000/stations?lat=26.772&lon=-80.05&limit=5"
curl "http://localhost:3000/tides/stations?latitude=26.772&longitude=-80.05&limit=5"
```
### GET /stations/:id/extremes
### GET /tides/stations/:id/extremes

@@ -120,6 +120,6 @@ Get extremes prediction for a specific station.

```bash
curl "http://localhost:3000/stations/noaa%2F8722588/extremes?start=2025-12-17T00:00:00Z&end=2025-12-18T00:00:00Z"
curl "http://localhost:3000/tides/stations/noaa%2F8722588/extremes?start=2025-12-17T00:00:00Z&end=2025-12-18T00:00:00Z"
```
### GET /stations/:id/timeline
### GET /tides/stations/:id/timeline

@@ -132,3 +132,3 @@ Get timeline prediction for a specific station.

### GET /openapi.json
### GET /tides/openapi.json

@@ -135,0 +135,0 @@ Get the OpenAPI 3.0 specification for this API.