@chrtco/sdk 0.3.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +144 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +143 -3
- package/dist/index.d.ts +143 -3
- package/dist/index.js +141 -18
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Chrt: () => Chrt,
|
|
24
|
-
ChrtError: () => ChrtError
|
|
24
|
+
ChrtError: () => ChrtError,
|
|
25
|
+
paginate: () => paginate,
|
|
26
|
+
parseWebhook: () => parseWebhook
|
|
25
27
|
});
|
|
26
28
|
module.exports = __toCommonJS(index_exports);
|
|
27
29
|
|
|
@@ -54,7 +56,7 @@ var HttpClient = class {
|
|
|
54
56
|
getApiKey() {
|
|
55
57
|
return this.apiKey;
|
|
56
58
|
}
|
|
57
|
-
async request(path, params) {
|
|
59
|
+
async request(path, params, signal) {
|
|
58
60
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
59
61
|
if (params) {
|
|
60
62
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -63,9 +65,13 @@ var HttpClient = class {
|
|
|
63
65
|
}
|
|
64
66
|
let lastError = null;
|
|
65
67
|
for (let attempt = 0; attempt <= this.retries; attempt++) {
|
|
68
|
+
if (signal?.aborted) {
|
|
69
|
+
throw signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
70
|
+
}
|
|
66
71
|
try {
|
|
67
72
|
const res = await fetch(url.toString(), {
|
|
68
|
-
headers: { "x-api-key": this.apiKey }
|
|
73
|
+
headers: { "x-api-key": this.apiKey },
|
|
74
|
+
signal
|
|
69
75
|
});
|
|
70
76
|
if (!res.ok) {
|
|
71
77
|
let body;
|
|
@@ -84,6 +90,7 @@ var HttpClient = class {
|
|
|
84
90
|
return await res.json();
|
|
85
91
|
} catch (e) {
|
|
86
92
|
if (e instanceof ChrtError) throw e;
|
|
93
|
+
if (signal?.aborted) throw e;
|
|
87
94
|
lastError = e;
|
|
88
95
|
if (attempt < this.retries) {
|
|
89
96
|
await sleep(100 * 2 ** attempt);
|
|
@@ -124,11 +131,36 @@ var CellsEndpoint = class {
|
|
|
124
131
|
}
|
|
125
132
|
/** List ENC cells with optional filters */
|
|
126
133
|
async list(options) {
|
|
127
|
-
|
|
134
|
+
const { signal, ...params } = options ?? {};
|
|
135
|
+
return this.http.request(
|
|
136
|
+
"/cells",
|
|
137
|
+
params,
|
|
138
|
+
signal
|
|
139
|
+
);
|
|
128
140
|
}
|
|
129
141
|
/** Get a single cell by ID */
|
|
130
|
-
async get(cellId) {
|
|
131
|
-
return this.http.request(
|
|
142
|
+
async get(cellId, signal) {
|
|
143
|
+
return this.http.request(
|
|
144
|
+
`/cells/${encodeURIComponent(cellId)}`,
|
|
145
|
+
void 0,
|
|
146
|
+
signal
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// src/endpoints/coverage.ts
|
|
152
|
+
var CoverageEndpoint = class {
|
|
153
|
+
constructor(http) {
|
|
154
|
+
this.http = http;
|
|
155
|
+
}
|
|
156
|
+
/** List available chart sources with cell counts */
|
|
157
|
+
async list(options) {
|
|
158
|
+
const { signal, ...params } = options ?? {};
|
|
159
|
+
return this.http.request(
|
|
160
|
+
"/coverage",
|
|
161
|
+
params,
|
|
162
|
+
signal
|
|
163
|
+
);
|
|
132
164
|
}
|
|
133
165
|
};
|
|
134
166
|
|
|
@@ -139,12 +171,16 @@ var FeaturesEndpoint = class {
|
|
|
139
171
|
}
|
|
140
172
|
/** Query features within a bounding box */
|
|
141
173
|
async list(options) {
|
|
142
|
-
return this.http.request(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
174
|
+
return this.http.request(
|
|
175
|
+
"/features",
|
|
176
|
+
{
|
|
177
|
+
bbox: options.bbox.join(","),
|
|
178
|
+
layer: options.layer,
|
|
179
|
+
limit: options.limit,
|
|
180
|
+
offset: options.offset
|
|
181
|
+
},
|
|
182
|
+
options.signal
|
|
183
|
+
);
|
|
148
184
|
}
|
|
149
185
|
};
|
|
150
186
|
|
|
@@ -155,21 +191,68 @@ var QueryEndpoint = class {
|
|
|
155
191
|
}
|
|
156
192
|
/** Spatial proximity search around a point */
|
|
157
193
|
async nearby(options) {
|
|
158
|
-
return this.http.request(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
194
|
+
return this.http.request(
|
|
195
|
+
"/query",
|
|
196
|
+
{
|
|
197
|
+
point: options.point.join(","),
|
|
198
|
+
radius: options.radius,
|
|
199
|
+
layer: options.layer,
|
|
200
|
+
limit: options.limit
|
|
201
|
+
},
|
|
202
|
+
options.signal
|
|
203
|
+
);
|
|
164
204
|
}
|
|
165
205
|
};
|
|
166
206
|
|
|
207
|
+
// src/pagination.ts
|
|
208
|
+
async function* paginate(fetchPage, getItems, getTotalCount, options) {
|
|
209
|
+
const pageSize = options?.pageSize ?? 100;
|
|
210
|
+
const signal = options?.signal;
|
|
211
|
+
let offset = 0;
|
|
212
|
+
while (true) {
|
|
213
|
+
if (signal?.aborted) return;
|
|
214
|
+
const response = await fetchPage(offset, signal);
|
|
215
|
+
const items = getItems(response);
|
|
216
|
+
for (const item of items) {
|
|
217
|
+
yield item;
|
|
218
|
+
}
|
|
219
|
+
if (items.length === 0) return;
|
|
220
|
+
offset += items.length;
|
|
221
|
+
const total = getTotalCount(response);
|
|
222
|
+
if (total > 0 && offset >= total) return;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/webhooks.ts
|
|
227
|
+
var VALID_TYPES = /* @__PURE__ */ new Set(["cell.updated", "cell.new", "cell.deleted"]);
|
|
228
|
+
function parseWebhook(body) {
|
|
229
|
+
if (typeof body !== "object" || body === null) {
|
|
230
|
+
throw new Error("Invalid webhook payload: expected an object");
|
|
231
|
+
}
|
|
232
|
+
const obj = body;
|
|
233
|
+
if (typeof obj.id !== "string") {
|
|
234
|
+
throw new Error("Invalid webhook payload: missing 'id'");
|
|
235
|
+
}
|
|
236
|
+
if (typeof obj.timestamp !== "string") {
|
|
237
|
+
throw new Error("Invalid webhook payload: missing 'timestamp'");
|
|
238
|
+
}
|
|
239
|
+
if (typeof obj.type !== "string" || !VALID_TYPES.has(obj.type)) {
|
|
240
|
+
throw new Error(`Invalid webhook payload: unknown type '${String(obj.type)}'`);
|
|
241
|
+
}
|
|
242
|
+
if (typeof obj.data !== "object" || obj.data === null) {
|
|
243
|
+
throw new Error("Invalid webhook payload: missing 'data'");
|
|
244
|
+
}
|
|
245
|
+
return obj;
|
|
246
|
+
}
|
|
247
|
+
|
|
167
248
|
// src/index.ts
|
|
168
249
|
var Chrt = class {
|
|
169
250
|
/** Tile endpoints — styleUrl, tileUrl, groups */
|
|
170
251
|
tiles;
|
|
171
252
|
/** Cell catalogue endpoints — list, get */
|
|
172
253
|
cells;
|
|
254
|
+
/** Coverage data — list available chart sources */
|
|
255
|
+
coverage;
|
|
173
256
|
featuresEp;
|
|
174
257
|
queryEp;
|
|
175
258
|
http;
|
|
@@ -178,6 +261,7 @@ var Chrt = class {
|
|
|
178
261
|
this.http = new HttpClient(config.apiKey, config.baseUrl, config.retries);
|
|
179
262
|
this.tiles = new TilesEndpoint(this.http);
|
|
180
263
|
this.cells = new CellsEndpoint(this.http);
|
|
264
|
+
this.coverage = new CoverageEndpoint(this.http);
|
|
181
265
|
this.featuresEp = new FeaturesEndpoint(this.http);
|
|
182
266
|
this.queryEp = new QueryEndpoint(this.http);
|
|
183
267
|
}
|
|
@@ -190,6 +274,45 @@ var Chrt = class {
|
|
|
190
274
|
async query(options) {
|
|
191
275
|
return this.queryEp.nearby(options);
|
|
192
276
|
}
|
|
277
|
+
/* ── Auto-pagination iterators ───────────────────── */
|
|
278
|
+
/**
|
|
279
|
+
* Iterate all features matching a bounding-box query, auto-paginating.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```ts
|
|
283
|
+
* for await (const feature of chrt.featuresIter({ bbox: [-74, 40, -73, 41] })) {
|
|
284
|
+
* console.log(feature.properties);
|
|
285
|
+
* }
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
featuresIter(options) {
|
|
289
|
+
const pageSize = options.limit ?? 100;
|
|
290
|
+
return paginate(
|
|
291
|
+
(offset, signal) => this.features({ ...options, limit: pageSize, offset, signal }),
|
|
292
|
+
(r) => r.features,
|
|
293
|
+
(r) => r.total_count ?? 0,
|
|
294
|
+
{ pageSize, signal: options.signal }
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Iterate all cells matching a query, auto-paginating.
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```ts
|
|
302
|
+
* for await (const cell of chrt.cellsIter({ source: "NOAA" })) {
|
|
303
|
+
* console.log(cell.cell_id, cell.title);
|
|
304
|
+
* }
|
|
305
|
+
* ```
|
|
306
|
+
*/
|
|
307
|
+
cellsIter(options) {
|
|
308
|
+
const pageSize = options?.limit ?? 100;
|
|
309
|
+
return paginate(
|
|
310
|
+
(offset, signal) => this.cells.list({ ...options, limit: pageSize, offset, signal }),
|
|
311
|
+
(r) => r.cells,
|
|
312
|
+
(r) => r.total_count,
|
|
313
|
+
{ pageSize, signal: options?.signal }
|
|
314
|
+
);
|
|
315
|
+
}
|
|
193
316
|
/* ── Map integration helpers ─────────────────────── */
|
|
194
317
|
/** Returns a style.json URL ready for Mapbox GL / MapLibre */
|
|
195
318
|
styleUrl() {
|
|
@@ -218,6 +341,8 @@ var Chrt = class {
|
|
|
218
341
|
// Annotate the CommonJS export names for ESM import in node:
|
|
219
342
|
0 && (module.exports = {
|
|
220
343
|
Chrt,
|
|
221
|
-
ChrtError
|
|
344
|
+
ChrtError,
|
|
345
|
+
paginate,
|
|
346
|
+
parseWebhook
|
|
222
347
|
});
|
|
223
348
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/endpoints/tiles.ts","../src/endpoints/cells.ts","../src/endpoints/features.ts","../src/endpoints/query.ts"],"sourcesContent":["import { HttpClient, ChrtError } from \"./client.js\";\nimport { TilesEndpoint } from \"./endpoints/tiles.js\";\nimport { CellsEndpoint } from \"./endpoints/cells.js\";\nimport { FeaturesEndpoint } from \"./endpoints/features.js\";\nimport { QueryEndpoint } from \"./endpoints/query.js\";\nimport type {\n ChrtConfig,\n FeaturesOptions,\n QueryOptions,\n CellsOptions,\n FeatureCollection,\n CellsResponse,\n CellDetail,\n TransformRequest,\n} from \"./types.js\";\n\nexport class Chrt {\n /** Tile endpoints — styleUrl, tileUrl, groups */\n readonly tiles: TilesEndpoint;\n /** Cell catalogue endpoints — list, get */\n readonly cells: CellsEndpoint;\n\n private featuresEp: FeaturesEndpoint;\n private queryEp: QueryEndpoint;\n private http: HttpClient;\n\n constructor(apiKeyOrConfig: string | ChrtConfig) {\n const config =\n typeof apiKeyOrConfig === \"string\"\n ? { apiKey: apiKeyOrConfig }\n : apiKeyOrConfig;\n\n this.http = new HttpClient(config.apiKey, config.baseUrl, config.retries);\n this.tiles = new TilesEndpoint(this.http);\n this.cells = new CellsEndpoint(this.http);\n this.featuresEp = new FeaturesEndpoint(this.http);\n this.queryEp = new QueryEndpoint(this.http);\n }\n\n /* ── Convenience shortcuts ───────────────────────── */\n\n /** Query GeoJSON features by bounding box */\n async features(options: FeaturesOptions): Promise<FeatureCollection> {\n return this.featuresEp.list(options);\n }\n\n /** Spatial proximity query around a point */\n async query(options: QueryOptions): Promise<FeatureCollection> {\n return this.queryEp.nearby(options);\n }\n\n /* ── Map integration helpers ─────────────────────── */\n\n /** Returns a style.json URL ready for Mapbox GL / MapLibre */\n styleUrl(): string {\n return this.tiles.styleUrl();\n }\n\n /**\n * Returns a `transformRequest` function for Mapbox GL JS / MapLibre GL JS.\n * Injects the API key header for requests to api.chrt.co.\n *\n * @example\n * const map = new mapboxgl.Map({\n * style: chrt.styleUrl(),\n * transformRequest: chrt.transformRequest(),\n * });\n */\n transformRequest(): (url: string) => TransformRequest | undefined {\n const apiKey = this.http.getApiKey();\n return (url: string) => {\n if (url.includes(\"api.chrt.co\")) {\n return { url, headers: { \"x-api-key\": apiKey } };\n }\n return undefined;\n };\n }\n}\n\n/* ── Re-exports ────────────────────────────────────── */\n\nexport { ChrtError } from \"./client.js\";\nexport type {\n ChrtConfig,\n FeaturesOptions,\n QueryOptions,\n CellsOptions,\n FeatureCollection,\n GeoJsonFeature,\n GeoJsonGeometry,\n CellsResponse,\n CellDetail,\n TileGroup,\n TileGroupsResponse,\n TransformRequest,\n} from \"./types.js\";\n","import type { ChrtErrorBody } from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.chrt.co/v1\";\n\nexport class ChrtError extends Error {\n status: number;\n code?: string;\n body?: ChrtErrorBody;\n\n constructor(status: number, body?: ChrtErrorBody) {\n super(body?.message ?? body?.error ?? `CHRT API error ${status}`);\n this.name = \"ChrtError\";\n this.status = status;\n this.code = body?.code;\n this.body = body;\n }\n}\n\nexport class HttpClient {\n private apiKey: string;\n private baseUrl: string;\n private retries: number;\n\n constructor(apiKey: string, baseUrl?: string, retries = 2) {\n this.apiKey = apiKey;\n this.baseUrl = (baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.retries = retries;\n }\n\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n getApiKey(): string {\n return this.apiKey;\n }\n\n async request<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.retries; attempt++) {\n try {\n const res = await fetch(url.toString(), {\n headers: { \"x-api-key\": this.apiKey },\n });\n\n if (!res.ok) {\n let body: ChrtErrorBody | undefined;\n try {\n body = await res.json() as ChrtErrorBody;\n } catch {\n /* empty */\n }\n const err = new ChrtError(res.status, body);\n\n // Only retry on 5xx\n if (res.status >= 500 && attempt < this.retries) {\n lastError = err;\n await sleep(100 * 2 ** attempt);\n continue;\n }\n throw err;\n }\n\n return (await res.json()) as T;\n } catch (e) {\n if (e instanceof ChrtError) throw e;\n lastError = e as Error;\n if (attempt < this.retries) {\n await sleep(100 * 2 ** attempt);\n continue;\n }\n }\n }\n\n throw lastError ?? new Error(\"CHRT API request failed\");\n }\n}\n\nfunction sleep(ms: number) {\n return new Promise((r) => setTimeout(r, ms));\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { TileGroupsResponse } from \"../types.js\";\n\nexport class TilesEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Full style.json URL with API key as query param */\n styleUrl(): string {\n return `${this.http.getBaseUrl()}/tiles/style.json?key=${this.http.getApiKey()}`;\n }\n\n /** Individual tile URL template */\n tileUrl(z: number, x: number, y: number, format: \"pbf\" | \"mvt\" = \"mvt\"): string {\n return `${this.http.getBaseUrl()}/tiles/${z}/${x}/${y}.${format}?key=${this.http.getApiKey()}`;\n }\n\n /** List available tileset groups */\n async groups(): Promise<TileGroupsResponse> {\n return this.http.request<TileGroupsResponse>(\"/tiles/groups\");\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { CellsOptions, CellsResponse, CellDetail } from \"../types.js\";\n\nexport class CellsEndpoint {\n constructor(private http: HttpClient) {}\n\n /** List ENC cells with optional filters */\n async list(options?: CellsOptions): Promise<CellsResponse> {\n return this.http.request<CellsResponse>(\"/cells\", options as Record<string, string | number | undefined>);\n }\n\n /** Get a single cell by ID */\n async get(cellId: string): Promise<CellDetail> {\n return this.http.request<CellDetail>(`/cells/${encodeURIComponent(cellId)}`);\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { FeaturesOptions, FeatureCollection } from \"../types.js\";\n\nexport class FeaturesEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Query features within a bounding box */\n async list(options: FeaturesOptions): Promise<FeatureCollection> {\n return this.http.request<FeatureCollection>(\"/features\", {\n bbox: options.bbox.join(\",\"),\n layer: options.layer,\n limit: options.limit,\n offset: options.offset,\n });\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { QueryOptions, FeatureCollection } from \"../types.js\";\n\nexport class QueryEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Spatial proximity search around a point */\n async nearby(options: QueryOptions): Promise<FeatureCollection> {\n return this.http.request<FeatureCollection>(\"/query\", {\n point: options.point.join(\",\"),\n radius: options.radius,\n layer: options.layer,\n limit: options.limit,\n });\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/endpoints/tiles.ts","../src/endpoints/cells.ts","../src/endpoints/coverage.ts","../src/endpoints/features.ts","../src/endpoints/query.ts","../src/pagination.ts","../src/webhooks.ts"],"sourcesContent":["import { HttpClient, ChrtError } from \"./client.js\";\nimport { TilesEndpoint } from \"./endpoints/tiles.js\";\nimport { CellsEndpoint } from \"./endpoints/cells.js\";\nimport { CoverageEndpoint } from \"./endpoints/coverage.js\";\nimport { FeaturesEndpoint } from \"./endpoints/features.js\";\nimport { QueryEndpoint } from \"./endpoints/query.js\";\nimport { paginate } from \"./pagination.js\";\nimport type {\n ChrtConfig,\n FeaturesOptions,\n QueryOptions,\n CellsOptions,\n CoverageOptions,\n FeatureCollection,\n GeoJsonFeature,\n CellsResponse,\n CellDetail,\n CoverageResponse,\n TransformRequest,\n} from \"./types.js\";\n\nexport class Chrt {\n /** Tile endpoints — styleUrl, tileUrl, groups */\n readonly tiles: TilesEndpoint;\n /** Cell catalogue endpoints — list, get */\n readonly cells: CellsEndpoint;\n /** Coverage data — list available chart sources */\n readonly coverage: CoverageEndpoint;\n\n private featuresEp: FeaturesEndpoint;\n private queryEp: QueryEndpoint;\n private http: HttpClient;\n\n constructor(apiKeyOrConfig: string | ChrtConfig) {\n const config =\n typeof apiKeyOrConfig === \"string\"\n ? { apiKey: apiKeyOrConfig }\n : apiKeyOrConfig;\n\n this.http = new HttpClient(config.apiKey, config.baseUrl, config.retries);\n this.tiles = new TilesEndpoint(this.http);\n this.cells = new CellsEndpoint(this.http);\n this.coverage = new CoverageEndpoint(this.http);\n this.featuresEp = new FeaturesEndpoint(this.http);\n this.queryEp = new QueryEndpoint(this.http);\n }\n\n /* ── Convenience shortcuts ───────────────────────── */\n\n /** Query GeoJSON features by bounding box */\n async features(options: FeaturesOptions): Promise<FeatureCollection> {\n return this.featuresEp.list(options);\n }\n\n /** Spatial proximity query around a point */\n async query(options: QueryOptions): Promise<FeatureCollection> {\n return this.queryEp.nearby(options);\n }\n\n /* ── Auto-pagination iterators ───────────────────── */\n\n /**\n * Iterate all features matching a bounding-box query, auto-paginating.\n *\n * @example\n * ```ts\n * for await (const feature of chrt.featuresIter({ bbox: [-74, 40, -73, 41] })) {\n * console.log(feature.properties);\n * }\n * ```\n */\n featuresIter(\n options: Omit<FeaturesOptions, \"offset\">,\n ): AsyncIterable<GeoJsonFeature> {\n const pageSize = options.limit ?? 100;\n return paginate<FeatureCollection, GeoJsonFeature>(\n (offset, signal) =>\n this.features({ ...options, limit: pageSize, offset, signal }),\n (r) => r.features,\n (r) => r.total_count ?? 0,\n { pageSize, signal: options.signal },\n );\n }\n\n /**\n * Iterate all cells matching a query, auto-paginating.\n *\n * @example\n * ```ts\n * for await (const cell of chrt.cellsIter({ source: \"NOAA\" })) {\n * console.log(cell.cell_id, cell.title);\n * }\n * ```\n */\n cellsIter(\n options?: Omit<CellsOptions, \"offset\">,\n ): AsyncIterable<CellDetail> {\n const pageSize = options?.limit ?? 100;\n return paginate<CellsResponse, CellDetail>(\n (offset, signal) =>\n this.cells.list({ ...options, limit: pageSize, offset, signal }),\n (r) => r.cells,\n (r) => r.total_count,\n { pageSize, signal: options?.signal },\n );\n }\n\n /* ── Map integration helpers ─────────────────────── */\n\n /** Returns a style.json URL ready for Mapbox GL / MapLibre */\n styleUrl(): string {\n return this.tiles.styleUrl();\n }\n\n /**\n * Returns a `transformRequest` function for Mapbox GL JS / MapLibre GL JS.\n * Injects the API key header for requests to api.chrt.co.\n *\n * @example\n * const map = new mapboxgl.Map({\n * style: chrt.styleUrl(),\n * transformRequest: chrt.transformRequest(),\n * });\n */\n transformRequest(): (url: string) => TransformRequest | undefined {\n const apiKey = this.http.getApiKey();\n return (url: string) => {\n if (url.includes(\"api.chrt.co\")) {\n return { url, headers: { \"x-api-key\": apiKey } };\n }\n return undefined;\n };\n }\n}\n\n/* ── Re-exports ────────────────────────────────────── */\n\nexport { ChrtError } from \"./client.js\";\nexport { paginate } from \"./pagination.js\";\nexport { parseWebhook } from \"./webhooks.js\";\nexport type {\n ChrtConfig,\n FeaturesOptions,\n QueryOptions,\n CellsOptions,\n CoverageOptions,\n FeatureCollection,\n GeoJsonFeature,\n GeoJsonGeometry,\n CellsResponse,\n CellDetail,\n CoverageSource,\n CoverageResponse,\n TileGroup,\n TileGroupsResponse,\n TransformRequest,\n} from \"./types.js\";\nexport type {\n WebhookPayload,\n WebhookEvent,\n WebhookEventType,\n CellUpdatedEvent,\n CellNewEvent,\n CellDeletedEvent,\n CellEventData,\n CellUpdatedData,\n} from \"./webhooks.js\";\n","import type { ChrtErrorBody } from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.chrt.co/v1\";\n\nexport class ChrtError extends Error {\n status: number;\n code?: string;\n body?: ChrtErrorBody;\n\n constructor(status: number, body?: ChrtErrorBody) {\n super(body?.message ?? body?.error ?? `CHRT API error ${status}`);\n this.name = \"ChrtError\";\n this.status = status;\n this.code = body?.code;\n this.body = body;\n }\n}\n\nexport class HttpClient {\n private apiKey: string;\n private baseUrl: string;\n private retries: number;\n\n constructor(apiKey: string, baseUrl?: string, retries = 2) {\n this.apiKey = apiKey;\n this.baseUrl = (baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.retries = retries;\n }\n\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n getApiKey(): string {\n return this.apiKey;\n }\n\n async request<T>(\n path: string,\n params?: Record<string, string | number | undefined>,\n signal?: AbortSignal,\n ): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.retries; attempt++) {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException(\"The operation was aborted.\", \"AbortError\");\n }\n\n try {\n const res = await fetch(url.toString(), {\n headers: { \"x-api-key\": this.apiKey },\n signal,\n });\n\n if (!res.ok) {\n let body: ChrtErrorBody | undefined;\n try {\n body = await res.json() as ChrtErrorBody;\n } catch {\n /* empty */\n }\n const err = new ChrtError(res.status, body);\n\n // Only retry on 5xx\n if (res.status >= 500 && attempt < this.retries) {\n lastError = err;\n await sleep(100 * 2 ** attempt);\n continue;\n }\n throw err;\n }\n\n return (await res.json()) as T;\n } catch (e) {\n if (e instanceof ChrtError) throw e;\n // Don't retry aborted requests\n if (signal?.aborted) throw e;\n lastError = e as Error;\n if (attempt < this.retries) {\n await sleep(100 * 2 ** attempt);\n continue;\n }\n }\n }\n\n throw lastError ?? new Error(\"CHRT API request failed\");\n }\n}\n\nfunction sleep(ms: number) {\n return new Promise((r) => setTimeout(r, ms));\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { TileGroupsResponse } from \"../types.js\";\n\nexport class TilesEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Full style.json URL with API key as query param */\n styleUrl(): string {\n return `${this.http.getBaseUrl()}/tiles/style.json?key=${this.http.getApiKey()}`;\n }\n\n /** Individual tile URL template */\n tileUrl(z: number, x: number, y: number, format: \"pbf\" | \"mvt\" = \"mvt\"): string {\n return `${this.http.getBaseUrl()}/tiles/${z}/${x}/${y}.${format}?key=${this.http.getApiKey()}`;\n }\n\n /** List available tileset groups */\n async groups(): Promise<TileGroupsResponse> {\n return this.http.request<TileGroupsResponse>(\"/tiles/groups\");\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { CellsOptions, CellsResponse, CellDetail } from \"../types.js\";\n\nexport class CellsEndpoint {\n constructor(private http: HttpClient) {}\n\n /** List ENC cells with optional filters */\n async list(options?: CellsOptions): Promise<CellsResponse> {\n const { signal, ...params } = options ?? {};\n return this.http.request<CellsResponse>(\n \"/cells\",\n params as Record<string, string | number | undefined>,\n signal,\n );\n }\n\n /** Get a single cell by ID */\n async get(cellId: string, signal?: AbortSignal): Promise<CellDetail> {\n return this.http.request<CellDetail>(\n `/cells/${encodeURIComponent(cellId)}`,\n undefined,\n signal,\n );\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { CoverageOptions, CoverageResponse } from \"../types.js\";\n\nexport class CoverageEndpoint {\n constructor(private http: HttpClient) {}\n\n /** List available chart sources with cell counts */\n async list(options?: CoverageOptions): Promise<CoverageResponse> {\n const { signal, ...params } = options ?? {};\n return this.http.request<CoverageResponse>(\n \"/coverage\",\n params as Record<string, string | number | undefined>,\n signal,\n );\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { FeaturesOptions, FeatureCollection } from \"../types.js\";\n\nexport class FeaturesEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Query features within a bounding box */\n async list(options: FeaturesOptions): Promise<FeatureCollection> {\n return this.http.request<FeatureCollection>(\n \"/features\",\n {\n bbox: options.bbox.join(\",\"),\n layer: options.layer,\n limit: options.limit,\n offset: options.offset,\n },\n options.signal,\n );\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { QueryOptions, FeatureCollection } from \"../types.js\";\n\nexport class QueryEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Spatial proximity search around a point */\n async nearby(options: QueryOptions): Promise<FeatureCollection> {\n return this.http.request<FeatureCollection>(\n \"/query\",\n {\n point: options.point.join(\",\"),\n radius: options.radius,\n layer: options.layer,\n limit: options.limit,\n },\n options.signal,\n );\n }\n}\n","/**\n * Generic async auto-pagination generator.\n *\n * Fetches pages from a callback that returns `{ items, total_count }` and\n * yields individual items until all pages are consumed or the signal is aborted.\n *\n * @example\n * ```ts\n * for await (const feature of paginate(\n * (offset, signal) => chrt.features({ bbox, limit: 100, offset, signal }),\n * f => f.features,\n * f => f.total_count ?? 0,\n * )) {\n * console.log(feature);\n * }\n * ```\n */\nexport async function* paginate<TResponse, TItem>(\n fetchPage: (offset: number, signal?: AbortSignal) => Promise<TResponse>,\n getItems: (response: TResponse) => TItem[],\n getTotalCount: (response: TResponse) => number,\n options?: { pageSize?: number; signal?: AbortSignal },\n): AsyncGenerator<TItem, void, undefined> {\n const pageSize = options?.pageSize ?? 100;\n const signal = options?.signal;\n let offset = 0;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (signal?.aborted) return;\n\n const response = await fetchPage(offset, signal);\n const items = getItems(response);\n\n for (const item of items) {\n yield item;\n }\n\n if (items.length === 0) return;\n\n offset += items.length;\n const total = getTotalCount(response);\n if (total > 0 && offset >= total) return;\n }\n}\n","/* ── Webhook event types ───────────────────────────── */\n\n/** Base envelope for all webhook payloads */\nexport interface WebhookPayload<T extends string, D> {\n /** Unique event ID */\n id: string;\n /** ISO 8601 timestamp */\n timestamp: string;\n /** Event type discriminator */\n type: T;\n /** Event-specific data */\n data: D;\n}\n\n/* ── Event data shapes ─────────────────────────────── */\n\nexport interface CellEventData {\n cell_id: string;\n chart_source: string;\n title: string | null;\n edition: number | null;\n update_number: number | null;\n scale_band: string | null;\n}\n\nexport interface CellUpdatedData extends CellEventData {\n previous_edition: number | null;\n previous_update_number: number | null;\n}\n\n/* ── Concrete event types ──────────────────────────── */\n\nexport type CellUpdatedEvent = WebhookPayload<\"cell.updated\", CellUpdatedData>;\nexport type CellNewEvent = WebhookPayload<\"cell.new\", CellEventData>;\nexport type CellDeletedEvent = WebhookPayload<\"cell.deleted\", CellEventData>;\n\n/** Discriminated union of all webhook events */\nexport type WebhookEvent = CellUpdatedEvent | CellNewEvent | CellDeletedEvent;\n\n/** All recognised webhook event type strings */\nexport type WebhookEventType = WebhookEvent[\"type\"];\n\n/* ── Runtime parser ────────────────────────────────── */\n\nconst VALID_TYPES = new Set<string>([\"cell.updated\", \"cell.new\", \"cell.deleted\"]);\n\n/**\n * Parse and validate a raw webhook payload.\n *\n * @param body - The raw request body (parsed JSON object)\n * @returns A typed `WebhookEvent`\n * @throws Error if the payload is malformed or has an unknown event type\n *\n * @example\n * ```ts\n * import { parseWebhook } from \"@chrtco/sdk\";\n *\n * app.post(\"/webhooks/chrt\", (req, res) => {\n * const event = parseWebhook(req.body);\n * switch (event.type) {\n * case \"cell.updated\":\n * console.log(\"Cell updated:\", event.data.cell_id);\n * break;\n * case \"cell.new\":\n * console.log(\"New cell:\", event.data.cell_id);\n * break;\n * }\n * res.sendStatus(200);\n * });\n * ```\n */\nexport function parseWebhook(body: unknown): WebhookEvent {\n if (typeof body !== \"object\" || body === null) {\n throw new Error(\"Invalid webhook payload: expected an object\");\n }\n\n const obj = body as Record<string, unknown>;\n\n if (typeof obj.id !== \"string\") {\n throw new Error(\"Invalid webhook payload: missing 'id'\");\n }\n if (typeof obj.timestamp !== \"string\") {\n throw new Error(\"Invalid webhook payload: missing 'timestamp'\");\n }\n if (typeof obj.type !== \"string\" || !VALID_TYPES.has(obj.type)) {\n throw new Error(`Invalid webhook payload: unknown type '${String(obj.type)}'`);\n }\n if (typeof obj.data !== \"object\" || obj.data === null) {\n throw new Error(\"Invalid webhook payload: missing 'data'\");\n }\n\n return obj as unknown as WebhookEvent;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -23,6 +23,8 @@ interface FeaturesOptions {
|
|
|
23
23
|
limit?: number;
|
|
24
24
|
/** Pagination offset */
|
|
25
25
|
offset?: number;
|
|
26
|
+
/** AbortSignal to cancel the request */
|
|
27
|
+
signal?: AbortSignal;
|
|
26
28
|
}
|
|
27
29
|
interface QueryOptions {
|
|
28
30
|
/** [longitude, latitude] */
|
|
@@ -32,6 +34,8 @@ interface QueryOptions {
|
|
|
32
34
|
/** Comma-separated layer codes */
|
|
33
35
|
layer?: string;
|
|
34
36
|
limit?: number;
|
|
37
|
+
/** AbortSignal to cancel the request */
|
|
38
|
+
signal?: AbortSignal;
|
|
35
39
|
}
|
|
36
40
|
interface CellsOptions {
|
|
37
41
|
/** Filter by chart source, e.g. "NOAA" */
|
|
@@ -40,6 +44,8 @@ interface CellsOptions {
|
|
|
40
44
|
status?: string;
|
|
41
45
|
limit?: number;
|
|
42
46
|
offset?: number;
|
|
47
|
+
/** AbortSignal to cancel the request */
|
|
48
|
+
signal?: AbortSignal;
|
|
43
49
|
}
|
|
44
50
|
interface CellDetail {
|
|
45
51
|
cell_id: string;
|
|
@@ -62,6 +68,30 @@ interface CellsResponse {
|
|
|
62
68
|
limit: number;
|
|
63
69
|
offset: number;
|
|
64
70
|
}
|
|
71
|
+
interface CoverageSource {
|
|
72
|
+
/** Chart source ID, e.g. "NOAA" */
|
|
73
|
+
source: string;
|
|
74
|
+
/** ISO 3166-1 alpha-2 country code */
|
|
75
|
+
country: string;
|
|
76
|
+
/** Display name of the agency */
|
|
77
|
+
agency: string;
|
|
78
|
+
/** Number of available ENC cells */
|
|
79
|
+
cell_count: number;
|
|
80
|
+
/** Scale bands with cell counts */
|
|
81
|
+
scale_bands?: Record<string, number>;
|
|
82
|
+
}
|
|
83
|
+
interface CoverageResponse {
|
|
84
|
+
sources: CoverageSource[];
|
|
85
|
+
total_cells: number;
|
|
86
|
+
}
|
|
87
|
+
interface CoverageOptions {
|
|
88
|
+
/** Filter by country code */
|
|
89
|
+
country?: string;
|
|
90
|
+
/** Filter by chart source */
|
|
91
|
+
source?: string;
|
|
92
|
+
/** AbortSignal to cancel the request */
|
|
93
|
+
signal?: AbortSignal;
|
|
94
|
+
}
|
|
65
95
|
interface TileGroup {
|
|
66
96
|
id: string;
|
|
67
97
|
display_name: string;
|
|
@@ -102,7 +132,7 @@ declare class HttpClient {
|
|
|
102
132
|
constructor(apiKey: string, baseUrl?: string, retries?: number);
|
|
103
133
|
getBaseUrl(): string;
|
|
104
134
|
getApiKey(): string;
|
|
105
|
-
request<T>(path: string, params?: Record<string, string | number | undefined
|
|
135
|
+
request<T>(path: string, params?: Record<string, string | number | undefined>, signal?: AbortSignal): Promise<T>;
|
|
106
136
|
}
|
|
107
137
|
|
|
108
138
|
declare class TilesEndpoint {
|
|
@@ -122,14 +152,102 @@ declare class CellsEndpoint {
|
|
|
122
152
|
/** List ENC cells with optional filters */
|
|
123
153
|
list(options?: CellsOptions): Promise<CellsResponse>;
|
|
124
154
|
/** Get a single cell by ID */
|
|
125
|
-
get(cellId: string): Promise<CellDetail>;
|
|
155
|
+
get(cellId: string, signal?: AbortSignal): Promise<CellDetail>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
declare class CoverageEndpoint {
|
|
159
|
+
private http;
|
|
160
|
+
constructor(http: HttpClient);
|
|
161
|
+
/** List available chart sources with cell counts */
|
|
162
|
+
list(options?: CoverageOptions): Promise<CoverageResponse>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Generic async auto-pagination generator.
|
|
167
|
+
*
|
|
168
|
+
* Fetches pages from a callback that returns `{ items, total_count }` and
|
|
169
|
+
* yields individual items until all pages are consumed or the signal is aborted.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* for await (const feature of paginate(
|
|
174
|
+
* (offset, signal) => chrt.features({ bbox, limit: 100, offset, signal }),
|
|
175
|
+
* f => f.features,
|
|
176
|
+
* f => f.total_count ?? 0,
|
|
177
|
+
* )) {
|
|
178
|
+
* console.log(feature);
|
|
179
|
+
* }
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
declare function paginate<TResponse, TItem>(fetchPage: (offset: number, signal?: AbortSignal) => Promise<TResponse>, getItems: (response: TResponse) => TItem[], getTotalCount: (response: TResponse) => number, options?: {
|
|
183
|
+
pageSize?: number;
|
|
184
|
+
signal?: AbortSignal;
|
|
185
|
+
}): AsyncGenerator<TItem, void, undefined>;
|
|
186
|
+
|
|
187
|
+
/** Base envelope for all webhook payloads */
|
|
188
|
+
interface WebhookPayload<T extends string, D> {
|
|
189
|
+
/** Unique event ID */
|
|
190
|
+
id: string;
|
|
191
|
+
/** ISO 8601 timestamp */
|
|
192
|
+
timestamp: string;
|
|
193
|
+
/** Event type discriminator */
|
|
194
|
+
type: T;
|
|
195
|
+
/** Event-specific data */
|
|
196
|
+
data: D;
|
|
197
|
+
}
|
|
198
|
+
interface CellEventData {
|
|
199
|
+
cell_id: string;
|
|
200
|
+
chart_source: string;
|
|
201
|
+
title: string | null;
|
|
202
|
+
edition: number | null;
|
|
203
|
+
update_number: number | null;
|
|
204
|
+
scale_band: string | null;
|
|
205
|
+
}
|
|
206
|
+
interface CellUpdatedData extends CellEventData {
|
|
207
|
+
previous_edition: number | null;
|
|
208
|
+
previous_update_number: number | null;
|
|
126
209
|
}
|
|
210
|
+
type CellUpdatedEvent = WebhookPayload<"cell.updated", CellUpdatedData>;
|
|
211
|
+
type CellNewEvent = WebhookPayload<"cell.new", CellEventData>;
|
|
212
|
+
type CellDeletedEvent = WebhookPayload<"cell.deleted", CellEventData>;
|
|
213
|
+
/** Discriminated union of all webhook events */
|
|
214
|
+
type WebhookEvent = CellUpdatedEvent | CellNewEvent | CellDeletedEvent;
|
|
215
|
+
/** All recognised webhook event type strings */
|
|
216
|
+
type WebhookEventType = WebhookEvent["type"];
|
|
217
|
+
/**
|
|
218
|
+
* Parse and validate a raw webhook payload.
|
|
219
|
+
*
|
|
220
|
+
* @param body - The raw request body (parsed JSON object)
|
|
221
|
+
* @returns A typed `WebhookEvent`
|
|
222
|
+
* @throws Error if the payload is malformed or has an unknown event type
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* import { parseWebhook } from "@chrtco/sdk";
|
|
227
|
+
*
|
|
228
|
+
* app.post("/webhooks/chrt", (req, res) => {
|
|
229
|
+
* const event = parseWebhook(req.body);
|
|
230
|
+
* switch (event.type) {
|
|
231
|
+
* case "cell.updated":
|
|
232
|
+
* console.log("Cell updated:", event.data.cell_id);
|
|
233
|
+
* break;
|
|
234
|
+
* case "cell.new":
|
|
235
|
+
* console.log("New cell:", event.data.cell_id);
|
|
236
|
+
* break;
|
|
237
|
+
* }
|
|
238
|
+
* res.sendStatus(200);
|
|
239
|
+
* });
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
declare function parseWebhook(body: unknown): WebhookEvent;
|
|
127
243
|
|
|
128
244
|
declare class Chrt {
|
|
129
245
|
/** Tile endpoints — styleUrl, tileUrl, groups */
|
|
130
246
|
readonly tiles: TilesEndpoint;
|
|
131
247
|
/** Cell catalogue endpoints — list, get */
|
|
132
248
|
readonly cells: CellsEndpoint;
|
|
249
|
+
/** Coverage data — list available chart sources */
|
|
250
|
+
readonly coverage: CoverageEndpoint;
|
|
133
251
|
private featuresEp;
|
|
134
252
|
private queryEp;
|
|
135
253
|
private http;
|
|
@@ -138,6 +256,28 @@ declare class Chrt {
|
|
|
138
256
|
features(options: FeaturesOptions): Promise<FeatureCollection>;
|
|
139
257
|
/** Spatial proximity query around a point */
|
|
140
258
|
query(options: QueryOptions): Promise<FeatureCollection>;
|
|
259
|
+
/**
|
|
260
|
+
* Iterate all features matching a bounding-box query, auto-paginating.
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* for await (const feature of chrt.featuresIter({ bbox: [-74, 40, -73, 41] })) {
|
|
265
|
+
* console.log(feature.properties);
|
|
266
|
+
* }
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
featuresIter(options: Omit<FeaturesOptions, "offset">): AsyncIterable<GeoJsonFeature>;
|
|
270
|
+
/**
|
|
271
|
+
* Iterate all cells matching a query, auto-paginating.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* for await (const cell of chrt.cellsIter({ source: "NOAA" })) {
|
|
276
|
+
* console.log(cell.cell_id, cell.title);
|
|
277
|
+
* }
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
cellsIter(options?: Omit<CellsOptions, "offset">): AsyncIterable<CellDetail>;
|
|
141
281
|
/** Returns a style.json URL ready for Mapbox GL / MapLibre */
|
|
142
282
|
styleUrl(): string;
|
|
143
283
|
/**
|
|
@@ -153,4 +293,4 @@ declare class Chrt {
|
|
|
153
293
|
transformRequest(): (url: string) => TransformRequest | undefined;
|
|
154
294
|
}
|
|
155
295
|
|
|
156
|
-
export { type CellDetail, type CellsOptions, type CellsResponse, Chrt, type ChrtConfig, ChrtError, type FeatureCollection, type FeaturesOptions, type GeoJsonFeature, type GeoJsonGeometry, type QueryOptions, type TileGroup, type TileGroupsResponse, type TransformRequest };
|
|
296
|
+
export { type CellDeletedEvent, type CellDetail, type CellEventData, type CellNewEvent, type CellUpdatedData, type CellUpdatedEvent, type CellsOptions, type CellsResponse, Chrt, type ChrtConfig, ChrtError, type CoverageOptions, type CoverageResponse, type CoverageSource, type FeatureCollection, type FeaturesOptions, type GeoJsonFeature, type GeoJsonGeometry, type QueryOptions, type TileGroup, type TileGroupsResponse, type TransformRequest, type WebhookEvent, type WebhookEventType, type WebhookPayload, paginate, parseWebhook };
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ interface FeaturesOptions {
|
|
|
23
23
|
limit?: number;
|
|
24
24
|
/** Pagination offset */
|
|
25
25
|
offset?: number;
|
|
26
|
+
/** AbortSignal to cancel the request */
|
|
27
|
+
signal?: AbortSignal;
|
|
26
28
|
}
|
|
27
29
|
interface QueryOptions {
|
|
28
30
|
/** [longitude, latitude] */
|
|
@@ -32,6 +34,8 @@ interface QueryOptions {
|
|
|
32
34
|
/** Comma-separated layer codes */
|
|
33
35
|
layer?: string;
|
|
34
36
|
limit?: number;
|
|
37
|
+
/** AbortSignal to cancel the request */
|
|
38
|
+
signal?: AbortSignal;
|
|
35
39
|
}
|
|
36
40
|
interface CellsOptions {
|
|
37
41
|
/** Filter by chart source, e.g. "NOAA" */
|
|
@@ -40,6 +44,8 @@ interface CellsOptions {
|
|
|
40
44
|
status?: string;
|
|
41
45
|
limit?: number;
|
|
42
46
|
offset?: number;
|
|
47
|
+
/** AbortSignal to cancel the request */
|
|
48
|
+
signal?: AbortSignal;
|
|
43
49
|
}
|
|
44
50
|
interface CellDetail {
|
|
45
51
|
cell_id: string;
|
|
@@ -62,6 +68,30 @@ interface CellsResponse {
|
|
|
62
68
|
limit: number;
|
|
63
69
|
offset: number;
|
|
64
70
|
}
|
|
71
|
+
interface CoverageSource {
|
|
72
|
+
/** Chart source ID, e.g. "NOAA" */
|
|
73
|
+
source: string;
|
|
74
|
+
/** ISO 3166-1 alpha-2 country code */
|
|
75
|
+
country: string;
|
|
76
|
+
/** Display name of the agency */
|
|
77
|
+
agency: string;
|
|
78
|
+
/** Number of available ENC cells */
|
|
79
|
+
cell_count: number;
|
|
80
|
+
/** Scale bands with cell counts */
|
|
81
|
+
scale_bands?: Record<string, number>;
|
|
82
|
+
}
|
|
83
|
+
interface CoverageResponse {
|
|
84
|
+
sources: CoverageSource[];
|
|
85
|
+
total_cells: number;
|
|
86
|
+
}
|
|
87
|
+
interface CoverageOptions {
|
|
88
|
+
/** Filter by country code */
|
|
89
|
+
country?: string;
|
|
90
|
+
/** Filter by chart source */
|
|
91
|
+
source?: string;
|
|
92
|
+
/** AbortSignal to cancel the request */
|
|
93
|
+
signal?: AbortSignal;
|
|
94
|
+
}
|
|
65
95
|
interface TileGroup {
|
|
66
96
|
id: string;
|
|
67
97
|
display_name: string;
|
|
@@ -102,7 +132,7 @@ declare class HttpClient {
|
|
|
102
132
|
constructor(apiKey: string, baseUrl?: string, retries?: number);
|
|
103
133
|
getBaseUrl(): string;
|
|
104
134
|
getApiKey(): string;
|
|
105
|
-
request<T>(path: string, params?: Record<string, string | number | undefined
|
|
135
|
+
request<T>(path: string, params?: Record<string, string | number | undefined>, signal?: AbortSignal): Promise<T>;
|
|
106
136
|
}
|
|
107
137
|
|
|
108
138
|
declare class TilesEndpoint {
|
|
@@ -122,14 +152,102 @@ declare class CellsEndpoint {
|
|
|
122
152
|
/** List ENC cells with optional filters */
|
|
123
153
|
list(options?: CellsOptions): Promise<CellsResponse>;
|
|
124
154
|
/** Get a single cell by ID */
|
|
125
|
-
get(cellId: string): Promise<CellDetail>;
|
|
155
|
+
get(cellId: string, signal?: AbortSignal): Promise<CellDetail>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
declare class CoverageEndpoint {
|
|
159
|
+
private http;
|
|
160
|
+
constructor(http: HttpClient);
|
|
161
|
+
/** List available chart sources with cell counts */
|
|
162
|
+
list(options?: CoverageOptions): Promise<CoverageResponse>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Generic async auto-pagination generator.
|
|
167
|
+
*
|
|
168
|
+
* Fetches pages from a callback that returns `{ items, total_count }` and
|
|
169
|
+
* yields individual items until all pages are consumed or the signal is aborted.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* for await (const feature of paginate(
|
|
174
|
+
* (offset, signal) => chrt.features({ bbox, limit: 100, offset, signal }),
|
|
175
|
+
* f => f.features,
|
|
176
|
+
* f => f.total_count ?? 0,
|
|
177
|
+
* )) {
|
|
178
|
+
* console.log(feature);
|
|
179
|
+
* }
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
declare function paginate<TResponse, TItem>(fetchPage: (offset: number, signal?: AbortSignal) => Promise<TResponse>, getItems: (response: TResponse) => TItem[], getTotalCount: (response: TResponse) => number, options?: {
|
|
183
|
+
pageSize?: number;
|
|
184
|
+
signal?: AbortSignal;
|
|
185
|
+
}): AsyncGenerator<TItem, void, undefined>;
|
|
186
|
+
|
|
187
|
+
/** Base envelope for all webhook payloads */
|
|
188
|
+
interface WebhookPayload<T extends string, D> {
|
|
189
|
+
/** Unique event ID */
|
|
190
|
+
id: string;
|
|
191
|
+
/** ISO 8601 timestamp */
|
|
192
|
+
timestamp: string;
|
|
193
|
+
/** Event type discriminator */
|
|
194
|
+
type: T;
|
|
195
|
+
/** Event-specific data */
|
|
196
|
+
data: D;
|
|
197
|
+
}
|
|
198
|
+
interface CellEventData {
|
|
199
|
+
cell_id: string;
|
|
200
|
+
chart_source: string;
|
|
201
|
+
title: string | null;
|
|
202
|
+
edition: number | null;
|
|
203
|
+
update_number: number | null;
|
|
204
|
+
scale_band: string | null;
|
|
205
|
+
}
|
|
206
|
+
interface CellUpdatedData extends CellEventData {
|
|
207
|
+
previous_edition: number | null;
|
|
208
|
+
previous_update_number: number | null;
|
|
126
209
|
}
|
|
210
|
+
type CellUpdatedEvent = WebhookPayload<"cell.updated", CellUpdatedData>;
|
|
211
|
+
type CellNewEvent = WebhookPayload<"cell.new", CellEventData>;
|
|
212
|
+
type CellDeletedEvent = WebhookPayload<"cell.deleted", CellEventData>;
|
|
213
|
+
/** Discriminated union of all webhook events */
|
|
214
|
+
type WebhookEvent = CellUpdatedEvent | CellNewEvent | CellDeletedEvent;
|
|
215
|
+
/** All recognised webhook event type strings */
|
|
216
|
+
type WebhookEventType = WebhookEvent["type"];
|
|
217
|
+
/**
|
|
218
|
+
* Parse and validate a raw webhook payload.
|
|
219
|
+
*
|
|
220
|
+
* @param body - The raw request body (parsed JSON object)
|
|
221
|
+
* @returns A typed `WebhookEvent`
|
|
222
|
+
* @throws Error if the payload is malformed or has an unknown event type
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* import { parseWebhook } from "@chrtco/sdk";
|
|
227
|
+
*
|
|
228
|
+
* app.post("/webhooks/chrt", (req, res) => {
|
|
229
|
+
* const event = parseWebhook(req.body);
|
|
230
|
+
* switch (event.type) {
|
|
231
|
+
* case "cell.updated":
|
|
232
|
+
* console.log("Cell updated:", event.data.cell_id);
|
|
233
|
+
* break;
|
|
234
|
+
* case "cell.new":
|
|
235
|
+
* console.log("New cell:", event.data.cell_id);
|
|
236
|
+
* break;
|
|
237
|
+
* }
|
|
238
|
+
* res.sendStatus(200);
|
|
239
|
+
* });
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
declare function parseWebhook(body: unknown): WebhookEvent;
|
|
127
243
|
|
|
128
244
|
declare class Chrt {
|
|
129
245
|
/** Tile endpoints — styleUrl, tileUrl, groups */
|
|
130
246
|
readonly tiles: TilesEndpoint;
|
|
131
247
|
/** Cell catalogue endpoints — list, get */
|
|
132
248
|
readonly cells: CellsEndpoint;
|
|
249
|
+
/** Coverage data — list available chart sources */
|
|
250
|
+
readonly coverage: CoverageEndpoint;
|
|
133
251
|
private featuresEp;
|
|
134
252
|
private queryEp;
|
|
135
253
|
private http;
|
|
@@ -138,6 +256,28 @@ declare class Chrt {
|
|
|
138
256
|
features(options: FeaturesOptions): Promise<FeatureCollection>;
|
|
139
257
|
/** Spatial proximity query around a point */
|
|
140
258
|
query(options: QueryOptions): Promise<FeatureCollection>;
|
|
259
|
+
/**
|
|
260
|
+
* Iterate all features matching a bounding-box query, auto-paginating.
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* for await (const feature of chrt.featuresIter({ bbox: [-74, 40, -73, 41] })) {
|
|
265
|
+
* console.log(feature.properties);
|
|
266
|
+
* }
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
featuresIter(options: Omit<FeaturesOptions, "offset">): AsyncIterable<GeoJsonFeature>;
|
|
270
|
+
/**
|
|
271
|
+
* Iterate all cells matching a query, auto-paginating.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* for await (const cell of chrt.cellsIter({ source: "NOAA" })) {
|
|
276
|
+
* console.log(cell.cell_id, cell.title);
|
|
277
|
+
* }
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
cellsIter(options?: Omit<CellsOptions, "offset">): AsyncIterable<CellDetail>;
|
|
141
281
|
/** Returns a style.json URL ready for Mapbox GL / MapLibre */
|
|
142
282
|
styleUrl(): string;
|
|
143
283
|
/**
|
|
@@ -153,4 +293,4 @@ declare class Chrt {
|
|
|
153
293
|
transformRequest(): (url: string) => TransformRequest | undefined;
|
|
154
294
|
}
|
|
155
295
|
|
|
156
|
-
export { type CellDetail, type CellsOptions, type CellsResponse, Chrt, type ChrtConfig, ChrtError, type FeatureCollection, type FeaturesOptions, type GeoJsonFeature, type GeoJsonGeometry, type QueryOptions, type TileGroup, type TileGroupsResponse, type TransformRequest };
|
|
296
|
+
export { type CellDeletedEvent, type CellDetail, type CellEventData, type CellNewEvent, type CellUpdatedData, type CellUpdatedEvent, type CellsOptions, type CellsResponse, Chrt, type ChrtConfig, ChrtError, type CoverageOptions, type CoverageResponse, type CoverageSource, type FeatureCollection, type FeaturesOptions, type GeoJsonFeature, type GeoJsonGeometry, type QueryOptions, type TileGroup, type TileGroupsResponse, type TransformRequest, type WebhookEvent, type WebhookEventType, type WebhookPayload, paginate, parseWebhook };
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ var HttpClient = class {
|
|
|
27
27
|
getApiKey() {
|
|
28
28
|
return this.apiKey;
|
|
29
29
|
}
|
|
30
|
-
async request(path, params) {
|
|
30
|
+
async request(path, params, signal) {
|
|
31
31
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
32
32
|
if (params) {
|
|
33
33
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -36,9 +36,13 @@ var HttpClient = class {
|
|
|
36
36
|
}
|
|
37
37
|
let lastError = null;
|
|
38
38
|
for (let attempt = 0; attempt <= this.retries; attempt++) {
|
|
39
|
+
if (signal?.aborted) {
|
|
40
|
+
throw signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
41
|
+
}
|
|
39
42
|
try {
|
|
40
43
|
const res = await fetch(url.toString(), {
|
|
41
|
-
headers: { "x-api-key": this.apiKey }
|
|
44
|
+
headers: { "x-api-key": this.apiKey },
|
|
45
|
+
signal
|
|
42
46
|
});
|
|
43
47
|
if (!res.ok) {
|
|
44
48
|
let body;
|
|
@@ -57,6 +61,7 @@ var HttpClient = class {
|
|
|
57
61
|
return await res.json();
|
|
58
62
|
} catch (e) {
|
|
59
63
|
if (e instanceof ChrtError) throw e;
|
|
64
|
+
if (signal?.aborted) throw e;
|
|
60
65
|
lastError = e;
|
|
61
66
|
if (attempt < this.retries) {
|
|
62
67
|
await sleep(100 * 2 ** attempt);
|
|
@@ -97,11 +102,36 @@ var CellsEndpoint = class {
|
|
|
97
102
|
}
|
|
98
103
|
/** List ENC cells with optional filters */
|
|
99
104
|
async list(options) {
|
|
100
|
-
|
|
105
|
+
const { signal, ...params } = options ?? {};
|
|
106
|
+
return this.http.request(
|
|
107
|
+
"/cells",
|
|
108
|
+
params,
|
|
109
|
+
signal
|
|
110
|
+
);
|
|
101
111
|
}
|
|
102
112
|
/** Get a single cell by ID */
|
|
103
|
-
async get(cellId) {
|
|
104
|
-
return this.http.request(
|
|
113
|
+
async get(cellId, signal) {
|
|
114
|
+
return this.http.request(
|
|
115
|
+
`/cells/${encodeURIComponent(cellId)}`,
|
|
116
|
+
void 0,
|
|
117
|
+
signal
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// src/endpoints/coverage.ts
|
|
123
|
+
var CoverageEndpoint = class {
|
|
124
|
+
constructor(http) {
|
|
125
|
+
this.http = http;
|
|
126
|
+
}
|
|
127
|
+
/** List available chart sources with cell counts */
|
|
128
|
+
async list(options) {
|
|
129
|
+
const { signal, ...params } = options ?? {};
|
|
130
|
+
return this.http.request(
|
|
131
|
+
"/coverage",
|
|
132
|
+
params,
|
|
133
|
+
signal
|
|
134
|
+
);
|
|
105
135
|
}
|
|
106
136
|
};
|
|
107
137
|
|
|
@@ -112,12 +142,16 @@ var FeaturesEndpoint = class {
|
|
|
112
142
|
}
|
|
113
143
|
/** Query features within a bounding box */
|
|
114
144
|
async list(options) {
|
|
115
|
-
return this.http.request(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
145
|
+
return this.http.request(
|
|
146
|
+
"/features",
|
|
147
|
+
{
|
|
148
|
+
bbox: options.bbox.join(","),
|
|
149
|
+
layer: options.layer,
|
|
150
|
+
limit: options.limit,
|
|
151
|
+
offset: options.offset
|
|
152
|
+
},
|
|
153
|
+
options.signal
|
|
154
|
+
);
|
|
121
155
|
}
|
|
122
156
|
};
|
|
123
157
|
|
|
@@ -128,21 +162,68 @@ var QueryEndpoint = class {
|
|
|
128
162
|
}
|
|
129
163
|
/** Spatial proximity search around a point */
|
|
130
164
|
async nearby(options) {
|
|
131
|
-
return this.http.request(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
165
|
+
return this.http.request(
|
|
166
|
+
"/query",
|
|
167
|
+
{
|
|
168
|
+
point: options.point.join(","),
|
|
169
|
+
radius: options.radius,
|
|
170
|
+
layer: options.layer,
|
|
171
|
+
limit: options.limit
|
|
172
|
+
},
|
|
173
|
+
options.signal
|
|
174
|
+
);
|
|
137
175
|
}
|
|
138
176
|
};
|
|
139
177
|
|
|
178
|
+
// src/pagination.ts
|
|
179
|
+
async function* paginate(fetchPage, getItems, getTotalCount, options) {
|
|
180
|
+
const pageSize = options?.pageSize ?? 100;
|
|
181
|
+
const signal = options?.signal;
|
|
182
|
+
let offset = 0;
|
|
183
|
+
while (true) {
|
|
184
|
+
if (signal?.aborted) return;
|
|
185
|
+
const response = await fetchPage(offset, signal);
|
|
186
|
+
const items = getItems(response);
|
|
187
|
+
for (const item of items) {
|
|
188
|
+
yield item;
|
|
189
|
+
}
|
|
190
|
+
if (items.length === 0) return;
|
|
191
|
+
offset += items.length;
|
|
192
|
+
const total = getTotalCount(response);
|
|
193
|
+
if (total > 0 && offset >= total) return;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/webhooks.ts
|
|
198
|
+
var VALID_TYPES = /* @__PURE__ */ new Set(["cell.updated", "cell.new", "cell.deleted"]);
|
|
199
|
+
function parseWebhook(body) {
|
|
200
|
+
if (typeof body !== "object" || body === null) {
|
|
201
|
+
throw new Error("Invalid webhook payload: expected an object");
|
|
202
|
+
}
|
|
203
|
+
const obj = body;
|
|
204
|
+
if (typeof obj.id !== "string") {
|
|
205
|
+
throw new Error("Invalid webhook payload: missing 'id'");
|
|
206
|
+
}
|
|
207
|
+
if (typeof obj.timestamp !== "string") {
|
|
208
|
+
throw new Error("Invalid webhook payload: missing 'timestamp'");
|
|
209
|
+
}
|
|
210
|
+
if (typeof obj.type !== "string" || !VALID_TYPES.has(obj.type)) {
|
|
211
|
+
throw new Error(`Invalid webhook payload: unknown type '${String(obj.type)}'`);
|
|
212
|
+
}
|
|
213
|
+
if (typeof obj.data !== "object" || obj.data === null) {
|
|
214
|
+
throw new Error("Invalid webhook payload: missing 'data'");
|
|
215
|
+
}
|
|
216
|
+
return obj;
|
|
217
|
+
}
|
|
218
|
+
|
|
140
219
|
// src/index.ts
|
|
141
220
|
var Chrt = class {
|
|
142
221
|
/** Tile endpoints — styleUrl, tileUrl, groups */
|
|
143
222
|
tiles;
|
|
144
223
|
/** Cell catalogue endpoints — list, get */
|
|
145
224
|
cells;
|
|
225
|
+
/** Coverage data — list available chart sources */
|
|
226
|
+
coverage;
|
|
146
227
|
featuresEp;
|
|
147
228
|
queryEp;
|
|
148
229
|
http;
|
|
@@ -151,6 +232,7 @@ var Chrt = class {
|
|
|
151
232
|
this.http = new HttpClient(config.apiKey, config.baseUrl, config.retries);
|
|
152
233
|
this.tiles = new TilesEndpoint(this.http);
|
|
153
234
|
this.cells = new CellsEndpoint(this.http);
|
|
235
|
+
this.coverage = new CoverageEndpoint(this.http);
|
|
154
236
|
this.featuresEp = new FeaturesEndpoint(this.http);
|
|
155
237
|
this.queryEp = new QueryEndpoint(this.http);
|
|
156
238
|
}
|
|
@@ -163,6 +245,45 @@ var Chrt = class {
|
|
|
163
245
|
async query(options) {
|
|
164
246
|
return this.queryEp.nearby(options);
|
|
165
247
|
}
|
|
248
|
+
/* ── Auto-pagination iterators ───────────────────── */
|
|
249
|
+
/**
|
|
250
|
+
* Iterate all features matching a bounding-box query, auto-paginating.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* for await (const feature of chrt.featuresIter({ bbox: [-74, 40, -73, 41] })) {
|
|
255
|
+
* console.log(feature.properties);
|
|
256
|
+
* }
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
featuresIter(options) {
|
|
260
|
+
const pageSize = options.limit ?? 100;
|
|
261
|
+
return paginate(
|
|
262
|
+
(offset, signal) => this.features({ ...options, limit: pageSize, offset, signal }),
|
|
263
|
+
(r) => r.features,
|
|
264
|
+
(r) => r.total_count ?? 0,
|
|
265
|
+
{ pageSize, signal: options.signal }
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Iterate all cells matching a query, auto-paginating.
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```ts
|
|
273
|
+
* for await (const cell of chrt.cellsIter({ source: "NOAA" })) {
|
|
274
|
+
* console.log(cell.cell_id, cell.title);
|
|
275
|
+
* }
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
cellsIter(options) {
|
|
279
|
+
const pageSize = options?.limit ?? 100;
|
|
280
|
+
return paginate(
|
|
281
|
+
(offset, signal) => this.cells.list({ ...options, limit: pageSize, offset, signal }),
|
|
282
|
+
(r) => r.cells,
|
|
283
|
+
(r) => r.total_count,
|
|
284
|
+
{ pageSize, signal: options?.signal }
|
|
285
|
+
);
|
|
286
|
+
}
|
|
166
287
|
/* ── Map integration helpers ─────────────────────── */
|
|
167
288
|
/** Returns a style.json URL ready for Mapbox GL / MapLibre */
|
|
168
289
|
styleUrl() {
|
|
@@ -190,6 +311,8 @@ var Chrt = class {
|
|
|
190
311
|
};
|
|
191
312
|
export {
|
|
192
313
|
Chrt,
|
|
193
|
-
ChrtError
|
|
314
|
+
ChrtError,
|
|
315
|
+
paginate,
|
|
316
|
+
parseWebhook
|
|
194
317
|
};
|
|
195
318
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/endpoints/tiles.ts","../src/endpoints/cells.ts","../src/endpoints/features.ts","../src/endpoints/query.ts","../src/index.ts"],"sourcesContent":["import type { ChrtErrorBody } from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.chrt.co/v1\";\n\nexport class ChrtError extends Error {\n status: number;\n code?: string;\n body?: ChrtErrorBody;\n\n constructor(status: number, body?: ChrtErrorBody) {\n super(body?.message ?? body?.error ?? `CHRT API error ${status}`);\n this.name = \"ChrtError\";\n this.status = status;\n this.code = body?.code;\n this.body = body;\n }\n}\n\nexport class HttpClient {\n private apiKey: string;\n private baseUrl: string;\n private retries: number;\n\n constructor(apiKey: string, baseUrl?: string, retries = 2) {\n this.apiKey = apiKey;\n this.baseUrl = (baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.retries = retries;\n }\n\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n getApiKey(): string {\n return this.apiKey;\n }\n\n async request<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.retries; attempt++) {\n try {\n const res = await fetch(url.toString(), {\n headers: { \"x-api-key\": this.apiKey },\n });\n\n if (!res.ok) {\n let body: ChrtErrorBody | undefined;\n try {\n body = await res.json() as ChrtErrorBody;\n } catch {\n /* empty */\n }\n const err = new ChrtError(res.status, body);\n\n // Only retry on 5xx\n if (res.status >= 500 && attempt < this.retries) {\n lastError = err;\n await sleep(100 * 2 ** attempt);\n continue;\n }\n throw err;\n }\n\n return (await res.json()) as T;\n } catch (e) {\n if (e instanceof ChrtError) throw e;\n lastError = e as Error;\n if (attempt < this.retries) {\n await sleep(100 * 2 ** attempt);\n continue;\n }\n }\n }\n\n throw lastError ?? new Error(\"CHRT API request failed\");\n }\n}\n\nfunction sleep(ms: number) {\n return new Promise((r) => setTimeout(r, ms));\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { TileGroupsResponse } from \"../types.js\";\n\nexport class TilesEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Full style.json URL with API key as query param */\n styleUrl(): string {\n return `${this.http.getBaseUrl()}/tiles/style.json?key=${this.http.getApiKey()}`;\n }\n\n /** Individual tile URL template */\n tileUrl(z: number, x: number, y: number, format: \"pbf\" | \"mvt\" = \"mvt\"): string {\n return `${this.http.getBaseUrl()}/tiles/${z}/${x}/${y}.${format}?key=${this.http.getApiKey()}`;\n }\n\n /** List available tileset groups */\n async groups(): Promise<TileGroupsResponse> {\n return this.http.request<TileGroupsResponse>(\"/tiles/groups\");\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { CellsOptions, CellsResponse, CellDetail } from \"../types.js\";\n\nexport class CellsEndpoint {\n constructor(private http: HttpClient) {}\n\n /** List ENC cells with optional filters */\n async list(options?: CellsOptions): Promise<CellsResponse> {\n return this.http.request<CellsResponse>(\"/cells\", options as Record<string, string | number | undefined>);\n }\n\n /** Get a single cell by ID */\n async get(cellId: string): Promise<CellDetail> {\n return this.http.request<CellDetail>(`/cells/${encodeURIComponent(cellId)}`);\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { FeaturesOptions, FeatureCollection } from \"../types.js\";\n\nexport class FeaturesEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Query features within a bounding box */\n async list(options: FeaturesOptions): Promise<FeatureCollection> {\n return this.http.request<FeatureCollection>(\"/features\", {\n bbox: options.bbox.join(\",\"),\n layer: options.layer,\n limit: options.limit,\n offset: options.offset,\n });\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { QueryOptions, FeatureCollection } from \"../types.js\";\n\nexport class QueryEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Spatial proximity search around a point */\n async nearby(options: QueryOptions): Promise<FeatureCollection> {\n return this.http.request<FeatureCollection>(\"/query\", {\n point: options.point.join(\",\"),\n radius: options.radius,\n layer: options.layer,\n limit: options.limit,\n });\n }\n}\n","import { HttpClient, ChrtError } from \"./client.js\";\nimport { TilesEndpoint } from \"./endpoints/tiles.js\";\nimport { CellsEndpoint } from \"./endpoints/cells.js\";\nimport { FeaturesEndpoint } from \"./endpoints/features.js\";\nimport { QueryEndpoint } from \"./endpoints/query.js\";\nimport type {\n ChrtConfig,\n FeaturesOptions,\n QueryOptions,\n CellsOptions,\n FeatureCollection,\n CellsResponse,\n CellDetail,\n TransformRequest,\n} from \"./types.js\";\n\nexport class Chrt {\n /** Tile endpoints — styleUrl, tileUrl, groups */\n readonly tiles: TilesEndpoint;\n /** Cell catalogue endpoints — list, get */\n readonly cells: CellsEndpoint;\n\n private featuresEp: FeaturesEndpoint;\n private queryEp: QueryEndpoint;\n private http: HttpClient;\n\n constructor(apiKeyOrConfig: string | ChrtConfig) {\n const config =\n typeof apiKeyOrConfig === \"string\"\n ? { apiKey: apiKeyOrConfig }\n : apiKeyOrConfig;\n\n this.http = new HttpClient(config.apiKey, config.baseUrl, config.retries);\n this.tiles = new TilesEndpoint(this.http);\n this.cells = new CellsEndpoint(this.http);\n this.featuresEp = new FeaturesEndpoint(this.http);\n this.queryEp = new QueryEndpoint(this.http);\n }\n\n /* ── Convenience shortcuts ───────────────────────── */\n\n /** Query GeoJSON features by bounding box */\n async features(options: FeaturesOptions): Promise<FeatureCollection> {\n return this.featuresEp.list(options);\n }\n\n /** Spatial proximity query around a point */\n async query(options: QueryOptions): Promise<FeatureCollection> {\n return this.queryEp.nearby(options);\n }\n\n /* ── Map integration helpers ─────────────────────── */\n\n /** Returns a style.json URL ready for Mapbox GL / MapLibre */\n styleUrl(): string {\n return this.tiles.styleUrl();\n }\n\n /**\n * Returns a `transformRequest` function for Mapbox GL JS / MapLibre GL JS.\n * Injects the API key header for requests to api.chrt.co.\n *\n * @example\n * const map = new mapboxgl.Map({\n * style: chrt.styleUrl(),\n * transformRequest: chrt.transformRequest(),\n * });\n */\n transformRequest(): (url: string) => TransformRequest | undefined {\n const apiKey = this.http.getApiKey();\n return (url: string) => {\n if (url.includes(\"api.chrt.co\")) {\n return { url, headers: { \"x-api-key\": apiKey } };\n }\n return undefined;\n };\n }\n}\n\n/* ── Re-exports ────────────────────────────────────── */\n\nexport { ChrtError } from \"./client.js\";\nexport type {\n ChrtConfig,\n FeaturesOptions,\n QueryOptions,\n CellsOptions,\n FeatureCollection,\n GeoJsonFeature,\n GeoJsonGeometry,\n CellsResponse,\n CellDetail,\n TileGroup,\n TileGroupsResponse,\n TransformRequest,\n} from \"./types.js\";\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/endpoints/tiles.ts","../src/endpoints/cells.ts","../src/endpoints/coverage.ts","../src/endpoints/features.ts","../src/endpoints/query.ts","../src/pagination.ts","../src/webhooks.ts","../src/index.ts"],"sourcesContent":["import type { ChrtErrorBody } from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.chrt.co/v1\";\n\nexport class ChrtError extends Error {\n status: number;\n code?: string;\n body?: ChrtErrorBody;\n\n constructor(status: number, body?: ChrtErrorBody) {\n super(body?.message ?? body?.error ?? `CHRT API error ${status}`);\n this.name = \"ChrtError\";\n this.status = status;\n this.code = body?.code;\n this.body = body;\n }\n}\n\nexport class HttpClient {\n private apiKey: string;\n private baseUrl: string;\n private retries: number;\n\n constructor(apiKey: string, baseUrl?: string, retries = 2) {\n this.apiKey = apiKey;\n this.baseUrl = (baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.retries = retries;\n }\n\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n getApiKey(): string {\n return this.apiKey;\n }\n\n async request<T>(\n path: string,\n params?: Record<string, string | number | undefined>,\n signal?: AbortSignal,\n ): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.retries; attempt++) {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException(\"The operation was aborted.\", \"AbortError\");\n }\n\n try {\n const res = await fetch(url.toString(), {\n headers: { \"x-api-key\": this.apiKey },\n signal,\n });\n\n if (!res.ok) {\n let body: ChrtErrorBody | undefined;\n try {\n body = await res.json() as ChrtErrorBody;\n } catch {\n /* empty */\n }\n const err = new ChrtError(res.status, body);\n\n // Only retry on 5xx\n if (res.status >= 500 && attempt < this.retries) {\n lastError = err;\n await sleep(100 * 2 ** attempt);\n continue;\n }\n throw err;\n }\n\n return (await res.json()) as T;\n } catch (e) {\n if (e instanceof ChrtError) throw e;\n // Don't retry aborted requests\n if (signal?.aborted) throw e;\n lastError = e as Error;\n if (attempt < this.retries) {\n await sleep(100 * 2 ** attempt);\n continue;\n }\n }\n }\n\n throw lastError ?? new Error(\"CHRT API request failed\");\n }\n}\n\nfunction sleep(ms: number) {\n return new Promise((r) => setTimeout(r, ms));\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { TileGroupsResponse } from \"../types.js\";\n\nexport class TilesEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Full style.json URL with API key as query param */\n styleUrl(): string {\n return `${this.http.getBaseUrl()}/tiles/style.json?key=${this.http.getApiKey()}`;\n }\n\n /** Individual tile URL template */\n tileUrl(z: number, x: number, y: number, format: \"pbf\" | \"mvt\" = \"mvt\"): string {\n return `${this.http.getBaseUrl()}/tiles/${z}/${x}/${y}.${format}?key=${this.http.getApiKey()}`;\n }\n\n /** List available tileset groups */\n async groups(): Promise<TileGroupsResponse> {\n return this.http.request<TileGroupsResponse>(\"/tiles/groups\");\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { CellsOptions, CellsResponse, CellDetail } from \"../types.js\";\n\nexport class CellsEndpoint {\n constructor(private http: HttpClient) {}\n\n /** List ENC cells with optional filters */\n async list(options?: CellsOptions): Promise<CellsResponse> {\n const { signal, ...params } = options ?? {};\n return this.http.request<CellsResponse>(\n \"/cells\",\n params as Record<string, string | number | undefined>,\n signal,\n );\n }\n\n /** Get a single cell by ID */\n async get(cellId: string, signal?: AbortSignal): Promise<CellDetail> {\n return this.http.request<CellDetail>(\n `/cells/${encodeURIComponent(cellId)}`,\n undefined,\n signal,\n );\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { CoverageOptions, CoverageResponse } from \"../types.js\";\n\nexport class CoverageEndpoint {\n constructor(private http: HttpClient) {}\n\n /** List available chart sources with cell counts */\n async list(options?: CoverageOptions): Promise<CoverageResponse> {\n const { signal, ...params } = options ?? {};\n return this.http.request<CoverageResponse>(\n \"/coverage\",\n params as Record<string, string | number | undefined>,\n signal,\n );\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { FeaturesOptions, FeatureCollection } from \"../types.js\";\n\nexport class FeaturesEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Query features within a bounding box */\n async list(options: FeaturesOptions): Promise<FeatureCollection> {\n return this.http.request<FeatureCollection>(\n \"/features\",\n {\n bbox: options.bbox.join(\",\"),\n layer: options.layer,\n limit: options.limit,\n offset: options.offset,\n },\n options.signal,\n );\n }\n}\n","import type { HttpClient } from \"../client.js\";\nimport type { QueryOptions, FeatureCollection } from \"../types.js\";\n\nexport class QueryEndpoint {\n constructor(private http: HttpClient) {}\n\n /** Spatial proximity search around a point */\n async nearby(options: QueryOptions): Promise<FeatureCollection> {\n return this.http.request<FeatureCollection>(\n \"/query\",\n {\n point: options.point.join(\",\"),\n radius: options.radius,\n layer: options.layer,\n limit: options.limit,\n },\n options.signal,\n );\n }\n}\n","/**\n * Generic async auto-pagination generator.\n *\n * Fetches pages from a callback that returns `{ items, total_count }` and\n * yields individual items until all pages are consumed or the signal is aborted.\n *\n * @example\n * ```ts\n * for await (const feature of paginate(\n * (offset, signal) => chrt.features({ bbox, limit: 100, offset, signal }),\n * f => f.features,\n * f => f.total_count ?? 0,\n * )) {\n * console.log(feature);\n * }\n * ```\n */\nexport async function* paginate<TResponse, TItem>(\n fetchPage: (offset: number, signal?: AbortSignal) => Promise<TResponse>,\n getItems: (response: TResponse) => TItem[],\n getTotalCount: (response: TResponse) => number,\n options?: { pageSize?: number; signal?: AbortSignal },\n): AsyncGenerator<TItem, void, undefined> {\n const pageSize = options?.pageSize ?? 100;\n const signal = options?.signal;\n let offset = 0;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (signal?.aborted) return;\n\n const response = await fetchPage(offset, signal);\n const items = getItems(response);\n\n for (const item of items) {\n yield item;\n }\n\n if (items.length === 0) return;\n\n offset += items.length;\n const total = getTotalCount(response);\n if (total > 0 && offset >= total) return;\n }\n}\n","/* ── Webhook event types ───────────────────────────── */\n\n/** Base envelope for all webhook payloads */\nexport interface WebhookPayload<T extends string, D> {\n /** Unique event ID */\n id: string;\n /** ISO 8601 timestamp */\n timestamp: string;\n /** Event type discriminator */\n type: T;\n /** Event-specific data */\n data: D;\n}\n\n/* ── Event data shapes ─────────────────────────────── */\n\nexport interface CellEventData {\n cell_id: string;\n chart_source: string;\n title: string | null;\n edition: number | null;\n update_number: number | null;\n scale_band: string | null;\n}\n\nexport interface CellUpdatedData extends CellEventData {\n previous_edition: number | null;\n previous_update_number: number | null;\n}\n\n/* ── Concrete event types ──────────────────────────── */\n\nexport type CellUpdatedEvent = WebhookPayload<\"cell.updated\", CellUpdatedData>;\nexport type CellNewEvent = WebhookPayload<\"cell.new\", CellEventData>;\nexport type CellDeletedEvent = WebhookPayload<\"cell.deleted\", CellEventData>;\n\n/** Discriminated union of all webhook events */\nexport type WebhookEvent = CellUpdatedEvent | CellNewEvent | CellDeletedEvent;\n\n/** All recognised webhook event type strings */\nexport type WebhookEventType = WebhookEvent[\"type\"];\n\n/* ── Runtime parser ────────────────────────────────── */\n\nconst VALID_TYPES = new Set<string>([\"cell.updated\", \"cell.new\", \"cell.deleted\"]);\n\n/**\n * Parse and validate a raw webhook payload.\n *\n * @param body - The raw request body (parsed JSON object)\n * @returns A typed `WebhookEvent`\n * @throws Error if the payload is malformed or has an unknown event type\n *\n * @example\n * ```ts\n * import { parseWebhook } from \"@chrtco/sdk\";\n *\n * app.post(\"/webhooks/chrt\", (req, res) => {\n * const event = parseWebhook(req.body);\n * switch (event.type) {\n * case \"cell.updated\":\n * console.log(\"Cell updated:\", event.data.cell_id);\n * break;\n * case \"cell.new\":\n * console.log(\"New cell:\", event.data.cell_id);\n * break;\n * }\n * res.sendStatus(200);\n * });\n * ```\n */\nexport function parseWebhook(body: unknown): WebhookEvent {\n if (typeof body !== \"object\" || body === null) {\n throw new Error(\"Invalid webhook payload: expected an object\");\n }\n\n const obj = body as Record<string, unknown>;\n\n if (typeof obj.id !== \"string\") {\n throw new Error(\"Invalid webhook payload: missing 'id'\");\n }\n if (typeof obj.timestamp !== \"string\") {\n throw new Error(\"Invalid webhook payload: missing 'timestamp'\");\n }\n if (typeof obj.type !== \"string\" || !VALID_TYPES.has(obj.type)) {\n throw new Error(`Invalid webhook payload: unknown type '${String(obj.type)}'`);\n }\n if (typeof obj.data !== \"object\" || obj.data === null) {\n throw new Error(\"Invalid webhook payload: missing 'data'\");\n }\n\n return obj as unknown as WebhookEvent;\n}\n","import { HttpClient, ChrtError } from \"./client.js\";\nimport { TilesEndpoint } from \"./endpoints/tiles.js\";\nimport { CellsEndpoint } from \"./endpoints/cells.js\";\nimport { CoverageEndpoint } from \"./endpoints/coverage.js\";\nimport { FeaturesEndpoint } from \"./endpoints/features.js\";\nimport { QueryEndpoint } from \"./endpoints/query.js\";\nimport { paginate } from \"./pagination.js\";\nimport type {\n ChrtConfig,\n FeaturesOptions,\n QueryOptions,\n CellsOptions,\n CoverageOptions,\n FeatureCollection,\n GeoJsonFeature,\n CellsResponse,\n CellDetail,\n CoverageResponse,\n TransformRequest,\n} from \"./types.js\";\n\nexport class Chrt {\n /** Tile endpoints — styleUrl, tileUrl, groups */\n readonly tiles: TilesEndpoint;\n /** Cell catalogue endpoints — list, get */\n readonly cells: CellsEndpoint;\n /** Coverage data — list available chart sources */\n readonly coverage: CoverageEndpoint;\n\n private featuresEp: FeaturesEndpoint;\n private queryEp: QueryEndpoint;\n private http: HttpClient;\n\n constructor(apiKeyOrConfig: string | ChrtConfig) {\n const config =\n typeof apiKeyOrConfig === \"string\"\n ? { apiKey: apiKeyOrConfig }\n : apiKeyOrConfig;\n\n this.http = new HttpClient(config.apiKey, config.baseUrl, config.retries);\n this.tiles = new TilesEndpoint(this.http);\n this.cells = new CellsEndpoint(this.http);\n this.coverage = new CoverageEndpoint(this.http);\n this.featuresEp = new FeaturesEndpoint(this.http);\n this.queryEp = new QueryEndpoint(this.http);\n }\n\n /* ── Convenience shortcuts ───────────────────────── */\n\n /** Query GeoJSON features by bounding box */\n async features(options: FeaturesOptions): Promise<FeatureCollection> {\n return this.featuresEp.list(options);\n }\n\n /** Spatial proximity query around a point */\n async query(options: QueryOptions): Promise<FeatureCollection> {\n return this.queryEp.nearby(options);\n }\n\n /* ── Auto-pagination iterators ───────────────────── */\n\n /**\n * Iterate all features matching a bounding-box query, auto-paginating.\n *\n * @example\n * ```ts\n * for await (const feature of chrt.featuresIter({ bbox: [-74, 40, -73, 41] })) {\n * console.log(feature.properties);\n * }\n * ```\n */\n featuresIter(\n options: Omit<FeaturesOptions, \"offset\">,\n ): AsyncIterable<GeoJsonFeature> {\n const pageSize = options.limit ?? 100;\n return paginate<FeatureCollection, GeoJsonFeature>(\n (offset, signal) =>\n this.features({ ...options, limit: pageSize, offset, signal }),\n (r) => r.features,\n (r) => r.total_count ?? 0,\n { pageSize, signal: options.signal },\n );\n }\n\n /**\n * Iterate all cells matching a query, auto-paginating.\n *\n * @example\n * ```ts\n * for await (const cell of chrt.cellsIter({ source: \"NOAA\" })) {\n * console.log(cell.cell_id, cell.title);\n * }\n * ```\n */\n cellsIter(\n options?: Omit<CellsOptions, \"offset\">,\n ): AsyncIterable<CellDetail> {\n const pageSize = options?.limit ?? 100;\n return paginate<CellsResponse, CellDetail>(\n (offset, signal) =>\n this.cells.list({ ...options, limit: pageSize, offset, signal }),\n (r) => r.cells,\n (r) => r.total_count,\n { pageSize, signal: options?.signal },\n );\n }\n\n /* ── Map integration helpers ─────────────────────── */\n\n /** Returns a style.json URL ready for Mapbox GL / MapLibre */\n styleUrl(): string {\n return this.tiles.styleUrl();\n }\n\n /**\n * Returns a `transformRequest` function for Mapbox GL JS / MapLibre GL JS.\n * Injects the API key header for requests to api.chrt.co.\n *\n * @example\n * const map = new mapboxgl.Map({\n * style: chrt.styleUrl(),\n * transformRequest: chrt.transformRequest(),\n * });\n */\n transformRequest(): (url: string) => TransformRequest | undefined {\n const apiKey = this.http.getApiKey();\n return (url: string) => {\n if (url.includes(\"api.chrt.co\")) {\n return { url, headers: { \"x-api-key\": apiKey } };\n }\n return undefined;\n };\n }\n}\n\n/* ── Re-exports ────────────────────────────────────── */\n\nexport { ChrtError } from \"./client.js\";\nexport { paginate } from \"./pagination.js\";\nexport { parseWebhook } from \"./webhooks.js\";\nexport type {\n ChrtConfig,\n FeaturesOptions,\n QueryOptions,\n CellsOptions,\n CoverageOptions,\n FeatureCollection,\n GeoJsonFeature,\n GeoJsonGeometry,\n CellsResponse,\n CellDetail,\n CoverageSource,\n CoverageResponse,\n TileGroup,\n TileGroupsResponse,\n TransformRequest,\n} from \"./types.js\";\nexport type {\n WebhookPayload,\n WebhookEvent,\n WebhookEventType,\n CellUpdatedEvent,\n CellNewEvent,\n CellDeletedEvent,\n CellEventData,\n CellUpdatedData,\n} from \"./webhooks.js\";\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrtco/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Official TypeScript SDK for the CHRT nautical chart API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
|
26
|
-
"url": "https://github.com/chrt-co/enc-hub",
|
|
26
|
+
"url": "git+https://github.com/chrt-co/enc-hub.git",
|
|
27
27
|
"directory": "packages/sdk"
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://chrt.co/docs/sdks",
|