Перейти до вмісту

@overvis/hmi-shared

Цей контент ще не доступний вашою мовою.

This package is for dashboard, CLI, and integration developers. For network operations, start with the API reference.

Cross-cutting TypeBox schemas, Standard Schema validators, value codecs, and small pure utilities shared by API clients, CLI, and UI. No I/O, no DB, no node:* builtins. The main export is loaded by the browser.

Install with pnpm add @overvis/hmi-shared (or npm or Yarn). Node.js consumers need Node.js 22 or later; browsers load it through a bundler. @overvis/hmi-shared/app-router contains only types and needs @trpc/server 11 as a peer; the other entries do not. Keep @overvis/hmi-shared on the same version as the other HMI packages; see Versions and updates.

The installed package includes TypeScript declarations for every exported schema and helper, including fields this chapter does not list. The generated PublicAppRouter declaration provides procedure inputs and outputs.

ImportPurpose
@overvis/hmi-sharedCore: schema, API errors, localized strings, locales, date formatting
@overvis/hmi-shared/workspaceWorkspaces, members, uploads, branding, audit, maps, jobs, support
@overvis/hmi-shared/setupPull, push, setup database, parameter graph, templates
@overvis/hmi-shared/parameterParameter sources, value types, live values, readings
@overvis/hmi-shared/endpointEndpoints, connection status, device discovery
@overvis/hmi-shared/dashboardDashboard configuration, host props, host externals, build manifest, and fleet schemas
@overvis/hmi-shared/reportReport presets, representations, comments
@overvis/hmi-shared/graphicsDrawings, symbols, and connectors
@overvis/hmi-shared/parameter-engineValue codecs, formula evaluation, and formula validation
@overvis/hmi-shared/taskfileMC252 taskfile diagnostics, definitions, and logging-target mappings
@overvis/hmi-shared/expressionThe formula language: compile, type-check, and evaluate formulas
@overvis/hmi-shared/automationAutomation and alarm rule schemas, setup payloads, limits, the validator, and the language service
@overvis/hmi-shared/device-apiDevice API manifests: schema, validator, path blocking, roles, redaction
@overvis/hmi-shared/billingBilled cost types and their units, the published price list, and cost estimates (estimateCost)
@overvis/hmi-shared/starterEdge program starters: placeholder schemas and value checks
@overvis/hmi-shared/documentDocuments: schema, validator, and formula evaluation
@overvis/hmi-shared/api-errorsError code → status tables and the client version header
@overvis/hmi-shared/translationLocale and catalog helpers without other domain schemas
@overvis/hmi-shared/app-routerGenerated PublicAppRouter declaration for typed tRPC clients

Each name is exported from one entry; only the locale helpers are also in ./translation. The package has no side effects, so bundlers keep only what a program imports.

import type { PublicAppRouter } from "@overvis/hmi-shared/app-router";
import { formatAppDateTime, parseAppDateTime } from "@overvis/hmi-shared";
import { endpointStatusOutputSchema } from "@overvis/hmi-shared/endpoint";
import { parameterSetupPayloadSchema } from "@overvis/hmi-shared/parameter";
import { canWriteSetup } from "@overvis/hmi-shared/workspace";

The published declaration is @overvis/hmi-shared/app-router. PublicAppRouter declares every public API procedure. Each procedure has a comment with the dotted path and a one-line description next to the input type. Types this package exports are imported from their entry (for example @overvis/hmi-shared/parameter) rather than repeated.

Types come from Static<typeof …Type>, or from exported aliases next to each schema. Accepted values are exported as string literal unions.

schema(type) wraps TypeBox compile as a Standard Schema object (~standard.validate).

Compilation happens on the first validation call, not when importing or wrapping a schema. Plain and defaulting wrappers share the compiled validator for the same TypeBox schema object, so a schema object changed after its first validation keeps the old compiled validator.

