@mailwoman/tiger 9.4.0 → 10.1.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.
Files changed (130) hide show
  1. package/lib/block-group.ts +7 -7
  2. package/lib/class-code.ts +387 -266
  3. package/lib/constants.ts +7 -6
  4. package/lib/{county-subdivision.ts → county/subdivision.ts} +7 -7
  5. package/lib/county.ts +7 -7
  6. package/lib/files.ts +37 -20
  7. package/lib/functional-status.ts +6 -4
  8. package/lib/geoid.ts +44 -26
  9. package/lib/index.ts +1 -1
  10. package/lib/legal-statistical-area.ts +7 -5
  11. package/lib/{sdk/schema.ts → schema.ts} +32 -42
  12. package/lib/state.ts +23 -20
  13. package/lib/tabulation-block.ts +4 -4
  14. package/lib/tract.ts +3 -3
  15. package/out/block-group.d.ts +7 -7
  16. package/out/class-code.d.ts +387 -266
  17. package/out/class-code.d.ts.map +1 -1
  18. package/out/class-code.js +387 -266
  19. package/out/class-code.js.map +1 -1
  20. package/out/constants.d.ts +7 -6
  21. package/out/constants.d.ts.map +1 -1
  22. package/out/constants.js +3 -3
  23. package/out/{county-subdivision.d.ts → county/subdivision.d.ts} +8 -8
  24. package/out/county/subdivision.d.ts.map +1 -0
  25. package/out/{county-subdivision.js → county/subdivision.js} +1 -1
  26. package/out/county/subdivision.js.map +1 -0
  27. package/out/county.d.ts +7 -7
  28. package/out/county.js +1 -1
  29. package/out/files.d.ts +21 -13
  30. package/out/files.d.ts.map +1 -1
  31. package/out/files.js +27 -13
  32. package/out/files.js.map +1 -1
  33. package/out/functional-status.d.ts +6 -4
  34. package/out/functional-status.d.ts.map +1 -1
  35. package/out/functional-status.js +6 -4
  36. package/out/functional-status.js.map +1 -1
  37. package/out/geoid.d.ts +34 -16
  38. package/out/geoid.d.ts.map +1 -1
  39. package/out/geoid.js +16 -10
  40. package/out/geoid.js.map +1 -1
  41. package/out/index.d.ts +1 -1
  42. package/out/index.js +1 -1
  43. package/out/legal-statistical-area.d.ts +7 -5
  44. package/out/legal-statistical-area.d.ts.map +1 -1
  45. package/out/legal-statistical-area.js +7 -5
  46. package/out/legal-statistical-area.js.map +1 -1
  47. package/out/{sdk/schema.d.ts → schema.d.ts} +26 -20
  48. package/out/schema.d.ts.map +1 -0
  49. package/out/{sdk/schema.js → schema.js} +12 -31
  50. package/out/schema.js.map +1 -0
  51. package/out/sdk/download.d.ts +7 -15
  52. package/out/sdk/download.d.ts.map +1 -1
  53. package/out/sdk/download.js +5 -14
  54. package/out/sdk/download.js.map +1 -1
  55. package/out/sdk/fetch.d.ts +32 -29
  56. package/out/sdk/fetch.d.ts.map +1 -1
  57. package/out/sdk/fetch.js +18 -53
  58. package/out/sdk/fetch.js.map +1 -1
  59. package/out/sdk/redistricting.d.ts +29 -15
  60. package/out/sdk/redistricting.d.ts.map +1 -1
  61. package/out/sdk/redistricting.js +47 -36
  62. package/out/sdk/redistricting.js.map +1 -1
  63. package/out/sdk/state/TIGERTabulatedBlockEntity.d.ts +2 -2
  64. package/out/sdk/state/TIGERTabulatedBlockEntity.d.ts.map +1 -1
  65. package/out/sdk/state/TIGERTabulatedBlockEntity.js +3 -3
  66. package/out/sdk/state/TIGERTabulatedBlockEntity.js.map +1 -1
  67. package/out/sdk/state/TIGERTractEntity.d.ts.map +1 -1
  68. package/out/sdk/state/TIGERTractEntity.js.map +1 -1
  69. package/out/sdk/state/block-operations.d.ts +3 -2
  70. package/out/sdk/state/block-operations.d.ts.map +1 -1
  71. package/out/sdk/state/block-operations.js +4 -3
  72. package/out/sdk/state/block-operations.js.map +1 -1
  73. package/out/sdk/{state/index.d.ts → state.d.ts} +1 -1
  74. package/out/sdk/state.d.ts.map +1 -0
  75. package/out/sdk/{state/index.js → state.js} +1 -1
  76. package/out/sdk/state.js.map +1 -0
  77. package/out/sdk/tsconfig.tsbuildinfo +1 -0
  78. package/out/state.d.ts +45 -42
  79. package/out/state.d.ts.map +1 -1
  80. package/out/state.js +17 -14
  81. package/out/state.js.map +1 -1
  82. package/out/tabulation-block.d.ts +4 -4
  83. package/out/tools/{race-dots-map.d.ts → race/dots/map.d.ts} +24 -10
  84. package/out/tools/race/dots/map.d.ts.map +1 -0
  85. package/out/tools/{race-dots-map.js → race/dots/map.js} +10 -6
  86. package/out/tools/race/dots/map.js.map +1 -0
  87. package/out/tools/race/dots.d.ts +47 -0
  88. package/out/tools/race/dots.d.ts.map +1 -0
  89. package/out/tools/{race-dots.js → race/dots.js} +10 -27
  90. package/out/tools/race/dots.js.map +1 -0
  91. package/out/tools/serve-range.d.ts +13 -7
  92. package/out/tools/serve-range.d.ts.map +1 -1
  93. package/out/tools/serve-range.js +9 -7
  94. package/out/tools/serve-range.js.map +1 -1
  95. package/out/tools/tsconfig.tsbuildinfo +1 -0
  96. package/out/tract.d.ts +3 -3
  97. package/out/tsconfig.test.tsbuildinfo +1 -1
  98. package/package.json +59 -42
  99. package/sdk/download.ts +41 -0
  100. package/{lib/sdk → sdk}/fetch.ts +54 -62
  101. package/{lib/sdk → sdk}/redistricting.ts +57 -40
  102. package/{lib/sdk → sdk}/state/TIGERTabulatedBlockEntity.ts +3 -3
  103. package/{lib/sdk → sdk}/state/block-operations.ts +5 -4
  104. package/{lib/tools/race-dots-map.ts → tools/race/dots/map.ts} +30 -12
  105. package/{lib/tools/race-dots.ts → tools/race/dots.ts} +14 -31
  106. package/{lib/tools → tools}/serve-range.ts +15 -9
  107. package/lib/sdk/download.ts +0 -49
  108. package/lib/sdk/index.ts +0 -10
  109. package/lib/tools/index.ts +0 -11
  110. package/out/county-subdivision.d.ts.map +0 -1
  111. package/out/county-subdivision.js.map +0 -1
  112. package/out/sdk/index.d.ts +0 -10
  113. package/out/sdk/index.d.ts.map +0 -1
  114. package/out/sdk/index.js +0 -10
  115. package/out/sdk/index.js.map +0 -1
  116. package/out/sdk/schema.d.ts.map +0 -1
  117. package/out/sdk/schema.js.map +0 -1
  118. package/out/sdk/state/index.d.ts.map +0 -1
  119. package/out/sdk/state/index.js.map +0 -1
  120. package/out/tools/index.d.ts +0 -11
  121. package/out/tools/index.d.ts.map +0 -1
  122. package/out/tools/index.js +0 -11
  123. package/out/tools/index.js.map +0 -1
  124. package/out/tools/race-dots-map.d.ts.map +0 -1
  125. package/out/tools/race-dots-map.js.map +0 -1
  126. package/out/tools/race-dots.d.ts +0 -61
  127. package/out/tools/race-dots.d.ts.map +0 -1
  128. package/out/tools/race-dots.js.map +0 -1
  129. /package/{lib/sdk → sdk}/state/TIGERTractEntity.ts +0 -0
  130. /package/{lib/sdk/state/index.ts → sdk/state.ts} +0 -0
