Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NpgsqlRest/NpgsqlRest
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: e036f3d
Choose a base ref
...
head repository: NpgsqlRest/NpgsqlRest
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 7573f8f
Choose a head ref
  • 20 commits
  • 70 files changed
  • 1 contributor

Commits on Jun 23, 2026

  1. feat(proxy): forward all automatic parameters to proxy upstream consi…

    …stently (v3.18.1)
    
    When an endpoint is a proxy, all server-filled parameters now forward to
    the upstream through one unified path: user claims, IP address, HTTP
    Custom Type fields, and resolved-parameter expressions.
    
    Placement mirrors the endpoint's own signature, NOT the HTTP verb:
    - a @body_parameter_name param carries the raw request body;
    - otherwise RequestParamType decides — QueryString appends to the proxy
      query string, BodyJson merges into the proxy JSON body (typed:
      numbers / booleans / embedded json / strings) when the method carries
      a body.
    
    Additive: the verbatim incoming request is still forwarded; the
    automatic params are added on top so the upstream receives the same
    parameter set the routine would.
    
    The previous claim/IP-only query append in BuildTargetUrl is removed and
    folded into the unified mechanism. Behavior change: claims/IP now follow
    RequestParamType (unchanged for GET/QueryString — still query; for
    BodyJson endpoints they now go to the body).
    
    Notes: body merge only for JSON content types (multipart/non-JSON
    forwarded verbatim); only expanded per-field HTTP-type params forwarded.
    
    Tests cover GET-query, POST-body (typed), param_type-query on POST, and
    resolved-param forwarding; existing claim/IP proxy tests pass unchanged.
    Full suite green (2288). See changelog/v3.18.1.md.
    
    Also adds a Claude Code skill bundle (.claude/skills/npgsqlrest/) — a usage
    guide plus full annotation and configuration references generated from
    --annotations/--config — and a README section on installing it.
    vbilopav committed Jun 23, 2026
    Configuration menu
    Copy the full SHA
    c74a4b5 View commit details
    Browse the repository at this point in the history