Use the exported validators at input boundaries: a TypeBox shape alone does not perform all domain validation. In particular, use graphicsDocumentSchema for drawing references and grouping constraints, and parameterSetupIssues from ./parameter-engine for formulas, Modbus byte capacity, references, and cycles. Setup form …FormToInput functions return API input or { ok: false, field, problem }; problem IDs are stable and carry no user-facing text.

  • Issues always include an English message for logs and tooling. keyword and params are the structured fields that translation catalogs map to user-facing text.
  • Parameter runtime errors work the same way: error.message is debug text, and reason and params are the structured fields. The TypeBox runtime-error union pairs each structured reason with its valid code and required context, so clients can distinguish valid error combinations.
  • tRPC wire: validation failures use stable message input_invalid plus data.issues (ApiValidationIssue: path / keyword / params). Domain failures keep a stable code in message and may attach data.details.

Helpers:

HelperUse
schema(type)Compile + Standard Schema validate
schemaWithDefaults / applySchemaDefaultsFill TypeBox { default: … } before check (older push JSON omitting defaulted keys)
parseDashboardConfigurationNarrow host configuration to a dashboard’s TypeBox-derived type
readerSchema(type)Read a response from a newer API: unknown keys and changed string/array limits pass

readerSchema keeps checking types, required keys, and literal values; it drops only additionalProperties: false and string and array length, pattern, and format limits.

@overvis/hmi-shared/api-errors exports HMI_CLIENT_HEADER (x-hmi-client), formatHmiClient({ name, version }) (→ cli/0.1.0), and parseHmiClient(value). A client that sends the header and is older than the API supports gets client_outdated (PRECONDITION_FAILED); parseClientOutdatedDetails reads its { client, minimum } details.

A request body larger than the API accepts for the called procedure fails with REQUEST_BODY_TOO_LARGE_CODE (request_body_too_large, PAYLOAD_TOO_LARGE, HTTP 413). parseRequestBodyTooLargeDetails reads its RequestBodyTooLargeDetails: { limitBytes }, the largest body the procedure accepts. All three come from @overvis/hmi-shared/api-errors.

"view_only" | "operator" | "editor" | "admin" | "owner";

The roles are listed from least to most access; each includes everything before it.

HelperWho
canWriteSetupeditor, admin, owner
canWriteParameterValuesoperator, editor, admin, owner
canManageWorkspaceadmin, owner

Schemas: workspace list/get/create/rename/updateSettings/delete I/O, from @overvis/hmi-shared/workspace. List and get responses include disabledSince, an ISO timestamp or null. workspaceWatchChangesInputSchema and workspaceChangeEventSchema (WorkspaceChangeEvent) are the input and events of the workspace.watchChanges subscription.

Workspace get responses include an IANA timezone and nullable location ({ lat, lng }). workspaceTimezoneType, workspaceLocationType, workspaceSettingsSchema, and workspaceUpdateSettingsInputSchema validate those settings. workspaceSettingsFormSchema, workspaceSettingsFormValues, and workspaceSettingsFormToInput support coordinate text fields. Setup payloads also carry these fields; omission preserves existing server values on push, while location: null clears a saved location.

@overvis/hmi-shared/dashboard has the configuration building blocks of dashboards that watch many sites; the matching components are in @overvis/hmi-workspace-ui.

