mailwoman 10.4.0 → 10.6.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/cli/commands/corpus/download.tsx +33 -7
- package/cli/commands/corpus/fetch.tsx +3 -0
- package/cli/commands/corpus/sub/venue/extract.tsx +4 -4
- package/cli/commands/corpus/upload.tsx +6 -5
- package/cli/commands/eval/pins.tsx +4 -4
- package/cli/commands/gazetteer/build/poi/coverage.tsx +2 -2
- package/cli/commands/gazetteer/build/poi/index.tsx +4 -4
- package/cli/commands/gazetteer/inspect/sync/index.tsx +42 -12
- package/cli/commands/gazetteer/repos-sync.tsx +40 -28
- package/cli/commands/release/merge-admin.tsx +3 -3
- package/cli/doctor/checks.ts +20 -2
- package/cli/doctor/runner.ts +37 -1
- package/cli/kit/progress.ts +103 -0
- package/cli/kit.ts +1 -0
- package/lib/data/bom.ts +3 -0
- package/lib/data/bundles.ts +1 -1
- package/lib/data/inventory.ts +12 -0
- package/lib/data/obligations.ts +35 -0
- package/lib/fork-entity.ts +6 -0
- package/lib/geocode/layer-paths.ts +43 -0
- package/lib/geocode/osm-layers.ts +47 -0
- package/lib/geocode/parse-priors.ts +1 -1
- package/lib/geocode/result.ts +30 -3
- package/lib/geocode/session.ts +3 -9
- package/lib/plus-code-override.ts +1 -0
- package/man/mailwoman.1 +1 -1
- package/out/cli/commands/corpus/download.d.ts.map +1 -1
- package/out/cli/commands/corpus/download.js +30 -6
- package/out/cli/commands/corpus/download.js.map +1 -1
- package/out/cli/commands/corpus/fetch.d.ts.map +1 -1
- package/out/cli/commands/corpus/fetch.js +3 -0
- package/out/cli/commands/corpus/fetch.js.map +1 -1
- package/out/cli/commands/corpus/sub/venue/extract.js +4 -4
- package/out/cli/commands/corpus/sub/venue/extract.js.map +1 -1
- package/out/cli/commands/corpus/upload.d.ts.map +1 -1
- package/out/cli/commands/corpus/upload.js +2 -4
- package/out/cli/commands/corpus/upload.js.map +1 -1
- package/out/cli/commands/eval/pins.js +4 -4
- package/out/cli/commands/eval/pins.js.map +1 -1
- package/out/cli/commands/gazetteer/build/poi/coverage.js +2 -2
- package/out/cli/commands/gazetteer/build/poi/coverage.js.map +1 -1
- package/out/cli/commands/gazetteer/build/poi/index.js +4 -4
- package/out/cli/commands/gazetteer/build/poi/index.js.map +1 -1
- package/out/cli/commands/gazetteer/inspect/sync/index.d.ts.map +1 -1
- package/out/cli/commands/gazetteer/inspect/sync/index.js +30 -7
- package/out/cli/commands/gazetteer/inspect/sync/index.js.map +1 -1
- package/out/cli/commands/gazetteer/repos-sync.d.ts.map +1 -1
- package/out/cli/commands/gazetteer/repos-sync.js +22 -26
- package/out/cli/commands/gazetteer/repos-sync.js.map +1 -1
- package/out/cli/commands/release/merge-admin.js +3 -3
- package/out/cli/commands/release/merge-admin.js.map +1 -1
- package/out/cli/doctor/checks.d.ts +1 -1
- package/out/cli/doctor/checks.d.ts.map +1 -1
- package/out/cli/doctor/checks.js +17 -2
- package/out/cli/doctor/checks.js.map +1 -1
- package/out/cli/doctor/runner.d.ts +6 -1
- package/out/cli/doctor/runner.d.ts.map +1 -1
- package/out/cli/doctor/runner.js +24 -2
- package/out/cli/doctor/runner.js.map +1 -1
- package/out/cli/kit/progress.d.ts +55 -0
- package/out/cli/kit/progress.d.ts.map +1 -0
- package/out/cli/kit/progress.js +46 -0
- package/out/cli/kit/progress.js.map +1 -0
- package/out/cli/kit.d.ts +1 -0
- package/out/cli/kit.d.ts.map +1 -1
- package/out/cli/kit.js +1 -0
- package/out/cli/kit.js.map +1 -1
- package/out/data/bom.d.ts.map +1 -1
- package/out/data/bom.js +3 -0
- package/out/data/bom.js.map +1 -1
- package/out/data/bundles.js +1 -1
- package/out/data/bundles.js.map +1 -1
- package/out/data/inventory.d.ts +12 -0
- package/out/data/inventory.d.ts.map +1 -1
- package/out/data/inventory.js.map +1 -1
- package/out/data/obligations.d.ts +12 -0
- package/out/data/obligations.d.ts.map +1 -1
- package/out/data/obligations.js +25 -0
- package/out/data/obligations.js.map +1 -1
- package/out/fork-entity.d.ts +4 -0
- package/out/fork-entity.d.ts.map +1 -1
- package/out/fork-entity.js +2 -0
- package/out/fork-entity.js.map +1 -1
- package/out/geocode/layer-paths.d.ts +25 -0
- package/out/geocode/layer-paths.d.ts.map +1 -1
- package/out/geocode/layer-paths.js +27 -0
- package/out/geocode/layer-paths.js.map +1 -1
- package/out/geocode/osm-layers.d.ts +27 -0
- package/out/geocode/osm-layers.d.ts.map +1 -0
- package/out/geocode/osm-layers.js +42 -0
- package/out/geocode/osm-layers.js.map +1 -0
- package/out/geocode/parse-priors.js +1 -1
- package/out/geocode/result.d.ts.map +1 -1
- package/out/geocode/result.js +15 -0
- package/out/geocode/result.js.map +1 -1
- package/out/geocode/session.d.ts.map +1 -1
- package/out/geocode/session.js +3 -8
- package/out/geocode/session.js.map +1 -1
- package/out/plus-code-override.d.ts.map +1 -1
- package/out/plus-code-override.js +1 -0
- package/out/plus-code-override.js.map +1 -1
- package/out/tools/coverage/core.js +2 -2
- package/out/tools/coverage/core.js.map +1 -1
- package/out/tools/coverage/jurisdictions.d.ts.map +1 -1
- package/out/tools/coverage/jurisdictions.js +4 -3
- package/out/tools/coverage/jurisdictions.js.map +1 -1
- package/out/tools/dev-tools/build/oa-coord-golden.run.js +1 -1
- package/out/tools/dev-tools/build/oa-coord-golden.run.js.map +1 -1
- package/out/tools/dev-tools/corpus/freeze-overlay-manifest.run.d.ts +31 -0
- package/out/tools/dev-tools/corpus/freeze-overlay-manifest.run.d.ts.map +1 -0
- package/out/tools/dev-tools/corpus/freeze-overlay-manifest.run.js +125 -0
- package/out/tools/dev-tools/corpus/freeze-overlay-manifest.run.js.map +1 -0
- package/out/tools/dev-tools/harness-neural.run.js +2 -2
- package/out/tools/dev-tools/harness-neural.run.js.map +1 -1
- package/out/tools/dev-tools/heldout-geography-eval.run.js +9 -9
- package/out/tools/dev-tools/heldout-geography-eval.run.js.map +1 -1
- package/out/tools/dev-tools/lint-mdx-angles.js +2 -2
- package/out/tools/dev-tools/lint-mdx-angles.js.map +1 -1
- package/out/tools/dev-tools/locality/context-swap.run.js +2 -1
- package/out/tools/dev-tools/locality/context-swap.run.js.map +1 -1
- package/out/tools/eval-harness/external-arenas.d.ts +1 -1
- package/out/tools/eval-harness/external-arenas.d.ts.map +1 -1
- package/out/tools/eval-harness/external-arenas.js +29 -9
- package/out/tools/eval-harness/external-arenas.js.map +1 -1
- package/out/tools/eval-harness/gauntlet/harness.js +2 -2
- package/out/tools/eval-harness/gauntlet/harness.js.map +1 -1
- package/out/tools/eval-harness/semantic-utility/run.d.ts +1 -1
- package/out/tools/gazetteer-pipeline/postcode/codepoint/database.d.ts +2 -1
- package/out/tools/gazetteer-pipeline/postcode/codepoint/database.d.ts.map +1 -1
- package/out/tools/gazetteer-pipeline/postcode/codepoint/database.js +2 -1
- package/out/tools/gazetteer-pipeline/postcode/codepoint/database.js.map +1 -1
- package/out/tools/gazetteer-pipeline/postcode/codepoint/fetch.d.ts +4 -38
- package/out/tools/gazetteer-pipeline/postcode/codepoint/fetch.d.ts.map +1 -1
- package/out/tools/gazetteer-pipeline/postcode/codepoint/fetch.js +1 -5
- package/out/tools/gazetteer-pipeline/postcode/codepoint/fetch.js.map +1 -1
- package/out/tools/gazetteer-pipeline/postcode/codepoint.d.ts +2 -3
- package/out/tools/gazetteer-pipeline/postcode/codepoint.d.ts.map +1 -1
- package/out/tools/gazetteer-pipeline/postcode/codepoint.js +2 -3
- package/out/tools/gazetteer-pipeline/postcode/codepoint.js.map +1 -1
- package/out/tools/gazetteer-pipeline/postcode/geonames/tail.js +1 -1
- package/out/tools/gazetteer-pipeline/postcode/ni/osm/parse.d.ts +1 -1
- package/out/tools/gazetteer-pipeline/postcode/ni/osm/parse.js +2 -2
- package/out/tools/gazetteer-pipeline/postcode/ni/osm/parse.js.map +1 -1
- package/out/tools/gazetteer-pipeline/repos/audit.js +2 -2
- package/out/tools/gazetteer-pipeline/repos/audit.js.map +1 -1
- package/out/tools/gazetteer-pipeline/repos/sync.d.ts +18 -1
- package/out/tools/gazetteer-pipeline/repos/sync.d.ts.map +1 -1
- package/out/tools/gazetteer-pipeline/repos/sync.js +58 -32
- package/out/tools/gazetteer-pipeline/repos/sync.js.map +1 -1
- package/out/tools/gazetteer-pipeline/stamp-manifest.d.ts.map +1 -1
- package/out/tools/gazetteer-pipeline/stamp-manifest.js +2 -6
- package/out/tools/gazetteer-pipeline/stamp-manifest.js.map +1 -1
- package/out/tools/gazetteer-pipeline/uprn-layer.d.ts +2 -14
- package/out/tools/gazetteer-pipeline/uprn-layer.d.ts.map +1 -1
- package/out/tools/gazetteer-pipeline/uprn-layer.js +2 -1
- package/out/tools/gazetteer-pipeline/uprn-layer.js.map +1 -1
- package/out/tools/generate-clients.d.ts.map +1 -1
- package/out/tools/generate-clients.js +15 -7
- package/out/tools/generate-clients.js.map +1 -1
- package/out/tools/release-tools/publish/hf.d.ts.map +1 -1
- package/out/tools/release-tools/publish/hf.js +21 -18
- package/out/tools/release-tools/publish/hf.js.map +1 -1
- package/out/tools/tiles/publish.d.ts.map +1 -1
- package/out/tools/tiles/publish.js +2 -2
- package/out/tools/tiles/publish.js.map +1 -1
- package/package.json +42 -39
- package/tools/coverage/core.ts +2 -2
- package/tools/coverage/jurisdictions.ts +5 -3
- package/tools/dev-tools/build/oa-coord-golden.run.ts +1 -1
- package/tools/dev-tools/corpus/freeze-overlay-manifest.run.ts +162 -0
- package/tools/dev-tools/harness-neural.run.ts +2 -2
- package/tools/dev-tools/heldout-geography-eval.run.ts +13 -10
- package/tools/dev-tools/lint-mdx-angles.ts +2 -2
- package/tools/dev-tools/locality/context-swap.run.ts +2 -1
- package/tools/eval-harness/external-arenas.ts +29 -12
- package/tools/eval-harness/gauntlet/harness.ts +2 -2
- package/tools/eval-harness/premise-linkage/adapter.ts +1 -1
- package/tools/eval-harness/semantic-utility/run.ts +1 -1
- package/tools/gazetteer-pipeline/postcode/codepoint/database.ts +1 -3
- package/tools/gazetteer-pipeline/postcode/codepoint/fetch.ts +5 -48
- package/tools/gazetteer-pipeline/postcode/codepoint.ts +2 -3
- package/tools/gazetteer-pipeline/postcode/geonames/tail.ts +1 -1
- package/tools/gazetteer-pipeline/postcode/ni/osm/parse.ts +2 -2
- package/tools/gazetteer-pipeline/repos/audit.ts +2 -2
- package/tools/gazetteer-pipeline/repos/sync.ts +79 -32
- package/tools/gazetteer-pipeline/stamp-manifest.ts +2 -6
- package/tools/gazetteer-pipeline/uprn-layer.ts +4 -17
- package/tools/generate-clients.ts +16 -7
- package/tools/release-tools/publish/hf.ts +21 -19
- package/tools/tiles/publish.ts +2 -2
- package/lib/process.ts +0 -36
- package/out/process.d.ts +0 -15
- package/out/process.d.ts.map +0 -1
- package/out/process.js +0 -27
- package/out/process.js.map +0 -1
- package/out/tools/gazetteer-pipeline/postcode/codepoint/parse.d.ts +0 -126
- package/out/tools/gazetteer-pipeline/postcode/codepoint/parse.d.ts.map +0 -1
- package/out/tools/gazetteer-pipeline/postcode/codepoint/parse.js +0 -117
- package/out/tools/gazetteer-pipeline/postcode/codepoint/parse.js.map +0 -1
- package/out/tools/gazetteer-pipeline/postcode/display-form.d.ts +0 -14
- package/out/tools/gazetteer-pipeline/postcode/display-form.d.ts.map +0 -1
- package/out/tools/gazetteer-pipeline/postcode/display-form.js +0 -16
- package/out/tools/gazetteer-pipeline/postcode/display-form.js.map +0 -1
- package/tools/gazetteer-pipeline/postcode/codepoint/parse.ts +0 -210
- package/tools/gazetteer-pipeline/postcode/display-form.ts +0 -16
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { dataRootPath } from "@mailwoman/core/data-root"
|
|
16
|
+
import { runProcess } from "@mailwoman/core/process"
|
|
16
17
|
import { Box, Text } from "ink"
|
|
17
18
|
import { useState } from "react"
|
|
18
19
|
|
|
@@ -52,17 +53,26 @@ const CorpusDownload: CommandComponent<typeof spec> = ({ options }) => {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
useCommandTask(async () => {
|
|
55
|
-
const { $ } = await import("zx")
|
|
56
|
-
|
|
57
56
|
const rcloneBase = `:s3:${options.bucket}`
|
|
58
|
-
const dryFlag = options.dryRun ? "--dry-run" :
|
|
57
|
+
const dryFlag = options.dryRun ? ["--dry-run"] : []
|
|
59
58
|
const out = options.outDir
|
|
60
59
|
|
|
61
60
|
// Step 0: Download v0.3.0 corpus
|
|
62
61
|
updateStep(0, { status: "running" })
|
|
63
62
|
|
|
64
63
|
try {
|
|
65
|
-
await
|
|
64
|
+
await runProcess("rclone", [
|
|
65
|
+
"sync",
|
|
66
|
+
`${rcloneBase}/corpus/v0.3.0/`,
|
|
67
|
+
`${out}/corpus/versioned/v0.3.0/corpus-v0.3.0/`,
|
|
68
|
+
"--progress",
|
|
69
|
+
"--transfers",
|
|
70
|
+
"8",
|
|
71
|
+
"--checkers",
|
|
72
|
+
"16",
|
|
73
|
+
...dryFlag,
|
|
74
|
+
])
|
|
75
|
+
|
|
66
76
|
updateStep(0, { status: "done" })
|
|
67
77
|
} catch (error: unknown) {
|
|
68
78
|
const e = error as Record<string, unknown>
|
|
@@ -75,7 +85,16 @@ const CorpusDownload: CommandComponent<typeof spec> = ({ options }) => {
|
|
|
75
85
|
updateStep(1, { status: "running" })
|
|
76
86
|
|
|
77
87
|
try {
|
|
78
|
-
await
|
|
88
|
+
await runProcess("rclone", [
|
|
89
|
+
"sync",
|
|
90
|
+
`${rcloneBase}/corpus/v0.4.0/`,
|
|
91
|
+
`${out}/corpus/versioned/v0.4.0/corpus-v0.4.0/`,
|
|
92
|
+
"--progress",
|
|
93
|
+
"--transfers",
|
|
94
|
+
"4",
|
|
95
|
+
...dryFlag,
|
|
96
|
+
])
|
|
97
|
+
|
|
79
98
|
updateStep(1, { status: "done" })
|
|
80
99
|
} catch (error: unknown) {
|
|
81
100
|
const e = error as Record<string, unknown>
|
|
@@ -88,7 +107,14 @@ const CorpusDownload: CommandComponent<typeof spec> = ({ options }) => {
|
|
|
88
107
|
updateStep(2, { status: "running" })
|
|
89
108
|
|
|
90
109
|
try {
|
|
91
|
-
await
|
|
110
|
+
await runProcess("rclone", [
|
|
111
|
+
"sync",
|
|
112
|
+
`${rcloneBase}/models/tokenizer/`,
|
|
113
|
+
`${out}/models/tokenizer/`,
|
|
114
|
+
"--progress",
|
|
115
|
+
...dryFlag,
|
|
116
|
+
])
|
|
117
|
+
|
|
92
118
|
updateStep(2, { status: "done" })
|
|
93
119
|
} catch (error: unknown) {
|
|
94
120
|
const e = error as Record<string, unknown>
|
|
@@ -101,7 +127,7 @@ const CorpusDownload: CommandComponent<typeof spec> = ({ options }) => {
|
|
|
101
127
|
updateStep(3, { status: "running" })
|
|
102
128
|
|
|
103
129
|
try {
|
|
104
|
-
await
|
|
130
|
+
await runProcess("rclone", ["sync", `${rcloneBase}/corpus-python/`, "./corpus-python/", "--progress", ...dryFlag])
|
|
105
131
|
updateStep(3, { status: "done" })
|
|
106
132
|
} catch (error: unknown) {
|
|
107
133
|
const e = error as Record<string, unknown>
|
|
@@ -132,6 +132,7 @@ async function runSource(source: FetchSourceID, options: Options): Promise<Fetch
|
|
|
132
132
|
const { fetchCORDIS } = await import("@mailwoman/corpus/tools/fetch/cordis")
|
|
133
133
|
const { fetchAnnuaireEducationOverseas } = await import("@mailwoman/corpus/fr/tools/fetch/annuaire-education")
|
|
134
134
|
const { fetchFinessOverseas } = await import("@mailwoman/corpus/fr/tools/fetch/finess")
|
|
135
|
+
const { fetchGBOSOpen } = await import("@mailwoman/corpus/gb/tools/fetch/os-open")
|
|
135
136
|
const { fetchGLEIF } = await import("@mailwoman/corpus/tools/fetch/gleif")
|
|
136
137
|
const { fetchVaris } = await import("@mailwoman/corpus/lv/tools/fetch/varis")
|
|
137
138
|
const { fetchVlaanderenAD } = await import("@mailwoman/corpus/tools/fetch/vlaanderen-ad")
|
|
@@ -185,6 +186,8 @@ async function runSource(source: FetchSourceID, options: Options): Promise<Fetch
|
|
|
185
186
|
return fetchAnnuaireEducationOverseas(base, reportToStderr)
|
|
186
187
|
case "fr-finess-overseas":
|
|
187
188
|
return fetchFinessOverseas(base, reportToStderr)
|
|
189
|
+
case "gb-os-open":
|
|
190
|
+
return fetchGBOSOpen(base, reportToStderr)
|
|
188
191
|
case "gleif":
|
|
189
192
|
return fetchGLEIF(base, reportToStderr)
|
|
190
193
|
case "ro-retea-scolara":
|
|
@@ -40,12 +40,12 @@ export const spec = {
|
|
|
40
40
|
|
|
41
41
|
const CorpusSubVenueExtract: CommandComponent<typeof spec> = ({ options }) => {
|
|
42
42
|
const state = useCommandTask(async () => {
|
|
43
|
-
// @mailwoman/osm is
|
|
44
|
-
//
|
|
43
|
+
// @mailwoman/osm is an optional peer that an operator installs to opt in to OpenStreetMap data, so a
|
|
44
|
+
// static import would break every install without it. Lazy-load it and say how to install it when absent.
|
|
45
45
|
const { writeSubVenueJSONL } = await import("@mailwoman/osm/sdk/extract/subvenue").catch(() => {
|
|
46
46
|
throw new Error(
|
|
47
|
-
"corpus sub-venue-extract requires @mailwoman/osm,
|
|
48
|
-
"
|
|
47
|
+
"corpus sub-venue-extract requires @mailwoman/osm, an optional package — install it with " +
|
|
48
|
+
"`npm install @mailwoman/osm` (its README states the OpenStreetMap license terms)."
|
|
49
49
|
)
|
|
50
50
|
})
|
|
51
51
|
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { dataRootPath } from "@mailwoman/core/data-root"
|
|
11
11
|
import { pathExists } from "@mailwoman/core/fs/readers/stat"
|
|
12
|
+
import { runProcess } from "@mailwoman/core/process"
|
|
12
13
|
import { extractDelimited } from "@mailwoman/core/scripting/arguments"
|
|
13
14
|
import { childEnv } from "@mailwoman/core/scripting/utils"
|
|
14
15
|
import type { BuildCorpusManifest } from "@mailwoman/corpus/build"
|
|
@@ -235,8 +236,6 @@ const CorpusUpload: CommandComponent<typeof spec> = ({ options }) => {
|
|
|
235
236
|
const [steps, setSteps] = useState<Step[]>([])
|
|
236
237
|
|
|
237
238
|
const state = useCommandTask(async () => {
|
|
238
|
-
const { $ } = await import("zx")
|
|
239
|
-
|
|
240
239
|
const corpusRoot = PathBuilder.from(options.corpusDir ?? dataRootPath("corpus", "versioned"))
|
|
241
240
|
|
|
242
241
|
const directories = extractDelimited(options.corpusDirectory)
|
|
@@ -350,9 +349,11 @@ const CorpusUpload: CommandComponent<typeof spec> = ({ options }) => {
|
|
|
350
349
|
update(index, { status: "running" })
|
|
351
350
|
|
|
352
351
|
try {
|
|
353
|
-
await
|
|
354
|
-
|
|
355
|
-
|
|
352
|
+
await runProcess(
|
|
353
|
+
"rclone",
|
|
354
|
+
["sync", job.source.toString(), job.dest, ...job.extra, ...dry, ...R2_RETRIES, "--stats-one-line"],
|
|
355
|
+
{ env }
|
|
356
|
+
)
|
|
356
357
|
|
|
357
358
|
update(index, { status: "done", detail: options.dryRun ? "would sync" : "synced" })
|
|
358
359
|
} catch (error: unknown) {
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
import { gitHead } from "@mailwoman/core/git"
|
|
18
18
|
import { parseJSONStrict, stringifyJSON } from "@mailwoman/core/json"
|
|
19
19
|
import { repoRootPath } from "@mailwoman/core/paths"
|
|
20
|
+
import { runProcess } from "@mailwoman/core/process"
|
|
20
21
|
import { Box, Text } from "ink"
|
|
21
|
-
import { $ } from "zx"
|
|
22
22
|
|
|
23
23
|
import { type CommandSpec, CommandTaskResult, type CommandComponent, useCommandTask } from "#cli-kit"
|
|
24
24
|
|
|
@@ -68,17 +68,17 @@ async function reportStalePins(drift: string, testPath: string): Promise<string>
|
|
|
68
68
|
`Run \`mailwoman eval pins --update\`, commit ${testPath}, and this issue closes on the next green audit.`
|
|
69
69
|
|
|
70
70
|
const search = `in:title ${stringifyJSON(STALE_PIN_ISSUE_TITLE)}`
|
|
71
|
-
const issueList = await
|
|
71
|
+
const issueList = await runProcess("gh", ["issue", "list", "--state", "open", "--search", search, "--json", "number"])
|
|
72
72
|
const existing = parseJSONStrict<Array<{ number: number }>>(issueList.stdout)
|
|
73
73
|
const first = existing[0]
|
|
74
74
|
|
|
75
75
|
if (first) {
|
|
76
|
-
await
|
|
76
|
+
await runProcess("gh", ["issue", "comment", String(first.number), "--body", body])
|
|
77
77
|
|
|
78
78
|
return `updated issue #${first.number}`
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
await
|
|
81
|
+
await runProcess("gh", ["issue", "create", "--title", STALE_PIN_ISSUE_TITLE, "--body", body])
|
|
82
82
|
|
|
83
83
|
return "opened the audit issue"
|
|
84
84
|
}
|
|
@@ -117,8 +117,8 @@ const GazetteerBuildPOICoverage: CommandComponent<typeof spec> = ({ options }) =
|
|
|
117
117
|
const out = options.out ?? poiDatabasePath(`poi-coverage-${options.category}-${slugify(region)}.db`)
|
|
118
118
|
const buildSHA = resolveBuildSHA(repoRootPath())
|
|
119
119
|
|
|
120
|
-
// Dynamic import is required: @mailwoman/osm is
|
|
121
|
-
// import breaks the published CLI on
|
|
120
|
+
// Dynamic import is required: @mailwoman/osm is an optional peer, so a top-level
|
|
121
|
+
// import breaks the published CLI on an install without it.
|
|
122
122
|
const [{ extractOSMBoundary }, { extractOSMPOIs, tagRuleFromOSMTag }] = await Promise.all([
|
|
123
123
|
import("@mailwoman/osm/sdk/extract/boundary"),
|
|
124
124
|
import("@mailwoman/osm/sdk/extract/poi"),
|
|
@@ -115,10 +115,10 @@ const GazetteerBuildPOI: CommandComponent<typeof spec> = ({ options }) => {
|
|
|
115
115
|
// twice, once here to derive coverage, once as buildPOIDatabase's `rows` injection point.
|
|
116
116
|
const rows: POISourceRow[] = []
|
|
117
117
|
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
118
|
+
// Dynamic import, required: @mailwoman/osm is an optional peer that an operator installs
|
|
119
|
+
// to opt in to OpenStreetMap data, and this dependency belongs only on the selected build
|
|
120
|
+
// path, so a top-level import breaks the whole published CLI on an install without it.
|
|
121
|
+
// The smoke test's clean-install leg is what catches it.
|
|
122
122
|
// The osm source branch is build-local by design.
|
|
123
123
|
// It may only resolve its SDK when a run selects it.
|
|
124
124
|
const { extractOSMPOIs } = await import("@mailwoman/osm/sdk/extract/poi")
|
|
@@ -21,6 +21,9 @@ import { ProgressBar } from "@inkjs/ui"
|
|
|
21
21
|
import type { RepositorySource, SynchronizeAction } from "@mailwoman/core"
|
|
22
22
|
import { wofReposPath } from "@mailwoman/core/data-root"
|
|
23
23
|
import { ByteFormatter } from "@mailwoman/core/fs/formatters"
|
|
24
|
+
import { GitClient } from "@mailwoman/core/git"
|
|
25
|
+
import { parseJSONStrict } from "@mailwoman/core/json"
|
|
26
|
+
import { runFile } from "@mailwoman/core/process"
|
|
24
27
|
import { formatQuantity } from "@mailwoman/core/resources/locale"
|
|
25
28
|
import { CommandError } from "@mailwoman/core/scripting/command"
|
|
26
29
|
import { Box, Text } from "ink"
|
|
@@ -33,7 +36,9 @@ import {
|
|
|
33
36
|
type CommandSpec,
|
|
34
37
|
CommandTaskResult,
|
|
35
38
|
type CommandComponent,
|
|
39
|
+
ProgressLines,
|
|
36
40
|
useCommandTask,
|
|
41
|
+
useProgressFeed,
|
|
37
42
|
} from "#cli-kit"
|
|
38
43
|
import {
|
|
39
44
|
assertDestinationNotARepoName,
|
|
@@ -92,12 +97,25 @@ interface SyncPlan {
|
|
|
92
97
|
* failure surfaces as an opaque spawn error from deep inside the task.
|
|
93
98
|
*/
|
|
94
99
|
async function discoverRepos(): Promise<DiscoveredRepo[]> {
|
|
95
|
-
const { $ } = await import("zx")
|
|
96
|
-
|
|
97
100
|
try {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
const { stdout } = await runFile("gh", [
|
|
102
|
+
"repo",
|
|
103
|
+
"list",
|
|
104
|
+
WOF_REPO_OWNER,
|
|
105
|
+
"--no-archived",
|
|
106
|
+
"--limit",
|
|
107
|
+
"1000",
|
|
108
|
+
"--json",
|
|
109
|
+
"name",
|
|
110
|
+
"--json",
|
|
111
|
+
"url",
|
|
112
|
+
"--json",
|
|
113
|
+
"diskUsage",
|
|
114
|
+
])
|
|
115
|
+
|
|
116
|
+
return parseJSONStrict<Array<{ name: string; url: string; diskUsage: number }>>(stdout).map(
|
|
117
|
+
({ name, url, diskUsage }) => ({ name, url, diskUsageKB: diskUsage })
|
|
118
|
+
)
|
|
101
119
|
} catch (error) {
|
|
102
120
|
throw new CommandError(
|
|
103
121
|
`Could not list ${WOF_REPO_OWNER} through \`gh\`. Install the GitHub CLI and run \`gh auth login\`.`,
|
|
@@ -110,6 +128,7 @@ const WOFSync: CommandComponent<typeof spec, [string?]> = ({ options, args }) =>
|
|
|
110
128
|
const [plan, setPlan] = useState<SyncPlan>()
|
|
111
129
|
const [active, setActive] = useState<readonly string[]>([])
|
|
112
130
|
const [doneCount, setDoneCount] = useState(0)
|
|
131
|
+
const progress = useProgressFeed()
|
|
113
132
|
|
|
114
133
|
const state = useCommandTask(
|
|
115
134
|
async () => {
|
|
@@ -197,11 +216,18 @@ const WOFSync: CommandComponent<typeof spec, [string?]> = ({ options, args }) =>
|
|
|
197
216
|
let action: SynchronizeAction | null = null
|
|
198
217
|
let failure: string | null = null
|
|
199
218
|
|
|
219
|
+
await using client = new GitClient({ cwd: destination })
|
|
220
|
+
|
|
221
|
+
const unwatch = progress.watch(client, source.name)
|
|
222
|
+
|
|
200
223
|
try {
|
|
201
|
-
action = await synchronizeRepo(source, destination)
|
|
224
|
+
action = await synchronizeRepo(source, destination, client)
|
|
202
225
|
} catch (error) {
|
|
203
226
|
ok = false
|
|
204
227
|
failure = error instanceof Error ? error.message : String(error)
|
|
228
|
+
} finally {
|
|
229
|
+
unwatch()
|
|
230
|
+
progress.clear(source.name)
|
|
205
231
|
}
|
|
206
232
|
|
|
207
233
|
const elapsed = `${((performance.now() - startedAt) / 1000).toFixed(1)}s`
|
|
@@ -260,12 +286,16 @@ const WOFSync: CommandComponent<typeof spec, [string?]> = ({ options, args }) =>
|
|
|
260
286
|
{ByteFormatter.formatSI(plan.selection.totalDiskUsageKB * 1024)}
|
|
261
287
|
</Text>
|
|
262
288
|
|
|
263
|
-
{active
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
289
|
+
{active
|
|
290
|
+
.filter((name) => !progress.entries.has(name))
|
|
291
|
+
.map((name) => (
|
|
292
|
+
<Text key={name} color="cyan">
|
|
293
|
+
{" "}
|
|
294
|
+
⠋ {name}
|
|
295
|
+
</Text>
|
|
296
|
+
))}
|
|
297
|
+
|
|
298
|
+
<ProgressLines entries={progress.entries} />
|
|
269
299
|
|
|
270
300
|
<Box paddingX={1}>
|
|
271
301
|
<ProgressBar value={percentage} />
|
|
@@ -9,12 +9,19 @@
|
|
|
9
9
|
|
|
10
10
|
import { pathExists } from "@mailwoman/core/fs/readers/stat"
|
|
11
11
|
import { makeDirectories, writeLocalJSONFile } from "@mailwoman/core/fs/writers"
|
|
12
|
-
import {
|
|
12
|
+
import { GitClient } from "@mailwoman/core/git"
|
|
13
13
|
import { extractDelimited } from "@mailwoman/core/scripting/arguments"
|
|
14
14
|
import { Box, Text } from "ink"
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
import {
|
|
15
|
+
import { PathBuilder } from "path-ts"
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
type CommandSpec,
|
|
19
|
+
CommandTaskResult,
|
|
20
|
+
type CommandComponent,
|
|
21
|
+
ProgressLines,
|
|
22
|
+
useCommandTask,
|
|
23
|
+
useProgressFeed,
|
|
24
|
+
} from "#cli-kit"
|
|
18
25
|
import type { RepoSyncPlan } from "#gazetteer/repos/sync"
|
|
19
26
|
|
|
20
27
|
/**
|
|
@@ -43,11 +50,13 @@ const ACTION_MARK: Record<string, string> = {
|
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
const GazetteerReposSync: CommandComponent<typeof spec> = ({ options }) => {
|
|
53
|
+
const progress = useProgressFeed()
|
|
54
|
+
|
|
46
55
|
const state = useCommandTask(async () => {
|
|
47
56
|
const { wofReposPath } = await import("@mailwoman/core/data-root")
|
|
48
57
|
const { wofDatabasePath } = await import("@mailwoman/resolver-wof-sqlite/paths")
|
|
49
58
|
const { auditReposRoot } = await import("#gazetteer/repos/audit")
|
|
50
|
-
const {
|
|
59
|
+
const { applyRepoSyncPlan, planReposSync, syncSentence } = await import("#gazetteer/repos/sync")
|
|
51
60
|
const { githubForkProbe, UPSTREAM_ORG } = await import("#gazetteer/wof/repo-origin")
|
|
52
61
|
|
|
53
62
|
const root = PathBuilder.from(options.root ?? wofReposPath())
|
|
@@ -79,39 +88,33 @@ const GazetteerReposSync: CommandComponent<typeof spec> = ({ options }) => {
|
|
|
79
88
|
probe: githubForkProbe,
|
|
80
89
|
directoryFor: (repo) => directories.get(repo)!,
|
|
81
90
|
fetchFirst: !options.offline,
|
|
91
|
+
watch: (client) => progress.watch(client, PathBuilder.from(client.cwd).basename()),
|
|
82
92
|
})
|
|
83
93
|
|
|
94
|
+
for (const repo of repos) {
|
|
95
|
+
progress.clear(repo)
|
|
96
|
+
}
|
|
97
|
+
|
|
84
98
|
const performed: string[] = []
|
|
85
99
|
|
|
86
100
|
if (options.apply) {
|
|
87
101
|
for (const plan of plans) {
|
|
102
|
+
await using client = new GitClient({ cwd: root })
|
|
103
|
+
|
|
104
|
+
const unwatch = progress.watch(client, plan.repo)
|
|
105
|
+
|
|
88
106
|
try {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
performed.push(
|
|
93
|
-
} else if (plan.action === SyncAction.FastForward) {
|
|
94
|
-
await git(["-C", plan.directory, "merge", "--ff-only", "origin/HEAD"])
|
|
95
|
-
performed.push(`fast-forwarded ${plan.repo}`)
|
|
96
|
-
} else if (plan.action === SyncAction.RepointRequired && options.repoint) {
|
|
97
|
-
// The next upstream sync would need a guess without the fork's address.
|
|
98
|
-
await git(["-C", plan.directory, "remote", "rename", "origin", "upstream"])
|
|
99
|
-
await git(["-C", plan.directory, "remote", "add", "origin", plan.origin.url])
|
|
100
|
-
await git(["-C", plan.directory, "fetch", "--quiet", "origin"])
|
|
101
|
-
|
|
102
|
-
// The rename preserved `branch.<name>.remote`, so without re-pointing tracking the
|
|
103
|
-
// next `git pull` here pulls upstream over the corrections this command preserves.
|
|
104
|
-
const branch = await git(["-C", plan.directory, "rev-parse", "--abbrev-ref", "HEAD"])
|
|
105
|
-
|
|
106
|
-
if (branch && branch !== "HEAD") {
|
|
107
|
-
await git(["-C", plan.directory, "branch", `--set-upstream-to=origin/${branch}`, branch])
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
performed.push(`re-pointed ${plan.repo} at ${plan.origin.org} (previous remote kept as upstream)`)
|
|
107
|
+
const sentence = await applyRepoSyncPlan(plan, { client, repoint: options.repoint })
|
|
108
|
+
|
|
109
|
+
if (sentence) {
|
|
110
|
+
performed.push(sentence)
|
|
111
111
|
}
|
|
112
112
|
} catch (error) {
|
|
113
113
|
// oxlint-disable-next-line mailwoman/prefer-spliterator -- one git error message, read for its first line only
|
|
114
114
|
performed.push(`FAILED ${plan.repo}: ${(error as Error).message.split("\n")[0]}`)
|
|
115
|
+
} finally {
|
|
116
|
+
unwatch()
|
|
117
|
+
progress.clear(plan.repo)
|
|
115
118
|
}
|
|
116
119
|
}
|
|
117
120
|
}
|
|
@@ -140,7 +143,16 @@ const GazetteerReposSync: CommandComponent<typeof spec> = ({ options }) => {
|
|
|
140
143
|
return { plans, sentence: syncSentence(plans), performed, vintagePath, applied: options.apply }
|
|
141
144
|
})
|
|
142
145
|
|
|
143
|
-
if (state.status
|
|
146
|
+
if (state.status === "running") {
|
|
147
|
+
return (
|
|
148
|
+
<Box flexDirection="column">
|
|
149
|
+
<Text color="gray">Resolving WOF repo origins…</Text>
|
|
150
|
+
<ProgressLines entries={progress.entries} />
|
|
151
|
+
</Box>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (state.status !== "done") return <CommandTaskResult state={state} />
|
|
144
156
|
|
|
145
157
|
const { plans, sentence, performed, vintagePath, applied } = state.result
|
|
146
158
|
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
import { gitHead } from "@mailwoman/core/git"
|
|
15
15
|
import { parseJSONStrict, stringifyJSON } from "@mailwoman/core/json"
|
|
16
16
|
import { repoRootPath } from "@mailwoman/core/paths"
|
|
17
|
+
import { runProcess } from "@mailwoman/core/process"
|
|
17
18
|
import { Box, Text } from "ink"
|
|
18
|
-
import { $ } from "zx"
|
|
19
19
|
|
|
20
20
|
import { type CommandSpec, CommandTaskResult, CLIUsageError, type CommandComponent, useCommandTask } from "#cli-kit"
|
|
21
21
|
|
|
@@ -64,7 +64,7 @@ async function mergeAdmin(prNumber: string, method: string): Promise<MergeAdminR
|
|
|
64
64
|
throw new CLIUsageError(`--method ${stringifyJSON(method)} is not one of ${MERGE_METHODS.join("|")}`)
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const prView = await
|
|
67
|
+
const prView = await runProcess("gh", ["pr", "view", String(prNumber), "--json", "state,title,headRefOid,files"])
|
|
68
68
|
|
|
69
69
|
const pr = parseJSONStrict<{
|
|
70
70
|
state: string
|
|
@@ -109,7 +109,7 @@ async function mergeAdmin(prNumber: string, method: string): Promise<MergeAdminR
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
const methodFlag = `--${method}`
|
|
112
|
-
const merged = await
|
|
112
|
+
const merged = await runProcess("gh", ["pr", "merge", String(prNumber), "--admin", methodFlag])
|
|
113
113
|
|
|
114
114
|
return { prNumber, title: pr.title, method, ranChecks, mergeOutput: merged.stdout.trim() }
|
|
115
115
|
}
|
package/cli/doctor/checks.ts
CHANGED
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
docsSiteURL,
|
|
13
13
|
isSelfServicePayload,
|
|
14
14
|
chooseLicenseBranch,
|
|
15
|
+
LicenseObligation,
|
|
15
16
|
summarizeLicense,
|
|
16
17
|
type LicenseKeyVerification,
|
|
17
|
-
type LicenseObligation,
|
|
18
18
|
} from "@mailwoman/core/license"
|
|
19
19
|
import type { LicenseKeyPublication } from "@mailwoman/core/license/publication"
|
|
20
20
|
import type { LicenseStatusAnswer } from "@mailwoman/core/license/status"
|
|
@@ -711,10 +711,27 @@ export function layerLicenseCheck(o: LayerLicenseObservation): DoctorCheck {
|
|
|
711
711
|
...base,
|
|
712
712
|
status: CheckStatus.OK,
|
|
713
713
|
detail: `${o.manifest.name} · ${o.manifest.license} · obligations: ${describeObligations(summary.obligations, true)}`,
|
|
714
|
+
consequence: summary.obligations.includes(LicenseObligation.ShareAlike) ? shareAlikeNotice(o.manifest) : null,
|
|
714
715
|
license,
|
|
715
716
|
}
|
|
716
717
|
}
|
|
717
718
|
|
|
719
|
+
/**
|
|
720
|
+
* The notice the doctor prints under a layer whose license carries share-alike.
|
|
721
|
+
*
|
|
722
|
+
* It states the credit the manifest records and what the license asks when the layer,
|
|
723
|
+
* or a database derived from it, is publicly used.
|
|
724
|
+
* Reading results from the layer for the operator's own use is not public use,
|
|
725
|
+
* so the notice states the condition rather than an obligation already owed.
|
|
726
|
+
*/
|
|
727
|
+
function shareAlikeNotice(manifest: LayerIdentity): string {
|
|
728
|
+
return (
|
|
729
|
+
`${manifest.name} is a database under ${manifest.license}. ` +
|
|
730
|
+
`Credit ${stringifyJSON(manifest.attribution ?? "the source the license names")} wherever its data is shown. ` +
|
|
731
|
+
"If you publicly use it, or a database derived from it, the license requires that database to be offered under the same terms."
|
|
732
|
+
)
|
|
733
|
+
}
|
|
734
|
+
|
|
718
735
|
/**
|
|
719
736
|
* Formats obligations as a comma-separated list for a check detail.
|
|
720
737
|
*/
|
|
@@ -776,7 +793,8 @@ export function obligationPostureCheck(o: ObligationPostureObservation): DoctorC
|
|
|
776
793
|
status: CheckStatus.Degraded,
|
|
777
794
|
detail: `${refusing}: ${findings.length} finding(s) on disk — ${findings.join("; ")}`,
|
|
778
795
|
consequence:
|
|
779
|
-
"A layer on disk carries an obligation this installation declined. Reading from it takes the obligation on
|
|
796
|
+
"A layer on disk carries an obligation this installation declined. Reading from it takes the obligation on. " +
|
|
797
|
+
"The geocode session refuses to start while an OpenStreetMap address-point database carrying it is on disk, " +
|
|
780
798
|
"and `mailwoman data pull` refuses the bundle that carries it.",
|
|
781
799
|
fix: "remove the layer, or drop the class from MAILWOMAN_REFUSE_OBLIGATIONS",
|
|
782
800
|
}
|
package/cli/doctor/runner.ts
CHANGED
|
@@ -42,9 +42,11 @@ import {
|
|
|
42
42
|
import { type ObligationRefusal, parseObligationRefusals } from "#data/obligations"
|
|
43
43
|
import { $public } from "#env"
|
|
44
44
|
import {
|
|
45
|
+
addressPointDatabases,
|
|
45
46
|
layerDatabaseAlternates,
|
|
46
47
|
layerDatabasePath,
|
|
47
48
|
layerDatabases,
|
|
49
|
+
type AddressPointDatabaseRef,
|
|
48
50
|
type LayerDatabaseRef,
|
|
49
51
|
type LayerID,
|
|
50
52
|
} from "#geocode/layer-paths"
|
|
@@ -102,6 +104,11 @@ export interface DoctorDeps {
|
|
|
102
104
|
* `.db` files in a layer's directory other than the one the session attaches, a build under another name.
|
|
103
105
|
*/
|
|
104
106
|
layerAlternates(id: LayerID): Promise<string[]>
|
|
107
|
+
/**
|
|
108
|
+
* The per-country address-point databases on disk, including the OpenStreetMap
|
|
109
|
+
* databases an operator built.
|
|
110
|
+
*/
|
|
111
|
+
addressPointDatabases(): Promise<AddressPointDatabaseRef[]>
|
|
105
112
|
runtimeLicense(): Promise<string>
|
|
106
113
|
/**
|
|
107
114
|
* The configured license key, verified offline against the trusted keys this
|
|
@@ -196,6 +203,7 @@ export async function defaultDoctorDeps(): Promise<DoctorDeps> {
|
|
|
196
203
|
readLayerIdentity,
|
|
197
204
|
layerDatabases: () => layerDatabases(dataRoot),
|
|
198
205
|
layerAlternates: (id) => layerDatabaseAlternates(dataRoot, id),
|
|
206
|
+
addressPointDatabases: () => addressPointDatabases(dataRoot),
|
|
199
207
|
runtimeLicense: readRuntimeLicense,
|
|
200
208
|
licenseKey: () => verifyConfiguredLicenseKey(),
|
|
201
209
|
confirmLicenseKeyPublished: (kid) => confirmLicenseKeyPublished(kid),
|
|
@@ -313,6 +321,28 @@ async function gatherLayerLicense(deps: DoctorDeps, layer: LayerDatabaseRef): Pr
|
|
|
313
321
|
}
|
|
314
322
|
}
|
|
315
323
|
|
|
324
|
+
/**
|
|
325
|
+
* The license observation for an address-point database found on disk.
|
|
326
|
+
*
|
|
327
|
+
* The file was listed from its directory, so it exists, and an unreadable manifest
|
|
328
|
+
* is reported as unreadable rather than as an absent layer.
|
|
329
|
+
*/
|
|
330
|
+
async function gatherAddressPointLicense(
|
|
331
|
+
deps: DoctorDeps,
|
|
332
|
+
database: AddressPointDatabaseRef
|
|
333
|
+
): Promise<LayerLicenseObservation> {
|
|
334
|
+
try {
|
|
335
|
+
return { ...database, manifest: await deps.readLayerIdentity(database.path), error: null, alternates: null }
|
|
336
|
+
} catch (error) {
|
|
337
|
+
return {
|
|
338
|
+
...database,
|
|
339
|
+
manifest: null,
|
|
340
|
+
error: error instanceof Error ? error.message : String(error),
|
|
341
|
+
alternates: null,
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
316
346
|
async function gatherOverlay(deps: DoctorDeps, locale: string): Promise<DoctorCheck> {
|
|
317
347
|
const packageName = deps.weightsPackageName(locale)
|
|
318
348
|
|
|
@@ -373,10 +403,16 @@ export async function runDoctor(overrides?: Partial<DoctorDeps>): Promise<Doctor
|
|
|
373
403
|
lidStatus: lidStatus ?? null,
|
|
374
404
|
})
|
|
375
405
|
|
|
376
|
-
const
|
|
406
|
+
const fixedLayerObservations = (
|
|
377
407
|
await Promise.all(deps.layerDatabases().map((layer) => gatherLayerLicense(deps, layer)))
|
|
378
408
|
).filter((o): o is LayerLicenseObservation => o !== null)
|
|
379
409
|
|
|
410
|
+
const addressPointObservations = await Promise.all(
|
|
411
|
+
(await deps.addressPointDatabases()).map((database) => gatherAddressPointLicense(deps, database))
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
const layerObservations = [...fixedLayerObservations, ...addressPointObservations]
|
|
415
|
+
|
|
380
416
|
const layerLicenses = layerObservations.map(layerLicenseCheck)
|
|
381
417
|
|
|
382
418
|
const posture = await gatherObligationPosture(deps, gazetteerObservation, layerObservations)
|