@seta-labs/geo 0.2.0 → 0.3.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @seta-labs/geo
2
2
 
3
+ ## 0.3.0
4
+
5
+ - `geocode`, `geocodeBatch`, `reverseGeocode` and `cadastreParcel` take
6
+ `cache: { refresh, noStore, maxAge, onlyIfCached }`, sent as
7
+ `Cache-Control` (`no-cache`, `no-store`, `max-age`, `only-if-cached`).
8
+ Without it nothing changes. New types `LookupCache` and `LookupOptions`.
9
+ - A lookup refused by `onlyIfCached` rejects with `ApiError` 504
10
+ (`code: 'NOT_CACHED'`); a batch item has `status: 'NOT_CACHED'`.
11
+
3
12
  ## 0.2.0
4
13
 
5
14
  - New `cadastreParcel({ lat, lng })` on `POST /cadastre/parcel`: what the AdE
package/README.md CHANGED
@@ -64,6 +64,13 @@ OpenAPI remains authoritative: TypeScript provides static types, not runtime
64
64
  validation of received data. `include` may be omitted and defaults to `best`
65
65
  on the service.
66
66
 
67
+ The four lookups (`geocode`, `geocodeBatch`, `reverseGeocode`,
68
+ `cadastreParcel`) take `cache` in their options, sent as `Cache-Control`:
69
+ `{ refresh: true }` asks the provider again and stores the new answer,
70
+ `{ refresh: true, noStore: true }` bypasses the store entirely, `{ maxAge }`
71
+ asks again past that many seconds, `{ onlyIfCached: true }` never asks the
72
+ provider (504 `NOT_CACHED`, or `status: 'NOT_CACHED'` for a batch item).
73
+
67
74
  HTTP errors are `ApiError`; their body is JSON when parseable, otherwise text,
68
75
  or `undefined` for an empty response. A `503` from `cadastreParcel` with
69
76
  `code: 'CADASTRE_BLOCKED'` means the cadastral map is refusing calls: its
package/dist/index.d.ts CHANGED
@@ -355,8 +355,11 @@ interface components {
355
355
  include?: 'best' | 'full';
356
356
  };
357
357
  GeocodeResultDto: {
358
- /** @enum {string} */
359
- status: 'OK' | 'NO_RESULT' | 'INVALID_QUERY';
358
+ /**
359
+ * @description NOT_CACHED: a batch item refused by Cache-Control only-if-cached.
360
+ * @enum {string}
361
+ */
362
+ status: 'OK' | 'NO_RESULT' | 'INVALID_QUERY' | 'NOT_CACHED';
360
363
  /** @description The normalized query actually used as cache key and provider input. */
361
364
  query: string | null;
362
365
  lat: number | null;
@@ -652,7 +655,10 @@ interface operations {
652
655
  GeocodeController_geocode: {
653
656
  parameters: {
654
657
  query?: never;
655
- header?: never;
658
+ header?: {
659
+ /** @description no-cache: ask the provider and store its answer; no-store: never write the store; max-age=N: a stored answer older than N seconds is asked again; only-if-cached: never ask the provider. The response says what happened in Cache-Status (hit, fwd=miss|request|stale, stored) and, on a hit, Age. */
660
+ 'Cache-Control'?: string;
661
+ };
656
662
  path?: never;
657
663
  cookie?: never;
658
664
  };
@@ -684,12 +690,22 @@ interface operations {
684
690
  };
685
691
  content?: never;
686
692
  };
693
+ /** @description NOT_CACHED: only-if-cached and no usable stored answer. */
694
+ 504: {
695
+ headers: {
696
+ [name: string]: unknown;
697
+ };
698
+ content?: never;
699
+ };
687
700
  };
688
701
  };
