@welldot/core 0.3.0 → 0.4.0-next.20261015.d7c7365

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 (54) hide show
  1. package/README.md +288 -75
  2. package/dist/index.cjs +2342 -44
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +14060 -4206
  5. package/dist/index.d.ts +14060 -4206
  6. package/dist/index.js +2220 -43
  7. package/dist/index.js.map +1 -1
  8. package/docs/schema/v2/well.schema.json +1043 -17
  9. package/docs/spec/v2/format-reference.md +507 -41
  10. package/docs/spec/v2/interoperability.md +44 -27
  11. package/docs/spec/v2/object-schemas.md +983 -113
  12. package/docs/spec/v2/overview.md +122 -6
  13. package/docs/spec/v2/water-quality.md +483 -0
  14. package/examples/README.md +83 -0
  15. package/examples/belem-pa-condominio-outorga.well +961 -0
  16. package/examples/belem-pa-monitoramento-posto.well +1141 -0
  17. package/examples/belem-pa-pirabas-abastecimento.well +1751 -0
  18. package/examples/belem-pa-poco-colmatado.well +496 -0
  19. package/examples/belem-pa-poco-raso-abandonado.well +388 -0
  20. package/examples/belem-pa-qualidade-agua-acida.well +895 -0
  21. package/examples/colatina-es-gnaisse-rural.well +1047 -0
  22. package/examples/coluna-mg-cristalino-abastecimento.well +1437 -0
  23. package/examples/estrela-doeste-sp-bauru-serra-geral.well +919 -0
  24. package/examples/extremoz-rn-barreiras-raso.well +623 -0
  25. package/examples/gastao-vidigal-sp-dados-inconsistentes.well +222 -0
  26. package/examples/index.json +452 -0
  27. package/examples/irece-ba-carste-bambui.well +861 -0
  28. package/examples/lagoa-santa-mg-carste-abastecimento.well +1087 -0
  29. package/examples/minas-novas-mg-cadastro-incompleto.well +157 -0
  30. package/examples/quixeramobim-ce-cristalino-salobro.well +989 -0
  31. package/examples/rinopolis-sp-estratigrafico-guarani.well +287 -0
  32. package/examples/santarem-pa-jorrante-alter-do-chao.well +554 -0
  33. package/examples/sao-gabriel-rs-cacimba.well +360 -0
  34. package/examples/sete-lagoas-mg-carste-industrial.well +410 -0
  35. package/examples/thumbs/belem-pa-condominio-outorga.svg +1 -0
  36. package/examples/thumbs/belem-pa-monitoramento-posto.svg +1 -0
  37. package/examples/thumbs/belem-pa-pirabas-abastecimento.svg +1 -0
  38. package/examples/thumbs/belem-pa-poco-colmatado.svg +1 -0
  39. package/examples/thumbs/belem-pa-poco-raso-abandonado.svg +1 -0
  40. package/examples/thumbs/belem-pa-qualidade-agua-acida.svg +1 -0
  41. package/examples/thumbs/colatina-es-gnaisse-rural.svg +1 -0
  42. package/examples/thumbs/coluna-mg-cristalino-abastecimento.svg +1 -0
  43. package/examples/thumbs/estrela-doeste-sp-bauru-serra-geral.svg +1 -0
  44. package/examples/thumbs/extremoz-rn-barreiras-raso.svg +1 -0
  45. package/examples/thumbs/gastao-vidigal-sp-dados-inconsistentes.svg +1 -0
  46. package/examples/thumbs/irece-ba-carste-bambui.svg +1 -0
  47. package/examples/thumbs/lagoa-santa-mg-carste-abastecimento.svg +1 -0
  48. package/examples/thumbs/minas-novas-mg-cadastro-incompleto.svg +1 -0
  49. package/examples/thumbs/quixeramobim-ce-cristalino-salobro.svg +1 -0
  50. package/examples/thumbs/rinopolis-sp-estratigrafico-guarani.svg +1 -0
  51. package/examples/thumbs/santarem-pa-jorrante-alter-do-chao.svg +1 -0
  52. package/examples/thumbs/sao-gabriel-rs-cacimba.svg +1 -0
  53. package/examples/thumbs/sete-lagoas-mg-carste-industrial.svg +1 -0
  54. package/package.json +5 -3
package/README.md CHANGED
@@ -15,11 +15,20 @@ TypeScript types, Zod validators, and serialization utilities for the `.well` op
15
15
  - **Administrative metadata** — well identity, construction type and purpose (`well_type`, `well_purpose`), authority-scoped IDs (`well_id`), driller, construction date, and geographic coordinates with explicit CRS
16
16
  - **Hydrodynamic events** — append-only ledger of static level readings, pumping tests (constant-rate, step-drawdown, airlift), and recovery phases
17
17
  - **Aquifer analysis** — interpreted parameter sets: transmissivity, specific capacity, storativity, and Jacob loss coefficients
