@mailwoman/bdc 8.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/README.md +7 -0
- package/index.ts +2 -0
- package/out/index.d.ts +3 -0
- package/out/index.d.ts.map +1 -0
- package/out/index.js +3 -0
- package/out/index.js.map +1 -0
- package/out/schema.d.ts +138 -0
- package/out/schema.d.ts.map +1 -0
- package/out/schema.js +88 -0
- package/out/schema.js.map +1 -0
- package/out/sdk/build-bdc.d.ts +214 -0
- package/out/sdk/build-bdc.d.ts.map +1 -0
- package/out/sdk/build-bdc.js +500 -0
- package/out/sdk/build-bdc.js.map +1 -0
- package/out/sdk/buildings.d.ts +84 -0
- package/out/sdk/buildings.d.ts.map +1 -0
- package/out/sdk/buildings.js +75 -0
- package/out/sdk/buildings.js.map +1 -0
- package/out/sdk/client.d.ts +237 -0
- package/out/sdk/client.d.ts.map +1 -0
- package/out/sdk/client.js +430 -0
- package/out/sdk/client.js.map +1 -0
- package/out/sdk/common.d.ts +279 -0
- package/out/sdk/common.d.ts.map +1 -0
- package/out/sdk/common.js +162 -0
- package/out/sdk/common.js.map +1 -0
- package/out/sdk/data-collection.d.ts +244 -0
- package/out/sdk/data-collection.d.ts.map +1 -0
- package/out/sdk/data-collection.js +50 -0
- package/out/sdk/data-collection.js.map +1 -0
- package/out/sdk/download.d.ts +36 -0
- package/out/sdk/download.d.ts.map +1 -0
- package/out/sdk/download.js +76 -0
- package/out/sdk/download.js.map +1 -0
- package/out/sdk/filing-dates.d.ts +57 -0
- package/out/sdk/filing-dates.d.ts.map +1 -0
- package/out/sdk/filing-dates.js +50 -0
- package/out/sdk/filing-dates.js.map +1 -0
- package/out/sdk/filing-landscape.d.ts +124 -0
- package/out/sdk/filing-landscape.d.ts.map +1 -0
- package/out/sdk/filing-landscape.js +216 -0
- package/out/sdk/filing-landscape.js.map +1 -0
- package/out/sdk/index.d.ts +15 -0
- package/out/sdk/index.d.ts.map +1 -0
- package/out/sdk/index.js +15 -0
- package/out/sdk/index.js.map +1 -0
- package/out/sdk/list-files.d.ts +39 -0
- package/out/sdk/list-files.d.ts.map +1 -0
- package/out/sdk/list-files.js +26 -0
- package/out/sdk/list-files.js.map +1 -0
- package/out/sdk/location.d.ts +40 -0
- package/out/sdk/location.d.ts.map +1 -0
- package/out/sdk/location.js +23 -0
- package/out/sdk/location.js.map +1 -0
- package/out/sdk/nearest-infrastructure.d.ts +84 -0
- package/out/sdk/nearest-infrastructure.d.ts.map +1 -0
- package/out/sdk/nearest-infrastructure.js +84 -0
- package/out/sdk/nearest-infrastructure.js.map +1 -0
- package/out/sdk/parsing.d.ts +57 -0
- package/out/sdk/parsing.d.ts.map +1 -0
- package/out/sdk/parsing.js +87 -0
- package/out/sdk/parsing.js.map +1 -0
- package/out/sdk/plausibility.d.ts +256 -0
- package/out/sdk/plausibility.d.ts.map +1 -0
- package/out/sdk/plausibility.js +334 -0
- package/out/sdk/plausibility.js.map +1 -0
- package/out/sdk/technologies.d.ts +229 -0
- package/out/sdk/technologies.d.ts.map +1 -0
- package/out/sdk/technologies.js +217 -0
- package/out/sdk/technologies.js.map +1 -0
- package/package.json +97 -0
- package/schema.ts +172 -0
- package/sdk/build-bdc.ts +810 -0
- package/sdk/buildings.ts +87 -0
- package/sdk/client.ts +643 -0
- package/sdk/common.ts +363 -0
- package/sdk/data-collection.ts +268 -0
- package/sdk/download.ts +91 -0
- package/sdk/filing-dates.ts +88 -0
- package/sdk/filing-landscape.ts +287 -0
- package/sdk/index.ts +14 -0
- package/sdk/list-files.ts +72 -0
- package/sdk/location.ts +45 -0
- package/sdk/nearest-infrastructure.ts +129 -0
- package/sdk/parsing.ts +124 -0
- package/sdk/plausibility.ts +512 -0
- package/sdk/technologies.ts +258 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The `bdc.db` builder (2a Task 8) — ingests parsed FCC BDC availability rows (Task 6's
|
|
7
|
+
* {@link BDCAvailabilityRow}) into Task 7's schema (`schema.ts`), producing a sealed layer database.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors `mailwoman/gazetteer-pipeline/poi/build-poi.ts`'s shape closely: the same build-tuning
|
|
10
|
+
* pragmas, the same single-pass `Map<number, number>` coverage aggregation taken during the load
|
|
11
|
+
* (no second scan), the same {@link asContractDB} Kysely-invariance cast for the shared
|
|
12
|
+
* `@mailwoman/core/layers` calls, and the same `writeLayerManifest` → `sealDatabase` tail.
|
|
13
|
+
*
|
|
14
|
+
* Two differences from that precedent, both deliberate:
|
|
15
|
+
*
|
|
16
|
+
* 1. **A staging-dedup pass.** BDC's per-provider CSVs can carry exact-duplicate rows (repeat
|
|
17
|
+
* filings, overlapping re-downloads); poi's Overture rows never needed this. `bdc_stage` is a
|
|
18
|
+
* plain Kysely-built table (not part of the public {@link BDCDatabase} — it never survives to the
|
|
19
|
+
* sealed artifact) carrying a composite PRIMARY KEY on the natural key
|
|
20
|
+
* `(geoid, provider_id, technology_code, location_id)`. Rows load via a RAW prepared
|
|
21
|
+
* `INSERT OR IGNORE` — the AGENTS.md "hot bulk write" carve-out, same discipline as the
|
|
22
|
+
* candidate-gazetteer builder — which is the direct replacement for the Redis
|
|
23
|
+
* set-membership check Nexus's `sync/commands/bdc/infer-locations.ts` used for this exact
|
|
24
|
+
* dedup (dedup semantics only; Nexus's Redis-backed location inference itself has no analog
|
|
25
|
+
* here). `h3_cell` is computed only AFTER staging, per distinct geoid, against the deduped set —
|
|
26
|
+
* so a duplicate row is never charged twice against `unknownGeoids` or `layer_coverage` either.
|
|
27
|
+
* The natural key's `location_id` component means the SAME (geoid, provider_id, technology_code)
|
|
28
|
+
* triple legitimately survives staging once per distinct BSL — correct when `includeLocationIDs`
|
|
29
|
+
* is true. The default (NULL `location_id`) mode collapses at MATERIALIZE time via `SELECT DISTINCT`
|
|
30
|
+
* over every column EXCEPT `location_id` — that is, to one row per distinct (geoid, provider_id,
|
|
31
|
+
* technology_code, max_advertised_download_speed, max_advertised_upload_speed, low_latency,
|
|
32
|
+
* business_residential_code) TUPLE, NOT one row per (geoid, provider_id, technology_code) triple.
|
|
33
|
+
* When every BSL in a block shares identical speeds/flags for a given provider/technology (the
|
|
34
|
+
* common case), those two are the same thing and the collapse yields exactly one row per triple.
|
|
35
|
+
* But when BSLs at the SAME triple carry DIFFERENT speeds/flags (a real, accepted FCC filing
|
|
36
|
+
* pattern — a provider filing different advertised speeds at different addresses in one block),
|
|
37
|
+
* the distinct rows survive collapse as multiple NULL-`location_id` rows at that one triple. This
|
|
38
|
+
* is deliberate, matches the FCC source data's own granularity, and is NOT a bug to fix — see
|
|
39
|
+
* `filing-landscape.ts`'s module docstring for the read-side consequence (the same provider/tech
|
|
40
|
+
* can surface in more than one `speed_bucket` for one queried block).
|
|
41
|
+
* 2. **Temp-path build + move-aside-first swap**, per AGENTS.md's database house rule ("build
|
|
42
|
+
* successfully, then move the previous version to a temp directory, and then move the new
|
|
43
|
+
* version into place... ensures the database is always in a consistent state, even if the build
|
|
44
|
+
* script fails halfway through"): build lands at `${out}.building`, seals there, then
|
|
45
|
+
* `${out}` (if present) is renamed to `${out}.prev` BEFORE the sealed build takes its place —
|
|
46
|
+
* mirroring `mailwoman/eval-harness/gauntlet/build-regression-db.ts`'s swap. `build-poi.ts`
|
|
47
|
+
* instead writes `out` directly (removing any stale file first) and records ITS OWN deviation
|
|
48
|
+
* from an even older staging-suffix convention — see that file's docstring. This builder takes
|
|
49
|
+
* the opposite fork on purpose: a from-scratch nationwide BDC ingest is long enough that a
|
|
50
|
+
* mid-build crash mustn't cost the previously-good artifact, which is exactly the failure mode
|
|
51
|
+
* the house rule exists for.
|
|
52
|
+
*/
|
|
53
|
+
import { existsSync, mkdirSync, renameSync, rmSync } from "node:fs";
|
|
54
|
+
import { readFile } from "node:fs/promises";
|
|
55
|
+
import { dirname } from "node:path";
|
|
56
|
+
import { DatabaseSync } from "node:sqlite";
|
|
57
|
+
import { DatabaseClient } from "@mailwoman/core/kysley/client";
|
|
58
|
+
import { createLayerCoverageTable, createLayerManifestTable, LayerFreshnessPolicy, LayerTier, writeLayerCoverage, writeLayerManifest, } from "@mailwoman/core/layers";
|
|
59
|
+
import { openBuiltDatabase, sealDatabase } from "@mailwoman/core/utils";
|
|
60
|
+
import { shortCellToInt } from "@mailwoman/spatial";
|
|
61
|
+
import { cellToParent, latLngToCell } from "h3-js";
|
|
62
|
+
import { BDC_COVERAGE_H3_RESOLUTION, BDC_H3_RESOLUTION, createBDCAvailabilityTable, createBDCGeoidIndex, createBDCProviderTable, } from "../schema.js";
|
|
63
|
+
import { takeAvailabilityLine } from "./parsing.js";
|
|
64
|
+
/**
|
|
65
|
+
* Rows committed per `BEGIN`/`COMMIT` batch during both the staging load and the materialize pass — matches
|
|
66
|
+
* `build-poi.ts`'s `STAGE_BATCH_SIZE` discipline.
|
|
67
|
+
*/
|
|
68
|
+
const STAGE_BATCH_SIZE = 10_000;
|
|
69
|
+
/**
|
|
70
|
+
* The manifest's `attribution` — names the FCC as the source, then copies the Fabric-boundary sentence verbatim from
|
|
71
|
+
* `bdc/README.md`'s "CostQuest Fabric boundary" section (backticks stripped — this is plain prose, not markdown).
|
|
72
|
+
*/
|
|
73
|
+
export const BDC_ATTRIBUTION = "FCC Broadband Data Collection. This workspace never ingests, ships, or derives data from the Fabric: " +
|
|
74
|
+
"location_id is carried only as an opaque join key that a licensed user may join against their own Fabric copy.";
|
|
75
|
+
/**
|
|
76
|
+
* `BDCDatabase extends LayerContractDatabase` structurally, but Kysely's `transaction()` makes `Kysely<DB>` INVARIANT
|
|
77
|
+
* in `DB` — narrows a `DatabaseClient<BDCDatabase>` handle back down for the `@mailwoman/core/layers` calls. Exact
|
|
78
|
+
* precedent: `build-poi.ts`'s own `asContractDB`; see that file for the full rationale (tried widening the shared
|
|
79
|
+
* package's signatures first — breaks THEIR internal `insertInto`/`selectFrom` calls instead).
|
|
80
|
+
*/
|
|
81
|
+
function asContractDB(kdb) {
|
|
82
|
+
return kdb;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Create the build-only `bdc_stage` table — deliberately NOT part of the public {@link BDCDatabase} interface (it's
|
|
86
|
+
* dropped before the artifact seals, so it never appears in the shipped schema). Built via Kysely's schema builder per
|
|
87
|
+
* the AGENTS.md DDL convention (`createTable` takes any string table name — it doesn't need to be a `keyof DB` to
|
|
88
|
+
* type-check); all of `bdc_stage`'s actual reads/writes below go through raw `.prepare()` on the shared `DatabaseSync`
|
|
89
|
+
* instead, per the "hot bulk write" carve-out.
|
|
90
|
+
*
|
|
91
|
+
* The composite PRIMARY KEY on the natural key is what makes `INSERT OR IGNORE` a dedup: SQLite silently drops any
|
|
92
|
+
* insert whose key already exists rather than raising the constraint violation.
|
|
93
|
+
*/
|
|
94
|
+
async function createBDCStageTable(db) {
|
|
95
|
+
await db.schema
|
|
96
|
+
.createTable("bdc_stage")
|
|
97
|
+
.addColumn("geoid", "text", (c) => c.notNull())
|
|
98
|
+
.addColumn("provider_id", "integer", (c) => c.notNull())
|
|
99
|
+
.addColumn("technology_code", "integer", (c) => c.notNull())
|
|
100
|
+
.addColumn("location_id", "text", (c) => c.notNull())
|
|
101
|
+
.addColumn("max_advertised_download_speed", "integer", (c) => c.notNull())
|
|
102
|
+
.addColumn("max_advertised_upload_speed", "integer", (c) => c.notNull())
|
|
103
|
+
.addColumn("low_latency", "integer", (c) => c.notNull())
|
|
104
|
+
.addColumn("business_residential_code", "text", (c) => c.notNull())
|
|
105
|
+
.addPrimaryKeyConstraint("bdc_stage_pk", ["geoid", "provider_id", "technology_code", "location_id"])
|
|
106
|
+
.execute();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Peek the constant `provider_id` column (index 1) off an FCC BDC availability CSV's first data row.
|
|
110
|
+
*
|
|
111
|
+
* Production per-provider files carry the SAME `provider_id` in every row (the FCC partitions availability files per
|
|
112
|
+
* provider) — `parsing.ts`'s `takeAvailabilityLine` already assumes this, taking `providerID` as a parameter rather
|
|
113
|
+
* than re-slicing column 1 per row. This reads it once, directly off the raw bytes, rather than threading a parallel
|
|
114
|
+
* `providerID` array alongside `csvPaths` through the public options shape.
|
|
115
|
+
*
|
|
116
|
+
* `csvPath` is optional and used ONLY to name the offending file in a thrown error (the direct-buffer unit tests call
|
|
117
|
+
* this without one; {@linkcode readAvailabilityRowsFromCSVPaths} always supplies it). The `Number.isSafeInteger` guard
|
|
118
|
+
* below is load-bearing, not defensive dressing: `bdc_stage.provider_id` is `INTEGER NOT NULL`, and a bare
|
|
119
|
+
* `Number.parseInt` on a non-numeric field (a malformed/re-headered/truncated CSV) silently produces `NaN`. `NaN` binds
|
|
120
|
+
* to that NOT NULL column as SQLite `NULL`, `INSERT OR IGNORE` then drops the row without a constraint error, and every
|
|
121
|
+
* dropped row gets counted as `deduped` — the ENTIRE file's rows vanish silently, misreported as ordinary dedup. A
|
|
122
|
+
* malformed CSV must be loud, never silently absorbed, so this throws instead.
|
|
123
|
+
*/
|
|
124
|
+
export function peekProviderID(csvBuffer, csvPath) {
|
|
125
|
+
const headerEnd = csvBuffer.indexOf(0x0a);
|
|
126
|
+
const fileSuffix = csvPath ? ` (${csvPath})` : "";
|
|
127
|
+
if (headerEnd === -1) {
|
|
128
|
+
throw new Error(`peekProviderID: no newline found — empty or header-only CSV buffer${fileSuffix}`);
|
|
129
|
+
}
|
|
130
|
+
const nextNewline = csvBuffer.indexOf(0x0a, headerEnd + 1);
|
|
131
|
+
const firstDataLine = csvBuffer
|
|
132
|
+
.subarray(headerEnd + 1, nextNewline === -1 ? undefined : nextNewline)
|
|
133
|
+
.toString("ascii");
|
|
134
|
+
const providerIDField = firstDataLine.split(",")[1];
|
|
135
|
+
if (!providerIDField) {
|
|
136
|
+
throw new Error(`peekProviderID: could not read provider_id (column 1) from the first data row${fileSuffix}`);
|
|
137
|
+
}
|
|
138
|
+
const providerID = Number.parseInt(providerIDField, 10);
|
|
139
|
+
if (!Number.isSafeInteger(providerID)) {
|
|
140
|
+
throw new TypeError(`peekProviderID: provider_id column (1) did not parse to a safe integer — got ${JSON.stringify(providerIDField)}` +
|
|
141
|
+
`${fileSuffix}. Refusing to silently drop this file's rows: an unguarded NaN binds to bdc_stage.provider_id ` +
|
|
142
|
+
`(INTEGER NOT NULL) as SQLite NULL, and INSERT OR IGNORE would then discard every row uncounted as ordinary dedup.`);
|
|
143
|
+
}
|
|
144
|
+
return providerID;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Reads each of `csvPaths` fully into memory, peeks its `provider_id` ({@linkcode peekProviderID}, passing the path
|
|
148
|
+
* through so a malformed file's error names it), then yields every row via `takeAvailabilityLine`. This is the
|
|
149
|
+
* production counterpart to the test seam's injected `rows` — exercised by `build-bdc.test.ts` only for the
|
|
150
|
+
* malformed-provider-id rejection path, same as `build-poi.ts`'s `readParquetRows`.
|
|
151
|
+
*/
|
|
152
|
+
async function* readAvailabilityRowsFromCSVPaths(csvPaths) {
|
|
153
|
+
for (const csvPath of csvPaths) {
|
|
154
|
+
const buffer = await readFile(csvPath);
|
|
155
|
+
const providerID = peekProviderID(buffer, csvPath);
|
|
156
|
+
yield* takeAvailabilityLine(buffer, providerID);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Naive (vertex-average, NOT area-weighted) centroid of a GeoJSON `Polygon`/`MultiPolygon`'s EXTERIOR ring(s) only
|
|
161
|
+
* (interior rings/holes are ignored). A known simplification, not an oversight: census blocks are small relative to a
|
|
162
|
+
* res-9 H3 cell (~174m edge), so the vertex-average and a proper area-weighted centroid land in the same cell for all
|
|
163
|
+
* but pathologically elongated or holed block shapes. A precise area-weighted centroid is a reasonable future upgrade
|
|
164
|
+
* if that ever proves wrong in practice — no polygon-centroid library is pulled in for this first cut.
|
|
165
|
+
*
|
|
166
|
+
* Returns `undefined` for anything that doesn't parse as one of the two geometry types (including `null` geometry).
|
|
167
|
+
*/
|
|
168
|
+
export function geometryCentroid(geometryJSON) {
|
|
169
|
+
if (!geometryJSON)
|
|
170
|
+
return undefined;
|
|
171
|
+
let geometry;
|
|
172
|
+
try {
|
|
173
|
+
geometry = JSON.parse(geometryJSON);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
const exteriorRings = geometry.type === "Polygon"
|
|
179
|
+
? [geometry.coordinates[0] ?? []]
|
|
180
|
+
: geometry.type === "MultiPolygon"
|
|
181
|
+
? geometry.coordinates.map((polygon) => polygon[0] ?? [])
|
|
182
|
+
: [];
|
|
183
|
+
let sumLon = 0;
|
|
184
|
+
let sumLat = 0;
|
|
185
|
+
let count = 0;
|
|
186
|
+
for (const ring of exteriorRings) {
|
|
187
|
+
for (const point of ring) {
|
|
188
|
+
const [lon, lat] = point;
|
|
189
|
+
if (typeof lon !== "number" || typeof lat !== "number")
|
|
190
|
+
continue;
|
|
191
|
+
sumLon += lon;
|
|
192
|
+
sumLat += lat;
|
|
193
|
+
count++;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (count === 0)
|
|
197
|
+
return undefined;
|
|
198
|
+
return { lat: sumLat / count, lon: sumLon / count };
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* The production `blockCentroids` supplier: opens the TIGER blocks database READ-ONLY and probes `tabblock20.GEOID`
|
|
202
|
+
* (uppercase) per lookup, decoding its GeoJSON `geometry` column via {@linkcode geometryCentroid}. Kept synchronous —
|
|
203
|
+
* `BuildBDCOptions.blockCentroids` is a plain sync function (the same sync-by-interface discipline AGENTS.md documents
|
|
204
|
+
* for the resolver ladder), so this uses `node:sqlite`'s raw `.prepare()`/`.get()` directly rather than Kysely. The
|
|
205
|
+
* connection is left open for the caller's process lifetime (a read-path lookup, not a build) — same lifecycle as the
|
|
206
|
+
* resolver-wof-sqlite lookups.
|
|
207
|
+
*/
|
|
208
|
+
export function createTIGERBlockCentroidLookup(tigerDBPath) {
|
|
209
|
+
const db = openBuiltDatabase(tigerDBPath);
|
|
210
|
+
const stmt = db.prepare("SELECT geometry FROM tabblock20 WHERE GEOID = ?");
|
|
211
|
+
return (geoid) => {
|
|
212
|
+
const row = stmt.get(geoid);
|
|
213
|
+
return row ? geometryCentroid(row.geometry) : undefined;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Rows per `INSERT` batch when populating `bdc_provider`. Far smaller than {@link STAGE_BATCH_SIZE}: that constant
|
|
218
|
+
* tunes `bdc_availability`'s multi-million-row raw-prepared-statement path, whereas `bdc_provider` is a small
|
|
219
|
+
* per-provider dictionary (thousands of rows, not millions) inserted through Kysely's typed `insertInto` — this batches
|
|
220
|
+
* only to stay comfortably under SQLite's bound-parameter ceiling, not for throughput.
|
|
221
|
+
*/
|
|
222
|
+
const PROVIDER_INSERT_BATCH_SIZE = 500;
|
|
223
|
+
/**
|
|
224
|
+
* Groups `providers` by `providerID`. Task 3's `parseProviderList` yields one {@link ProviderListRow} PER LINE of the
|
|
225
|
+
* source CSV, preserving cardinality (never folded, never last-wins — see that module's docstring) — so a `provider_id`
|
|
226
|
+
* appearing on N rows arrives here as N separate rows, exactly as decision 6 requires downstream.
|
|
227
|
+
*/
|
|
228
|
+
async function groupProviderListRows(providers) {
|
|
229
|
+
const byProviderID = new Map();
|
|
230
|
+
for await (const row of providers) {
|
|
231
|
+
const rows = byProviderID.get(row.providerID);
|
|
232
|
+
if (rows) {
|
|
233
|
+
rows.push(row);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
byProviderID.set(row.providerID, [row]);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return byProviderID;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Populate `bdc_provider` from `options.providers` (2a decision 8 / 3a decision 6, Task 8) — see `schema.ts`'s
|
|
243
|
+
* `BDCProviderTable` docstring for the full lossy-denormalization rationale. For each distinct `provider_id`:
|
|
244
|
+
*
|
|
245
|
+
* - Exactly one `frn` among its rows → that FRN is primary by construction; no `filerDB` query needed at all.
|
|
246
|
+
* - More than one distinct `frn` → `readFRNFilingCandidates` (`@mailwoman/filer/sdk`, lazily imported — see below) reads
|
|
247
|
+
* each FRN's own most recent IN-FORCE `form-499` filing edge from `filerDB`, `asOf` the given date, and
|
|
248
|
+
* `pickPrimaryFRN` picks the winner (decision 6: most recent 499 filing date wins). A `provider_id` whose FRNs carry
|
|
249
|
+
* NO 499 filing to rank by inserts `frn: NULL` rather than guessing — `pickPrimaryFRN` throws on empty input, so this
|
|
250
|
+
* checks `candidates.length` first, mirroring `filerLookup`'s own `primary_frn: null` handling of the same case.
|
|
251
|
+
* - `filerDB` is REQUIRED the instant a multi-FRN `provider_id` is encountered; its absence throws immediately, naming
|
|
252
|
+
* the offending `provider_id`, rather than silently picking an arbitrary FRN.
|
|
253
|
+
* - `holding_company` gets the IDENTICAL single-distinct-value shortcut `frn` gets (review fix round 1, IMPORTANT-3):
|
|
254
|
+
* exactly one distinct non-null `holdingCompany` across a provider's rows means there's no conflict to resolve, so
|
|
255
|
+
* it's populated directly, no rule needed. Two or more distinct values IS the real conflict decision 6 refuses to
|
|
256
|
+
* paper over with last-wins — that case inserts NULL, and every value stays recoverable from `filer.db`. A `null`
|
|
257
|
+
* `holdingCompany` on some rows doesn't count as a competing value (a row simply not stating it isn't a conflicting
|
|
258
|
+
* assertion) — only distinct NON-NULL strings are compared.
|
|
259
|
+
*
|
|
260
|
+
* `brand_name` is always inserted NULL — the provider list carries no brand-name column at all, so there is nothing to
|
|
261
|
+
* populate it from, primary or otherwise (see the schema docstring).
|
|
262
|
+
*
|
|
263
|
+
* **Lazy `@mailwoman/filer/sdk` import (review fix round 1, IMPORTANT-1).** `readFRNFilingCandidates`/`pickPrimaryFRN`
|
|
264
|
+
* are loaded via `await import("@mailwoman/filer/sdk")`, memoized in `filerSDK` below, rather than a top-level static
|
|
265
|
+
* import — that barrel re-exports `cluster-filers.ts`, which pulls in `@mailwoman/match`/`record`/`registry`. A
|
|
266
|
+
* top-level import regressed `@mailwoman/bdc`'s import time ~32% for EVERY consumer, including ones that never populate
|
|
267
|
+
* providers at all; the dynamic import here only ever runs when a multi-FRN `provider_id` is actually encountered, so a
|
|
268
|
+
* `providers`-less build (or one whose providers are all single-FRN) pays nothing.
|
|
269
|
+
*/
|
|
270
|
+
async function populateBDCProviderTable(kdb, providers, filerDB, asOf) {
|
|
271
|
+
const byProviderID = await groupProviderListRows(providers);
|
|
272
|
+
const insertRows = [];
|
|
273
|
+
let filerSDK;
|
|
274
|
+
for (const [providerID, rows] of byProviderID) {
|
|
275
|
+
const distinctFRNs = [...new Set(rows.map((row) => row.frn))];
|
|
276
|
+
let frn;
|
|
277
|
+
if (distinctFRNs.length === 1) {
|
|
278
|
+
frn = distinctFRNs[0];
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
if (!filerDB) {
|
|
282
|
+
throw new Error(`buildBDCDatabase: provider_id ${providerID} carries ${distinctFRNs.length} distinct FRNs across the ` +
|
|
283
|
+
"provider list — resolving the primary FRN (decision 6) requires `filerDB` to be supplied " +
|
|
284
|
+
"alongside `providers`");
|
|
285
|
+
}
|
|
286
|
+
filerSDK ??= await import("@mailwoman/filer/sdk");
|
|
287
|
+
const candidates = await filerSDK.readFRNFilingCandidates(filerDB, distinctFRNs, asOf);
|
|
288
|
+
frn = candidates.length ? filerSDK.pickPrimaryFRN(candidates) : null;
|
|
289
|
+
}
|
|
290
|
+
const distinctHoldingCompanies = [
|
|
291
|
+
...new Set(rows.map((row) => row.holdingCompany).filter((value) => value !== null)),
|
|
292
|
+
];
|
|
293
|
+
const holdingCompany = distinctHoldingCompanies.length === 1 ? distinctHoldingCompanies[0] : null;
|
|
294
|
+
insertRows.push({ provider_id: providerID, frn, brand_name: null, holding_company: holdingCompany });
|
|
295
|
+
}
|
|
296
|
+
for (let index = 0; index < insertRows.length; index += PROVIDER_INSERT_BATCH_SIZE) {
|
|
297
|
+
await kdb
|
|
298
|
+
.insertInto("bdc_provider")
|
|
299
|
+
.values(insertRows.slice(index, index + PROVIDER_INSERT_BATCH_SIZE))
|
|
300
|
+
.execute();
|
|
301
|
+
}
|
|
302
|
+
return insertRows.length;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Build `bdc.db`: stage (raw dedup) → materialize (resolve `h3_cell` per geoid, skip+count unknown geoids) → drop stage
|
|
306
|
+
* → geoid index (index-after-load) → coverage → layer manifest → seal → atomic move-into-place. See the module
|
|
307
|
+
* docstring for the two deliberate deviations from `build-poi.ts`.
|
|
308
|
+
*/
|
|
309
|
+
export async function buildBDCDatabase(options) {
|
|
310
|
+
const progress = options.onProgress ?? (() => { });
|
|
311
|
+
if (!options.rows && (!options.csvPaths || !options.csvPaths.length)) {
|
|
312
|
+
throw new Error("buildBDCDatabase: pass either `rows` (test/injected source) or `csvPaths` (per-provider availability CSVs)");
|
|
313
|
+
}
|
|
314
|
+
const buildingPath = `${options.out}.building`;
|
|
315
|
+
if (existsSync(buildingPath)) {
|
|
316
|
+
rmSync(buildingPath);
|
|
317
|
+
}
|
|
318
|
+
mkdirSync(dirname(options.out), { recursive: true });
|
|
319
|
+
const rowSource = options.rows ?? readAvailabilityRowsFromCSVPaths(options.csvPaths);
|
|
320
|
+
const db = new DatabaseSync(buildingPath);
|
|
321
|
+
// Build-tuning pragmas — identical to build-poi.ts's discipline.
|
|
322
|
+
db.exec("PRAGMA page_size=8192; PRAGMA journal_mode=OFF; PRAGMA synchronous=OFF; PRAGMA cache_size=-2000000;");
|
|
323
|
+
const kdb = new DatabaseClient({ database: db });
|
|
324
|
+
progress("creating manifest/coverage/availability/provider/stage tables");
|
|
325
|
+
await createLayerManifestTable(asContractDB(kdb));
|
|
326
|
+
await createLayerCoverageTable(asContractDB(kdb));
|
|
327
|
+
await createBDCAvailabilityTable(kdb);
|
|
328
|
+
await createBDCProviderTable(kdb);
|
|
329
|
+
await createBDCStageTable(kdb);
|
|
330
|
+
const insStage = db.prepare(`INSERT OR IGNORE INTO bdc_stage (
|
|
331
|
+
geoid, provider_id, technology_code, location_id,
|
|
332
|
+
max_advertised_download_speed, max_advertised_upload_speed, low_latency, business_residential_code
|
|
333
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
334
|
+
let staged = 0;
|
|
335
|
+
let batch = 0;
|
|
336
|
+
progress("staging rows — raw prepared INSERT OR IGNORE on the natural key (the Redis-dedup replacement)");
|
|
337
|
+
db.exec("BEGIN");
|
|
338
|
+
for await (const row of rowSource) {
|
|
339
|
+
insStage.run(row.geoid, row.provider_id, row.technology_code, row.location_id, row.max_advertised_download_speed, row.max_advertised_upload_speed, row.low_latency, row.business_residential_code);
|
|
340
|
+
staged++;
|
|
341
|
+
batch++;
|
|
342
|
+
if (batch >= STAGE_BATCH_SIZE) {
|
|
343
|
+
db.exec("COMMIT");
|
|
344
|
+
db.exec("BEGIN");
|
|
345
|
+
batch = 0;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
db.exec("COMMIT");
|
|
349
|
+
const stagedCountRow = db.prepare("SELECT COUNT(*) AS staged_count FROM bdc_stage").get();
|
|
350
|
+
const deduped = staged - stagedCountRow.staged_count;
|
|
351
|
+
progress(`staged ${stagedCountRow.staged_count.toLocaleString()} distinct row(s), ${deduped.toLocaleString()} deduped`);
|
|
352
|
+
const centroidCache = new Map();
|
|
353
|
+
/**
|
|
354
|
+
* Res-6 short-cell int → observed row count, aggregated during materialize (one pass, no second scan) — matches
|
|
355
|
+
* `build-poi.ts`'s `coverage` Map.
|
|
356
|
+
*/
|
|
357
|
+
const coverage = new Map();
|
|
358
|
+
const providers = new Set();
|
|
359
|
+
let unknownGeoids = 0;
|
|
360
|
+
let inserted = 0;
|
|
361
|
+
const insAvailability = db.prepare(`INSERT INTO bdc_availability (
|
|
362
|
+
h3_cell, geoid, wof_id, provider_id, technology_code,
|
|
363
|
+
max_advertised_download_speed, max_advertised_upload_speed, low_latency, business_residential_code, location_id
|
|
364
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
365
|
+
// The FCC's per-provider CSVs are per-BSL: the SAME (geoid, provider_id, technology_code, speeds, low_latency,
|
|
366
|
+
// business_residential_code) tuple can repeat once per Broadband Serviceable Location within that block (a
|
|
367
|
+
// dense urban block can carry ~100 BSLs) — `bdc_stage`'s natural key includes `location_id`, so those BSL rows
|
|
368
|
+
// all survive the staging dedup as distinct staged rows. In `includeLocationIDs` mode that's correct: every BSL
|
|
369
|
+
// is a real, distinct row the caller asked to keep. In the default (NULL `location_id`) mode, when those BSLs
|
|
370
|
+
// ALSO share identical speeds/flags, they'd otherwise materialize as byte-identical rows, inflating `result.rows`
|
|
371
|
+
// and `layer_coverage.observed_rows` by the BSL count (found in review, ~100x at real scale) — `SELECT DISTINCT`
|
|
372
|
+
// over every column EXCEPT `location_id` collapses those byte-identical BSL duplicates down to one row.
|
|
373
|
+
// IMPORTANT — this is NOT a guarantee of one row per (geoid, provider_id, technology_code) triple: BSLs at the
|
|
374
|
+
// same triple with DIFFERING speeds/flags are NOT the same tuple, so `SELECT DISTINCT` does not merge them —
|
|
375
|
+
// they survive as multiple NULL-`location_id` rows at that one triple. Accepted, not a bug; see the module
|
|
376
|
+
// docstring and `filing-landscape.ts`'s docstring for the read-side consequence.
|
|
377
|
+
const stageStmt = options.includeLocationIDs
|
|
378
|
+
? db.prepare(`SELECT geoid, provider_id, technology_code, location_id,
|
|
379
|
+
max_advertised_download_speed, max_advertised_upload_speed, low_latency, business_residential_code
|
|
380
|
+
FROM bdc_stage`)
|
|
381
|
+
: db.prepare(`SELECT DISTINCT geoid, provider_id, technology_code,
|
|
382
|
+
max_advertised_download_speed, max_advertised_upload_speed, low_latency, business_residential_code
|
|
383
|
+
FROM bdc_stage`);
|
|
384
|
+
progress("materializing bdc_availability — resolving block centroids to h3_cell (unknown geoids skipped, never guessed)");
|
|
385
|
+
db.exec("BEGIN");
|
|
386
|
+
batch = 0;
|
|
387
|
+
for (const row of stageStmt.iterate()) {
|
|
388
|
+
let resolved = centroidCache.get(row.geoid);
|
|
389
|
+
if (resolved === undefined) {
|
|
390
|
+
const centroid = options.blockCentroids(row.geoid);
|
|
391
|
+
resolved = centroid
|
|
392
|
+
? (() => {
|
|
393
|
+
// Coverage cell MUST be derived as the res-9 cell's H3 hierarchy parent — NOT a second,
|
|
394
|
+
// independent `latLngToCell(centroid, 6)` call. H3's cell hierarchy is not geometrically
|
|
395
|
+
// exact: a point's directly-indexed res-6 cell and its res-9 cell's `cellToParent(…, 6)`
|
|
396
|
+
// disagree for a real fraction of points (~6% empirically over CONUS, reviewer-verified —
|
|
397
|
+
// hexagon/pentagon boundary artifacts). Deriving both `h3_cell` and the coverage cell from
|
|
398
|
+
// the SAME full res-9 index is what lets `filing-landscape.ts`'s reader reconstruct this
|
|
399
|
+
// exact coverage cell from nothing but the stored `h3_cell` (its `res9ShortCellToRes6Parent`
|
|
400
|
+
// applies `cellToParent` to the reconstructed res-9 cell) — builder and reader must derive
|
|
401
|
+
// the res-6 parent identically, or a genuinely-surveyed block can read back as unknown.
|
|
402
|
+
const fullRes9Cell = latLngToCell(centroid.lat, centroid.lon, BDC_H3_RESOLUTION);
|
|
403
|
+
return {
|
|
404
|
+
h3Cell: shortCellToInt(fullRes9Cell),
|
|
405
|
+
coverageCell: shortCellToInt(cellToParent(fullRes9Cell, BDC_COVERAGE_H3_RESOLUTION)),
|
|
406
|
+
};
|
|
407
|
+
})()
|
|
408
|
+
: null;
|
|
409
|
+
centroidCache.set(row.geoid, resolved);
|
|
410
|
+
}
|
|
411
|
+
if (!resolved) {
|
|
412
|
+
unknownGeoids++;
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
insAvailability.run(resolved.h3Cell, row.geoid,
|
|
416
|
+
// wof_id stays NULL here — WOF point-in-polygon resolution against the block centroid is a later
|
|
417
|
+
// registry-join task, the same decision-8 scoping schema.ts documents for `bdc_provider`.
|
|
418
|
+
null, row.provider_id, row.technology_code, row.max_advertised_download_speed, row.max_advertised_upload_speed, row.low_latency, row.business_residential_code, options.includeLocationIDs ? (row.location_id ?? null) : null);
|
|
419
|
+
inserted++;
|
|
420
|
+
providers.add(row.provider_id);
|
|
421
|
+
coverage.set(resolved.coverageCell, (coverage.get(resolved.coverageCell) ?? 0) + 1);
|
|
422
|
+
batch++;
|
|
423
|
+
if (batch >= STAGE_BATCH_SIZE) {
|
|
424
|
+
db.exec("COMMIT");
|
|
425
|
+
db.exec("BEGIN");
|
|
426
|
+
batch = 0;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
db.exec("COMMIT");
|
|
430
|
+
progress(`materialized ${inserted.toLocaleString()} row(s) across ${providers.size} provider(s) ` +
|
|
431
|
+
`(${unknownGeoids.toLocaleString()} unknown geoid(s) skipped)`);
|
|
432
|
+
await kdb.schema.dropTable("bdc_stage").execute();
|
|
433
|
+
progress("geoid index (index-after-load — see schema.ts)");
|
|
434
|
+
await createBDCGeoidIndex(kdb);
|
|
435
|
+
// Coverage is SOURCE-LEVEL, not survey completeness — same convention build-poi.ts documents: a res-6 cell we
|
|
436
|
+
// have availability rows in is recorded at completeness 1.0. A cell absent from `layer_coverage` means no rows
|
|
437
|
+
// were observed there at all (the meaning-of-zero rule — missing = unknown, never `{completeness: 0}`).
|
|
438
|
+
const coverageCells = [...coverage.entries()].map(([h3Cell, observedRows]) => ({
|
|
439
|
+
h3Cell,
|
|
440
|
+
completeness: 1,
|
|
441
|
+
observedRows,
|
|
442
|
+
}));
|
|
443
|
+
await writeLayerCoverage(asContractDB(kdb), coverageCells);
|
|
444
|
+
progress("writing layer manifest");
|
|
445
|
+
await writeLayerManifest(asContractDB(kdb), {
|
|
446
|
+
name: "bdc",
|
|
447
|
+
version: options.asOfDate,
|
|
448
|
+
schemaVersion: 1,
|
|
449
|
+
tier: LayerTier.Shipped,
|
|
450
|
+
license: "public-domain",
|
|
451
|
+
attribution: BDC_ATTRIBUTION,
|
|
452
|
+
source: "fcc-bdc",
|
|
453
|
+
sourceVintage: options.asOfDate,
|
|
454
|
+
buildCmd: "mailwoman gazetteer build bdc",
|
|
455
|
+
buildSHA: options.buildSHA,
|
|
456
|
+
freshnessPolicy: LayerFreshnessPolicy.VersionedRefresh,
|
|
457
|
+
spineKeys: { h3: { column: "h3_cell", resolution: BDC_H3_RESOLUTION }, wofID: "wof_id" },
|
|
458
|
+
createdAt: new Date().toISOString(),
|
|
459
|
+
});
|
|
460
|
+
// bdc_provider population (2a decision 8 / 3a decision 6, Task 8) — entirely additive and gated behind
|
|
461
|
+
// `options.providers`: when absent, this block never runs and `bdc_provider` stays empty, exactly as before
|
|
462
|
+
// Task 8 (see `BuildBDCOptions.providers`'s docstring — the default-path byte-identical guarantee).
|
|
463
|
+
let providersPopulated = 0;
|
|
464
|
+
if (options.providers) {
|
|
465
|
+
progress("populating bdc_provider from the provider list (decision 6 — lossy denormalization, see schema.ts)");
|
|
466
|
+
providersPopulated = await populateBDCProviderTable(kdb, options.providers, options.filerDB, options.primaryFRNAsOf ?? options.asOfDate);
|
|
467
|
+
progress(`bdc_provider: ${providersPopulated.toLocaleString()} provider(s) populated`);
|
|
468
|
+
}
|
|
469
|
+
progress("finalize: ANALYZE + VACUUM");
|
|
470
|
+
db.exec("ANALYZE");
|
|
471
|
+
// page_size MUST be set right before VACUUM — node:sqlite initializes the file at the 4096 default on
|
|
472
|
+
// `new DatabaseSync`, so the earlier pragma is a no-op until a VACUUM rebuilds at the new size (build-poi.ts's
|
|
473
|
+
// same discipline).
|
|
474
|
+
db.exec("PRAGMA page_size=8192");
|
|
475
|
+
db.exec("VACUUM");
|
|
476
|
+
await kdb.destroy();
|
|
477
|
+
progress("seal");
|
|
478
|
+
sealDatabase(buildingPath);
|
|
479
|
+
// Atomic move-into-place — the previous version is moved ASIDE FIRST, per the AGENTS.md database house rule
|
|
480
|
+
// ("build successfully, then move the previous version to a temp directory, and then move the new version into
|
|
481
|
+
// place"). Mirrors `mailwoman/eval-harness/gauntlet/build-regression-db.ts`'s `${output}.prev` swap. Deliberate
|
|
482
|
+
// deviation from `build-poi.ts`'s direct-write — see the module docstring.
|
|
483
|
+
if (existsSync(options.out)) {
|
|
484
|
+
renameSync(options.out, `${options.out}.prev`);
|
|
485
|
+
}
|
|
486
|
+
renameSync(buildingPath, options.out);
|
|
487
|
+
if (existsSync(`${options.out}.prev`)) {
|
|
488
|
+
rmSync(`${options.out}.prev`);
|
|
489
|
+
}
|
|
490
|
+
return {
|
|
491
|
+
out: options.out,
|
|
492
|
+
rows: inserted,
|
|
493
|
+
deduped,
|
|
494
|
+
providers: providers.size,
|
|
495
|
+
coverageCells: coverageCells.length,
|
|
496
|
+
unknownGeoids,
|
|
497
|
+
providersPopulated,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
//# sourceMappingURL=build-bdc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-bdc.js","sourceRoot":"","sources":["../../sdk/build-bdc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software.
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
* @file FCC/NITA Building Type Codes
|
|
6
|
+
* @see https://www.fcc.gov/sites/default/files/2019-08/Building%20Type%20Code%20List.pdf
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Code indicating the type of building at the location.
|
|
10
|
+
*
|
|
11
|
+
* @category NTIA
|
|
12
|
+
* @category FCC
|
|
13
|
+
* @title Building Type Code
|
|
14
|
+
*/
|
|
15
|
+
export declare const BuildingTypeCode: {
|
|
16
|
+
/**
|
|
17
|
+
* A single-family home, apartment building, or condominium.
|
|
18
|
+
*
|
|
19
|
+
* @title Residential Building
|
|
20
|
+
*/
|
|
21
|
+
readonly Residential: "R";
|
|
22
|
+
/**
|
|
23
|
+
* A office building, retail store, or warehouse.
|
|
24
|
+
*
|
|
25
|
+
* @title Non-residential Building
|
|
26
|
+
*/
|
|
27
|
+
readonly NonResidential: "B";
|
|
28
|
+
/**
|
|
29
|
+
* Both residential and non-residential units.
|
|
30
|
+
*
|
|
31
|
+
* @title Mixed-use Building
|
|
32
|
+
*/
|
|
33
|
+
readonly Mixed: "X";
|
|
34
|
+
/**
|
|
35
|
+
* A dormitory, nursing home, or prison.
|
|
36
|
+
*
|
|
37
|
+
* @title Group Quarters
|
|
38
|
+
*/
|
|
39
|
+
readonly GroupQuarters: "G";
|
|
40
|
+
/**
|
|
41
|
+
* @title Community Anchor Institution
|
|
42
|
+
* @see {@link https://help.bdc.fcc.gov/hc/en-us/articles/13471550784411-How-to-Identify-a-Community-Anchor-Institution-as-a-Broadband-Serviceable-Location CAI Guide}
|
|
43
|
+
*/
|
|
44
|
+
readonly CAI: "C";
|
|
45
|
+
/**
|
|
46
|
+
* @title Enterprise or business park.
|
|
47
|
+
*/
|
|
48
|
+
readonly Enterprise: "E";
|
|
49
|
+
/**
|
|
50
|
+
* Building type not covered by the above categories.
|
|
51
|
+
*
|
|
52
|
+
* @title Other
|
|
53
|
+
*/
|
|
54
|
+
readonly Other: "O";
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* @category NTIA
|
|
58
|
+
* @category FCC
|
|
59
|
+
*/
|
|
60
|
+
export type BuildingTypeCode = (typeof BuildingTypeCode)[keyof typeof BuildingTypeCode];
|
|
61
|
+
/**
|
|
62
|
+
* Code indicating the type of building at the location.
|
|
63
|
+
*
|
|
64
|
+
* @title Business/Residential Code
|
|
65
|
+
*/
|
|
66
|
+
export declare const BusinessResidentialCode: {
|
|
67
|
+
/**
|
|
68
|
+
* @title Business-only service.
|
|
69
|
+
*/
|
|
70
|
+
readonly Business: "B";
|
|
71
|
+
/**
|
|
72
|
+
* @title Residential-only service.
|
|
73
|
+
*/
|
|
74
|
+
readonly Residential: "R";
|
|
75
|
+
/**
|
|
76
|
+
* @title Mixed business and residential service.
|
|
77
|
+
*/
|
|
78
|
+
readonly Mixed: "X";
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* @title Business/Residential Code
|
|
82
|
+
*/
|
|
83
|
+
export type BusinessResidentialCode = (typeof BusinessResidentialCode)[keyof typeof BusinessResidentialCode];
|
|
84
|
+
//# sourceMappingURL=buildings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildings.d.ts","sourceRoot":"","sources":["../../sdk/buildings.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;IAC5B;;;;OAIG;;IAEH;;;;OAIG;;IAEH;;;;OAIG;;IAEH;;;;OAIG;;IAEH;;;OAGG;;IAEH;;OAEG;;IAEH;;;;OAIG;;CAEM,CAAA;AAEV;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAA;AAEvF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;IACnC;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;CAEM,CAAA;AAEV;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,OAAO,uBAAuB,CAAC,CAAA"}
|