689
702
  GeocodeController_geocodeBatch: {
690
703
  parameters: {
691
704
  query?: never;
692
- header?: never;
705
+ header?: {
706
+ /** @description Applies to every item: no-cache, no-store, max-age=N, only-if-cached. An item only-if-cached refuses has status NOT_CACHED; the batch still answers 200. */
707
+ 'Cache-Control'?: string;
708
+ };
693
709
  path?: never;
694
710
  cookie?: never;
695
711
  };
@@ -726,7 +742,10 @@ interface operations {
726
742
  GeocodeController_reverseGeocode: {
727
743
  parameters: {
728
744
  query?: never;
729
- header?: never;
745
+ header?: {
746
+ /** @description no-cache: ask the provider and store its answer; no-store: never write the store; max-age=N: a stored answer older than N seconds is asked again; only-if-cached: never ask the provider. The response says what happened in Cache-Status (hit, fwd=miss|request|stale, stored) and, on a hit, Age. */
747
+ 'Cache-Control'?: string;
748
+ };
730
749
  path?: never;
731
750
  cookie?: never;
732
751
  };
@@ -758,12 +777,22 @@ interface operations {
758
777
  };
759
778
  content?: never;
760
779
  };
780
+ /** @description NOT_CACHED: only-if-cached and no usable stored answer. */
781
+ 504: {
782
+ headers: {
783
+ [name: string]: unknown;
784
+ };
785
+ content?: never;
786
+ };
761
787
  };
762
788
  };
763
789
  CadastreController_parcel: {
764
790
  parameters: {
765
791
  query?: never;
766
- header?: never;
792
+ header?: {
793
+ /** @description no-cache: ask the provider and store its answer; no-store: never write the store; max-age=N: a stored answer older than N seconds is asked again; only-if-cached: never ask the provider. The response says what happened in Cache-Status (hit, fwd=miss|request|stale, stored) and, on a hit, Age. */
794
+ 'Cache-Control'?: string;
795
+ };
767
796
  path?: never;
768
797
  cookie?: never;
769
798
  };
@@ -803,6 +832,13 @@ interface operations {
803
832
  };
804
833
  content?: never;
805
834
  };
835
+ /** @description NOT_CACHED: only-if-cached and no usable stored answer. */
836
+ 504: {
837
+ headers: {
838
+ [name: string]: unknown;
839
+ };
840
+ content?: never;
841
+ };
806
842
  };
807
843
  };