18
- - **Operational history** — timestamped log of maintenance, inspections, and incidents with HTTPS attachment references
18
+ - **Operational history** — timestamped log of maintenance, inspections, incidents, status changes and permit condition fulfillment, with HTTPS attachment references
19
+ - **Pump installations** — installation history of the well's pumps: nameplate data, intake depth, riser and electrical data (v2.3)
20
+ - **Permits** — legal instruments governing abstraction (outorgas): validity, granted flow and daily operating time, volume limits, monthly schedule, and conditions (condicionantes) whose deadlines are derived from ISO 8601 date durations, with their fulfillments, an administrative status (requested, suspended…) and an administrative history (v2.3)
21
+ - **Meters, production and operating regime** — totalizer (hidrômetro) installation history, an append-only production ledger of meter readings and declared volumes (volumes are derived, never stored as totals), and the declared operating regime in force from a given instant (v2.3)
22
+ - **Water quality** — a `water_samples` ledger of samples, each with its field and laboratory results, sampling point and depth, purge and stabilization readings, laboratory and chain of custody, field QA/QC (duplicates, splits, blanks), per-result filtration, lab flags and data validation. Parameters are identified by `{ code, vocabulary }` against a versioned vocabulary of 98 `welldot` codes (plus CAS numbers and `x-` codes), and units come from the code. Limits are never stored in the file (v2.3)
23
+ - **Attachments** — HTTPS-referenced files typed by `document_type`: a root-level `attachments` array for general files about the well (e.g. the drilling report), plus per-record attachments on pumps, events, analyses and log entries. The root array does not collect the per-record ones (v2.3)
19
24
 
20
25
  The format is designed for three use cases: visualization of technical well profiles, registration with regulatory bodies, and hydrogeological research.
21
26
 
22
- See the [v2 format specification](./docs/spec/v2/overview.md) (current revision: v2.1) for the complete schema reference and design rationale. The [v1 spec](./docs/spec/v1/well-format.md) remains available for reference.
27
+ See the [v2 format specification](./docs/spec/v2/overview.md) (current revision: v2.3) for the complete schema reference and design rationale. The [v1 spec](./docs/spec/v1/well-format.md) remains available for reference.
28
+
29
+ ### Example wells
30
+
31
+ The package ships 14 complete, valid `.well` files in [`examples/`](./examples/README.md). They are built on real wells from the Brazilian SIAGAS registry, with illustrative names, permits, analyses and operating data. Together they cover crystalline, karst, sedimentary, deep, flowing, hand-dug and monitoring wells, plus incomplete and inconsistent records. Import them as `@welldot/core/examples/<file>`, or download them from `https://welldot.org/examples/<file>`.
23
32
 
24
33
  ## Installation
25
34
 
@@ -45,6 +54,7 @@ import {
45
54
  parseWell,
46
55
  serializeWell,
47
56
  deserializeWell,
57
+ hasWellData,
48
58
  isWellEmpty,
49
59
  } from '@welldot/core';
50
60
 
@@ -60,8 +70,9 @@ const jsonString = serializeWell(well);
60
70
  // Deserialize — handles versioned format and legacy formats
61
71
  const restored = deserializeWell(jsonString);
62
72
 
63
- // Check if a well contains any data
73
+ // Check if a well has a profile to draw, or holds any data at all
64
74
  const empty = isWellEmpty(well);