Commits on Jun 26, 2026

  1. fix(proxy): cap oversized forwarded query params; match @body_paramet…

    …er_name by any param name
    
    Two fixes for proxy endpoints that forward auto-filled parameters (v3.18.1),
    surfaced by combining @Proxy with an HTTP Custom Type parameter.
    
    - ProxyOptions.MaxForwardedQueryParamLength (default 2048): a server-filled
      value longer than the limit is skipped with a warning instead of being
      percent-encoded into the upstream query string, which overflowed the request
      line (HTTP 414/431 / connection reset). Wired through Builder, ConfigDefaults,
      ConfigSchemaGenerator, ConfigTemplate, and appsettings.json.
    
    - @body_parameter_name now matches case-insensitively and accepts any of a
      parameter's names. For an HTTP Custom Type field expanded from a composite,
      the converted name (responseBody), the expanded signature name
      (_response_body, via the new NpgsqlRestParameter.ExpandedName alias), and the
      shared base name (_response) all resolve. ActualName is unchanged so the
      fields still reassemble into the single composite SQL argument.
    
    Tests: ProxyHttpTypeProbeTest adds converted-name redirect, expanded-name
    redirect, and oversized-field-skipped cases. Full suite green (2291).
    
    See changelog/v3.18.2.md. version.txt / npm not bumped yet.
    vbilopav committed Jun 26, 2026
    Configuration menu
    Copy the full SHA
    62ef5cf View commit details
    Browse the repository at this point in the history
  2. fix(tsclient): correct generated client for @body_parameter_name endp…

    …oints
    
    The TypeScript client generator was broken for any endpoint using
    @body_parameter_name:
    
    - It emitted "body: request.<name>?" — the TS optional "?" suffix (added for
      parameters with a default or a custom type) leaked into the runtime property
      name, a syntax error. The query-exclusion key was likewise ["<name>?"], so the
      body parameter was not stripped from the query string. Now the bare converted
      name is used for both the body expression and the exclusion key.
    
    - It emitted a fetch body even for GET, which fetch forbids. A body is now only
      emitted for methods that can carry one (not GET); a GET body parameter (e.g. a
      server-filled HTTP Custom Type field forwarded by a proxy POST) is simply
      excluded from the query and not sent.
    
    - It matched @body_parameter_name only against the converted and actual names,
      so the expanded HTTP-type field name (e.g. _response_body) was ignored — the
      parameter leaked into the query and no body was emitted, even though the server
      resolved it. The generator now also matches the ExpandedName alias, consistent
      with the server-side body-parameter resolution.
    
    Tests: NpgsqlRestTests/TsClientTests/BodyParamGetTests.cs covers a GET endpoint
    (converted name) and a POST HTTP-Custom-Type endpoint targeted by the expanded
    name _response_body. Full suite green (2293).
    
    See changelog/v3.18.2.md. version.txt / npm not bumped yet.
    vbilopav committed Jun 26, 2026
    Configuration menu
    Copy the full SHA
    673624c View commit details
    Browse the repository at this point in the history
  3. feat(codegen): OmitAutomaticParameters option to drop server-filled p…

    …arams from generated requests
    
    New opt-in option (default false) on TsClientOptions, HttpFileOptions, and
    OpenApiOptions. When enabled, parameters that are filled by the server and
    cannot be set by the client are omitted from the generated request shape:
    the TypeScript request interface, the .http query string / JSON body, and the
    OpenAPI query parameters / request body.
    
    A parameter is omitted when it is automatic AND optional. The shared rule lives
    on the core endpoint as RoutineEndpoint.OmitParameterFromGeneratedRequest, built
    on RoutineEndpoint.IsAutomaticParameter, so all three generators stay consistent:
    
    - always automatic: HTTP Custom Type fields, resolved-parameter expressions,
      upload-metadata parameters;
    - automatic only when the endpoint uses user parameters: IP-address and
      user-claim parameters;
    - optional guard: HasDefault or a composite/HTTP-type field, so omission can
      never make a required argument un-sendable.
    
    When every parameter is omitted the request collapses cleanly: a no-argument
    TypeScript function, a bare .http URL, and no OpenAPI parameters/requestBody.
    
    Default is false to keep generated output byte-stable on upgrade; each generator
    opts in independently. The option is wired through the client config for all
    three plugins (binding, ConfigDefaults, schema, template, appsettings.json).
    
    Tests: omission coverage for each generator (TsClientTests/HttpFilesTests/
    OpenApiTests OmitAutomaticParamsTests), reusing the tsclient_test.bodyparam_*
    HTTP-type fixtures with separate omit-enabled generator configs. Full suite
    green (2299).
    
    See changelog/v3.18.2.md. version.txt / npm not bumped yet.
    vbilopav committed Jun 26, 2026
    Configuration menu
    Copy the full SHA
    2482ede View commit details
    Browse the repository at this point in the history
  4. fix(codegen): @body_parameter_name resolves in HTTP files and OpenAPI…

    …; unify body-param matching
    
    The HTTP file and OpenAPI generators matched the @body_parameter_name parameter
    with a case-sensitive Ordinal comparison against only the converted and actual
    names. An HTTP Custom Type field targeted by its expanded name (e.g.
    _response_body) was therefore not recognized as the body parameter: it stayed on
    the query string instead of being moved into the request body. (The TypeScript
    client and the server were already fixed; these two generators had drifted.)
    
    Body-parameter resolution is now a single shared rule, RoutineEndpoint.
    IsBodyParameter(param) — case-insensitive, matching the converted, actual, or
    expanded per-field name. Every call site routes through it: the proxy forwarder,
    the request-handling body branch, and the TypeScript, HTTP file, and OpenAPI
    generators. No more re-inlined three-name comparisons to drift.
    
    Tests: HttpFilesTests/BodyParamToBodyTests and OpenApiTests/BodyParamToBodyTests
    assert that an expanded-name body parameter is emitted as the request body (not
    the query) in both generators. Full suite green (2301).
    
    See changelog/v3.18.2.md. version.txt / npm not bumped yet.
    vbilopav committed Jun 26, 2026
    Configuration menu
    Copy the full SHA
    185698f View commit details
    Browse the repository at this point in the history
  5. bump version 3.18.2

    vbilopav committed Jun 26, 2026
    Configuration menu
    Copy the full SHA
    b82f9a2 View commit details
    Browse the repository at this point in the history

