@urbankitstudio/atlas 0.6.14 → 0.6.15
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/README.md +12 -2
- package/dist/index.cjs +33 -4
- package/dist/index.d.cts +70 -5
- package/dist/index.d.ts +70 -5
- package/dist/index.js +31 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,7 +67,14 @@ import { findCounty, buildParcelLookupDeepLink } from "@urbankitstudio/atlas";
|
|
|
67
67
|
|
|
68
68
|
const kane = findCounty("illinois", "kane-county")!;
|
|
69
69
|
const link = buildParcelLookupDeepLink(kane.endpoints[0]);
|
|
70
|
-
// → https://urbankitstudio.com/tools/parcel-lookup?endpoint=https%3A%2F%2F
|
|
70
|
+
// → https://urbankitstudio.com/tools/parcel-lookup?endpoint=https%3A%2F%2F...
|
|
71
|
+
|
|
72
|
+
// Pass the county record as well and its primary layer (endpoints[0]), when
|
|
73
|
+
// that layer refuses typed search or several counties share it, links to the
|
|
74
|
+
// county's own page, which scopes it. Any other endpoint keeps the generic link:
|
|
75
|
+
const alachua = findCounty("florida", "alachua-county")!;
|
|
76
|
+
buildParcelLookupDeepLink(alachua.endpoints[0], undefined, alachua);
|
|
77
|
+
// → https://urbankitstudio.com/tools/parcel-lookup/florida/alachua-county
|
|
71
78
|
|
|
72
79
|
For single-address owner lookups in the browser, the free [Owner Lookup](https://urbankitstudio.com/tools/owner-lookup) tool reads the same county layers this package indexes.
|
|
73
80
|
```
|
|
@@ -81,7 +88,10 @@ For single-address owner lookups in the browser, the free [Owner Lookup](https:/
|
|
|
81
88
|
| `findCounty(stateSlug, countySlug)` | `CountyRecord \| undefined` |
|
|
82
89
|
| `findCountyByFips(fips)` | `CountyRecord \| undefined` — match by 5-digit county FIPS |
|
|
83
90
|
| `listCountiesByState(stateSlug)` | `CountyRecord[]` |
|
|
84
|
-
| `buildParcelLookupDeepLink(endpoint)` | URL string into the UrbanKit lookup tool |
|
|
91
|
+
| `buildParcelLookupDeepLink(endpoint, base?, county?)` | URL string into the UrbanKit lookup tool; with `county`, the county route when the endpoint is that county's flagged or shared primary layer |
|
|
92
|
+
| `canonicalLayerUrl(url)` | one key per layer: host + path, scheme, default port, query, fragment, a trailing `/query` and case folded away |
|
|
93
|
+
| `offersAttributeSearch(endpoint)` | `false` when the layer refuses a `where` clause (`attributeSearch: "unsupported"`); query it by geometry instead |
|
|
94
|
+
| `countyOffersAttributeSearch(county)` | `true` when any of the county's layers takes a `where` clause |
|
|
85
95
|
| `slugify(s)`, `countySlugFromName(s)` | string helpers matching the atlas slug convention |
|
|
86
96
|
| `statePath(slug)`, `countyPath(state, county)` | URL paths under `/parcel-atlas` |
|
|
87
97
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// src/capability.ts
|
|
4
|
+
function offersAttributeSearch(endpoint) {
|
|
5
|
+
return !!endpoint && endpoint.attributeSearch !== "unsupported";
|
|
6
|
+
}
|
|
7
|
+
function countyOffersAttributeSearch(county) {
|
|
8
|
+
const endpoints = Array.isArray(county.endpoints) ? county.endpoints : [];
|
|
9
|
+
return endpoints.some(offersAttributeSearch);
|
|
10
|
+
}
|
|
4
11
|
function reviewedCapability(county, field) {
|
|
5
12
|
return county.capabilityOverrides?.[field] ?? null;
|
|
6
13
|
}
|
|
@@ -22794,12 +22801,31 @@ function countySlugFromName(county) {
|
|
|
22794
22801
|
const name = county.trim();
|
|
22795
22802
|
return slugify(COUNTY_UNIT_SUFFIX.test(name) ? name : `${name} County`);
|
|
22796
22803
|
}
|
|
22797
|
-
function
|
|
22804
|
+
function canonicalLayerUrl(url) {
|
|
22805
|
+
const raw = url.trim();
|
|
22806
|
+
let parsed;
|
|
22807
|
+
try {
|
|
22808
|
+
parsed = new URL(raw);
|
|
22809
|
+
} catch {
|
|
22810
|
+
parsed = null;
|
|
22811
|
+
}
|
|
22812
|
+
if (!parsed || parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
22813
|
+
return raw.replace(/\/+$/, "").toLowerCase();
|
|
22814
|
+
}
|
|
22815
|
+
const path = parsed.pathname.replace(/\/{2,}/g, "/").replace(/\/+$/, "").replace(/\/query$/i, "").replace(/\/+$/, "");
|
|
22816
|
+
const port = parsed.port ? `:${parsed.port}` : "";
|
|
22817
|
+
return `${parsed.hostname}${port}${path}`.toLowerCase();
|
|
22818
|
+
}
|
|
22819
|
+
function buildParcelLookupDeepLink(endpoint, base = "https://urbankitstudio.com/tools/parcel-lookup", county) {
|
|
22820
|
+
const searchable = offersAttributeSearch(endpoint);
|
|
22821
|
+
const primary = county?.endpoints ? county.endpoints[0] : endpoint;
|
|
22822
|
+
const isPrimary = !!primary && canonicalLayerUrl(primary.url) === canonicalLayerUrl(endpoint.url);
|
|
22823
|
+
if (county && isPrimary && (!searchable || endpoint.scopeWhere)) {
|
|
22824
|
+
return `${base}/${encodeURIComponent(county.stateSlug)}/${encodeURIComponent(county.countySlug)}`;
|
|
22825
|
+
}
|
|
22798
22826
|
const params = new URLSearchParams();
|
|
22799
22827
|
params.set("endpoint", endpoint.url);
|
|
22800
|
-
const ownerField = endpoint.searchFields.find(
|
|
22801
|
-
(f) => /owner|taxpayer/i.test(f.name)
|
|
22802
|
-
);
|
|
22828
|
+
const ownerField = searchable ? endpoint.searchFields.find((f) => /owner|taxpayer/i.test(f.name)) : void 0;
|
|
22803
22829
|
if (ownerField) params.set("fieldHint", ownerField.name);
|
|
22804
22830
|
return `${base}?${params.toString()}`;
|
|
22805
22831
|
}
|
|
@@ -22814,6 +22840,8 @@ exports.LIVE_STATUS_URL = LIVE_STATUS_URL;
|
|
|
22814
22840
|
exports.atlas = atlas;
|
|
22815
22841
|
exports.atlasIndex = atlasIndex;
|
|
22816
22842
|
exports.buildParcelLookupDeepLink = buildParcelLookupDeepLink;
|
|
22843
|
+
exports.canonicalLayerUrl = canonicalLayerUrl;
|
|
22844
|
+
exports.countyOffersAttributeSearch = countyOffersAttributeSearch;
|
|
22817
22845
|
exports.countyPath = countyPath;
|
|
22818
22846
|
exports.countySlugFromName = countySlugFromName;
|
|
22819
22847
|
exports.findCounty = findCounty;
|
|
@@ -22822,6 +22850,7 @@ exports.findState = findState;
|
|
|
22822
22850
|
exports.isReviewedUnservable = isReviewedUnservable;
|
|
22823
22851
|
exports.listCountiesByState = listCountiesByState;
|
|
22824
22852
|
exports.listStates = listStates;
|
|
22853
|
+
exports.offersAttributeSearch = offersAttributeSearch;
|
|
22825
22854
|
exports.reviewedCapability = reviewedCapability;
|
|
22826
22855
|
exports.slugify = slugify;
|
|
22827
22856
|
exports.statePath = statePath;
|
package/dist/index.d.cts
CHANGED
|
@@ -47,8 +47,12 @@ interface EndpointRecord {
|
|
|
47
47
|
* Declares that this layer CANNOT serve an ATTRIBUTE search - a `where`-clause
|
|
48
48
|
* query on a column, which is what "search by owner name / APN / address text"
|
|
49
49
|
* means everywhere on the site. Absent (the default, and true of every
|
|
50
|
-
* registration except the 67 Florida counties on the shared FDOR layer,
|
|
51
|
-
*
|
|
50
|
+
* registration except 68: the 67 Florida counties on the shared FDOR layer,
|
|
51
|
+
* see below, and Orleans Parish LA, whose own layer never finishes a table
|
|
52
|
+
* scan) means an attribute search IS offered.
|
|
53
|
+
*
|
|
54
|
+
* Read it through `offersAttributeSearch` / `countyOffersAttributeSearch`,
|
|
55
|
+
* exported by this package, rather than comparing the string yourself.
|
|
52
56
|
*
|
|
53
57
|
* THIS IS THE ONE FLAG. Do not add a second field, and do not express this
|
|
54
58
|
* with `searchable: false` on every column: `searchable` is a FIELD-level
|
|
@@ -65,7 +69,7 @@ interface EndpointRecord {
|
|
|
65
69
|
* owner_name to not_published would be a false claim about data we do serve
|
|
66
70
|
* and do bill for. Search and availability are different questions.
|
|
67
71
|
*
|
|
68
|
-
* THE
|
|
72
|
+
* THE SHARED-LAYER CASE, and what would retire it. Florida's FDOR statewide
|
|
69
73
|
* cadastral layer carries all 67 counties and 10.8M parcels, and CO_NO - the
|
|
70
74
|
* county code, the only column identifying a county, and therefore every
|
|
71
75
|
* Florida registration's scopeWhere - is NOT INDEXED. Its declared indexes
|
|
@@ -213,6 +217,23 @@ interface AtlasIndex {
|
|
|
213
217
|
};
|
|
214
218
|
}
|
|
215
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Can this layer take a `where`-clause search at all (owner name, address text,
|
|
222
|
+
* a field filter)? False on a layer flagged `attributeSearch: "unsupported"`,
|
|
223
|
+
* which answers SPATIAL queries only: query it by geometry, as its
|
|
224
|
+
* `sampleQuery` shows, and read the owner and address fields off the hits.
|
|
225
|
+
*
|
|
226
|
+
* This is the package's one reader of that flag, so test it rather than the
|
|
227
|
+
* raw field. It is held equal to the site's reader (offersAttributeSearch in
|
|
228
|
+
* the monorepo's src/lib/capability-core.ts) on every bundled endpoint by the
|
|
229
|
+
* monorepo's atlas-package-capability-parity test.
|
|
230
|
+
*/
|
|
231
|
+
declare function offersAttributeSearch(endpoint: Pick<EndpointRecord, "attributeSearch"> | null | undefined): boolean;
|
|
232
|
+
/**
|
|
233
|
+
* Does ANY of this county's layers take a `where` clause? Miami-Dade answers
|
|
234
|
+
* true through its own layer although its statewide FDOR layer is flagged.
|
|
235
|
+
*/
|
|
236
|
+
declare function countyOffersAttributeSearch(county: Pick<CountyRecord, "endpoints">): boolean;
|
|
216
237
|
/**
|
|
217
238
|
* The reviewed capability assertion for one field, or null when none exists.
|
|
218
239
|
*
|
|
@@ -280,7 +301,51 @@ declare function listCountiesByState(stateSlug: string): CountyRecord[];
|
|
|
280
301
|
|
|
281
302
|
declare function slugify(s: string): string;
|
|
282
303
|
declare function countySlugFromName(county: string): string;
|
|
283
|
-
|
|
304
|
+
/**
|
|
305
|
+
* One spelling for one ArcGIS layer, so two spellings of the same layer compare
|
|
306
|
+
* equal. A URL that parses as http(s) keys on host + path, with no scheme (an
|
|
307
|
+
* http and an https spelling are one layer): the host lowercased, a default
|
|
308
|
+
* port dropped, dot segments resolved, repeated slashes collapsed, the query
|
|
309
|
+
* string and fragment dropped, a trailing `/query` (the operation, not the
|
|
310
|
+
* layer) and trailing slashes stripped, and the path lowercased, since ArcGIS
|
|
311
|
+
* REST paths are case-insensitive. Anything else is trimmed, stripped of
|
|
312
|
+
* trailing slashes and lowercased. Distinct layers stay distinct:
|
|
313
|
+
* `MapServer/15`, `MapServer/1` and `MapServer/150` are three keys.
|
|
314
|
+
*
|
|
315
|
+
* The site's copy (layerKey in the monorepo's src/lib/registered-layer.ts) is
|
|
316
|
+
* held equal to this one by its registered-layer test.
|
|
317
|
+
*/
|
|
318
|
+
declare function canonicalLayerUrl(url: string): string;
|
|
319
|
+
/**
|
|
320
|
+
* The county a deep link is for. A CountyRecord satisfies it. Pass the record
|
|
321
|
+
* with its `endpoints` so the link can tell whether the endpoint is the
|
|
322
|
+
* county's primary layer; without them the caller vouches that it is.
|
|
323
|
+
*/
|
|
324
|
+
interface DeepLinkCounty {
|
|
325
|
+
stateSlug: string;
|
|
326
|
+
countySlug: string;
|
|
327
|
+
endpoints?: readonly Pick<EndpointRecord, "url">[];
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* A link into the UrbanKit parcel-lookup tool for one endpoint.
|
|
331
|
+
*
|
|
332
|
+
* Without `county` this is the generic `?endpoint=<url>` link it always was,
|
|
333
|
+
* except that `fieldHint` (the owner column to pre-select) is omitted on a
|
|
334
|
+
* layer that refuses typed search (`attributeSearch: "unsupported"`): there is
|
|
335
|
+
* no text search to pre-select a field for, and the generic route recognises
|
|
336
|
+
* a registry-flagged layer and offers none.
|
|
337
|
+
*
|
|
338
|
+
* With `county` (pass the CountyRecord itself), a flagged or SHARED layer
|
|
339
|
+
* (`scopeWhere` set) links to the county route `<base>/<state>/<county>`, but
|
|
340
|
+
* only when it IS the county's primary layer (`county.endpoints[0]`, compared
|
|
341
|
+
* by canonicalLayerUrl), because the county route always loads that one. Only
|
|
342
|
+
* the county route knows which county it is looking at, so only it can scope a
|
|
343
|
+
* shared layer and show a flagged layer as lookup by location. Any other
|
|
344
|
+
* endpoint keeps the generic link to exactly that endpoint: Broward's FDOR
|
|
345
|
+
* layer is its second, behind the county's own layer, so a county-route link
|
|
346
|
+
* for it would open a different layer.
|
|
347
|
+
*/
|
|
348
|
+
declare function buildParcelLookupDeepLink(endpoint: EndpointRecord, base?: string, county?: DeepLinkCounty): string;
|
|
284
349
|
declare function statePath(stateSlug: string): string;
|
|
285
350
|
declare function countyPath(stateSlug: string, countySlug: string): string;
|
|
286
351
|
|
|
@@ -294,4 +359,4 @@ interface Atlas {
|
|
|
294
359
|
}
|
|
295
360
|
declare const atlas: Atlas;
|
|
296
361
|
|
|
297
|
-
export { type Atlas, type AtlasIndex, type CapabilityBasis, type CapabilityBasisType, type CapabilityField, type CapabilityOverrides, type CapabilityStatus, type ContactInfo, type CountyAdvisory, type CountyRecord, type EndpointRecord, type EndpointStatus, LIVE_STATUS_URL, type LicenseType, type SearchField, type ServiceType, type StateFile, type StateIndexEntry, type VerifiedBy, atlas, atlasIndex, buildParcelLookupDeepLink, countyPath, countySlugFromName, findCounty, findCountyByFips, findState, isReviewedUnservable, listCountiesByState, listStates, reviewedCapability, slugify, statePath };
|
|
362
|
+
export { type Atlas, type AtlasIndex, type CapabilityBasis, type CapabilityBasisType, type CapabilityField, type CapabilityOverrides, type CapabilityStatus, type ContactInfo, type CountyAdvisory, type CountyRecord, type DeepLinkCounty, type EndpointRecord, type EndpointStatus, LIVE_STATUS_URL, type LicenseType, type SearchField, type ServiceType, type StateFile, type StateIndexEntry, type VerifiedBy, atlas, atlasIndex, buildParcelLookupDeepLink, canonicalLayerUrl, countyOffersAttributeSearch, countyPath, countySlugFromName, findCounty, findCountyByFips, findState, isReviewedUnservable, listCountiesByState, listStates, offersAttributeSearch, reviewedCapability, slugify, statePath };
|
package/dist/index.d.ts
CHANGED
|
@@ -47,8 +47,12 @@ interface EndpointRecord {
|
|
|
47
47
|
* Declares that this layer CANNOT serve an ATTRIBUTE search - a `where`-clause
|
|
48
48
|
* query on a column, which is what "search by owner name / APN / address text"
|
|
49
49
|
* means everywhere on the site. Absent (the default, and true of every
|
|
50
|
-
* registration except the 67 Florida counties on the shared FDOR layer,
|
|
51
|
-
*
|
|
50
|
+
* registration except 68: the 67 Florida counties on the shared FDOR layer,
|
|
51
|
+
* see below, and Orleans Parish LA, whose own layer never finishes a table
|
|
52
|
+
* scan) means an attribute search IS offered.
|
|
53
|
+
*
|
|
54
|
+
* Read it through `offersAttributeSearch` / `countyOffersAttributeSearch`,
|
|
55
|
+
* exported by this package, rather than comparing the string yourself.
|
|
52
56
|
*
|
|
53
57
|
* THIS IS THE ONE FLAG. Do not add a second field, and do not express this
|
|
54
58
|
* with `searchable: false` on every column: `searchable` is a FIELD-level
|
|
@@ -65,7 +69,7 @@ interface EndpointRecord {
|
|
|
65
69
|
* owner_name to not_published would be a false claim about data we do serve
|
|
66
70
|
* and do bill for. Search and availability are different questions.
|
|
67
71
|
*
|
|
68
|
-
* THE
|
|
72
|
+
* THE SHARED-LAYER CASE, and what would retire it. Florida's FDOR statewide
|
|
69
73
|
* cadastral layer carries all 67 counties and 10.8M parcels, and CO_NO - the
|
|
70
74
|
* county code, the only column identifying a county, and therefore every
|
|
71
75
|
* Florida registration's scopeWhere - is NOT INDEXED. Its declared indexes
|
|
@@ -213,6 +217,23 @@ interface AtlasIndex {
|
|
|
213
217
|
};
|
|
214
218
|
}
|
|
215
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Can this layer take a `where`-clause search at all (owner name, address text,
|
|
222
|
+
* a field filter)? False on a layer flagged `attributeSearch: "unsupported"`,
|
|
223
|
+
* which answers SPATIAL queries only: query it by geometry, as its
|
|
224
|
+
* `sampleQuery` shows, and read the owner and address fields off the hits.
|
|
225
|
+
*
|
|
226
|
+
* This is the package's one reader of that flag, so test it rather than the
|
|
227
|
+
* raw field. It is held equal to the site's reader (offersAttributeSearch in
|
|
228
|
+
* the monorepo's src/lib/capability-core.ts) on every bundled endpoint by the
|
|
229
|
+
* monorepo's atlas-package-capability-parity test.
|
|
230
|
+
*/
|
|
231
|
+
declare function offersAttributeSearch(endpoint: Pick<EndpointRecord, "attributeSearch"> | null | undefined): boolean;
|
|
232
|
+
/**
|
|
233
|
+
* Does ANY of this county's layers take a `where` clause? Miami-Dade answers
|
|
234
|
+
* true through its own layer although its statewide FDOR layer is flagged.
|
|
235
|
+
*/
|
|
236
|
+
declare function countyOffersAttributeSearch(county: Pick<CountyRecord, "endpoints">): boolean;
|
|
216
237
|
/**
|
|
217
238
|
* The reviewed capability assertion for one field, or null when none exists.
|
|
218
239
|
*
|
|
@@ -280,7 +301,51 @@ declare function listCountiesByState(stateSlug: string): CountyRecord[];
|
|
|
280
301
|
|
|
281
302
|
declare function slugify(s: string): string;
|
|
282
303
|
declare function countySlugFromName(county: string): string;
|
|
283
|
-
|
|
304
|
+
/**
|
|
305
|
+
* One spelling for one ArcGIS layer, so two spellings of the same layer compare
|
|
306
|
+
* equal. A URL that parses as http(s) keys on host + path, with no scheme (an
|
|
307
|
+
* http and an https spelling are one layer): the host lowercased, a default
|
|
308
|
+
* port dropped, dot segments resolved, repeated slashes collapsed, the query
|
|
309
|
+
* string and fragment dropped, a trailing `/query` (the operation, not the
|
|
310
|
+
* layer) and trailing slashes stripped, and the path lowercased, since ArcGIS
|
|
311
|
+
* REST paths are case-insensitive. Anything else is trimmed, stripped of
|
|
312
|
+
* trailing slashes and lowercased. Distinct layers stay distinct:
|
|
313
|
+
* `MapServer/15`, `MapServer/1` and `MapServer/150` are three keys.
|
|
314
|
+
*
|
|
315
|
+
* The site's copy (layerKey in the monorepo's src/lib/registered-layer.ts) is
|
|
316
|
+
* held equal to this one by its registered-layer test.
|
|
317
|
+
*/
|
|
318
|
+
declare function canonicalLayerUrl(url: string): string;
|
|
319
|
+
/**
|
|
320
|
+
* The county a deep link is for. A CountyRecord satisfies it. Pass the record
|
|
321
|
+
* with its `endpoints` so the link can tell whether the endpoint is the
|
|
322
|
+
* county's primary layer; without them the caller vouches that it is.
|
|
323
|
+
*/
|
|
324
|
+
interface DeepLinkCounty {
|
|
325
|
+
stateSlug: string;
|
|
326
|
+
countySlug: string;
|
|
327
|
+
endpoints?: readonly Pick<EndpointRecord, "url">[];
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* A link into the UrbanKit parcel-lookup tool for one endpoint.
|
|
331
|
+
*
|
|
332
|
+
* Without `county` this is the generic `?endpoint=<url>` link it always was,
|
|
333
|
+
* except that `fieldHint` (the owner column to pre-select) is omitted on a
|
|
334
|
+
* layer that refuses typed search (`attributeSearch: "unsupported"`): there is
|
|
335
|
+
* no text search to pre-select a field for, and the generic route recognises
|
|
336
|
+
* a registry-flagged layer and offers none.
|
|
337
|
+
*
|
|
338
|
+
* With `county` (pass the CountyRecord itself), a flagged or SHARED layer
|
|
339
|
+
* (`scopeWhere` set) links to the county route `<base>/<state>/<county>`, but
|
|
340
|
+
* only when it IS the county's primary layer (`county.endpoints[0]`, compared
|
|
341
|
+
* by canonicalLayerUrl), because the county route always loads that one. Only
|
|
342
|
+
* the county route knows which county it is looking at, so only it can scope a
|
|
343
|
+
* shared layer and show a flagged layer as lookup by location. Any other
|
|
344
|
+
* endpoint keeps the generic link to exactly that endpoint: Broward's FDOR
|
|
345
|
+
* layer is its second, behind the county's own layer, so a county-route link
|
|
346
|
+
* for it would open a different layer.
|
|
347
|
+
*/
|
|
348
|
+
declare function buildParcelLookupDeepLink(endpoint: EndpointRecord, base?: string, county?: DeepLinkCounty): string;
|
|
284
349
|
declare function statePath(stateSlug: string): string;
|
|
285
350
|
declare function countyPath(stateSlug: string, countySlug: string): string;
|
|
286
351
|
|
|
@@ -294,4 +359,4 @@ interface Atlas {
|
|
|
294
359
|
}
|
|
295
360
|
declare const atlas: Atlas;
|
|
296
361
|
|
|
297
|
-
export { type Atlas, type AtlasIndex, type CapabilityBasis, type CapabilityBasisType, type CapabilityField, type CapabilityOverrides, type CapabilityStatus, type ContactInfo, type CountyAdvisory, type CountyRecord, type EndpointRecord, type EndpointStatus, LIVE_STATUS_URL, type LicenseType, type SearchField, type ServiceType, type StateFile, type StateIndexEntry, type VerifiedBy, atlas, atlasIndex, buildParcelLookupDeepLink, countyPath, countySlugFromName, findCounty, findCountyByFips, findState, isReviewedUnservable, listCountiesByState, listStates, reviewedCapability, slugify, statePath };
|
|
362
|
+
export { type Atlas, type AtlasIndex, type CapabilityBasis, type CapabilityBasisType, type CapabilityField, type CapabilityOverrides, type CapabilityStatus, type ContactInfo, type CountyAdvisory, type CountyRecord, type DeepLinkCounty, type EndpointRecord, type EndpointStatus, LIVE_STATUS_URL, type LicenseType, type SearchField, type ServiceType, type StateFile, type StateIndexEntry, type VerifiedBy, atlas, atlasIndex, buildParcelLookupDeepLink, canonicalLayerUrl, countyOffersAttributeSearch, countyPath, countySlugFromName, findCounty, findCountyByFips, findState, isReviewedUnservable, listCountiesByState, listStates, offersAttributeSearch, reviewedCapability, slugify, statePath };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
// src/capability.ts
|
|
2
|
+
function offersAttributeSearch(endpoint) {
|
|
3
|
+
return !!endpoint && endpoint.attributeSearch !== "unsupported";
|
|
4
|
+
}
|
|
5
|
+
function countyOffersAttributeSearch(county) {
|
|
6
|
+
const endpoints = Array.isArray(county.endpoints) ? county.endpoints : [];
|
|
7
|
+
return endpoints.some(offersAttributeSearch);
|
|
8
|
+
}
|
|
2
9
|
function reviewedCapability(county, field) {
|
|
3
10
|
return county.capabilityOverrides?.[field] ?? null;
|
|
4
11
|
}
|
|
@@ -22792,12 +22799,31 @@ function countySlugFromName(county) {
|
|
|
22792
22799
|
const name = county.trim();
|
|
22793
22800
|
return slugify(COUNTY_UNIT_SUFFIX.test(name) ? name : `${name} County`);
|
|
22794
22801
|
}
|
|
22795
|
-
function
|
|
22802
|
+
function canonicalLayerUrl(url) {
|
|
22803
|
+
const raw = url.trim();
|
|
22804
|
+
let parsed;
|
|
22805
|
+
try {
|
|
22806
|
+
parsed = new URL(raw);
|
|
22807
|
+
} catch {
|
|
22808
|
+
parsed = null;
|
|
22809
|
+
}
|
|
22810
|
+
if (!parsed || parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
22811
|
+
return raw.replace(/\/+$/, "").toLowerCase();
|
|
22812
|
+
}
|
|
22813
|
+
const path = parsed.pathname.replace(/\/{2,}/g, "/").replace(/\/+$/, "").replace(/\/query$/i, "").replace(/\/+$/, "");
|
|
22814
|
+
const port = parsed.port ? `:${parsed.port}` : "";
|
|
22815
|
+
return `${parsed.hostname}${port}${path}`.toLowerCase();
|
|
22816
|
+
}
|
|
22817
|
+
function buildParcelLookupDeepLink(endpoint, base = "https://urbankitstudio.com/tools/parcel-lookup", county) {
|
|
22818
|
+
const searchable = offersAttributeSearch(endpoint);
|
|
22819
|
+
const primary = county?.endpoints ? county.endpoints[0] : endpoint;
|
|
22820
|
+
const isPrimary = !!primary && canonicalLayerUrl(primary.url) === canonicalLayerUrl(endpoint.url);
|
|
22821
|
+
if (county && isPrimary && (!searchable || endpoint.scopeWhere)) {
|
|
22822
|
+
return `${base}/${encodeURIComponent(county.stateSlug)}/${encodeURIComponent(county.countySlug)}`;
|
|
22823
|
+
}
|
|
22796
22824
|
const params = new URLSearchParams();
|
|
22797
22825
|
params.set("endpoint", endpoint.url);
|
|
22798
|
-
const ownerField = endpoint.searchFields.find(
|
|
22799
|
-
(f) => /owner|taxpayer/i.test(f.name)
|
|
22800
|
-
);
|
|
22826
|
+
const ownerField = searchable ? endpoint.searchFields.find((f) => /owner|taxpayer/i.test(f.name)) : void 0;
|
|
22801
22827
|
if (ownerField) params.set("fieldHint", ownerField.name);
|
|
22802
22828
|
return `${base}?${params.toString()}`;
|
|
22803
22829
|
}
|
|
@@ -22808,4 +22834,4 @@ function countyPath(stateSlug, countySlug) {
|
|
|
22808
22834
|
return `/parcel-atlas/${stateSlug}/${countySlug}`;
|
|
22809
22835
|
}
|
|
22810
22836
|
|
|
22811
|
-
export { LIVE_STATUS_URL, atlas, atlasIndex, buildParcelLookupDeepLink, countyPath, countySlugFromName, findCounty, findCountyByFips, findState, isReviewedUnservable, listCountiesByState, listStates, reviewedCapability, slugify, statePath };
|
|
22837
|
+
export { LIVE_STATUS_URL, atlas, atlasIndex, buildParcelLookupDeepLink, canonicalLayerUrl, countyOffersAttributeSearch, countyPath, countySlugFromName, findCounty, findCountyByFips, findState, isReviewedUnservable, listCountiesByState, listStates, offersAttributeSearch, reviewedCapability, slugify, statePath };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@urbankitstudio/atlas",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.15",
|
|
4
4
|
"description": "JS SDK for the UrbanKit County Parcel REST API Atlas: 246 counties across all 50 US states and DC, 258 verified public ArcGIS REST endpoints for county parcel data, with searchable field metadata, sample queries and license info.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"parcel",
|