75
+ const hasData = hasWellData(well);
65
76
  ```
66
77
 
67
78
  ## API Reference
@@ -70,91 +81,155 @@ const empty = isWellEmpty(well);
70
81
 
71
82
  All types are exported as TypeScript type-only exports (zero runtime cost).
72
83
 
73
- | Type | Description |
74
- | ----------------------- | ------------------------------------------------------------------------------- |
75
- | `Well` | Complete static record of a water well (v2) |
76
- | `BoreHole` | A drilled interval with diameter and optional drilling method |
77
- | `WellCase` | Steel or plastic casing installed in the borehole |
78
- | `Reduction` | Transition piece between different casing diameters |
79
- | `WellScreen` | Slotted screen section for water intake (`screen_slot` in mm) |
80
- | `SurfaceCase` | Protective casing near the surface |
81
- | `HoleFill` | Annular fill material (`gravel_pack` or `seal`) |
82
- | `CementPad` | Concrete wellhead pad dimensions |
83
- | `Centralizer` | Casing/screen centralizers over a depth interval with spacing (v2.1) |
84
- | `Lithology` | Geological description of a depth interval |
85
- | `Texture` | `{ code: string \| number; vocabulary?: string }` — lithology texture reference |
86
- | `Fracture` | A discrete fracture or fracture zone |
87
- | `Cave` | A cavity or void zone |
88
- | `Constructive` | Grouped type: borehole + casings + screens + fills |
89
- | `Geologic` | Grouped type: lithology + fractures + caves |
90
- | `WellId` | Authority-scoped well identifier `{ authority, id, primary? }` |
91
- | `Location` | Geographic location with optional elevation and CRS properties |
92
- | `LocationProperties` | CRS, datum, and precision metadata for a `Location` |
93
- | `LevelReading` | A single depth/time reading during a pumping or recovery phase |
94
- | `RecoveryPhase` | Time-series of level readings after pump shutdown |
95
- | `PumpingStep` | One flow-rate step in a pumping test |
96
- | `HydrodynamicEventBase` | Common fields shared by all hydrodynamic event types |
97
- | `SpotMeasurementEvent` | A single static water level reading |
98
- | `ConstantRateEvent` | A constant-rate pumping test |
99
- | `StepDrawdownEvent` | A step-drawdown pumping test |
100
- | `AirliftEvent` | An air-lift development or test |
101
- | `RecoveryOnlyEvent` | Recovery measurements without drawdown data |
102
- | `HydrodynamicEvent` | Discriminated union of all event types + x- custom events |
103
- | `AquiferAnalysis` | An interpreted set of aquifer parameters |
104
- | `Attachment` | An HTTPS-referenced file attached to a history log entry |
105
- | `HistoryLogEntry` | One entry in the operational history log |
106
- | `Units` | `{ length: 'm' \| 'ft'; diameter: 'mm' \| 'inches' }` |
107
- | `LengthUnits` | `'m' \| 'ft'` |
108
- | `DiameterUnits` | `'mm' \| 'inches'` |
109
- | `UnitsTypes` | `'metric' \| 'imperial'` |
110
- | `TextureType` | `{ code: TextureCode; label: string }` — a single FGDC texture entry (lookup) |
111
- | `TextureCode` | `number \| string` — numeric FGDC code or custom string code |
84
+ | Type | Description |
85
+ | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
86
+ | `Well` | Complete static record of a water well (v2) |
87
+ | `BoreHole` | A drilled interval with diameter and optional drilling method |
88
+ | `WellCase` | Steel or plastic casing installed in the borehole |
89
+ | `Reduction` | Transition piece between different casing diameters |
90
+ | `WellScreen` | Slotted screen section for water intake (`screen_slot` in mm) |
91
+ | `WellStatus` | Closed `status_change` vocabulary: `active`, `maintenance`, `inactive`, `decommissioned`, `abandoned` (v2.3) |
92
+ | `SurfaceCase` | Protective casing near the surface |
93
+ | `HoleFill` | Annular fill material (`gravel_pack` or `seal`) |
94
+ | `CementPad` | Concrete wellhead pad dimensions |
95
+ | `Centralizer` | Casing/screen centralizers over a depth interval with spacing (v2.1) |
96
+ | `Lithology` | Geological description of a depth interval |
97
+ | `Texture` | `{ code: string \| number; vocabulary?: string }` — lithology texture reference |
98
+ | `Fracture` | A discrete fracture or fracture zone |
99
+ | `Cave` | A cavity or void zone |
100
+ | `Constructive` | Grouped type: borehole + casings + screens + fills |
101
+ | `Geologic` | Grouped type: lithology + fractures + caves |
102
+ | `WellId` | Authority-scoped well identifier `{ authority, id, primary? }` |
103
+ | `Location` | Geographic location with optional elevation and CRS properties |
104
+ | `LocationProperties` | CRS, datum, and precision metadata for a `Location` |
105
+ | `LevelReading` | A single depth/time reading during a pumping or recovery phase |
106
+ | `RecoveryPhase` | Time-series of level readings after pump shutdown |
107
+ | `PumpingStep` | One flow-rate step in a pumping test |
108
+ | `HydrodynamicEventBase` | Common fields shared by all hydrodynamic event types |
109
+ | `SpotMeasurementEvent` | A single static water level reading |
110
+ | `ConstantRateEvent` | A constant-rate pumping test |
111
+ | `StepDrawdownEvent` | A step-drawdown pumping test |
112
+ | `AirliftEvent` | An air-lift development or test |
113
+ | `RecoveryOnlyEvent` | Recovery measurements without drawdown data |
114
+ | `HydrodynamicEvent` | Discriminated union of all event types + x- custom events |
115
+ | `AquiferAnalysis` | An interpreted set of aquifer parameters |
116
+ | `Attachment` | An HTTPS-referenced document, typed by `document_type` (common type since v2.3) |
117
+ | `PumpInstallation` | One installation of a pump in the well (v2.3) |
118
+ | `PumpElectrical` | Electrical data of a pump installation (v2.3) |
119
+ | `Permit` | A legal instrument governing abstraction — outorga, dispensa, cadastro (v2.3) |
120
+ | `PermitAdministrativeStatus` | `'requested' \| 'granted' \| 'suspended' \| 'revoked' \| 'denied' \| 'withdrawn'` (v2.3) |
121
+ | `PermitHistoryEntry` | One administrative step of a permit: filing, process, notification, fee (v2.3) |
122
+ | `PermitCondition` | An obligation (condicionante) of a permit, with derived deadlines and its `fulfillments` (v2.3) |
123
+ | `ConditionFulfillment` | The record of one condition deadline being met (v2.3) |
124
+ | `VolumeLimit` | A volume stated in a permit for a daily, monthly or annual period (v2.3) |
125
+ | `MonthlyGrant` | One month of a permit's month-by-month grant (v2.3) |
126
+ | `Meter` | One installation of a totalizer (hidrômetro) in the well (v2.3) |
127
+ | `ProductionEntryBase` | Common fields of every `production` ledger entry (v2.3) |
128
+ | `MeterReading` | A totalizer register value in m³ (`type: 'meter_reading'`) (v2.3) |
129
+ | `EstimatedVolume` | A volume estimated for a period, without a meter (`type: 'estimated_volume'`) (v2.3) |
130
+ | `ProductionEntry` | `MeterReading \| EstimatedVolume` — one entry of the `production` ledger (v2.3) |
131
+ | `OperatingRegime` | Declared flow rate, daily operating time and days per week from `effective_from` (v2.3) |
132
+ | `WaterSample` | One water sample and its results; `water_samples` is a ledger corrected with `corrects` (v2.3) |
133
+ | `SamplingPoint` | Where the sample was taken: `type`, point `depth` or `from`/`to` interval, `device`, `pump_installation_id` (v2.3) |
134
+ | `Purge` | Purge before collection: duration, volume, flow rate, stabilization and readings (v2.3) |
135
+ | `PurgeReading` | One stabilization reading during the purge (v2.3) |
136
+ | `Laboratory` | Laboratory, accreditation, report number, batch, receipt instant and temperature (v2.3) |
137
+ | `WaterQualityResult` | One result: exactly one of `value`, `presence` or `text`, plus qualifier, limits, fraction, validation (v2.3) |
138
+ | `ResultQualifier` | `'<' \| '>' \| 'not_detected' \| 'estimated'` (v2.3) |
139
+ | `Parameter` | `{ code; vocabulary }` — `welldot`, `cas` or `x-…` parameter identity (v2.3) |
140
+ | `Filtration` | Filter `pore_size` (µm) and `location` (`field` \| `lab`) (v2.3) |
141
+ | `ResultValidation` | Reviewer validation: `status` (`unvalidated`, `validated`, `qualified`, `rejected`), qualifier, guideline (v2.3) |
142
+ | `TimeResolution` | `'day'` — marks an instant known only by its date (`*_resolution` fields) (v2.3) |
143
+ | `HistoryLogEntry` | One entry in the operational history log (incl. `maintenance` and `status_change` fields, and `hydrodynamic_event_ids` / `sample_ids` links) |
144
+ | `Units` | `{ length; diameter; flow?; power?; volume? }` — display/input unit preferences |
145
+ | `LengthUnits` | `'m' \| 'ft'` |
146
+ | `DiameterUnits` | `'mm' \| 'inches'` |
147
+ | `FlowUnits` | `'m3/h' \| 'L/s' \| 'gpm'` |
148
+ | `PowerUnits` | `'kW' \| 'cv' \| 'hp'` |
149
+ | `VolumeUnits` | `'m3' \| 'L' \| 'ft3' \| 'gal'` (`gal` is the US gallon) |
150
+ | `UnitsTypes` | `'metric' \| 'imperial'` |
151
+ | `TextureType` | `{ code: TextureCode; label: string }` — a single FGDC texture entry (lookup) |
152
+ | `TextureCode` | `number \| string` — numeric FGDC code or custom string code |
153
+ | `SectionKey` | Section of a well for share visibility / redaction (includes `'water_quality'` since v2.3) |
154
+ | `ParameterDefinition` | One vocabulary entry: code, group, EN label, translations, unit, value form, CAS, molar mass, charge (v2.3) |
155
+ | `ParameterGroup` | Vocabulary group (`physical`, `major_ion`, `metal`, `microbiology`, …) (v2.3) |
156
+ | `ParameterForm` | `'value' \| 'presence' \| 'text'` (v2.3) |
157
+ | `ParameterUnit` | `{ symbol; ucum? }` — canonical unit of a parameter (v2.3) |
158
+ | `LimitSet`, `Limit`, `LimitBasis` | A jurisdiction limit set and its per-code limits (v2.3) |
159
+ | `ConcentrationUnits` | `'mg/L' \| 'ug/L' \| 'ng/L' \| 'g/L'` (v2.3) |
112
160
 
113
161
  ### Validators
114
162
 
115
163
  Each schema validates its corresponding type at runtime. All schemas are Zod objects and compose with standard Zod methods (`.parse`, `.safeParse`, `.extend`, etc.).
116
164
 
117
- | Export | Validates |
118
- | ----------------------------- | ----------------------------------------------------------------------- |
119
- | `WellSchema` | `Well` (the complete document; `.passthrough()` preserves unknown keys) |
120
- | `BoreHoleSchema` | `BoreHole` |
121
- | `WellCaseSchema` | `WellCase` |
122
- | `ReductionSchema` | `Reduction` |
123
- | `WellScreenSchema` | `WellScreen` |
124
- | `SurfaceCaseSchema` | `SurfaceCase` |
125
- | `HoleFillSchema` | `HoleFill` |
126
- | `CementPadSchema` | `CementPad` |
127
- | `CentralizerSchema` | `Centralizer` |
128
- | `LithologySchema` | `Lithology` |
129
- | `TextureSchema` | `Texture` |
130
- | `FractureSchema` | `Fracture` |
131
- | `CaveSchema` | `Cave` |
132
- | `WellIdSchema` | `WellId` |
133
- | `LocationPropertiesSchema` | `LocationProperties` |
134
- | `LocationSchema` | `Location` |
135
- | `LevelReadingSchema` | `LevelReading` |
136
- | `RecoveryPhaseSchema` | `RecoveryPhase` |
137
- | `PumpingStepSchema` | `PumpingStep` |
138
- | `HydrodynamicEventBaseSchema` | `HydrodynamicEventBase` |
139
- | `SpotMeasurementEventSchema` | `SpotMeasurementEvent` |
140
- | `ConstantRateEventSchema` | `ConstantRateEvent` |
141
- | `StepDrawdownEventSchema` | `StepDrawdownEvent` |
142
- | `AirliftEventSchema` | `AirliftEvent` |
143
- | `RecoveryOnlyEventSchema` | `RecoveryOnlyEvent` |
144
- | `HydrodynamicEventSchema` | `HydrodynamicEvent` (discriminated union + x- passthrough) |
145
- | `AquiferAnalysisSchema` | `AquiferAnalysis` |
146
- | `AttachmentSchema` | `Attachment` |
147
- | `HistoryLogEntrySchema` | `HistoryLogEntry` |
165
+ | Export | Validates |
166
+ | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
167
+ | `WellSchema` | `Well` (the complete document; `.passthrough()` preserves unknown keys) |
168
+ | `BoreHoleSchema` | `BoreHole` |
169
+ | `WellCaseSchema` | `WellCase` |
170
+ | `ReductionSchema` | `Reduction` |
171
+ | `WellScreenSchema` | `WellScreen` |
172
+ | `WellStatusSchema` | `WellStatus` (z.enum — any other value is rejected) |
173
+ | `SurfaceCaseSchema` | `SurfaceCase` |
174
+ | `HoleFillSchema` | `HoleFill` |
175
+ | `CementPadSchema` | `CementPad` |
176
+ | `CentralizerSchema` | `Centralizer` |
177
+ | `LithologySchema` | `Lithology` |
178
+ | `TextureSchema` | `Texture` |
179
+ | `FractureSchema` | `Fracture` |
180
+ | `CaveSchema` | `Cave` |
181
+ | `WellIdSchema` | `WellId` |
182
+ | `LocationPropertiesSchema` | `LocationProperties` |
183
+ | `LocationSchema` | `Location` |
184
+ | `LevelReadingSchema` | `LevelReading` |
185
+ | `RecoveryPhaseSchema` | `RecoveryPhase` |
186
+ | `PumpingStepSchema` | `PumpingStep` |
187
+ | `HydrodynamicEventBaseSchema` | `HydrodynamicEventBase` |
188
+ | `SpotMeasurementEventSchema` | `SpotMeasurementEvent` |
189
+ | `ConstantRateEventSchema` | `ConstantRateEvent` |
190
+ | `StepDrawdownEventSchema` | `StepDrawdownEvent` |
191
+ | `AirliftEventSchema` | `AirliftEvent` |
192
+ | `RecoveryOnlyEventSchema` | `RecoveryOnlyEvent` |
193
+ | `HydrodynamicEventSchema` | `HydrodynamicEvent` (discriminated union + x- passthrough) |
194
+ | `AquiferAnalysisSchema` | `AquiferAnalysis` |
195
+ | `AttachmentSchema` | `Attachment` |
196
+ | `PumpInstallationSchema` | `PumpInstallation` |
197
+ | `PumpElectricalSchema` | `PumpElectrical` |
198
+ | `PermitSchema` | `Permit` |
199
+ | `PermitConditionSchema` | `PermitCondition` |
200
+ | `ConditionFulfillmentSchema` | `ConditionFulfillment` |
201
+ | `PermitHistoryEntrySchema` | `PermitHistoryEntry` |
202
+ | `PermitAdministrativeStatusSchema` | `PermitAdministrativeStatus` |
203
+ | `VolumeLimitSchema` | `VolumeLimit` |
204
+ | `MonthlyGrantSchema` | `MonthlyGrant` |
205
+ | `MeterSchema` | `Meter` (`max_reading` > 0) |
206
+ | `ProductionEntryBaseSchema` | `ProductionEntryBase` |
207
+ | `MeterReadingSchema` | `MeterReading` |
208
+ | `EstimatedVolumeSchema` | `EstimatedVolume` |
209
+ | `ProductionEntrySchema` | `ProductionEntry` (discriminated union + x- passthrough; a known type missing required fields is rejected) |
210
+ | `OperatingRegimeSchema` | `OperatingRegime` (`daily_operating_time` 0–24, integer `days_per_week` 1–7) |
211
+ | `HistoryLogEntrySchema` | `HistoryLogEntry` |
212
+ | `WaterSampleSchema` | `WaterSample` (`results` must have at least one entry) |
213
+ | `SamplingPointSchema` | `SamplingPoint` (`depth` and `from`/`to` are mutually exclusive) |
214
+ | `PurgeSchema` | `Purge` |
215
+ | `PurgeReadingSchema` | `PurgeReading` |
216
+ | `LaboratorySchema` | `Laboratory` |
217
+ | `WaterQualityResultSchema` | `WaterQualityResult` (exactly one value form except `not_detected`; `<`/`>`/`estimated` need `value`; `unit` required for `x-` and forbidden for `welldot`/`cas`) |
218
+ | `ResultQualifierSchema` | `ResultQualifier` (z.enum) |
219
+ | `ParameterSchema` | `Parameter` (`vocabulary` must be `welldot`, `cas` or `x-…`) |
220
+ | `FiltrationSchema` | `Filtration` |
221
+ | `ResultValidationSchema` | `ResultValidation` (closed `status` enum) |
222
+ | `TimeResolutionSchema` | `TimeResolution` (`z.literal('day')`) |
148
223
 
149
224
  ### Functions
150
225
 
151
226
  #### `parseWell(json: string): Well`
152
227
 
153
- Parses a raw JSON string and validates it against `WellSchema`. Throws a `ZodError` on validation failure. Does not handle legacy formats — use `deserializeWell` for that.
228
+ Parses a raw JSON string and validates it against `WellSchema`. Throws a `ZodError` on validation failure. Unknown members (`x-` fields and blocks) are kept at every level, including inside array items. Does not handle legacy formats — use `deserializeWell` for that.
154
229
 
155
230
  #### `serializeWell(well: Well): string`
156
231
 
157
- Serializes a `Well` object to a versioned `.well` JSON string (`"version": 2`). Emits `location` when set; does not emit deprecated flat `lat`/`lng`/`elevation`. Omits `undefined` optional fields.
232
+ Serializes a `Well` object to a versioned `.well` JSON string (`"version": 2`). Emits `location` when set; does not emit deprecated flat `lat`/`lng`/`elevation` or other legacy v1 keys. Omits `undefined` optional fields. Root members the spec does not define (`x-` fields and blocks, foreign members) are written unchanged after the spec blocks, as the spec requires.
158
233
 
159
234
  #### `deserializeWell(jsonString: string): Well | null`
160
235
 
@@ -172,6 +247,144 @@ Returns `null` if the parsed data is empty. Throws on unrecognized version numbe
172
247
 
173
248
  Returns `true` when all constructive and geologic arrays are empty. Accepts `null` and `undefined` (both treated as empty).
174
249
 
250
+ #### `hasWellData(well: Well | null | undefined): boolean`
251
+
252
+ Returns `true` when the well holds any data: a profile, or any other filled root member (identification, location, events, samples, permits, `x-` members, …). A registry record without construction or geology has data although `isWellEmpty` says it has nothing to draw. Blank strings, zeros and objects holding only those do not count.
253
+
254
+ #### `calculatedWellDepth(well: Well): number`
255
+
256
+ Deepest `to` across the constructive arrays (bore hole, casings, screens, reductions, hole fill), ignoring geology. `0` without constructive data. Used as the fallback for a missing `well_depth`.
257
+
258
+ #### `redactWell(well: Well, visibility: WellVisibility): Well`
259
+
260
+ Copy of `well` with hidden parts emptied (arrays become `[]`, optional fields are removed), so the result stays schema-valid. `visibility` keys are the 7 `SECTION_KEYS` and/or the fields of `VISIBILITY_TREE` (e.g. `meters`, `permits`, `location`); `false` hides, missing keys are visible, and a hidden section hides all its fields. `VISIBILITY_LEAF_KEYS` lists the finest toggles. References from visible records to hidden ones are left as-is. Unknown root members (`x-` fields and blocks) are kept only when nothing is hidden; nested ones follow their record.
261
+
262
+ ```typescript
263
+ redactWell(well, { operation: false }); // whole section
264
+ redactWell(well, { meters: false, location: false }); // single fields
265
+ ```
266
+
267
+ #### Unit conversion
268
+
269
+ `.well` files always store canonical SI units; these helpers convert for display and input.
270
+
271
+ | Quantity | Functions |
272
+ | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
273
+ | Length | `metersToFeet`, `feetToMeters` |
274
+ | Diameter | `mmToInches`, `inchesToMm`, `slotNumberToMm`, `mmToSlotNumber` |
275
+ | Flow | `cubicMeterPerHourToLitersPerSecond`, `litersPerSecondToCubicMeterPerHour`, `cubicMeterPerHourToUsGallonsPerMinute`, `usGallonsPerMinuteToCubicMeterPerHour`, `flowFromCanonical`, `flowToCanonical` |
276
+ | Power | `cvToKilowatts`, `kilowattsToCv`, `hpToKilowatts`, `kilowattsToHp`, `powerFromCanonical`, `powerToCanonical` |
277
+ | Volume | `litersToCubicMeters`, `cubicMetersToLiters`, `cubicMetersToCubicFeet`, `cubicFeetToCubicMeters`, `cubicMetersToUsGallons`, `usGallonsToCubicMeters`, `volumeFromCanonical`, `volumeToCanonical` |
278
+ | Time | `minutesToHours`, `hoursToMinutes` |
279
+ | Pressure | `kilopascalToPsi`, `psiToKilopascal` |
280
+ | Transmissivity | `squareMeterPerSecondToSquareMeterPerDay`, `squareMeterPerDayToSquareMeterPerSecond` |
281
+ | Coordinates | `decimalDegreesToDms`, `dmsToDecimalDegrees` (`DmsCoordinate`) |
282
+ | Concentration | `concentrationToCanonical`, `concentrationFromCanonical` (`ConcentrationUnits`: mg/L, µg/L, ng/L, g/L ↔ canonical mg/L) (v2.3) |
283
+ | Conductivity | `milliSiemensPerMeterToMicroSiemensPerCm` (×10), `milliSiemensPerCmToMicroSiemensPerCm` (×1000) (v2.3) |
284
+ | Lab report import | `turbidityCodeForUnit` (NTU/FNU/FAU/FTU/uT → turbidity code), `normalizeColorUnit` (mg Pt-Co/L, PCU, TCU → `uH`), `expandedToStandardUncertainty` (U / k → one-sigma `value_precision`) (v2.3) |
285
+
286
+ `*FromCanonical(value, unit)` converts from the canonical unit (m³/h, kW, m³) to the given display unit; `*ToCanonical(value, unit)` converts back.
287
+
288
+ ## Water Quality Vocabulary and Limit Sets _(v2.3)_
289
+
290
+ The `welldot` water quality parameter vocabulary is published as versioned data. Each `ParameterDefinition` carries the code, group, canonical English label, translations (`labels.pt`), canonical unit (display symbol and UCUM code), value form (`value`, `presence` or `text`), the CAS equivalence when one exists, and molar mass and charge for ions used in ion balance and hydrochemical diagrams.
291
+
292
+ | Export | Description |
293
+ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
294
+ | `WATER_QUALITY_PARAMETERS` | The 98 `welldot` parameter definitions |
295
+ | `WATER_QUALITY_VOCABULARY_VERSION` | Version of the published vocabulary |
296
+ | `WATER_QUALITY_UNITS` | Canonical units used by the vocabulary (`mg/L`, `µS/cm`, `°C`, `mV`, NTU/FNU/FAU/FTU, `uH`, MPN/CFU counts, `Bq/L`) |
297
+ | `getParameterDefinition(param)` | Looks up a `{ code, vocabulary }`; a `cas` code resolves through the published equivalences to its `welldot` entry |
298
+ | `parameterKey(param)` | Canonical identity — a `welldot` code and its CAS equivalent produce the same key (duplicate detection, grouping) |
299
+ | `isKnownParameter(param)` | `false` only for a `welldot` code unknown to this vocabulary version (`cas` and `x-` are always accepted) |
300
+ | `WHO_GDWQ_2022` | WHO Guidelines for Drinking-water Quality (2022) |
301
+ | `BR_GM_MS_888_2021` | Brasil — Portaria GM/MS 888/2021 |
302
+ | `EU_2020_2184` | EU Drinking Water Directive 2020/2184 |
303
+ | `WATER_QUALITY_LIMIT_SETS` | All bundled limit sets, in display order |
304
+ | `getLimitSet(id)` | Finds a bundled limit set by `id` (`who_gdwq_2022`, `br_gm_ms_888_2021`, `eu_2020_2184`) |
305
+ | `NEPHELOMETRIC_TURBIDITY_CODES` | `turbidity`, `turbidity_ntu`, `turbidity_fnu` — compared with nephelometric limits; `turbidity_fau` never is |
306
+
307
+ ```typescript
308
+ import {
309
+ getLimitSet,
310
+ getParameterDefinition,
311
+ parameterKey,
312
+ } from '@welldot/core';
313
+
314
+ getParameterDefinition({ code: '14808-79-8', vocabulary: 'cas' })?.code; // 'sulfate'
315
+ parameterKey({ code: 'sulfate', vocabulary: 'welldot' }); // 'sulfate'
316
+ getLimitSet('who_gdwq_2022')?.limits.find(l => l.code === 'arsenic');
317
+ ```
318
+
319
+ Limits are never written to `.well` files (jurisdiction neutrality): consumers pick a set at display time and compare results with the derivation helpers of `@welldot/utils`. **The bundled limit values are transcribed from the cited sources and must be verified against the official documents before any regulatory use.**
320
+
321
+ ## Recommended Vocabularies _(v2.3)_
322
+
323
+ Many `.well` string fields are **open**: any value (or an `x-` extension) is valid, but the spec recommends a vocabulary. Each one is exported as data, with display labels keyed by BCP 47 language tag (`en` and `pt` always present). Field types use `OpenVocab<T>` (`T | (string & {})`), so the recommended values autocomplete while any string still type-checks.
324
+
325
+ | Vocabulary | Field |
326
+ | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
327
+ | `WELL_TYPES` | `well_type` (`artesian` is `deprecated`) |
328
+ | `WELL_PURPOSES` | `well_purpose[]` |
329
+ | `DRILLING_METHODS` | `bore_hole[].drilling_method` |
330
+ | `CONSTRUCTION_MATERIALS` | `well_case[].type`, `well_screen[].type`, `reduction[].type`, `pump_installations[].riser_material` |
331
+ | `SCREEN_SLOT_TYPES` | `well_screen[].slot_type` |
332
+ | `CENTRALIZER_TYPES` | `centralizers[].type` |
333
+ | `CEMENT_PAD_TYPES` | `cement_pad.type` |
334
+ | `PUMP_TYPES`, `POWER_SOURCES` | `pump_installations[].type`, `.power_source` |
335
+ | `METER_TYPES` | `meters[].type` |
336
+ | `READING_SOURCES`, `ESTIMATION_METHODS` | `production[].source`, `production[].method` |
337
+ | `PERMIT_TYPES`, `WATER_USES` | `permits[].type`, `permits[].water_use[]` |
338
+ | `CONDITION_CATEGORIES` | `permits[].conditions[].category` |
339
+ | `PERMIT_HISTORY_TYPES` | `permits[].history[].type` |
340
+ | `HISTORY_LOG_CATEGORIES`, `HISTORY_LOG_SEVERITIES`, `MAINTENANCE_TYPES` | `history_logs[].category`, `.severity`, `.maintenance_type` |
341
+ | `HYDRODYNAMIC_EVENT_TYPES`, `MEASUREMENT_METHODS` | `hydrodynamic_events[].type`, `.measurement_method` |
342
+ | `AQUIFER_ANALYSIS_METHODS` | `aquifer_analysis[].method` |
343
+ | `DOCUMENT_TYPES`, `DOCUMENT_TYPE_SUGGESTIONS` | `attachments[].document_type` (and per-context suggestion order) |
344
+ | `SAMPLE_TYPES`, `SAMPLING_METHODS`, `SAMPLING_POINT_TYPES`, `SAMPLING_DEVICES` | `water_samples[]` sampling fields |
345
+
346
+ Labels are `LanguageText<'en' | 'pt'>`. `LanguageText<L>` is the general localized-text type (`Record<string, string>` keyed by BCP 47 tag), where `L` lists the tags that must be present — `LanguageText` alone requires none. `LanguageTextInput` adds the plain-string, JSON-string and `null`/`undefined` forms accepted by `resolveLanguageText`.
347
+
348
+ | Helper | Description |
349
+ | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
350
+ | `getVocabLabel(vocab, value, locale)` | Label in `locale` (`pt-BR` → `pt` → `en`); free-text and `x-` values are returned as-is |
351
+ | `resolveLanguageText(text, locale)` | Resolves a `LanguageTextInput` with the same fallback: a `LanguageText` object, a plain string (returned as-is), a JSON object string (parsed; malformed JSON stays a plain string) or `null`/`undefined` (`''`); empty labels count as missing |
352
+ | `getVocabEntry` / `isVocabValue` | Looks up a value's entry / checks whether it is recommended |
353
+ | `vocabValues(vocab, { includeDeprecated })` | The recommended values, deprecated ones left out by default |
354
+ | `formatVocabList(vocab, values, locale)` | Comma-separated labels of a multi-valued field (e.g. `well_purpose`), unknown values kept as-is |
355
+
356
+ ```typescript
357
+ import {
358
+ CONSTRUCTION_MATERIALS,
359
+ PERMIT_TYPES,
360
+ getVocabLabel,
361
+ type OpenVocab,
362
+ type PermitType,
363
+ } from '@welldot/core';
364
+
365
+ const type: OpenVocab<PermitType> = 'abstraction_permit'; // or any string
366
+ getVocabLabel(PERMIT_TYPES, type, 'pt'); // 'Outorga de direito de uso'
367
+ getVocabLabel(PERMIT_TYPES, 'x-special', 'pt'); // 'x-special'
368
+ ```
369
+
370
+ ### Closed vocabularies
371
+
372
+ Fields whose values the schema fixes (no free text, no `x-`) ship in the same shape as the open vocabularies — `VocabEntry` lists with `en`/`pt` labels — so `getVocabLabel` labels any value without consumer translations. Each list is checked at compile time against its type, so it cannot drift from the schema. The `*_VALUES` lists are the bare values.
373
+
374
+ | Vocabulary | Field |
375
+ | -------------------------------------------------- | --------------------------------------------- |
376
+ | `WELL_STATUSES` (`WELL_STATUS_VALUES`) | `history_logs[].status` (`status_change`) |
377
+ | `PERMIT_ADMINISTRATIVE_STATUSES` | `permits[].status` |
378
+ | `VOLUME_LIMIT_PERIODS` | `permits[].volume_limits[].period` |
379
+ | `PRODUCTION_ENTRY_TYPES` | `production[].type` |
380
+ | `RESULT_QUALIFIERS` (`QUALIFIER_VALUES`) | result `qualifier` |
381
+ | `RESULT_FRACTIONS` (`FRACTION_VALUES`) | result `fraction` |
382
+ | `MEASUREMENT_LOCATIONS` | result `measured_in`, `filtration.location` |
383
+ | `VALIDATION_STATUSES` (`VALIDATION_STATUS_VALUES`) | result `validation.status` |
384
+ | `PARAMETER_GROUPS` (`PARAMETER_GROUP_VALUES`) | parameter vocabulary groups, in display order |
385
+
386
+ Value rules without labels: `NUMERIC_QUALIFIERS`, `PARENT_SAMPLE_TYPES`, `BLANK_SAMPLE_TYPES`. Parameter names: `getParameterLabel(parameter, locale)` (CAS numbers resolve through the published equivalences; unknown codes are returned as-is).
387
+
175
388
  ## FGDC Texture Patterns
176
389
 
177
390
  `FGDC_TEXTURES_OPTIONS` is a typed array of 284 texture pattern entries drawn from the **FGDC Digital Cartographic Standard for Geologic Map Symbolization (FGDC-STD-013-2006)**. Assign a `code` to the `texture` field of a `Lithology` record to attach a standardized fill pattern to a depth interval.