ExportUse
sitePropertiesid, name, optional zoneId and position, spread into a site schema
sitePositionType, isSiteGeoPositionFixed { lat, lng } or a geo parameter { parameterId }
siteEquipmentProperties(category)Readings, alarms, controls, and a maintenance due date parameter
siteReadingType(category), siteAlarmTypeA reading with its category (role) and unit; an alarm condition
fleetBoardType(category), fleetZoneTypeCards or table, zone grouping, summary columns (categories); zones
defineSiteRoles, siteRoleTypesNamed single slots: a reading, a condition, or a control
equipmentParameterIds, equipmentEntityIds, controlEntityIdsParameters to read (live) or everything bound (bound); owned item IDs
saveFleetBoard, assertBaseline, assertUniqueEntityIdsConflict-checked configuration updates
saveFleetDrawnSite, deleteFleetDrawnSiteSave or delete a site together with its generated drawing
reorderItems, moveItem, upsertItem, updateItem, starterEquipmentOrdering, adding or replacing by ID, merging an update into one item, and typical bindings for a new site
evaluateSitesEvaluate every site once per render, such as its status, as a lookup by site
saveEquipmentItem, removeEquipmentItem, reorderEquipmentListApply a binding editor’s save, delete or new order to one equipment list
treeParameters, commonBranch, branchPaths, remapParameters, duplicateEquipmentCopy a site to identical equipment elsewhere in the parameter tree
FleetConfigurationErrorconflict, duplicate-id, drawing-limit, invalid-drawing, invalid-configuration
siteStatuses, siteStatusTypeSite states in overview order, from alarm to unconfigured
dashboardPageSummaryType, dashboardSummaryListLimitThe page summary a fleet or site page publishes to the assistant; its list limit (20)

saveFleetDrawnSite(config, baseline, site, dashboardId, sites) adds a site (baseline null) or replaces it, creating its drawing in graphics or reconciling the stored one so an editor’s layout survives. sites is a FleetDrawnSiteList: get and set for the site list, entityIds (the IDs a site owns), an optional validate for rules the schema cannot express, createDrawing and reconcileDrawing. It throws conflict when the site or its zone changed since baseline and duplicate-id when an ID is used elsewhere. deleteFleetDrawnSite(config, baseline, sites) removes a site and its drawing.

category is the dashboard’s own TypeBox union of reading categories (pressure, flow, temperature…). defineSiteRoles({ running: siteRoleTypes.condition }) returns properties for the entity schema, parameterIds, mapParameterIds (reports slots without a match), and formValue/fromForm for form rows. Update helpers take the latest configuration and the baseline the editor opened with, so inside a configuration mutation a concurrent change fails with conflict instead of being overwritten.

upsertItem(items, item) replaces the item with the same ID or appends a new one, and updateItem(items, id, update) merges what update returns into one item, such as new equipment lists into one pump of a station. evaluateSites(sites, evaluate) evaluates each site once and returns a lookup that evaluates a site outside the list on demand, in the shape of a block’s stateOf.

@overvis/hmi-shared/graphics has builders for drawings generated from a configuration. Each item gets the default appearance, with the given appearance fields over it, and no bindings or action unless given:

  • graphicsSymbolElement({ id, name, symbolId, x, y, width, height, ... }) places a symbol, with optional rotation, properties, propertyBindings and action.
  • graphicsShapeElement({ id, name, kind, x, y, width, height, appearance }) is a rectangle, ellipse, text, group or junction, such as a busbar or a label.
  • graphicsConnector({ id, from, to, bends, arrow, route, appearance }) is an unnamed line, routed orthogonally and without an arrow by default, such as a pipe or a cable.

App locales: en | pl | de | uk | ru. Helpers: coerceAppLocale and appLocaleUiCode from the root or @overvis/hmi-shared/translation, and isAppLocale from @overvis/hmi-shared/translation only.

Localized content accepts two-letter language keys beyond the supported UI languages. Missing translations fall back to English and then the first available key. Date display helpers use YYYY-MM-DD HH:mm:ss in the browser’s timezone. parseExactAppDateTime accepts only that exact format; appDateTimeToISOString converts it to an ISO wire timestamp.

CRUD I/O, name/settings form schemas, enable/disable, Lucide icon id list, dashboardEntrypointUrl(id) → /static/dashboard/<id>/index.js, dashboardStylesheetUrl(id) → /static/dashboard/<id>/index.css.

wrapDashboardCss(css, { hostClassNames }) turns compiled Tailwind utilities into a dashboard stylesheet: rules are scoped to the DASHBOARD_SCOPE_ATTR element and everything inside it (DASHBOARD_SCOPE_PROPS spreads the attribute), plain classes go to the dashboard cascade layer, and variant classes go to dashboard-variants unless hostClassNames contains them or they are !important. cssClassNames(css) lists the unescaped class names that a stylesheet’s rules select, for building hostClassNames. hmi build applies both.