package/out/tract.d.ts CHANGED
@@ -7,7 +7,7 @@ import type { GeoFeature, MultiPolygonLiteral } from "@mailwoman/spatial";
7
7
  import type { TIGERProperty, TIGERPropertyRecord } from "#constants";
8
8
  import type { FIPSTractGeoID, GeoIDPart, ParsedGeoIDTractLevel } from "#geoid";
9
9
  /**
10
- * @title TIGER Tract
10
+ * @title tiger Tract
11
11
  * @public
12
12
  */
13
13
  export interface TIGERTract extends ParsedGeoIDTractLevel {
@@ -18,8 +18,8 @@ export interface TIGERTract extends ParsedGeoIDTractLevel {
18
18
  */
19
19
  GEOID: FIPSTractGeoID;
20
20
  /**
21
- * The geometry of the tabulated block, typically a polygon, but may be a multi-polygon for blocks with holes, or
22
- * islands.
21
+ * The geometry of the tabulated block, typically a polygon, but may be a
22
+ * multi-polygon for blocks with holes, or islands.
23
23
  *
24
24
  * @title Geometry
25
25
  */
@@ -1 +1 @@
1
- {"root":["../test/unit/sdk/redistricting.test.ts"],"version":"6.0.3"}
1
+ {"version":"7.0.2","root":["../lib/files.test.ts","../sdk/redistricting.test.ts"],"packageJsons":["../../../node_modules/@googlemaps/google-maps-services-js/package.json","../../../node_modules/@standard-schema/spec/package.json","../../../node_modules/@types/chai/package.json","../../../node_modules/@types/deep-eql/package.json","../../../node_modules/@types/node/package.json","../../../node_modules/@vitest/browser-playwright/package.json","../../../node_modules/@vitest/coverage-v8/package.json","../../../node_modules/@vitest/expect/package.json","../../../node_modules/@vitest/mocker/package.json","../../../node_modules/@vitest/pretty-format/package.json","../../../node_modules/@vitest/runner/package.json","../../../node_modules/@vitest/snapshot/package.json","../../../node_modules/@vitest/spy/package.json","../../../node_modules/@vitest/utils/package.json","../../../node_modules/agentkeepalive/package.json","../../../node_modules/assertion-error/package.json","../../../node_modules/axios/package.json","../../../node_modules/buffer/package.json","../../../node_modules/chai/package.json","../../../node_modules/deep-eql/package.json","../../../node_modules/esbuild/package.json","../../../node_modules/events/package.json","../../../node_modules/expect-type/package.json","../../../node_modules/jiti/package.json","../../../node_modules/jsdom/package.json","../../../node_modules/punycode/package.json","../../../node_modules/retry-axios/package.json","../../../node_modules/string_decoder/package.json","../../../node_modules/tagged-tag/package.json","../../../node_modules/terser/package.json","../../../node_modules/tinybench/package.json","../../../node_modules/tinyrainbow/package.json","../../../node_modules/type-fest/package.json","../../../node_modules/undici-types/package.json","../../../node_modules/vite/package.json","../../../node_modules/vitest/package.json","../../../node_modules/yaml/package.json","../../annotations/package.json","../../spatial/package.json","../package.json"],"missingPackageJsons":["../../../node_modules/@edge-runtime/vm/package.json","../../../node_modules/@googlemaps/google-maps-services-js/dist/geocode/package.json","../../../node_modules/@googlemaps/google-maps-services-js/dist/package.json","../../../node_modules/@googlemaps/google-maps-services-js/dist/places/package.json","../../../node_modules/@googlemaps/google-maps-services-js/dist/roads/package.json","../../../node_modules/@opentelemetry/api/package.json","../../../node_modules/@standard-schema/spec/dist/package.json","../../../node_modules/@types/assert/package.json","../../../node_modules/@types/assert/strict/package.json","../../../node_modules/@types/async_hooks/package.json","../../../node_modules/@types/child_process/package.json","../../../node_modules/@types/cluster/package.json","../../../node_modules/@types/console/package.json","../../../node_modules/@types/constants/package.json","../../../node_modules/@types/crypto/package.json","../../../node_modules/@types/dgram/package.json","../../../node_modules/@types/diagnostics_channel/package.json","../../../node_modules/@types/dns/package.json","../../../node_modules/@types/dns/promises/package.json","../../../node_modules/@types/domain/package.json","../../../node_modules/@types/events/package.json","../../../node_modules/@types/fs/package.json","../../../node_modules/@types/fs/promises/package.json","../../../node_modules/@types/http/package.json","../../../node_modules/@types/http2/package.json","../../../node_modules/@types/https/package.json","../../../node_modules/@types/inspector/package.json","../../../node_modules/@types/inspector/promises/package.json","../../../node_modules/@types/module/package.json","../../../node_modules/@types/net/package.json","../../../node_modules/@types/node/assert/package.json","../../../node_modules/@types/node/dns/package.json","../../../node_modules/@types/node/fs/package.json","../../../node_modules/@types/node/inspector/package.json","../../../node_modules/@types/node/path/package.json","../../../node_modules/@types/node/readline/package.json","../../../node_modules/@types/node/stream/package.json","../../../node_modules/@types/node/test/package.json","../../../node_modules/@types/node/timers/package.json","../../../node_modules/@types/node/util/package.json","../../../node_modules/@types/node/web-globals/package.json","../../../node_modules/@types/node/zlib/package.json","../../../node_modules/@types/os/package.json","../../../node_modules/@types/path/package.json","../../../node_modules/@types/path/posix/package.json","../../../node_modules/@types/path/win32/package.json","../../../node_modules/@types/perf_hooks/package.json","../../../node_modules/@types/process/package.json","../../../node_modules/@types/punycode/package.json","../../../node_modules/@types/querystring/package.json","../../../node_modules/@types/readline/package.json","../../../node_modules/@types/readline/promises/package.json","../../../node_modules/@types/repl/package.json","../../../node_modules/@types/stream/consumers/package.json","../../../node_modules/@types/stream/iter/package.json","../../../node_modules/@types/stream/package.json","../../../node_modules/@types/stream/promises/package.json","../../../node_modules/@types/stream/web/package.json","../../../node_modules/@types/string_decoder/package.json","../../../node_modules/@types/timers/package.json","../../../node_modules/@types/timers/promises/package.json","../../../node_modules/@types/tls/package.json","../../../node_modules/@types/trace_events/package.json","../../../node_modules/@types/tty/package.json","../../../node_modules/@types/url/package.json","../../../node_modules/@types/util/package.json","../../../node_modules/@types/util/types/package.json","../../../node_modules/@types/v8/package.json","../../../node_modules/@types/vm/package.json","../../../node_modules/@types/wasi/package.json","../../../node_modules/@types/worker_threads/package.json","../../../node_modules/@types/zlib/package.json","../../../node_modules/@vitejs/devtools/package.json","../../../node_modules/@vitest/browser-preview/package.json","../../../node_modules/@vitest/browser-webdriverio/package.json","../../../node_modules/@vitest/coverage-istanbul/package.json","../../../node_modules/@vitest/expect/dist/package.json","../../../node_modules/@vitest/mocker/dist/package.json","../../../node_modules/@vitest/pretty-format/dist/package.json","../../../node_modules/@vitest/runner/dist/package.json","../../../node_modules/@vitest/runner/utils/package.json","../../../node_modules/@vitest/snapshot/dist/package.json","../../../node_modules/@vitest/spy/dist/package.json","../../../node_modules/@vitest/ui/package.json","../../../node_modules/@vitest/utils/diff/package.json","../../../node_modules/@vitest/utils/display/package.json","../../../node_modules/@vitest/utils/dist/package.json","../../../node_modules/assert/package.json","../../../node_modules/assert/strict/package.json","../../../node_modules/async_hooks/package.json","../../../node_modules/child_process/package.json","../../../node_modules/cluster/package.json","../../../node_modules/console/package.json","../../../node_modules/constants/package.json","../../../node_modules/crypto/package.json","../../../node_modules/dgram/package.json","../../../node_modules/diagnostics_channel/package.json","../../../node_modules/dns/package.json","../../../node_modules/dns/promises/package.json","../../../node_modules/domain/package.json","../../../node_modules/expect-type/dist/package.json","../../../node_modules/fs/package.json","../../../node_modules/fs/promises/package.json","../../../node_modules/happy-dom/package.json","../../../node_modules/http/package.json","../../../node_modules/http2/package.json","../../../node_modules/https/package.json","../../../node_modules/inspector/package.json","../../../node_modules/inspector/promises/package.json","../../../node_modules/less/package.json","../../../node_modules/module/package.json","../../../node_modules/msw/package.json","../../../node_modules/net/package.json","../../../node_modules/os/package.json","../../../node_modules/path/package.json","../../../node_modules/path/posix/package.json","../../../node_modules/path/win32/package.json","../../../node_modules/perf_hooks/package.json","../../../node_modules/process/package.json","../../../node_modules/querystring/package.json","../../../node_modules/readline/package.json","../../../node_modules/readline/promises/package.json","../../../node_modules/repl/package.json","../../../node_modules/retry-axios/dist/package.json","../../../node_modules/retry-axios/dist/src/package.json","../../../node_modules/sass-embedded/package.json","../../../node_modules/sass/package.json","../../../node_modules/stream/consumers/package.json","../../../node_modules/stream/iter/package.json","../../../node_modules/stream/package.json","../../../node_modules/stream/promises/package.json","../../../node_modules/stream/web/package.json","../../../node_modules/stylus/package.json","../../../node_modules/sugarss/package.json","../../../node_modules/timers/package.json","../../../node_modules/timers/promises/package.json","../../../node_modules/tinybench/dist/package.json","../../../node_modules/tinyrainbow/dist/package.json","../../../node_modules/tls/package.json","../../../node_modules/trace_events/package.json","../../../node_modules/tsx/package.json","../../../node_modules/tty/package.json","../../../node_modules/type-fest/source/internal/package.json","../../../node_modules/type-fest/source/package.json","../../../node_modules/url/package.json","../../../node_modules/util/package.json","../../../node_modules/util/types/package.json","../../../node_modules/v8/package.json","../../../node_modules/vite/dist/node/chunks/package.json","../../../node_modules/vite/dist/node/package.json","../../../node_modules/vite/dist/package.json","../../../node_modules/vite/module-runner/package.json","../../../node_modules/vite/types/package.json","../../../node_modules/vitest/dist/chunks/package.json","../../../node_modules/vitest/dist/package.json","../../../node_modules/vm/package.json","../../../node_modules/wasi/package.json","../../../node_modules/worker_threads/package.json","../../../node_modules/zlib/package.json"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailwoman/tiger",
3
- "version": "9.4.0",
3
+ "version": "10.1.0",
4
4
  "description": "US Census TIGER/Line data processing and analysis.",
5
5
  "keywords": [
6
6
  "census",
@@ -25,20 +25,27 @@
25
25
  },
26
26
  "files": [
27
27
  "out/",
28
- "*.ts",
29
- "*.tsx",
30
- "**/*.ts",
31
- "**/*.tsx",
32
- "!*.test.ts",
33
- "!*.test.tsx",
28
+ "lib/**/*.ts",
29
+ "lib/**/*.tsx",
30
+ "sdk/**/*.ts",
31
+ "tools/**/*.ts",
34
32
  "!**/*.test.ts",
35
33
  "!**/*.test.tsx",
36
- "!__snapshots__/",
37
- "!test/**"
34
+ "!__snapshots__/"
38
35
  ],
39
36
  "type": "module",
40
37
  "sideEffects": false,
41
38
  "imports": {
39
+ "#sdk/*": {
40
+ "types": "./out/sdk/*.d.ts",
41
+ "node": "./out/sdk/*.js",
42
+ "default": "./out/sdk/*.js"
43
+ },
44
+ "#tools/*": {
45
+ "types": "./out/tools/*.d.ts",
46
+ "node": "./out/tools/*.js",
47
+ "default": "./out/tools/*.js"
48
+ },
42
49
  "#index": {
43
50
  "types": "./out/index.d.ts",
44
51
  "node": "./out/index.js",
@@ -52,53 +59,63 @@
52
59
  },
53
60
  "exports": {
54
61
  "./package.json": "./package.json",
55
- "./sdk": {
56
- "types": "./out/sdk/index.d.ts",
57
- "node": "./out/sdk/index.js",
58
- "default": "./out/sdk/index.js"
59
- },
60
- "./tools": {
61
- "types": "./out/tools/index.d.ts",
62
- "node": "./out/tools/index.js",
63
- "default": "./out/tools/index.js"
64
- },
65
62
  ".": {
66
63
  "types": "./out/index.d.ts",
67
64
  "node": "./out/index.js",
68
65
  "default": "./out/index.js"
69
66
  },
70
- "./sdk/schema": {
71
- "types": "./out/sdk/schema.d.ts",
72
- "node": "./out/sdk/schema.js",
73
- "default": "./out/sdk/schema.js"
67
+ "./sdk/*": {
68
+ "types": "./out/sdk/*.d.ts",
69
+ "node": "./out/sdk/*.js",
70
+ "default": "./out/sdk/*.js"
71
+ },
72
+ "./tools/*": {
73
+ "types": "./out/tools/*.d.ts",
74
+ "node": "./out/tools/*.js",
75
+ "default": "./out/tools/*.js"
76
+ },
77
+ "./*": {
78
+ "types": "./out/*.d.ts",
79
+ "node": "./out/*.js",
80
+ "default": "./out/*.js"
74
81
  }
75
82
  },
76
83
  "publishConfig": {
77
84
  "access": "public",
78
85
  "exports": {
79
86
  "./package.json": "./package.json",
80
- "./sdk": {
81
- "types": "./out/sdk/index.d.ts",
82
- "node": "./out/sdk/index.js",
83
- "default": "./out/sdk/index.js"
84
- },
85
- "./tools": {
86
- "types": "./out/tools/index.d.ts",
87
- "node": "./out/tools/index.js",
88
- "default": "./out/tools/index.js"
89
- },
90
87
  ".": {
91
88
  "types": "./out/index.d.ts",
92
89
  "node": "./out/index.js",
93
90
  "default": "./out/index.js"
94
91
  },
95
- "./sdk/schema": {
96
- "types": "./out/sdk/schema.d.ts",
97
- "node": "./out/sdk/schema.js",
98
- "default": "./out/sdk/schema.js"
92
+ "./sdk/*": {
93
+ "types": "./out/sdk/*.d.ts",
94
+ "node": "./out/sdk/*.js",
95
+ "default": "./out/sdk/*.js"
96
+ },
97
+ "./tools/*": {
98
+ "types": "./out/tools/*.d.ts",
99
+ "node": "./out/tools/*.js",
100
+ "default": "./out/tools/*.js"
101
+ },
102
+ "./*": {
103
+ "types": "./out/*.d.ts",
104
+ "node": "./out/*.js",
105
+ "default": "./out/*.js"
99
106
  }
100
107
  },
101
108
  "imports": {
109
+ "#sdk/*": {
110
+ "types": "./out/sdk/*.d.ts",
111
+ "node": "./out/sdk/*.js",
112
+ "default": "./out/sdk/*.js"
113
+ },
114
+ "#tools/*": {
115
+ "types": "./out/tools/*.d.ts",
116
+ "node": "./out/tools/*.js",
117
+ "default": "./out/tools/*.js"
118
+ },
102
119
  "#index": {
103
120
  "types": "./out/index.d.ts",
104
121
  "node": "./out/index.js",
@@ -115,13 +132,13 @@
115
132
  "test": "node ./out/sdk/TIGERService.test.js"
116
133
  },
117
134
  "dependencies": {
118
- "@mailwoman/core": "9.4.0",
119
- "@mailwoman/spatial": "9.4.0",
120
- "@mailwoman/sqlite": "9.4.0",
135
+ "@mailwoman/core": "10.1.0",
136
+ "@mailwoman/spatial": "10.1.0",
137
+ "@mailwoman/sqlite": "10.1.0",
121
138
  "@turf/boolean-contains": "^7.4.0",
122
139
  "kysely": "^0.29.5",
123
- "path-ts": "^2.3.0",
124
- "spliterator": "^6.4.1",
140
+ "path-ts": "^2.5.0",
141
+ "spliterator": "^8.1.0",
125
142
  "type-fest": "^5.9.0"
126
143
  },
127
144
  "devDependencies": {
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Raw `fetch` is deliberate for a multi-gigabyte streamed transfer: `APIClient`'s response caching
7
+ * and pacing do not apply.
8
+ */
9
+
10
+ import { pathExists } from "@mailwoman/core/fs/readers"
11
+ import { openWriteStream, pipeline, Readable } from "@mailwoman/core/fs/streams"
12
+ import { movePath } from "@mailwoman/core/fs/writers"
13
+ import { verifyZipIntegrity } from "@mailwoman/core/fs/zip"
14
+ import type { PathBuilderLike } from "path-ts"
15
+
16
+ /**
17
+ * Download `url` to `dest` unless a valid copy is already there.
18
+ *
19
+ * "Valid" means every member's CRC-32 checks out.
20
+ * An interrupted download otherwise leaves a plausible file that fails later inside ogr2ogr.
21
+ */
22
+ export async function downloadIfNeeded(url: string, dest: PathBuilderLike): Promise<boolean> {
23
+ if (await pathExists(dest)) {
24
+ try {
25
+ await verifyZipIntegrity(dest)
26
+
27
+ return true
28
+ } catch {
29
+ // corrupt cache — re-download
30
+ }
31
+ }
32
+
33
+ const tmp = dest + ".tmp"
34
+ const res = await fetch(url, { redirect: "follow" })
35
+
36
+ if (!res.ok || !res.body) throw new Error(`HTTP ${res.status} fetching ${url}`)
37
+ await pipeline(Readable.fromWeb(res.body as Parameters<typeof Readable.fromWeb>[0]), openWriteStream(tmp))
38
+ await movePath(tmp, dest)
39
+
40
+ return false
41
+ }
@@ -2,45 +2,27 @@
2
2
  * @copyright Sister Software.
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * Pull TIGER geometry/attributes into a `node:sqlite` database via the Kysely
7
- * {@link DatabaseClient}.
8
- *
9
- * Replaces the ad-hoc wget and the retired corpus TIGER build script. Pared down from isp-nexus's
10
- * `generate-tiger-tiles.ts` (its per-level column mapping is the spec) into the playpen
11
- * `repo-tools` idiom: an async generator of progress events, idempotent, no SpatiaLite.
12
- *
13
- * Three levels:
14
- *
15
- * - `tabblock20` (per state) — tabulation blocks → `tabblock20`, geometry as GeoJSON text.
16
- * - `place` (per state) — incorporated/census places → `tiger_places` (attribute-only).
17
- * - `addrfeat` (per county) — named street segments + ZIPs → `tiger_streets` (attribute-only).
18
- *
19
- * `tiger_streets` + `tiger_places` match the schema the corpus `tiger` adapter reads, so this is a
20
- * drop-in replacement for the retired corpus TIGER build script.
21
- *
22
- * Flow per source unit: download (skips a valid cached zip) → unzip → stream `ogr2ogr -f
23
- * GeoJSONSeq` (mapping shapefile columns to the schema, WGS84 for geometry levels) → batched
24
- * inserts. Re-running a state replaces its rows.
25
5
  */
26
6
 
27
7
  import { APIClient, pluckResponseData } from "@mailwoman/core/api"
28
- import { mailwomanDataRoot } from "@mailwoman/core/data-root"
8
+ import { dataRootPath } from "@mailwoman/core/data-root"
29
9
  import { makeDirectories } from "@mailwoman/core/fs/writers"
30
10
  import { extractZipEntries } from "@mailwoman/core/fs/zip"
31
- import { ogr2ogrGeoJSONSeq } from "@mailwoman/spatial/tools/ogr-stream"
11
+ import { stringifyJSON } from "@mailwoman/core/json"
12
+ import { ogr2ogrGeoJSONSeq } from "@mailwoman/spatial/tools/ogr/stream"
32
13
  import { DatabaseClient } from "@mailwoman/sqlite/client"
33
- import { dirname, join } from "path-ts"
14
+ import { PathBuilder } from "path-ts"
34
15
 
16
+ import type { TIGERBlockTable, TIGERDatabase, TIGERPlaceTable, TIGERStreetTable } from "#schema"
17
+ import { initializeTIGERSchema, TIGER_PRAGMAS } from "#schema"
35
18
  import { downloadIfNeeded } from "#sdk/download"
36
- import type { TIGERBlockTable, TIGERDatabase, TIGERPlaceTable, TIGERStreetTable } from "#sdk/schema"
37
- import { initializeTIGERSchema, TIGER_PRAGMAS } from "#sdk/schema"
38
19
 
39
20
  const CENSUS_HOST = "https://www2.census.gov"
40
- const DEFAULT_DATA_ROOT = mailwomanDataRoot()
21
+ const DEFAULT_DATA_ROOT = dataRootPath()
41
22
 
42
23
  /**
43
- * Supported TIGER levels. `tabblock20` is per state + carries geometry; `place`/`addrfeat` are attribute-only.
24
+ * A TIGER/Line product that {@link fetchTIGER} can load.
25
+ * Only the `tabblock20` level keeps geometry.
44
26
  */
45
27
  export type TIGERFetchLevel = "tabblock20" | "place" | "addrfeat"
46
28
 
@@ -56,51 +38,73 @@ const LEVEL_TABLE: Record<TIGERFetchLevel, keyof TIGERDatabase> = {
56
38
  addrfeat: "tiger_streets",
57
39
  }
58
40
 
41
+ /**
42
+ * Options for {@link fetchTIGER}.
43
+ */
59
44
  export interface FetchTIGEROptions {
60
45
  /**
61
- * Two-digit state FIPS, e.g. `"06"`.
46
+ * The two-digit state FIPS code, such as `"06"`.
62
47
  */
63
48
  stateFIPS: string
49
+
64
50
  /**
65
- * TIGER level. Default `tabblock20`.
51
+ * The TIGER level to fetch.
52
+ * The default is `tabblock20`.
66
53
  */
67
54
  level?: TIGERFetchLevel
55
+
68
56
  /**
69
- * Vintage. Default 2020 for blocks (matches the 2020 P.L.), 2024 for place/addrfeat (current).
57
+ * The TIGER vintage year.
58
+ *
59
+ * The default is 2020 for blocks to match redistricting data.
60
+ * Other levels default to 2024.
70
61
  */
71
62
  vintage?: number
63
+
72
64
  /**
73
- * Output SQLite path. Default `<dataRoot>/tiger/tiger.db` (the name the corpus `tiger` adapter reads).
65
+ * The output SQLite path.
66
+ *
67
+ * The default is `<dataRoot>/tiger/tiger.db`, which the corpus `tiger` adapter reads.
74
68
  */
75
69
  outPath?: string
70
+
76
71
  /**
77
- * Download cache + default output root.
72
+ * The root of the download cache and the default output.
73
+ * The default is the Mailwoman data root.
78
74
  */
79
75
  dataRoot?: string
76
+
80
77
  /**
81
- * Optional three-digit county FIPS filter (blocks only — addrfeat is already per-county).
78
+ * A three-digit county FIPS filter that applies only to the `tabblock20` level.
82
79
  */
83
80
  county?: string
81
+
84
82
  /**
85
- * Rows per insert. Default 1000.
83
+ * The number of rows per insert batch.
84
+ * The default is 1000.
86
85
  */
87
86
  batchSize?: number
88
87
  }
89
88
 
89
+ /**
90
+ * A progress event from {@link fetchTIGER}.
91
+ *
92
+ * Each event reports a file downloaded or found in the cache, a shapefile extracted, or rows inserted.
93
+ */
90
94
  export type FetchTIGEREvent =
91
95
  | { phase: "download"; file: string; cached: boolean }
92
96
  | { phase: "extract"; file: string }
93
97
  | { phase: "load"; inserted: number; total: number }
94
98
 
99
+ /**
100
+ * The database path, table name and inserted row count that {@link fetchTIGER} returns.
101
+ */
95
102
  export interface FetchTIGERResult {
96
103
  outPath: string
97
104
  table: string
98
105
  inserted: number
99
106
  }
100
107
 
101
- /**
102
- * The isp-nexus column map for `tabblock20`. Geometry rides along implicitly.
103
- */
104
108
  function blockSelectSQL(layer: string, county?: string): string {
105
109
  const where = county ? ` WHERE COUNTYFP20 = '${county}'` : ""
106
110
 
@@ -121,7 +125,6 @@ function selectSQL(level: TIGERFetchLevel, layer: string, county?: string): stri
121
125
  case "place":
122
126
  return `SELECT GEOID AS geoid, NAME AS name, STATEFP AS statefp, LSAD AS lsad, NAMELSAD AS namelsad, CLASSFP AS classfp FROM "${layer}"`
123
127
  case "addrfeat":
124
- // ADDRFEAT has no STATEFP column — injected per-row from the state we're fetching.
125
128
  return `SELECT LINEARID AS linearid, FULLNAME AS fullname, ZIPL AS zipl, ZIPR AS zipr FROM "${layer}" WHERE FULLNAME IS NOT NULL AND FULLNAME != ''`
126
129
  }
127
130
  }
@@ -161,16 +164,12 @@ function buildRow(level: TIGERFetchLevel, p: Record<string, unknown>, geometry:
161
164
  land_area_sqm: Number(p.land_area_sqm ?? 0),
162
165
  water_area_sqm: Number(p.water_area_sqm ?? 0),
163
166
  population: Number(p.population ?? 0),
164
- geometry: JSON.stringify(geometry),
167
+ geometry: stringifyJSON(geometry),
165
168
  }
166
169
  }
167
170
  }
168
171
 
169
- /**
170
- * Scrape the ADDRFEAT directory listing for a state's county FIPS codes.
171
- */
172
172
  async function discoverCounties(state: string, vintage: number): Promise<string[]> {
173
- // The LISTING only — a small HTML index. The per-county archives below stay on raw `fetch`, streaming to disk.
174
173
  const html = await new APIClient({ displayName: "tiger-listing", retry: true })
175
174
  .fetch<string>({ url: `${CENSUS_HOST}/geo/tiger/TIGER${vintage}/ADDRFEAT/`, responseType: "text" })
176
175
  .then(pluckResponseData)
@@ -186,26 +185,25 @@ async function discoverCounties(state: string, vintage: number): Promise<string[
186
185
  }
187
186
 
188
187
  /**
189
- * Fetch one state's TIGER data at `level` into a SQLite DB. Yields progress; returns the final tally.
188
+ * Downloads one state's TIGER/Line files at `level`, loads them into a SQLite database
189
+ * and yields progress events.
190
+ *
191
+ * It first deletes the state's existing rows from the target table so that a rerun replaces them.
190
192
  */
191
193
  export async function* fetchTIGER(options: FetchTIGEROptions): AsyncGenerator<FetchTIGEREvent, FetchTIGERResult> {
192
194
  const level = options.level ?? "tabblock20"
193
195
  const vintage = options.vintage ?? (level === "tabblock20" ? 2020 : 2024)
194
- const dataRoot = options.dataRoot ?? DEFAULT_DATA_ROOT
196
+ const dataRoot = PathBuilder.from(options.dataRoot ?? DEFAULT_DATA_ROOT)
195
197
  const batchSize = options.batchSize ?? 1000
196
198
  const state = options.stateFIPS
197
199
  const table = LEVEL_TABLE[level]
198
200
 
199
- const cacheDir = join(dataRoot, "tiger", String(vintage), state)
200
- // Default to a stable, vintage-agnostic `tiger.db` — the filename the corpus `tiger` adapter reads
201
- // (run-corpus-build → `${ROOT}/tiger/tiger.db`). The vintage is a content detail, not a path one;
202
- // the per-table idempotent delete keeps a re-fetch (newer vintage) clean. The download CACHE stays
203
- // vintage-partitioned below so zips don't collide across vintages.
204
- const outPath = options.outPath ?? join(dataRoot, "tiger", "tiger.db")
201
+ const cacheDir = dataRoot("tiger", String(vintage), state)
202
+
203
+ const outPath = PathBuilder.from(options.outPath ?? dataRoot("tiger", "tiger.db"))
205
204
  await makeDirectories(cacheDir)
206
- await makeDirectories(dirname(outPath))
205
+ await makeDirectories(outPath.dirname())
207
206
 
208
- // Source units: one (per-state) for block/place; one per county for addrfeat.
209
207
  const geoCodes = level === "addrfeat" ? await discoverCounties(state, vintage) : [""]
210
208
 
211
209
  if (level === "addrfeat" && !geoCodes.length) {
@@ -216,11 +214,6 @@ export async function* fetchTIGER(options: FetchTIGEROptions): AsyncGenerator<Fe
216
214
  kdb.exec(TIGER_PRAGMAS)
217
215
  await initializeTIGERSchema(kdb)
218
216
 
219
- /**
220
- * `level` picks the table AND the row shape, but nothing ties them at the type level: {@link buildRow} returns the
221
- * union and Kysely needs the arm's concrete row. The narrowings below are sound only because `buildRow` switches on
222
- * the SAME `level` — keep the two switches in step, or a row shape reaches the wrong table.
223
- */
224
217
  const insertBatch = async (rows: Row[]): Promise<void> => {
225
218
  if (level === "tabblock20") {
226
219
  await kdb
@@ -241,7 +234,6 @@ export async function* fetchTIGER(options: FetchTIGEROptions): AsyncGenerator<Fe
241
234
  }
242
235
 
243
236
  try {
244
- // Idempotent re-run: drop this state's rows first (state_code for blocks, statefp otherwise).
245
237
  if (level === "tabblock20") {
246
238
  await kdb.deleteFrom("tabblock20").where("state_code", "=", state).execute()
247
239
  } else if (level === "place") {
@@ -264,7 +256,7 @@ export async function* fetchTIGER(options: FetchTIGEROptions): AsyncGenerator<Fe
264
256
  for (const geo of geoCodes) {
265
257
  const unit = level === "addrfeat" ? state + geo : state
266
258
  const zipName = `tl_${vintage}_${unit}_${level}.zip`
267
- const zipPath = join(cacheDir, zipName)
259
+ const zipPath = cacheDir(zipName)
268
260
  const url = `${CENSUS_HOST}/geo/tiger/TIGER${vintage}/${LEVEL_DIR[level]}/${zipName}`
269
261
 
270
262
  const cached = await downloadIfNeeded(url, zipPath)
@@ -272,7 +264,7 @@ export async function* fetchTIGER(options: FetchTIGEROptions): AsyncGenerator<Fe
272
264
 
273
265
  await extractZipEntries(zipPath, cacheDir)
274
266
  const layer = `tl_${vintage}_${unit}_${level}`
275
- const shpPath = join(cacheDir, layer + ".shp")
267
+ const shpPath = cacheDir(layer + ".shp")
276
268
  yield { phase: "extract", file: layer + ".shp" }
277
269
 
278
270
  const args = [
@@ -307,7 +299,7 @@ export async function* fetchTIGER(options: FetchTIGEROptions): AsyncGenerator<Fe
307
299
 
308
300
  kdb.exec("PRAGMA wal_checkpoint(TRUNCATE);")
309
301
 
310
- return { outPath, table, inserted }
302
+ return { outPath: outPath.toString(), table, inserted }
311
303
  } finally {
312
304
  await kdb.destroy()
313
305
  }