@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 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
- return this.http.request("/cells", options);
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(`/cells/${encodeURIComponent(cellId)}`);
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("/features", {
143
- bbox: options.bbox.join(","),
144
- layer: options.layer,
145
- limit: options.limit,
146
- offset: options.offset
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("/query", {
159
- point: options.point.join(","),
160
- radius: options.radius,
161
- layer: options.layer,
162
- limit: options.limit
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
@@ -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>): Promise<T>;
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>): Promise<T>;
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
- return this.http.request("/cells", options);
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(`/cells/${encodeURIComponent(cellId)}`);
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("/features", {
116
- bbox: options.bbox.join(","),
117
- layer: options.layer,
118
- limit: options.limit,
119
- offset: options.offset
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("/query", {
132
- point: options.point.join(","),
133
- radius: options.radius,
134
- layer: options.layer,
135
- limit: options.limit
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.2",
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",