Commits on Jun 30, 2026

  1. feat: SQL test runner (--test) [WIP], SqlFileSource SkipPattern, Mini…

    …malLevels Off
    
    SQL test runner (--test) — WIP, not yet in changelog (still stabilizing):
    - Discovers *.test.sql, runs each on its own non-pooled connection, invokes
      endpoints in-process on the test's own connection/transaction, asserts via
      boolean SELECTs / DO blocks / `# @expect-status`. Captures responses into
      per-block pg_temp tables.
    - Per-assertion reporting (each boolean SELECT / DO block / expect-status HTTP
      step is one test); console grouped per file, JUnit one <testcase> per assertion.
    - Dedicated `NpgsqlRestTest` log channel (configurable TestRunner.LoggerName,
      leveled independently via Log:MinimalLevels): discovery/parsing at Debug,
      queries + HTTP calls at Verbose, notices by severity. Console report stays
      separate.
    - Core (additive, inert when unused): NpgsqlRestOptions.AmbientConnectionAccessor
      + connection-selection branch; RoutineInvokeResult.Headers; endpoint
      notice-handler leak fix (named delegate detached in finally);
      NpgsqlRestLogger.LogConnectionNotice ILogger overload.
    - 33 parser unit tests (NpgsqlRestTests/TestRunnerTests/ParserTests/).
    
    Shippable in 3.19.0 (changelog/v3.19.0.md):
    - SqlFileSource.SkipPattern (default *.test.sql) — exclude files from endpoint
      discovery; included iff matches FilePattern AND not SkipPattern.
    - Log:MinimalLevels accepts "Off"/"None"/"Silent" to fully mute a logger
      (minimum level above Fatal); null/omitted still falls back to the default.
    
    Config synced across appsettings.json, ConfigTemplate.cs, ConfigDefaults.cs,
    ConfigSchemaGenerator.cs. Full suite green (2334) + 33 parser + 94 config tests.
    vbilopav committed Jun 30, 2026
    Configuration menu
    Copy the full SHA
    6a0f03f View commit details
    Browse the repository at this point in the history