dashboard.create accepts optional template (catalog id, default "empty") and optional configuration. template is create-only; see Dashboard procedures for how changes are checked. applyJsonPatch(document, operations) applies JSON Patch (RFC 6902) operations, typed by jsonPatchOperationType, to a copy; jsonPatchChangedPaths lists the paths a patch changes. templates.dashboard.list is the picker (summaries, filterable by kind, parameterTemplate and query); templates.dashboard.get returns one template’s details, including the configSchema of its configuration.

DashboardRootProps are the host props of a dashboard’s default export; see the dashboard host contract.

dashboardLocationType and dashboardHref scope paths, queries, and anchors to one dashboard. Graphics actions use the same optional fields; omitting them targets the root.

documentType is the schema of a document, and DocumentSpec its type. validateDocument(document, context) checks one without network access and returns ok and diagnostics (documentDiagnosticType), each with a code, a severity, a JSON pointer path, args and, for a formula, a range; describeDocumentDiagnostic gives English text. context lists the workspace’s parameters (documentParameterInfo builds an entry from a parameter’s setup payload) and, when known, its endpoints with their device operations, alarm rules and automations, and uploaded files. analyzeDocument also returns the compiled formulas by JSON pointer, which evaluateDocumentValues, evaluateDocumentFormula, documentRepeatRows and documentSourceInput evaluate. documentApiProcedureInfo lists the HMI API procedures a document may read, with their input and answer schemas.

Pure functions in @overvis/hmi-shared/dashboard for consumption totals of counters (energy, heat, water, gas). @overvis/hmi-workspace-ui builds useCounterTotals on them.

counterPeriodIds (schema counterPeriodIdType, guard isCounterPeriodId). A dashboard’s time zone setting for calendar periods is dashboardTimeZoneType: an IANA zone, or browser for each viewer’s own (isValidDashboardTimeZone checks it; browserTimeZone() in @overvis/hmi-shared/report is the viewer’s zone).

PeriodWindowComparison window
24h, 7d, 30dRolling, ending at the last closed minuteThe preceding window of the same length
yesterday, last-week, last-monthThe previous calendar day, week or monthThe calendar period before it
today, this-week, this-monthStart of the calendar period to last closed minuteThe previous period up to the same wall time

resolveCounterPeriod(id, now, timeZone) returns { id, window, comparison, inProgress } with half-open [from, to) windows in epoch ms. Calendar periods use the given IANA zone (invalid → UTC) and weeks start on Monday. DST days are 23 or 25 hours long. A period in progress compares with the same wall time, so “today until 14:37” compares with “yesterday until 14:37”. When the previous month has no such day, the comparison is the whole previous month. A wall time skipped by DST moves to the next valid minute. In the first minute of a period in progress, both windows are empty. Local calendar boundaries are specified for zones whose UTC offset is a whole number of hours, as for reports.

classifyCounterTotal({ window, delta, first, latest, min, extent, backgroundReadingPeriodMs, deviceLogCompleteThrough? }) takes readings.aggregate values for one window (null when the pair was left out) and the counter’s reading time extent. It returns one of:

  • { kind: "value", value }. The value is the server’s delta, so it can be 0.
  • { kind: "no-data" }: the window has no readings.
  • { kind: "reset" }: a counter reset, flagged and never corrected. It is detected by delta < 0, min < first, or delta < latest − first. The last means the reading before the window was above the first reading in it.
  • { kind: "partial", value, since, until }: the first reading is more than counterCoverageToleranceMs(backgroundReadingPeriodMs) (background period + 1 minute) after the window start (since), or the last is that far before its end (until). The other side is null. For a counter whose readings a device records and HMI imports later, pass the time through which they are imported as deviceLogCompleteThrough (the parameter’s deviceLog.completeThrough): a window ending after it is partial (until is the last reading in the window, or else that time), unless background readings reach the window end. Its start is judged only with a background period. Counters with neither are never partial.

