tag:github.com,2008:https://github.com/lowdefy/lowdefy/releasesRelease notes from lowdefy2026-03-19T09:35:28Ztag:github.com,2008:Repository/300252678/v4.7.12026-03-19T09:51:09Zv4.7.1<h2>Highlights</h2>
<ul>
<li>
<p><strong>Dev server</strong>: Icons referenced in page blocks are now dynamically discovered and served without requiring a rebuild or server restart.</p>
</li>
<li>
<p><strong>Date picker e2e testing</strong>: All date picker blocks now support <code>do.select()</code> for calendar interaction and <code>do.fill()</code> for typing dates directly. DateTimeSelector also supports time selection.</p>
</li>
<li>
<p><strong>Operators</strong>: Fixed <code>_function</code> callback mutation in <code>evaluateOperators</code> that was causing <code>_build.array.map</code> and similar operators to produce duplicate results on repeated invocations.</p>
</li>
<li>
<p><strong>E2E testing</strong>: Environment variable <code>LOWDEFY_E2E_SECRET_*</code> can now override <code>LOWDEFY_SECRET_*</code> values, allowing test infrastructure (e.g., MongoMemoryServer) to coexist with secret managers.</p>
</li>
<li>
<p><strong>E2E navigation</strong>: Page navigation now waits for <code>domcontentloaded</code> instead of the full <code>load</code> event, preventing hangs on pages with WebSocket connections or slow resources.</p>
</li>
<li>
<p><strong>E2E utils</strong>: Renamed <code>MDB_E2E_URI</code> to <code>LOWDEFY_E2E_MONGODB_URI</code> in scaffold templates. Init script no longer auto-installs dependencies — they're added to <code>package.json</code> and the user is prompted to install.</p>
</li>
<li>
<p><strong>Build performance</strong>: Sibling refs are now resolved in parallel to interleave CPU and I/O operations.</p>
</li>
</ul>
<h2>Fixes & Improvements</h2>
<ul>
<li>
<p><strong>fix(build): Dev server dynamically loads icons discovered during JIT page builds.</strong> (<code>@lowdefy/build</code>, <code>@lowdefy/server-dev</code>)</p>
<p>Icons referenced only inside page blocks (e.g., <code>icon: FiAperture</code> on a Button) were not available in the dev server's static bundle, causing a fallback icon to render. The JIT page builder now detects missing icons when a page is compiled, extracts their SVG data from react-icons, and serves it via a dynamic API endpoint. The client fetches and merges these icons at runtime without triggering a Next.js rebuild or server restart.</p>
</li>
<li>
<p><strong>Resolve sibling refs in parallel using Promise.all to interleave CPU and I/O during build.</strong> (<code>@lowdefy/build</code>)</p>
</li>
<li>
<p><strong>feat(blocks-antd): Add do.select() and do.fill() to date picker e2e helpers.</strong> (<code>@lowdefy/blocks-antd</code>)</p>
<p>All five date picker e2e helpers (DateSelector, DateTimeSelector,<br>
DateRangeSelector, MonthSelector, WeekSelector) now support<br>
<code>do.select()</code> for calendar UI interaction and <code>do.fill()</code> for<br>
typing dates directly. DateTimeSelector also supports time<br>
selection via the time panel.</p>
</li>
<li>
<p><strong>Fix <code>_function</code> callback template being mutated in-place by <code>evaluateOperators</code>, causing <code>_build.array.map</code> and similar operators to produce duplicate results from repeated callback invocations.</strong> (<code>@lowdefy/operators-js</code>)</p>
</li>
<li>
<p><strong>feat(server-e2e): Add LOWDEFY<em>E2E_SECRET</em>* override support.</strong> (<code>@lowdefy/server-e2e</code>)</p>
<p>Secrets can now be overridden in e2e tests using <code>LOWDEFY_E2E_SECRET_*</code> environment variables. These take precedence over <code>LOWDEFY_SECRET_*</code> values, allowing test infrastructure (e.g. MongoMemoryServer) to coexist with secret managers injected via <code>commandPrefix</code>.</p>
</li>
<li>
<p><strong>fix(e2e-utils): Use domcontentloaded for page navigation.</strong> (<code>@lowdefy/e2e-utils</code>)</p>
<p>Page navigation now uses <code>waitUntil: 'domcontentloaded'</code> instead of the default <code>load</code> event. This prevents hangs on pages with WebSocket connections or slow-loading resources, since the Lowdefy client readiness check is already a stronger signal.</p>
</li>
<li>
<p><strong>refactor(e2e-utils): Update scaffold env vars and simplify init.</strong> (<code>@lowdefy/e2e-utils</code>)</p>
<p>Renamed <code>MDB_E2E_URI</code> to <code>LOWDEFY_E2E_MONGODB_URI</code> in scaffold templates to align with the new <code>LOWDEFY_E2E_SECRET_*</code> override pattern. The init script no longer runs install automatically — dependencies are added to <code>package.json</code> and the user is prompted to install.</p>
</li>
</ul>github-actions[bot]tag:github.com,2008:Repository/300252678/v4.7.02026-03-11T13:50:09Zv4.7.0<h2>Highlights</h2>
<ul>
<li><strong>Build Performance:</strong> Single-pass async walker replaces multi-pass JSON serialization for ref resolution, eliminating 5+ <code>serializer.copy</code> calls per ref</li>
<li><strong>Build Errors:</strong> Collect and report all ref errors at once instead of stopping on the first failure — faster fix-rebuild cycles when multiple config files have issues</li>
<li><strong>Inline Pages:</strong> Fixed inline page content being stripped during JIT builds with no recovery path</li>
<li><strong>Template Errors:</strong> YAML errors in <code>.yaml.njk</code> templates now show "Nunjucks template produced invalid YAML" instead of misleading line numbers</li>
<li><strong>CLI:</strong> Build now properly exits with <code>process.exit(1)</code> on errors instead of hanging with a spinner</li>
<li><strong>CLI Start:</strong> Fixed port availability check when no <code>--port</code> flag is passed (now defaults to port 3000)</li>
<li><strong>Build Logs:</strong> Removed spurious <code>print: warn</code> fields from build logger output</li>
<li><strong>E2E Testing:</strong> New <code>ldf.api()</code> assertions for API endpoint testing — <code>expect.toFinish()</code>, <code>expect.toHaveResponse()</code>, <code>expect.toHavePayload()</code>, <code>response()</code>, <code>state()</code></li>
<li><strong>E2E Developer Experience:</strong> New scaffold scripts (<code>e2e:headed</code>, <code>e2e:server</code>), <code>SLOW_MO</code> env var support, and fixed template defaults (<code>appDir</code>, health check, fixtures)</li>
<li><strong>E2E Block IDs:</strong> Dotted block IDs (e.g., <code>form.field.name</code>) now work correctly in CSS selectors with new <code>escapeId()</code> utility</li>
<li><strong>Helpers:</strong> Fixed <code>makeReplacer</code> mutating object marker enumerability, preventing internal markers from leaking to plugin components</li>
</ul>
<h2>What's New</h2>
<h3>feat: Single-pass async walker for ref resolution</h3>
<p>Packages: <code>@lowdefy/build</code>, <code>@lowdefy/operators</code>, <code>@lowdefy/helpers</code></p>
<p><strong>Single-Pass Walker (<code>@lowdefy/build</code>)</strong></p>
<ul>
<li>New <code>walker</code> module replaces the multi-pass JSON round-trip architecture in <code>buildRefs</code> with a single async tree walk</li>
<li>Resolves <code>_ref</code> markers, evaluates <code>_build.*</code> operators, and tags <code>~r</code> provenance in one pass instead of 5+ <code>serializer.copy</code> calls per ref</li>
<li>Wired into both <code>buildRefs</code> (production) and <code>buildPageJit</code> (dev server)</li>
<li>Added <code>isPageContentPath</code> for semantic shallow build matching, replacing brittle path-index checks</li>
<li>Deleted redundant code replaced by walker: <code>getRefsFromFile</code>, <code>populateRefs</code>, <code>createRefReviver</code>, and the <code>evaluateStaticOperators</code> wrapper</li>
</ul>
<p><strong>In-Place Operator Evaluation (<code>@lowdefy/operators</code>)</strong></p>
<ul>
<li>New <code>evaluateOperators</code> function walks a tree in-place and evaluates operator nodes, avoiding JSON serialization round-trips</li>
<li>Used by the walker module to evaluate <code>_build.*</code> operators inline during ref resolution</li>
</ul>
<p><strong>Serializer Fix (<code>@lowdefy/helpers</code>)</strong></p>
<ul>
<li>Added <code>skipMarkers</code> option to <code>serializer.serializeToString</code> to exclude internal markers (<code>~k</code>, <code>~r</code>, <code>~l</code>, <code>~arr</code>) from serialized output</li>
</ul>
<h3>feat(e2e-utils): Add ldf.api() assertions for API endpoint testing</h3>
<p>Packages: <code>@lowdefy/e2e-utils</code></p>
<ul>
<li>New <code>api.js</code> core module with <code>getApiState</code>, <code>getApiResponse</code>, <code>expectApi</code> functions</li>
<li>Reads from <code>window.lowdefy.apiResponses[endpointId][0]</code> (mirrors request pattern)</li>
<li><code>ldf.api(endpointId).expect.toFinish()</code> — wait for API call completion</li>
<li><code>ldf.api(endpointId).expect.toHaveResponse(response)</code> — assert response</li>
<li><code>ldf.api(endpointId).expect.toHavePayload(payload)</code> — assert sent payload</li>
<li><code>ldf.api(endpointId).response()</code> — get raw response value</li>
<li><code>ldf.api(endpointId).state()</code> — get full API state object</li>
<li><code>ldf.mock.api()</code> now captures payloads for assertion</li>
<li><code>ldf.mock.getCapturedApi(endpointId)</code> — retrieve captured API data</li>
</ul>
<h2>Fixes & Improvements</h2>
<ul>
<li>
<p><strong>fix(build): Report all ref errors at once instead of stopping on the first one.</strong> (<code>@lowdefy/build</code>)</p>
<p>When multiple referenced files have errors (missing files, YAML parse errors, invalid refs), the build now collects and reports all errors at once instead of stopping on the first failure. This reduces the fix-rebuild-fix cycle when multiple config files have issues.</p>
</li>
<li>
<p><strong>fix(build): Preserve inline page content in JIT builds</strong> (<code>@lowdefy/build</code>, <code>lowdefy</code>, <code>@lowdefy/docs</code>)</p>
<p>Pages declared inline in <code>lowdefy.yaml</code> (not via <code>_ref</code>) had their content stripped during shallow builds with no way to recover at JIT time, resulting in empty page shells. Detect inline pages by checking refId matches root ref with no sourceRef, and skip stripping. Set refId to null for inline pages in <code>createPageRegistry</code> so <code>buildPageJit</code> reads the pre-built artifact instead of attempting JIT resolution.</p>
</li>
<li>
<p><strong>fix(build): Improve error message for YAML errors in njk templates</strong> (<code>@lowdefy/build</code>)</p>
<p>When a .yaml.njk nunjucks template produces invalid YAML, the error now says "Nunjucks template produced invalid YAML" instead of showing a misleading line number from the generated output.</p>
</li>
<li>
<p><strong>fix(cli): Exit process and stop spinner on build errors.</strong> (<code>lowdefy</code>)</p>
<p>The CLI error handler logged errors but never called <code>process.exit(1)</code>, so the process continued running with a spinning indicator after a build failure. Added <code>process.exit(1)</code> to <code>runCommand</code> after error handling, and added <code>{ spin: 'fail' }</code> to stop the spinner in <code>runLowdefyBuild</code>, <code>runNextBuild</code>, and <code>installServer</code> catch blocks.</p>
</li>
<li>
<p><strong>fix(cli): Fix port availability check for start command</strong> (<code>lowdefy</code>, <code>@lowdefy/server-dev</code>)</p>
<p>The CLI's <code>checkPortAvailable</code> was called with <code>undefined</code> port when no <code>--port</code> flag was passed, causing <code>net.listen(undefined)</code> to bind a random port instead of checking port 3000. Added default <code>port: 3000</code> in <code>getOptions</code>. Removed redundant <code>checkPortAvailable</code> from server-dev manager since the CLI now catches port conflicts before the server starts.</p>
</li>
<li>
<p><strong>fix(e2e-utils): Escape dotted block IDs in e2e CSS selectors.</strong> (<code>@lowdefy/blocks-antd</code>, <code>@lowdefy/blocks-basic</code>, <code>@lowdefy/e2e-utils</code>)</p>
<p>Block IDs containing dots (e.g., <code>form.field.name</code>) now work correctly in e2e test locators. Added <code>escapeId()</code> utility to <code>@lowdefy/e2e-utils</code> that escapes CSS special characters, and updated all block e2e helpers and test specs to use it.</p>
</li>
<li>
<p><strong>fix(server): Remove unused print mixin from build logger</strong> (<code>@lowdefy/server</code>)</p>
<p>Removed the pino <code>mixin</code> that added a <code>print</code> field to every build log entry. This field was a leftover from a previous CLI display system and caused spurious <code>print: warn</code> lines in build output.</p>
</li>
<li>
<p><strong>feat(e2e-utils): Improved e2e scaffold with new scripts and SLOW_MO support.</strong> (<code>@lowdefy/e2e-utils</code>)</p>
<p><strong>New scaffold scripts:</strong></p>
<ul>
<li><code>e2e:headed</code> — Run tests with a visible browser in slow motion (<code>SLOW_MO=500</code>, <code>--workers=1</code>)</li>
<li><code>e2e:server</code> — Start the e2e server once, then rerun tests without rebuilding</li>
</ul>
<p><strong>SLOW_MO env var:</strong></p>
<ul>
<li><code>createConfig</code> now reads the <code>SLOW_MO</code> environment variable and passes it to Playwright's <code>launchOptions.slowMo</code></li>
<li>No manual config extension needed — just set <code>SLOW_MO=500</code> in your npm script</li>
</ul>
<p><strong>Scaffold template fixes:</strong></p>
<ul>
<li>Fixed <code>appDir</code> from <code>'../'</code> to <code>'./'</code> — <code>path.resolve</code> resolves relative to cwd, not the config file</li>
<li>Fixed <code>fixtures.js</code> template to use <code>mdbFixtures</code> (plural) from <code>/fixtures</code> subpath with <code>mergeTests</code></li>
<li>Simplified <code>example.spec.js</code> to use <code>/api/auth/session</code> health check — works on auth-protected apps</li>
<li>Fixed README template with correct <code>appDir</code> values, "Faster Test Runs" section, and "Common Patterns" section</li>
</ul>
</li>
<li>
<p><strong>fix(helpers): Prevent makeReplacer from mutating original object marker enumerability.</strong> (<code>@lowdefy/helpers</code>)</p>
<p>makeReplacer used Object.defineProperty to make ~k, ~r, ~l enumerable for JSON.stringify, but operated on the original object reference instead of a copy. This permanently mutated the original, causing internal markers to leak to plugin components via Object.entries/Object.keys.</p>
</li>
</ul>github-actions[bot]tag:github.com,2008:Repository/300252678/v4.6.02026-03-09T14:01:34Zv4.6.0<h2>Highlights</h2>
<p>Looking at the release notes file and the changelog entries you've provided, here are the developer-friendly bullet points organized by theme:</p>
<p><strong>Error Handling & Debugging</strong></p>
<ul>
<li>Config-aware error tracing shows exact YAML file:line locations with clickable VSCode links in terminal and browser</li>
<li>Unified error system in <code>@lowdefy/errors</code> package with standardized TC39 constructor signatures (<code>new MyError(message, { cause, ...options })</code>)</li>
<li>Build-time validation provides "Did you mean?" suggestions for typos and catches NEXTAUTH_SECRET configuration issues</li>
<li>Plugin errors simplified — operators, actions, and connections throw plain messages; interface layer adds context (received value, location)</li>
<li>Sentry integration (zero-config: just set <code>SENTRY_DSN</code>) captures errors on client and server with Lowdefy context (pageId, blockId, config location)</li>
<li>New <code>UserError</code> class for expected user-facing errors (validation, intentional throws) — logs to browser console only, never to server terminal</li>
</ul>
<p><strong>Build & Performance</strong></p>
<ul>
<li>JIT page building for dev server — pages build on-demand when requested instead of all at once, speeding up development</li>
<li>Shallow <code>_ref</code> resolution leaves on-demand markers for faster initial builds with file dependency tracking for targeted rebuilds</li>
<li>New <code>~ignoreBuildChecks</code> property suppresses specific build validation errors/warnings, with cascade support to suppress entire page subtrees</li>
<li>Build now collects all errors before stopping instead of failing on first error, showing developers all issues at once</li>
<li>Schema validation errors stop the build immediately to prevent cascading failures</li>
</ul>
<p><strong>Developer Experience</strong></p>
<ul>
<li>New <code>@lowdefy/logger</code> package with environment-specific variants (Node, CLI, browser) and standardized logging interface</li>
<li>Dev server now accepts <code>LOWDEFY_DEV_USER</code> env var or <code>auth.dev.mockUser</code> config for mock user support in e2e testing</li>
<li>Port-in-use check displays clear error message before starting server</li>
<li>Build-time validation no longer warns about <code>_state</code> references set by SetState actions</li>
</ul>
<p><strong>Testing</strong></p>
<ul>
<li>New <code>@lowdefy/e2e-utils</code> package for Playwright e2e testing with locator-first API (<code>ldf.block('id').do.*</code>), request mocking, and state assertions</li>
<li>Comprehensive e2e tests for blocks-antd (~700 tests covering 63 blocks) and blocks-basic (~40 tests)</li>
<li><code>npx @lowdefy/e2e-utils</code> scaffold command sets up e2e testing in your project</li>
</ul>
<p><strong>Bug Fixes</strong></p>
<ul>
<li>Fixed env vars not being passed to Next.js build subprocess (<code>NEXT_TELEMETRY_DISABLED</code> was being ignored)</li>
<li>Array line number metadata (<code>~l</code>) now preserved through <code>serializer.copy()</code> — schema validation errors show correct line numbers</li>
<li>Error cause chains properly preserved across plugin boundaries and CLI</li>
<li>Input block onChange events now pass the input value</li>
</ul>
<h2>What's New</h2>
<h3>feat: Config-aware error tracing and Sentry integration</h3>
<p>Packages: <code>@lowdefy/api</code>, <code>@lowdefy/build</code>, <code>lowdefy</code>, <code>@lowdefy/client</code>, <code>@lowdefy/engine</code>, <code>@lowdefy/operators</code>, <code>@lowdefy/actions-core</code>, <code>@lowdefy/blocks-basic</code>, <code>@lowdefy/connection-axios-http</code>, <code>@lowdefy/connection-knex</code>, <code>@lowdefy/connection-redis</code>, <code>@lowdefy/connection-sendgrid</code>, <code>@lowdefy/operators-change-case</code>, <code>@lowdefy/operators-diff</code>, <code>@lowdefy/operators-js</code>, <code>@lowdefy/operators-jsonata</code>, <code>@lowdefy/operators-moment</code>, <code>@lowdefy/operators-mql</code>, <code>@lowdefy/operators-nunjucks</code>, <code>@lowdefy/operators-uuid</code>, <code>@lowdefy/operators-yaml</code>, <code>@lowdefy/server</code>, <code>@lowdefy/server</code>, <code>@lowdefy/server-dev</code>, <code>@lowdefy/server-dev</code>, <code>@lowdefy/block-utils</code>, <code>@lowdefy/errors</code>, <code>@lowdefy/helpers</code>, <code>@lowdefy/node-utils</code></p>
<p><strong>Config-Aware Error Tracing (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3794680189" data-permission-text="Title is private" data-url="https://github.com/lowdefy/lowdefy/issues/1940" data-hovercard-type="issue" data-hovercard-url="/lowdefy/lowdefy/issues/1940/hovercard" href="https://github.com/lowdefy/lowdefy/issues/1940">#1940</a>)</strong></p>
<ul>
<li>Errors now trace back to exact YAML config locations with file:line</li>
<li>Clickable VSCode links in terminal and browser</li>
<li>Build-time validation catches typos with "Did you mean?" suggestions</li>
<li>Service vs Config error classification</li>
</ul>
<p><strong>Plugin Error Refactoring</strong></p>
<ul>
<li>Operators throw simple error messages without formatting</li>
<li>Parsers (WebParser, ServerParser, BuildParser) format errors with received value and location</li>
<li>Removed redundant "Operator Error:" prefix from error messages</li>
<li>Consistent error format: "{message} Received: {params} at {location}."</li>
<li>Actions and connections also simplified: removed inline <code>received</code> from error messages (interface layer adds it)</li>
<li>Connection plugins (axios-http, knex, redis, sendgrid) no longer expose raw response data in errors</li>
</ul>
<p><strong>Error Class Hierarchy</strong></p>
<ul>
<li>Unified error system in <code>@lowdefy/errors</code> with all error classes
<ul>
<li><code>@lowdefy/errors/build</code> - Build-time classes with sync location resolution</li>
</ul>
</li>
<li>Error classes: <code>LowdefyError</code>, <code>ConfigError</code>, <code>ConfigWarning</code>, <code>PluginError</code>, <code>ServiceError</code></li>
<li><code>ConfigWarning</code> supports <code>prodError</code> flag to throw in production builds</li>
<li><code>ServiceError.isServiceError()</code> detects network/timeout/5xx errors</li>
<li><code>~ignoreBuildChecks</code> cascades through descendants to suppress warnings/errors</li>
</ul>
<p><strong>Build Error Collection</strong></p>
<ul>
<li>Errors collected in <code>context.errors[]</code> instead of throwing immediately</li>
<li><code>tryBuildStep()</code> wrapper catches and collects errors from build steps</li>
<li>All errors logged together before summary message for proper ordering</li>
</ul>
<p><strong>Sentry Integration (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3801248669" data-permission-text="Title is private" data-url="https://github.com/lowdefy/lowdefy/issues/1945" data-hovercard-type="issue" data-hovercard-url="/lowdefy/lowdefy/issues/1945/hovercard" href="https://github.com/lowdefy/lowdefy/issues/1945">#1945</a>)</strong></p>
<ul>
<li>Zero-config Sentry support - just set SENTRY_DSN</li>
<li>Client and server error capture with Lowdefy context (pageId, blockId, config location)</li>
<li>Configurable sampling rates, session replay, user feedback</li>
<li>Graceful no-op when DSN not set</li>
</ul>
<h3>feat(server-dev): Add mock user support for e2e testing</h3>
<p>Packages: <code>@lowdefy/api</code>, <code>@lowdefy/build</code>, <code>@lowdefy/server-dev</code></p>
<p>Set <code>LOWDEFY_DEV_USER</code> env var or <code>auth.dev.mockUser</code> in config to bypass login in dev server.</p>
<h3>Collect all build errors before stopping</h3>
<p>Packages: <code>@lowdefy/build</code>, <code>@lowdefy/build</code>, <code>@lowdefy/helpers</code></p>
<h3>feat: JIT page building for dev server</h3>
<p>Packages: <code>@lowdefy/build</code>, <code>@lowdefy/operators-js</code>, <code>@lowdefy/server</code>, <code>@lowdefy/server-dev</code></p>
<p><strong>Shallow Refs and JIT Build (<code>@lowdefy/build</code>)</strong></p>
<ul>
<li>Shallow <code>_ref</code> resolution stops at configured JSON paths, leaving <code>~shallow</code> markers for on-demand resolution</li>
<li><code>shallowBuild</code> produces a page registry with dependency tracking instead of fully built pages</li>
<li><code>buildPageJit</code> fully resolves a single page on demand using the shallow build output</li>
<li>File dependency map tracks which config files affect which pages for targeted rebuilds</li>
<li>Build package reorganized: <code>jit/</code> folder for dev-server-only files, <code>full/</code> folder for production-only files</li>
</ul>
<p><strong>JIT Page Building (<code>@lowdefy/server-dev</code>)</strong></p>
<ul>
<li>Pages are built on-demand when requested instead of all at once during initial build</li>
<li>Page cache with file-watcher invalidation for fast rebuilds</li>
<li><code>/api/page/[pageId]</code> endpoint triggers JIT build if page not cached</li>
<li><code>/api/js/[env]</code> endpoint serves operator JS maps</li>
<li>Build error page component displays errors inline in the browser</li>
</ul>
<p><strong>Operator JS Hash Check (<code>@lowdefy/operators-js</code>)</strong></p>
<ul>
<li>Added hash validation for jsMap to detect stale operator definitions</li>
</ul>
<h3>Add build-time validation for NEXTAUTH_SECRET environment variable when auth providers are configured</h3>
<p>Packages: <code>@lowdefy/build</code></p>
<h3>feat(build): Add ~ignoreBuildChecks property to suppress build validation</h3>
<p>Packages: <code>@lowdefy/build</code>, <code>@lowdefy/docs</code></p>
<p><strong>Build Validation Suppression (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3817203334" data-permission-text="Title is private" data-url="https://github.com/lowdefy/lowdefy/issues/1949" data-hovercard-type="issue" data-hovercard-url="/lowdefy/lowdefy/issues/1949/hovercard" href="https://github.com/lowdefy/lowdefy/issues/1949">#1949</a>, <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3854919231" data-permission-text="Title is private" data-url="https://github.com/lowdefy/lowdefy/issues/1963" data-hovercard-type="pull_request" data-hovercard-url="/lowdefy/lowdefy/pull/1963/hovercard" href="https://github.com/lowdefy/lowdefy/pull/1963">#1963</a>)</strong></p>
<ul>
<li>New <code>~ignoreBuildChecks</code> property suppresses build-time validation errors and warnings</li>
<li>Supports <code>true</code> (suppress all) or array of specific check slugs (e.g., <code>['state-refs', 'types']</code>)</li>
<li>Cascades to all descendant config objects - set on a page to suppress for all child blocks</li>
<li>Silent suppression - no log output when validation is skipped (visible with <code>--log-level debug</code>)</li>
</ul>
<blockquote>
<p><strong>Renamed:</strong> Previously <code>~ignoreBuildCheck</code> (singular) - using the old name throws a helpful migration error.</p>
</blockquote>
<p><strong>Available Check Slugs:</strong></p>
<ul>
<li><code>state-refs</code>, <code>payload-refs</code>, <code>step-refs</code> - Reference validation warnings</li>
<li><code>link-refs</code>, <code>request-refs</code>, <code>connection-refs</code> - Action reference validation</li>
<li><code>types</code> - All type validation (blocks, operators, actions, etc.)</li>
<li><code>schema</code> - JSON schema validation errors</li>
</ul>
<p><strong>Use Cases:</strong></p>
<ul>
<li>Dynamic state references created at runtime by custom blocks</li>
<li>Multi-app monorepos with conditional configurations</li>
<li>Work-in-progress features during development</li>
<li>Plugin development with custom types not yet registered</li>
</ul>
<p><strong>Example:</strong></p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="# Suppress all checks for this page and descendants
pages:
- id: dynamic-page
type: Box
~ignoreBuildChecks: true
blocks:
- id: block1
type: TextInput
properties:
value:
_state: dynamicField # No warning
# Suppress only specific checks
blocks:
- id: custom_block
type: CustomBlock
~ignoreBuildChecks:
- state-refs
- types
properties:
onClick:
_state: dynamicState # No warning (state-refs suppressed)"><pre><span class="pl-c"><span class="pl-c">#</span> Suppress all checks for this page and descendants</span>
<span class="pl-ent">pages</span>:
- <span class="pl-ent">id</span>: <span class="pl-s">dynamic-page</span>
<span class="pl-ent">type</span>: <span class="pl-s">Box</span>
<span class="pl-ent">~ignoreBuildChecks</span>: <span class="pl-c1">true</span>
<span class="pl-ent">blocks</span>:
- <span class="pl-ent">id</span>: <span class="pl-s">block1</span>
<span class="pl-ent">type</span>: <span class="pl-s">TextInput</span>
<span class="pl-ent">properties</span>:
<span class="pl-ent">value</span>:
<span class="pl-ent">_state</span>: <span class="pl-s">dynamicField </span><span class="pl-c"><span class="pl-c">#</span> No warning</span>
<span class="pl-c"><span class="pl-c">#</span> Suppress only specific checks</span>
<span class="pl-ent">blocks</span>:
- <span class="pl-ent">id</span>: <span class="pl-s">custom_block</span>
<span class="pl-ent">type</span>: <span class="pl-s">CustomBlock</span>
<span class="pl-ent">~ignoreBuildChecks</span>:
- <span class="pl-s">state-refs</span>
- <span class="pl-s">types</span>
<span class="pl-ent">properties</span>:
<span class="pl-ent">onClick</span>:
<span class="pl-ent">_state</span>: <span class="pl-s">dynamicState </span><span class="pl-c"><span class="pl-c">#</span> No warning (state-refs suppressed)</span></pre></div>
<h3>Add e2e testing package for Lowdefy apps</h3>
<p>Packages: <code>lowdefy</code>, <code>@lowdefy/client</code>, <code>@lowdefy/blocks-antd</code>, <code>@lowdefy/block-dev-e2e</code>, <code>@lowdefy/e2e-utils</code></p>
<p><strong>@lowdefy/e2e-utils</strong> (new package)</p>
<ul>
<li>Locator-first API via <code>ldf</code> Playwright fixture: <code>ldf.block('id').do.*</code>, <code>ldf.block('id').expect.*</code></li>
<li>Request mocking with static YAML files (<code>mocks.yaml</code>) and inline per-test overrides</li>
<li>Request assertion API: <code>ldf.request('id').expect.toFinish()</code>, <code>.toHaveResponse()</code>, <code>.toHavePayload()</code></li>
<li>State and URL assertions: <code>ldf.state('key').expect.toBe()</code>, <code>ldf.url().expect.toBe()</code></li>
<li>Manifest generation from build artifacts for block type resolution and helper loading</li>
<li><code>createConfig()</code> and <code>createMultiAppConfig()</code> for Playwright config with automatic build/server management</li>
<li>Scaffold command (<code>npx @lowdefy/e2e-utils</code>) for project setup with templates and dependency management</li>
<li>Block helper factory with auto-provided expect methods (visible, hidden, disabled, validation)</li>
</ul>
<p><strong>@lowdefy/cli</strong></p>
<ul>
<li>Add <code>--server</code> option to <code>lowdefy build</code> for server variant selection (e.g., <code>--server e2e</code>)</li>
</ul>
<p><strong>@lowdefy/client</strong></p>
<ul>
<li>Expose <code>window.lowdefy</code> when <code>stage="e2e"</code> for e2e state/validation access</li>
</ul>
<p><strong>@lowdefy/blocks-antd</strong></p>
<ul>
<li>Flatten e2e helper APIs for polymorphic proxy compatibility</li>
<li>Add TextArea e2e helper</li>
</ul>
<p><strong>@lowdefy/block-dev-e2e</strong></p>
<ul>
<li>Remove unused srcDir variable</li>
</ul>
<h3>feat(logger): Add centralized @lowdefy/logger package and standardize logging</h3>
<p>Packages: <code>lowdefy</code>, <code>@lowdefy/server-dev</code>, <code>@lowdefy/logger</code></p>
<p><strong>New @lowdefy/logger Package</strong></p>
<ul>
<li>Centralized logging with environment-specific subpaths: <code>/node</code>, <code>/cli</code>, <code>/browser</code></li>
<li><code>createNodeLogger</code> — pino factory with custom error serializer preserving Lowdefy error metadata (source, configKey, isServiceError)</li>
<li><code>createCliLogger</code> — wraps <code>createPrint</code> (ora spinners, colored output) with standard logger interface</li>
<li><code>createBrowserLogger</code> — maps to <code>console.*</code> with error formatting</li>
<li><code>wrapErrorLogger</code> — formats Lowdefy errors, emits source as separate <code>{ print: 'link' }</code> line for blue clickable links</li>
</ul>
<p><strong>Standardized <code>.ui</code> Interface</strong></p>
<p>All logger variants expose <code>logger.ui</code> with consistent methods: <code>log</code>, <code>dim</code>, <code>info</code>, <code>warn</code>, <code>error</code>, <code>debug</code>, <code>link</code>, <code>spin</code>, <code>succeed</code>. This allows any component to emit structured output without knowing the runtime environment.</p>
<ul>
<li><code>dim</code> renders as dimmed text in the CLI — useful for low-priority trace lines (e.g., request logs) that shouldn't compete visually with build output</li>
</ul>
<p><strong>CLI Logger Migration</strong></p>
<ul>
<li>CLI now uses <code>createCliLogger</code> instead of raw <code>createPrint</code></li>
<li><code>context.print</code> replaced with <code>context.logger</code> / <code>context.logger.ui</code></li>
<li><code>createPrint</code> and <code>createStdOutLineHandler</code> moved from CLI to <code>@lowdefy/logger/cli</code></li>
</ul>
<p><strong>Server-Dev stdio:inherit</strong></p>
<ul>
<li>Server process spawned with <code>stdio: ['ignore', 'inherit', 'pipe']</code></li>
<li>Server pino JSON flows directly to manager stdout (inherited by CLI) — eliminates dev stdout line handler</li>
<li>Only stderr piped for error formatting through manager logger</li>
<li>Server <code>createLogger</code> includes <code>print</code> mixin so CLI can render each line correctly</li>
</ul>
<h3>feat(errors): Add UserError class and thread actionId through request pipeline</h3>
<p>Packages: <code>@lowdefy/engine</code>, <code>@lowdefy/actions-core</code>, <code>@lowdefy/connection-test</code>, <code>@lowdefy/errors</code></p>
<p><strong>UserError Class</strong></p>
<ul>
<li>New <code>UserError</code> in <code>@lowdefy/errors</code> for expected user-facing errors (validation failures, intentional throws)</li>
<li>UserError logs to browser console only — never sent to the server terminal</li>
<li><code>Throw</code> action now throws <code>UserError</code> instead of custom <code>ThrowActionError</code></li>
</ul>
<p><strong>Engine Error Routing</strong></p>
<ul>
<li><code>Actions.logActionError()</code> routes errors by type: <code>UserError</code> → <code>console.error()</code>, all others → <code>logError()</code> (terminal)</li>
<li>Deduplication by error message + action ID prevents repeated logging</li>
</ul>
<p><strong>actionId Threading</strong></p>
<ul>
<li><code>actionId</code> threaded from <code>callAction</code> through <code>createRequest</code> to <code>Requests.callRequests</code></li>
<li>Server-dev request handler logs request trace via <code>logger.ui.dim()</code> for dimmed output</li>
<li>Enables request logs to include the triggering action for better debugging context</li>
</ul>
<h3>test(blocks): Add comprehensive Playwright e2e tests for blocks-antd and blocks-basic</h3>
<p>Packages: <code>@lowdefy/blocks-antd</code>, <code>@lowdefy/blocks-basic</code>, <code>@lowdefy/block-dev-e2e</code></p>
<p><strong>@lowdefy/block-dev-e2e</strong> (new package)</p>
<ul>
<li>Shared test utilities for block e2e testing in the monorepo</li>
<li><code>createPlaywrightConfig</code> for consistent Playwright setup</li>
<li><code>getBlock</code> helper using framework wrapper ID pattern (<code>#bl-{blockId}</code>)</li>
<li><code>navigateToTestPage</code> for test page navigation</li>
</ul>
<p><strong>@lowdefy/blocks-antd</strong></p>
<ul>
<li>~700 e2e tests covering all 63 blocks</li>
<li>Test coverage for input, display, layout, navigation, and overlay blocks</li>
<li>Block-specific e2e helpers (Button, TextInput, Selector)</li>
</ul>
<p><strong>@lowdefy/blocks-basic</strong></p>
<ul>
<li>~40 e2e tests covering core blocks (Box, Span, Anchor, Html, etc.)</li>
</ul>
<h2>Fixes & Improvements</h2>
<ul>
<li>
<p><strong>refactor: Consolidate error classes into @lowdefy/errors package with environment-specific subpaths</strong> (<code>@lowdefy/api</code>, <code>@lowdefy/build</code>, <code>@lowdefy/client</code>, <code>@lowdefy/engine</code>, <code>@lowdefy/operators</code>, <code>@lowdefy/errors</code>, <code>@lowdefy/helpers</code>, <code>@lowdefy/logger</code>)</p>
<p><strong>Error Package Restructure</strong></p>
<ul>
<li>New <code>@lowdefy/errors</code> package with all error classes (<code>ConfigError</code>, <code>PluginError</code>, <code>ServiceError</code>, <code>UserError</code>, <code>LowdefyInternalError</code>, <code>ConfigWarning</code>)
<ul>
<li><code>@lowdefy/errors/build</code> - Build-time errors with sync resolution via keyMap/refMap</li>
</ul>
</li>
<li>Moved ConfigMessage, resolveConfigLocation from node-utils to errors/build</li>
</ul>
<p><strong>TC39 Standard Constructor Signatures</strong></p>
<ul>
<li>All error constructors standardized to <code>new MyError(message, { cause, ...options })</code>:
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="new ConfigError('Property must be a string.', { configKey });
new OperatorError(e.message, { cause: e, typeName: '_if', received: params });
new ServiceError(undefined, { cause: error, service: 'MongoDB', configKey });"><pre><span class="pl-k">new</span> <span class="pl-v">ConfigError</span><span class="pl-kos">(</span><span class="pl-s">'Property must be a string.'</span><span class="pl-kos">,</span> <span class="pl-kos">{</span> configKey <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-k">new</span> <span class="pl-v">OperatorError</span><span class="pl-kos">(</span><span class="pl-s1">e</span><span class="pl-kos">.</span><span class="pl-c1">message</span><span class="pl-kos">,</span> <span class="pl-kos">{</span> <span class="pl-c1">cause</span>: <span class="pl-s1">e</span><span class="pl-kos">,</span> <span class="pl-c1">typeName</span>: <span class="pl-s">'_if'</span><span class="pl-kos">,</span> <span class="pl-c1">received</span>: <span class="pl-s1">params</span> <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-k">new</span> <span class="pl-v">ServiceError</span><span class="pl-kos">(</span><span class="pl-c1">undefined</span><span class="pl-kos">,</span> <span class="pl-kos">{</span> <span class="pl-c1">cause</span>: <span class="pl-s1">error</span><span class="pl-kos">,</span> <span class="pl-c1">service</span>: <span class="pl-s">'MongoDB'</span><span class="pl-kos">,</span> configKey <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
</li>
<li>Plugins throw simple errors without knowing about configKey</li>
<li>Interface layer adds configKey before re-throwing</li>
</ul>
<p><strong>configKey Added to ALL Errors</strong></p>
<ul>
<li>Interface layer now adds configKey to ALL error types (not just PluginError):
<ul>
<li>ConfigError: adds configKey if not present, re-throws</li>
<li>ServiceError: created via <code>new ServiceError(undefined, { cause: error, service, configKey })</code></li>
<li>Plain Error: wraps in PluginError with configKey</li>
</ul>
</li>
<li>Helps developers trace any error back to its config source, including service/network errors</li>
</ul>
<p><strong>Cause Chain Support</strong></p>
<ul>
<li>All error classes use TC39 <code>error.cause</code> instead of custom stack copying</li>
<li>CLI logger walks cause chain displaying <code>Caused by:</code> lines</li>
<li><code>extractErrorProps</code> recursively serializes Error causes for pino JSON logs</li>
<li>ConfigError and PluginError extract <code>received</code> and <code>configKey</code> from <code>cause</code>:
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="new ConfigError(undefined, { cause: plainError }); // extracts cause.received and cause.configKey
new PluginError(undefined, { cause: plainError }); // same extraction"><pre><span class="pl-k">new</span> <span class="pl-v">ConfigError</span><span class="pl-kos">(</span><span class="pl-c1">undefined</span><span class="pl-kos">,</span> <span class="pl-kos">{</span> <span class="pl-c1">cause</span>: <span class="pl-s1">plainError</span> <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// extracts cause.received and cause.configKey</span>
<span class="pl-k">new</span> <span class="pl-v">PluginError</span><span class="pl-kos">(</span><span class="pl-c1">undefined</span><span class="pl-kos">,</span> <span class="pl-kos">{</span> <span class="pl-c1">cause</span>: <span class="pl-s1">plainError</span> <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// same extraction</span></pre></div>
</li>
</ul>
<p><strong>Error Display</strong></p>
<ul>
<li><code>errorToDisplayString()</code> formats errors for display, appending <code>Received: <JSON></code> when <code>error.received</code> is defined</li>
<li><code>rawMessage</code> stores the original unformatted message on PluginError</li>
</ul>
</li>
<li>
<p><strong>fix(build): Eliminate false positive warnings for _state references set by SetState actions</strong> (<code>@lowdefy/build</code>)</p>
<p>The validateStateReferences validator now recognizes state keys initialized by SetState actions in page or block events, eliminating false positive warnings when _state references legitimate state that's set programmatically rather than from input blocks.</p>
</li>
<li>
<p><strong>Improve build error handling and test infrastructure:</strong> (<code>@lowdefy/build</code>, <code>@lowdefy/engine</code>)</p>
<ul>
<li>Stop build after schema validation errors to prevent cascading failures</li>
<li>Convert makeId to class with reset() method for reliable test isolation</li>
<li>Add parseTestYaml helper for realistic YAML-based test fixtures</li>
<li>Simplify buildConnections by removing duplicate validations handled by schema</li>
<li>Fix addKeys to not store undefined values in keyMap</li>
<li>Menu link to missing page is warning in dev, error in prod</li>
<li>Handle areas with no blocks gracefully - render as empty page instead of crashing</li>
<li>Filter out anyOf/oneOf cascade errors in schema validation - only show the specific error</li>
</ul>
</li>
<li>
<p><strong>fix(helpers): Preserve ~l line numbers on arrays in serializer.copy</strong> (<code>@lowdefy/build</code>, <code>@lowdefy/helpers</code>)</p>
<p>Fixed an issue where line number metadata (<code>~l</code>) on arrays was lost during <code>serializer.copy()</code>, causing schema validation errors to show incorrect line numbers.</p>
<p><strong>Problem:</strong></p>
<ul>
<li>Schema errors for properties like <code>requests:</code> at line 7 were showing <code>:1</code> instead of <code>:7</code></li>
<li>The <code>~l</code> property on arrays was stripped during JSON round-trip in <code>evaluateBuildOperators</code></li>
</ul>
<p><strong>Solution:</strong></p>
<ul>
<li>Arrays with <code>~l</code> are now wrapped in a marker object <code>{ '~arr': [...], '~l': N }</code> during serialization</li>
<li>The reviver restores the array with <code>~l</code> preserved as a non-enumerable property</li>
<li>Custom revivers now receive the restored array instead of the wrapper object</li>
</ul>
<p><strong>Result:</strong></p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Before: lowdefy.yaml:1 at root
After: lowdefy.yaml:7 at root"><pre class="notranslate"><code>Before: lowdefy.yaml:1 at root
After: lowdefy.yaml:7 at root
</code></pre></div>
</li>
<li>
<p><strong>fix(errors): Preserve error cause chains in catch-and-rethrow blocks across plugins and CLI</strong> (<code>lowdefy</code>, <code>@lowdefy/connection-axios-http</code>, <code>@lowdefy/connection-sendgrid</code>, <code>@lowdefy/operators-js</code>, <code>@lowdefy/operators-jsonata</code>, <code>@lowdefy/operators-nunjucks</code>)</p>
</li>
<li>
<p><strong>Fix env vars not being passed to Next.js build subprocess. The <code>env</code> object was passed as a separate parameter to <code>spawnProcess</code> instead of inside <code>processOptions</code>, so <code>NEXT_TELEMETRY_DISABLED</code> was silently ignored during <code>next build</code>.</strong> (<code>lowdefy</code>)</p>
</li>
<li>
<p>Add port-in-use check with clear error message before starting server.</p>
</li>
<li>
<p><strong>fix(errors): Remove redundant try/catch in operator runners, add cause chains to remaining throws</strong> (<code>@lowdefy/actions-core</code>, <code>@lowdefy/server-dev</code>, <code>@lowdefy/block-dev</code>)</p>
</li>
<li>
<p><strong>Pass value of inputs to onChange event.</strong> (<code>@lowdefy/blocks-antd</code>, <code>@lowdefy/blocks-color-selectors</code>)</p>
</li>
</ul>SamTolmaytag:github.com,2008:Repository/300252678/v4.5.22025-11-10T14:51:00Zv4.5.2<h3>Patch Changes</h3>
<ul>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/d573e8ff8d61a72f8007e49eb8e2c7fbc856ff13/hovercard" href="https://github.com/lowdefy/lowdefy/commit/d573e8ff8d61a72f8007e49eb8e2c7fbc856ff13"><tt>d573e8f</tt></a>: Add guard to prevent TypeError in icon <code>formatTitle</code>.</li>
</ul>SamTolmaytag:github.com,2008:Repository/300252678/v4.5.12025-11-03T10:05:54Zv4.5.1<h3>Patch Changes</h3>
<ul>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/51f7f9dbe27659dd91b754825725152d202df478/hovercard" href="https://github.com/lowdefy/lowdefy/commit/51f7f9dbe27659dd91b754825725152d202df478"><tt>51f7f9d</tt></a>: Use uuid instead of crypto.randomUUID(), update uuid to v13.</li>
</ul>SamTolmaytag:github.com,2008:Repository/300252678/v4.5.02025-10-20T08:24:15Zv4.5.0<h3>Minor Changes</h3>
<ul>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/16084c1bd8b4eaca8661e58427ea26807bf1a2af/hovercard" href="https://github.com/lowdefy/lowdefy/commit/16084c1bd8b4eaca8661e58427ea26807bf1a2af"><tt>16084c1</tt></a>: Adds Lowdefy APIs. Lowdefy APIs allow you to create custom server-side API endpoints within your Lowdefy application. See <a href="https://docs.lowdefy.com/lowdefy-api" rel="nofollow">https://docs.lowdefy.com/lowdefy-api</a> for more info.</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/abc90f3f7e0c940743be2c677a0a233270a31dff/hovercard" href="https://github.com/lowdefy/lowdefy/commit/abc90f3f7e0c940743be2c677a0a233270a31dff"><tt>abc90f3</tt></a>: Change to Apache 2.0 license for all packages. All license checks and restrictions have been removed.</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/09ae496d8e71f815f0e264f55085418d1ee6b549/hovercard" href="https://github.com/lowdefy/lowdefy/commit/09ae496d8e71f815f0e264f55085418d1ee6b549"><tt>09ae496</tt></a>: Add JSONata operator.</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/d6c58fe97f59a2e7e5fea28eeba9b01b803314e4/hovercard" href="https://github.com/lowdefy/lowdefy/commit/d6c58fe97f59a2e7e5fea28eeba9b01b803314e4"><tt>d6c58fe</tt></a>: Add minItems property to ControlledList block.</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/b3a2e66620cac07af7e0a70b6caa6f84c50e8f12/hovercard" href="https://github.com/lowdefy/lowdefy/commit/b3a2e66620cac07af7e0a70b6caa6f84c50e8f12"><tt>b3a2e66</tt></a>: Add afterOpenChange and afterClose events to Drawer block.</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/d9512d9be2f7e7643f6bf063dd16033b6f30ec15/hovercard" href="https://github.com/lowdefy/lowdefy/commit/d9512d9be2f7e7643f6bf063dd16033b6f30ec15"><tt>d9512d9</tt></a>: Refactor build to create individual block instances.</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/d9512d9be2f7e7643f6bf063dd16033b6f30ec15/hovercard" href="https://github.com/lowdefy/lowdefy/commit/d9512d9be2f7e7643f6bf063dd16033b6f30ec15"><tt>d9512d9</tt></a>: Add hybrid block type to extend block functionality.</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/4f610de5c7ed92963913cfea843f0f32eb362b36/hovercard" href="https://github.com/lowdefy/lowdefy/commit/4f610de5c7ed92963913cfea843f0f32eb362b36"><tt>4f610de</tt></a>: Allow custom icon titles and format the icon name if a title is not specified.</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/fe3676ce072344d90f36147e2726e9fc63738bbb/hovercard" href="https://github.com/lowdefy/lowdefy/commit/fe3676ce072344d90f36147e2726e9fc63738bbb"><tt>fe3676c</tt></a>: Add missing events to S3Upload block schemas.</li>
</ul>SamTolmaytag:github.com,2008:Repository/300252678/v4.4.02024-12-06T15:23:52Zv4.4.0<h3>Minor Changes</h3>
<ul>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/bcfbb1a9bb650e2deb4eb28bc4f29dfc540d2742/hovercard" href="https://github.com/lowdefy/lowdefy/commit/bcfbb1a9bb650e2deb4eb28bc4f29dfc540d2742"><tt>bcfbb1a</tt></a>: Add autoClearSearchValue property to MultipleSelector block.</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/156fa7f2eb99c8fbdff0029c27572c96d0851ed3/hovercard" href="https://github.com/lowdefy/lowdefy/commit/156fa7f2eb99c8fbdff0029c27572c96d0851ed3"><tt>156fa7f</tt></a>: Add <code>urlQuery</code> filter to the nunjucks operator.</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/1a81e100eacf16f2059c85daeb7d953cca2c63bc/hovercard" href="https://github.com/lowdefy/lowdefy/commit/1a81e100eacf16f2059c85daeb7d953cca2c63bc"><tt>1a81e10</tt></a>: Update echarts dependecy to 5.5.1</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/a491106b777afaaea5712a1eb08f6d82861a10a0/hovercard" href="https://github.com/lowdefy/lowdefy/commit/a491106b777afaaea5712a1eb08f6d82861a10a0"><tt>a491106</tt></a>: Fix spawnProcess options on Windows</li>
</ul>SamTolmaytag:github.com,2008:Repository/300252678/v4.3.02024-07-31T15:06:54Zv4.3.0<h3>Minor Changes</h3>
<ul>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/fe1347f6d883b22f8d9dd848b6124d822e2b466c/hovercard" href="https://github.com/lowdefy/lowdefy/commit/fe1347f6d883b22f8d9dd848b6124d822e2b466c"><tt>fe1347f</tt></a>: Init VSCode extension with syntax highlighting for yaml.</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/fe1347f6d883b22f8d9dd848b6124d822e2b466c/hovercard" href="https://github.com/lowdefy/lowdefy/commit/fe1347f6d883b22f8d9dd848b6124d822e2b466c"><tt>fe1347f</tt></a>: VSCode extension to read activeAppRoot from local dev server for clickable _ref links.</li>
</ul>SamTolmaytag:github.com,2008:Repository/300252678/v4.2.22024-07-17T06:44:01Zv4.2.2<h3>Patch Changes</h3>
<ul>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/e4ec43505f969269a5701f77e97ede0dbabdb06c/hovercard" href="https://github.com/lowdefy/lowdefy/commit/e4ec43505f969269a5701f77e97ede0dbabdb06c"><tt>e4ec435</tt></a>: Fix undefined property access in PhoneNumberInput component</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/50af1e8aa430fd0c58df9d7b785d87f3af2527c3/hovercard" href="https://github.com/lowdefy/lowdefy/commit/50af1e8aa430fd0c58df9d7b785d87f3af2527c3"><tt>50af1e8</tt></a>: Add MongoDbBulkWrite to MongoDBCollection requests.</li>
</ul>SamTolmaytag:github.com,2008:Repository/300252678/v4.2.12024-07-04T15:07:38Zv4.2.1<h3>Minor Changes</h3>
<ul>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/143c83e7dd69968b4325a8e1bc54e1e8f066777e/hovercard" href="https://github.com/lowdefy/lowdefy/commit/143c83e7dd69968b4325a8e1bc54e1e8f066777e"><tt>143c83e</tt></a>: Add request type <code>MongoDBBulkWrite</code> to the MongoDB connection.</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/95663d1d57251b926f5db2a89647162063a1935d/hovercard" href="https://github.com/lowdefy/lowdefy/commit/95663d1d57251b926f5db2a89647162063a1935d"><tt>95663d1</tt></a>: Add <code>unique</code> filter to <code>_nunjucks</code> operator.</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/47d8559186e6077c0eb588a14a631379230f05a4/hovercard" href="https://github.com/lowdefy/lowdefy/commit/47d8559186e6077c0eb588a14a631379230f05a4"><tt>47d8559</tt></a>: Move layoutParamsToArea into Area component.</li>
<li><a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/lowdefy/lowdefy/commit/47d8559186e6077c0eb588a14a631379230f05a4/hovercard" href="https://github.com/lowdefy/lowdefy/commit/47d8559186e6077c0eb588a14a631379230f05a4"><tt>47d8559</tt></a>: Remove highlightBorders from layout.</li>
</ul>SamTolmay