Commits on Jul 2, 2026

  1. feat(test-runner): test database support, per-assertion hardening, ch…

    …angelog
    
    Test database / separate test connection:
    - TestRunner.ConnectionName: run tests (endpoint Describe + execution) against
      a named ConnectionStrings entry instead of the app's main connection; not
      opened/validated at startup, so a Setup step can create the database first.
    - Per-step ConnectionName on Setup/Teardown Sql/SqlFile steps (e.g. an Admin
      maintenance connection running `create database`); runner issues no DDL of
      its own. Steps now run in the exact order written (dropped Command-first
      grouping). BuildTestRunnerConnectionStrings resolves entries unvalidated.
    - {rnd1}..{rnd10} config placeholders: random lowercase tokens (length =
      digit), generated once per run, substituted everywhere {ENV} works — one
      stable name across connection string, create, and drop.
    
    Hardening round (full-feature review):
    - `# @expect-status` removed — assert on the response temp table's status
      column instead; HTTP blocks are pure act steps.
    - Code generation (HTTP files, TS client, OpenAPI) skipped in --test mode —
      a test run never rewrites generated artifacts.
    - login/logout endpoints rejected in test mode with a clear error (inject
      the principal with `# @claim` instead).
    - Request matching no endpoint logs a warning (path typo / missing /api
      prefix) — the 404 remains assertable.
    - Per-step exception attribution: errors thrown by HTTP steps (e.g. 42P07
      duplicate `# @response` table) report with the block's file:line.
    
    Rename: TestRunner.Verbose -> DetailedReport (detailed console report:
    passed ✓ lines, full failing SQL, notices for passing tests) — removes the
    collision with the Serilog "Verbose" level in Log:MinimalLevels.
    
    changelog/v3.19.0.md rewritten: SQL test runner documented as the headline
    feature with a full user manual (execution model, file anatomy, HTTP-block
    syntax, response temp tables, Setup/Teardown, test-database workflow,
    reporting, logging, config reference), plus SqlFileSource.SkipPattern and
    MinimalLevels "Off".
    
    Config synced across appsettings.json, ConfigTemplate.cs, ConfigDefaults.cs,
    ConfigSchemaGenerator.cs. Full suite green (2334); examples 19 + 20 verified
    end-to-end (fresh {rnd}-named test DB created, migrated, tested, dropped).
    vbilopav committed Jul 2, 2026
    Configuration menu
    Copy the full SHA
    0a20ec9 View commit details
    Browse the repository at this point in the history
  2. feat(test-runner): script includes, named steps, per-file setup/teard…

    …own/connection
    
    Script reuse — \i / \ir includes (psql syntax, paste semantics):
    - `\i path` (cwd-relative) / `\ir path` (relative to the including file) on
      its own line splices the included file's content in place, as if pasted:
      SQL statements, assertions (attributed to the included file), HTTP blocks
      (participate in _response_{n} numbering), and header annotations (a shared
      annotation "profile" attaches with one include line).
    - Recognized only between statements (never inside strings/dollar-quotes;
      no statement fragments — use functions/views). Nested, cycle-safe,
      depth-capped. Works in Setup/Teardown SqlFile steps. Errors report the
      included file's name and line. Quoted paths and a trailing ';' accepted;
      other backslash lines pass through to PostgreSQL untouched.
    
    Named steps (TestRunner.Steps):
    - name -> step dictionary (same idiom as ConnectionStrings/Profiles);
      Setup/Teardown arrays mix name references and inline step objects;
      unknown name is a configuration error (exit 3). Step logs show
      phase + name + resolved code: `setup step CreateTemplate: create
      database app_template_abc`.
    
    Per-file header annotations (leading -- comments, before the first statement):
    - `-- @setup Name [Name ...]`  runs named steps before the file
    - `-- @teardown Name [Name ...]` runs after the file — always, best-effort,
      after the file's connection closes (so `drop database ... with (force)`
      works), on the run token (survives per-test timeout)
    - `-- @connection Name` runs the file, including its in-process endpoint
      calls, on a named ConnectionStrings entry
    - Repeatable; names whitespace- or comma-separated; accumulate and execute
      in written order (teardown not reversed); setup fail-fast, teardown
      best-effort. Together these enable per-test database isolation: clone a
      template, run in the clone, drop it (deterministic sequence ids).
    
    {rnd} indexed instances:
    - {rndN_1}..{rndN_9} are independent random tokens per length (all stable
      per run) for when several distinct same-length names are needed.
    
    Changelog updated (paste-model includes, named steps, per-file annotations,
    indexed tokens, example 21). Config synced across appsettings.json,
    ConfigTemplate.cs, ConfigDefaults.cs, ConfigSchemaGenerator.cs. Full suite
    green (2351) incl. 50 parser tests; examples 19/20/21 verified end-to-end
    (21: two parallel per-test clones from one template, shared annotation
    profile via \ir, Command step with {rnd} substitution).
    vbilopav committed Jul 2, 2026
    Configuration menu
    Copy the full SHA
    58ae087 View commit details
    Browse the repository at this point in the history
  3. feat(test-runner): filtering, watch mode, tags, endpoint coverage (Ph…

    …ase 3)
    
    Test filtering (TestRunner.Filter):
    - Narrow the discovered set: a value without wildcards is a case-insensitive
      substring match against the cwd-relative path (--testrunner:filter=login);
      with wildcards it uses the same glob engine as FilePattern. Setup/Teardown
      still run; zero matches exits 4 (AllowEmpty applies).
    
    Watch mode (--watch / TestRunner.Watch, interactive/dev-only):
    - Setup once, run once, then watch the FilePattern base directory recursively
      for *.sql changes (debounced): a changed test file re-runs alone (Filter
      applies); any other changed .sql (an included fixture/profile) re-runs
      everything. Endpoint sources are not watched — restart to rebuild.
    - Teardown runs ONCE, on exit: SIGINT and SIGTERM are intercepted via
      PosixSignalRegistration (Console.CancelKeyPress proved unreliable headless;
      plain SIGTERM previously killed the process without teardown, leaking the
      test database). Graceful stop exits 0 — not for CI gating.
    - Extracted ExecuteDiscoveredFilesAsync (run core without teardown); RunAsync
      keeps teardown-in-finally for CI mode.
    
    Tags (-- @tag name [name ...] header annotation):
    - TestRunner.Tag runs only files carrying at least one listed tag;
      TestRunner.ExcludeTag skips files carrying any (exclude wins). CSV or
      whitespace-separated, case-insensitive, composes with Filter. Tags travel
      through profile includes (as-if-pasted), so e.g. every clone-isolated test
      is automatically tagged slow by its profile.
    
    Endpoint coverage (TestRunner.Coverage / CoverageThreshold):
    - After the run: exercised N of M testable endpoints + the list of untested
      ones (kinds the runner rejects — SSE, upload, login/logout, outbound proxy —
      are excluded from the ratio and counted separately). CoverageThreshold
      (0-100, implies Coverage) fails an otherwise-passing run with exit 2 —
      CI gating for "every endpoint has a test". Covered = invoked at least once.
    
    Config synced across appsettings.json, ConfigTemplate.cs, ConfigDefaults.cs,
    ConfigSchemaGenerator.cs; --watch whitelisted; --help updated. Changelog
    v3.19.0.md updated with all four features. Full suite green (2364) incl. 63
    runner unit tests; examples 19/20/21 verified end-to-end (filter modes, watch
    rerun-on-change + signal teardown, tag taxonomies incl. profile-carried tags,
    coverage report + failing threshold gate).
    vbilopav committed Jul 2, 2026
    Configuration menu
    Copy the full SHA
    bd4fed5 View commit details
    Browse the repository at this point in the history
  4. feat(test-runner): watch rebuilds endpoints, guaranteed teardown, the…

    …med report
    
    Watch mode — endpoint source tree (SqlFileSource):
    - When the SQL file source is enabled, watch mode also watches its tree: a
      changed endpoint file triggers an in-process endpoint rebuild (UseNpgsqlRest
      re-reads + re-describes against the test database and atomically swaps the
      endpoint registry; a failed rebuild keeps the previous endpoints) followed
      by a full rerun. The endpoint delta is reported after each rebuild
      (+ METHOD path / - METHOD path "endpoint dropped - check its SQL file"), so
      breaking an endpoint mid-session is visible immediately and fixing it
      recovers - no restart. Watch mode forces SqlFileSource.ErrorMode from Exit
      to Skip (a broken file must not kill the session); non-watch --test keeps
      Exit for CI.
    
    Guaranteed teardown (fixes two database-leak classes):
    - From Setup onward, SIGINT (Ctrl+C) and SIGTERM are intercepted and Teardown
      runs SYNCHRONOUSLY in the signal handler - wrappers like `bun run` forward
      Ctrl+C to the process group and kill their children immediately, so the old
      cooperative unwind lost the race and leaked the test database. A second
      signal force-quits. Non-watch --test previously had no handler at all.
    - An AppDomain.ProcessExit hook covers hard exits: SqlFileSource
      ErrorMode=Exit's Environment.Exit(1) previously leaked the just-created
      test database; exit code unchanged, Teardown now runs first.
    - All paths funnel into a run-once teardown that hands every caller the SAME
      task (a fire-once no-op guard let the process exit mid-DROP).
    - An interrupted non-watch run exits 2.
    
    Console report themed to Serilog's Code theme:
    - FAIL/ERROR labels render as the byte-identical chip the Code theme uses for
      its ERR/FTL level (red-197 on grey-238); PASS uses the same chip grammar in
      the mirror green-47; line text stays in the terminal's normal color; ALL
      failure text unified on the theme's red-197 foreground (two stragglers used
      16-color ConsoleColor.Red, which renders orange in some themes).
      Out.LineChip renders chips, plain when redirected.
    
    Changelog updated (endpoint watching, teardown guarantees, report theming,
    exit codes). Full suite green (2364); verified live: benign endpoint edit ->
    rebuild + green, broken endpoint -> dropped-endpoint delta + failing tests,
    fix -> recovery; broken-endpoint hard exit -> exit 1 with ZERO leaked
    databases; watch SIGTERM -> full teardown, zero leftovers.
    vbilopav committed Jul 2, 2026
    Configuration menu
    Copy the full SHA
    45a5e81 View commit details
    Browse the repository at this point in the history
  5. fix(test-runner): one red style, one green style in the console report

    AnsiFail is now the full Serilog Code ERR chip style (red-197 on grey-238)
    and is the ONLY red in the report — FAIL/ERROR labels and every failure line
    (assertion details, messages, failing summary, coverage gate, setup/config
    errors) share the exact same escape sequence, so they can never render
    differently. AnsiOk is the mirror (green-47 on grey-238) for the PASS label
    and the all-green summary. Indentation and blank lines are written outside
    the styled block so the grey strip starts at the text.
    vbilopav committed Jul 2, 2026
    Configuration menu
    Copy the full SHA
    94667a9 View commit details
    Browse the repository at this point in the history
  6. feat(test-runner): endpoint coverage on by default for full runs (tri…

    …-state)
    
    TestRunner.Coverage is now tri-state:
    - null (the new default): report the endpoint-coverage summary after FULL
      runs — it costs one line and surfaces untested endpoints passively — but
      stay quiet when the run is narrowed by Filter/Tag (a deliberately partial
      run would just nag about endpoints its subset never touches);
    - true: always report, including narrowed runs;
    - false: never report.
    CoverageThreshold always reports and gates when set, regardless of Coverage
    or narrowing — it is an explicit ask for enforcement.
    
    Config synced (appsettings.json, ConfigTemplate.cs, ConfigDefaults.cs,
    ConfigSchemaGenerator.cs — incl. previously missing schema descriptions for
    Coverage/CoverageThreshold). Changelog updated. Full suite green (2364);
    all five behaviors verified live (default-on full run, narrowed suppression,
    forced on, forced off, threshold reporting).
    vbilopav committed Jul 2, 2026
    Configuration menu
    Copy the full SHA
    19ecbae View commit details
    Browse the repository at this point in the history
  7. feat(sql-file-source): named parameters (:name) in SQL files

    SQL file endpoints can use named placeholders instead of positional $1..$N:
    
        select * from users where email = :email and age > :age;
    
    The placeholder IS the parameter: :email becomes API parameter "email"
    through the same NameConverter routine parameters use (:user_id -> userId),
    so @param annotations that existed only to NAME positional parameters become
    unnecessary. Claim mappings (ParameterNameClaimsMapping, @user_parameters)
    hook up by the placeholder's own name with zero annotations.
    
    Mechanics: statements are rewritten to native $N right after parsing —
    PostgreSQL never sees :name, so Describe, type inference, and runtime
    behavior are identical to positional files. The same name (case-insensitive,
    first spelling wins) maps to ONE parameter file-wide, including across
    statements in multi-command files. Token-aware rewriter: strings ('', ""),
    comments (nested /* */), and dollar-quoted bodies untouched; :: casts, :=
    calls, and numeric slice bounds (a[1:3]) never match (variable slice bounds
    need a space: a[1 : n]). Mixing $N and :name in one file is rejected
    (ErrorMode semantics).
    
    Annotations match named parameters by name: `@param email default null`
    works as-is; `@param :email citext` is a Describe type hint; the new
    `@param email type is citext` form retypes WITHOUT rename (core
    ParameterHandler, delegates to the rename path as rename-to-same-name;
    also tolerates a leading ':' on the parameter reference). Typed-annotation
    detection for HTTP-client/composite types accepts the named form.
    
    `?` (JDBC style) considered and rejected: ?, ?|, ?& and @? are PostgreSQL's
    own jsonb/geometric operators — no rewriter can safely claim `?`; same
    reason the PG JDBC driver requires ?? escapes. Documented in the changelog.
    
    Tests: 22 rewriter unit tests + 7 end-to-end endpoint tests (auto-naming,
    repeated placeholder from one value, required-param matching, name-matched
    defaults, `type is` retype producing an unquoted JSON int, claim mapping by
    placeholder name, mixed-style rejection, multi-command cross-statement
    sharing). Full suite green (2393). Example 20's login.sql converted to
    :email/:password live (docs repo, committed separately). Changelog section
    + plugin README updated.
    vbilopav committed Jul 2, 2026
    Configuration menu
    Copy the full SHA
    6cd7150 View commit details
    Browse the repository at this point in the history

