@welldot/core 0.4.0-next.20261015.d7c7365 → 0.5.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 (36) hide show
  1. package/README.md +59 -19
  2. package/dist/index.cjs +344 -178
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +9203 -5704
  5. package/dist/index.d.ts +9203 -5704
  6. package/dist/index.js +334 -178
  7. package/dist/index.js.map +1 -1
  8. package/docs/reference/fgdc-textures.md +15 -0
  9. package/docs/schema/v2/well.schema.json +128 -11
  10. package/docs/spec/v1/well-format.md +26 -0
  11. package/docs/spec/v2/format-reference.md +170 -45
  12. package/docs/spec/v2/interoperability.md +25 -4
  13. package/docs/spec/v2/object-schemas.md +243 -96
  14. package/docs/spec/v2/overview.md +92 -9
  15. package/docs/spec/v2/water-quality.md +26 -3
  16. package/examples/README.md +12 -0
  17. package/examples/belem-pa-condominio-outorga.well +2 -1
  18. package/examples/belem-pa-monitoramento-posto.well +2 -1
  19. package/examples/belem-pa-pirabas-abastecimento.well +2 -2
  20. package/examples/belem-pa-poco-colmatado.well +2 -1
  21. package/examples/belem-pa-poco-raso-abandonado.well +2 -1
  22. package/examples/belem-pa-qualidade-agua-acida.well +2 -1
  23. package/examples/colatina-es-gnaisse-rural.well +2 -2
  24. package/examples/coluna-mg-cristalino-abastecimento.well +2 -2
  25. package/examples/estrela-doeste-sp-bauru-serra-geral.well +2 -1
  26. package/examples/extremoz-rn-barreiras-raso.well +2 -1
  27. package/examples/gastao-vidigal-sp-dados-inconsistentes.well +2 -1
  28. package/examples/irece-ba-carste-bambui.well +2 -1
  29. package/examples/lagoa-santa-mg-carste-abastecimento.well +2 -2
  30. package/examples/minas-novas-mg-cadastro-incompleto.well +2 -1
  31. package/examples/quixeramobim-ce-cristalino-salobro.well +2 -2
  32. package/examples/rinopolis-sp-estratigrafico-guarani.well +2 -1
  33. package/examples/santarem-pa-jorrante-alter-do-chao.well +2 -1
  34. package/examples/sao-gabriel-rs-cacimba.well +2 -1
  35. package/examples/sete-lagoas-mg-carste-industrial.well +2 -1
  36. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -24,6 +24,8 @@ __export(index_exports, {
24
24
  AirliftEventSchema: () => AirliftEventSchema,
25
25
  AquiferAnalysisSchema: () => AquiferAnalysisSchema,
26
26
  AttachmentSchema: () => AttachmentSchema,
27
+ BAROMETRIC_COMPENSATIONS: () => BAROMETRIC_COMPENSATIONS,
28
+ BAROMETRIC_COMPENSATION_VALUES: () => BAROMETRIC_COMPENSATION_VALUES,
27
29
  BLANK_SAMPLE_TYPES: () => BLANK_SAMPLE_TYPES,
28
30
  BR_GM_MS_888_2021: () => BR_GM_MS_888_2021,
29
31
  BoreHoleSchema: () => BoreHoleSchema,
@@ -59,6 +61,8 @@ __export(index_exports, {
59
61
  LithologySchema: () => LithologySchema,
60
62
  LocationPropertiesSchema: () => LocationPropertiesSchema,
61
63
  LocationSchema: () => LocationSchema,
64
+ LoggerReadingSchema: () => LoggerReadingSchema,
65
+ LoggerSeriesEventSchema: () => LoggerSeriesEventSchema,
62
66
  MAINTENANCE_TYPES: () => MAINTENANCE_TYPES,
63
67
  MEASURED_IN_VALUES: () => MEASURED_IN_VALUES,
64
68
  MEASUREMENT_LOCATIONS: () => MEASUREMENT_LOCATIONS,
@@ -93,6 +97,8 @@ __export(index_exports, {
93
97
  PurgeReadingSchema: () => PurgeReadingSchema,
94
98
  PurgeSchema: () => PurgeSchema,
95
99
  QUALIFIER_VALUES: () => QUALIFIER_VALUES,
100
+ READING_AGGREGATIONS: () => READING_AGGREGATIONS,
101
+ READING_AGGREGATION_VALUES: () => READING_AGGREGATION_VALUES,
96
102
  READING_SOURCES: () => READING_SOURCES,
97
103
  RESULT_FRACTIONS: () => RESULT_FRACTIONS,
98
104
  RESULT_QUALIFIERS: () => RESULT_QUALIFIERS,
@@ -124,6 +130,7 @@ __export(index_exports, {
124
130
  WATER_QUALITY_UNITS: () => WATER_QUALITY_UNITS,
125
131
  WATER_QUALITY_VOCABULARY_VERSION: () => WATER_QUALITY_VOCABULARY_VERSION,
126
132
  WATER_USES: () => WATER_USES,
133
+ WELL_FORMAT_VERSION: () => WELL_FORMAT_VERSION,
127
134
  WELL_PURPOSES: () => WELL_PURPOSES,
128
135
  WELL_STATUSES: () => WELL_STATUSES,
129
136
  WELL_STATUS_VALUES: () => WELL_STATUS_VALUES,
@@ -138,6 +145,7 @@ __export(index_exports, {
138
145
  WellStatusSchema: () => WellStatusSchema,
139
146
  calculatedWellDepth: () => calculatedWellDepth,
140
147
  checkIfProfileIsEmpty: () => checkIfProfileIsEmpty,
148
+ compareFormatVersions: () => compareFormatVersions,
141
149
  concentrationFromCanonical: () => concentrationFromCanonical,
142
150
  concentrationToCanonical: () => concentrationToCanonical,
143
151
  convertProfileFromJSON: () => convertProfileFromJSON,
@@ -165,6 +173,7 @@ __export(index_exports, {
165
173
  hoursToMinutes: () => hoursToMinutes,
166
174
  hpToKilowatts: () => hpToKilowatts,
167
175
  inchesToMm: () => inchesToMm,
176
+ isIanaTimeZone: () => isIanaTimeZone,
168
177
  isKnownParameter: () => isKnownParameter,
169
178
  isVocabValue: () => isVocabValue,
170
179
  isWellEmpty: () => isWellEmpty,
@@ -181,6 +190,7 @@ __export(index_exports, {
181
190
  mmToSlotNumber: () => mmToSlotNumber,
182
191
  normalizeColorUnit: () => normalizeColorUnit,
183
192
  parameterKey: () => parameterKey,
193
+ parseFormatVersion: () => parseFormatVersion,
184
194
  parseWell: () => parseWell,
185
195
  powerFromCanonical: () => powerFromCanonical,
186
196
  powerToCanonical: () => powerToCanonical,
@@ -204,6 +214,215 @@ module.exports = __toCommonJS(index_exports);
204
214
  // src/validators/well.validators.ts
205
215
  var import_defu = require("defu");
206
216
  var import_zod = require("zod");
217
+
218
+ // src/utils/timezone.utils.ts
219
+ var IANA_SHAPE = /^[A-Za-z][A-Za-z0-9_+\-/]*$/;
220
+ function isIanaTimeZone(value) {
221
+ if (!IANA_SHAPE.test(value)) return false;
222
+ try {
223
+ new Intl.DateTimeFormat("en-US", { timeZone: value });
224
+ return true;
225
+ } catch {
226
+ return false;
227
+ }
228
+ }
229
+
230
+ // src/utils/version.utils.ts
231
+ var WELL_FORMAT_VERSION = "2.4";
232
+ var VERSION_PATTERN = /^(\d+)\.(\d+)$/;
233
+ function parseFormatVersion(value) {
234
+ if (typeof value === "number") {
235
+ return Number.isInteger(value) && value >= 0 ? { major: value, minor: void 0 } : void 0;
236
+ }
237
+ if (typeof value === "string") {
238
+ const match = VERSION_PATTERN.exec(value);
239
+ if (match) return { major: Number(match[1]), minor: Number(match[2]) };
240
+ }
241
+ return void 0;
242
+ }
243
+ function compareFormatVersions(a, b) {
244
+ const pa = parseFormatVersion(a);
245
+ const pb = parseFormatVersion(b);
246
+ if (!pa || pb === void 0 || pa.minor === void 0 || pb.minor === void 0) {
247
+ throw new Error(
248
+ `Malformed .well format version: ${JSON.stringify(pa?.minor === void 0 ? a : b)}`
249
+ );
250
+ }
251
+ return pa.major - pb.major || pa.minor - pb.minor;
252
+ }
253
+
254
+ // src/vocab/closed.vocab.ts
255
+ function closedVocab() {
256
+ return (entries) => entries;
257
+ }
258
+ var valuesOf = (vocab) => vocab.map((entry) => entry.value);
259
+ var WELL_STATUSES = closedVocab()([
260
+ { value: "active", label: { en: "Active", pt: "Ativo" } },
261
+ {
262
+ value: "maintenance",
263
+ label: { en: "Under maintenance", pt: "Em manuten\xE7\xE3o" }
264
+ },
265
+ { value: "inactive", label: { en: "Inactive", pt: "Paralisado" } },
266
+ {
267
+ value: "decommissioned",
268
+ label: { en: "Decommissioned", pt: "Desativado" }
269
+ },
270
+ { value: "abandoned", label: { en: "Abandoned", pt: "Abandonado" } }
271
+ ]);
272
+ var PERMIT_ADMINISTRATIVE_STATUSES = closedVocab()([
273
+ { value: "requested", label: { en: "Requested", pt: "Requerida" } },
274
+ { value: "granted", label: { en: "Granted", pt: "Concedida" } },
275
+ { value: "suspended", label: { en: "Suspended", pt: "Suspensa" } },
276
+ { value: "revoked", label: { en: "Revoked", pt: "Revogada" } },
277
+ { value: "denied", label: { en: "Denied", pt: "Indeferida" } },
278
+ { value: "withdrawn", label: { en: "Withdrawn", pt: "Cancelada" } }
279
+ ]);
280
+ var VOLUME_LIMIT_PERIODS = closedVocab()([
281
+ { value: "daily", label: { en: "Daily", pt: "Di\xE1rio" } },
282
+ { value: "monthly", label: { en: "Monthly", pt: "Mensal" } },
283
+ { value: "annual", label: { en: "Annual", pt: "Anual" } }
284
+ ]);
285
+ var PRODUCTION_ENTRY_TYPES = closedVocab()([
286
+ {
287
+ value: "meter_reading",
288
+ label: { en: "Meter reading", pt: "Leitura de hidr\xF4metro" }
289
+ },
290
+ {
291
+ value: "estimated_volume",
292
+ label: { en: "Estimated volume", pt: "Volume estimado" }
293
+ }
294
+ ]);
295
+ var RESULT_QUALIFIERS = closedVocab()([
296
+ { value: "<", label: { en: "< less than", pt: "< menor que" } },
297
+ { value: ">", label: { en: "> greater than", pt: "> maior que" } },
298
+ { value: "not_detected", label: { en: "Not detected", pt: "N\xE3o detectado" } },
299
+ { value: "estimated", label: { en: "Estimated", pt: "Estimado" } }
300
+ ]);
301
+ var RESULT_FRACTIONS = closedVocab()([
302
+ { value: "total", label: { en: "Total", pt: "Total" } },
303
+ { value: "dissolved", label: { en: "Dissolved", pt: "Dissolvida" } },
304
+ { value: "suspended", label: { en: "Suspended", pt: "Suspensa" } }
305
+ ]);
306
+ var MEASUREMENT_LOCATIONS = closedVocab()([
307
+ { value: "field", label: { en: "Field", pt: "Campo" } },
308
+ { value: "lab", label: { en: "Lab", pt: "Laborat\xF3rio" } }
309
+ ]);
310
+ var VALIDATION_STATUSES = closedVocab()([
311
+ { value: "unvalidated", label: { en: "Unvalidated", pt: "N\xE3o validado" } },
312
+ { value: "validated", label: { en: "Validated", pt: "Validado" } },
313
+ { value: "qualified", label: { en: "Qualified", pt: "Qualificado" } },
314
+ { value: "rejected", label: { en: "Rejected", pt: "Rejeitado" } }
315
+ ]);
316
+ var PARAMETER_GROUPS = closedVocab()([
317
+ {
318
+ value: "physical",
319
+ label: { en: "Physical / field", pt: "F\xEDsico / campo" }
320
+ },
321
+ { value: "aggregate", label: { en: "Aggregate", pt: "Agregados" } },
322
+ { value: "major_ion", label: { en: "Major ions", pt: "\xCDons maiores" } },
323
+ { value: "nutrient", label: { en: "Nutrients", pt: "Nutrientes" } },
324
+ { value: "organic", label: { en: "Organic", pt: "Org\xE2nicos" } },
325
+ { value: "disinfection", label: { en: "Disinfection", pt: "Desinfec\xE7\xE3o" } },
326
+ {
327
+ value: "mining_redox",
328
+ label: { en: "Mining and redox", pt: "Minera\xE7\xE3o e redox" }
329
+ },
330
+ { value: "metal", label: { en: "Metals / trace", pt: "Metais / tra\xE7o" } },
331
+ { value: "microbiology", label: { en: "Microbiology", pt: "Microbiologia" } },
332
+ {
333
+ value: "radioactivity",
334
+ label: { en: "Radioactivity", pt: "Radioatividade" }
335
+ }
336
+ ]);
337
+ var BAROMETRIC_COMPENSATIONS = closedVocab()([
338
+ { value: "barologger", label: { en: "Barologger", pt: "Barologger" } },
339
+ { value: "vented", label: { en: "Vented cable", pt: "Cabo ventilado" } },
340
+ { value: "none", label: { en: "None", pt: "Nenhuma" } }
341
+ ]);
342
+ var READING_AGGREGATIONS = closedVocab()([
343
+ { value: "none", label: { en: "Raw readings", pt: "Leituras brutas" } },
344
+ { value: "hourly", label: { en: "Hourly", pt: "Hor\xE1ria" } },
345
+ { value: "daily", label: { en: "Daily", pt: "Di\xE1ria" } }
346
+ ]);
347
+ var WELL_STATUS_VALUES = valuesOf(WELL_STATUSES);
348
+ var PERMIT_ADMINISTRATIVE_STATUS_VALUES = valuesOf(
349
+ PERMIT_ADMINISTRATIVE_STATUSES
350
+ );
351
+ var QUALIFIER_VALUES = valuesOf(RESULT_QUALIFIERS);
352
+ var FRACTION_VALUES = valuesOf(RESULT_FRACTIONS);
353
+ var MEASURED_IN_VALUES = valuesOf(MEASUREMENT_LOCATIONS);
354
+ var FILTRATION_LOCATION_VALUES = valuesOf(MEASUREMENT_LOCATIONS);
355
+ var VALIDATION_STATUS_VALUES = valuesOf(VALIDATION_STATUSES);
356
+ var PARAMETER_GROUP_VALUES = valuesOf(PARAMETER_GROUPS);
357
+ var BAROMETRIC_COMPENSATION_VALUES = valuesOf(
358
+ BAROMETRIC_COMPENSATIONS
359
+ );
360
+ var READING_AGGREGATION_VALUES = valuesOf(READING_AGGREGATIONS);
361
+ var NUMERIC_QUALIFIERS = [
362
+ "<",
363
+ ">",
364
+ "estimated"
365
+ ];
366
+ var PARENT_SAMPLE_TYPES = [
367
+ "field_duplicate",
368
+ "split_sample"
369
+ ];
370
+ var BLANK_SAMPLE_TYPES = [
371
+ "field_blank",
372
+ "trip_blank",
373
+ "equipment_blank"
374
+ ];
375
+
376
+ // src/vocab/hydrodynamic.vocab.ts
377
+ var HYDRODYNAMIC_EVENT_TYPES = [
378
+ {
379
+ value: "spot_measurement",
380
+ label: { en: "Spot measurement", pt: "Medi\xE7\xE3o pontual" }
381
+ },
382
+ {
383
+ value: "constant_rate",
384
+ label: { en: "Constant rate", pt: "Vaz\xE3o constante" }
385
+ },
386
+ { value: "step_drawdown", label: { en: "Step drawdown", pt: "Escalonada" } },
387
+ { value: "airlift", label: { en: "Air-lift", pt: "Air-lift" } },
388
+ {
389
+ value: "recovery_only",
390
+ label: { en: "Recovery only", pt: "Recupera\xE7\xE3o" }
391
+ },
392
+ {
393
+ value: "logger_series",
394
+ label: { en: "Logger series", pt: "S\xE9rie de datalogger" }
395
+ }
396
+ ];
397
+ var MEASUREMENT_METHODS = [
398
+ {
399
+ value: "electric_probe",
400
+ label: { en: "Electric probe", pt: "Sonda el\xE9trica" }
401
+ },
402
+ {
403
+ value: "pressure_transducer",
404
+ label: { en: "Pressure transducer", pt: "Transdutor de press\xE3o" }
405
+ },
406
+ { value: "air_line", label: { en: "Air line", pt: "Linha de ar" } },
407
+ { value: "tape", label: { en: "Tape", pt: "Trena" } }
408
+ ];
409
+ var AQUIFER_ANALYSIS_METHODS = [
410
+ { value: "cooper_jacob", label: { en: "Cooper-Jacob", pt: "Cooper-Jacob" } },
411
+ { value: "theis", label: { en: "Theis", pt: "Theis" } },
412
+ { value: "neuman", label: { en: "Neuman", pt: "Neuman" } },
413
+ { value: "hantush", label: { en: "Hantush", pt: "Hantush" } },
414
+ {
415
+ value: "birsoy_summers",
416
+ label: { en: "Birsoy-Summers", pt: "Birsoy-Summers" }
417
+ },
418
+ { value: "eden_hazel", label: { en: "Eden-Hazel", pt: "Eden-Hazel" } },
419
+ {
420
+ value: "visual_inspection",
421
+ label: { en: "Visual inspection", pt: "Inspe\xE7\xE3o visual" }
422
+ }
423
+ ];
424
+
425
+ // src/validators/well.validators.ts
207
426
  var mergeWell = (0, import_defu.createDefu)((obj, key, value) => {
208
427
  if (Array.isArray(value)) {
209
428
  obj[key] = value;
@@ -246,7 +465,8 @@ var LocationPropertiesSchema = import_zod.z.object({
246
465
  lat_precision: import_zod.z.number().optional(),
247
466
  lng_precision: import_zod.z.number().optional(),
248
467
  elevation_precision: import_zod.z.number().optional(),
249
- original_crs: import_zod.z.string().optional()
468
+ original_crs: import_zod.z.string().optional(),
469
+ timezone: import_zod.z.string().refine(isIanaTimeZone, "timezone must be an IANA time zone name").optional()
250
470
  });
251
471
  var LocationSchema = import_zod.z.object({
252
472
  lat: import_zod.z.number(),
@@ -337,6 +557,7 @@ var CaveSchema = import_zod.z.object({
337
557
  });
338
558
  var LevelReadingSchema = import_zod.z.object({
339
559
  elapsed: import_zod.z.number(),
560
+ datetime: rfc3339().optional(),
340
561
  depth: import_zod.z.number(),
341
562
  depth_precision: import_zod.z.number().optional(),
342
563
  pressure: import_zod.z.number().optional()
@@ -366,14 +587,16 @@ var SpotMeasurementEventSchema = HydrodynamicEventBaseSchema.extend({
366
587
  static_level: import_zod.z.number(),
367
588
  static_level_precision: import_zod.z.number().optional(),
368
589
  measurement_method: import_zod.z.string().optional(),
369
- steps: import_zod.z.array(PumpingStepSchema).max(1).optional(),
590
+ // SHOULD carry at most one step; another count is valid (v2.4).
591
+ steps: import_zod.z.array(PumpingStepSchema).optional(),
370
592
  recovery: RecoveryPhaseSchema.optional()
371
593
  });
372
594
  var ConstantRateEventSchema = HydrodynamicEventBaseSchema.extend({
373
595
  type: import_zod.z.literal("constant_rate"),
374
596
  static_level: import_zod.z.number().optional(),
375
597
  static_level_precision: import_zod.z.number().optional(),
376
- steps: import_zod.z.tuple([PumpingStepSchema]).optional(),
598
+ // SHOULD carry exactly one step; another count is valid (v2.4).
599
+ steps: import_zod.z.array(PumpingStepSchema).optional(),
377
600
  recovery: RecoveryPhaseSchema.optional()
378
601
  });
379
602
  var StepDrawdownEventSchema = HydrodynamicEventBaseSchema.extend({
@@ -394,13 +617,62 @@ var RecoveryOnlyEventSchema = HydrodynamicEventBaseSchema.extend({
394
617
  pumping_duration: import_zod.z.number().optional(),
395
618
  recovery: RecoveryPhaseSchema
396
619
  });
620
+ var LoggerReadingSchema = import_zod.z.object({
621
+ datetime: rfc3339(),
622
+ elapsed: import_zod.z.undefined({ message: "logger_series readings use datetime, not elapsed" }).optional(),
623
+ depth: import_zod.z.number(),
624
+ depth_precision: import_zod.z.number().optional(),
625
+ pressure: import_zod.z.number().optional(),
626
+ depth_min: import_zod.z.number().optional(),
627
+ depth_max: import_zod.z.number().optional()
628
+ });
629
+ var closedEnum = (values) => import_zod.z.enum(values);
630
+ var LoggerSeriesEventSchema = HydrodynamicEventBaseSchema.extend({
631
+ type: import_zod.z.literal("logger_series"),
632
+ measurement_method: import_zod.z.string().optional(),
633
+ sensor_depth: import_zod.z.number().optional(),
634
+ interval: import_zod.z.number().positive().optional(),
635
+ barometric_compensation: closedEnum(
636
+ BAROMETRIC_COMPENSATION_VALUES
637
+ ).optional(),
638
+ aggregation: closedEnum(READING_AGGREGATION_VALUES).optional(),
639
+ readings: import_zod.z.array(LoggerReadingSchema).min(1),
640
+ steps: import_zod.z.undefined({ message: "logger_series carries no steps" }).optional(),
641
+ recovery: import_zod.z.undefined({ message: "logger_series carries no recovery" }).optional()
642
+ });
643
+ var HYDRODYNAMIC_TYPES = HYDRODYNAMIC_EVENT_TYPES.map(
644
+ (t) => t.value
645
+ );
397
646
  var HydrodynamicEventSchema = import_zod.z.discriminatedUnion("type", [
398
647
  SpotMeasurementEventSchema,
399
648
  ConstantRateEventSchema,
400
649
  StepDrawdownEventSchema,
401
650
  AirliftEventSchema,
402
- RecoveryOnlyEventSchema
403
- ]).or(HydrodynamicEventBaseSchema.passthrough());
651
+ RecoveryOnlyEventSchema,
652
+ LoggerSeriesEventSchema
653
+ ]).or(
654
+ HydrodynamicEventBaseSchema.passthrough().refine(
655
+ (e) => !HYDRODYNAMIC_TYPES.includes(e.type),
656
+ { message: "malformed hydrodynamic event" }
657
+ )
658
+ ).superRefine((e, ctx) => {
659
+ if (e.type !== "logger_series") return;
660
+ const series = e;
661
+ if (series.aggregation !== "hourly" && series.aggregation !== "daily") {
662
+ return;
663
+ }
664
+ series.readings.forEach((r, i) => {
665
+ for (const key of ["depth_min", "depth_max"]) {
666
+ if (r[key] === void 0) {
667
+ ctx.addIssue({
668
+ code: import_zod.z.ZodIssueCode.custom,
669
+ path: ["readings", i, key],
670
+ message: `${key} is required in an aggregated logger_series`
671
+ });
672
+ }
673
+ }
674
+ });
675
+ });
404
676
  var WellStatusSchema = import_zod.z.enum([
405
677
  "active",
406
678
  "maintenance",
@@ -442,7 +714,7 @@ var AquiferAnalysisSchema = import_zod.z.object({
442
714
  max_flow_rate_basis: import_zod.z.string().optional(),
443
715
  specific_capacity: import_zod.z.number().optional(),
444
716
  transmissivity: import_zod.z.number().optional(),
445
- storativity: import_zod.z.number().nullable().optional(),
717
+ storativity: import_zod.z.number().optional(),
446
718
  hydraulic_conductivity: import_zod.z.number().optional(),
447
719
  aquifer_thickness: import_zod.z.number().optional(),
448
720
  jacob_b: import_zod.z.number().optional(),
@@ -603,7 +875,7 @@ var OperatingRegimeSchema = import_zod.z.object({
603
875
  id: import_zod.z.string(),
604
876
  effective_from: rfc3339(),
605
877
  flow_rate: import_zod.z.number().nonnegative().optional(),
606
- daily_operating_time: import_zod.z.number().min(0).max(24).optional(),
878
+ daily_operating_time: import_zod.z.number().nonnegative().optional(),
607
879
  days_per_week: import_zod.z.number().int().min(1).max(7).optional(),
608
880
  notes: import_zod.z.string().optional(),
609
881
  updated_at: rfc3339().optional()
@@ -740,7 +1012,10 @@ var WaterSampleSchema = import_zod.z.object({
740
1012
  results: import_zod.z.array(WaterQualityResultSchema).min(1)
741
1013
  });
742
1014
  var WellSchema = import_zod.z.object({
743
- version: import_zod.z.number().int(),
1015
+ version: import_zod.z.union([
1016
+ import_zod.z.number().int().nonnegative(),
1017
+ import_zod.z.string().regex(/^\d+\.\d+$/, 'version must be "M.m"')
1018
+ ]),
744
1019
  "@context": import_zod.z.union([import_zod.z.string(), import_zod.z.array(import_zod.z.unknown()), import_zod.z.record(import_zod.z.unknown())]).optional(),
745
1020
  well_id: import_zod.z.array(WellIdSchema).optional(),
746
1021
  location: LocationSchema.optional(),
@@ -844,28 +1119,39 @@ function normalizeV1Fields(raw) {
844
1119
  }
845
1120
  function parseWell(json) {
846
1121
  const raw = JSON.parse(json);
847
- const version = raw.version;
848
- if (version !== void 0 && typeof version === "number") {
849
- if (version !== 1 && version !== 2) {
850
- throw new Error(`Unsupported .well format version: ${version}`);
1122
+ if (raw.version === void 0) {
1123
+ raw.version = 2;
1124
+ } else {
1125
+ const parsed = parseFormatVersion(raw.version);
1126
+ if (!parsed) {
1127
+ throw new Error(
1128
+ `Malformed .well format version: ${JSON.stringify(raw.version)}`
1129
+ );
851
1130
  }
852
- if (version === 1) {
1131
+ if (parsed.major === 1) {
853
1132
  const normalized = normalizeV1Fields(raw);
854
1133
  return mergeParsed(
855
1134
  WellSchema.parse(normalized),
856
1135
  normalized
857
1136
  );
858
1137
  }
859
- } else if (version !== void 0) {
860
- throw new Error(`Unsupported .well format version: ${String(version)}`);
861
- } else {
862
- raw.version = 2;
1138
+ if (parsed.major !== 2) {
1139
+ throw new Error(
1140
+ `Unsupported .well format version: ${JSON.stringify(raw.version)}`
1141
+ );
1142
+ }
1143
+ }
1144
+ if (raw.version === 2 && Array.isArray(raw.aquifer_analysis)) {
1145
+ for (const item of raw.aquifer_analysis) {
1146
+ if (isPlainObject(item) && item.storativity === null) {
1147
+ delete item.storativity;
1148
+ }
1149
+ }
863
1150
  }
864
1151
  return mergeParsed(WellSchema.parse(raw), raw);
865
1152
  }
866
1153
 
867
1154
  // src/utils/well.utils.ts
868
- var WELL_FORMAT_VERSION = 2;
869
1155
  var INCHES_TO_MM = 25.4;
870
1156
  var LEGACY_ROOT_KEYS = /* @__PURE__ */ new Set([
871
1157
  "lat",
@@ -912,7 +1198,7 @@ var VISIBILITY_LEAF_KEYS = SECTION_KEYS.flatMap(
912
1198
  (section) => VISIBILITY_TREE[section].length ? VISIBILITY_TREE[section] : [section]
913
1199
  );
914
1200
  var EMPTY_WELL = {
915
- version: 2,
1201
+ version: WELL_FORMAT_VERSION,
916
1202
  bore_hole: [],
917
1203
  well_case: [],
918
1204
  reduction: [],
@@ -1008,7 +1294,9 @@ function decodeV2Well(raw) {
1008
1294
  const rawScreens = raw.well_screen ?? [];
1009
1295
  const decoded = {
1010
1296
  ...createEmptyWell(),
1011
- ...raw.version !== void 0 && { version: raw.version },
1297
+ ...raw.version !== void 0 && {
1298
+ version: raw.version
1299
+ },
1012
1300
  ...raw["@context"] !== void 0 && {
1013
1301
  "@context": raw["@context"]
1014
1302
  },
@@ -1124,6 +1412,19 @@ function unknownRootKeys(well) {
1124
1412
  (key) => !(key in WellSchema.shape) && !LEGACY_ROOT_KEYS.has(key)
1125
1413
  );
1126
1414
  }
1415
+ function omitNulls(value) {
1416
+ if (Array.isArray(value)) return value.map(omitNulls);
1417
+ if (typeof value !== "object" || value === null) return value;
1418
+ const out = {};
1419
+ for (const [key, member] of Object.entries(value)) {
1420
+ if (key.startsWith("x-") || key === "@context") {
1421
+ out[key] = member;
1422
+ } else if (member !== null) {
1423
+ out[key] = omitNulls(member);
1424
+ }
1425
+ }
1426
+ return out;
1427
+ }
1127
1428
  function serializeWell(well) {
1128
1429
  const payload = {
1129
1430
  version: WELL_FORMAT_VERSION,
@@ -1180,7 +1481,7 @@ function serializeWell(well) {
1180
1481
  const value = well[key];
1181
1482
  if (value !== void 0) payload[key] = value;
1182
1483
  }
1183
- return JSON.stringify(payload);
1484
+ return JSON.stringify(omitNulls(payload));
1184
1485
  }
1185
1486
  function isWellEmpty(well) {
1186
1487
  if (!well) return true;
@@ -1263,10 +1564,18 @@ function deserializeWell(jsonString) {
1263
1564
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
1264
1565
  throw new Error("Invalid profile format");
1265
1566
  }
1266
- if (typeof raw.version === "number") {
1267
- if (raw.version === 2) return withCalculatedWellDepth(decodeV2Well(raw));
1268
- if (raw.version === 1) return withCalculatedWellDepth(decodeV1Well(raw));
1269
- throw new Error(`Unsupported .well format version: ${raw.version}`);
1567
+ if (raw.version !== void 0) {
1568
+ const parsed = parseFormatVersion(raw.version);
1569
+ if (!parsed) {
1570
+ throw new Error(
1571
+ `Malformed .well format version: ${JSON.stringify(raw.version)}`
1572
+ );
1573
+ }
1574
+ if (parsed.major === 2) return withCalculatedWellDepth(decodeV2Well(raw));
1575
+ if (parsed.major === 1) return withCalculatedWellDepth(decodeV1Well(raw));
1576
+ throw new Error(
1577
+ `Unsupported .well format version: ${JSON.stringify(raw.version)}`
1578
+ );
1270
1579
  }
1271
1580
  if (isWellEmpty(raw)) return null;
1272
1581
  const constructiveSource = raw.constructive ?? raw;
@@ -2676,51 +2985,6 @@ var HISTORY_LOG_SEVERITIES = [
2676
2985
  { value: "critical", label: { en: "Critical", pt: "Cr\xEDtica" } }
2677
2986
  ];
2678
2987
 
2679
- // src/vocab/hydrodynamic.vocab.ts
2680
- var HYDRODYNAMIC_EVENT_TYPES = [
2681
- {
2682
- value: "spot_measurement",
2683
- label: { en: "Spot measurement", pt: "Medi\xE7\xE3o pontual" }
2684
- },
2685
- {
2686
- value: "constant_rate",
2687
- label: { en: "Constant rate", pt: "Vaz\xE3o constante" }
2688
- },
2689
- { value: "step_drawdown", label: { en: "Step drawdown", pt: "Escalonada" } },
2690
- { value: "airlift", label: { en: "Air-lift", pt: "Air-lift" } },
2691
- {
2692
- value: "recovery_only",
2693
- label: { en: "Recovery only", pt: "Recupera\xE7\xE3o" }
2694
- }
2695
- ];
2696
- var MEASUREMENT_METHODS = [
2697
- {
2698
- value: "electric_probe",
2699
- label: { en: "Electric probe", pt: "Sonda el\xE9trica" }
2700
- },
2701
- {
2702
- value: "pressure_transducer",
2703
- label: { en: "Pressure transducer", pt: "Transdutor de press\xE3o" }
2704
- },
2705
- { value: "air_line", label: { en: "Air line", pt: "Linha de ar" } },
2706
- { value: "tape", label: { en: "Tape", pt: "Trena" } }
2707
- ];
2708
- var AQUIFER_ANALYSIS_METHODS = [
2709
- { value: "cooper_jacob", label: { en: "Cooper-Jacob", pt: "Cooper-Jacob" } },
2710
- { value: "theis", label: { en: "Theis", pt: "Theis" } },
2711
- { value: "neuman", label: { en: "Neuman", pt: "Neuman" } },
2712
- { value: "hantush", label: { en: "Hantush", pt: "Hantush" } },
2713
- {
2714
- value: "birsoy_summers",
2715
- label: { en: "Birsoy-Summers", pt: "Birsoy-Summers" }
2716
- },
2717
- { value: "eden_hazel", label: { en: "Eden-Hazel", pt: "Eden-Hazel" } },
2718
- {
2719
- value: "visual_inspection",
2720
- label: { en: "Visual inspection", pt: "Inspe\xE7\xE3o visual" }
2721
- }
2722
- ];
2723
-
2724
2988
  // src/vocab/operation.vocab.ts
2725
2989
  var PUMP_TYPES = [
2726
2990
  {
@@ -2931,114 +3195,6 @@ function vocabValues(vocab, { includeDeprecated = false } = {}) {
2931
3195
  return vocab.filter((entry) => includeDeprecated || !entry.deprecated).map((entry) => entry.value);
2932
3196
  }
2933
3197
 
2934
- // src/vocab/closed.vocab.ts
2935
- function closedVocab() {
2936
- return (entries) => entries;
2937
- }
2938
- var valuesOf = (vocab) => vocab.map((entry) => entry.value);
2939
- var WELL_STATUSES = closedVocab()([
2940
- { value: "active", label: { en: "Active", pt: "Ativo" } },
2941
- {
2942
- value: "maintenance",
2943
- label: { en: "Under maintenance", pt: "Em manuten\xE7\xE3o" }
2944
- },
2945
- { value: "inactive", label: { en: "Inactive", pt: "Paralisado" } },
2946
- {
2947
- value: "decommissioned",
2948
- label: { en: "Decommissioned", pt: "Desativado" }
2949
- },
2950
- { value: "abandoned", label: { en: "Abandoned", pt: "Abandonado" } }
2951
- ]);
2952
- var PERMIT_ADMINISTRATIVE_STATUSES = closedVocab()([
2953
- { value: "requested", label: { en: "Requested", pt: "Requerida" } },
2954
- { value: "granted", label: { en: "Granted", pt: "Concedida" } },
2955
- { value: "suspended", label: { en: "Suspended", pt: "Suspensa" } },
2956
- { value: "revoked", label: { en: "Revoked", pt: "Revogada" } },
2957
- { value: "denied", label: { en: "Denied", pt: "Indeferida" } },
2958
- { value: "withdrawn", label: { en: "Withdrawn", pt: "Cancelada" } }
2959
- ]);
2960
- var VOLUME_LIMIT_PERIODS = closedVocab()([
2961
- { value: "daily", label: { en: "Daily", pt: "Di\xE1rio" } },
2962
- { value: "monthly", label: { en: "Monthly", pt: "Mensal" } },
2963
- { value: "annual", label: { en: "Annual", pt: "Anual" } }
2964
- ]);
2965
- var PRODUCTION_ENTRY_TYPES = closedVocab()([
2966
- {
2967
- value: "meter_reading",
2968
- label: { en: "Meter reading", pt: "Leitura de hidr\xF4metro" }
2969
- },
2970
- {
2971
- value: "estimated_volume",
2972
- label: { en: "Estimated volume", pt: "Volume estimado" }
2973
- }
2974
- ]);
2975
- var RESULT_QUALIFIERS = closedVocab()([
2976
- { value: "<", label: { en: "< less than", pt: "< menor que" } },
2977
- { value: ">", label: { en: "> greater than", pt: "> maior que" } },
2978
- { value: "not_detected", label: { en: "Not detected", pt: "N\xE3o detectado" } },
2979
- { value: "estimated", label: { en: "Estimated", pt: "Estimado" } }
2980
- ]);
2981
- var RESULT_FRACTIONS = closedVocab()([
2982
- { value: "total", label: { en: "Total", pt: "Total" } },
2983
- { value: "dissolved", label: { en: "Dissolved", pt: "Dissolvida" } },
2984
- { value: "suspended", label: { en: "Suspended", pt: "Suspensa" } }
2985
- ]);
2986
- var MEASUREMENT_LOCATIONS = closedVocab()([
2987
- { value: "field", label: { en: "Field", pt: "Campo" } },
2988
- { value: "lab", label: { en: "Lab", pt: "Laborat\xF3rio" } }
2989
- ]);
2990
- var VALIDATION_STATUSES = closedVocab()([
2991
- { value: "unvalidated", label: { en: "Unvalidated", pt: "N\xE3o validado" } },
2992
- { value: "validated", label: { en: "Validated", pt: "Validado" } },
2993
- { value: "qualified", label: { en: "Qualified", pt: "Qualificado" } },
2994
- { value: "rejected", label: { en: "Rejected", pt: "Rejeitado" } }
2995
- ]);
2996
- var PARAMETER_GROUPS = closedVocab()([
2997
- {
2998
- value: "physical",
2999
- label: { en: "Physical / field", pt: "F\xEDsico / campo" }
3000
- },
3001
- { value: "aggregate", label: { en: "Aggregate", pt: "Agregados" } },
3002
- { value: "major_ion", label: { en: "Major ions", pt: "\xCDons maiores" } },
3003
- { value: "nutrient", label: { en: "Nutrients", pt: "Nutrientes" } },
3004
- { value: "organic", label: { en: "Organic", pt: "Org\xE2nicos" } },
3005
- { value: "disinfection", label: { en: "Disinfection", pt: "Desinfec\xE7\xE3o" } },
3006
- {
3007
- value: "mining_redox",
3008
- label: { en: "Mining and redox", pt: "Minera\xE7\xE3o e redox" }
3009
- },
3010
- { value: "metal", label: { en: "Metals / trace", pt: "Metais / tra\xE7o" } },
3011
- { value: "microbiology", label: { en: "Microbiology", pt: "Microbiologia" } },
3012
- {
3013
- value: "radioactivity",
3014
- label: { en: "Radioactivity", pt: "Radioatividade" }
3015
- }
3016
- ]);
3017
- var WELL_STATUS_VALUES = valuesOf(WELL_STATUSES);
3018
- var PERMIT_ADMINISTRATIVE_STATUS_VALUES = valuesOf(
3019
- PERMIT_ADMINISTRATIVE_STATUSES
3020
- );
3021
- var QUALIFIER_VALUES = valuesOf(RESULT_QUALIFIERS);
3022
- var FRACTION_VALUES = valuesOf(RESULT_FRACTIONS);
3023
- var MEASURED_IN_VALUES = valuesOf(MEASUREMENT_LOCATIONS);
3024
- var FILTRATION_LOCATION_VALUES = valuesOf(MEASUREMENT_LOCATIONS);
3025
- var VALIDATION_STATUS_VALUES = valuesOf(VALIDATION_STATUSES);
3026
- var PARAMETER_GROUP_VALUES = valuesOf(PARAMETER_GROUPS);
3027
- var NUMERIC_QUALIFIERS = [
3028
- "<",
3029
- ">",
3030
- "estimated"
3031
- ];
3032
- var PARENT_SAMPLE_TYPES = [
3033
- "field_duplicate",
3034
- "split_sample"
3035
- ];
3036
- var BLANK_SAMPLE_TYPES = [
3037
- "field_blank",
3038
- "trip_blank",
3039
- "equipment_blank"
3040
- ];
3041
-
3042
3198
  // src/vocab/waterSample.vocab.ts
3043
3199
  var SAMPLE_TYPES = [
3044
3200
  { value: "routine", label: { en: "Routine", pt: "Rotina" } },
@@ -3304,6 +3460,8 @@ function expandedToStandardUncertainty(expanded, coverageFactor = 2) {
3304
3460
  AirliftEventSchema,
3305
3461
  AquiferAnalysisSchema,
3306
3462
  AttachmentSchema,
3463
+ BAROMETRIC_COMPENSATIONS,
3464
+ BAROMETRIC_COMPENSATION_VALUES,
3307
3465
  BLANK_SAMPLE_TYPES,
3308
3466
  BR_GM_MS_888_2021,
3309
3467
  BoreHoleSchema,
@@ -3339,6 +3497,8 @@ function expandedToStandardUncertainty(expanded, coverageFactor = 2) {
3339
3497
  LithologySchema,
3340
3498
  LocationPropertiesSchema,
3341
3499
  LocationSchema,
3500
+ LoggerReadingSchema,
3501
+ LoggerSeriesEventSchema,
3342
3502
  MAINTENANCE_TYPES,
3343
3503
  MEASURED_IN_VALUES,
3344
3504
  MEASUREMENT_LOCATIONS,
@@ -3373,6 +3533,8 @@ function expandedToStandardUncertainty(expanded, coverageFactor = 2) {
3373
3533
  PurgeReadingSchema,
3374
3534
  PurgeSchema,
3375
3535
  QUALIFIER_VALUES,
3536
+ READING_AGGREGATIONS,
3537
+ READING_AGGREGATION_VALUES,
3376
3538
  READING_SOURCES,
3377
3539
  RESULT_FRACTIONS,
3378
3540
  RESULT_QUALIFIERS,
@@ -3404,6 +3566,7 @@ function expandedToStandardUncertainty(expanded, coverageFactor = 2) {
3404
3566
  WATER_QUALITY_UNITS,
3405
3567
  WATER_QUALITY_VOCABULARY_VERSION,
3406
3568
  WATER_USES,
3569
+ WELL_FORMAT_VERSION,
3407
3570
  WELL_PURPOSES,
3408
3571
  WELL_STATUSES,
3409
3572
  WELL_STATUS_VALUES,
@@ -3418,6 +3581,7 @@ function expandedToStandardUncertainty(expanded, coverageFactor = 2) {
3418
3581
  WellStatusSchema,
3419
3582
  calculatedWellDepth,
3420
3583
  checkIfProfileIsEmpty,
3584
+ compareFormatVersions,
3421
3585
  concentrationFromCanonical,
3422
3586
  concentrationToCanonical,
3423
3587
  convertProfileFromJSON,
@@ -3445,6 +3609,7 @@ function expandedToStandardUncertainty(expanded, coverageFactor = 2) {
3445
3609
  hoursToMinutes,
3446
3610
  hpToKilowatts,
3447
3611
  inchesToMm,
3612
+ isIanaTimeZone,
3448
3613
  isKnownParameter,
3449
3614
  isVocabValue,
3450
3615
  isWellEmpty,
@@ -3461,6 +3626,7 @@ function expandedToStandardUncertainty(expanded, coverageFactor = 2) {
3461
3626
  mmToSlotNumber,
3462
3627
  normalizeColorUnit,
3463
3628
  parameterKey,
3629
+ parseFormatVersion,
3464
3630
  parseWell,
3465
3631
  powerFromCanonical,
3466
3632
  powerToCanonical,