@avytheone/efmesh 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/README.md +22 -0
- package/README.ru.md +23 -0
- package/SPEC.md +1 -1
- package/package.json +1 -1
- package/src/bin.ts +39 -2
- package/src/cli.ts +204 -124
- package/src/config.ts +15 -15
- package/src/core/audit.ts +15 -11
- package/src/core/errors.ts +37 -7
- package/src/core/graph.ts +6 -6
- package/src/core/interval.ts +18 -18
- package/src/core/model.ts +76 -75
- package/src/core/sql.ts +21 -21
- package/src/discovery.ts +16 -8
- package/src/efmesh.ts +26 -15
- package/src/engine/adapter.ts +29 -12
- package/src/engine/duckdb.ts +7 -7
- package/src/engine/postgres.ts +17 -17
- package/src/error-text.ts +35 -0
- package/src/index.ts +12 -11
- package/src/init.ts +100 -28
- package/src/plan/audit-run.ts +21 -13
- package/src/plan/categorize.ts +7 -7
- package/src/plan/contract.ts +23 -19
- package/src/plan/diff.ts +33 -29
- package/src/plan/executor.ts +207 -125
- package/src/plan/explain.ts +29 -29
- package/src/plan/fingerprint.ts +35 -31
- package/src/plan/graph-html.ts +7 -7
- package/src/plan/janitor.ts +30 -25
- package/src/plan/lineage.ts +20 -16
- package/src/plan/lock.ts +27 -10
- package/src/plan/metrics.ts +4 -4
- package/src/plan/naming.ts +23 -23
- package/src/plan/planner.ts +107 -90
- package/src/plan/run.ts +27 -19
- package/src/plan/schedule.ts +46 -41
- package/src/plan/status.ts +15 -14
- package/src/state/postgres.ts +19 -19
- package/src/state/sqlite.ts +27 -27
- package/src/state/store.ts +67 -55
- package/src/testing/index.ts +27 -26
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,41 @@ versioning — [SemVer](https://semver.org/).
|
|
|
5
5
|
Internal development history was tracked in phases F0–F6 (SPEC.md §13);
|
|
6
6
|
the first version gathers them in full.
|
|
7
7
|
|
|
8
|
+
## [0.2.1] — 2026-07-16
|
|
9
|
+
|
|
10
|
+
Theme: hygiene for the first stranger — the whole surface is English,
|
|
11
|
+
failures explain themselves, execution is visible.
|
|
12
|
+
|
|
13
|
+
- Detailed execution log (#14). `apply` and `run` now narrate their work
|
|
14
|
+
through Effect's logging system: per-model build start/finish with duration,
|
|
15
|
+
backfill batch progress (`batch n of m` with interval bounds), warn-audits
|
|
16
|
+
and promotion. Levels — **info** for lifecycle (visible by default), **warn**
|
|
17
|
+
for warn-audits/retries, **debug** for the rendered SQL and lock internals;
|
|
18
|
+
the existing `--log-level` flag sets the minimum. Every line carries
|
|
19
|
+
structured fields (`model`, `env`, `interval`) as annotations. Logs go to
|
|
20
|
+
**stderr** (stdout and `--json` stay byte-clean); a TTY gets pretty colored
|
|
21
|
+
output, a pipe/journal gets one-line logfmt with no ANSI. Embedders provide
|
|
22
|
+
their own `Logger` layer to redirect sinks and levels. Exit codes unchanged.
|
|
23
|
+
- Human-readable, precise errors everywhere (#13). Every tagged error now
|
|
24
|
+
derives its `message` from its typed fields, so the culprit (model, env,
|
|
25
|
+
file, interval) and the underlying engine/system text are always present —
|
|
26
|
+
an empty `EngineError:` is constructively impossible. `EngineError` carries
|
|
27
|
+
the failing model and the engine's own message; a failing `apply` names the
|
|
28
|
+
model, quotes DuckDB/Postgres verbatim, and shows the SQL context. The CLI
|
|
29
|
+
now renders one failure screen (cause first, an actionable hint where one
|
|
30
|
+
exists) and prints the Effect fiber trace only under `--log-level debug`.
|
|
31
|
+
Exit codes (0/1/2) and `--json` shapes are unchanged. New exported error
|
|
32
|
+
`UnknownModelError` (render/lineage against a name not in the project).
|
|
33
|
+
- The entire user-facing surface is English: CLI output and help, error
|
|
34
|
+
messages, `--json` string values (key names and exit codes unchanged),
|
|
35
|
+
the `init` scaffold, the hospital example data (#11). Source comments
|
|
36
|
+
and test names too (#12).
|
|
37
|
+
- **Breaking:** the `apply` confirmation prompt accepts only `y`/`yes`
|
|
38
|
+
(case-insensitive); the Cyrillic tokens are no longer recognized.
|
|
39
|
+
- `efmesh init` scaffold now teaches the core lifecycle: a seed feeding
|
|
40
|
+
an incremental-by-time-range model with a blocking audit and a full
|
|
41
|
+
rollup on top, runnable immediately after `init` (#11).
|
|
42
|
+
|
|
8
43
|
## [0.2.0] — 2026-07-16
|
|
9
44
|
|
|
10
45
|
Theme: "operator and team" — efmesh in the hands of a non-author: the
|
package/README.md
CHANGED
|
@@ -211,6 +211,28 @@ changes await a human.
|
|
|
211
211
|
|
|
212
212
|
Exit codes: `0` — success, `1` — error, `2` — "awaiting a human": the plan needs confirmation in a non-TTY (add `--yes`), or `run` hit unapplied changes. In a non-TTY, `apply` with changes and no `--yes` refuses — efmesh will not silently roll out a plan nobody has seen.
|
|
213
213
|
|
|
214
|
+
## Logging
|
|
215
|
+
|
|
216
|
+
`apply` and `run` narrate what they do. Logs go to **stderr** — stdout stays
|
|
217
|
+
reserved for the plan screen, summaries and `--json`, which stays byte-clean.
|
|
218
|
+
Levels, set by the built-in `--log-level` flag (minimum level, default `info`):
|
|
219
|
+
|
|
220
|
+
- **info** — lifecycle a human watches: per-model build start/finish with
|
|
221
|
+
duration, backfill batch progress (`batch 3 of 7` with the interval bounds),
|
|
222
|
+
promotion.
|
|
223
|
+
- **warn** — warn-audits (violations that do not block) and retries.
|
|
224
|
+
- **debug** — the rendered SQL about to run, lock acquire/release, and other
|
|
225
|
+
internals. `--log-level debug` also prints the full fiber trace on a failure.
|
|
226
|
+
|
|
227
|
+
Each line carries structured fields as annotations (`model`, `env`, `interval`,
|
|
228
|
+
…). At a TTY the output is pretty and colored; piped to a file or the systemd
|
|
229
|
+
journal it is one-line [logfmt](https://brandur.org/logfmt) with no ANSI, so a
|
|
230
|
+
log reader (or an AI agent post-morteming a 3am tick) can group by field.
|
|
231
|
+
|
|
232
|
+
Embedding efmesh as a library? Logging is Effect's `Effect.log*` — provide your
|
|
233
|
+
own `Logger` layer (sink, format, minimum level) and the CLI's choices do not
|
|
234
|
+
apply. Row counts are not logged: efmesh never runs an extra query just to count.
|
|
235
|
+
|
|
214
236
|
## Performance
|
|
215
237
|
|
|
216
238
|
The framework overhead is negligible for any realistic project (in-memory DuckDB, `bun bench/plan-bench.ts N`):
|
package/README.ru.md
CHANGED
|
@@ -218,6 +218,29 @@ Exit-коды: `0` — успех, `1` — ошибка, `2` — «ждёт че
|
|
|
218
218
|
изменения. В не-TTY `apply` с изменениями БЕЗ `--yes` не применяется —
|
|
219
219
|
молча катить план, который никто не видел, efmesh отказывается.
|
|
220
220
|
|
|
221
|
+
## Логирование
|
|
222
|
+
|
|
223
|
+
`apply` и `run` рассказывают, что делают. Логи идут в **stderr** — stdout
|
|
224
|
+
остаётся под план, сводки и `--json`, который остаётся байт-чистым JSON.
|
|
225
|
+
Уровни задаёт встроенный флаг `--log-level` (минимальный уровень, по
|
|
226
|
+
умолчанию `info`):
|
|
227
|
+
|
|
228
|
+
- **info** — жизненный цикл, за которым следит человек: старт/финиш сборки
|
|
229
|
+
каждой модели с длительностью, прогресс бэкфила (`batch 3 of 7` с границами
|
|
230
|
+
интервала), промоушен.
|
|
231
|
+
- **warn** — warn-аудиты (нарушения, которые не блокируют) и ретраи.
|
|
232
|
+
- **debug** — рендер SQL перед выполнением, взятие/освобождение лока и прочие
|
|
233
|
+
внутренности. `--log-level debug` также печатает полный fiber-trace при сбое.
|
|
234
|
+
|
|
235
|
+
В каждой строке структурные поля-аннотации (`model`, `env`, `interval`, …). В
|
|
236
|
+
TTY вывод красивый и цветной; в файл или systemd journal — однострочный
|
|
237
|
+
[logfmt](https://brandur.org/logfmt) без ANSI, чтобы читатель логов (или
|
|
238
|
+
ИИ-агент, разбирающий трёхчасовой ночной тик) группировал по полям.
|
|
239
|
+
|
|
240
|
+
Встраиваете efmesh как библиотеку? Логирование — это `Effect.log*`: дайте свой
|
|
241
|
+
`Logger`-слой (приёмник, формат, минимальный уровень), и выбор CLI не действует.
|
|
242
|
+
Число строк не логируется: efmesh не делает лишний запрос ради подсчёта.
|
|
243
|
+
|
|
221
244
|
## Перфоманс
|
|
222
245
|
|
|
223
246
|
Оверхед фреймворка пренебрежим для любого реалистичного проекта (in-memory DuckDB, `bun bench/plan-bench.ts N`):
|
package/SPEC.md
CHANGED
|
@@ -220,7 +220,7 @@ on encountering a snapshot of another version, honestly stops
|
|
|
220
220
|
how many intervals will be recomputed, a textual SQL diff.
|
|
221
221
|
6. `efmesh apply` shows the same plan and applies it: creates physical
|
|
222
222
|
tables, runs the backfill, swaps the views. *Implemented in F4: in a TTY the plan
|
|
223
|
-
is applied only after an explicit "y" (y/yes
|
|
223
|
+
is applied only after an explicit "y" (y/yes, case-insensitive; `--yes`/`-y` skips the
|
|
224
224
|
question), exactly the shown plan is applied without recomputation; non-TTY (CI,
|
|
225
225
|
pipes) proceeds without asking.*
|
|
226
226
|
|
package/package.json
CHANGED
package/src/bin.ts
CHANGED
|
@@ -1,12 +1,49 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { BunRuntime, BunServices } from "@effect/platform-bun"
|
|
3
|
-
import { Effect } from "effect"
|
|
3
|
+
import { Cause, Effect, Logger } from "effect"
|
|
4
4
|
import { Command } from "effect/unstable/cli"
|
|
5
|
-
import { rootCommand } from "./cli.ts"
|
|
5
|
+
import { renderFailure, rootCommand, wantsTrace } from "./cli.ts"
|
|
6
6
|
import { version } from "../package.json"
|
|
7
7
|
|
|
8
|
+
// Logging (#14) goes to STDERR so `--json` stdout stays byte-clean — a contract
|
|
9
|
+
// for CI and agents. A human at a stderr TTY gets colored pretty output; a pipe,
|
|
10
|
+
// log file or the systemd journal gets plain one-line logfmt with no ANSI, which
|
|
11
|
+
// the fields (model=, env=, interval=) make machine-readable. The minimum level
|
|
12
|
+
// is Info by default (lifecycle visible during apply/run); the CLI's built-in
|
|
13
|
+
// `--log-level` flag lowers it to debug (SQL, lock internals) or raises it. An
|
|
14
|
+
// embedder wanting different sinks/levels provides its own Logger layer instead.
|
|
15
|
+
const loggerLayer = Logger.layer([
|
|
16
|
+
process.stderr.isTTY === true
|
|
17
|
+
? Logger.consolePretty({ stderr: true })
|
|
18
|
+
: Logger.withConsoleError(Logger.formatLogFmt),
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
// One central failure screen (#13): catch every cause here and render it
|
|
22
|
+
// ourselves (cause first, one screen, fiber trace only under --log-level
|
|
23
|
+
// debug) instead of letting runMain dump a pretty cause over an empty message.
|
|
24
|
+
// Interrupts (Ctrl+C) are not failures — leave them to the default teardown.
|
|
25
|
+
// The exit code stays a frozen contract: a rendered failure is 1, unless a
|
|
26
|
+
// command already claimed 2 ("awaiting a human") before failing.
|
|
27
|
+
const debug = wantsTrace(process.argv)
|
|
28
|
+
|
|
29
|
+
// Interrupt with nothing failed/died — Ctrl+C during a clean wait: not a
|
|
30
|
+
// failure to render, hand it back to the default teardown.
|
|
31
|
+
const isInterruptOnly = (cause: Cause.Cause<unknown>): boolean =>
|
|
32
|
+
!Cause.hasFails(cause) && Cause.interruptors(cause).size > 0
|
|
33
|
+
|
|
8
34
|
rootCommand.pipe(
|
|
9
35
|
Command.run({ version }),
|
|
36
|
+
Effect.catchCause((cause) =>
|
|
37
|
+
isInterruptOnly(cause)
|
|
38
|
+
? Effect.failCause(cause)
|
|
39
|
+
: Effect.sync(() => {
|
|
40
|
+
process.stderr.write(`${renderFailure(cause, { debug })}\n`)
|
|
41
|
+
if (process.exitCode === undefined || process.exitCode === 0) {
|
|
42
|
+
process.exitCode = 1
|
|
43
|
+
}
|
|
44
|
+
}),
|
|
45
|
+
),
|
|
46
|
+
Effect.provide(loggerLayer),
|
|
10
47
|
Effect.provide(BunServices.layer),
|
|
11
48
|
BunRuntime.runMain,
|
|
12
49
|
)
|