Commits on Jul 3, 2026

  1. feat(watch): server watch mode — --watch restarts the server on SQL/c…

    …onfig changes
    
    --watch is now a two-mode flag: with --test it is the test runner's in-process
    watch (unchanged); WITHOUT --test it runs the SERVER under a watch supervisor.
    
    Supervisor/child design (same model as dotnet watch): the process spawns
    itself as a child server (marked via NPGSQLREST_WATCH_CHILD; value = the
    supervisor PID) and watches the SqlFileSource tree plus the configuration
    files. On a debounced change it stops the child gracefully and starts a fresh
    one — the child runs the completely normal server pipeline (discovery,
    describe, code generation, Kestrel), so dev is production behavior. The one
    relaxation: SqlFileSource ErrorMode is forced Exit->Skip in the watch child so
    a broken SQL file logs its error and drops only its own endpoint.
    
    Details:
    - Edits to files matching SqlFileSource.SkipPattern (test files) do NOT
      restart the server.
    - Child crash: the supervisor prints "server exited (code N) — waiting for
      file changes" and revives on the next relevant save (no crash-looping).
    - Graceful stop: SIGTERM via libc DllImport on Unix (Process.Kill is SIGKILL);
      hard process-tree kill on Windows (a dev server has no teardown needs).
    - Parent watchdog in the child: if the supervisor dies without stopping it
      (SIGKILL, wrapper runners like bun run), the child exits by itself — no
      orphan holding the port.
    - Spawn works from the AOT binary (Environment.ProcessPath) and under
      `dotnet NpgsqlRestClient.dll` (dotnet host re-invoked with the dll).
    - DOTNET_USE_POLLING_FILE_WATCHER=1 switches to a 1s polling scan for
      environments where file events don't cross the filesystem boundary
      (Docker Desktop bind mounts).
    - --watch without --test and without an enabled SqlFileSource exits with a
      clear error (nothing to watch).
    - WatchUtils extracts the shared watch primitives (base dir, debounce,
      pattern match); the test runner delegates to it, behavior identical.
    
    Plan B (in-process endpoint hot-swap via a dynamic EndpointDataSource,
    target 3.20) is designed in scrap/WATCH_HOT_SWAP_PLAN.md.
    
    Verified live across 12 scenarios: initial serve, add/break/fix endpoint
    file (broken file 404s while the rest keeps serving), test-file edit ignored,
    config-edit restart, child crash recovery, supervisor SIGKILL -> child
    self-exit (no orphans), SIGTERM graceful stop with port freed, polling mode,
    nothing-to-watch error, and test mode unaffected. Full suite green (2393).
    Changelog: watch documented as a single two-mode feature.
    vbilopav committed Jul 3, 2026
    Configuration menu
    Copy the full SHA
    f859a4f View commit details
    Browse the repository at this point in the history
  2. feat(watch): database routine polling + one Watch config section for …

    …both flavors
    
    Watch mode now watches ALL endpoint sources in BOTH flavors. Routine-source
    endpoints (functions/procedures) have no files to watch, so watch mode polls
    the DATABASE — with perfect fidelity: RoutineSource.CreateFingerprintCommand
    runs the SAME discovery query the endpoint source uses (same Query property,
    same ten configured filter parameters — refactored into a shared command
    builder used by Read() too), wrapped in an order-independent server-side hash
    (select sum(hashtextextended(q::text,0)) from (<query>) q). If the hash
    changes, the discovered endpoints changed — by definition: create/replace/
    drop/alter of functions and procedures (function bodies are in the result via
    pg_get_functiondef), GRANT/REVOKE, COMMENT ON (annotations), and changes to
    the composite types and tables used as parameter/return types. Anything the
    discovery query does not read — unrelated tables, temp objects, data — can
    never cause a spurious trigger.
    
    - WatchDbPoller (client): polls on a dedicated non-pooled connection, takes
      the actual configured RoutineSource instances from EndpointSources; skips
      cleanly when there are none. Rebaseline() prevents self-triggering.
    - Server watch: the child polls and exits with code 64 -> the supervisor
      respawns immediately ("database change detected — restarting"). Supervisor
      loop reworked so the pending change batch survives respawn iterations (no
      competing channel readers). Routines-only projects are now watchable:
      --watch is valid without a SQL file source when polling is active.
    - Test watch: the poller feeds a sentinel into the existing change channel ->
      endpoint rebuild + full rerun ("change detected (database)"); the runner
      re-baselines after every rerun so committed fixtures never re-trigger.
    
    Config consolidated into ONE top-level Watch section (TestRunner.Watch
    REMOVED — it was the same word with a different type and role):
    
        "Watch": { "Enabled": false, "DatabasePollingInterval": "2s" }
    
    --watch is the CLI shorthand for Watch:Enabled (--watch:enabled=true also
    works); the flavor is picked by --test. Polling accepts "2s"/"500ms"/"1m"/
    seconds/hh:mm:ss, 0 disables, and deactivates automatically when
    RoutineOptions.Enabled is false. Config synced x4 + schema descriptions.
    
    Tests: WatchDbFingerprintTests proves the hash tracks the discovery result
    exactly (create/replace/comment/drop and used-type changes fire; temp objects
    and unrelated tables never do; nameSimilarTo filter wiring verified). Full
    suite green (2394). Live-verified: function created in psql serving ~2s
    later on a routines-only watch; replace -> new body; drop -> 404; alter table
    add column reshaping a `returns setof` endpoint ([{"id":1}] ->
    [{"id":1,"name":"ada"}]); unrelated table create/drop -> zero restarts;
    test-watch database rerun without loops; all four activation paths
    (server/test x flag/config). Changelog: watch section finalized.
    vbilopav committed Jul 3, 2026
    Configuration menu
    Copy the full SHA
    ba18c74 View commit details
    Browse the repository at this point in the history
  3. feat(test-runner): ResponseTempTable.DebugTable — permanent response …

    …debug mirror
    
    Captured responses live in per-connection temp tables that vanish with the
    test's rollback and connection close — impossible to examine in a query
    editor afterwards, and re-issuing the request from an .http file cannot
    reproduce a response that depended on the test's uncommitted transactional
    fixtures.
    
    New setting ResponseTempTable.DebugTable (default null = off): when set
    (e.g. "_responses_debug"), every captured response is ALSO mirrored into a
    PERMANENT table with that name, written on a separate autocommit pooled
    connection — immune to rollbacks and connection closes, append-only so
    parallel test files cannot collide. The temp-table semantics (assertions,
    naming, transactions) are completely unchanged.
    
    ONE table covers the whole run: each HTTP block adds one row with
    captured_at, test_file, block (that block's response-table name — Name, a
    MultiNamePattern ordinal, or the # @response name), method, path, status,
    body, content_type, headers, is_success. Recreated at the start of every
    run, so it always holds the LAST run. Enabling it prints a loud warning
    (debugging aid — do not enable in CI); mirror inserts are best-effort (a
    failure logs a warning, never fails the test). In the fresh-test-database
    workflow combine with Keep, or teardown drops the database and mirror.
    
    Note: the naive alternative ("Permanent": true on the response table) was
    rejected — parallel tests would collide on one permanent name, and CREATE
    TABLE is transactional, so the rollback would erase the table exactly when
    it was needed.
    
    Config synced (appsettings.json, ConfigTemplate.cs, ConfigDefaults.cs,
    ConfigSchemaGenerator.cs). Full suite green (2394). Live-verified on
    example 19: all three block-naming schemes distinguishable in one query,
    including the response body of a rolled-back fixture; rerun leaves exactly
    the last run's rows. Changelog + docs updated.
    vbilopav committed Jul 3, 2026
    Configuration menu
    Copy the full SHA
    76f4f56 View commit details
    Browse the repository at this point in the history
  4. fix(config): sync ConfigTemplate with the trimmed DebugTable comment …

    …in appsettings.json
    
    The DebugTable comment block was removed from appsettings.json (leaving a
    stray whitespace-only line) but ConfigTemplate.cs still carried it, so the
    Config_OutputMatchesAppsettingsJson byte-match test failed on CI.
    appsettings.json is the source of truth: the stray line is removed and the
    template now matches it exactly. The setting stays documented in the JSON
    schema description, the changelog, and the docs site.
    vbilopav committed Jul 3, 2026
    Configuration menu
    Copy the full SHA
    0ef7a2f View commit details
    Browse the repository at this point in the history
  5. feat(test-runner): Steps.Enabled + shipped disabled example steps; "m…

    …s" interval support
    
    Every Setup/Teardown step now has an "Enabled" flag (default true). A
    disabled step is simply IGNORED wherever it is referenced — Setup/Teardown
    arrays or per-file -- @setup / -- @teardown annotations — skipped with a
    debug log line, never an error. Referencing an UNKNOWN name remains a loud
    configuration error; Enabled=false is the one sanctioned skip.
    
    The default configuration ships six DISABLED example steps in
    TestRunner:Steps showing every step property (Sql, SqlFile, Command,
    WorkingDirectory, ConnectionName, Enabled) across the typical scenarios:
    CreateTestDatabase / DropTestDatabase ({rnd5}-named database on an "Admin"
    connection), ApplySchema (SqlFile), RunMigrationTool (Command +
    WorkingDirectory), StartDockerPostgres / StopDockerPostgres. Instead of
    typing a step from scratch, copy one, adjust names/paths/connections, and
    flip Enabled to true. (Defaults are a copy-from reference visible in
    appsettings.json and --config output — not a runtime layer merged into
    explicit config files.)
    
    Also from the config-comment review:
    - ParseTestTimeout gained "ms" support ("500ms") — the Watch comments and
      changelog already promised it but the parser silently fell back to the
      default (verified: --watch:databasepollinginterval=500ms -> poll 0.5s).
    - ResponseTempTable section comment carries a compact one-line DebugTable
      description; FilePattern comment names both test layouts.
    
    Config synced x4 + schema (byte-match green). Full suite green (2394).
    Live-verified: disabled-referenced step skipped with debug line + run green;
    flipped on via CLI override -> step executes; custom user step names produce
    no validation warnings (example 20 clean). Changelog + docs updated.
    vbilopav committed Jul 3, 2026
    Configuration menu
    Copy the full SHA
    536b122 View commit details
    Browse the repository at this point in the history

Commits on Jul 4, 2026

  1. Configuration menu
    Copy the full SHA
    7573f8f View commit details
    Browse the repository at this point in the history
Loading