lance-ts 1.0.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.
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Batch geocoder session handler
3
+ * @param {string} inputFilePath
4
+ * @param {string} outputFilePath - @default
5
+ */
6
+ export declare class BatchAddressGeocoder {
7
+ url: string;
8
+ inputFilePath: string;
9
+ outputFilePath: string;
10
+ constructor(inputFilePath: string, outputFilePath?: string);
11
+ /**
12
+ * Finds relative path for a file name
13
+ * @param {string} localFilePath
14
+ * @returns {string | undefined} the realtive file path, if exists
15
+ * @throws {Error} if unable to find relative path
16
+ */
17
+ _parseFilePath(localFilePath: string): string | undefined;
18
+ /**
19
+ * Generates FormData from a file, according to census geocoder specs
20
+ * @param {string} filePath
21
+ * @returns {FormData} Formatted form data, converting the file into a {Blob}
22
+ */
23
+ _createFormData(filePath: string): FormData | undefined;
24
+ /**
25
+ * Sends generated FormData from blob-ified {@link inputFilePath} to Census Geocoder endpoint,
26
+ * and writes response to a local file based on {@link outputFilePath}.
27
+ * @returns {void}
28
+ */
29
+ geocodeBatch(): Promise<void>;
30
+ }
31
+ //# sourceMappingURL=batch-address.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-address.d.ts","sourceRoot":"","sources":["../src/batch-address.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,qBAAa,oBAAoB;IAE/B,GAAG,SAAsE;IAEzE,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAA2B;gBAErC,aAAa,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM;IAK1D;;;;;OAKG;IACH,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAYzD;;;;OAIG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAcvD;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CAkBpC"}
@@ -0,0 +1,70 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "path";
3
+ /**
4
+ * Batch geocoder session handler
5
+ * @param {string} inputFilePath
6
+ * @param {string} outputFilePath - @default
7
+ */
8
+ export class BatchAddressGeocoder {
9
+ constructor(inputFilePath, outputFilePath) {
10
+ // Set geocoder endpoint
11
+ this.url = "https://geocoding.geo.census.gov/geocoder/locations/addressbatch";
12
+ this.outputFilePath = "batch_coordinates.csv";
13
+ this.inputFilePath = inputFilePath;
14
+ if (outputFilePath)
15
+ this.outputFilePath = outputFilePath;
16
+ }
17
+ /**
18
+ * Finds relative path for a file name
19
+ * @param {string} localFilePath
20
+ * @returns {string | undefined} the realtive file path, if exists
21
+ * @throws {Error} if unable to find relative path
22
+ */
23
+ _parseFilePath(localFilePath) {
24
+ // create absolute directory name for fs methods
25
+ let parsedFilePath;
26
+ try {
27
+ parsedFilePath = path.join(import.meta.dirname, localFilePath);
28
+ }
29
+ catch (error) {
30
+ throw new Error(`Error parsing file path: ${error}`);
31
+ }
32
+ return parsedFilePath ? parsedFilePath : undefined;
33
+ }
34
+ /**
35
+ * Generates FormData from a file, according to census geocoder specs
36
+ * @param {string} filePath
37
+ * @returns {FormData} Formatted form data, converting the file into a {Blob}
38
+ */
39
+ _createFormData(filePath) {
40
+ const fileBuffer = fs.readFileSync(filePath);
41
+ if (!fileBuffer)
42
+ throw new Error(`Unable to read file ${filePath}`);
43
+ const blob = new Blob([fileBuffer], { type: "text/plain" });
44
+ const formData = new FormData();
45
+ formData.append("addressFile", blob, path.basename(filePath));
46
+ formData.append("benchmark", "Public_AR_Current");
47
+ formData.append("returntype", "location");
48
+ return formData;
49
+ }
50
+ /**
51
+ * Sends generated FormData from blob-ified {@link inputFilePath} to Census Geocoder endpoint,
52
+ * and writes response to a local file based on {@link outputFilePath}.
53
+ * @returns {void}
54
+ */
55
+ async geocodeBatch() {
56
+ const formData = this._createFormData(this.inputFilePath);
57
+ if (!formData)
58
+ throw new Error(`Error creating FormData from ${this.inputFilePath}`);
59
+ const response = await fetch(this.url, {
60
+ method: "POST",
61
+ body: formData,
62
+ });
63
+ if (!response.ok)
64
+ throw new Error(`HTTP Error: ${response.status}`);
65
+ const responseText = await response.text();
66
+ fs.writeFileSync(this.outputFilePath, responseText);
67
+ console.log(`Saved to ${this.outputFilePath}`);
68
+ }
69
+ }
70
+ //# sourceMappingURL=batch-address.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-address.js","sourceRoot":"","sources":["../src/batch-address.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAI7B;;;;GAIG;AACH,MAAM,OAAO,oBAAoB;IAO/B,YAAY,aAAqB,EAAE,cAAuB;QAN1D,wBAAwB;QACxB,QAAG,GAAG,kEAAkE,CAAC;QAGzE,mBAAc,GAAW,uBAAuB,CAAC;QAG/C,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,cAAc;YAAE,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IAC3D,CAAC;IAED;;;;;OAKG;IACH,cAAc,CAAC,aAAqB;QAClC,gDAAgD;QAChD,IAAI,cAAsB,CAAC;QAC3B,IAAI,CAAC;YACH,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC;IACrD,CAAC;IAED;;;;OAIG;IACH,eAAe,CAAC,QAAgB;QAC9B,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;QAEpE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;QAEhC,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC9D,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;QAClD,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;QAE1C,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY;QAChB,MAAM,QAAQ,GAAyB,IAAI,CAAC,eAAe,CACzD,IAAI,CAAC,aAAa,CACnB,CAAC;QACF,IAAI,CAAC,QAAQ;YACX,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,CAAC,aAAa,EAAE,CACrD,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;YACrC,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,QAAQ;SACf,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACpE,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAE3C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;IACjD,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=batch-address.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-address.test.d.ts","sourceRoot":"","sources":["../src/batch-address.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,98 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { fs, vol } from "memfs";
3
+ import * as path from "path";
4
+ vi.mock("node:fs", async () => {
5
+ const { fs } = await import("memfs");
6
+ return fs;
7
+ });
8
+ //import BatchAddressGeocoder AFTER fs is mocked
9
+ //so it initializes pointed at mocked module
10
+ import { BatchAddressGeocoder } from "./batch-address.js";
11
+ describe("BatchAddressGeocoder", () => {
12
+ let geocoder;
13
+ // set up memfs dir and file
14
+ const mockFilePath = "/out/mockfile.csv";
15
+ // mock function to replace fetch
16
+ const fetchMock = vi.fn();
17
+ // set test class function
18
+ beforeEach(() => {
19
+ // make fetch mock func globally available
20
+ vi.stubGlobal("fetch", fetchMock);
21
+ // reset the state of in-memory fs
22
+ vol.reset();
23
+ //generate entire filetree
24
+ vol.mkdirSync("/out", { recursive: true });
25
+ //initialize geocoder
26
+ geocoder = new BatchAddressGeocoder("");
27
+ });
28
+ afterEach(() => {
29
+ vi.unstubAllGlobals();
30
+ vi.resetAllMocks();
31
+ });
32
+ it("initializes with the correct endpoint", () => {
33
+ expect(geocoder.url).toEqual("https://geocoding.geo.census.gov/geocoder/locations/addressbatch");
34
+ });
35
+ describe("_parseFilePath", () => {
36
+ it("correctly parses the absolute file path", () => {
37
+ //generate mockFile
38
+ vol.writeFileSync(mockFilePath, "this is some data");
39
+ //point geocoder at mock file
40
+ geocoder.inputFilePath = mockFilePath;
41
+ // get absolute path from BatchAddressGeocoder
42
+ const parsedPath = geocoder._parseFilePath(geocoder.inputFilePath);
43
+ // it should exist
44
+ expect(parsedPath);
45
+ if (parsedPath) {
46
+ // it should be absolute
47
+ expect(path.isAbsolute(parsedPath));
48
+ }
49
+ });
50
+ });
51
+ describe("_createFormData", async () => {
52
+ it("generates accurate form data", async () => {
53
+ //initialize and write to mock file
54
+ const mockFilePath = "/mockfile.csv";
55
+ vol.writeFileSync(mockFilePath, "this is some data");
56
+ //point geocoder at mocked file
57
+ geocoder.inputFilePath = mockFilePath;
58
+ // generate form data
59
+ const formData = geocoder._createFormData(geocoder.inputFilePath);
60
+ // the file field should exist
61
+ expect(formData.get("addressFile")).toBeDefined();
62
+ // it should generate a blob
63
+ const fileField = formData.get("addressFile");
64
+ expect(fileField).toBeInstanceOf(Blob);
65
+ // it should hold data
66
+ expect(fileField.size).toBeGreaterThan(0);
67
+ // it should be text/plain
68
+ expect(fileField.type).toBe("text/plain");
69
+ // content should be accurate
70
+ const text = await fileField.text();
71
+ expect(text).toBe("this is some data");
72
+ });
73
+ });
74
+ describe("geocodeBatch", async () => {
75
+ it("returns correct data in .csv format", async () => {
76
+ const fetchSpy = vi.spyOn(global, "fetch");
77
+ //initialize and write to mock file
78
+ vol.writeFileSync(mockFilePath, "FIELD 1, FIELD 2, FIELD 3, FIELD 4");
79
+ const geocoder = new BatchAddressGeocoder(mockFilePath, "/out/output.csv");
80
+ const geocodeBatchSpy = vi.spyOn(geocoder, "geocodeBatch");
81
+ //mock file blob response
82
+ fetchMock.mockResolvedValue({
83
+ ok: true,
84
+ status: 200,
85
+ text: () => Promise.resolve("FIELD 1, FIELD 2, FIELD 3, FIELD 4"),
86
+ });
87
+ await geocoder.geocodeBatch();
88
+ //geocode function should be called once
89
+ expect(geocodeBatchSpy).toHaveBeenCalledOnce();
90
+ //output file should exist
91
+ expect(fs.existsSync("/out/output.csv")).toBeTruthy;
92
+ //fetch should have been called once
93
+ expect(fetchSpy).toHaveBeenCalledOnce();
94
+ expect(fs.readFileSync("/out/output.csv", "utf-8")).toContain("FIELD 1, FIELD 2, FIELD 3, FIELD 4");
95
+ });
96
+ });
97
+ });
98
+ //# sourceMappingURL=batch-address.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-address.test.js","sourceRoot":"","sources":["../src/batch-address.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @file Library entry point for one-line and batch geocoding functions
3
+ * @module lance-ts
4
+ * @author Matthew Morton
5
+ * @see {@link https://github.com/matt-the-thew/lance-ts}
6
+ */
7
+ export * from "./one-line-address.js";
8
+ export * from "./batch-address.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @file Library entry point for one-line and batch geocoding functions
3
+ * @module lance-ts
4
+ * @author Matthew Morton
5
+ * @see {@link https://github.com/matt-the-thew/lance-ts}
6
+ */
7
+ export * from "./one-line-address.js";
8
+ export * from "./batch-address.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Interface for lat/lng coordinates
3
+ * @interface CensusCoordinates
4
+ */
5
+ export interface CensusCoordinates {
6
+ x: number;
7
+ y: number;
8
+ }
9
+ /**
10
+ * Interface for a single coordinate pair as {@type CensusCoordinates}
11
+ * and the associated matched address, as {@type string}
12
+ * @interface OneLineAddressMatch
13
+ */
14
+ export interface OneLineAddressMatch {
15
+ coordinates: CensusCoordinates;
16
+ matchedAddress: string;
17
+ }
18
+ /**
19
+ * Interface representing an array of {@type OneLineAddressMatch}
20
+ * @interface OneLineAddressResult
21
+ */
22
+ export interface OneLineAddressResult {
23
+ addressMatches: OneLineAddressMatch[];
24
+ }
25
+ /**
26
+ * Interface for the optional result, of type {@type OneLineAddressResult}
27
+ */
28
+ export interface OneLineAddressResponse {
29
+ result?: OneLineAddressResult;
30
+ }
31
+ /**
32
+ * Returns the first result from whatever address matches it receives
33
+ * @param address {string}
34
+ * @returns {OneLineAddressMatch}
35
+ * @throws {Error} if response status is not OK
36
+ */
37
+ export declare function geocodeOneLineAddress(address: string): Promise<OneLineAddressMatch | undefined>;
38
+ /**
39
+ * Returns entire array of {@type OneLineAddressMatch}
40
+ * @param {string} address - The address to submit to the census geocoder
41
+ * @returns {OneLineAddressMatch[]} | undefined
42
+ * @throws {Error} if response status is not OK
43
+ */
44
+ export declare function geocodeOneLineAddressAll(address: string): Promise<OneLineAddressMatch[] | undefined>;
45
+ //# sourceMappingURL=one-line-address.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"one-line-address.d.ts","sourceRoot":"","sources":["../src/one-line-address.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,iBAAiB,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,mBAAmB,EAAE,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAkC1C;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,EAAE,GAAG,SAAS,CAAC,CAsB5C"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Returns the first result from whatever address matches it receives
3
+ * @param address {string}
4
+ * @returns {OneLineAddressMatch}
5
+ * @throws {Error} if response status is not OK
6
+ */
7
+ export async function geocodeOneLineAddress(address) {
8
+ //format parameters to use latest census data
9
+ const params = new URLSearchParams({
10
+ address,
11
+ benchmark: "Public_AR_Current",
12
+ format: "json",
13
+ });
14
+ const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;
15
+ const response = await fetch(url);
16
+ if (!response.ok)
17
+ throw new Error(`HTTP error: ${response.status}`);
18
+ // Retreives array of coordinates and string of matched addresses from API res
19
+ const data = (await response.json());
20
+ const matches = data.result?.addressMatches;
21
+ if (!matches?.length) {
22
+ console.log(`No matches found for: ${address}`);
23
+ return;
24
+ }
25
+ // warns if multiple matched addresses exist for a single address
26
+ if (matches?.length > 1) {
27
+ console.warn(`Multiple address matches found for ${address}`);
28
+ for (const match of matches) {
29
+ console.warn(`[WARNING}: multimple matched addresses for: ${match.matchedAddress}
30
+ Coordinates:
31
+ X: ${match.coordinates["x"]}
32
+ Y: ${match.coordinates["y"]}
33
+ Consider geocodeOneLineAddressAll()`);
34
+ }
35
+ }
36
+ return matches[0];
37
+ }
38
+ /**
39
+ * Returns entire array of {@type OneLineAddressMatch}
40
+ * @param {string} address - The address to submit to the census geocoder
41
+ * @returns {OneLineAddressMatch[]} | undefined
42
+ * @throws {Error} if response status is not OK
43
+ */
44
+ export async function geocodeOneLineAddressAll(address) {
45
+ //format parameters to use latest census data
46
+ const params = new URLSearchParams({
47
+ address,
48
+ benchmark: "Public_AR_Current",
49
+ format: "json",
50
+ });
51
+ const url = `https://geocoding.geo.census.gov/geocoder/locations/onelineaddress?${params}`;
52
+ const response = await fetch(url);
53
+ if (!response.ok)
54
+ throw new Error(`HTTP error: ${response.status}`);
55
+ // Retreives array of coordinates and string of matched addresses from API res
56
+ const data = (await response.json());
57
+ const matches = data.result?.addressMatches;
58
+ if (!matches?.length) {
59
+ console.log(`No matches found for: ${address}`);
60
+ return;
61
+ }
62
+ return matches;
63
+ }
64
+ //# sourceMappingURL=one-line-address.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"one-line-address.js","sourceRoot":"","sources":["../src/one-line-address.ts"],"names":[],"mappings":"AAkCA;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAe;IAEf,6CAA6C;IAC7C,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,OAAO;QACP,SAAS,EAAE,mBAAmB;QAC9B,MAAM,EAAE,MAAM;KACf,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,sEAAsE,MAAM,EAAE,CAAC;IAC3F,MAAM,QAAQ,GAAa,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAEpE,8EAA8E;IAC9E,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA2B,CAAC;IAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC;IAE5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;QAChD,OAAO;IACT,CAAC;IAED,iEAAiE;IACjE,IAAI,OAAO,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,sCAAsC,OAAO,EAAE,CAAC,CAAC;QAC9D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,IAAI,CAAC,+CAA+C,KAAK,CAAC,cAAc;;WAE3E,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC;WACtB,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC;0CACS,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAe;IAEf,6CAA6C;IAC7C,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,OAAO;QACP,SAAS,EAAE,mBAAmB;QAC9B,MAAM,EAAE,MAAM;KACf,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,sEAAsE,MAAM,EAAE,CAAC;IAC3F,MAAM,QAAQ,GAAa,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAEpE,8EAA8E;IAC9E,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA2B,CAAC;IAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC;IAE5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;QAChD,OAAO;IACT,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=one-line-address.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"one-line-address.test.d.ts","sourceRoot":"","sources":["../src/one-line-address.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,112 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { geocodeOneLineAddress, geocodeOneLineAddressAll, } from "./one-line-address.js";
3
+ // create mocking function
4
+ const fetchMock = vi.fn();
5
+ beforeEach(() => {
6
+ // substitute mocking function for node "fetch"
7
+ vi.stubGlobal("fetch", fetchMock);
8
+ });
9
+ afterEach(() => {
10
+ // reset everything after testing
11
+ vi.unstubAllGlobals();
12
+ vi.resetAllMocks();
13
+ });
14
+ describe("geocodeOneLineAddress", () => {
15
+ it("returns lat/lng coordinates when response includes a match", async () => {
16
+ fetchMock.mockResolvedValue({
17
+ ok: true,
18
+ json: async () => ({
19
+ result: {
20
+ addressMatches: [
21
+ {
22
+ coordinates: { x: -77.0365, y: 38.8977 },
23
+ matchedAddress: "1600 Pennsylvania Ave NW, Washington, DC",
24
+ },
25
+ ],
26
+ },
27
+ }),
28
+ });
29
+ const result = await geocodeOneLineAddress("1600 Pennsylvania Ave NW, Washington, DC");
30
+ //it should exist
31
+ expect(result).toBeTruthy();
32
+ //it should have correct data
33
+ expect(result?.coordinates).toEqual({
34
+ x: -77.0365,
35
+ y: 38.8977,
36
+ });
37
+ expect(result?.matchedAddress).toEqual("1600 Pennsylvania Ave NW, Washington, DC");
38
+ //it should call fetch once per response
39
+ expect(fetchMock).toHaveBeenCalledOnce();
40
+ });
41
+ it("warns when multiple address matches exist", async () => {
42
+ //spy on warn output stream
43
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => { });
44
+ //set up mocked fetch result with multiple entires
45
+ fetchMock.mockResolvedValue({
46
+ ok: true,
47
+ json: async () => ({
48
+ result: {
49
+ addressMatches: [
50
+ {
51
+ coordinates: { x: -77.0365, y: 38.8977 },
52
+ matchedAddress: "1600 Pennsylvania Ave NW, Washington, DC",
53
+ },
54
+ {
55
+ coordinates: { x: -78.0, y: 40.0 },
56
+ matchedAddress: "1602 Pennsylvania Ave NW, Washington, DC",
57
+ },
58
+ ],
59
+ },
60
+ }),
61
+ });
62
+ const result = await geocodeOneLineAddress("1600 Pennsylvania Ave NW, Washington, DC");
63
+ //it should exist
64
+ expect(result).toBeTruthy;
65
+ //it should raise a warning
66
+ expect(warnSpy).toHaveBeenCalled();
67
+ //it should raise the correct warning
68
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("Consider geocodeOneLineAddressAll()"));
69
+ });
70
+ });
71
+ describe("geocodeOneLineAddressAll", () => {
72
+ it("returns array of all matched addresses", async () => {
73
+ fetchMock.mockResolvedValue({
74
+ ok: true,
75
+ json: async () => ({
76
+ result: {
77
+ addressMatches: [
78
+ {
79
+ coordinates: { x: -77.0365, y: 38.8977 },
80
+ matchedAddress: "1600 Pennsylvania Ave NW, Washington, DC",
81
+ },
82
+ {
83
+ coordinates: { x: -78.0, y: 40.0 },
84
+ matchedAddress: "1602 Pennsylvania Ave NW, Washington, DC",
85
+ },
86
+ ],
87
+ },
88
+ }),
89
+ });
90
+ const result = await geocodeOneLineAddressAll("1600 Pennsylvania Ave NW, Washington, DC");
91
+ //it should exist
92
+ expect(result).toBeTruthy;
93
+ if (!result)
94
+ return false;
95
+ //it should be the correct size
96
+ expect(result?.length).toBe(2);
97
+ //it should have correct data
98
+ expect(result[0]?.coordinates).toEqual({
99
+ x: -77.0365,
100
+ y: 38.8977,
101
+ });
102
+ expect(result[0]?.matchedAddress).toEqual("1600 Pennsylvania Ave NW, Washington, DC");
103
+ expect(result[1]?.coordinates).toEqual({
104
+ x: -78.0,
105
+ y: 40.0,
106
+ });
107
+ expect(result[1]?.matchedAddress).toEqual("1602 Pennsylvania Ave NW, Washington, DC");
108
+ //it should call fetch once per response
109
+ expect(fetchMock).toHaveBeenCalledOnce();
110
+ });
111
+ });
112
+ //# sourceMappingURL=one-line-address.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"one-line-address.test.js","sourceRoot":"","sources":["../src/one-line-address.test.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "lance-ts",
3
+ "version": "1.0.0",
4
+ "description": "Lat/Lng from Address via National Census Endpoint",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/esm/index.js",
10
+ "types": "./dist/index.js",
11
+ "require": "./dist/cjs/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "test": "vitest",
19
+ "build": "tsc",
20
+ "prepublishOnly": "pnpm run build"
21
+ },
22
+ "keywords": [],
23
+ "author": "",
24
+ "license": "ISC",
25
+ "packageManager": "pnpm@10.30.3",
26
+ "devDependencies": {
27
+ "@types/node": "^25.9.1",
28
+ "@vitest/ui": "4.1.7",
29
+ "publint": "^0.3.21",
30
+ "tsup": "^8.5.1",
31
+ "typescript": "^6.0.3"
32
+ },
33
+ "dependencies": {
34
+ "memfs": "^4.57.2",
35
+ "vitest": "^4.1.7"
36
+ }
37
+ }