counterChangeRatio(current, previous) is (current − previous) / previous. It is null unless both are value and previous is above zero.

Settings defaults and normalization (normalizeEndpointHostname, normalizeEndpointSettingsInput, default TCP port) and CRUD inputs and outputs support endpoint configuration.

MC252 edge programs have their own contracts in ./endpoint: the edgeProgram.* procedure inputs and outputs, the setup payload, source paths, file name and source checks, edge settings with their defaults (endpointEdgeSettings), and analyzeEdgeProgramSet. See MC252 edge programs.

Live endpoint shapes from @overvis/hmi-shared/endpoint describe what endpoint.status, endpoint.watchStatus, and endpoint.traffic return:

ExportShape
EndpointStatusstate, remote address, connectedAt or lastError, updatedAt
EndpointStatusItem, EndpointStatusOutput{ endpointId, status } per endpoint; status is null until reported
EndpointStatusEventsnapshot (every endpoint), then change (endpointId, status)
EndpointTrafficChanneltx, sent, rx, events (endpointTrafficChannels lists them)
EndpointTrafficFrameFrame bytes (data, lowercase hex, id, write) or an EndpointEvent

Traffic channels:

  • tx — requests handed to the connector: Modbus RTU without CRC (unit id + PDU)
  • sent / rx — full Modbus TCP ADUs written to and received from the device
  • events — EndpointEvent; error events carry the required machine-readable reason, while message is debug text

The CLI prints both with hmi endpoint status and hmi endpoint traffic — see HMI CLI reference.

./endpoint exports MC252 HTTP status schemas and freshMc252Status. HTTP and Modbus reachability are independent; stale HTTP observations become unknown.

./taskfile exports analyzeTaskfile and analyzeTaskfiles (MC252 program diagnostics, outlines, logging targets) and taskfileLanguageService (editor features over a device’s programs).

@overvis/hmi-shared/parameter covers:

AreaConcern
Value typesDiscriminated valueType codecs / display strings
SourcesDiscriminated source (const / calc / modbus-tcp, …)
Forms and APIForms + tRPC I/O
RuntimeRuntime errors, write items, and write acknowledgements
DefaultsDefaults helpers
ZonesDisplay zones of a value, and zone bands across a range
NumbersDecimal places, number formatting, numeric value types
ConditionsNumeric conditions on a parameter’s value
EntitiesRow shapes + setup payloads

parameterValueZone(valueType, value) returns green, orange, red or null for a value. For a num parameter it compares the engineering value with the numeric zones: below lowOrangeBegins red, from it orange, from greenBegins green, from highOrangeBegins orange, from highRedBegins red; a boundary that is not set continues the band below it. For an int parameter it returns the zone of the label that matches the value. parameterZoneBands(valueType, { min, max }) lays a num parameter’s zones across a range as bands from 0 to 1, such as for a GaugeBar, and rangeFraction(range, value) places a value on it, pinned to the ends. parameterValueTypeHasZones(valueType) tells whether a value type has any zone to color by: a numeric boundary, or a zone on an int label.

formatNumValue(value, decimalPlaces, multiplier) formats a num value as live readings show it: with decimalPlaces when set (trailing zeros kept), else with the places of multiplier and trailing zeros dropped, never in exponential notation. decimalPlacesFromMultiplier(multiplier) returns those places, at most 15.

A parameter condition (parameterConditionType, ParameterCondition: parameterId, operator, value) compares a parameter with a number. evaluateNumericCondition(value, condition) applies one to a value, and negateParameterCondition(condition) returns the condition that holds exactly when it does not, such as “supply available” from “supply lost”. parameterFlagCondition(parameterId) is the condition of a flag or status code that holds while it is set: nonzero, or true.

parameterDecimals(valueType) is the number of places a parameter’s readings show: its own, else its multiplier’s; 0 for int, null for values that are not numbers. formatParameterNumber(value, valueType, places?) formats a number that way, with places overriding the parameter’s. isNumericValueType is true for values a chart or gauge plots (num, and int without labels); isComparableValueType also accepts labelled int and bool, which numeric conditions compare.

