Getting started

First use

Read a .well file with @welldot/core, handle validation errors, inspect the typed data, change it and save it again.

This page reads a well, looks at a few fields, changes one and writes the file back. It runs in Node 18 or later; in a browser, replace the file system calls with a File or fetch.

Read and validate

read.ts
import { readFile } from 'node:fs/promises';
import { parseWell, type Well } from '@welldot/core';

const text = await readFile('PT-01.well', 'utf8');
const well: Well = parseWell(text);

parseWell takes the JSON text (not an already parsed object), validates it against the schema and returns a typed Well. A file with "version": 1 is upgraded to v2 first. It throws when:

  • the text is not JSON (a SyntaxError from JSON.parse);
  • the version is malformed or unsupported (an Error naming the value);
  • the document does not match the schema (a ZodError listing every problem).
type Issue = { path: (string | number)[]; message: string };

try {
  parseWell(text);
} catch (error) {
  if (error instanceof Error && error.name === 'ZodError') {
    for (const issue of (error as Error & { issues: Issue[] }).issues) {
      console.log(issue.path.join('.'), issue.message);
      // well_screen.0.screen_slot Required
    }
  } else {
    console.log((error as Error).message);
  }
}

Checking name rather than instanceof ZodError avoids importing zod yourself and keeps working if your project resolves a different copy of it.

Parsing and validating shows how to turn these into readable messages.

Inspect the data

Every block is typed. The construction and geology arrays are always present; the other blocks are optional.

console.log(well.name, well.well_type);

for (const layer of well.lithology) {
  console.log(`${layer.from}–${layer.to} m  ${layer.description}`);
}

const screens = well.well_screen.map(s => `${s.from}–${s.to} m`);
const tests = (well.hydrodynamic_events ?? []).filter(
  e => e.type === 'constant_rate' || e.type === 'step_drawdown',
);

HydrodynamicEvent is a union discriminated by type, so narrowing on type gives you the fields of that event kind.

For derived values, such as the latest static level, gravel pack volume or permit status, use @welldot/utils.

Change and save

save.ts
import { writeFile } from 'node:fs/promises';
import { serializeWell } from '@welldot/core';

well.well_purpose = ['production', 'monitoring'];
await writeFile('PT-01.well', serializeWell(well), 'utf8');

serializeWell writes "version": "2.4", leaves out empty optional fields and keeps every member it does not know, including x- extensions, as the spec requires (Serializing and sharing).

Show labels and convert units

Values are stored as keys and in SI units. Convert for display:

import {
  CONSTRUCTION_MATERIALS,
  getVocabLabel,
  metersToFeet,
  mmToInches,
} from '@welldot/core';

const casing = well.well_case[0];
getVocabLabel(CONSTRUCTION_MATERIALS, casing.type, 'pt'); // 'Aço carbono'
mmToInches(casing.diameter); // 8.62… for 219 mm
metersToFeet(casing.to);

Next