808
844
  TerritoryController_status: {
@@ -1065,6 +1101,23 @@ type TerritoryCompravenditeQuery = NonNullable<ApiPaths['/territory/compravendit
1065
1101
  interface RequestOptions {
1066
1102
  signal?: AbortSignal;
1067
1103
  }
1104
+ /**
1105
+ * How a lookup may use seta-geo's store, sent as `Cache-Control` (RFC 9111).
1106
+ * Without it the store answers when it can and the provider otherwise.
1107
+ */
1108
+ interface LookupCache {
1109
+ /** `no-cache`: ask the provider even with a stored answer, and store the new one. */
1110
+ refresh?: boolean;
1111
+ /** `no-store`: never write the provider's answer (with `refresh`: a pure bypass). */
1112
+ noStore?: boolean;
1113
+ /** `max-age`: a stored answer older than this many seconds is asked again. */
1114
+ maxAge?: number;
1115
+ /** `only-if-cached`: never ask the provider; 504 `NOT_CACHED` without a stored answer. */
1116
+ onlyIfCached?: boolean;
1117
+ }
1118
+ interface LookupOptions extends RequestOptions {
1119
+ cache?: LookupCache;
1120
+ }
1068
1121
  interface GeoClientOptions {
1069
1122
  /** Complete API root, including /api. Relative roots are supported in browsers. */
1070
1123
  baseUrl: string;
@@ -1083,15 +1136,16 @@ interface TerritoryClient {
1083
1136
  resolve(input: TerritoryResolveRequest, options?: RequestOptions): Promise<TerritoryResolveResponse>;
1084
1137
  }
1085
1138
  interface GeoClient {
1086
- geocode(input: GeocodeRequest, options?: RequestOptions): Promise<GeocodeResult>;
1087
- geocodeBatch(input: GeocodeBatchRequest, options?: RequestOptions): Promise<GeocodeBatchResponse>;
1088
- reverseGeocode(input: ReverseGeocodeRequest, options?: RequestOptions): Promise<ReverseGeocodeResult>;
1139
+ geocode(input: GeocodeRequest, options?: LookupOptions): Promise<GeocodeResult>;
1140
+ /** `cache` applies to every item; an item only-if-cached refuses has status NOT_CACHED. */
1141
+ geocodeBatch(input: GeocodeBatchRequest, options?: LookupOptions): Promise<GeocodeBatchResponse>;
1142
+ reverseGeocode(input: ReverseGeocodeRequest, options?: LookupOptions): Promise<ReverseGeocodeResult>;
1089
1143
  /** What the AdE cadastral map finds under a point; 503 `CADASTRE_BLOCKED` carries Retry-After. */
1090
- cadastreParcel(input: CadastreParcelRequest, options?: RequestOptions): Promise<CadastreParcelResult>;
1144
+ cadastreParcel(input: CadastreParcelRequest, options?: LookupOptions): Promise<CadastreParcelResult>;
1091
1145
  territory: TerritoryClient;
1092
1146
  }
1093
1147
 
1094
1148
  declare function createGeoClient(options: GeoClientOptions): GeoClient;
1095
1149
 
1096
1150
  export { ApiError, createGeoClient };
1097
- export type { CadastreParcelRequest, CadastreParcelResult, GeoClient, GeoClientOptions, GeocodeBatchRequest, GeocodeBatchResponse, GeocodeRequest, GeocodeResult, RequestOptions, ReverseGeocodeRequest, ReverseGeocodeResult, TerritoryClient, TerritoryCompravendita, TerritoryCompravenditePage, TerritoryCompravenditeQuery, TerritoryComune, TerritoryComuneDetail, TerritoryComuneRiga, TerritoryComuniPage, TerritoryComuniQuery, TerritoryDatasets, TerritoryOmi, TerritoryPointResult, TerritoryQuotazione, TerritoryQuotazioniPage, TerritoryQuotazioniQuery, TerritoryResolveRequest, TerritoryResolveResponse, TerritoryStatus };
1151
+ export type { CadastreParcelRequest, CadastreParcelResult, GeoClient, GeoClientOptions, GeocodeBatchRequest, GeocodeBatchResponse, GeocodeRequest, GeocodeResult, LookupCache, LookupOptions, RequestOptions, ReverseGeocodeRequest, ReverseGeocodeResult, TerritoryClient, TerritoryCompravendita, TerritoryCompravenditePage, TerritoryCompravenditeQuery, TerritoryComune, TerritoryComuneDetail, TerritoryComuneRiga, TerritoryComuniPage, TerritoryComuniQuery, TerritoryDatasets, TerritoryOmi, TerritoryPointResult, TerritoryQuotazione, TerritoryQuotazioniPage, TerritoryQuotazioniQuery, TerritoryResolveRequest, TerritoryResolveResponse, TerritoryStatus };
package/dist/index.js CHANGED
@@ -39,6 +39,21 @@ async function data(request) {
39
39
  if (result.data === void 0) throw new SyntaxError("Geo API returned an empty JSON response");
40
40
  return result.data;
41
41
  }
42
+ function cacheControl(cache) {
43
+ if (!cache) return void 0;
44
+ const directives = [
45
+ cache.refresh ? "no-cache" : void 0,
46
+ cache.noStore ? "no-store" : void 0,
47
+ cache.maxAge === void 0 ? void 0 : `max-age=${Math.max(0, Math.floor(cache.maxAge))}`,
48
+ cache.onlyIfCached ? "only-if-cached" : void 0
49
+ ].filter((directive) => directive !== void 0);
50
+ return directives.length > 0 ? directives.join(", ") : void 0;
51
+ }
52
+ function lookupInit(options) {
53
+ const { cache, ...request } = options ?? {};
54
+ const header = cacheControl(cache);
55
+ return header ? { ...request, headers: { "cache-control": header } } : request;
56
+ }
42
57
  function territory(client) {
43
58
  return {
44
59
  status: (options) => data(client.GET("/territory/status", { ...options })),
@@ -67,10 +82,10 @@ function createGeoClient(options) {
67
82
  }
68
83
  });
69
84
  return {
70
- geocode: (input, request) => data(client.POST("/geocode", { ...request, body: input })),
71
- geocodeBatch: (input, request) => data(client.POST("/geocode/batch", { ...request, body: input })),
72
- reverseGeocode: (input, request) => data(client.POST("/geocode/reverse", { ...request, body: input })),
73
- cadastreParcel: (input, request) => data(client.POST("/cadastre/parcel", { ...request, body: input })),
85
+ geocode: (input, request) => data(client.POST("/geocode", { ...lookupInit(request), body: input })),
86
+ geocodeBatch: (input, request) => data(client.POST("/geocode/batch", { ...lookupInit(request), body: input })),
87
+ reverseGeocode: (input, request) => data(client.POST("/geocode/reverse", { ...lookupInit(request), body: input })),
88
+ cadastreParcel: (input, request) => data(client.POST("/cadastre/parcel", { ...lookupInit(request), body: input })),
74
89
  territory: territory(client)
75
90
  };
76
91
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seta-labs/geo",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Typed Fetch client for the Seta Geo HTTP API",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",