Live values are read and written with parameter.readValues, parameter.readValuesStream, and parameter.writeValues; their types are in this entry. See live value procedures.

Parameter writes return kind: "write-acknowledgement", sourceKind, writtenAt, and sent or stored bytes. Only read observations, kind: "observation", belong in measured-value displays and history.

executeParameterWriteSequence awaits each acknowledgement and stops at the first failed, missing, or cancelled result. The result names the stopping step and distinguishes a reported error from an unknown outcome. Cancellation prevents later steps; it cannot undo a sent write. Callers own retries, intent confirmation, and permission checks.

Payload-only types for pull/push and local SQLite rows live next to each entity (no self workspaceId / timestamps; entity id is the row UUID or upsert key):

TypeLocal projection
WorkspaceSetupPayloadsetup.db workspace singleton
ParameterSetupPayloadsetup.db parameter rows
ParameterBranchSetupPayloadsetup.db parameter_tree_branch rows
EndpointSetupPayloadsetup.db endpoint rows
EdgeProgramSetupPayloadsetup.db edge_program rows
DashboardSetupPayloadsetup.db dashboard rows
ReportPresetSetupPayloadsetup.db report_preset rows
ReportCommentSetupPayloadsetup.db report_comment rows
AlarmRuleSetupPayloadsetup.db alarm_rule rows
AutomationSetupPayloadsetup.db automation rows

SETUP_DB_DDL and SETUP_DB_SCHEMA_VERSION (from ./setup) describe the local SQLite checkout. The CLI upgrades an older checkout to its schema version and refuses to open a newer one. Complex values remain JSON columns; parameterSetupIssues (from ./parameter-engine) checks formulas, references, and cycles that foreign keys cannot express. isMirroredWorkspaceSourcePath defines which source files synchronize with the cloud. Unsupported paths fail with push_unknown_path.

@overvis/hmi-shared/report exports ReportPreset and ReportComment with their setup payloads. A comment payload is the comment without id. Comment times are product epochs in milliseconds and must fit a JavaScript Date (isSafeProductEpochMs, PRODUCT_EPOCH_MS_MAX).

report.comment.list returns { reportComments, viewerAccountId, authors }. authors lists each author’s accountId and name; a deleted user’s comments carry no account id, missing users are left out, and names longer than reportCommentListAuthorNameMaxLength are truncated. The comment helpers decide where a comment shows (reportCommentSessionWindow, reportCommentOnWidget, reportCommentOnPlot, reportCommentInFooter) and whether the viewer may delete it (canDeleteReportComment).

validateReportPresetRecipe checks what the schema cannot express: layout keys and that a Sankey is acyclic. workspace.push, hmi check and hmi import all run it and fail with recipe_invalid. Widget recipe types are exported from the same entry; readings I/O is in @overvis/hmi-shared/parameter. planQuickReport(items, counters) picks a quick report’s parameters in priority order up to REPORT_PRESET_TEMPORARY_MAX_PARAMETER_IDS, counting a repeated parameter once, and lists the names of the items it leaves out.

@overvis/hmi-shared/setup exports these contracts:

ContractContent
PullMint/redeem token I/O, entity/tombstone deltas since a revision (since), file hashes
PushUpserts / tombstones / file and dist references, pushId, per-entity and per-file bases
Blobs{ sha256, size } file references, workspace.missingBlobs I/O, blob route and limits
Push error detailsClosed workspace.push data.details + formatPushErrorLines
Staged pushsplitWorkspacePush / mergeWorkspacePushParts for staged pushes (workspace.pushStage)
Base revisionsbaseRevision / setup_conflict for setup edits: parseSetupConflictDetails, types
File encodingchoosePushFileEncoding / binary-safe encodings

setupEntityTypes includes report_preset and report_comment. Fail-fast push errors put the code in tRPC message and a closed object in data.details. canonicalJsonString sorts object keys at every nesting level for stable JSON (e.g. mirrored-file digests). Pull is incremental (since); push is checked per entity and per file against bases (push_conflict) and is idempotent per pushId. A push too large for one request is staged in parts and committed as one push (workspace.pushStage, workspace.pushCommit).

WorkspaceCommitPlan describes what a push would change, as printed by hmi push --dry-run.

Workspace timezones are IANA names, such as Europe/Warsaw. Setup syncs timezone and nullable { lat, lng } location; omitted fields are kept.

Branding validators check reserved paths, one image per theme, file sizes, PNG checksums and dimensions, and SVG XML. Display a branding image only when its metadata has valid: true. The favicon is a separate 32 × 32 PNG at branding/favicon.png.

The package exports jobs.wait input and output schemas, endpoint device-discovery API schemas, and typed progress and result payloads. Clients consume these contracts through the authorized API.

Each jobs.wait output pairs its jobType with the correct payload. Modbus findings are UIDs with baud rate and character format. See Find devices on an MC252 RS-485 bus for the product workflow.

Failed device-classification rows can include optional diagnostics with controller-reported mbReqPassed, checksPassed, linesPassed, scriptTime, and scriptVersion. Counters are non-negative safe integers; metadata is text of at most 128 characters or null. Absent fields stay absent; out-of-bounds diagnostics are omitted without changing the classification outcome. Counters describe execution progress, not guaranteed failing-line coordinates. An error with a null classId and match leaves identification unresolved; it does not establish that a parameter template is missing. jobs.wait also retains valid partial classification results when the overall job fails.

ExportBehavior
formatAppDateTimeAlways YYYY-MM-DD HH:mm:ss in the viewer’s local timezone. Locale-independent. Accepts string | number | Date; invalid → original string or "".
parseAppDateTimeParse UI YYYY-MM-DD HH:mm:ss (or T separator) as local wall time, or ISO / other Date-parseable strings → Date | null. Setup editors use it to write datetime values back.
parseExactAppDateTimeParse only YYYY-MM-DD HH:mm:ss in local time; invalid dates, ISO strings and whitespace return null.
appDateTimeToISOStringConvert the same strict local input to an ISO wire timestamp, or return null.
formatReportDateTimeSame shape in an IANA report timezone (invalid zone → UTC).
formatReportDateTimeMsSame wall clock plus .SSS (epoch millisecond remainder) for precise CSV timestamps.
parseReportDateTimeParse YYYY-MM-DD HH:mm:ss as wall time in a report timezone → epoch ms or null (rejects non-existent civil times).
resolveReportTimeRangeResolve preset defaultTimeRange → { from, to, usedFullRangeFallback } (ISO weeks Monday-start; full-range-to-now uses optional extent).
canonicalJsonStringDeterministic JSON (stable key order) for digests / tooling
writeCanonicalJsonThe same text as canonicalJsonString, passed to a callback in pieces, so a large value can be hashed without building it as one string
isUuidV4UUID checks
Reserved display-name helpersGuard push artifact naming prefixes

@overvis/hmi-shared/api-errors exports publicApiErrorStatus (every public error code → the tRPC status it is always sent with), the same codes per domain in publicApiErrorStatusTables, and isPublicApiErrorCode. workspace-ui shows API error code X as its catalog message error_X (apiErrorMessage) and attaches it to the form field named by details.field (mapTrpcToFormErrors).

  • Entity ids are UUIDs (format: "uuid").
  • Object schemas are usually closed (additionalProperties: false), so unknown fields fail validation. Variants are discriminated unions.
  • The package has no user-facing text; translations live in the UI packages.
  • Database access or migrations
  • Modbus encode/decode (@overvis/modbus)
  • Network connections of any kind
  • translation catalogs
PackageRelationship
@overvis/hmi-workspace-uiDashboard kit, UI, and host shell; imports ./app-router
@overvis/hmi-cliPull/push payloads, setup database, and dashboard builds