diff --git a/.cargo/config.toml b/.cargo/config.toml index d22ed76a..9247b719 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,3 +5,6 @@ # set by the user, and this is a good thing. If the user already set some # LIBSQLITE3_FLAGS, he probably knows what he is doing. LIBSQLITE3_FLAGS = "-DSQLITE_ENABLE_MATH_FUNCTIONS" + +[build] +rustflags = [] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcb77d5e..8c11db3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,9 +29,12 @@ jobs: - run: npm test - name: Set up cargo cache uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + with: + shared-key: rust-sqlpage-proj-test + save-if: ${{ github.ref == 'refs/heads/main' }} - run: cargo fmt --all -- --check - run: cargo clippy --all-targets --all-features -- -D warnings - - run: cargo test + - run: cargo test --features odbc-static - name: Upload Linux binary uses: actions/upload-artifact@v4 with: @@ -42,23 +45,43 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - database: ["postgres", "mysql", "mssql"] + include: + - database: postgres + container: postgres + db_url: "postgres://root:Password123!@127.0.0.1/sqlpage" + - database: mysql + container: mysql + db_url: "mysql://root:Password123!@127.0.0.1/sqlpage" + - database: mssql + container: mssql + db_url: "mssql://root:Password123!@127.0.0.1/sqlpage" + - database: odbc + container: postgres + db_url: "Driver=PostgreSQL Unicode;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!" + setup_odbc: true steps: - uses: actions/checkout@v4 - name: Set up cargo cache uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + with: + shared-key: rust-sqlpage-proj-test + save-if: false + - name: Install PostgreSQL ODBC driver + if: matrix.setup_odbc + run: sudo apt-get install -y odbc-postgresql - name: Start database container - run: docker compose up --wait ${{ matrix.database }} + run: docker compose up --wait ${{ matrix.container }} - name: Show container logs if: failure() - run: docker compose logs ${{ matrix.database }} + run: docker compose logs ${{ matrix.container }} - name: Run tests against ${{ matrix.database }} timeout-minutes: 5 - run: cargo test + run: cargo test --features odbc-static env: - DATABASE_URL: ${{ matrix.database }}://root:Password123!@127.0.0.1/sqlpage + DATABASE_URL: ${{ matrix.db_url }} RUST_BACKTRACE: 1 - RUST_LOG: sqlpage=debug + MALLOC_CHECK_: 3 + MALLOC_PERTURB_: 10 windows_test: runs-on: windows-latest @@ -73,7 +96,7 @@ jobs: RUST_BACKTRACE: 1 - name: Upload Windows binary uses: actions/upload-artifact@v4 - with: + with: name: sqlpage-windows-debug path: "target/debug/sqlpage.exe" diff --git a/.github/workflows/official-site.yml b/.github/workflows/official-site.yml index 0ec66068..3ad48ef3 100644 --- a/.github/workflows/official-site.yml +++ b/.github/workflows/official-site.yml @@ -8,7 +8,7 @@ on: concurrency: site-deploy jobs: - deploy: + deploy_official_site: runs-on: ubuntu-latest steps: - name: Cloning repo diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 72fb4cd6..7865ab24 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -20,10 +20,8 @@ jobs: node-version: lts/* cache: 'npm' cache-dependency-path: ./tests/end-to-end/package-lock.json - - name: Install dependencies - run: | - npm ci - npx playwright install --with-deps chromium + - run: sudo apt-get update && sudo apt-get install -y unixodbc-dev + - run: npm ci && npx playwright install --with-deps chromium - name: build sqlpage run: cargo build working-directory: ./examples/official-site diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index de671e73..e32cf37e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,8 +24,10 @@ jobs: - os: windows-latest binary_extension: .exe target: x86_64-pc-windows-msvc + features: "" - os: macos-latest target: x86_64-apple-darwin + features: "odbc-static" steps: - uses: actions/checkout@v4 - name: Install Rust toolchain @@ -35,7 +37,7 @@ jobs: - name: Set up cargo cache uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - name: Build - run: cargo build --profile superoptimized --locked --target ${{ matrix.target }} + run: cargo build --profile superoptimized --locked --target ${{ matrix.target }} --features "${{ matrix.features }}" - name: Upload unsigned Windows artifact if: matrix.os == 'windows-latest' id: upload_unsigned @@ -57,6 +59,9 @@ jobs: github-artifact-id: ${{ steps.upload_unsigned.outputs.artifact-id }} wait-for-completion: true output-artifact-directory: './signed-windows' + wait-for-completion-timeout-in-seconds: 7200 + service-unavailable-timeout-in-seconds: 1800 + download-signed-artifact-timeout-in-seconds: 1800 - name: Upload signed Windows artifact if: matrix.os == 'windows-latest' @@ -87,7 +92,7 @@ jobs: - name: Set up cargo cache uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - name: Build - run: cargo build --profile superoptimized --locked --target x86_64-unknown-linux-gnu + run: cargo build --profile superoptimized --locked --target x86_64-unknown-linux-gnu --features "odbc-static" - uses: actions/upload-artifact@v4 with: name: sqlpage ubuntu-latest @@ -145,5 +150,6 @@ jobs: - uses: actions/checkout@v4 - name: Set up cargo cache uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - run: sudo apt-get update && sudo apt-get install -y unixodbc-dev - name: Publish to crates.io run: cargo publish --token ${{ secrets.CRATES_IO_TOKEN }} diff --git a/.gitignore b/.gitignore index a1a6898f..9cf4a906 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_STORE /target sqlpage.db .idea/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..ababedd5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,47 @@ +Core Concept: User writes .sql files, SQLPage executes queries, results mapped to handlebars UI components, +HTML streamed to client + +## Validation + +### When working on rust code +Mandatory formatting (rust): `cargo fmt --all` +Mandatory linting: `cargo clippy --all-targets --all-features -- -D warnings` + +### When working on css or js +Frontend formatting: `npm run format` + +More about testing: see [github actions](./.github/workflows/ci.yml). +Project structure: see [contribution guide](./CONTRIBUTING.md) + +NEVER reformat/lint/touch files unrelated to your task. Always run tests/lints/format before stopping when you changed code. + +### Testing + +``` +cargo test # tests with inmemory sqlite by default +``` + +For other databases, see [docker testing setup](./docker-compose.yml) + +``` +docker compose up -d mssql # or postgres or mysql +DATABASE_URL='mssql://root:Password123!@localhost/sqlpage' cargo test # all dbms use the same user:pass and db name +``` + +### Documentation + +Components and functions are documented in [official website](./examples/official-site/sqlpage/migrations/); one migration per component and per function. You CAN update existing migrations, the official site database is recreated from scratch on each deployment. + +official documentation website sql tables: + - `component(name,description,icon,introduced_in_version)` -- icon name from tabler icon + - `parameter(top_level BOOLEAN, name, component REFERENCES component(name), description, description_md, type, optional BOOLEAN)` parameter types: BOOLEAN, COLOR, HTML, ICON, INTEGER, JSON, REAL, TEXT, TIMESTAMP, URL + - `example(component REFERENCES component(name), description, properties JSON)` + +#### Project Conventions + +- Components: defined in `./sqlpage/templates/*.handlebars` +- Functions: `src/webserver/database/sqlpage_functions/functions.rs` registered with `make_function!`. +- [Configuration](./configuration.md): see [AppConfig](./src/app_config.rs) +- Routing: file-based in `src/webserver/routing.rs`; not found handled via `src/default_404.sql`. +- Follow patterns from similar modules before introducing new abstractions. +- frontend: see [css](./sqlpage/sqlpage.css) and [js](./sqlpage/sqlpage.js) \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 124126a3..067dfe10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,115 @@ # CHANGELOG.md +## unreleased + - fix: `sqlpage.variables()` now does not return json objects with duplicate keys when post, get and set variables of the same name are present. The semantics of the returned values remains the same (precedence: set > post > get). + +## 0.41.0 (2025-12-28) + - **New Function**: `sqlpage.oidc_logout_url(redirect_uri)` - Generates a secure logout URL for OIDC-authenticated users with support for [RP-Initiated Logout](https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout) + - Fix compatibility with Auth0 for OpenID-Connect authentification. See https://github.com/ramosbugs/openidconnect-rs/issues/23 + - updated sql parser: https://github.com/apache/datafusion-sqlparser-rs/blob/main/changelog/0.60.0.md + - updated apexcharts to 5.3.6: + - https://github.com/apexcharts/apexcharts.js/compare/v5.3.0...v5.3.6 + - https://github.com/apexcharts/apexcharts.js/releases/tag/v5.3.6 + - re-add the `lime` color option to charts + - update default chart color palette; use [Open Colors](https://yeun.github.io/open-color/) + - image + - re-enable text drop shadow in chart data labels + +## 0.40.0 (2025-11-28) + - OIDC login redirects now use HTTP 303 responses so POST submissions are converted to safe GET requests before reaching the identity provider, fixing incorrect reuse of the original POST (HTTP 307) that could break standard auth flows. + - SQLPage now respects [HTTP accept headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept) for JSON. You can now easily process the contents of any existing sql page programmatically with: + - `curl -H "Accept: application/json" http://example.com/page.sql`: returns a json array + - `curl -H "Accept: application/x-ndjson" http://example.com/page.sql`: returns one json object per line. + - Fixed a bug in `sqlpage.link`: a link with no path (link to the current page) and no url parameter now works as expected. It used to keep the existing url parameters instead of removing them. `sqlpage.link('', '{}')` now returns `'?'` instead of the empty string. + - `sqlpage.fetch(null)` and `sqlpage.fetch_with_meta(null)` now return `null` instead of throwing an error. + - **New Function**: `sqlpage.set_variable(name, value)` + - Returns a URL with the specified variable set to the given value, preserving other existing variables. + - This is a shorthand for `sqlpage.link(sqlpage.path(), json_patch(sqlpage.variables('get'), json_object(name, value)))`. + - **Variable System Improvements**: URL and POST parameters are now immutable, preventing accidental modification. User-defined variables created with `SET` remain mutable. + - **BREAKING**: `$variable` no longer accesses POST parameters. Use `:variable` instead. + - **What changed**: Previously, `$x` would return a POST parameter value if no GET parameter named `x` existed. + - **Fix**: Replace `$x` with `:x` when you need to access form field values. + - **Example**: Change `SELECT $username` to `SELECT :username` when reading form submissions. + - **BREAKING**: `SET $name` no longer makes GET (URL) parameters inaccessible when a URL parameter with the same name exists. + - **What changed**: `SET $name = 'value'` would previously overwrite the URL parameter `$name`. Now it creates an independent SET variable that shadows the URL parameter. + - **Fix**: This is generally the desired behavior. If you need to access the original URL parameter after setting a variable with the same name, extract it from the JSON returned by `sqlpage.variables('get')`. + - **Example**: If your URL is `page.sql?name=john`, and you do `SET $name = 'modified'`, then: + - `$name` will be `'modified'` (the SET variable) + - The original URL parameter is still preserved and accessible: + - `sqlpage.variables('get')->>'name'` returns `'john'` + - **New behavior**: Variable lookup now follows this precedence: + - `$variable` checks SET variables first, then URL parameters + - SET variables always shadow URL/POST parameters with the same name + - **New sqlpage.variables() filters**: + - `sqlpage.variables('get')` returns only URL parameters as JSON + - `sqlpage.variables('post')` returns only POST parameters as JSON + - `sqlpage.variables('set')` returns only user-defined SET variables as JSON + - `sqlpage.variables()` returns all variables merged together, with SET variables taking precedence + - **Deprecation warnings**: Using `$var` when both a URL parameter and POST parameter exist with the same name now shows a warning. In a future version, you'll need to explicitly choose between `$var` (URL) and `:var` (POST). + - Improved performance of `sqlpage.run_sql`. + - On a simple test that just runs 4 run_sql calls, the new version is about 2.7x faster (15,708 req/s vs 5,782 req/s) with lower latency (0.637 ms vs 1.730 ms per request). + - add support for postgres range types + +## v0.39.1 (2025-11-08) + - More precise server timing tracking to debug performance issues + - Fix missing server timing header in some cases + - Implement nice error messages for some header-related errors such as invalid header values. + - `compress_responses` is now set to `false` by default in the configuration. + - When response compression is enabled, additional buffering is needed. Users reported a better experience with pages that load more progressively, reducing the time before the pages' shell is rendered. + - When SQLPage is deployed behind a reverse proxy, compressing responses between sqlpage and the proxy is wasteful. + - In the table component, allow simple objects in custom_actions instead of requiring arrays of objects. + - Fatser icon loading. Previously, even a page containing a single icon required downloading and parsing a ~2MB file. This resulted in a delay where pages initially appeared with a blank space before icons appeared. Icons are now inlined inside pages and appear instantaneously. + - Updated tabler icons to 3.35 + - Fix inaccurate ODBC warnings + - Added support for Microsoft SQL Server named instances: `mssql://user:pass@localhost/db?instance_name=xxx` + - Added a detailed [performance guide](https://sql-page.com/blog?post=Performance+Guide) to the docs. + +## v0.39.0 (2025-10-28) + - Ability to execute sql for URL paths with another extension. If you create sitemap.xml.sql, it will be executed for example.com/sitemap.xml + - Display source line info in errors even when the database does not return a precise error position. In this case, the entire problematic SQL statement is referenced. + - The shell with a vertical sidebar can now have "active" elements, just like the horizontal header bar. + - New `edit_url`, `delete_url`, and `custom_actions` properties in the [table](https://sql-page.com/component.sql?component=table) component to easily add nice icon buttons to a table. + - SQLPage now sets the [`Server-Timing` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Server-Timing) in development. So when you have a page that loads slowly, you can open your browser's network inspector, click on the slow request, then open the timing tab to understand where it's spending its time. + - image + - Fixed a memory corruption issue in the builtin odbc driver manager + - ODBC: fix using globally installed system drivers by their name in debian-based linux distributions. + - New [login](https://sql-page.com/component.sql?component=table) component. + + +## v0.38.0 + - Added support for the Open Database Connectivity (ODBC) standard. + - This makes SQLPage compatible with many new databases, including: + - [*ClickHouse*](https://github.com/ClickHouse/clickhouse-odbc), + - [*MongoDB*](https://www.mongodb.com/docs/atlas/data-federation/query/sql/drivers/odbc/connect), + - [*DuckDB*](https://duckdb.org/docs/stable/clients/odbc/overview.html), and through it [many other data sources](https://duckdb.org/docs/stable/data/data_sources), + - [*Oracle*](https://www.oracle.com/database/technologies/releasenote-odbc-ic.html), + - [*Snowflake*](https://docs.snowflake.com/en/developer-guide/odbc/odbc), + - [*BigQuery*](https://cloud.google.com/bigquery/docs/reference/odbc-jdbc-drivers), + - [*IBM DB2*](https://www.ibm.com/support/pages/db2-odbc-cli-driver-download-and-installation-information), + - [*Trino*](https://docs.starburst.io/clients/odbc/odbc-v2.html), and through it [many other data sources](https://trino.io/docs/current/connector.html) + - Added a new `sqlpage.hmac()` function for cryptographic HMAC (Hash-based Message Authentication Code) operations. + - Create and verify secure signatures for webhooks (Shopify, Stripe, GitHub, etc.) + - Generate tamper-proof tokens for API authentication + - Secure download links and temporary access codes + - Supports SHA-256 (default) and SHA-512 algorithms + - Output formats: hexadecimal (default) or base64 (e.g., `sha256-base64`) + - See the [function documentation](https://sql-page.com/functions.sql?function=hmac) for detailed examples + - Fixed a slight spacing issue in the list components empty value display. + - Improved performance of setting a variable to a literal value. `SET x = 'hello'` is now executed locally by SQLPage and does not send anything to the database. This completely removes the cost of extracting static values into variables for cleaner SQL files. + - Enable arbitrary precision in the internal representation of numbers. This guarantees zero precision loss when the database returns very large or very small DECIMAL or NUMERIC values. + +## v0.37.1 + - fixed decoding of UUID values + - Fixed handling of NULL values in `sqlpage.link`. They were encoded as the string `'null'` instead of being omitted from the link's parameters. + - Enable submenu autoclosing (on click) in the shell. This is not ideal, but this prevents a bug introduced in v0.36.0 where the page would scroll back to the top when clicking anywhere on the page after navigating from a submenu. The next version will fix this properly. See https://github.com/sqlpage/SQLPage/issues/1011 + - Adopt the new nice visual errors introduced in v0.37.1 for "403 Forbidden" and "429 Too Many Requests" errors. + - Fix a bug in oidc login flows. When two tabs in the same browser initiated a login at the same time, an infinite redirect loop could be triggered. This mainly occured when restoring open tabs after a period of inactivity, often in mobile browsers. + - Multiple small sql parser improvements. + - Adds support for MERGE queries inside CTEs, and MERGE queries with a RETURNING clause. + - https://github.com/apache/datafusion-sqlparser-rs/blob/main/changelog/0.59.0.md + ## v0.37.0 - - We now cryptographically sign the Windows app during releases, which proves the file hasn’t been tampered with. Once the production certificate is active, Windows will show a "verified publisher" and should stop showing screens saying "This app might harm your device", "Windows protected your PC" or "Are you sure you want to run this application ?". + - We now cryptographically sign the Windows app during releases, which proves the file hasn’t been tampered with. Once the production certificate is active, Windows will show a "verified publisher" and should stop showing screens saying "This app might harm your device", "Windows protected your PC" or "Are you sure you want to run this application ?". - Thanks to https://signpath.io for providing us with a windows signing certificate ! - Added a new parameter `encoding` to the [fetch](https://sql-page.com/functions.sql?function=fetch) function: - All [standard web encodings](https://encoding.spec.whatwg.org/#concept-encoding-get) are supported. @@ -27,7 +135,7 @@ - The form component now considers numbers and their string representation as equal when comparing the `value` parameter and the values from the `options` parameter in dropdowns. This makes it easier to use variables (which are always strings) in the value parameter in order to preserve a dropdown field value across page reloads. The following is now valid: - ```sql select 'form' as component; - select + select 'select' as type, true as create_new, true as dropdown, @@ -42,17 +150,17 @@ ## v0.36.0 - added support for the MONEY and SMALLMONEY types in MSSQL. - - include [math functions](https://sqlite.org/lang_mathfunc.html) in the builtin sqlite3 database. + - include [math functions](https://sqlite.org/lang_mathfunc.html) in the builtin sqlite3 database. - the sqlpage binary can now help you create new empty migration files from the command line: ``` ❯ ./sqlpage create-migration my_new_table Migration file created: sqlpage/migrations/20250627095944_my_new_table.sql - ``` + ``` - New [modal](https://sql-page.com/component.sql?component=modal) component - In bar charts: Sort chart categories by name instead of first appearance. This is useful when displaying cumulative bar charts with some series missing data for some x values. - Updated tabler to v1.4 https://github.com/tabler/tabler/releases/tag/%40tabler%2Fcore%401.4.0 - Updated tabler-icons to v3.34 (19 new icons) https://tabler.io/changelog#/changelog/tabler-icons-3.34 - - Added support for partially private sites when using OIDC single sign-on: + - Added support for partially private sites when using OIDC single sign-on: - The same SQLPage application can now have both publicly accessible and private pages accessible to users authenticated with SSO. - This allows easily creating a "log in page" that redirects to the OIDC provider. - See the [configuration](./configuration.md) for `oidc_protected_paths` @@ -136,84 +244,84 @@ ## v0.34 (2025-03-23) ### ✨ Top Features at a Glance -- **Safer deletion flows** in lists -- **Better table styling control** with CSS updates -- **Right-to-Left language support** -- **HTML-enhanced Markdown** in text components -- **Sticky table footers** for better data presentation +- **Safer deletion flows** in lists +- **Better table styling control** with CSS updates +- **Right-to-Left language support** +- **HTML-enhanced Markdown** in text components +- **Sticky table footers** for better data presentation ### 🔒 Security First -#### **POST-based Deletions** -List component's `delete_link` now uses secure POST requests: +#### **POST-based Deletions** +List component's `delete_link` now uses secure POST requests: ```sql SELECT 'list' AS component; SELECT 'Delete me' AS title, 'delete_item.sql?id=77' AS delete_link; ``` *Prevents accidental deletions by web crawlers and follows REST best practices* -#### **Protected Internal Files** -- Files/folders starting with `.` (e.g., `.utils/`) are now inaccessible +#### **Protected Internal Files** +- Files/folders starting with `.` (e.g., `.utils/`) are now inaccessible - Perfect for internal scripts used with `sqlpage.run_sql()` ### 🎨 UI & Component Upgrades -#### **Table Styling Revolution** +#### **Table Styling Revolution** ```css /* Before: .price | After: */ -._col_price { +._col_price { background: #f8f9fa; border-right: 2px solid #dee2e6; } ``` -- New CSS class pattern: `._col_{column_name}` -- Fixes [#830](https://github.com/sqlpage/SQLPage/issues/830) +- New CSS class pattern: `._col_{column_name}` +- Fixes [#830](https://github.com/sqlpage/SQLPage/issues/830) -#### **Column component** +#### **Column component** ```sql SELECT 'columns' AS component; SELECT 'View details' AS title; -- No button shown ``` -- Columns without button text now hide empty buttons +- Columns without button text now hide empty buttons - Cleaner interfaces by default -#### **Sticky Table Footers** +#### **Sticky Table Footers** ```sql -SELECT +SELECT 'table' AS component, true AS freeze_footers; -SELECT +SELECT 'Total' AS label, SUM(price) AS value, true AS _sqlpage_footer; ``` -- Keep summary rows visible during scroll +- Keep summary rows visible during scroll - Use `_sqlpage_footer` on your final data row ### 🌍 Internationalization -#### **Right-to-Left Support** +#### **Right-to-Left Support** ```sql SELECT 'shell' AS component, true AS rtl; ``` -- Enable RTL mode per page via shell component +- Enable RTL mode per page via shell component - Perfect for Arabic, Hebrew, and Persian content ### 📝 Content Handling -#### **Rich Text Power** +#### **Rich Text Power** ```sql SELECT 'text' AS component, '
**Important!** - + New *HTML-enhanced* content. -
' + ' AS unsafe_contents_md; ``` -- New `unsafe_contents_md` allows HTML+Markdown mixing +- New `unsafe_contents_md` allows HTML+Markdown mixing -#### **Base64 Image Support** +#### **Base64 Image Support** ```markdown ![Alt text](data:image/png;base64,iVBORw0KGg...) ``` -- Embed images directly in Markdown fields +- Embed images directly in Markdown fields ### ⚙️ Configuration Tweaks ```json @@ -225,18 +333,18 @@ SELECT 'text' AS component, - **Markdown safety controls** to change markdown rendering settings ### 🐛 Notable Fixes -- **SQL Server** - Fixed TINYINT handling crashes -- **Anchor Links** +- **SQL Server** + Fixed TINYINT handling crashes +- **Anchor Links** Corrected display in tables with fixed headers -- **Form Inputs** +- **Form Inputs** Proper handling of `0` values in number fields ### 💡 Upgrade Guide -1. **CSS Updates** +1. **CSS Updates** Search/replace `.your_column` → `._col_your_column` if you have custom css targetting tables. -2. **Deletion Flows** - Test list components using `delete_link`. +2. **Deletion Flows** + Test list components using `delete_link`. You can now add a check that the request method is POST if you want to forbid deletions by simply loading pages. [View full configuration options →](./configuration.md) @@ -268,8 +376,8 @@ SELECT 'text' AS component, ### 1. Routing & URL Enhancements 🔀 -#### **Clean URLs:** -Access your pages without the extra “.sql” suffix. For instance, if your file is `page.sql`, you can now use either: +#### **Clean URLs:** +Access your pages without the extra “.sql” suffix. For instance, if your file is `page.sql`, you can now use either: | Old URL | New URL | |---|---| @@ -277,7 +385,7 @@ Access your pages without the extra “.sql” suffix. For instance, if your fil Big thanks to [@guspower](https://github.com/guspower) for their contributions! -#### **Complete Routing Rewrite:** +#### **Complete Routing Rewrite:** We overhauled our request routing system for smoother, more predictable routing across every request. --- @@ -286,8 +394,8 @@ We overhauled our request routing system for smoother, more predictable routing #### **sqlpage.fetch (Calling External Services)** -- **HTTP Basic Authentication:** - SQLPage’s `sqlpage.fetch(request)` now supports HTTP Basic Auth. Easily call APIs requiring a username/password. For example: +- **HTTP Basic Authentication:** + SQLPage’s `sqlpage.fetch(request)` now supports HTTP Basic Auth. Easily call APIs requiring a username/password. For example: ```sql SET result = sqlpage.fetch(json_object( @@ -295,10 +403,10 @@ We overhauled our request routing system for smoother, more predictable routing 'username', 'user', 'password', 'pass' )); - ``` + ``` Check out the [[fetch documentation](https://sql-page.com/documentation.sql?component=fetch#component)](https://sql-page.com/documentation.sql?component=fetch#component) for more. -- **Smarter Fetch Errors & Headers Defaults:** +- **Smarter Fetch Errors & Headers Defaults:** Get clearer error messages if your HTTP request definition is off (unknown fields, etc.). Plus, if you omit the `headers` parameter, SQLPage now sends a default User‑Agent header that includes the SQLPage version. - New Functions: [`sqlpage.request_body`](https://sql-page.com/functions.sql?function=request_body) and [`sqlpage.request_body_base64`](https://sql-page.com/functions.sql?function=request_body_base64) @@ -313,86 +421,86 @@ We overhauled our request routing system for smoother, more predictable routing ); ``` -- **New Function: [sqlpage.headers](https://sql-page.com/functions.sql?function=headers):** +- **New Function: [sqlpage.headers](https://sql-page.com/functions.sql?function=headers):** Easily manage and inspect HTTP headers with the brand‑new [`sqlpage.headers`](https://sql-page.com/functions.sql?function=headers) function. ### 3. UI Component Enhancements 🎨 #### **Table & Card Components** -- **Table CSS Fixes:** +- **Table CSS Fixes:** We fixed a bug where table cells weren’t getting the right CSS classes—your tables now align perfectly. -- **Native Number Formatting:** - Numeric values in tables are now automatically formatted to your visitor’s locale with proper thousands separators and decimal points, and sorted numerically. - _Example:_ +- **Native Number Formatting:** + Numeric values in tables are now automatically formatted to your visitor’s locale with proper thousands separators and decimal points, and sorted numerically. + _Example:_ ![Number Formatting Example](https://github.com/user-attachments/assets/ba51a63f-b9ce-4ab2-a6dd-dfa8e22396de) -- **Enhanced Card Layouts:** - Customizing your `card` components is now easier: - - The `embed` property auto‑appends the `_sqlpage_embed` parameter for embeddable fragments. - - When rendering an embedded page, the `shell` component is replaced by `shell-empty` to avoid duplicate headers and metadata. +- **Enhanced Card Layouts:** + Customizing your `card` components is now easier: + - The `embed` property auto‑appends the `_sqlpage_embed` parameter for embeddable fragments. + - When rendering an embedded page, the `shell` component is replaced by `shell-empty` to avoid duplicate headers and metadata. ![Card Layout Example](https://github.com/user-attachments/assets/c5b58402-178a-441e-8966-fd8e341b02bc) #### **Form Component Boosts** -- **Auto‑Submit Forms:** - Set `auto_submit` to true and your form will instantly submit on any field change—ideal for dashboard filters. - *Example:* +- **Auto‑Submit Forms:** + Set `auto_submit` to true and your form will instantly submit on any field change—ideal for dashboard filters. + *Example:* ```sql SELECT 'form' AS component, 'Filter Results' AS title, true AS auto_submit; SELECT 'date' AS name; ``` -- **Dynamic Options for Dropdowns:** - Use `options_source` to load dropdown options dynamically from another SQL file. Perfect for autocomplete with large option sets. - *Example:* +- **Dynamic Options for Dropdowns:** + Use `options_source` to load dropdown options dynamically from another SQL file. Perfect for autocomplete with large option sets. + *Example:* ```sql SELECT 'form' AS component, 'Select Country' AS title, 'countries.sql' AS options_source; SELECT 'country' AS name; ``` -- **Markdown in Field Descriptions:** +- **Markdown in Field Descriptions:** With the new `description_md` property, render markdown in form field descriptions for improved guidance. -- **Improved Header Error Messages:** +- **Improved Header Error Messages:** Now you’ll get more helpful errors if header components (e.g., `json`, `cookie`) are used incorrectly. --- ### 4. Chart, Icons & CSS Updates 📊 -- **ApexCharts Upgrade:** +- **ApexCharts Upgrade:** We updated ApexCharts to [[v4.4.0](https://github.com/apexcharts/apexcharts.js/releases/tag/v4.4.0)](https://github.com/apexcharts/apexcharts.js/releases/tag/v4.4.0) for smoother charts and minor bug fixes. -- **Tabler Icons & CSS:** - Enjoy a refreshed look: - - Tabler Icons are now [[v3.30.0](https://tabler.io/changelog#/changelog/tabler-icons-3.30)](https://tabler.io/changelog#/changelog/tabler-icons-3.30) with many new icons. +- **Tabler Icons & CSS:** + Enjoy a refreshed look: + - Tabler Icons are now [[v3.30.0](https://tabler.io/changelog#/changelog/tabler-icons-3.30)](https://tabler.io/changelog#/changelog/tabler-icons-3.30) with many new icons. - The CSS framework has been upgraded to [[Tabler 1.0.0](https://github.com/tabler/tabler/releases/tag/v1.0.0)](https://github.com/tabler/tabler/releases/tag/v1.0.0) for improved consistency and a sleeker interface. --- ### 5. CSV Import & Error Handling 📥 -- **Enhanced CSV Error Messages:** +- **Enhanced CSV Error Messages:** More descriptive error messages when a CSV import fails (via `copy` and file upload). -- **Postgres CSV Bug Fix:** - A bug that caused subsequent requests to fail after a CSV import error on PostgreSQL is now fixed. +- **Postgres CSV Bug Fix:** + A bug that caused subsequent requests to fail after a CSV import error on PostgreSQL is now fixed. (See [Issue #788](https://github.com/sqlpage/SQLPage/issues/788) for details.) --- ### 6. SQL Parser & Advanced SQL Support 🔍 -**Upgraded SQL Parser ([v0.54](https://github.com/apache/datafusion-sqlparser-rs/blob/main/changelog/0.54.0.md)):** -Our sqlparser is now at [v0.54](https://github.com/apache/datafusion-sqlparser-rs/blob/main/changelog/0.54.0.md), with support for advanced SQL syntax: +**Upgraded SQL Parser ([v0.54](https://github.com/apache/datafusion-sqlparser-rs/blob/main/changelog/0.54.0.md)):** +Our sqlparser is now at [v0.54](https://github.com/apache/datafusion-sqlparser-rs/blob/main/changelog/0.54.0.md), with support for advanced SQL syntax: -- **INSERT...SELECT...RETURNING:** +- **INSERT...SELECT...RETURNING:** ```sql INSERT INTO users (name, email) SELECT :name, :email WHERE :name IS NOT NULL RETURNING 'redirect' AS component, 'user.sql?id=' || id AS link; ``` -- **PostgreSQL’s overlaps operator:** +- **PostgreSQL’s overlaps operator:** ```sql SELECT 'card' AS component, event_name AS title, @@ -403,7 +511,7 @@ Our sqlparser is now at [v0.54](https://github.com/apache/datafusion-sqlparser-r OVERLAPS ($start_filter::timestamp, $end_filter::timestamp); ``` -- **MySQL’s INSERT...SET syntax:** +- **MySQL’s INSERT...SET syntax:** ```sql INSERT INTO users SET name = :name, email = :email; @@ -565,7 +673,7 @@ This is a bugfix release. ### 🤖 Easy APIs - **Enhanced CSV Support**: The [CSV component](https://sql-page.com/component.sql?component=csv) can now create URLs that trigger a CSV download directly on page load. - This finally makes it possible to allow the download of large datasets as CSV - - This makes it possible to create an API that returns data as CSV and can be easily exposed to other software for interoperabily. + - This makes it possible to create an API that returns data as CSV and can be easily exposed to other software for interoperabily. - **Easy [json](https://sql-page.com/component.sql?component=json) APIs** - The json component now accepts a second sql query, and will return the results as a json array in a very resource-efficient manner. This makes it easier and faster than ever to build REST APIs entirely in SQL. - ```sql @@ -594,7 +702,7 @@ This is a bugfix release. ) as dropdown_item from performance; ``` - + ### 📈 Table & Search Improvements - **Initial Search Value**: Pre-fill the search bar with a default value in tables with `initial_search_value`, making it easier to set starting filters. - **Faster Sorting and Searching**: Table filtering and sorting has been entirely rewritten. @@ -623,9 +731,9 @@ This is a bugfix release. - ``` $ sqlpage --help Build data user interfaces entirely in SQL. A web server that takes .sql files and formats the query result using pre-made configurable professional-looking components. - + Usage: sqlpage [OPTIONS] - + Options: -w, --web-root The directory where the .sql files are located -d, --config-dir The directory where the sqlpage.json configuration, the templates, and the migrations are located @@ -658,7 +766,7 @@ This is a bugfix release. - Chart component: fix the labels of pie charts displaying too many decimal places. - ![pie chart](https://github.com/user-attachments/assets/6cc4a522-b9dd-4005-92bc-dc92b16c7293) - You can now create a `404.sql` file anywhere in your SQLPage project to handle requests to non-existing pages. This allows you to create custom 404 pages, or create [nice URLs](https://sql-page.com/your-first-sql-website/custom_urls.sql) that don't end with `.sql`. - - Create if `/folder/404.sql` exists, then it will be called for all URLs that start with `folder` and do not match an existing file. + - Create if `/folder/404.sql` exists, then it will be called for all URLs that start with `folder` and do not match an existing file. - Updated SQL parser to [v0.50.0](https://github.com/sqlparser-rs/sqlparser-rs/blob/main/CHANGELOG.md#0500-2024-08-15) - Support postgres String Constants with Unicode Escapes, like `U&'\2713'`. Fixes https://github.com/sqlpage/SQLPage/discussions/511 - New [big_number](https://sql-page.com/documentation.sql?component=big_number#component) component to display key statistics and indicators in a large, easy-to-read format. Useful for displaying KPIs, metrics, and other important numbers in dashboards and reports. @@ -677,7 +785,7 @@ This is a bugfix release. - Nicer inline code style in markdown. - Fixed `width` attribute in the card component not being respected when the specified width was < 6. - Fixed small inaccuracies in decimal numbers leading to unexpectedly long numbers in the output, such as `0.47000000000000003` instead of `0.47`. -- [chart component](https://sql-page.com/documentation.sql?component=chart#component) +- [chart component](https://sql-page.com/documentation.sql?component=chart#component) - TreeMap charts in the chart component allow you to visualize hierarchical data structures. - Timeline charts allow you to visualize time intervals. - Fixed multiple small display issues in the chart component. @@ -723,9 +831,9 @@ Allow loading javascript ESM modules in the shell component with the new `javasc Added `text` and `post_html` properties to the [html](https://sql-page.com/documentation.sql?component=html#component) component. This allows to include sanitized user-generated content in the middle of custom HTML. ```sql -select +select 'html' as component; -select +select 'Username: ' as html, 'username that will be safely escaped: <"& ' as text, '' as post_html; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3735646f..e6ad47db 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,12 +37,24 @@ cargo build --release The resulting executable will be in `target/release/sqlpage`. +### ODBC build modes + +SQLPage can either be built with an integrated odbc driver manager (static linking), +or depend on having one already installed on the system where it is running (dynamic linking). + +- Dynamic ODBC (default): `cargo build` +- Static ODBC (Linux and MacOS only): `cargo build --features odbc-static` + +Windows comes with ODBC pre-installed; SQLPage cannot statically link to the unixODBC driver manager on windows. + ## Code Style and Linting ### Rust + - Use `cargo fmt` to format your Rust code - Run `cargo clippy` to catch common mistakes and improve code quality - All code must pass the following checks: + ```bash cargo fmt --all -- --check cargo clippy @@ -55,6 +67,7 @@ We use Biome for linting and formatting of the frontend code. ```bash npx @biomejs/biome check . ``` + This will check the entire codebase (html, css, js). ## Testing @@ -80,6 +93,7 @@ cargo test ``` ### End-to-End Tests + We use Playwright for end-to-end testing of dynamic frontend features. Tests are located in [`tests/end-to-end/`](./tests/end-to-end/). Key areas covered include: @@ -103,6 +117,7 @@ npm run test ## Documentation ### Component Documentation + When adding new components, comprehensive documentation is required. Example from a component documentation: ```sql @@ -110,7 +125,7 @@ INSERT INTO component(name, icon, description, introduced_in_version) VALUES ('component_name', 'icon_name', 'Description of the component', 'version'); -- Document all parameters -INSERT INTO parameter(component, name, description, type, top_level, optional) +INSERT INTO parameter(component, name, description, type, top_level, optional) VALUES ('component_name', 'param_name', 'param_description', 'TEXT|BOOLEAN|NUMBER|JSON|ICON|COLOR', false, true); -- Include usage examples @@ -127,6 +142,7 @@ If you are editing an existing component, edit the existing sql documentation fi If you are adding a new component, add a new sql file in the folder, and add the appropriate insert statements above. ### SQLPage Function Documentation + When adding new SQLPage functions, document them using a SQL migrations. Example structure: ```sql @@ -168,6 +184,7 @@ VALUES ( ``` Key elements to include in function documentation: + - Clear description of the function's purpose - Version number where the function was introduced - Appropriate icon @@ -179,11 +196,13 @@ Key elements to include in function documentation: ## Pull Request Process 1. Create a new branch for your feature/fix: + ```bash git checkout -b feature/your-feature-name ``` 2. Make your changes, ensuring: + - All tests pass - Code is properly formatted - New features are documented @@ -199,11 +218,10 @@ git checkout -b feature/your-feature-name - Run frontend linting with Biome - Test against multiple databases (PostgreSQL, MySQL, MSSQL) -5. Wait for review and address any feedback - ## Release Process Releases are automated when pushing tags that match the pattern `v*` (e.g., `v1.0.0`). The CI pipeline will: + - Build and test the code - Create Docker images for multiple architectures - Push images to Docker Hub diff --git a/Cargo.lock b/Cargo.lock index ef000e07..5a97b48a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags", + "bitflags 2.10.0", "bytes", "futures-core", "futures-sink", @@ -21,9 +21,9 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.11.1" +version = "3.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cceded2fb55f3c4b67068fa64962e2ca59614edc5b03167de9ff82ae803da0" +checksum = "7926860314cbe2fb5d1f13731e387ab43bd32bca224e82e6e2db85de0a3dba49" dependencies = [ "actix-codec", "actix-rt", @@ -31,14 +31,14 @@ dependencies = [ "actix-tls", "actix-utils", "base64 0.22.1", - "bitflags", + "bitflags 2.10.0", "brotli 8.0.2", "bytes", "bytestring", - "derive_more 2.0.1", + "derive_more 2.1.1", "encoding_rs", "flate2", - "foldhash", + "foldhash 0.1.5", "futures-core", "h2", "http 0.2.12", @@ -100,7 +100,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e11eb847f49a700678ea2fa73daeb3208061afa2b9d1a8527c03390f4c4a1c6b" dependencies = [ - "darling", + "darling 0.20.11", "parse-size", "proc-macro2", "quote", @@ -124,9 +124,9 @@ dependencies = [ [[package]] name = "actix-rt" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63" dependencies = [ "actix-macros", "futures-core", @@ -162,16 +162,16 @@ dependencies = [ [[package]] name = "actix-tls" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac453898d866cdbecdbc2334fe1738c747b4eba14a677261f2b768ba05329389" +checksum = "6176099de3f58fbddac916a7f8c6db297e021d706e7a6b99947785fee14abe9f" dependencies = [ "actix-rt", "actix-service", "actix-utils", "futures-core", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "impl-more", "pin-project-lite", "rustls-pki-types", @@ -194,9 +194,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.11.0" +version = "4.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea" +checksum = "1654a77ba142e37f049637a3e5685f864514af11fcbc51cb51eb6596afe5b8d6" dependencies = [ "actix-codec", "actix-http", @@ -212,9 +212,9 @@ dependencies = [ "bytestring", "cfg-if", "cookie", - "derive_more 2.0.1", + "derive_more 2.1.1", "encoding_rs", - "foldhash", + "foldhash 0.1.5", "futures-core", "futures-util", "impl-more", @@ -230,7 +230,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "smallvec", - "socket2 0.5.10", + "socket2 0.6.1", "time", "tracing", "url", @@ -263,15 +263,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - [[package]] name = "adler2" version = "2.0.1" @@ -291,7 +282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -299,9 +290,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -328,10 +319,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] -name = "android-tzdata" -version = "0.1.1" +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.10.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "android-properties" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" [[package]] name = "android_system_properties" @@ -344,9 +356,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -359,9 +371,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -374,29 +386,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "argon2" @@ -475,38 +487,27 @@ checksum = "096146020b08dbc4587685b0730a7ba905625af13c65f8028035cdfd69573c91" dependencies = [ "anyhow", "futures", - "http 1.3.1", + "http 1.4.0", "httparse", "log", ] [[package]] name = "async-io" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" dependencies = [ - "async-lock", + "autocfg", "cfg-if", "concurrent-queue", "futures-io", "futures-lite", "parking", "polling", - "rustix 1.0.8", + "rustix 1.1.3", "slab", - "windows-sys 0.60.2", -] - -[[package]] -name = "async-lock" -version = "3.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", + "windows-sys 0.61.2", ] [[package]] @@ -580,7 +581,7 @@ dependencies = [ "async-net", "futures", "futures-rustls", - "http 1.3.1", + "http 1.4.0", "lazy_static", "log", "rustls-pki-types", @@ -611,9 +612,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "awc" -version = "3.7.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e76d68b4f02400c2f9110437f254873e8f265b35ea87352f142bc7c8e878115a" +checksum = "3c170039c11c7f6c0a28f7b3bd4fb0c674cbfa317fabc1560022ad3ec2d69e7c" dependencies = [ "actix-codec", "actix-http", @@ -625,7 +626,7 @@ dependencies = [ "bytes", "cfg-if", "cookie", - "derive_more 2.0.1", + "derive_more 2.1.1", "futures-core", "futures-util", "h2", @@ -645,9 +646,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.13.3" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" +checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" dependencies = [ "aws-lc-sys", "zeroize", @@ -655,32 +656,16 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.30.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" +checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" dependencies = [ - "bindgen", "cc", "cmake", "dunce", "fs_extra", ] -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - [[package]] name = "base16ct" version = "0.2.0" @@ -707,40 +692,38 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] -name = "bindgen" -version = "0.69.5" +name = "bigdecimal" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +checksum = "4d6867f1565b3aad85681f1015055b087fcfd840d6aeee6eee7f2da317603695" dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", - "which", + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", + "serde", + "serde_json", ] [[package]] name = "bitflags" -version = "2.9.3" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -761,6 +744,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + [[package]] name = "blocking" version = "1.6.2" @@ -818,9 +810,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byteorder" @@ -830,52 +822,69 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "bytestring" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" +checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289" dependencies = [ "bytes", ] +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.10.0", + "log", + "polling", + "rustix 0.38.44", + "slab", + "thiserror 1.0.69", +] + [[package]] name = "cc" -version = "1.2.34" +version = "1.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", ] [[package]] -name = "cexpr" -version = "0.6.0" +name = "cesu8" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", @@ -884,22 +893,11 @@ dependencies = [ "windows-link", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" -version = "4.5.46" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", "clap_derive", @@ -907,9 +905,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.46" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", @@ -919,9 +917,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.45" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -931,15 +929,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "cmake" -version = "0.1.54" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" dependencies = [ "cc", ] @@ -950,6 +948,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -961,9 +969,9 @@ dependencies = [ [[package]] name = "config" -version = "0.15.14" +version = "0.15.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4092bf3922a966e2bd74640b80f36c73eaa7251a4fd0fbcda1f8a4de401352" +checksum = "b30fa8254caad766fc03cb0ccae691e14bf3bd72bfff27f72802ce729551b3d6" dependencies = [ "async-trait", "convert_case 0.6.0", @@ -971,8 +979,8 @@ dependencies = [ "pathdiff", "ron", "rust-ini", - "serde", "serde-untagged", + "serde_core", "serde_json", "toml", "winnow", @@ -1020,6 +1028,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.16.2" @@ -1031,6 +1048,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -1047,6 +1074,30 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + [[package]] name = "core2" version = "0.4.0" @@ -1067,9 +1118,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] @@ -1150,13 +1201,19 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ "memchr", ] +[[package]] +name = "cursor-icon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" + [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -1190,8 +1247,18 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", ] [[package]] @@ -1208,22 +1275,47 @@ dependencies = [ "syn", ] +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + [[package]] name = "darling_macro" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", "quote", "syn", ] [[package]] name = "dary_heap" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d2cd9c18b9f454ed67da600630b021a8a80bf33f8c95896ab33aaf1c26b728" +checksum = "06d2e3287df1c007e74221c49ca10a95d557349e54b3a75dc2fb14712c751f04" [[package]] name = "data-encoding" @@ -1258,12 +1350,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -1281,7 +1373,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", "syn", @@ -1312,21 +1404,23 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case 0.10.0", "proc-macro2", "quote", + "rustc_version", "syn", "unicode-xid", ] @@ -1361,9 +1455,15 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + [[package]] name = "displaydoc" version = "0.2.5" @@ -1375,6 +1475,15 @@ dependencies = [ "syn", ] +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + [[package]] name = "dlv-list" version = "0.5.2" @@ -1390,6 +1499,12 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" + [[package]] name = "dunce" version = "1.0.5" @@ -1478,9 +1593,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", "regex", @@ -1507,22 +1622,23 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" dependencies = [ "serde", + "serde_core", "typeid", ] [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1568,11 +1684,17 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "find-msvc-tools" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" + [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -1601,6 +1723,39 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1742,9 +1897,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -1760,34 +1915,22 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.3+wasi-0.2.4", + "wasip2", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - [[package]] name = "group" version = "0.13.0" @@ -1811,7 +1954,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.11.0", + "indexmap 2.12.1", "slab", "tokio", "tokio-util", @@ -1831,7 +1974,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -1845,10 +1988,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] [[package]] name = "hashbrown" @@ -1856,16 +1995,27 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] -name = "hashlink" -version = "0.10.0" +name = "hashbrown" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ - "hashbrown 0.15.5", + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", ] [[package]] @@ -1904,15 +2054,6 @@ dependencies = [ "digest", ] -[[package]] -name = "home" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "http" version = "0.2.12" @@ -1926,12 +2067,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1983,9 +2123,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2007,9 +2147,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -2020,9 +2160,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -2033,11 +2173,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -2048,42 +2187,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -2156,31 +2291,21 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "serde", -] - -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags", - "cfg-if", - "libc", + "serde_core", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -2191,60 +2316,73 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +checksum = "a87d9b8105c23642f50cbbae03d1f75d8422c5cb98ce7ee9271f7ff7505be6b8" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", - "serde", + "serde_core", ] [[package]] name = "jiff-static" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +checksum = "b787bebb543f8969132630c51fd0afab173a86c6abae56ff3b9e5e3e3f9f6e58" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -2325,23 +2463,17 @@ dependencies = [ "spin", ] -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" -version = "0.2.175" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libflate" -version = "2.1.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" +checksum = "e3248b8d211bd23a104a42d81b4fa8bb8ac4a3b75e7a43d85d2c9ccb6179cd74" dependencies = [ "adler32", "core2", @@ -2352,23 +2484,23 @@ dependencies = [ [[package]] name = "libflate_lz77" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" +checksum = "a599cb10a9cd92b1300debcef28da8f70b935ec937f44fcd1b70a7c986a11c5c" dependencies = [ "core2", - "hashbrown 0.14.5", + "hashbrown 0.16.1", "rle-decode-fast", ] [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets 0.53.3", + "windows-link", ] [[package]] @@ -2379,20 +2511,20 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags", + "bitflags 2.10.0", "libc", - "redox_syscall", + "redox_syscall 0.7.0", ] [[package]] name = "libsqlite3-sys" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" +checksum = "95b4103cffefa72eb8428cb6b47d6627161e51c2739fc5e3b734584157bc642a" dependencies = [ "cc", "pkg-config", @@ -2407,15 +2539,15 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "local-channel" @@ -2436,19 +2568,18 @@ checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "markdown" @@ -2472,9 +2603,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "mime" @@ -2505,18 +2636,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.10.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", ] [[package]] @@ -2541,11 +2703,10 @@ dependencies = [ [[package]] name = "num-bigint-dig" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" dependencies = [ - "byteorder", "lazy_static", "libm", "num-integer", @@ -2607,6 +2768,28 @@ dependencies = [ "libm", ] +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "oauth2" version = "5.0.0" @@ -2616,7 +2799,7 @@ dependencies = [ "base64 0.22.1", "chrono", "getrandom 0.2.16", - "http 1.3.1", + "http 1.4.0", "rand 0.8.5", "serde", "serde_json", @@ -2627,12 +2810,235 @@ dependencies = [ ] [[package]] -name = "object" -version = "0.36.7" +name = "objc-sys" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ - "memchr", + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.10.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.10.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.10.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.10.0", + "block2", + "dispatch", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.10.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.10.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.10.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.10.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "odbc-api" +version = "19.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f017d3949731e436bc1bb9a1fbc34197c2f39c588cdcb60d21adb1f8dd3b8514" +dependencies = [ + "atoi", + "log", + "odbc-sys 0.27.4", + "thiserror 2.0.17", + "widestring", + "winit", +] + +[[package]] +name = "odbc-sys" +version = "0.27.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1896e52e97c2f0cf997cc627380f1af1ecb3f6c29ce6175047cd38adaadb46f5" + +[[package]] +name = "odbc-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348f5e1d16a8aa07e9e76fc62f82bf44d94c099c0d291b4b4b7e10574447434c" +dependencies = [ + "unix-odbc", ] [[package]] @@ -2652,9 +3058,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openidconnect" @@ -2667,8 +3073,8 @@ dependencies = [ "dyn-clone", "ed25519-dalek", "hmac", - "http 1.3.1", - "itertools 0.10.5", + "http 1.4.0", + "itertools", "log", "oauth2", "p256", @@ -2699,6 +3105,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "orbclient" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "247ad146e19b9437f8604c21f8652423595cf710ad108af40e77d3ae6e96b827" +dependencies = [ + "libredox", +] + [[package]] name = "ordered-float" version = "2.10.1" @@ -2750,9 +3165,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -2760,15 +3175,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -2802,12 +3217,12 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pem" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ "base64 0.22.1", - "serde", + "serde_core", ] [[package]] @@ -2827,20 +3242,19 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" dependencies = [ "memchr", - "thiserror 2.0.16", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" +checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" dependencies = [ "pest", "pest_generator", @@ -2848,9 +3262,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" +checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" dependencies = [ "pest", "pest_meta", @@ -2861,9 +3275,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" +checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" dependencies = [ "pest", "sha2", @@ -2941,23 +3355,23 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "polling" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.0.8", - "windows-sys 0.60.2", + "rustix 1.1.3", + "windows-sys 0.61.2", ] [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] name = "portable-atomic-util" @@ -2970,9 +3384,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -2993,38 +3407,37 @@ dependencies = [ ] [[package]] -name = "prettyplease" -version = "0.2.37" +name = "primeorder" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ - "proc-macro2", - "syn", + "elliptic-curve", ] [[package]] -name = "primeorder" -version = "0.13.6" +name = "proc-macro-crate" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "elliptic-curve", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -3091,9 +3504,15 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + [[package]] name = "rcgen" version = "0.13.2" @@ -3109,11 +3528,29 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "redox_syscall" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +dependencies = [ + "bitflags 2.10.0", ] [[package]] @@ -3124,23 +3561,23 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", @@ -3149,9 +3586,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -3161,9 +3598,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -3172,15 +3609,15 @@ dependencies = [ [[package]] name = "regex-lite" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" +checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "rfc6979" @@ -3214,21 +3651,23 @@ checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" [[package]] name = "ron" -version = "0.8.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32" dependencies = [ - "base64 0.21.7", - "bitflags", + "bitflags 2.10.0", + "once_cell", "serde", "serde_derive", + "typeid", + "unicode-ident", ] [[package]] name = "rsa" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" dependencies = [ "const-oid", "digest", @@ -3246,27 +3685,14 @@ dependencies = [ [[package]] name = "rust-ini" -version = "0.21.1" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" dependencies = [ "cfg-if", "ordered-multimap", - "trim-in-place", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc_version" version = "0.4.1" @@ -3291,7 +3717,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3300,22 +3726,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags", + "bitflags 2.10.0", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.60.2", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.31" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "aws-lc-rs", "log", @@ -3328,9 +3754,9 @@ dependencies = [ [[package]] name = "rustls-acme" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30fb8ee724c1b72179735e44646bda27838bb72ff7e7eff97c7ac5787b6f4fac" +checksum = "4b49bf42910782ed684d992550c267c98fbe602320d6bb4a6362292791076eed" dependencies = [ "async-io", "async-trait", @@ -3341,22 +3767,22 @@ dependencies = [ "chrono", "futures", "futures-rustls", - "http 1.3.1", + "http 1.4.0", "log", "pem", "rcgen", "serde", "serde_json", - "thiserror 2.0.16", - "webpki-roots 1.0.2", + "thiserror 2.0.17", + "webpki-roots 1.0.4", "x509-parser", ] [[package]] name = "rustls-native-certs" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -3375,18 +3801,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "aws-lc-rs", "ring", @@ -3402,17 +3828,26 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" + +[[package]] +name = "same-file" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3429,9 +3864,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" dependencies = [ "dyn-clone", "ref-cast", @@ -3461,12 +3896,12 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.3.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags", - "core-foundation", + "bitflags 2.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -3474,9 +3909,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -3484,27 +3919,29 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] [[package]] name = "serde-untagged" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" dependencies = [ "erased-serde", "serde", + "serde_core", "typeid", ] @@ -3518,11 +3955,20 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -3531,25 +3977,27 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.143" +version = "1.0.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.12.1", "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] name = "serde_path_to_error" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" dependencies = [ "itoa", "serde", + "serde_core", ] [[package]] @@ -3563,11 +4011,11 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -3584,19 +4032,18 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.0", + "indexmap 2.12.1", "schemars 0.9.0", - "schemars 1.0.4", - "serde", - "serde_derive", + "schemars 1.2.0", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -3604,11 +4051,11 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.14.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ - "darling", + "darling 0.21.3", "proc-macro2", "quote", "syn", @@ -3644,10 +4091,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -3661,6 +4109,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "slab" version = "0.4.11" @@ -3673,6 +4127,15 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + [[package]] name = "socket2" version = "0.5.10" @@ -3685,12 +4148,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3714,8 +4177,9 @@ dependencies = [ [[package]] name = "sqlpage" -version = "0.37.0" +version = "0.41.0" dependencies = [ + "actix-http", "actix-multipart", "actix-rt", "actix-web", @@ -3727,6 +4191,7 @@ dependencies = [ "async-trait", "awc", "base64 0.22.1", + "bigdecimal", "chrono", "clap", "config", @@ -3736,12 +4201,14 @@ dependencies = [ "env_logger", "futures-util", "handlebars", + "hmac", "include_dir", "lambda-web", "libflate", "log", "markdown", "mime_guess", + "odbc-sys 0.28.0", "openidconnect", "password-hash", "percent-encoding", @@ -3751,6 +4218,7 @@ dependencies = [ "rustls-native-certs", "serde", "serde_json", + "sha2", "sqlparser", "sqlx-oldapi", "tokio", @@ -3760,9 +4228,9 @@ dependencies = [ [[package]] name = "sqlparser" -version = "0.58.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec4b661c54b1e4b603b37873a18c59920e4c51ea8ea2cf527d925424dbd4437c" +checksum = "505aa16b045c4c1375bf5f125cce3813d0176325bfe9ffc4a903f423de7774ff" dependencies = [ "log", "sqlparser_derive", @@ -3770,9 +4238,9 @@ dependencies = [ [[package]] name = "sqlparser_derive" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da5fc6819faabb412da764b99d3b713bb55083c11e7e0c00144d386cd6a1939c" +checksum = "028e551d5e270b31b9f3ea271778d9d827148d4287a5d96167b6bb9787f5cc38" dependencies = [ "proc-macro2", "quote", @@ -3781,14 +4249,15 @@ dependencies = [ [[package]] name = "sqlx-core-oldapi" -version = "0.6.47" +version = "0.6.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0446ebe9f8b666275713fc536b569b6b8aa09dc8eecfb5356e358784ccb97c" +checksum = "8b9869b844b6ab5f575c33e29ad579a3c880bc514bb47c4c9991d0dd6979949b" dependencies = [ "ahash", "atoi", "base64 0.22.1", - "bitflags", + "bigdecimal", + "bitflags 2.10.0", "byteorder", "bytes", "chrono", @@ -3810,7 +4279,7 @@ dependencies = [ "hex", "hkdf", "hmac", - "indexmap 2.11.0", + "indexmap 2.12.1", "itoa", "libc", "libsqlite3-sys", @@ -3818,6 +4287,7 @@ dependencies = [ "md-5", "memchr", "num-bigint", + "odbc-api", "once_cell", "paste", "percent-encoding", @@ -3833,20 +4303,20 @@ dependencies = [ "smallvec", "sqlx-rt-oldapi", "stringprep", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio-stream", "tokio-util", "url", "uuid", - "webpki-roots 1.0.2", + "webpki-roots 1.0.4", "whoami", ] [[package]] name = "sqlx-macros-oldapi" -version = "0.6.47" +version = "0.6.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c1613a71e084c0ad6b1c70c96ee1f8efa4ddf2b4d17eeb49aa36c27f6ae00ff" +checksum = "78820a192cc29b877b735c32e1c1a8e51459019b699fff6f5ba86a128fa9ef9d" dependencies = [ "dotenvy", "either", @@ -3864,9 +4334,9 @@ dependencies = [ [[package]] name = "sqlx-oldapi" -version = "0.6.47" +version = "0.6.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe40a0f0fd976ced05bc6756a714d51690254cffefc770d89fa510f22bdaa87" +checksum = "1a74816da5fc417f929012d46ca806381dabca75de303b248519aad466844044" dependencies = [ "sqlx-core-oldapi", "sqlx-macros-oldapi", @@ -3874,9 +4344,9 @@ dependencies = [ [[package]] name = "sqlx-rt-oldapi" -version = "0.6.47" +version = "0.6.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a86565e4e737bd99d215d5c1991b116b4320f9aaccae0868c2d33bc553f87d8d" +checksum = "b9b54748f0bfadc0b3407b4ee576132b4b5ad0730ebec82e0dbec9d0d1a233bc" dependencies = [ "once_cell", "tokio", @@ -3885,9 +4355,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stringprep" @@ -3914,9 +4384,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.106" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -3936,15 +4406,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.21.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", - "rustix 1.0.8", - "windows-sys 0.60.2", + "rustix 1.1.3", + "windows-sys 0.61.2", ] [[package]] @@ -3958,11 +4428,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.17", ] [[package]] @@ -3978,9 +4448,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -3989,9 +4459,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -4004,15 +4474,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -4029,9 +4499,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -4054,29 +4524,26 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -4085,9 +4552,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -4106,9 +4573,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -4120,11 +4587,11 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.5" +version = "0.9.10+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" +checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" dependencies = [ - "serde", + "serde_core", "serde_spanned", "toml_datetime", "toml_parser", @@ -4133,18 +4600,30 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.0" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ - "serde", + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap 2.12.1", + "toml_datetime", + "toml_parser", + "winnow", ] [[package]] name = "toml_parser" -version = "1.0.2" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ "winnow", ] @@ -4178,9 +4657,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -4190,9 +4669,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -4201,19 +4680,13 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] -[[package]] -name = "trim-in-place" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" - [[package]] name = "try-lock" version = "0.2.5" @@ -4228,9 +4701,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd-trie" @@ -4252,30 +4725,30 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-id" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" +checksum = "70ba288e709927c043cbe476718d37be306be53fb1fafecd0dbe36d072be2580" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] [[package]] name = "unicode-properties" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" [[package]] name = "unicode-segmentation" @@ -4289,6 +4762,15 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unix-odbc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26bdaf2156eebadc0dbabec5b2c2a6f92bff5cface28f3f0a367d2ee9aeca0e2" +dependencies = [ + "cc", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -4321,9 +4803,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "js-sys", "wasm-bindgen", @@ -4341,6 +4823,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -4357,10 +4849,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.3+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ "wit-bindgen", ] @@ -4373,35 +4865,35 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" +name = "wasm-bindgen-futures" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4409,64 +4901,62 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "webpki-roots" -version = "0.26.11" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ - "webpki-roots 1.0.2", + "js-sys", + "wasm-bindgen", ] [[package]] name = "webpki-roots" -version = "1.0.2" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "rustls-pki-types", + "webpki-roots 1.0.4", ] [[package]] -name = "which" -version = "4.4.2" +name = "webpki-roots" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.44", + "rustls-pki-types", ] [[package]] @@ -4480,11 +4970,26 @@ dependencies = [ "web-sys", ] +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "windows-core" -version = "0.61.2" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", @@ -4495,9 +5000,9 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -4506,9 +5011,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -4517,28 +5022,37 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -4563,7 +5077,31 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -4584,21 +5122,27 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -4607,9 +5151,15 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" @@ -4619,9 +5169,15 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" @@ -4631,9 +5187,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -4643,9 +5199,15 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" @@ -4655,9 +5217,15 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" @@ -4667,9 +5235,15 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" @@ -4679,9 +5253,15 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" @@ -4691,30 +5271,70 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winit" +version = "0.30.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" +dependencies = [ + "android-activity", + "atomic-waker", + "bitflags 2.10.0", + "block2", + "calloop", + "cfg_aliases", + "concurrent-queue", + "core-foundation 0.9.4", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "orbclient", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix 0.38.44", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "xkbcommon-dl", +] [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" -version = "0.45.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "x509-parser" @@ -4733,11 +5353,30 @@ dependencies = [ "time", ] +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.10.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + [[package]] name = "yaml-rust2" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7" +checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9" dependencies = [ "arraydeque", "encoding_rs", @@ -4755,11 +5394,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -4767,9 +5405,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", @@ -4779,18 +5417,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", @@ -4820,15 +5458,15 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -4837,9 +5475,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -4848,15 +5486,21 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "zmij" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d6085d62852e35540689d1f97ad663e3971fc19cf5eceab364d62c646ea167" + [[package]] name = "zstd" version = "0.13.3" @@ -4877,9 +5521,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.15+zstd.1.5.7" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 46ddb34b..044bef3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sqlpage" -version = "0.37.0" +version = "0.41.0" edition = "2021" description = "Build data user interfaces entirely in SQL. A web server that takes .sql files and formats the query result using pre-made configurable professional-looking components." keywords = ["web", "sql", "framework"] @@ -8,12 +8,7 @@ license = "MIT" homepage = "https://sql-page.com/" repository = "https://github.com/sqlpage/SQLPage" documentation = "https://docs.rs/sqlpage" -include = [ - "/src", - "/README.md", - "/build.rs", - "/sqlpage", -] +include = ["/src", "/README.md", "/build.rs", "/sqlpage"] [profile.superoptimized] inherits = "release" @@ -23,7 +18,7 @@ panic = "abort" codegen-units = 2 [dependencies] -sqlx = { package = "sqlx-oldapi", version = "0.6.47", default-features = false, features = [ +sqlx = { package = "sqlx-oldapi", version = "0.6.51", default-features = false, features = [ "any", "runtime-tokio-rustls", "migrate", @@ -31,8 +26,11 @@ sqlx = { package = "sqlx-oldapi", version = "0.6.47", default-features = false, "postgres", "mysql", "mssql", + "odbc", "chrono", + "bigdecimal", "json", + "uuid", ] } chrono = "0.4.23" actix-web = { version = "4", features = ["rustls-0_23", "cookies"] } @@ -46,15 +44,20 @@ tokio = { version = "1.24.1", features = ["macros", "rt", "process", "sync"] } tokio-stream = "0.1.9" anyhow = "1" serde = "1" -serde_json = { version = "1.0.82", features = ["preserve_order", "raw_value"] } +serde_json = { version = "1.0.82", features = [ + "preserve_order", + "raw_value", + "arbitrary_precision", +] } lambda-web = { version = "0.2.1", features = ["actix4"], optional = true } -sqlparser = { version = "0.58.0", default-features = false, features = [ +sqlparser = { version = "0.60.0", default-features = false, features = [ "std", "visitor", ] } async-stream = "0.3" async-trait = "0.1.61" async-recursion = "1.0.0" +bigdecimal = { version = "0.4.8", features = ["serde-json"] } include_dir = "0.7.2" config = { version = "0.15.4", features = ["json"] } markdown = { version = "1.0.0-alpha.23", features = ["log"] } @@ -64,6 +67,8 @@ actix-web-httpauth = "0.8.0" rand = "0.9.0" actix-multipart = "0.7.2" base64 = "0.22" +hmac = "0.12" +sha2 = "0.10" rustls-acme = "0.14" dotenvy = "0.15.7" csv-async = { version = "1.2.6", features = ["tokio"] } @@ -72,12 +77,22 @@ rustls-native-certs = "0.8.1" awc = { version = "3", features = ["rustls-0_23-webpki-roots"] } clap = { version = "4.5.17", features = ["derive"] } tokio-util = "0.7.12" -openidconnect = { version = "4.0.0", default-features = false } +openidconnect = { version = "4.0.0", default-features = false, features = ["accept-rfc3339-timestamps"] } encoding_rs = "0.8.35" +odbc-sys = { version = "0.28.0", optional = true } + + +[features] +default = [] +odbc-static = ["odbc-sys", "odbc-sys/vendored-unix-odbc"] +lambda-web = ["dep:lambda-web", "odbc-static"] + +[dev-dependencies] +actix-http = "3" [build-dependencies] awc = { version = "3", features = ["rustls-0_23-webpki-roots"] } rustls = "0.23" actix-rt = "2.8" libflate = "2" -futures-util = "0.3.21" +futures-util = "0.3.21" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 625e054d..fb79c864 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,48 +1,19 @@ -FROM --platform=$BUILDPLATFORM rust:1.87-slim AS builder +FROM --platform=$BUILDPLATFORM rust:1.91-slim AS builder + WORKDIR /usr/src/sqlpage ARG TARGETARCH ARG BUILDARCH -RUN apt-get update && \ - if [ "$TARGETARCH" = "$BUILDARCH" ]; then \ - rustup target list --installed > TARGET && \ - echo gcc > LINKER && \ - apt-get install -y gcc libgcc-s1 cmake && \ - cp /lib/*/libgcc_s.so.1 .; \ - elif [ "$TARGETARCH" = "arm64" ]; then \ - echo aarch64-unknown-linux-gnu > TARGET && \ - echo aarch64-linux-gnu-gcc > LINKER && \ - apt-get install -y gcc-aarch64-linux-gnu libgcc-s1-arm64-cross && \ - cp /usr/aarch64-linux-gnu/lib/libgcc_s.so.1 .; \ - elif [ "$TARGETARCH" = "arm" ]; then \ - echo armv7-unknown-linux-gnueabihf > TARGET && \ - echo arm-linux-gnueabihf-gcc > LINKER && \ - apt-get install -y gcc-arm-linux-gnueabihf libgcc-s1-armhf-cross cmake libclang1 && \ - cargo install --force --locked bindgen-cli && \ - echo "-I/usr/lib/gcc-cross/arm-linux-gnueabihf/12/include -I/usr/arm-linux-gnueabihf/include" > BINDGEN_EXTRA_CLANG_ARGS; \ - cp /usr/arm-linux-gnueabihf/lib/libgcc_s.so.1 .; \ - else \ - echo "Unsupported cross compilation target: $TARGETARCH"; \ - exit 1; \ - fi && \ - rustup target add $(cat TARGET) && \ - cargo init . -# Build dependencies (creates a layer that avoids recompiling dependencies on every build) +COPY scripts/ /usr/local/bin/ +RUN cargo init . + +RUN /usr/local/bin/setup-cross-compilation.sh "$TARGETARCH" "$BUILDARCH" + COPY Cargo.toml Cargo.lock ./ -RUN BINDGEN_EXTRA_CLANG_ARGS=$(cat BINDGEN_EXTRA_CLANG_ARGS || true) \ - cargo build \ - --target $(cat TARGET) \ - --config target.$(cat TARGET).linker='"'$(cat LINKER)'"' \ - --profile superoptimized +RUN /usr/local/bin/build-dependencies.sh -# Build the project COPY . . -RUN touch src/main.rs && \ - cargo build \ - --target $(cat TARGET) \ - --config target.$(cat TARGET).linker='"'$(cat LINKER)'"' \ - --profile superoptimized && \ - mv target/$(cat TARGET)/superoptimized/sqlpage sqlpage.bin +RUN /usr/local/bin/build-project.sh FROM busybox:glibc RUN addgroup --gid 1000 --system sqlpage && \ @@ -54,7 +25,8 @@ ENV SQLPAGE_WEB_ROOT=/var/www ENV SQLPAGE_CONFIGURATION_DIRECTORY=/etc/sqlpage WORKDIR /var/www COPY --from=builder /usr/src/sqlpage/sqlpage.bin /usr/local/bin/sqlpage -COPY --from=builder /usr/src/sqlpage/libgcc_s.so.1 /lib/libgcc_s.so.1 +# Provide runtime helper libs in system lib directory for the glibc busybox base +COPY --from=builder /tmp/sqlpage-libs/* /lib/ USER sqlpage COPY --from=builder --chown=sqlpage:sqlpage /usr/src/sqlpage/sqlpage/sqlpage.db sqlpage/sqlpage.db EXPOSE 8080 diff --git a/README.md b/README.md index 051ca3f4..66fcf064 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ SQLpage [![A short video explaining the concept of sqlpage](./docs/sqlpage.gif)](./docs/sqlpage.mp4) -[SQLpage](https://sql-page.com) is an **SQL**-only webapp builder. +[SQLPage](https://sql-page.com) is an **SQL**-only webapp builder. It allows building powerful data-centric user interfaces quickly, by tranforming simple database queries into interactive websites. @@ -128,6 +128,14 @@ select - [PostgreSQL](https://www.postgresql.org/), and other compatible databases such as *YugabyteDB*, *CockroachDB* and *Aurora*. - [MySQL](https://www.mysql.com/), and other compatible databases such as *MariaDB* and *TiDB*. - [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server), and all compatible databases and providers such as *Azure SQL* and *Amazon RDS*. +- Any **ODBC-compatible database**, such as +[*ClickHouse*](https://github.com/ClickHouse/clickhouse-odbc), +[*MongoDB*](https://www.mongodb.com/docs/atlas/data-federation/query/sql/drivers/odbc/connect), +[*DuckDB*](https://duckdb.org/docs/stable/clients/odbc/overview.html), +[*Oracle*](https://www.oracle.com/database/technologies/releasenote-odbc-ic.html), +[*Snowflake*](https://docs.snowflake.com/en/developer-guide/odbc/odbc), +[*BigQuery*](https://cloud.google.com/bigquery/docs/reference/odbc-jdbc-drivers), +[*IBM DB2*](https://www.ibm.com/support/pages/db2-odbc-cli-driver-download-and-installation-information), and many others through their respective ODBC drivers. ## Get started @@ -156,12 +164,12 @@ To run on a server, you can use [the docker image](https://hub.docker.com/r/lova custom components, and migrations (see [configuration.md](./configuration.md)) to `/etc/sqlpage` in the container. - For instance, you can use: - - `docker run -it --name sqlpage -p 8080:8080 --volume "$(pwd)/source:/var/www" --volume "$(pwd)/configuration:/etc/sqlpage:ro" --rm sqlpage/SQLPage` + - `docker run -it --name sqlpage -p 80:8080 --volume "$(pwd)/source:/var/www" --volume "$(pwd)/configuration:/etc/sqlpage:ro" --rm lovasoa/sqlpage` - And place your website in a folder named `source` and your `sqlpage.json` in a folder named `configuration`. - If you want to build your own docker image, taking the raw sqlpage image as a base is not recommended, since it is extremely stripped down and probably won't contain the dependencies you need. Instead, you can take debian as a base and simply copy the sqlpage binary from the official image to your own image: - ```Dockerfile FROM debian:stable-slim - COPY --from=sqlpage/SQLPage:main /usr/local/bin/sqlpage /usr/local/bin/sqlpage + COPY --from=lovasoa/sqlpage:main /usr/local/bin/sqlpage /usr/local/bin/sqlpage ``` We provide compiled binaries only for the x86_64 architecture, but provide docker images for other architectures, including arm64 and armv7. If you want to run SQLPage on a Raspberry Pi or @@ -175,6 +183,29 @@ An alternative for Mac OS users is to use [SQLPage's homebrew package](https://f - In a terminal, run the following commands: - `brew install sqlpage` + +### ODBC Setup + +SQLPage supports ODBC connections to connect to databases that don't have native drivers. +You can skip this section if you want to use one of the built-in database drivers (SQLite, PostgreSQL, MySQL, Microsoft SQL Server). + +Linux and MacOS release binaries conatain a built-in statically linked ODBC driver manager (unixODBC). +You still need to install or provide the database-specific ODBC driver for the database you want to connect to. + +#### Install your ODBC database driver + - [DuckDB](https://duckdb.org/docs/stable/clients/odbc/overview.html) + - [Snowflake](https://docs.snowflake.com/en/developer-guide/odbc/odbc) + - [BigQuery](https://cloud.google.com/bigquery/docs/reference/odbc-jdbc-drivers) + - For other databases, follow your database's official odbc install instructions. + +#### Connect to your database + + - Find your [connection string](https://www.connectionstrings.com/). + - It will look like this: `Driver=/opt/snowflake_odbc/lib/libSnowflake.so;Server=xyz.snowflakecomputing.com;Database=MY_DB;Schema=PUBLIC;UID=my_user;PWD=my_password` + - It must reference the path to the database driver you installed earlier, plus any connection parameter required by the driver itself. Follow the instructions from the driver's own documentation. + - Use it in the [DATABASE_URL configuration option](./configuration.md) + + ## How it works ![architecture diagram](./docs/architecture-detailed.png) @@ -328,4 +359,4 @@ Check out our [Contributing Guide](./CONTRIBUTING.md) for detailed instructions Our windows binaries are digitally signed, so they should be recognized as safe by Windows. Free code signing provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/). [Contributors](https://github.com/sqlpage/SQLPage/graphs/contributors), [Owners](https://github.com/orgs/sqlpage/people?query=role%3Aowner). -This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it \ No newline at end of file +This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it diff --git a/build.rs b/build.rs index a39ea959..3e594482 100644 --- a/build.rs +++ b/build.rs @@ -22,13 +22,17 @@ async fn main() { for h in [ spawn(download_deps(c.clone(), "sqlpage.js")), spawn(download_deps(c.clone(), "sqlpage.css")), - spawn(download_deps(c.clone(), "tabler-icons.svg")), + spawn(download_tabler_icons( + c.clone(), + "https://cdn.jsdelivr.net/npm/@tabler/icons-sprite@3.35.0/dist/tabler-sprite.svg", + )), spawn(download_deps(c.clone(), "apexcharts.js")), spawn(download_deps(c.clone(), "tomselect.js")), spawn(download_deps(c.clone(), "favicon.svg")), ] { h.await.unwrap(); } + set_odbc_rpath(); } fn make_client() -> awc::Client { @@ -171,3 +175,43 @@ fn make_url_path(url: &str) -> PathBuf { ); sqlpage_artefacts.join(filename) } + +async fn download_tabler_icons(client: Rc, sprite_url: &str) { + let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + let icon_map_path = out_dir.join("icons.rs"); + let mut sprite_content = Vec::with_capacity(3 * 1024 * 1024); + copy_url_to_opened_file(&client, sprite_url, &mut sprite_content).await; + let mut file = File::create(icon_map_path).unwrap(); + file.write_all(b"[").unwrap(); + extract_icons_from_sprite(&sprite_content, |name, content| { + writeln!(file, "({name:?}, r#\"{content}\"#),").unwrap(); + }); + file.write_all(b"]").unwrap(); +} + +fn extract_icons_from_sprite(sprite_content: &[u8], mut callback: impl FnMut(&str, &str)) { + let mut sprite_str = std::str::from_utf8(sprite_content).unwrap(); + fn take_between<'a>(s: &mut &'a str, start: &str, end: &str) -> Option<&'a str> { + let start_index = s.find(start)?; + let end_index = s[start_index + start.len()..].find(end)?; + let result = &s[start_index + start.len()..][..end_index]; + *s = &s[start_index + start.len() + end_index + end.len()..]; + Some(result) + } + while let Some(mut symbol_tag) = take_between(&mut sprite_str, "") { + let id = take_between(&mut symbol_tag, "id=\"tabler-", "\"").expect("id not found"); + let content_start = symbol_tag.find('>').unwrap() + 1; + callback(id, &symbol_tag[content_start..]); + } +} + +/// On debian-based linux distributions, odbc drivers are installed in /usr/lib/-linux-gnu/odbc +/// which is not in the default library search path. +fn set_odbc_rpath() { + if cfg!(all(target_os = "linux", feature = "odbc-static")) { + println!( + "cargo:rustc-link-arg=-Wl,-rpath,/usr/lib/{}-linux-gnu/odbc", + std::env::var("TARGET").unwrap().split('-').next().unwrap() + ); + } +} diff --git a/configuration.md b/configuration.md index 2fd78166..faa15d79 100644 --- a/configuration.md +++ b/configuration.md @@ -33,7 +33,7 @@ Here are the available configuration options and their default values: | `oidc_scopes` | openid email profile | Space-separated list of [scopes](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) your app requests from the OIDC provider. | | `oidc_additional_trusted_audiences` | unset | A list of additional audiences that are allowed in JWT tokens, beyond the client ID. When empty or unset, any additional audience is accepted. For increased security, set to an empty list `[]` to only allow the client ID as audience. | | `max_pending_rows` | 256 | Maximum number of rendered rows that can be queued up in memory when a client is slow to receive them. | -| `compress_responses` | true | When the client supports it, compress the http response body. This can save bandwidth and speed up page loading on slow connections, but can also increase CPU usage and cause rendering delays on pages that take time to render (because streaming responses are buffered for longer than necessary). | +| `compress_responses` | false | When the client supports it, compress the http response body. This can save bandwidth and speed up page loading on slow connections, but can also increase CPU usage and cause rendering delays on pages that take time to render (because streaming responses are buffered for longer than necessary). | | `https_domain` | | Domain name to request a certificate for. Setting this parameter will automatically make SQLPage listen on port 443 and request an SSL certificate. The server will take a little bit longer to start the first time it has to request a certificate. | | `https_certificate_email` | contact@ | The email address to use when requesting a certificate. | | `https_certificate_cache_dir` | ./sqlpage/https | A writeable directory where to cache the certificates, so that SQLPage can serve https traffic immediately when it restarts. | @@ -66,7 +66,7 @@ If you have a `.env` file in the current directory or in any of its parent direc The `database_url` parameter sets all the connection parameters for the database, including - - the database engine type (`sqlite`, `postgres`, `mysql`, `mssql`, etc.) + - the database engine type (`sqlite`, `postgres`, `mysql`, `mssql`, or ODBC connection strings) - the username and password - the host (or ip adress) and port - the database name @@ -87,6 +87,26 @@ A full connection string for a PostgreSQL database might look like this: postgres://my_user:p%40ss@localhost:5432/my_database?sslmode=verify-ca&sslrootcert=/path/to/ca.pem&sslcert=/path/to/cert.pem&sslkey=/path/to/key.pem&application_name=my_application ``` +#### ODBC Connection Strings + +For ODBC-compatible databases (Oracle, Snowflake, BigQuery, IBM DB2, etc.), you can use ODBC connection strings directly: + +```bash +# Using a Data Source Name (DSN) +DATABASE_URL="DSN=MyDatabase" + +# Using inline connection parameters +DATABASE_URL="Driver={PostgreSQL};Server=localhost;Port=5432;Database=mydb;UID=myuser;PWD=mypassword" + +# Oracle example +DATABASE_URL="Driver={Oracle ODBC Driver};Server=localhost:1521/XE;UID=hr;PWD=password" + +# Snowflake example +DATABASE_URL="Driver={SnowflakeDSIIDriver};Server=account.snowflakecomputing.com;Database=mydb;UID=user;PWD=password" +``` + +ODBC drivers must be installed and configured on your system. On Linux, the `unixODBC` driver manager is statically linked into the SQLPage binary, so you usually only need to install and configure the database-specific ODBC driver for your target database (for example Snowflake, Oracle, DuckDB...). + If the `database_password` configuration parameter is set, it will override any password specified in the `database_url`. It does not need to be percent-encoded. This allows you to keep the password separate from the connection string, which can be useful for security purposes, especially when storing configurations in version control systems. @@ -124,7 +144,7 @@ Example configuration: - **Microsoft Entra ID** (formerly Azure AD) - Documentation: https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app - - Set *oidc_issuer_url* to `https://login.microsoftonline.com/{tenant}/v2.0` + - Set *oidc_issuer_url* to `https://login.microsoftonline.com/{tenant}/v2.0` - ([Find your tenant name](https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols-oidc#find-your-apps-openid-configuration-document-uri)) - **GitHub** @@ -173,7 +193,7 @@ For instance, if you want to create a custom `my_component` component, that disp ## Directories -SQLPage needs two important directories to work: the configuration directory, and the web root. +SQLPage needs two important directories to work: the configuration directory, and the web root. ### Configuration directory @@ -235,7 +255,7 @@ This can be useful to clean up temporary tables, rollback transactions that were left open, or other resources that were created during the request. -You can also use this script to close database connections that are +You can also use this script to close database connections that are in an undesirable state, such as being in a transaction that was left open. To close a connection, write a select statement that returns a single row with a single boolean column named `is_healthy`, and set it to false. diff --git a/docker-compose.yml b/docker-compose.yml index 561fc39c..b67355d9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,7 @@ # DATABASE_URL='postgres://root:Password123!@localhost/sqlpage' # DATABASE_URL='mssql://root:Password123!@localhost/sqlpage' # DATABASE_URL='mysql://root:Password123!@localhost/sqlpage' +# DATABASE_URL='Driver={/usr/lib64/psqlodbcw.so};Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!' # Run for instance: # docker compose up postgres @@ -59,4 +60,4 @@ services: image: mariadb environment: MYSQL_ROOT_PASSWORD: Password123! - MYSQL_DATABASE: sqlpage \ No newline at end of file + MYSQL_DATABASE: sqlpage diff --git a/examples/nginx/README.md b/examples/nginx/README.md index e1b838b2..46f38ee8 100644 --- a/examples/nginx/README.md +++ b/examples/nginx/README.md @@ -43,6 +43,26 @@ This service sets up a MySQL database with predefined credentials and a persiste The `nginx.conf` file contains the NGINX configuration: +### Streaming and compression + +SQLPage streams HTML as it is generated, so browsers can start rendering before the database finishes returning rows. NGINX enables `proxy_buffering` by default, which can delay those first bytes but stores responses for slow clients. Start with a modest buffer configuration and let the proxy handle compression: + +``` + proxy_buffering on; + proxy_buffer_size 16k; + proxy_buffers 4 16k; + + gzip on; + gzip_buffers 2 4k; + gzip_types text/html text/plain text/css application/javascript application/json; + + chunked_transfer_encoding on; +``` + +Keep buffering when you expect slow clients or longer SQLPage queries, increasing the buffer sizes only if responses overflow. When most users are on fast connections reading lightweight pages, consider reducing the buffer counts or flipping to `proxy_buffering off;` to minimise latency, accepting the extra load on SQLPage. See the [proxy buffering](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering), [gzip](https://nginx.org/en/docs/http/ngx_http_gzip_module.html), and [chunked transfer](https://nginx.org/en/docs/http/ngx_http_core_module.html#chunked_transfer_encoding) directives for more guidance. + +When SQLPage runs behind a reverse proxy, set `compress_responses` to `false` in its configuration (documented [here](https://github.com/sqlpage/SQLPage/blob/main/configuration.md)) so that NGINX can perform compression once at the edge. + ### Rate Limiting diff --git a/examples/official-site/assets/db-bigquery.svg b/examples/official-site/assets/db-bigquery.svg new file mode 100644 index 00000000..21fbe250 --- /dev/null +++ b/examples/official-site/assets/db-bigquery.svg @@ -0,0 +1 @@ +Google BigQuery diff --git a/examples/official-site/assets/db-clickhouse.svg b/examples/official-site/assets/db-clickhouse.svg new file mode 100644 index 00000000..82d7d981 --- /dev/null +++ b/examples/official-site/assets/db-clickhouse.svg @@ -0,0 +1 @@ +ClickHouse diff --git a/examples/official-site/assets/db-databricks.svg b/examples/official-site/assets/db-databricks.svg new file mode 100644 index 00000000..129ea2ac --- /dev/null +++ b/examples/official-site/assets/db-databricks.svg @@ -0,0 +1 @@ +Databricks diff --git a/examples/official-site/assets/db-db2.svg b/examples/official-site/assets/db-db2.svg new file mode 100644 index 00000000..02811396 --- /dev/null +++ b/examples/official-site/assets/db-db2.svg @@ -0,0 +1 @@ +IBM \ No newline at end of file diff --git a/examples/official-site/assets/db-duckdb.svg b/examples/official-site/assets/db-duckdb.svg new file mode 100644 index 00000000..7e590871 --- /dev/null +++ b/examples/official-site/assets/db-duckdb.svg @@ -0,0 +1 @@ +DuckDB diff --git a/examples/official-site/assets/db-mysql.svg b/examples/official-site/assets/db-mysql.svg new file mode 100644 index 00000000..e1606ffd --- /dev/null +++ b/examples/official-site/assets/db-mysql.svg @@ -0,0 +1 @@ +MySQL diff --git a/examples/official-site/assets/db-odbc.svg b/examples/official-site/assets/db-odbc.svg new file mode 100644 index 00000000..b364b5a6 --- /dev/null +++ b/examples/official-site/assets/db-odbc.svg @@ -0,0 +1 @@ +ODBCODBC diff --git a/examples/official-site/assets/db-oracle.svg b/examples/official-site/assets/db-oracle.svg new file mode 100644 index 00000000..1e41072f --- /dev/null +++ b/examples/official-site/assets/db-oracle.svg @@ -0,0 +1 @@ +Oracle \ No newline at end of file diff --git a/examples/official-site/assets/db-postgres.svg b/examples/official-site/assets/db-postgres.svg new file mode 100644 index 00000000..d7ccd9e3 --- /dev/null +++ b/examples/official-site/assets/db-postgres.svg @@ -0,0 +1 @@ +PostgreSQL diff --git a/examples/official-site/assets/db-snowflake.svg b/examples/official-site/assets/db-snowflake.svg new file mode 100644 index 00000000..b62af544 --- /dev/null +++ b/examples/official-site/assets/db-snowflake.svg @@ -0,0 +1 @@ +Snowflake diff --git a/examples/official-site/assets/db-sqlite.svg b/examples/official-site/assets/db-sqlite.svg new file mode 100644 index 00000000..e6e77901 --- /dev/null +++ b/examples/official-site/assets/db-sqlite.svg @@ -0,0 +1 @@ +SQLite diff --git a/examples/official-site/assets/db-sqlserver.svg b/examples/official-site/assets/db-sqlserver.svg new file mode 100644 index 00000000..ecb4c222 --- /dev/null +++ b/examples/official-site/assets/db-sqlserver.svg @@ -0,0 +1 @@ +Microsoft SQL Server \ No newline at end of file diff --git a/examples/official-site/blog/pagination.png b/examples/official-site/blog/pagination.png new file mode 100644 index 00000000..4c4137ea Binary files /dev/null and b/examples/official-site/blog/pagination.png differ diff --git a/examples/official-site/component.sql b/examples/official-site/component.sql index f482d50e..c10943f3 100644 --- a/examples/official-site/component.sql +++ b/examples/official-site/component.sql @@ -75,7 +75,14 @@ select group_concat( 'select ' || char(10) || ( - with t as (select * from json_tree(top.value)), + with t as ( + select *, + case type + when 'array' then json_array_length(value)>1 + else false + end as is_arr + from json_tree(top.value) + ), key_val as (select CASE t.type WHEN 'integer' THEN t.atom @@ -92,8 +99,8 @@ select ELSE parent.key END as key from t inner join t parent on parent.id = t.parent - where ((parent.fullkey = '$' and t.type != 'array') - or (parent.type = 'array' and parent.path = '$')) + where ((parent.fullkey = '$' and not t.is_arr) + or (parent.path = '$' and parent.is_arr)) ), key_val_padding as (select CASE @@ -142,6 +149,6 @@ select select name as title, icon, - sqlpage.link('component.sql', json_object('component', name)) as link + sqlpage.set_variable('component', name) as link from component order by name; \ No newline at end of file diff --git a/examples/official-site/documentation.sql b/examples/official-site/documentation.sql index b4bbed93..e848f351 100644 --- a/examples/official-site/documentation.sql +++ b/examples/official-site/documentation.sql @@ -12,11 +12,11 @@ FROM example WHERE component = 'shell' LIMIT 1; select 'text' as component, format('SQLPage v%s documentation', sqlpage.version()) as title; select ' -If you are completely new to SQLPage, you should start by reading the [get started tutorial](get%20started.sql), +If you are completely new to SQLPage, you should start by reading the [get started tutorial](/your-first-sql-website/), which will guide you through the process of creating your first SQLPage application. Building an application with SQLPage is quite simple. -To create a new web page, just create a new SQL file. +To create a new web page, just create a new SQL file. For each SELECT statement that you write, the data it returns will be analyzed and rendered to the user. The two most important concepts in SQLPage are **components** and **parameters**. @@ -24,7 +24,7 @@ The two most important concepts in SQLPage are **components** and **parameters** - *top-level* **parameters** are the properties of these components, allowing you to customize their appearance and behavior. - *row-level* **parameters** constitute the data that you want to display in the components. -To select a component and set its top-level properties, you write the following SQL statement: +To select a component and set its top-level properties, you write the following SQL statement: ```sql SELECT ''component_name'' AS component, ''my value'' AS top_level_parameter_1; @@ -38,6 +38,9 @@ SELECT my_column_1 AS row_level_parameter_1, my_column_2 AS row_level_parameter_ This page documents all the components provided by default in SQLPage and their parameters. Use this as a reference when building your SQL application. +For more information about SQLPage variables and [SQLPage functions](/functions), +read about [the SQLPage data model](/extensions-to-sql). + If at any point you need help, you can ask for it on the [SQLPage forum](https://github.com/sqlpage/SQLPage/discussions). If you know some [HTML](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics), diff --git a/examples/official-site/examples/authentication/create_session_token.sql b/examples/official-site/examples/authentication/create_session_token.sql index 645fedc4..8ea8cd19 100644 --- a/examples/official-site/examples/authentication/create_session_token.sql +++ b/examples/official-site/examples/authentication/create_session_token.sql @@ -4,12 +4,12 @@ delete from user_sessions where created_at < datetime('now', '-1 day'); -- check that the SELECT 'authentication' AS component, 'login.sql?failed' AS link, -- redirect to the login page on error - (SELECT password_hash FROM users WHERE username = :Username) AS password_hash, -- this is a hash of the password 'admin' - :Password AS password; -- this is the password that the user sent through our form in 'index.sql' + (SELECT password_hash FROM users WHERE username = :username) AS password_hash, -- this is a hash of the password 'admin' + :password AS password; -- this is the password that the user sent through our form in 'index.sql' -- if we haven't been redirected, then the password is correct -- create a new session -insert into user_sessions (session_token, username) values (sqlpage.random_string(32), :Username) +insert into user_sessions (session_token, username) values (sqlpage.random_string(32), :username) returning 'cookie' as component, 'session_token' as name, session_token as value; -- redirect to the authentication example home page diff --git a/examples/official-site/examples/authentication/login.sql b/examples/official-site/examples/authentication/login.sql index b634de30..b0a39f5b 100644 --- a/examples/official-site/examples/authentication/login.sql +++ b/examples/official-site/examples/authentication/login.sql @@ -1,10 +1,15 @@ select 'dynamic' as component, properties FROM example WHERE component = 'shell' LIMIT 1; -select 'form' as component, 'Authentication' as title, 'Log in' as validate, 'create_session_token.sql' as action; -select 'Username' as name, 'user' as prefix_icon, 'admin' as placeholder; -select 'Password' as name, 'lock' as prefix_icon, 'admin' as placeholder, 'password' as type; - -select 'alert' as component, 'danger' as color, 'Invalid username or password' as title where $failed is not null; +select + 'login' as component, + 'create_session_token.sql' as action, + '/assets/icon.webp' as image, + 'Demo Login Form' as title, + 'Username' as username, + 'Password' as password, + case when $failed is not null then 'Invalid username or password. In this demo, you can log in with admin / admin.' end as error_message, + 'In this demo, the username is "admin" and the password is "admin".' as footer_md, + 'Log in' as validate; select 'text' as component, ' @@ -12,7 +17,7 @@ select 'text' as component, ' This is a simple example of an authentication form. It uses - - the [`form`](/documentation.sql?component=form#component) component to create a login form + - the [`login`](/documentation.sql?component=login#component) component to create a login form - the [`authentication`](/documentation.sql?component=authentication#component) component to check the user password - the [`cookie`](/documentation.sql?component=cookie#component) component to store a unique session token in the user browser - the [`redirect`](/documentation.sql?component=redirect#component) component to redirect the user to the login page if they are not logged in diff --git a/examples/official-site/examples/form.sql b/examples/official-site/examples/form.sql index aef25559..913074e5 100644 --- a/examples/official-site/examples/form.sql +++ b/examples/official-site/examples/form.sql @@ -1,8 +1,92 @@ -select - 'form' as component, - 'Save' as validate; -select - 'username' as name; -select - 'password' as name, - 'password' as type; \ No newline at end of file +select 'shell' as component, 'dark' as theme, '[View source on Github](https://github.com/sqlpage/SQLPage/blob/main/examples/official-site/examples/form.sql)' as footer; + +SELECT 'form' AS component, 'Complete Input Types Reference' AS title, '/examples/show_variables.sql' as action; + +SELECT 'header' AS type, 'Text Input Types' AS label; + +SELECT 'username' AS name, 'text' AS type, 'Enter your username' AS placeholder, + '**Text** - Default single-line text input. Use for short text like names, usernames, titles. Supports `minlength`, `maxlength`, `pattern` for validation.' AS description_md; + +SELECT 'password' AS name, 'password' AS type, '^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$' AS pattern, + '**Password** - Masked text input that hides characters. Use for passwords and sensitive data. Combine with `pattern` attribute for password strength requirements.' AS description_md; + +SELECT 'search_query' AS name, 'search' AS type, 'Search...' AS placeholder, + '**Search** - Search input field, may display a clear button. Use for search boxes. Mobile browsers may show optimized keyboard.' AS description_md; + +SELECT 'bio' AS name, 'textarea' AS type, 5 AS rows, 'Tell us about yourself...' AS placeholder, + '**Textarea** (SQLPage custom) - Multi-line text input. Use for long text like comments, descriptions, articles. Set `rows` to control initial height.' AS description_md; + +SELECT 'header' AS type, 'Numeric Input Types' AS label; + +SELECT 'age' AS name, 'number' AS type, 0 AS min, 120 AS max, 1 AS step, + '**Number** - Numeric input with up/down arrows. Use for quantities, ages, counts. Supports `min`, `max`, `step`. Mobile shows numeric keyboard.' AS description_md; + +SELECT 'price' AS name, 'number' AS type, 0.01 AS step, '$' AS prefix, + '**Number with decimals** - Set `step="0.01"` for currency. Use `prefix`/`suffix` for units. Great for prices, measurements, percentages.' AS description_md; + +SELECT 'volume' AS name, 'range' AS type, 0 AS min, 100 AS max, 50 AS value, 1 AS step, + '**Range** - Slider control for selecting a value. Use for volume, brightness, ratings, or any bounded numeric value where precision isn''t critical.' AS description_md; + +SELECT 'header' AS type, 'Date and Time Types' AS label; + +SELECT 'birth_date' AS name, 'date' AS type, + '**Date** - Date picker (year, month, day). Use for birthdays, deadlines, event dates. Most browsers show a calendar widget. Supports `min` and `max` for date ranges.' AS description_md; + +SELECT 'appointment_time' AS name, 'time' AS type, + '**Time** - Time picker (hours and minutes). Use for appointment times, opening hours, alarms. Shows time selector in supported browsers.' AS description_md; + +SELECT 'meeting_datetime' AS name, 'datetime-local' AS type, + '**Datetime-local** - Date and time picker without timezone. Use for scheduling events, booking appointments, logging timestamps in local time.' AS description_md; + +SELECT 'birth_month' AS name, 'month' AS type, + '**Month** - Month and year picker. Use for credit card expiration dates, monthly reports, subscription periods.' AS description_md; + +SELECT 'vacation_week' AS name, 'week' AS type, + '**Week** - Week and year picker. Use for week-based scheduling, timesheet entry, weekly reports.' AS description_md; + +SELECT 'header' AS type, 'Contact Information Types' AS label; + +SELECT 'user_email' AS name, 'email' AS type, 'user@example.com' AS placeholder, + '**Email** - Email address input with built-in validation. Use for email fields. Browser validates format automatically. Mobile shows @ key on keyboard.' AS description_md; + +SELECT 'phone' AS name, 'tel' AS type, '+1 (555) 123-4567' AS placeholder, + '**Tel** - Telephone number input. Use for phone numbers. Mobile browsers show numeric keyboard with phone symbols. No automatic validation - use `pattern` if needed.' AS description_md; + +SELECT 'website' AS name, 'url' AS type, 'https://example.com' AS placeholder, + '**URL** - URL input with validation. Use for website addresses, links. Browser validates URL format. Mobile may show .com key on keyboard.' AS description_md; + +SELECT 'header' AS type, 'Selection Types' AS label; + +SELECT 'country' AS name, 'select' AS type, + '[{"label": "United States", "value": "US"}, {"label": "Canada", "value": "CA"}, {"label": "United Kingdom", "value": "GB"}]' AS options, + '**Select** (SQLPage custom) - Dropdown menu. Use for single choice from many options. Add `multiple` for multi-select. Use `searchable` for long lists. Set `dropdown` for enhanced UI.' AS description_md; + +SELECT 'gender' AS name, 'radio' AS type, 'Male' AS value, 'Male' AS label, + '**Radio** - Radio button for mutually exclusive choices. Create multiple rows with same `name` for a radio group. One option can be selected. Use for 2-5 options.' AS description_md; + +SELECT 'gender' AS name, 'radio' AS type, 'Female' AS value, 'Female' AS label; + +SELECT 'gender' AS name, 'radio' AS type, 'Other' AS value, 'Other' AS label; + +SELECT 'interests' AS name, 'checkbox' AS type, 'Technology' AS value, 'Technology' AS label, + '**Checkbox** - Checkbox for multiple selections. Each checkbox is independent. Use for yes/no questions or multiple selections from a list.' AS description_md; + +SELECT 'terms' AS name, 'checkbox' AS type, TRUE AS required, 'I accept the terms and conditions' AS label, + '**Checkbox (required)** - Use `required` to make acceptance mandatory. Common for terms of service, privacy policies, consent forms.' AS description_md; + +SELECT 'notifications' AS name, 'switch' AS type, 'Enable email notifications' AS label, TRUE AS checked, + '**Switch** (SQLPage custom) - Toggle switch, styled checkbox alternative. Use for on/off settings, feature toggles, preferences. More intuitive than checkboxes for boolean settings.' AS description_md; + +SELECT 'header' AS type, 'File and Media Types' AS label; + +SELECT 'profile_picture' AS name, 'file' AS type, 'image/*' AS accept, + '**File** - File upload control. Use `accept` to limit file types (image/\*, .pdf, .doc). Use `multiple` to allow multiple files. Automatically sets form enctype to multipart/form-data.' AS description_md; + +SELECT 'documents[]' AS name, 'Documents' as label, 'file' AS type, '.pdf,.doc,.docx' AS accept, TRUE AS multiple, + '**File (multiple)** - Allow multiple file uploads with `multiple` attribute. Specify exact extensions or MIME types in `accept`.' AS description_md; + +SELECT 'favorite_color' AS name, 'color' AS type, '#3b82f6' AS value, + '**Color** - Color picker. Use for theme customization, design settings, highlighting preferences. Returns hex color code (#RRGGBB).' AS description_md; + +SELECT 'user_id' AS name, 'hidden' AS type, '12345' AS value, + '**Hidden** - Hidden input, not visible to users. Use for IDs, tokens, state information that needs to be submitted but not displayed or edited.' AS description_md; diff --git a/examples/official-site/examples/layouts.sql b/examples/official-site/examples/layouts.sql index 13e81750..1169d771 100644 --- a/examples/official-site/examples/layouts.sql +++ b/examples/official-site/examples/layouts.sql @@ -30,7 +30,7 @@ For more information on how to use layouts, see the [shell component documentati select 'list' as component, 'Available SQLPage shell layouts' as title; select column1 as title, - sqlpage.link('', json_object('layout', lower(column1), 'sidebar', $sidebar)) as link, + sqlpage.set_variable('layout', lower(column1)) as link, $layout = lower(column1) as active, column3 as icon, column2 as description @@ -43,7 +43,7 @@ from (VALUES select 'list' as component, 'Available Menu layouts' as title; select column1 as title, - sqlpage.link('', json_object('layout', $layout, 'sidebar', column1 = 'Sidebar')) as link, + sqlpage.set_variable('sidebar', column1 = 'Sidebar') as link, (column1 = 'Sidebar' AND $sidebar = 1) OR (column1 = 'Horizontal' AND $sidebar = 0) as active, column2 as description, column3 as icon diff --git a/examples/official-site/extensions-to-sql.md b/examples/official-site/extensions-to-sql.md index 81ee0902..36eb57af 100644 --- a/examples/official-site/extensions-to-sql.md +++ b/examples/official-site/extensions-to-sql.md @@ -1,257 +1,281 @@ -# Extensions to SQL +## How SQLPage runs your SQL -SQLPage makes some special treatment before executing your SQL queries. +SQLPage reads your SQL file and runs one statement at a time. For each statement, it -When executing your SQL file, SQLPage executes each query one at a time. -It doesn't send the whole file as-is to the database engine. +- decides whether to: + - handle it inside SQLPage, or + - prepare it as a (potentially slightly modified) sql statement on the database. +- extracts values from the request to pass them as prepared statements parameters +- runs [`sqlpage.*` functions](/functions) +- passes the database results to components -## Performance +This page explains every step of the process, +with examples and details about differences between how SQLPage understands SQL and how your database does. -See the [performance page](/performance.sql) for details on the optimizations -made to run your queries as fast as possible. +## What runs where -## Variables +### Handled locally by SQLPage -SQL doesn't have its own mechanism for variables. -SQLPage implements variables in the following way: +- Static simple selects (a tiny, fast subset of SELECT) +- Simple variable assignments that use only literals or variables + - All sqlpage functions + -### POST parameters +### Sent to your database -When sending a POST request, most often by sending a form with the -[form component](/component.sql?component=form), the form data is made -available as variables prefixed by a colon. +Everything else: joins, subqueries, arithmetic, database functions, `SELECT @@VERSION`, `CURRENT_TIMESTAMP`, `SELECT *`, expressions, `FROM`, `WHERE`, `GROUP BY`, `ORDER BY`, `LIMIT`/`FETCH`, `WITH`, `DISTINCT`, etc. -So when this form is sent: +### Mixed statements using `sqlpage.*` functions -`form.sql` -```sql -SELECT - 'form' AS component, - 'POST' AS method, -- form defaults to using the HTTP POST method - 'result.sql' AS action; +[`sqlpage.*` functions](/functions.sql) are executed by SQLPage; your database never sees them. They can run: -SELECT - 'age' AS name, - 'How old are you?' AS label, - 'number' AS type; -``` +- Before the query, when used as values inside conditions or parameters. +- After the query, when used as top-level selected columns (applied per row). -It will make a request to this page: +Examples are shown below. -`result.sql` -```sql -SELECT - 'text' AS component, - 'You are ' || :age || ' years old!' AS contents; -``` +## Static simple selects -`:age` will be substituted by the actual value of the POST parameter. +A *static simple select* is a very restricted `SELECT` that SQLPage can execute entirely by itself. This avoids back and forths between SQLPage and the database for trivial queries. -### URL parameters +To be static and simple, a statement must satisfy all of the following: -Likewise, URL parameters are available as variables prefixed by a dollar sign. +- No `FROM`, `WHERE`, `GROUP BY`, `HAVING`, `ORDER BY`, `LIMIT`/`FETCH`, `WITH`, `DISTINCT`, `TOP`, windowing, locks, or other clauses. +- Each selected item is of the form `value AS alias`. +- Each `value` is either: + - a literal (single-quoted string, number, boolean, or `NULL`), or + - a variable (like `$name`, `:message`) -> URL parameters are often called GET parameters because they can originate -> from a form with 'GET' as the method. +That’s it. If any part is more complex, it is not a static simple select and will be sent to the database. -So the previous example can be reworked to handle URL parameters: +#### Examples that ARE static (executed by SQLPage) -`result.sql` ```sql -SELECT - 'text' AS component, - 'You are ' || $age || ' years old!' AS contents; +SELECT 'text' AS component, 'Hello' AS contents; +SELECT 'text' AS component, $name AS contents; ``` -By querying this page with this URL: `/request.sql?age=42` -we would get `You are 42 years old!` as a response. - -### The `SET` command - -SQLPage overrides the behavior of `SET` statements in SQL to store variables in SQLPage itself instead of running the statement on the database. +#### Examples that are NOT static (sent to the database) ```sql -SET coalesced_post_id = COALESCE($post_id, 0); +-- Has string concatenation +select 'from' as component, 'handle_form.sql?id=' || $id as action; + +-- Has WHERE +select 'text' as component, $alert_message as contents where $should_alert; + +-- Uses database functions or expressions +SELECT 1 + 1 AS two; +SELECT CURRENT_TIMESTAMP AS now; +SELECT @@VERSION AS version; -- SQL Server variables +-- Uses a subquery +SELECT (select 1) AS one; ``` -`SET` statements are transformed into `SELECT` queries, and their result is stored in a `$`-variable: +## Variables -```sql -SELECT COALESCE($post_id, 0); -``` +SQLPage communicates information about incoming HTTP requests to your SQL code through prepared statement variables. -We can override a previous `$`-variable: +### Variable Types and Mutability -```sql -SET post_id = COALESCE($post_id, 0); -``` +There are three types of variables in SQLPage: -### Limitations +1. `GET` variables, or **URL parameters** (immutable): + - data sent in the URL query string. For example, in `http://example.com/my_page.sql?id=123`, your SQL code would have access to `$id`. +2. `POST` variables, or **form parameters** (immutable): + - data sent in the HTTP request body. For example, submitting a form with a field named `username` would make `:username` available in your SQL code. +3. `SET` variables, or **User-defined variables** (mutable): + - Variables created and modified with the `SET` command. For example, `SET greetings = $greetings || '!'` would update the value of `$greetings`. -`$`-variables and `:`-variables are stored by SQLPage, not in the database. +`SET` variables shadow `GET` variables with the same name, but the underlying url parameter value is still accessible using [`sqlpage.variables('get')`](/functions?function=variables). -They can only store a string, or null. +### POST parameters -As such, they're not designed to store table-valued results. -They will only store the first value of the first column: +Form fields sent with POST are available as `:name`. ```sql -CREATE TABLE t(a, b); -INSERT INTO t(a, b) VALUES (1, 2), (3, 4); +SELECT + 'form' AS component, + 'POST' AS method, + 'result.sql' AS action; -SET var = (SELECT * FROM t); +SELECT 'age' AS name, 'How old are you?' AS label, 'number' AS type; +``` --- now $var contains '1' +```sql +-- result.sql +SELECT 'text' AS component, 'You are ' || :age || ' years old!' AS contents; ``` -Temporary table-valued results can be stored in two ways. +### URL parameters -## Storing large datasets in the database with temporary tables +Query-string parameters are available as `$name`. -This is the most efficient method to store large values. ```sql --- Database connections are reused and temporary tables are stored at the --- connection level, so we make sure the table doesn't exist already -DROP TABLE IF EXISTS my_temp_table; -CREATE TEMPORARY TABLE my_temp_table AS -SELECT a, b -FROM my_stored_table ... - --- Insert data from direct values -INSERT INTO my_temp_table(a, b) -VALUES (1, 2), (3, 4); +SELECT 'text' AS component, 'You are ' || $age || ' years old!' AS contents; +-- /result.sql?age=42 → You are 42 years old! ``` -## Storing rich structured data in memory using JSON +When a URL parameter is not set, its value is `NULL`. -This can be more convenient, but should only be used for small values, because data -is copied from the database into SQLPage memory, and to the database again at each use. +### The SET command -You can use the [JSON functions from your database](/blog.sql?post=JSON+in+SQL%3A+A+Comprehensive+Guide). +`SET` creates or updates a user-defined variable in SQLPage (not in the database). Only strings and `NULL` are stored. -Here are some examples with SQLite: ```sql --- CREATE TABLE my_table(a, b); --- INSERT INTO my_table(a, b) --- VALUES (1, 2), (3, 4); - -SET my_json = ( - SELECT json_group_array(a) - FROM my_table -); --- [1, 3] +-- Give a default value to a variable +SET post_id = COALESCE($post_id, 0); -SET my_json = json_array(1, 2, 3); --- [1, 2, 3] +-- User-defined variables shadow URL parameters with the same name +SET my_var = 'custom value'; -- This value takes precedence over ?my_var=... ``` -## Functions +**Variable Lookup Precedence:** +- `$var`: checks user-defined variables first, then URL parameters +- `:var`: checks user-defined variables first, then POST parameters -Functions starting with `sqlpage.` are executed by SQLPage, not by your database engine. -See the [functions page](/functions.sql) for more details. +This means `SET` variables always take precedence over request parameters when using `$var` or `:var` syntax. -They're either executed before or after the query is run in the database. +**How SET works:** +- If the right-hand side is purely literals/variables, SQLPage computes it directly. See the section about *static simple select* above. +- If it needs the database (for example, calls a database function), SQLPage runs an internal `SELECT` to compute it and stores the first column of the first row of results. -### Executing functions *before* sending a query to the database +Only a single textual value (**string or `NULL`**) is stored. +`SET id = 1` will store the string `'1'`, not the number `1`. -When they don't process results coming from the database: +On databases with a strict type system, such as PostgreSQL, if you need a number, you will need to cast your variables: `SELECT * FROM post WHERE id = $id::int`. -```sql -SELECT * FROM blog WHERE slug = sqlpage.path() -``` +Complex structures can be stored as json strings. + +For larger temporary results, prefer temporary tables on your database; do not send them to SQLPage at all. + +## `sqlpage.*` functions + +Functions under the `sqlpage.` prefix run in SQLPage. See the [functions page](/functions.sql). -`sqlpage.path()` will get replaced by the result of the function. +They can run: -### Executing functions *after* receiving results from the database +### Before sending the query (as input values) -When they process results coming from the database: +Used inside conditions or parameters, the function is evaluated first and its result is passed to the database. ```sql -SELECT sqlpage.read_file_as_text(blog_post_file) AS title -FROM blog; +SELECT * +FROM blog +WHERE slug = sqlpage.path(); ``` -The query executed will be: +### After receiving results (as top-level selected columns) + +Used as top-level selected columns, the query is rewritten to first fetch the raw column, and the function is applied per row in SQLPage. ```sql -SELECT blog_post_file AS title FROM blog; +SELECT sqlpage.read_file_as_text(file_path) AS contents +FROM blog_posts; ``` -Then `sqlpage.read_file_as_text()` will be called on each row. +## Performance -## Implementation details of variables and functions +See the [performance page](/performance.sql) for details. In short: -All queries run by SQLPage in the database are first prepared, then executed. +- Statements sent to the database are prepared and cached. +- Variables and pre-computed values are bound as parameters. +- This keeps queries fast and repeatable. -Statements are prepared and cached the first time they're encountered by SQLPage. -Then those cached prepared statements are executed at each run, with parameter substitution. +## Working with larger temporary results -All variables and function results are cast as text, to let the -database query optimizer know only strings (or nulls) will be passed. +### Temporary tables in your database -Examples: +When you reuse the same values multiple times in your page, +store them in a temporary table. ```sql --- Source query -SELECT * FROM blog WHERE slug = sqlpage.path(); +DROP TABLE IF EXISTS filtered_posts; +CREATE TEMPORARY TABLE filtered_posts AS +SELECT * FROM posts where category = $category; + +select 'alert' as component, count(*) || 'results' as title +from filtered_posts; --- Prepared statement (SQLite syntax) -SELECT * FROM blog WHERE slug = CAST(?1 AS TEXT) +select 'list' as component; +select name from filtered_posts; ``` +### Small JSON values in variables + +Useful for small datasets that you want to keep in memory. +See the [guide on JSON in SQL](/blog.sql?post=JSON+in+SQL%3A+A+Comprehensive+Guide). + ```sql --- Source query -SET post_id = COALESCE($post_id, 0); +set product = ( + select json_object('name', name, 'price', price) + from products where id = $product_id +); +``` + +## CSV imports + +When you write a compatible `COPY ... FROM 'field'` statement and upload a file with the matching form field name, SQLPage orchestrates the import: + +- PostgreSQL: the file is streamed directly to the database using `COPY FROM STDIN`; the database performs the import. +- Other databases: SQLPage reads the CSV and inserts rows using a prepared `INSERT` statement. Options like delimiter, quote, header, escape, and a custom `NULL` string are supported. With a header row, column names are matched by name; otherwise, the order is used. + +Example: --- Prepared statement (SQLite syntax) -SELECT COALESCE(CAST(?1 AS TEXT), 0) +```sql +COPY my_table (col1, col2) +FROM 'my_csv' +(DELIMITER ';', HEADER); ``` -# Data types +The uploaded file should be provided in a form field with `'file' as type, 'my_csv' as name`. + +## Data types -Each database has its own rich set of data types. -The data modal in SQLPage itself is simpler, mainly composed of text strings and json objects. +Each database has its own usually large set of data types. +SQLPage itself has a much more rudimentary type system. ### From the user to SQLPage -Form fields and URL parameters may contain arrays. These are converted to JSON strings before processing. +Form fields and URL parameters in HTTP are fundamentally untyped. +They are just sequences of bytes. SQLPage requires them to be valid utf8 strings. + +SQLPage follows the convention that when a parameter name ends with `[]`, it represents an array. +Arrays in SQLPage are represented as JSON strings. -For instance, Loading `users.sql?user[]=Tim&user[]=Tom` will result in a single variable `$user` with the textual value `["Tim", "Tom"]`. +Example: In `users.sql?user[]=Tim&user[]=Tom`, `$user` becomes `'["Tim", "Tom"]'` (a JSON string exploitable with your database's builtin json functions). ### From SQLPage to the database -SQLPage sends only text strings (`VARCHAR`) and `NULL`s to the database, since these are the only possible variable and function return values. +SQLPage sends only strings (`TEXT` or `VARCHAR`) and `NULL`s as parameters. ### From the database to SQLPage -Each row of data returned by a SQL query is converted to a JSON object before being passed to components. +Each row returned by the database becomes a JSON object +before its passed to components: -- Each column becomes a key in the json object. If a row has two columns of the same name, they become an array in the json object. -- Each value is converted to the closest JSON value - - all number types map to json numbers, booleans to booleans, and `NULL` to `null`, - - all text types map to json strings - - date and time types map to json strings containing ISO datetime values - - binary values (BLOBs) map to json strings containing [data URLs](https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data) +- Each column is a key. Duplicate column names turn into arrays. +- Numbers, booleans, text, and `NULL` map naturally. +- Dates/times become ISO strings. +- Binary data (BLOBs) becomes a data URL (with mime type auto-detection). #### Example -The following PostgreSQL query: - ```sql -select - 1 as one, - 'x' as my_array, 'y' as my_array, - now() as today, - ''::bytea as my_image; +SELECT + 1 AS one, + 'x' AS my_array, 'y' AS my_array, + now() AS today, + ''::bytea AS my_image; ``` -will result in the following JSON object being passed to components for rendering +Produces something like: ```json { - "one" : 1, - "my_array" : ["x","y"], - "today":"2025-08-30T06:40:13.894918+00:00", - "my_image":"data:image/svg+xml;base64,PHN2Zz48L3N2Zz4=" + "one": 1, + "my_array": ["x", "y"], + "today": "2025-08-30T06:40:13.894918+00:00", + "my_image": "data:image/svg+xml;base64,PHN2Zz48L3N2Zz4=" } -``` \ No newline at end of file +``` diff --git a/examples/official-site/functions.sql b/examples/official-site/functions.sql index 943bfe16..ad91cfa7 100644 --- a/examples/official-site/functions.sql +++ b/examples/official-site/functions.sql @@ -11,7 +11,7 @@ FROM example WHERE component = 'shell' LIMIT 1; select 'breadcrumb' as component; select 'SQLPage' as title, '/' as link, 'Home page' as description; select 'Functions' as title, '/functions.sql' as link, 'List of all functions' as description; -select $function as title, sqlpage.link('functions.sql', json_object('function', $function)) as link where $function IS NOT NULL; +select $function as title, sqlpage.set_variable('function', $function) as link where $function IS NOT NULL; select 'text' as component, 'SQLPage built-in functions' as title where $function IS NULL; select ' @@ -24,6 +24,8 @@ Thus, they require all the parameters to be known at the time the query is sent Function parameters cannot reference columns from the rest of your query. The only case when you can call a SQLPage function with a parameter that is not a constant is when it appears at the top level of a `SELECT` statement. For example, `SELECT sqlpage.url_encode(url) FROM t` is allowed because SQLPage can execute `SELECT url FROM t` and then apply the `url_encode` function to each value. + +For more information about how SQLPage functions are evaluated, and data types in SQLPage, read [the SQLPage data model documentation](/extensions-to-sql). ' as contents_md where $function IS NULL; select 'list' as component, 'SQLPage functions' as title where $function IS NULL; @@ -53,14 +55,14 @@ select from sqlpage_function_parameters where "function" = $function ORDER BY "index"; -select +select 'button' as component, 'sm' as size, 'pill' as shape; select name as title, icon, - sqlpage.link('functions.sql', json_object('function', name)) as link + sqlpage.set_variable('function', name) as link from sqlpage_functions where $function IS NOT NULL -order by name; \ No newline at end of file +order by name; diff --git a/examples/official-site/llms.txt.sql b/examples/official-site/llms.txt.sql new file mode 100644 index 00000000..a1f5cda5 --- /dev/null +++ b/examples/official-site/llms.txt.sql @@ -0,0 +1,145 @@ +select + 'http_header' as component, + 'text/markdown; charset=utf-8' as "Content-Type", + 'inline; filename="llms.txt"' as "Content-Disposition"; + +select + 'shell-empty' as component, + '# SQLPage + +> SQLPage is a SQL-only web application framework. It lets you build entire websites and web applications using nothing but SQL queries. Write `.sql` files, and SQLPage executes them, maps results to UI components (handlebars templates), and streams HTML to the browser. + +SQLPage is designed for developers who are comfortable with SQL but want to avoid the complexity of traditional web frameworks. It works with SQLite, PostgreSQL, MySQL, and Microsoft SQL Server, and through ODBC with any other database that has an ODBC driver installed. + +Key features: +- No backend code needed: Your SQL files are your backend +- Component-based UI: Built-in components for forms, tables, charts, maps, and more +- Database-first: Every HTTP request triggers a sequence of SQL queries from a .sql file, the results are rendered with built-in or custom components, defined as .handlebars files in the sqlpage/templates folder. +- Simple deployment: Single binary with no runtime dependencies +- Secure by default: Parameterized queries prevent SQL injection + +## Getting Started + +- [Introduction to SQLPage: installation, guiding principles, and a first example](/your-first-sql-website/tutorial.md): Complete beginner tutorial covering setup, database connections, forms, and deployment + +## Core Documentation + +- [Components reference](/documentation.sql): List of all ' || ( + select + count(*) + from + component + ) || ' built-in UI components with parameters and examples +- [Functions reference](/functions.sql): SQLPage built-in functions for handling requests, encoding data, and more +- [Configuration guide](https://github.com/sqlpage/SQLPage/blob/main/configuration.md): Complete list of configuration options in sqlpage.json + +## Components + +' || ( + select + group_concat ( + '### [' || c.name || '](/component.sql?component=' || c.name || ') + +' || c.description || ' + +' || ( + select + case when exists ( + select + 1 + from + parameter + where + component = c.name + and top_level + ) then '#### Top-level parameters + +' || group_concat ( + '- `' || name || '` (' || type || ')' || case when not optional then ' **REQUIRED**' else '' end || ': ' || description, + char(10) + ) + else + '' + end + from + parameter + where + component = c.name + and top_level + ) || ' + +' || ( + select + case when exists ( + select + 1 + from + parameter + where + component = c.name + and not top_level + ) then '#### Row-level parameters + +' || group_concat ( + '- `' || name || '` (' || type || ')' || case when not optional then ' **REQUIRED**' else '' end || ': ' || description, + char(10) + ) + else + '' + end + from + parameter + where + component = c.name + and not top_level + ) || ' + +', + '' + ) + from + component c + order by + c.name + ) || ' + +## Functions + +' || ( + select + group_concat ( + '### [sqlpage.' || name || '()](/functions.sql?function=' || name || ') +' || replace ( + replace ( + description_md, + char(10) || '#', + char(10) || '###' + ), + ' ', + ' ' + ), + char(10) + ) + from + sqlpage_functions + order by + name + ) || ' + +## Examples + +- [Authentication example](https://github.com/sqlpage/SQLPage/tree/main/examples/user-authentication): Complete user registration and login system +- [CRUD application](https://github.com/sqlpage/SQLPage/tree/main/examples/CRUD%20-%20Authentication): Create, read, update, delete with authentication +- [Image gallery](https://github.com/sqlpage/SQLPage/tree/main/examples/image%20gallery%20with%20user%20uploads): File upload and image display +- [Todo application](https://github.com/sqlpage/SQLPage/tree/main/examples/todo%20application): Simple CRUD app +- [Master-detail forms](https://github.com/sqlpage/SQLPage/tree/main/examples/master-detail-forms): Working with related data +- [Charts example](https://github.com/sqlpage/SQLPage/tree/main/examples/plots%20tables%20and%20forms): Data visualization + +## Optional + +- [Custom components guide](/custom_components.sql): Create your own handlebars components +- [Safety and security](/safety.sql): Understanding SQL injection prevention +- [Docker deployment](https://github.com/sqlpage/SQLPage#with-docker): Running SQLPage in containers +- [Systemd service](https://github.com/sqlpage/SQLPage/blob/main/sqlpage.service): Production deployment setup +- [Repository structure](https://github.com/sqlpage/SQLPage/blob/main/CONTRIBUTING.md): Project organization and contribution guide +' as html; \ No newline at end of file diff --git a/examples/official-site/performance.sql b/examples/official-site/performance.sql index 771d5c0b..332f6e3b 100644 --- a/examples/official-site/performance.sql +++ b/examples/official-site/performance.sql @@ -22,13 +22,13 @@ as opposed to writing imperative code in a backend programming language like Jav This declarative approach allows SQLPage to offer **optimizations** out of the box that are difficult or time-consuming to achieve in traditional web development stacks. -## Server-side rendering +## Progressive server-side rendering SQLPage applications are [server-side rendered](https://web.dev/articles/rendering-on-the-web), -which means that the SQL queries are executed on the server, and the results are sent to the user''s browser -as HTML, which allows it to start rendering the page as soon as the first byte is received. +which means that the SQL queries are executed on the server, and the results are sent to the user''s browser as HTML. In contrast, many other web frameworks render the page on the client side, which means that the browser has to download some HTML, then download some JavaScript, then execute the JavaScript, then make more requests, +wait for the database to produce a full result set, then process the responses before it can start rendering the actual data the user is interested in. This can lead to loading times that are several times longer than a SQLPage application. @@ -36,6 +36,7 @@ This can lead to loading times that are several times longer than a SQLPage appl SQLPage applications will often feel faster than even equivalent applications written even in alternative server-side rendering frameworks, because SQLPage streams the results of the SQL queries to the browser as soon as they are available. +The user sees the start of the page even before the database has finished producing the last query results. Most server-side rendering frameworks will first wait for all the SQL queries to finish, then render the page in memory on the server, and only then send the HTML webpage to the browser. If a page contains a long list of items, the user @@ -51,7 +52,7 @@ an execution plan every time an user requests a page. When an user loads a page, all SQLPage has to do is tell the database: "Hey, do you remember that query we talked about earlier? Can you give me the results for these specific parameters?". This is much faster than sending the whole SQL query -string to the database every time. +string to the database every time, especially for large complex queries that require heavy planning on the database side. ## Compiled templates @@ -87,16 +88,15 @@ interaction. ## Key Takeaways -SQLPage offers a radically different approach to web development, -resolving the classical tension between performance and ease of use. - -By leveraging a declarative approach, server-side rendering, and advanced optimization techniques, SQLPage enables: - -* **Faster page loads**: Long loading times make your website feel sluggish and unresponsive, causing users to leave. -* **Easier development**: Focus on writing SQL queries; all the heavy lifting is done for you. -* **Cost effective**: SQLPage''s low CPU and memory usage means you can host your website extremely cheaply, even if it gets significant traffic. +Performance is a key feature of SQLPage. +Its architecture allows you to build fast websites without having to implement advanced optimizations yourself. ## Ready to get started? [Build your fast, secure, and beautiful website](/your-first-sql-website) with SQLPage today! + +## Already a SQLPage developer ? + +Have a look at our [performance guide](/blog?post=Performance+Guide) to learn the best practices to leverage +all the features that will make your site faster. ' as contents_md; diff --git a/examples/official-site/sqlpage/migrations/01_documentation.sql b/examples/official-site/sqlpage/migrations/01_documentation.sql index ee2b8b51..f562cb23 100644 --- a/examples/official-site/sqlpage/migrations/01_documentation.sql +++ b/examples/official-site/sqlpage/migrations/01_documentation.sql @@ -220,32 +220,24 @@ INSERT INTO component(name, icon, description) VALUES ('form', 'cursor-text', ' # Building forms in SQL -So, you have an SQL database, and would like to let users input data into it? -The `form` component is what you are looking for. - -## Collecting data from users to your database - The form component will display a series of input fields of various types, that can be filled in by the user. When the user submits the form, the data is posted to an SQL file specified in the `action` property. ## Handle Data with SQL -User-entered data is posted to an SQL file, that will handle the data, -and will be able to insert it into the database, search for it, format it, etc. +The receiving SQL page will be able to handle the data, +and insert it into the database, use it to perform a search, format it, update existing data, etc. -For example, a value in a field named "x" -can be referenced as `:x` in the SQL query of the target page. +A value in a field named "x" will be available as `:x` in the SQL query of the target page. ## Examples -- **Data Entry Automation**: Forms for tasks like inventory management. -- **Custom Report Builder**: Generate reports based on user-specified criteria. -- **Database Management**: Update records or query data. -- **Admin Panel**: Manage user roles and permissions. -- **Data Analytics with SQL**: Collect data for analytics. -- **SQL Query Parametrization**: Build and execute complex SQL queries that depend on user input. -- **SQL CRUD Operations**: Perform Create, Read, Update, and Delete operations. -- **Web SQL**: Integrate forms into web applications. + - [A multi-step form](https://github.com/sqlpage/SQLPage/tree/main/examples/forms-with-multiple-steps), guiding the user through a process without overwhelming them with a large form. + - [File upload form](https://github.com/sqlpage/SQLPage/tree/main/examples/image%20gallery%20with%20user%20uploads), letting users upload images to a gallery. + - [Rich text editor](https://github.com/sqlpage/SQLPage/tree/main/examples/rich-text-editor), letting users write text with bold, italics, links, images, etc. + - [Master-detail form](https://github.com/sqlpage/SQLPage/tree/main/examples/master-detail-forms), to edit a list of structured items. + - [Form with a variable number of fields](https://github.com/sqlpage/SQLPage/tree/main/examples/forms%20with%20a%20variable%20number%20of%20fields), when the fields are not known in advance. + - [Demo of all input types](/examples/form), showing all the input types supported by SQLPage. '); INSERT INTO parameter(component, name, description_md, type, top_level, optional) SELECT 'form', * FROM (VALUES -- top level @@ -276,7 +268,7 @@ INSERT INTO parameter(component, name, description, type, top_level, optional) S ('id', 'A unique identifier for the form, which can then be used to validate the form from a button outside of the form.', 'TEXT', TRUE, TRUE), ('auto_submit', 'Automatically submit the form when the user changes any of its fields, and remove the validation button.', 'BOOLEAN', TRUE, TRUE), -- item level - ('type', 'The type of input to use: text for a simple text field, textarea for a multi-line text input control, number to accept only numbers, checkbox, switch, or radio for a button that is part of a group specified in the ''name'' parameter, header for a form header, hidden for a value that will be submitted but not shown to the user. text by default.', 'TEXT', FALSE, TRUE), + ('type', 'Declares input control behavior and expected format. All HTML input types are supported (text, number, date, file, checkbox, radio, hidden, ...). SQLPage adds some custom types: textarea, switch, header. text by default. See https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input#input_types', 'TEXT', FALSE, TRUE), ('name', 'The name of the input field, that you can use in the target page to get the value the user entered for the field.', 'TEXT', FALSE, FALSE), ('label', 'A friendly name for the text field to show to the user.', 'TEXT', FALSE, TRUE), ('placeholder', 'A placeholder text that will be shown in the field when is is empty.', 'TEXT', FALSE, TRUE), @@ -810,11 +802,15 @@ INSERT INTO parameter(component, name, description, type, top_level, optional) S ('money', 'Name of a numeric column whose values should be displayed as currency amounts, in the currency defined by the `currency` property. This argument can be repeated multiple times.', 'TEXT', TRUE, TRUE), ('currency', 'The ISO 4217 currency code (e.g., USD, EUR, GBP, etc.) to use when formatting monetary values.', 'TEXT', TRUE, TRUE), ('number_format_digits', 'Maximum number of decimal digits to display for numeric values.', 'INTEGER', TRUE, TRUE), + ('edit_url', 'If set, an edit button will be added to each row. The value of this property should be a URL, possibly containing the `{id}` placeholder that will be replaced by the value of the `_sqlpage_id` property for that row. Clicking the edit button will take the user to that URL. Added in v0.39.0', 'TEXT', TRUE, TRUE), + ('delete_url', 'If set, a delete button will be added to each row. The value of this property should be a URL, possibly containing the `{id}` placeholder that will be replaced by the value of the `_sqlpage_id` property for that row. Clicking the delete button will take the user to that URL. Added in v0.39.0', 'TEXT', TRUE, TRUE), + ('custom_actions', 'If set, a column of custom action buttons will be added to each row. The value of this property should be a JSON array of objects, each object defining a button with the following properties: `name` (the text to display on the button), `icon` (the tabler icon name or image link to display on the button), `link` (the URL to navigate to when the button is clicked, possibly containing the `{id}` placeholder that will be replaced by the value of the `_sqlpage_id` property for that row), and `tooltip` (optional text to display when hovering over the button). Added in v0.39.0', 'JSON', TRUE, TRUE), -- row level ('_sqlpage_css_class', 'For advanced users. Sets a css class on the table row. Added in v0.8.0.', 'TEXT', FALSE, TRUE), ('_sqlpage_color', 'Sets the background color of the row. Added in v0.8.0.', 'COLOR', FALSE, TRUE), ('_sqlpage_footer', 'Sets this row as the table footer. It is recommended that this parameter is applied to the last row. Added in v0.34.0.', 'BOOLEAN', FALSE, TRUE), - ('_sqlpage_id', 'Sets the id of the html tabler row element. Allows you to make links targeting a specific row in a table.', 'TEXT', FALSE, TRUE) + ('_sqlpage_id', 'Sets the id of the html tabler row element. Allows you to make links targeting a specific row in a table.', 'TEXT', FALSE, TRUE), + ('_sqlpage_actions', 'Sets custom action buttons for this specific row in addition to any defined at the table level, The value of this property should be a JSON array of objects, each object defining a button with the following properties: `name` (the text to display on the button), `icon` (the tabler icon name or image link to display on the button), `link` (the URL to navigate to when the button is clicked, possibly containing the `{id}` placeholder that will be replaced by the value of the `_sqlpage_id` property for that row), and `tooltip` (optional text to display when hovering over the button). Added in v0.39.0', 'JSON', FALSE, TRUE) ) x; INSERT INTO example(component, description, properties) VALUES @@ -994,7 +990,124 @@ GROUP BY This will generate a table with the stores in the first column, and the items in the following columns, with the quantity sold in each store for each item. ', NULL - ); + ), + ( + 'table', +'## Using Action Buttons in a table. + +### Preset Actions: `edit_url` & `delete_url` +Since edit and delete are common actions, the `table` component has dedicated `edit_url` and `delete_url` properties to add buttons for these actions. +The value of these properties should be a URL, containing the `{id}` placeholder that will be replaced by the value of the `_sqlpage_id` property for that row. + +### Column with fixed action buttons + +You may want to add custom action buttons to your table rows, for instance to view details, download a file, or perform a custom operation. +For this, the `table` component has a `custom_actions` top-level property that lets you define a column of buttons, each button defined by a name, an icon, a link, and an optional tooltip. + +### Column with variable action buttons + +The `table` component also supports the row level `_sqlpage_actions` column in your data table. +This is helpful if you want a more complex logic, for instance to disable a button on some rows, or to change the link or icon based on the row data. + +> WARNING! +> If the number of array items in `_sqlpage_actions` is not consistent across all rows, the table may not render correctly. +> You can leave blank spaces by including an object with only the `name` property. + +The table has a column of buttons, each button defined by the `custom_actions` column at the table level, and by the `_sqlpage_actions` property at the row level. + +### `custom_actions` & `_sqlpage_actions` JSON properties. + +Each button is defined by the following properties: +* `name`: sets the column header and the tooltip if no tooltip is provided, +* `tooltip`: text to display when hovering over the button, +* `link`: the URL to navigate to when the button is clicked, possibly containing the `{id}` placeholder that will be replaced by the value of the `_sqlpage_id` property for that row, +* `icon`: the tabler icon name or image link to display on the button + +### Example using all of the above +' + , + json('[ + { + "component": "table", + "edit_url": "/examples/show_variables.sql?action=edit&update_id={id}", + "delete_url": "/examples/show_variables.sql?action=delete&delete_id={id}", + "custom_actions": { + "name": "history", + "tooltip": "View Standard History", + "link": "/examples/show_variables.sql?action=history&standard_id={id}", + "icon": "history" + } + }, + { + "name": "CalStd", + "vendor": "PharmaCo", + "Product": "P1234", + "lot number": "T23523", + "status": "Available", + "expires on": "2026-10-13", + "_sqlpage_id": 32, + "_sqlpage_actions": [ + { + "name": "View PDF", + "tooltip": "View Presentation", + "link": "https://sql-page.com/pgconf/2024-sqlpage-badass.pdf", + "icon": "file-type-pdf" + }, + { + "name": "Action", + "tooltip": "Set In Use", + "link": "/examples/show_variables.sql?action=set_in_use&standard_id=32", + "icon": "caret-right" + } + ] + }, + { + "name": "CalStd", + "vendor": "PharmaCo", + "Product": "P1234", + "lot number": "T2352", + "status": "In Use", + "expires on": "2026-10-14", + "_sqlpage_id": 33, + "_sqlpage_actions": [ + { + "name": "View PDF", + "tooltip": "View Presentation", + "link": "https://sql-page.com/pgconf/2024-sqlpage-badass.pdf", + "icon": "file-type-pdf" + }, + { + "name": "Action", + "tooltip": "Retire Standard", + "link": "/examples/show_variables.sql?action=retire&standard_id=33", + "icon": "test-pipe-off" + } + ] + }, + { + "name": "CalStd", + "vendor": "PharmaCo", + "Product": "P1234", + "lot number": "A123", + "status": "Discarded", + "expires on": "2026-09-30", + "_sqlpage_id": 31, + "_sqlpage_actions": [ + { + "name": "View PDF", + "tooltip": "View Presentation", + "link": "https://sql-page.com/pgconf/2024-sqlpage-badass.pdf", + "icon": "file-type-pdf" + }, + { + "name": "Action" + } + ] + } +]' +) +); + INSERT INTO component(name, icon, description) VALUES @@ -1177,7 +1290,20 @@ GROUP BY name; ', NULL); INSERT INTO component(name, icon, description) VALUES - ('shell', 'layout-navbar', 'Personalize the "shell" surrounding your page contents. Used to set properties for the entire page.'); + ('shell', 'layout-navbar', ' +Customize the overall layout, header and footer of the page. + +This is a special component that provides the page structure wrapping all other components on your page. + +It generates the complete HTML document including the `` section with metadata, title, and stylesheets, +as well as the navigation bar, main content area, and footer. + +If you don''t explicitly call the shell component at the top of your SQL file, SQLPage will automatically +add a default shell component before your first try to display data on the page. + +Use the shell component to customize page-wide settings like the page title, navigation menu, theme, fonts, +and to include custom visual styles (with CSS) or interactive behavior (with JavaScript) that should be loaded on the page. +'); INSERT INTO parameter(component, name, description_md, type, top_level, optional) SELECT 'shell', * FROM (VALUES ('favicon', 'The URL of the icon the web browser should display in bookmarks and tabs. This property is particularly useful if multiple sites are hosted on the same domain with different [``site_prefix``](https://github.com/sqlpage/SQLPage/blob/main/configuration.md#configuring-sqlpage).', 'URL', TRUE, TRUE), @@ -1408,16 +1534,41 @@ SELECT ``` ', NULL), ('shell', ' -### A page without a shell -SQLPage provides the `shell-empty` component to create a page without a shell. -In this case, the `html` and `body` tags are not generated, and the components are rendered directly in the page -without any styling, navigation bar, footer, or dynamic content. -This is useful when you want to generate a snippet of HTML that can be dynamically included in a larger page. +### Returning custom HTML, XML, plain text, or other formats + +Use `shell-empty` to opt out of SQLPage''s component system and return raw data directly. -Any component whose name starts with `shell` will be considered as a shell component, -so you can also [create your own shell component](custom_components.sql#custom-shell). +By default, SQLPage wraps all your content in a complete HTML page with navigation and styling. +The `shell-empty` component tells SQLPage to skip this HTML wrapper and return only the raw content you specify. +Use it to create endpoints that return things like + - XML (for JSON, use the [json](?component=json) component) + - plain text or markdown content (for instance for consumption by LLMs) + - a custom data format you need + +When using `shell-empty`, you should use the [http_header](component.sql?component=http%5Fheader) component first +to set the correct [content type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) (like `application/json` or `application/xml`). +', + json('[ + { + "component":"http_header", + "Content-Type":"application/xml" + }, + { + "component":"shell-empty", + "contents": "\n \n 42\n john.doe\n " + } + ]') + ), + ('shell',' +### Generate your own HTML If you generate your own HTML from a SQL query, you can also use the `shell-empty` component to include it in a page. +This is useful when you want to generate a snippet of HTML that can be dynamically included in a larger page. Make sure you know what you are doing, and be careful to escape the HTML properly, -as you are stepping out of the safe SQLPage framework and into the wild world of HTML.', +as you are stepping out of the safe SQLPage framework and into the wild world of HTML. + +In this scenario, you can use the `html` property, which serves as an alias for the `contents` property. +This property improves code readability by clearly indicating that you are generating HTML. +Since SQLPage returns HTML by default, there is no need to specify the content type in the HTTP header. +', json('[{"component":"shell-empty", "html": "\n\n\n My page\n\n\n

My page

\n\n"}]')); diff --git a/examples/official-site/sqlpage/migrations/07_authentication.sql b/examples/official-site/sqlpage/migrations/07_authentication.sql index fd342fc6..c759dee8 100644 --- a/examples/official-site/sqlpage/migrations/07_authentication.sql +++ b/examples/official-site/sqlpage/migrations/07_authentication.sql @@ -14,7 +14,7 @@ you have two main options: - does not require any external service - gives you fine-grained control over - which pages and actions are protected - - the look of the login form + - the look of the [login form](?component=login) - the duration of the session - the permissions of each user 2. [**Single sign-on**](/sso) @@ -128,12 +128,10 @@ Then, in all the pages that require authentication, you check if the cookie is p You can check if the user has sent the correct password in a form, and if not, redirect them to a login page. -Create a login form in a file called `login.sql`: +Create a login form in a file called `login.sql` that uses the [login component](?component=login): ```sql -select ''form'' as component, ''Authentication'' as title, ''Log in'' as validate, ''create_session_token.sql'' as action; -select ''Username'' as name, ''admin'' as placeholder; -select ''Password'' as name, ''admin'' as placeholder, ''password'' as type; +select ''login'' as component; ``` And then, in `create_session_token.sql` : diff --git a/examples/official-site/sqlpage/migrations/08_functions.sql b/examples/official-site/sqlpage/migrations/08_functions.sql index 18981b9f..2f5ca8a7 100644 --- a/examples/official-site/sqlpage/migrations/08_functions.sql +++ b/examples/official-site/sqlpage/migrations/08_functions.sql @@ -157,8 +157,11 @@ VALUES ( '0.7.2', 'spy', ' -Hashes a password using the [Argon2](https://en.wikipedia.org/wiki/Argon2) algorithm. -The resulting hash can be stored in the database and then used with the [authentication component](documentation.sql?component=authentication#component). +Hashes a password with the Argon2id variant and outputs it in the [PHC string format](https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md), ready to store in your users table. + +Every call generates a brand new cryptographic salt so that two people choosing the same password still end up with different hashes, which defeats rainbow-table attacks and lets you safely reveal only the hash. + +Use this function only when creating or resetting a password (for example while inserting a brand new user): it writes the stored value. Later, at login time, the [authentication component](documentation.sql?component=authentication#component) reads the stored hash, hashes the visitor''s password with the embedded salt and parameters, and grants access only if they match. ### Example diff --git a/examples/official-site/sqlpage/migrations/11_json.sql b/examples/official-site/sqlpage/migrations/11_json.sql index 65c3a8a1..8384ab58 100644 --- a/examples/official-site/sqlpage/migrations/11_json.sql +++ b/examples/official-site/sqlpage/migrations/11_json.sql @@ -18,6 +18,10 @@ This component **must appear at the top of your SQL file**, before any other dat An HTTP response can have only a single datatype, and it must be declared in the headers. So if you have already called the `shell` component, or another traditional HTML component, you cannot use this component in the same file. + +SQLPage can also return JSON or JSON Lines when the incoming request says it prefers them with an HTTP `Accept` header, so the same `/users.sql` page can show a table in a browser but return raw data to `curl -H "Accept: application/json" http://localhost:8080/users.sql`. + +Use this component when you want to control the payload or force JSON output even for requests that would normally get HTML. ', 'code', '0.9.0' @@ -84,6 +88,12 @@ select * from users; {"username":"James","userid":1}, {"username":"John","userid":2} ] +``` + +Clients can also receive JSON or JSON Lines automatically by requesting the same SQL file with an HTTP `Accept` header such as `application/json` or `application/x-ndjson` when the component is omitted, for example: + +``` +curl -H "Accept: application/json" http://localhost:8080/users.sql ``` ' ), diff --git a/examples/official-site/sqlpage/migrations/20_variables_function.sql b/examples/official-site/sqlpage/migrations/20_variables_function.sql index 69fe1f51..79228569 100644 --- a/examples/official-site/sqlpage/migrations/20_variables_function.sql +++ b/examples/official-site/sqlpage/migrations/20_variables_function.sql @@ -9,9 +9,28 @@ VALUES ( 'variables', '0.15.0', 'variable', - 'Returns a JSON string containing all variables passed as URL parameters or posted through a form. + 'Returns a JSON string containing variables from the HTTP request and user-defined variables. -The database''s json handling functions can then be used to process the data. +The [database''s json handling functions](/blog?post=JSON+in+SQL%3A+A+Comprehensive+Guide) can then be used to process the data. + +## Variable Types + +SQLPage distinguishes between three types of variables: + +- **GET variables**: URL parameters from the [query string](https://en.wikipedia.org/wiki/Query_string) (immutable) +- **POST variables**: Values from form fields [submitted](https://en.wikipedia.org/wiki/POST_(HTTP)#Use_for_submitting_web_forms) by the user (immutable) +- **SET variables**: User-defined variables created with the `SET` command (mutable) + +For more information about SQLPage variables, see the [*SQL in SQLPage* guide](/extensions-to-sql). + +## Usage + +- `sqlpage.variables()` - returns all variables (GET, POST, and SET combined). When multiple variables of the same name are present, the order of precedence is: set > post > get. +- `sqlpage.variables(''get'')` - returns only URL parameters +- `sqlpage.variables(''post'')` - returns only POST form data +- `sqlpage.variables(''set'')` - returns only user-defined variables created with `SET` + +When a SET variable has the same name as a GET or POST variable, the SET variable takes precedence in the combined result. ## Example: a form with a variable number of fields @@ -59,7 +78,7 @@ FROM OPENJSON(sqlpage.variables(''post'')); #### In MySQL -MySQL has [`JSON_TABLE`](https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html), +MySQL has [`JSON_TABLE`](https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html), and [`JSON_KEYS`](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-keys) which are a little bit less straightforward to use: @@ -95,6 +114,6 @@ VALUES ( 'variables', 1, 'method', - 'Optional. The HTTP request method (GET or POST). Must be a literal string. When not provided, all variables are returned.', + 'Optional. Filter variables by source: ''get'' (URL parameters), ''post'' (form data), or ''set'' (user-defined variables). When not provided, all variables are returned with SET variables taking precedence over request parameters.', 'TEXT' ); diff --git a/examples/official-site/sqlpage/migrations/34_carousel.sql b/examples/official-site/sqlpage/migrations/34_carousel.sql index b7fc395e..6736ccff 100644 --- a/examples/official-site/sqlpage/migrations/34_carousel.sql +++ b/examples/official-site/sqlpage/migrations/34_carousel.sql @@ -78,6 +78,14 @@ VALUES TRUE, TRUE ), + ( + 'carousel', + 'delay', + 'Specify the delay, in milliseconds, between two images.', + 'INTEGER', + TRUE, + TRUE + ), ( 'carousel', 'image', diff --git a/examples/official-site/sqlpage/migrations/40_fetch.sql b/examples/official-site/sqlpage/migrations/40_fetch.sql index 5a3d7b18..f93d6f50 100644 --- a/examples/official-site/sqlpage/migrations/40_fetch.sql +++ b/examples/official-site/sqlpage/migrations/40_fetch.sql @@ -36,7 +36,7 @@ In this example, we use the complex form of the function to make an authenticated POST request, with custom request headers and a custom request body. We use SQLite''s json functions to build the request body. -See [the list of SQL databases and their JSON functions](/blog.sql?post=JSON%20in%20SQL%3A%20A%20Comprehensive%20Guide) for +See [the list of SQL databases and their JSON functions](/blog.sql?post=JSON%20in%20SQL%3A%20A%20Comprehensive%20Guide) for more information on how to build JSON objects in your database. ```sql @@ -94,7 +94,22 @@ The fetch function accepts either a URL string, or a JSON object with the follow If the request fails, this function throws an error, that will be displayed to the user. The response headers are not available for inspection. -If you need to handle errors or inspect the response headers, use [`sqlpage.fetch_with_meta`](?function=fetch_with_meta). +## Conditional data fetching + +Since v0.40, `sqlpage.fetch(null)` returns null instead of throwing an error. +This makes it easier to conditionnally query an API: + +```sql +set current_field_value = (select field from my_table where id = 1); +set target_url = nullif(''http://example.com/api/field/1'', null); -- null if the field is currently null in the db +set api_value = sqlpage.fetch($target_url); -- no http request made if the field is not null in the db +update my_table set field = $api_value where id = 1 and $api_value is not null; -- update the field only if it was not present before +``` + +## Advanced usage + +If you need to handle errors or inspect the response headers or the status code, +use [`sqlpage.fetch_with_meta`](?function=fetch_with_meta). ' ); INSERT INTO sqlpage_function_parameters ( diff --git a/examples/official-site/sqlpage/migrations/47_link.sql b/examples/official-site/sqlpage/migrations/47_link.sql index 0405a53a..267a728d 100644 --- a/examples/official-site/sqlpage/migrations/47_link.sql +++ b/examples/official-site/sqlpage/migrations/47_link.sql @@ -22,20 +22,22 @@ In `index.sql`, you can use the `link` function to generate the URL of the produ select ''list'' as component; select name as title, - sqlpage.link(''product'', json_object(''product_name'', name)) as link; + sqlpage.link(''product'', json_object(''product_name'', name)) as link +from products; ``` -Using `sqlpage.link` is better than manually constructing the URL with `CONCAT(''product?product_name='', name)`, -because it ensures that the URL is properly encoded. -The former works when the product name contains special characters like `&`, while the latter would break the URL. - In `product.sql`, you can then use `$product_name` to get the name of the product from the URL parameter: ```sql -select ''text'' as component; -select CONCAT(''Product: '', $product_name) as contents; +select ''hero'' as component, $product_name as title, product_info as description +from products +where name = $product_name; ``` +> You could also have manually constructed the URL with `CONCAT(''product?product_name='', name)`, +> but using `sqlpage.link` is better because it ensures that the URL is properly encoded. +> `sqlpage.link` will work even if the product name contains special characters like `&`, while `CONCAT(...)` would break the URL. + ### Parameters - `file` (TEXT): The name of the SQLPage file to link to. - `parameters` (JSON): The parameters to pass to the linked file. @@ -72,4 +74,4 @@ VALUES 'fragment', 'An optional fragment (hash) to append to the URL to link to a specific section of the target page.', 'TEXT' - ); \ No newline at end of file + ); diff --git a/examples/official-site/sqlpage/migrations/61_oidc_functions.sql b/examples/official-site/sqlpage/migrations/61_oidc_functions.sql index eda1c2cc..71d1d849 100644 --- a/examples/official-site/sqlpage/migrations/61_oidc_functions.sql +++ b/examples/official-site/sqlpage/migrations/61_oidc_functions.sql @@ -169,4 +169,126 @@ VALUES 'claim', 'The name of the user information to retrieve. Common values include ''name'', ''email'', ''picture'', ''sub'', ''preferred_username'', ''given_name'', and ''family_name''. The exact values available depend on your OIDC provider and configuration.', 'TEXT' + ); + +INSERT INTO + sqlpage_functions ( + "name", + "introduced_in_version", + "icon", + "description_md" + ) +VALUES + ( + 'oidc_logout_url', + '0.41.0', + 'logout', + '# Secure OIDC Logout + +The `sqlpage.oidc_logout_url` function generates a secure logout URL for users authenticated via [OIDC Single Sign-On](/sso). + +When a user visits this URL, SQLPage will: +1. Remove the authentication cookie +2. Redirect the user to the OIDC provider''s logout endpoint (if available) +3. Finally redirect back to the specified `redirect_uri` + +## Security Features + +This function provides protection against **Cross-Site Request Forgery (CSRF)** attacks: +- The generated URL contains a cryptographically signed token +- The token includes a timestamp and expires after 10 minutes +- The token is signed using your OIDC client secret +- Only relative URLs (starting with `/`) are allowed as redirect targets + +This means that malicious websites cannot trick your users into logging out by simply including an image or link to your logout URL. + +## How to Use + +```sql +select ''button'' as component; +select + ''Logout'' as title, + sqlpage.oidc_logout_url(''/'') as link, + ''logout'' as icon, + ''red'' as outline; +``` + +This creates a logout button that, when clicked: +1. Logs the user out of your SQLPage application +2. Logs the user out of the OIDC provider (if the provider supports [RP-Initiated Logout](https://openid.net/specs/openid-connect-rpinitiated-1_0.html)) +3. Redirects the user back to your homepage (`/`) + +## Examples + +### Logout Button in Navigation + +```sql +select ''shell'' as component, + ''My App'' as title, + json_array( + json_object( + ''title'', ''Logout'', + ''link'', sqlpage.oidc_logout_url(''/''), + ''icon'', ''logout'' + ) + ) as menu_item; +``` + +### Logout with Return to Current Page + +```sql +select ''button'' as component; +select + ''Sign Out'' as title, + sqlpage.oidc_logout_url(sqlpage.path()) as link; +``` + +### Conditional Logout Link + +```sql +select ''button'' as component +where sqlpage.user_info(''sub'') is not null; +select + ''Logout '' || sqlpage.user_info(''name'') as title, + sqlpage.oidc_logout_url(''/'') as link +where sqlpage.user_info(''sub'') is not null; +``` + +## Requirements + +- OIDC must be [configured](/sso) in your `sqlpage.json` +- If OIDC is not configured, this function returns NULL +- The `redirect_uri` must be a relative path starting with `/` + +## Provider Support + +The logout behavior depends on your OIDC provider: + +| Provider | Full Logout Support | +|----------|-------------------| +| Keycloak | ✅ Yes | +| Auth0 | ✅ Yes | +| Google | ❌ No (local logout only) | +| Azure AD | ✅ Yes | +| Okta | ✅ Yes | + +When the provider doesn''t support RP-Initiated Logout, SQLPage will still remove the local authentication cookie and redirect to your specified URI. +' + ); + +INSERT INTO + sqlpage_function_parameters ( + "function", + "index", + "name", + "description_md", + "type" + ) +VALUES + ( + 'oidc_logout_url', + 1, + 'redirect_uri', + 'The relative URL path where the user should be redirected after logout. Must start with `/`. Defaults to `/` if not provided.', + 'TEXT' ); \ No newline at end of file diff --git a/examples/official-site/sqlpage/migrations/66_log_component.sql b/examples/official-site/sqlpage/migrations/66_log_component.sql new file mode 100644 index 00000000..65c0e7e6 --- /dev/null +++ b/examples/official-site/sqlpage/migrations/66_log_component.sql @@ -0,0 +1,61 @@ +INSERT INTO component(name, icon, introduced_in_version, description) VALUES +('log', 'logs', '0.37.1', 'A component that writes messages to the server logs. +When a page runs, it prints your message to the terminal/console (standard error). +Use it to track what happens and troubleshoot issues. + +### Where do the messages appear? + +- Running from a terminal (Linux, macOS, or Windows PowerShell/Command Prompt): they show up in the window. +- Docker: run `docker logs `. +- Linux service (systemd): run `journalctl -u sqlpage`. +- Output is written to [standard error (stderr)](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)). +'); + +INSERT INTO parameter(component, name, description, type, top_level, optional) SELECT 'log', * FROM (VALUES + -- top level + ('message', 'The text to write to the server logs. It is printed when the page runs.', 'TEXT', TRUE, FALSE), + ('level', 'How important the message is. One of ''trace'', ''debug'', ''info'' (default), ''warn'', ''error''. Not case-sensitive. Controls the level shown in the logs.', 'TEXT', TRUE, TRUE) +) x; + +INSERT INTO example(component, description) VALUES +('log', ' +### Record a simple message + +This writes "Hello, World!" to the server logs. + +```sql +SELECT ''log'' as component, ''Hello, World!'' as message; +``` + +Example output: + +```text +[2025-09-13T22:30:14.722Z INFO sqlpage::log from "x.sql" statement 1] Hello, World! +``` + +### Set the importance (level) + +Choose how important the message is. + +```sql +SELECT ''log'' as component, ''error'' as level, ''This is an error message'' as message; +``` + +Example output: + +```text +[2025-09-13T22:30:14.722Z ERROR sqlpage::log from "x.sql" statement 2] This is an error message +``` + +### Log dynamic information + +Include variables like a username. + +```sql +set username = ''user'' + +select ''log'' as component, + ''403 - failed for '' || coalesce($username, ''None'') as message, + ''error'' as level; +``` +') \ No newline at end of file diff --git a/examples/official-site/sqlpage/migrations/67_hmac_function.sql b/examples/official-site/sqlpage/migrations/67_hmac_function.sql new file mode 100644 index 00000000..e667f03f --- /dev/null +++ b/examples/official-site/sqlpage/migrations/67_hmac_function.sql @@ -0,0 +1,137 @@ +-- HMAC function documentation and examples +INSERT INTO + sqlpage_functions ( + "name", + "introduced_in_version", + "icon", + "description_md" + ) +VALUES + ( + 'hmac', + '0.38.0', + 'shield-lock', + 'Creates a unique "signature" for some data using a secret key. +This signature proves that the data hasn''t been tampered with and comes from someone who knows the secret. + +### What is HMAC used for? + +[**HMAC**](https://en.wikipedia.org/wiki/HMAC) (Hash-based Message Authentication Code) is commonly used to: + - **Verify webhooks**: Use HMAC to ensure only a given external service can call a given endpoint in your application. +The service signs their request with a secret key, and you verify the signature before processing the data they sent you. +Used for instance by [Stripe](https://docs.stripe.com/webhooks?verify=verify-manually), and [Shopify](https://shopify.dev/docs/apps/build/webhooks/subscribe/https#step-2-validate-the-origin-of-your-webhook-to-ensure-its-coming-from-shopify). + - **Secure API requests**: Prove that an API request comes from an authorized source + - **Generate secure tokens**: Create temporary access codes for downloads or password resets + - **Protect data**: Ensure data hasn''t been modified during transmission + +### How to use it + +The `sqlpage.hmac` function takes three inputs: +1. **Your data** - The text you want to sign (like a message or request body) +2. **Your secret key** - A password only you know (keep this safe!) +3. **Algorithm** (optional) - The hash algorithm and output format: + - `sha256` (default) - SHA-256 with hexadecimal output + - `sha256-base64` - SHA-256 with base64 output + - `sha512` - SHA-512 with hexadecimal output + - `sha512-base64` - SHA-512 with base64 output + +It returns a signature string. If someone changes even one letter in your data, the signature will be completely different. + +### Example: Verify a Webhooks signature + +When Shopify sends you a webhook (like when someone places an order), it includes a signature. Here''s how to verify it''s really from Shopify. +This supposes you store the secret key in an [environment variable](https://en.wikipedia.org/wiki/Environment_variable) named `WEBHOOK_SECRET`. + +```sql +SET body = sqlpage.request_body(); +SET secret = sqlpage.environment_variable(''WEBHOOK_SECRET''); +SET expected_signature = sqlpage.hmac($body, $secret, ''sha256''); +SET actual_signature = sqlpage.header(''X-Webhook-Signature''); + +-- redirect to an error page and stop execution if the signature does not match +SELECT + ''redirect'' as component, + ''/error.sql?err=bad_webhook_signature'' as link +WHERE $actual_signature != $expected_signature OR $actual_signature IS NULL; + +-- If we reach here, the signature is valid - process the order +INSERT INTO orders (order_data) VALUES ($body); + +SELECT ''json'' as component, ''jsonlines'' as type; +SELECT ''success'' as status; +``` + +### Example: Time-limited links + +You can create links that will be valid only for a limited time by including a signature in them. +Let''s say we have a `download.sql` page we want to link to, +but we don''t want it to be accessible to anyone who can find the link. +Sign `file_id|expires_at` with a secret. Accept only if not expired and the signature matches. + +#### Generate a signed link + +```sql +SET expires_at = datetime(''now'', ''+1 hour''); +SET token = sqlpage.hmac( + $file_id || ''|'' || $expires_at, + sqlpage.environment_variable(''DOWNLOAD_SECRET''), + ''sha256'' +); +SELECT ''/download.sql?file_id='' || $file_id || ''&expires_at='' || $expires_at || ''&token='' || $token AS link; +``` + +#### Verify the signed link + +```sql +SET expected = sqlpage.hmac( + $file_id || ''|'' || $expires_at, + sqlpage.environment_variable(''DOWNLOAD_SECRET''), + ''sha256'' +); +SELECT ''redirect'' AS component, ''/error.sql?err=expired'' AS link +WHERE $expected != $token OR $token IS NULL OR $expires_at < datetime(''now''); + +-- serve the file +``` + +### Important Security Notes + + - **Keep your secret key safe**: If your secret leaks, anyone can forge signatures and access protected pages + - **The signature is case-sensitive**: Even a single wrong letter means the signature won''t match + - **NULL handling**: Always use `IS DISTINCT FROM`, not `=` to check for hmac matches. + - `SELECT ''redirect'' as component WHERE sqlpage.hmac(...) != $signature` will not redirect if `$signature` is NULL (the signature is absent). + - `SELECT ''redirect'' as component WHERE sqlpage.hmac(...) IS DISTINCT FROM $signature` checks for both NULL and non-NULL values (but is not available in all SQL dialects). + - `SELECT ''redirect'' as component WHERE sqlpage.hmac(...) != $signature OR $signature IS NULL` is the most portable solution. +' + ); + +INSERT INTO + sqlpage_function_parameters ( + "function", + "index", + "name", + "description_md", + "type" + ) +VALUES + ( + 'hmac', + 1, + 'data', + 'The input data to compute the HMAC for. Can be any text string. Cannot be NULL.', + 'TEXT' + ), + ( + 'hmac', + 2, + 'key', + 'The secret key used to compute the HMAC. Should be kept confidential. Cannot be NULL.', + 'TEXT' + ), + ( + 'hmac', + 3, + 'algorithm', + 'The hash algorithm and output format. Optional, defaults to `sha256` (hex output). Supported values: `sha256`, `sha256-base64`, `sha512`, `sha512-base64`. Defaults to `sha256`.', + 'TEXT' + ); \ No newline at end of file diff --git a/examples/official-site/sqlpage/migrations/68_login.sql b/examples/official-site/sqlpage/migrations/68_login.sql new file mode 100644 index 00000000..a545105d --- /dev/null +++ b/examples/official-site/sqlpage/migrations/68_login.sql @@ -0,0 +1,74 @@ +INSERT INTO component(name, icon, description, introduced_in_version) VALUES + ('login', 'password-user', ' +The login component is an authentication form with numerous customization options. +It offers the main functionalities for this type of form. +The user can enter their username and password. +There are many optional attributes such as the use of icons on input fields, the insertion of a link to a page to reset the password, an option for the application to maintain the user''s identity via a cookie. +It is also possible to set the title of the form, display the company logo, or customize the appearance of the form submission button. + +This component should be used in conjunction with other components such as [authentication](component.sql?component=authentication) and [cookie](component.sql?component=cookie). +It does not implement any logic and simply collects the username and password to pass them to the code responsible for authentication. + +A few things to know : +- The form uses the POST method to transmit information to the destination page, +- The user''s username and password are entered into fields with the names `username` and `password`, +- To obtain the values of username and password, you must use the variables `:username` and `:password`, +- When you set the `remember_me_text` property, the variable `:remember` becomes available after form submission to check if the user checked the "remember me" checkbox. +', '0.39.0'); + +INSERT INTO parameter(component, name, description, type, top_level, optional) SELECT 'login', * FROM (VALUES + ('title','Title of the authentication form.','TEXT',TRUE,TRUE), + ('enctype','Form data encoding.','TEXT',TRUE,TRUE), + ('action','An optional link to a target page that will handle the results of the form. ','TEXT',TRUE,TRUE), + ('error_message','An error message to display above the form, typically shown after a failed login attempt.','TEXT',TRUE,TRUE), + ('error_message_md','A markdown error message to display above the form, typically shown after a failed login attempt.','TEXT',TRUE,TRUE), + ('username','Label and placeholder for the user account identifier text field.','TEXT',TRUE,FALSE), + ('password','Label and placeholder for the password field.','TEXT',TRUE,FALSE), + ('username_icon','Icon to display on the left side of the input field, on the same line.','ICON',TRUE,TRUE), + ('password_icon','Icon to display on the left side of the input field, on the same line.','ICON',TRUE,TRUE), + ('image','The URL of an centered image displayed before the title.','URL',TRUE,TRUE), + ('forgot_password_text','A text for the link allowing the user to reset their password. If the text is empty, the link is not displayed.','TEXT',TRUE,TRUE), + ('forgot_password_link','The link to the page allowing the user to reset their password.','TEXT',TRUE,TRUE), + ('remember_me_text','A text for the option allowing the user to request the preservation of their identity. If the text is empty, the option is not displayed.','TEXT',TRUE,TRUE), + ('footer','A text placed at the bottom of the authentication form. If both footer and footer_md are specified, footer takes precedence.','TEXT',TRUE,TRUE), + ('footer_md','A markdown text placed at the bottom of the authentication form. Useful for creating links to other pages (creating a new account, contacting technical support, etc.).','TEXT',TRUE,TRUE), + ('validate','The text to display in the button at the bottom of the form that submits the values.','TEXT',TRUE,TRUE), + ('validate_color','The color of the button at the bottom of the form that submits the values. Omit this property to use the default color.','COLOR',TRUE,TRUE), + ('validate_shape','The shape of the validation button.','TEXT',TRUE,TRUE), + ('validate_outline','A color to outline the validation button.','COLOR',TRUE,TRUE), + ('validate_size','The size of the validation button.','TEXT',TRUE,TRUE) +) x; + +-- Insert example(s) for the component +INSERT INTO example(component, description, properties) +VALUES ( + 'login', + 'Using the main options of the login component + +When the user clicks the "Sign in" button, the form is submitted to the `/examples/show_variables.sql` page. +There, you will have access to the variables: + - `:username`: the username entered by the user + - `:password`: the password entered by the user + - `:remember`: the string "on" if the checkbox was checked, or NULL if it was not checked +', + JSON( + '[ + { + "component": "login", + "action": "/examples/show_variables", + "image": "../assets/icon.webp", + "title": "Please login to your account", + "username": "Username", + "password": "Password", + "username_icon": "user", + "password_icon": "lock", + "forgot_password_text": "Forgot your password?", + "forgot_password_link": "reset_password.sql", + "remember_me_text": "Remember me", + "footer_md": "Don''t have an account? [Register here](register.sql)", + "validate": "Sign in" + } + ]' + ) + ), + ('login', 'Most basic login form', JSON('[{"component": "login"}]')); diff --git a/examples/official-site/sqlpage/migrations/69_blog_performance_guide.sql b/examples/official-site/sqlpage/migrations/69_blog_performance_guide.sql new file mode 100644 index 00000000..6dcdf111 --- /dev/null +++ b/examples/official-site/sqlpage/migrations/69_blog_performance_guide.sql @@ -0,0 +1,284 @@ + +INSERT INTO blog_posts (title, description, icon, created_at, content) +VALUES + ( + 'Performance Guide', + 'Concrete advice on how to make your SQLPage webapp fast', + 'bolt', + '2025-10-31', + ' +# Performance Guide + +SQLPage is [optimized](/performance) +to allow you to create web pages that feel snappy. +This guide contains advice on how to ensure your users never wait +behind a blank screen waiting for your pages to load. + +A lot of the advice here is not specific to SQLPage, but applies +to making SQL queries fast in general. +If you are already comfortable with SQL performance optimization, feel free to jump right to +the second part of the quide: *SQLPage-specific advice*. + +## Make your queries fast + +The best way to ensure your SQLPage webapp is fast is to ensure your +database is well managed and your SQL queries are well written. +We''ll go over the most common database performance pitfalls so that you know how to avoid them. + +### Choose the right database schema + +#### Normalize (but not too much) + +Your database schema should be [normalized](https://en.wikipedia.org/wiki/Database_normalization): +one piece of information should be stored in only one place in the database. +This is a good practice that will not only make your queries faster, +but also make it impossible to store incoherent data. +You should use meaningful natural [primary keys](https://en.wikipedia.org/wiki/Primary_key) for your tables +and resort to surrogate keys (such as auto-incremented integer ids) only when the data is not naturally keyed. +Relationships between tables should be explicitly represented by [foreign keys](https://en.wikipedia.org/wiki/Foreign_key). + +```sql +-- Products table, naturally keyed by catalog_number +CREATE TABLE product ( + catalog_number VARCHAR(20) PRIMARY KEY, + name TEXT NOT NULL, + price DECIMAL(10,2) NOT NULL +); + +-- Sales table: natural key = (sale_date, store_id, transaction_number) +-- composite primary key used since no single natural attribute alone uniquely identifies a sale +CREATE TABLE sale ( + sale_date DATE NOT NULL, + store_id VARCHAR(10) NOT NULL, + transaction_number INT NOT NULL, + product_catalog_number VARCHAR(20) NOT NULL, + quantity INT NOT NULL CHECK (quantity > 0), + PRIMARY KEY (sale_date, store_id, transaction_number), + FOREIGN KEY (product_catalog_number) REFERENCES product(catalog_number), + FOREIGN KEY (store_id) REFERENCES store(store_id) +); +``` + +Always use foreign keys instead of trying to store redundant data such as store names in the sales table. + +This way, when you need to display the list of stores in your application, you don''t have to +run a slow `select distinct store from sales`, that would have to go through your millions of sales +(*even if you have an index on the store column*), you just query the tiny `stores` table directly. + +You also need to use the right [data types](https://en.wikipedia.org/wiki/Data_type) for your columns, +otherwise you will waste a lot of space and time converting data at query time. +See [postgreSQL data types](https://www.postgresql.org/docs/current/datatype.html), +[MySQL data types](https://dev.mysql.com/doc/refman/8.0/en/data-types.html), +[Microsoft SQL Server data types](https://learn.microsoft.com/en-us/sql/t-sql/data-types/data-types-transact-sql?view=sql-server-ver16), +[SQLite data types](https://www.sqlite.org/datatype3.html). + +[Denormalization](https://en.wikipedia.org/wiki/Denormalization) can be introduced +only after you have already normalized your data, and is often not required at all. + +### Use views + +Querying normalized views can be cumbersome. +`select store_name, sum(paid_eur) from sale group by store_name` +is more readable than + +```sql +select store.name, sum(sale.paid_eur) +from sales + inner join stores on sale.store_id = store.store_id +group by store_name +``` + +To work around that, you can create views that contain +useful table joins so that you do not have to duplicate them in all your queries: + +```sql +create view enriched_sales as +select sales.sales_eur, sales.client_id, store.store_name +from sales +inner join store +``` + +#### Materialized views + +Some analytical queries just have to compute aggregated statistics over large quantities of data. +For instance, you might want to compute the total sales per store, or the total sales per product. +These queries are slow to compute when there are many rows, and you might not want to run them on every request. +You can use [materialized views](https://en.wikipedia.org/wiki/Materialized_view) to cache the results of these queries. +Materialized views are views that are stored as regular tables in the database. + +Depending on the database, you might have to refresh the materialized view manually. +You can either refresh the view manually from inside your sql pages when you detect they are outdated, +or write an external script to refresh the view periodically. + +```sql +create materialized view total_sales_per_store as +select store_name, sum(sales_eur) as total_sales +from sales +group by store_name; +``` + +### Use database indices + +When a query on a large table uses non-primary column in a `WHERE`, `GROUP BY`, `ORDER BY`, or `JOIN`, +you should create an [index](https://en.wikipedia.org/wiki/Database_index) on that column. +When multiple columns are used in the query, you should create a composite index on those columns. +When creating a composite index, the order of the columns is important. +The most frequently used columns should be first. + +```sql +create index idx_sales_store_date on sale (store_id, sale_date); -- useful for queries that filter by "store" or by "store and date" +create index idx_sales_product_date on sale (product_id, sale_date); +create index idx_sales_store_product_date on sale (store_id, product_id, sale_date); +``` + +Indexes are updated automatically when the table is modified. +They slow down the insertion and deletion of rows in the table, +but speed up the retrieval of rows in queries that use the indexed columns. + +### Query performance debugging + +When a query is slow, you can use the `EXPLAIN` keyword to see how the database will execute the query. +Just add `EXPLAIN` before the query you want to analyze. + +On PostgreSQL, you can use a tool like [explain.dalibo.com](https://explain.dalibo.com/) to visualize the query plan. + +What to look for: + - Are indexes used? You should see references to the indices you created. + - Are full table scans used? Large tables should never be scanned. + - Are expensive operations used? Such as sorting, hashing, bitmap index scans, etc. + - Are operations happening in the order you expected them to? Filtering large tables should come first. + +### Vacuum your database regularly + +On PostgreSQL, you can use the [`VACUUM`](https://www.postgresql.org/docs/current/sql-vacuum.html) command to garbage-collect and analyze a database. + +On MySQL, you can use the [`OPTIMIZE TABLE`](https://dev.mysql.com/doc/refman/8.0/en/optimize-table.html) command to reorganize it on disk and make it faster. +On Microsoft SQL Server, you can use the [`DBCC DBREINDEX`](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-dbreindex-transact-sql?view=sql-server-ver17) command to rebuild the indexes. +On SQLite, you can use the [`VACUUM`](https://www.sqlite.org/lang_vacuum.html) command to garbage-collect and analyze the database. + +### Use the right database engine + +If the amount of data you are working with is very large, does not change frequently, and you need to run complex queries on it, +you could use a specialized analytical database such as [ClickHouse](https://clickhouse.com/) or [DuckDB](https://duckdb.org/). +Such databases can be used with SQLPage by using their [ODBC](https://en.wikipedia.org/wiki/Open_Database_Connectivity) drivers. + +### Database-specific performance recommendations + + - [PostgreSQL "Performance Tips"](https://www.postgresql.org/docs/current/performance-tips.html) + - [MySQL optimization guide](https://dev.mysql.com/doc/refman/8.0/en/optimization.html) + - [Microsoft SQL Server "Monitor and Tune for Performance"](https://learn.microsoft.com/en-us/sql/relational-databases/performance/monitor-and-tune-for-performance?view=sql-server-ver17) + - [SQLite query optimizer overview](https://www.sqlite.org/optoverview.html) + +## SQLPage-specific advice + +The best way to make your SQLPage webapp fast is to make your queries fast. +Sometimes, you just don''t have control over the database, and have to run slow queries. +This section will help you minimize the impact to your users. + +### Order matters + +SQLPage executes the queries in your `.sql` files in order. +It does not start executing a query before the previous one has returned all its results. +So, if you have to execute a slow query, put it as far down in the page as possible. + +#### No heavy computation before the shell + +Every user-facing page in a SQLPage site has a [shell](/components?component=shell). + +The first queries in any sql file (all the ones that come before the [shell](/components?component=shell)) +are executed before any data has been sent to the user''s browser. +During that time, the user will see a blank screen. +So, ensure your shell comes as early as possible, and does not require any heavy computation. +If you can make your shell entirely static (independent of the database), do so, +and it will be rendered before SQLPage even finishes acquiring a database connection. + +#### Set variables just above their first usage + +For the reasons explained above, you should avoid defining all variables at the top of your sql file. +Instead, define them just above their first usage. + +### Avoid recomputing the same data multiple times + +Often, a single page will require the same pieces of data in multiple places. +In this case, avoid recomputing it on every use inside the page. + +#### Reusing a single database record + +When that data is small, store it in a sqlpage variable as JSON and then +extract the data you need using [json operations](/blog.sql?post=JSON%20in%20SQL%3A%20A%20Comprehensive%20Guide). + +```sql +set product = ( + select json_object(''name'', name, ''price'', price) -- in postgres, you can simply use row_to_json(product) + from products where id = $product_id +); + +select ''alert'' as component, ''Product'' as title, $product->>''name'' as description; +``` + +#### Reusing a large query result set + +You may have a page that lets the user filter a large dataset by many different criteria, +and then displays multiple charts and tables based on the filtered data. + +In this case, store the filtered data in a temporary table and then reuse it in multiple places. + +```sql +drop table if exists filtered_products; +create temporary table filtered_products as +select * from products where + ($category is null or category = $category) and + ($manufacturer is null or manufacturer = $manufacturer); + +select ''alert'' as component, count(*) || '' products'' as title +from filtered_products; + +select ''list'' as component; +select name as title from filtered_products; +``` + +### Reduce the number of queries + +Each query you execute has an overhead of at least the time it takes to send a packet back and forth +between SQLPage and the database. +When it''s possible, combine multiple queries into a single one, possibly using +[`UNION ALL`](https://en.wikipedia.org/wiki/Set_operations_(SQL)#UNION_operator). + +```sql +select ''big_number'' as component; + +with stats as ( + select count(*) as total, avg(price) as average_price from filtered_products +) +select ''count'' as title, stats.total as value from stats +union all +select ''average price'' as title, stats.average_price as value from stats; +``` + +### Lazy loading + +Use the [card](/component?component=card) and [modal](/component?component=modal) components +with the `embed` attribute to load data lazily. +Lazy loaded content is not sent to the user''s browser when the page initially loads, +so it does not block the initial rendering of the page and provides a better experience for +data that might be slow to load. + +### Database connections + +SQLPage uses connection pooling: it keeps multiple database connections opened, +and reuses them for consecutive requests. When it does not receive requests for a long time, +it closes idle connection. When it receives many requests, it opens new connection, +but never more than the value specified by `max_database_pool_connections` in its +[configuration](https://github.com/sqlpage/SQLPage/blob/main/configuration.md). +You can increase the value of that parameter if your website has many concurrent users and your +database is configured to allow opening many simultaneous connections. + +### SQLPage performance debugging + +When `environment` is set to `development` in its [configuration](https://github.com/sqlpage/SQLPage/blob/main/configuration.md), +SQLPage will include precise measurement of the time it spends in each of the steps it has to go through before starting to send data +back to the user''s browser. You can visualize that performance data in your browser''s network inspector. + +You can set the `RUST_LOG` environment variable to `sqlpage=debug` to make SQLPage +print detailed messages associated with precise timing for everything it does. +'); diff --git a/examples/official-site/sqlpage/migrations/70_pagination.sql b/examples/official-site/sqlpage/migrations/70_pagination.sql new file mode 100644 index 00000000..5bb50d01 --- /dev/null +++ b/examples/official-site/sqlpage/migrations/70_pagination.sql @@ -0,0 +1,252 @@ +INSERT INTO component(name, icon, description, introduced_in_version) VALUES + ('pagination', 'sailboat-2', ' +Navigation links to go to the first, previous, next, or last page of a dataset. +Useful when data is divided into pages, each containing a fixed number of rows. + +This component only handles the display of pagination. +**Your sql queries are responsible for filtering data** based on the page number passed as a URL parameter. + +This component is typically used in conjunction with a [table](?component=table), +[list](?component=list), or [card](?component=card) component. + +The pagination component displays navigation buttons (first, previous, next, last) customizable with text or icons. + +For large numbers of pages, an offset can limit the visible page links. + +A minimal example of a SQL query that uses the pagination would be: +```sql +select ''table'' as component; +select * from my_table limit 100 offset $offset; + +select ''pagination'' as component; +with recursive pages as ( + select 0 as offset + union all + select offset + 100 from pages + where offset + 100 < (select count(*) from my_table) +) +select + (offset/100+1) as contents, + sqlpage.link(sqlpage.path(), json_object(''offset'', offset)) as link, + offset = coalesce(cast($offset as integer), 0) as active +from pages; +``` + +For more advanced usage, the [pagination guide](blog.sql?post=How+to+use+the+pagination+component) provides a complete tutorial. +', '0.40.0'); + +INSERT INTO parameter(component, name, description, type, top_level, optional) SELECT 'pagination', * FROM (VALUES + -- Top-level parameters + ('first_link','A target URL to which the user should be directed to get to the first page. If none, the link is not displayed.','URL',TRUE,TRUE), + ('previous_link','A target URL to which the user should be directed to get to the previous page. If none, the link is not displayed.','URL',TRUE,TRUE), + ('next_link','A target URL to which the user should be directed to get to the next page. If none, the link is not displayed.','URL',TRUE,TRUE), + ('last_link','A target URL to which the user should be directed to get to the last page. If none, the link is not displayed.','URL',TRUE,TRUE), + ('first_title','The text displayed on the button to go to the first page.','TEXT',TRUE,TRUE), + ('previous_title','The text displayed on the button to go to the previous page.','TEXT',TRUE,TRUE), + ('next_title','The text displayed on the button to go to the next page.','TEXT',TRUE,TRUE), + ('last_title','The text displayed on the button to go to the last page.','TEXT',TRUE,TRUE), + ('first_disabled','disables the button to go to the first page.','BOOLEAN',TRUE,TRUE), + ('previous_disabled','disables the button to go to the previous page.','BOOLEAN',TRUE,TRUE), + ('next_disabled','Disables the button to go to the next page.','BOOLEAN',TRUE,TRUE), + ('last_disabled','disables the button to go to the last page.','BOOLEAN',TRUE,TRUE), + ('outline','Whether to use outline version of the pagination.','BOOLEAN',TRUE,TRUE), + ('circle','Whether to use circle version of the pagination.','BOOLEAN',TRUE,TRUE), + -- Item-level parameters (for each page) + ('contents','Page number.','INTEGER',FALSE,FALSE), + ('link','A target URL to which the user should be redirected to view the requested page of data.','URL',FALSE,TRUE), + ('offset','Whether to use offset to show only a few pages at a time. Usefull if the count of pages is too large. Defaults to false','BOOLEAN',FALSE,TRUE), + ('active','Whether the link is active or not. Defaults to false.','BOOLEAN',FALSE,TRUE) +) x; + + +-- Insert example(s) for the component +INSERT INTO example(component, description, properties) +VALUES ( + 'pagination', + 'This is an extremely simple example of a pagination component that displays only the page numbers, with the first page being the current page.', + JSON( + '[ + { + "component": "pagination" + }, + { + "contents": 1, + "link": "?component=pagination&page=1", + "active": true + }, + { + "contents": 2, + "link": "?component=pagination&page=2" + }, + { + "contents": 3, + "link": "?component=pagination&page=3" + } + ]' + ) + ), + ( + 'pagination', + 'The ouline style adds a rectangular border to each navigation link.', + JSON( + '[ + { + "component": "pagination", + "outline": true + }, + { + "contents": 1, + "link": "?component=pagination&page=1", + "active": true + }, + { + "contents": 2, + "link": "?component=pagination&page=2" + }, + { + "contents": 3, + "link": "?component=pagination&page=3" + } + ]' + ) + ), + ( + 'pagination', + 'The circle style adds a circular border to each navigation link.', + JSON( + '[ + { + "component": "pagination", + "circle": true + }, + { + "contents": 1, + "link": "?component=pagination&page=1", + "active": true + }, + { + "contents": 2, + "link": "?component=pagination&page=2" + }, + { + "contents": 3, + "link": "?component=pagination&page=3" + } + ]' + ) + ), + ( + 'pagination', + 'The following example implements navigation links that can be enabled or disabled as needed. Since a navigation link does not appear if no link is assigned to it, you must always assign a link to display it as disabled.', + JSON( + '[ + { + "component": "pagination", + "first_link": "?component=pagination", + "first_disabled": true, + "previous_link": "?component=pagination", + "previous_disabled": true, + "next_link": "#?page=2", + "last_link": "#?page=3" + + }, + { + "contents": 1, + "link": "?component=pagination&page=1", + "active": true + }, + { + "contents": 2, + "link": "?component=pagination&page=2" + }, + { + "contents": 3, + "link": "?component=pagination&page=3" + } + ]' + ) + ), + ( + 'pagination', + 'Instead of using icons, you can apply text to the navigation links.', + JSON( + '[ + { + "component": "pagination", + "first_title": "First", + "last_title": "Last", + "previous_title": "Previous", + "next_title": "Next", + "first_link": "?component=pagination", + "first_disabled": true, + "previous_link": "?component=pagination", + "previous_disabled": true, + "next_link": "#?page=2", + "last_link": "#?page=3" + + }, + { + "contents": 1, + "link": "?component=pagination&page=1", + "active": true + }, + { + "contents": 2, + "link": "?component=pagination&page=2" + }, + { + "contents": 3, + "link": "?component=pagination&page=3" + } + ]' + ) + ), + ( + 'pagination', + 'If you have a large number of pages to display, you can use an offset to represent a group of pages.', + JSON( + '[ + { + "component": "pagination", + "first_link": "#?page=1", + "previous_link": "#?page=3", + "next_link": "#?page=4", + "last_link": "#?page=99" + + }, + { + "contents": 1, + "link": "?component=pagination&page=1" + }, + { + "contents": 2, + "link": "?component=pagination&page=2" + }, + { + "contents": 3, + "link": "?component=pagination&page=3" + }, + { + "contents": 4, + "link": "?component=pagination&page=4", + "active": true + }, + { + "contents": 5, + "link": "?component=pagination&page=5" + }, + { + "contents": 6, + "link": "?component=pagination&page=6" + }, + { + "offset": true + }, + { + "contents": 99, + "link": "?component=pagination&page=99" + }, + ]' + ) + ); + \ No newline at end of file diff --git a/examples/official-site/sqlpage/migrations/71_blog_pagination.sql b/examples/official-site/sqlpage/migrations/71_blog_pagination.sql new file mode 100644 index 00000000..859b9e8c --- /dev/null +++ b/examples/official-site/sqlpage/migrations/71_blog_pagination.sql @@ -0,0 +1,163 @@ + +INSERT INTO blog_posts (title, description, icon, created_at, content) +VALUES + ( + 'How to use the pagination component', + 'A tutorial for using the pagination component', + 'sailboat-2', + '2025-11-10', + ' +# How to use the pagination component + +To display a large number of records from a database, it is often practical to split these data into pages. The user can thus navigate from one page to another, as well as directly to the first or last page. With SQLPage, it is possible to perform these operations using the pagination component. + +This component offers many options, and I recommend consulting its documentation before proceeding with the rest of this tutorial. + +Of course, this component only handles its display and does not implement any logic for data processing or state changes. In this tutorial, we will implement a complete example of using the pagination component with a SQLite database, but the code should work without modification (or with very little modification) with any relational database management system (RDBMS). + +> This article serves as a tutorial on the pagination component, rather than an advanced guide on paginated data retrieval from a database. The document employs a straightforward approach using the LIMIT and OFFSET instructions. This approach is interesting only for datasets that are big enough not to be realistically loadable on a single webpage, yet small enough for being queryable with OFFSET...LIMIT. + +## Initialization + +We first need to define two constants that indicate the maximum number of rows per page and the maximum number of pages that the component should display. + +``` +SET MAX_RECORD_PER_PAGE = 10; +SET MAX_PAGES = 10; +``` + +Now, we need to know the number of rows present in the table to be displayed. We can then calculate the number of pages required. + +``` +SET records_count = (SELECT COUNT(*) FROM album); +SET pages_count = (CAST($records_count AS INTEGER) / CAST($MAX_RECORD_PER_PAGE AS INTEGER)); +``` + +It is possible that the number of rows in the table is greater than the estimated number of pages multiplied by the number of rows per page. In this case, it is necessary to add an additional page. + +``` +SET pages_count = ( + CASE + WHEN MOD(CAST($records_count AS INTEGER),CAST($MAX_RECORD_PER_PAGE AS INTEGER)) = 0 THEN $pages_count + ELSE (CAST($pages_count AS INTEGER) + 1) + END +); +``` + +We will need to transmit the page number to be displayed in the URL using the `page` parameter. We do the same for the number of the first page (`idx_page`) appearing at the left end of the pagination component. + +![Meaning of URL parameters](blog/pagination.png) + + +If the page number or index is not present in the URL, the value of 1 is applied by default. + +``` +SET page = COALESCE($page,1); +SET idx_page = COALESCE($idx_page,1); +``` + +## Read the data + +We can now read and display the data based on the active page. To do this, we simply use a table component. + +``` +SELECT + ''table'' as component +SELECT + user_id AS id, + last_name AS "Last name", + first_name AS "First name" +FROM + users +LIMIT CAST($MAX_RECORD_PER_PAGE AS INTEGER) +OFFSET (CAST($page AS INTEGER) - 1) * CAST($MAX_RECORD_PER_PAGE AS INTEGER); +``` + +The SQL LIMIT clause allows us to not read more rows than the maximum allowed for a page. With the SQL OFFSET clause, we specify from which row the data is selected. + +On each HTML page load, the table content will be updated based on the `page` and `idx_page` variables, whose values will be extracted from the URL + +## Set up the pagination component + +Now, we need to set up the parameters that will be included in the URL for the buttons to navigate to the previous or next page. + +If the user wants to view the previous page and the current page is not the first one, the value of the `page` variable is decremented. The same applies to `idx_page`, which is decremented if its value does not correspond to the first page. + +``` +SET previous_parameters = ( + CASE + WHEN CAST($page AS INTEGER) > 1 THEN + json_object( + ''page'', (CAST($page AS INTEGER) - 1), + ''idx_page'', (CASE + WHEN CAST($idx_page AS INTEGER) > 1 THEN (CAST($idx_page AS INTEGER) - 1) + ELSE $idx_page + END) + ) + ELSE json_object() END +); +``` + +The logic is quite similar for the URL to view the next page. First, it is necessary to verify that the user is not already on the last page. Then, the `page` variable can be incremented and the `idx_page` variable updated. + +``` +SET next_parameters = ( + CASE + WHEN CAST($page AS INTEGER) < CAST($pages_count AS INTEGER) THEN + json_object( + ''page'', (CAST($page AS INTEGER) + 1), + ''idx_page'', (CASE + WHEN CAST($idx_page AS INTEGER) < (CAST($pages_count AS INTEGER) - CAST($MAX_PAGES AS INTEGER) + 1) THEN (CAST($idx_page AS INTEGER) + 1) + ELSE $idx_page + END) + ) + ELSE json_object() END +); +``` + +We can now add the pagination component, which is placed below the table displaying the data. All the logic for managing the buttons is entirely handled in SQL: +- the buttons to access the first or last page, +- the buttons to view the previous or next page, +- the enabling or disabling of these buttons based on the context. + +``` +SELECT + ''pagination'' AS component, + (CAST($page AS INTEGER) = 1) AS first_disabled, + (CAST($page AS INTEGER) = 1) AS previous_disabled, + (CAST($page AS INTEGER) = CAST($pages_count AS INTEGER)) AS next_disabled, + (CAST($page AS INTEGER) = CAST($pages_count AS INTEGER)) AS last_disabled, + sqlpage.link(sqlpage.path(), json_object(''page'', 1, ''idx_page'', 1)) as first_link, + sqlpage.link(sqlpage.path(), $previous_parameters) AS previous_link, + sqlpage.link(sqlpage.path(), $next_parameters) AS next_link, + sqlpage.link( + sqlpage.path(), + json_object(''page'', $pages_count, ''idx_page'', ( + CASE + WHEN (CAST($pages_count AS INTEGER) <= CAST($MAX_PAGES AS INTEGER)) THEN 1 + ELSE (CAST($pages_count AS INTEGER) - CAST($MAX_PAGES AS INTEGER) + 1) + END) + ) + ) AS last_link, + TRUE AS outline; +``` + +The final step is to generate the page numbers based on the number of pages and the index of the first page displayed to the left of the component. To do this, we use a recursive CTE query. + +``` +WITH RECURSIVE page_numbers AS ( + SELECT $idx_page AS number + UNION ALL + SELECT number + 1 + FROM page_numbers + LIMIT CAST($MAX_PAGES AS INTEGER) +) +SELECT + number AS contents, + sqlpage.link(sqlpage.path(), json_object(''page'', number, ''idx_page'', $idx_page)) as link, + (number = CAST($page AS INTEGER)) AS active +FROM page_numbers; +``` + +If the added page matches the content of the `page` variable, the `active` option is set to `TRUE` so that the user knows it is the current page. +'); \ No newline at end of file diff --git a/examples/official-site/sqlpage/migrations/72_set_variable.sql b/examples/official-site/sqlpage/migrations/72_set_variable.sql new file mode 100644 index 00000000..1a213656 --- /dev/null +++ b/examples/official-site/sqlpage/migrations/72_set_variable.sql @@ -0,0 +1,60 @@ +INSERT INTO + sqlpage_functions ( + "name", + "introduced_in_version", + "icon", + "description_md" + ) +VALUES + ( + 'set_variable', + '0.40.0', + 'variable', + 'Returns a URL that is the same as the current page''s URL, but with a variable set to a new value. + +This function is useful when you want to create a link that changes a parameter on the current page, while preserving other parameters. + +It is equivalent to `sqlpage.link(sqlpage.path(), json_patch(sqlpage.variables(''get''), json_object(name, value)))`. + +### Example + +Let''s say you have a list of products, and you want to filter them by category. You can use `sqlpage.set_variable` to create links that change the category filter, without losing other potential filters (like a search query or a sort order). + +```sql +select ''button'' as component, ''sm'' as size, ''center'' as justify; +select + category as title, + sqlpage.set_variable(''category'', category) as link, + case when $category = category then ''primary'' else ''secondary'' end as color +from categories; +``` + +### Parameters + - `name` (TEXT): The name of the variable to set. + - `value` (TEXT): The value to set the variable to. If `NULL` is passed, the variable is removed from the URL. +' + ); + +INSERT INTO + sqlpage_function_parameters ( + "function", + "index", + "name", + "description_md", + "type" + ) +VALUES + ( + 'set_variable', + 1, + 'name', + 'The name of the variable to set.', + 'TEXT' + ), + ( + 'set_variable', + 2, + 'value', + 'The value to set the variable to.', + 'TEXT' + ); diff --git a/examples/official-site/sqlpage/migrations/99_shared_id_class_attributes.sql b/examples/official-site/sqlpage/migrations/99_shared_id_class_attributes.sql index 47e66576..a5d438c8 100644 --- a/examples/official-site/sqlpage/migrations/99_shared_id_class_attributes.sql +++ b/examples/official-site/sqlpage/migrations/99_shared_id_class_attributes.sql @@ -18,7 +18,9 @@ FROM (VALUES ('title', TRUE), ('tracking', TRUE), ('text', TRUE), - ('carousel', TRUE) + ('carousel', TRUE), + ('login', TRUE), + ('pagination', TRUE) ); INSERT INTO parameter(component, top_level, name, description, type, optional) @@ -49,6 +51,8 @@ FROM (VALUES ('timeline', FALSE), ('title', TRUE), ('tracking', TRUE), - ('carousel', TRUE) + ('carousel', TRUE), + ('login', TRUE), + ('pagination', TRUE) ); diff --git a/examples/official-site/sqlpage/templates/color_swatch.handlebars b/examples/official-site/sqlpage/templates/color_swatch.handlebars new file mode 100644 index 00000000..e27fe787 --- /dev/null +++ b/examples/official-site/sqlpage/templates/color_swatch.handlebars @@ -0,0 +1,20 @@ +
+ {{#each_row}} +
+
+
+
+

{{name}}

+ {{#if hex}} +
{{hex}}
+ {{/if}} + {{#if css_var}} +
{{css_var}}
+ {{/if}} + {{#if description}} +

{{description}}

+ {{/if}} +
+
+ {{/each_row}} +
diff --git a/examples/official-site/sqlpage/templates/shell-home.handlebars b/examples/official-site/sqlpage/templates/shell-home.handlebars index f5839f09..b4203fa4 100644 --- a/examples/official-site/sqlpage/templates/shell-home.handlebars +++ b/examples/official-site/sqlpage/templates/shell-home.handlebars @@ -495,7 +495,7 @@ font-weight: 500; background: var(--gradient-primary); color: rgba(255, 255, 255, 0.95); - text-decoration: none; + text-decoration: none !important; border-radius: 12px; transition: all var(--transition-slow); border: 1px solid rgba(255, 255, 255, 0.1); @@ -803,6 +803,343 @@ } } + .compat-section { + position: relative; + background: radial-gradient(circle at 20% 20%, rgba(88, 125, 255, 0.18), transparent 50%), radial-gradient(circle at 80% 10%, rgba(255, 130, 255, 0.18), transparent 55%), linear-gradient(160deg, rgba(10, 19, 44, 0.95), rgba(7, 12, 26, 0.92)); + overflow: hidden; + isolation: isolate; + } + + .compat-section::before, + .compat-section::after { + content: ""; + position: absolute; + inset: 0; + pointer-events: none; + } + + .compat-section::before { + background-image: radial-gradient(2px 2px at 25px 35px, rgba(255, 255, 255, 0.6), transparent), radial-gradient(1px 1px at 120px 80px, rgba(255, 255, 255, 0.4), transparent), radial-gradient(1.5px 1.5px at 220px 140px, rgba(255, 255, 255, 0.55), transparent), radial-gradient(2px 2px at 320px 60px, rgba(255, 255, 255, 0.5), transparent), radial-gradient(1px 1px at 420px 180px, rgba(255, 255, 255, 0.4), transparent), radial-gradient(1.5px 1.5px at 520px 90px, rgba(255, 255, 255, 0.5), transparent); + background-size: 240px 220px; + opacity: 0.45; + animation: twinkle 14s linear infinite; + } + + .compat-section::after { + background: radial-gradient(450px 450px at 80% 110%, rgba(63, 104, 255, 0.35), transparent 70%); + } + + .compat-layout { + display: grid; + grid-template-columns: minmax(400px, 1fr) minmax(450px, 1.2fr); + gap: var(--gap-lg); + padding-top: calc(var(--container-padding) * 1.5); + padding-bottom: calc(var(--container-padding) * 1.5); + position: relative; + z-index: 1; + } + + .compat-intro { + display: flex; + flex-direction: column; + gap: 1.5rem; + } + + .compat-eyebrow { + font-size: 0.85rem; + letter-spacing: 0.3em; + text-transform: uppercase; + color: rgba(173, 188, 255, 0.75); + } + + .compat-intro h2 { + font-size: 3.2rem; + line-height: 1.1; + color: rgba(229, 237, 255, 0.94); + } + + .compat-intro p { + font-size: 1.15rem; + color: rgba(209, 219, 255, 0.78); + max-width: 90%; + } + + .compat-highlights { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + } + + .compat-pill { + padding: 0.45rem 0.9rem; + border-radius: 999px; + background: rgba(99, 118, 255, 0.12); + border: 1px solid rgba(143, 162, 255, 0.25); + color: rgba(207, 221, 255, 0.78); + font-size: 0.85rem; + backdrop-filter: blur(6px); + } + + .compat-showcase { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + width: 100%; + min-width: 0; + } + + .compat-orbit { + position: relative; + width: 320px; + height: 320px; + margin: 0 auto; + border-radius: 50%; + background: radial-gradient(circle, rgba(64, 84, 182, 0.35), rgba(19, 27, 51, 0.4)); + box-shadow: inset 0 0 40px rgba(127, 162, 255, 0.2), 0 20px 60px rgba(6, 12, 32, 0.7); + } + + .compat-orbit::before { + content: ""; + position: absolute; + inset: 14px; + border-radius: 50%; + border: 1px dashed rgba(162, 186, 255, 0.4); + animation: orbitSpin 26s linear infinite; + } + + .compat-orbit::after { + content: ""; + position: absolute; + inset: 44px; + border-radius: 50%; + border: 1px solid rgba(86, 112, 221, 0.2); + filter: blur(0.3px); + } + + .compat-core { + position: absolute; + width: 160px; + height: 160px; + border-radius: 50%; + background: radial-gradient(circle at 30% 30%, rgba(35, 46, 98, 0.95), rgba(20, 30, 70, 0.9)); + box-shadow: 0 0 40px rgba(150, 178, 255, 0.32); + display: flex; + align-items: center; + justify-content: center; + text-align: center; + padding: 1rem; + color: rgba(229, 236, 255, 0.95); + font-size: 0.9rem; + line-height: 1.3; + backdrop-filter: blur(6px); + margin: auto; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + .compat-node { + position: absolute; + width: 84px; + height: 84px; + border-radius: 18px; + background: rgba(10, 17, 36, 0.72); + box-shadow: 0 12px 28px rgba(2, 6, 18, 0.6), inset 0 0 24px rgba(137, 157, 255, 0.24); + display: flex; + align-items: center; + justify-content: center; + backdrop-filter: blur(10px); + border: 1px solid rgba(143, 166, 255, 0.35); + outline: 1px solid rgba(12, 18, 38, 0.6); + outline-offset: -4px; + } + + .compat-node img { + width: 54px; + height: 54px; + } + + .compat-node:nth-child(2) { + top: 6%; + left: 50%; + transform: translate(-50%, -50%); + animation: floatNode 7s ease-in-out infinite; + } + + .compat-node:nth-child(3) { + top: 50%; + right: 2%; + transform: translate(50%, -50%); + animation: floatNode 9s ease-in-out infinite; + } + + .compat-node:nth-child(4) { + bottom: 4%; + left: 50%; + transform: translate(-50%, 50%); + animation: floatNode 8s ease-in-out infinite; + } + + .compat-node:nth-child(5) { + top: 50%; + left: 2%; + transform: translate(-50%, -50%); + animation: floatNode 10s ease-in-out infinite; + } + + .compat-card { + background: rgba(12, 20, 42, 0.68); + border-radius: 18px; + padding: 1.8rem; + border: 1px solid rgba(110, 138, 238, 0.32); + box-shadow: 0 18px 40px rgba(4, 8, 22, 0.6); + backdrop-filter: blur(12px); + display: grid; + gap: 1.2rem; + width: 100%; + max-width: 500px; + min-width: 380px; + } + + .compat-card h3 { + font-size: 1.35rem; + color: rgba(228, 236, 255, 0.92); + } + + .compat-card p { + font-size: 0.98rem; + color: rgba(204, 214, 255, 0.72); + line-height: 1.6; + } + + .compat-logos { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 0.75rem; + } + + .compat-logos .db-logo { + display: flex; + align-items: center; + justify-content: center; + border-radius: 12px; + padding: 0.7rem; + background: rgba(19, 30, 58, 0.9); + border: 1px solid rgba(118, 140, 233, 0.25); + position: relative; + overflow: hidden; + } + + .compat-logos img { + width: 44px; + height: 44px; + } + + .compat-badges { + display: flex; + flex-wrap: wrap; + gap: 0.6rem 0.75rem; + } + + .compat-badge { + padding: 0.35rem 0.75rem; + border-radius: 999px; + background: rgba(57, 76, 162, 0.32); + color: rgba(225, 232, 255, 0.78); + font-size: 0.82rem; + } + + @keyframes twinkle { + 0% { + opacity: 0.4; + transform: scale(1) translate3d(0, 0, 0); + } + + 50% { + opacity: 0.75; + transform: scale(1.05) translate3d(-2%, -3%, 0); + } + + 100% { + opacity: 0.4; + transform: scale(1) translate3d(0, 0, 0); + } + } + + @keyframes orbitSpin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } + + @keyframes floatNode { + 0% { + transform: translate3d(0, 0, 0); + } + + 50% { + transform: translate3d(0, -10px, 0); + } + + 100% { + transform: translate3d(0, 0, 0); + } + } + + @media (max-width: 968px) { + .compat-layout { + grid-template-columns: 1fr; + text-align: center; + } + + .compat-intro p { + max-width: 100%; + } + + .compat-highlights { + justify-content: center; + } + + .compat-orbit { + width: 260px; + height: 260px; + } + + .compat-node { + width: 74px; + height: 74px; + } + + .compat-node img { + width: 46px; + height: 46px; + } + + .compat-logos { + grid-template-columns: repeat(3, 1fr); + } + } + + @media (max-width: 640px) { + .compat-intro h2 { + font-size: 2.6rem; + } + + .compat-logos { + grid-template-columns: repeat(2, 1fr); + } + + .compat-card { + padding: 1.4rem; + } + } + /* FOOTER STYLES */ footer { background: linear-gradient(135deg, @@ -1038,10 +1375,10 @@ -
+
-

More scalable than a spreadsheet

+

More scalable than a spreadsheet

SQL queries sort, filter, and aggregate millions of rows in milliseconds. No more slow spreadsheet formulas or memory limitations. Your app remains smooth and responsive even @@ -1054,7 +1391,7 @@

-
+

More dynamic than a dashboard

@@ -1074,6 +1411,56 @@
+
+
+
+
Database Compatibility
+

Works with your database

+

SQLPage connects to the database engine you already rely on today. + Keep your data in place and surface it through a + friendly interface that stays in sync. + If you don't have a DB yet, SQLPage comes with a built-in query engine. +

+
+
SQLite built-in
+
MySQL & MariaDB
+
PostgreSQL family
+
Microsoft SQL Server
+
ODBC bridge
+
+
+
+
+
+ Native connectors +
+
SQLite
+
MySQL
+
PostgreSQL
+
Microsoft SQL Server
+
+
+

Wherever your data lives

+

Through ODBC you can plug SQLPage into any warehouses and enterprise engines.

+
+ + + + + + + + +
+
+ No data copy + Streams query results +
+
+
+
+
+
diff --git a/examples/official-site/sqlpage/templates/typography_sample.handlebars b/examples/official-site/sqlpage/templates/typography_sample.handlebars new file mode 100644 index 00000000..647bcb0d --- /dev/null +++ b/examples/official-site/sqlpage/templates/typography_sample.handlebars @@ -0,0 +1,18 @@ +
+{{#each_row}} +
+ {{#if title}} +
{{title}}
+ {{/if}} +
+ {{sample_text}} +
+ {{#if description}} +
{{description}}
+ {{/if}} + {{#if usage}} +
Use for: {{usage}}
+ {{/if}} +
+{{/each_row}} +
diff --git a/examples/official-site/visual-identity.sql b/examples/official-site/visual-identity.sql new file mode 100644 index 00000000..5427d72c --- /dev/null +++ b/examples/official-site/visual-identity.sql @@ -0,0 +1,266 @@ +select 'http_header' as component, + 'public, max-age=600, stale-while-revalidate=3600, stale-if-error=86400' as "Cache-Control"; + +select 'dynamic' as component, json_patch(json_extract(properties, '$[0]'), json_object( + 'title', 'Visual Identity - SQLPage', + 'css', '/assets/highlightjs-and-tabler-theme.css', + 'theme', 'dark' +)) as properties +FROM example WHERE component = 'shell' LIMIT 1; + +select 'text' as component, 'Visual Identity Guide' as title, ' +This guide defines the visual identity of SQLPage for consistent brand representation. +' as contents_md; + +select 'text' as component, 'Personality' as title, ' +**Playful yet professional**: Approachable, innovative, confident, energetic, reliable, creative. +' as contents_md; + +select 'text' as component, 'Logo' as title, ' +Primary logo: `/assets/icon.webp` + +**Usage**: +- Minimum size: 48px height +- Clear space: 50% of logo height +- Do not distort, rotate, or modify +- Works on dark and light backgrounds +' as contents_md; + +select 'html' as component, ' +
+ SQLPage Logo +
+' as html; + +select 'button' as component; +select + 'Download Logo' as title, + '/assets/icon.webp' as link, + 'icon.webp' as download, + 'blue' as color, + 'download' as icon; + +select 'text' as component, 'Colors' as title, ' +Color palette extracted directly from the logo and design system. +' as contents_md; + +select 'color_swatch' as component; +select + 'Primary Cyan' as name, + '#37E5EF' as hex, + 'Main logo color - bright cyan' as description; +select + 'Teal Accent' as name, + '#2A9FAF' as hex, + 'Secondary teal from logo' as description; +select + 'Dark Navy' as name, + '#090D19' as hex, + 'Logo background - dark navy' as description; +select + 'Medium Blue' as name, + '#27314C' as hex, + 'Medium blue from logo' as description; +select + 'Blue Gray' as name, + '#304960' as hex, + 'Blue-gray from logo' as description; +select + 'Neutral Gray' as name, + '#4B4E5C' as hex, + 'Neutral gray from logo' as description; +select + 'Light Gray' as name, + '#9FA4AE' as hex, + 'Light gray from logo' as description; +select + 'Primary Background' as name, + '#0a0f1a' as hex, + 'Dark theme foundation' as description; +select + 'Primary Text' as name, + '#f7f7f7' as hex, + 'Main text color' as description; +select + 'White' as name, + '#ffffff' as hex, + 'Headings and emphasis' as description; + +select 'text' as component, 'Gradient' as title, ' +Primary gradient flows from primary cyan (#37E5EF) to teal accent (#2A9FAF). + +Use for buttons, highlights, and important elements. +' as contents_md; + +select 'text' as component, 'Typography' as title, ' +**Primary Font**: Inter + +Use Inter for all digital materials, websites, and presentations. Inter is a modern, highly legible sans-serif typeface designed specifically for user interfaces. + +**Font Source**: [Google Fonts - Inter](https://fonts.google.com/specimen/Inter) + +**Fallback Font Stack**: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif + +If Inter is not available, use the fallback stack in order. +' as contents_md; + +select 'typography_sample' as component; +select + 'Page Title' as title, + 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif' as font_family, + '64px' as font_size, + '800' as font_weight, + '1.1' as line_height, + '#ffffff' as text_color, + '-1px' as letter_spacing, + 'SQLPage Visual Identity' as sample_text, + 'Hero sections, main page titles, presentation title slides' as usage, + 'Bold, impactful text for maximum visual hierarchy' as description; + +select 'typography_sample' as component; +select + 'Section Heading' as title, + 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif' as font_family, + '56px' as font_size, + '700' as font_weight, + '1.2' as line_height, + '#ffffff' as text_color, + 'normal' as letter_spacing, + 'Section Title' as sample_text, + 'Major section breaks, chapter headings, presentation section slides' as usage, + 'Strong but slightly less prominent than page titles' as description; + +select 'typography_sample' as component; +select + 'Subsection Heading' as title, + 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif' as font_family, + '40px' as font_size, + '600' as font_weight, + '1.3' as line_height, + '#ffffff' as text_color, + 'normal' as letter_spacing, + 'Subsection Heading' as sample_text, + 'Card titles, subsection headers, content slide titles' as usage, + 'Clear hierarchy for organizing content' as description; + +select 'typography_sample' as component; +select + 'Body Text' as title, + 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif' as font_family, + '16px' as font_size, + '400' as font_weight, + '1.6' as line_height, + '#f7f7f7' as text_color, + 'normal' as letter_spacing, + 'This is body text used for paragraphs, descriptions, and main content. It should be comfortable to read with adequate spacing between lines.' as sample_text, + 'Paragraphs, descriptions, main content, presentation body text' as usage, + 'Standard reading size with comfortable line spacing' as description; + +select 'typography_sample' as component; +select + 'Small Text' as title, + 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif' as font_family, + '14px' as font_size, + '400' as font_weight, + '1.5' as line_height, + '#999999' as text_color, + 'normal' as letter_spacing, + 'Small text for captions and secondary information' as sample_text, + 'Captions, metadata, footnotes, fine print' as usage, + 'Supporting information that should not compete with main content' as description; + +select 'text' as component, 'Spacing' as title, ' +**Base unit**: 8 pixels + +**Spacing scale**: +- Extra small: 8 pixels +- Small: 16 pixels +- Medium: 24 pixels +- Large: 32 pixels +- Extra large: 48 pixels +- XXL: 64 pixels + +**Container**: Maximum width 1000 pixels, padding 40 pixels +' as contents_md; + +select 'text' as component, 'Dark Environments' as title, ' +For digital displays, presentations, and screens. +' as contents_md; + +select 'text' as component, 'Background Colors' as title, ' +- **Primary background**: #0a0f1a (deep navy blue) +- **Secondary background**: #0f1426 (slightly lighter navy) +- Use gradients with primary cyan (#37E5EF) and teal (#2A9FAF) for visual interest +' as contents_md; + +select 'text' as component, 'Text Colors' as title, ' +- **Primary text**: #f7f7f7 (almost white) - for main content +- **Secondary text**: #999999 (medium gray) - for supporting information +- **Headings**: #ffffff (pure white) - for maximum emphasis +- **Links**: #7db3e8 (bright blue) - for interactive elements +' as contents_md; + +select 'text' as component, 'Contrast Guidelines' as title, ' +- All text must meet WCAG AA contrast requirements (minimum 4.5:1 for normal text, 3:1 for large text) +- Primary text (#f7f7f7) on primary background (#0a0f1a) meets accessibility standards +- Use white (#ffffff) only for headings and emphasis +- Test all color combinations before finalizing designs +' as contents_md; + +select 'text' as component, 'Light Environments' as title, ' +For print materials, light-themed websites, and bright displays. +' as contents_md; + +select 'text' as component, 'Background Colors' as title, ' +- **Primary background**: #ffffff (white) or #f8f9fa (off-white) +- **Secondary background**: #f1f3f5 (light gray) +- Use subtle gradients or solid light colors +- Avoid pure white backgrounds in print to reduce glare +' as contents_md; + +select 'text' as component, 'Text Colors' as title, ' +- **Primary text**: #1a1a1a (near black) or #212529 (dark gray) - for main content +- **Secondary text**: #6c757d (medium gray) - for supporting information +- **Headings**: #000000 (black) or #0a0f1a (dark navy) - for emphasis +- **Links**: #2A9FAF (teal) or #37E5EF (cyan) - maintain brand colors +' as contents_md; + +select 'text' as component, 'Logo Usage in Light Environments' as title, ' +- Logo works on both light and dark backgrounds +- On light backgrounds, ensure sufficient contrast +- Consider using a darker version or adding a subtle shadow if needed +- Test logo visibility on various light backgrounds +' as contents_md; + +select 'text' as component, 'Print Guidelines' as title, ' +- Use CMYK color mode for print materials +- Convert hex colors to CMYK equivalents +- Primary cyan (#37E5EF) prints as: C: 76%, M: 0%, Y: 0%, K: 6% +- Teal accent (#2A9FAF) prints as: C: 76%, M: 9%, Y: 0%, K: 31% +- Test print samples to ensure color accuracy +- Use off-white paper (#f8f9fa equivalent) to reduce eye strain +- Minimum font size for print: 10 points (13 pixels) +- Ensure all text meets print contrast requirements +' as contents_md; + +select 'text' as component, 'Presentations' as title, ' +**Background**: Dark theme #0a0f1a with gradient overlays + +**Typography**: +- Title slide: Large bold text with gradient effect +- Body: Minimum readable size for your audience +- Code: Monospace font, minimum readable size + +**Logo**: +- Title slide: Large, centered +- Content slides: Small, bottom-right corner + +**Colors**: Use brand cyan/teal gradients (#37E5EF to #2A9FAF) for highlights. Maintain high contrast for readability. +' as contents_md; + +select 'text' as component, 'Resources' as title, ' +- Logo: `/assets/icon.webp` +- CSS Theme: `/assets/highlightjs-and-tabler-theme.css` +- [Components Documentation](/component.sql) +- [GitHub Discussions](https://github.com/sqlpage/SQLPage/discussions) +' as contents_md; diff --git a/examples/official-site/your-first-sql-website/nginx.md b/examples/official-site/your-first-sql-website/nginx.md index b910114a..9e156135 100644 --- a/examples/official-site/your-first-sql-website/nginx.md +++ b/examples/official-site/your-first-sql-website/nginx.md @@ -65,6 +65,23 @@ sudo systemctl reload nginx Your SQLPage instance is now hosted behind a reverse proxy using NGINX. You can access it by visiting `http://example.com`. + +### Streaming-friendly proxy settings + +SQLPage streams HTML by default so the browser can render results while the database is still sending rows. +If you have slow SQL queries (you shouldn't), you can add the following directive to your location block: + +```nginx +proxy_buffering off; +``` + +That will allow users to start seeing the top of your pages faster, +but will increase the load on your SQLPage server, and reduce the amount of users you can serve concurrently. + +Refer to the official documentation for [proxy buffering](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering), [gzip](https://nginx.org/en/docs/http/ngx_http_gzip_module.html), and [chunked transfer](https://nginx.org/en/docs/http/ngx_http_core_module.html#chunked_transfer_encoding) when tuning these values. + +When SQLPage sits behind a reverse proxy, set `compress_responses` to `false` [in `sqlpage.json`](https://github.com/sqlpage/SQLPage/blob/main/configuration.md) so that NGINX compresses once at the edge. + ### URL Rewriting URL rewriting is a powerful feature that allows you to manipulate URLs to make them more readable, search-engine-friendly, and easy to maintain. diff --git a/examples/official-site/your-first-sql-website/tutorial.md b/examples/official-site/your-first-sql-website/tutorial.md index 79819136..582a9463 100644 --- a/examples/official-site/your-first-sql-website/tutorial.md +++ b/examples/official-site/your-first-sql-website/tutorial.md @@ -91,6 +91,7 @@ Later, when you want to deploy your website online, you can switch back to a per - a PostgreSQL-compatible server with `postgres://user:password@host/database` ([see options](https://docs.rs/sqlx-oldapi/latest/sqlx_oldapi/postgres/struct.PgConnectOptions.html)), - a MySQL-compatible server with `mysql://user:password@host/database` ([see options](https://docs.rs/sqlx-oldapi/latest/sqlx_oldapi/mysql/struct.MySqlConnectOptions.html)), - a Microsoft SQL Server with `mssql://user:password@host/database` ([see options](https://docs.rs/sqlx-oldapi/latest/sqlx_oldapi/mssql/struct.MssqlConnectOptions.html#method.from_str), [note about named instances](https://github.com/sqlpage/SQLPage/issues/92)), +- any ODBC-compatible database like DuckDB, ClickHouse, Databricks, Snowflake, BigQuery, Oracle, Db2, and many more. See [ODBC database connection instructions](https://github.com/sqlpage/SQLPage#odbc-setup). > If `user` or `password` **contains special characters**, you should [**percent-encode**](https://en.wikipedia.org/wiki/Percent-encoding) them. > @@ -193,5 +194,6 @@ If you prefer to host your website yourself, you can use a cloud provider or a V - Check out [learnsqlpage.com](https://learnsqlpage.com) by Nick Antonaccio for an in-depth tutorial with many examples - Read the [SQLPage documentation](/documentation.sql) to learn about all the components available in SQLPage +- Read about [SQLPage's extensions to SQL](/extensions-to-sql) for a specification of the SQL syntax you can use in SQLPage, the data types used when exchanging data with the browser and with the database, a clear explanation of how *SQLPage variables* and *SQLPage functions* work. - Join the [SQLPage community](https://github.com/sqlpage/SQLPage/discussions) to ask questions and share your projects - If you like videos better, check this series that shows how to build and deploy your app from scratch [SQLPage on Youtube](https://www.youtube.com/playlist?list=PLTue_qIAHxAf9fEjBY2CN0N_5XOiffOk_) \ No newline at end of file diff --git a/examples/single sign on/index.sql b/examples/single sign on/index.sql index 4557d187..bc410f69 100644 --- a/examples/single sign on/index.sql +++ b/examples/single sign on/index.sql @@ -14,5 +14,14 @@ WHERE $email IS NULL; -- For logged-in users SELECT 'text' as component, 'Welcome back, ' || sqlpage.user_info('name') || '!' as title, - 'You are logged in as ' || sqlpage.user_info('email') || '. You can now access the [protected page](/protected) or [log out](/logout).' as contents_md + 'You are logged in as ' || sqlpage.user_info('email') || + '. You can now access the [protected page](/protected) or [log out](' || + -- Secure OIDC logout with CSRF protection + -- This redirects to /sqlpage/oidc_logout which: + -- 1. Verifies the CSRF token + -- 2. Removes the auth cookies + -- 3. Redirects to the OIDC provider's logout endpoint + -- 4. Finally redirects back to the homepage + sqlpage.oidc_logout_url() + || ').' as contents_md WHERE $email IS NOT NULL; diff --git a/examples/single sign on/logout.sql b/examples/single sign on/logout.sql deleted file mode 100644 index 855d5c68..00000000 --- a/examples/single sign on/logout.sql +++ /dev/null @@ -1,12 +0,0 @@ --- remove the session cookie -select - 'cookie' as component, - 'sqlpage_auth' as name, - true as remove; - -select - 'redirect' as component, - sqlpage.link('http://localhost:8181/realms/sqlpage_demo/protocol/openid-connect/logout', json_object( - 'post_logout_redirect_uri', 'http://localhost:8080/', - 'id_token_hint', sqlpage.cookie('sqlpage_auth') - )) as link; diff --git a/examples/single sign on/protected/index.sql b/examples/single sign on/protected/index.sql index 8956a6f6..b82ffe3f 100644 --- a/examples/single sign on/protected/index.sql +++ b/examples/single sign on/protected/index.sql @@ -1,7 +1,10 @@ set user_email = sqlpage.user_info('email'); select 'shell' as component, 'My secure app' as title, - json_object('title', 'Log Out', 'link', '/logout') as menu_item; + json_object( + 'title', 'Log Out', + 'link', sqlpage.oidc_logout_url() + ) as menu_item; select 'text' as component, 'You''re in, '|| sqlpage.user_info('name') || ' !' as title, diff --git a/examples/user-authentication/docker-compose.yml b/examples/user-authentication/docker-compose.yml index 5108ac1e..96b78c0e 100644 --- a/examples/user-authentication/docker-compose.yml +++ b/examples/user-authentication/docker-compose.yml @@ -1,6 +1,8 @@ services: web: image: lovasoa/sqlpage:main # main is cutting edge, use sqlpage/SQLPage:latest for the latest stable version + build: + context: "../.." ports: - "8080:8080" volumes: diff --git a/examples/user-authentication/signin.sql b/examples/user-authentication/signin.sql index 4057e44d..bab0e883 100644 --- a/examples/user-authentication/signin.sql +++ b/examples/user-authentication/signin.sql @@ -1,14 +1,9 @@ -SELECT 'form' AS component, +SELECT 'login' AS component, + 'login.sql' AS action, 'Sign in' AS title, - 'Sign in' AS validate, - 'login.sql' AS action; - -SELECT 'username' AS name; -SELECT 'password' AS name, 'password' AS type; - -SELECT 'alert' as component, - 'Sorry' as title, - 'We could not authenticate you. Please log in or [create an account](signup.sql).' as description_md, - 'alert-circle' as icon, - 'red' as color -WHERE $error IS NOT NULL; \ No newline at end of file + 'Username' AS username, + 'Password' AS password, + 'user' AS username_icon, + 'lock' AS password_icon, + case when $error is not null then 'We could not authenticate you. Please log in or [create an account](signup.sql).' end as error_message_md, + 'Sign in' AS validate; \ No newline at end of file diff --git a/lambda.Dockerfile b/lambda.Dockerfile index e627a21a..c91ef7f5 100644 --- a/lambda.Dockerfile +++ b/lambda.Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.87-alpine as builder +FROM rust:1.91-alpine AS builder RUN rustup component add clippy rustfmt RUN apk add --no-cache musl-dev zip WORKDIR /usr/src/sqlpage @@ -13,7 +13,7 @@ RUN mv target/release/sqlpage bootstrap && \ ldd bootstrap && \ zip -9 -r deploy.zip bootstrap index.sql -FROM public.ecr.aws/lambda/provided:al2 as runner +FROM public.ecr.aws/lambda/provided:al2 AS runner COPY --from=builder /usr/src/sqlpage/bootstrap /main COPY --from=builder /usr/src/sqlpage/index.sql ./index.sql ENTRYPOINT ["/main"] \ No newline at end of file diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..59986f41 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,20 @@ +# Docker Build Scripts + +This directory contains scripts used by the Dockerfile to build SQLPage with cross-compilation support. + +## Scripts + +- **`setup-cross-compilation.sh`**: Sets up the cross-compilation environment based on target and build architectures. Handles system dependencies, cross-compiler installation, and libgcc extraction for runtime. +- **`build-dependencies.sh`**: Builds only the project dependencies for Docker layer caching +- **`build-project.sh`**: Builds the final SQLPage binary + +## Usage + +These scripts are automatically copied and executed by the Dockerfile during the build process. They handle: + +- Cross-compilation setup for different architectures (amd64, arm64, arm) +- System dependencies installation +- Cargo build configuration with appropriate linkers +- Library extraction for runtime + +The scripts use temporary files in `/tmp/` to pass configuration between stages and export environment variables for use in subsequent build steps. diff --git a/scripts/build-dependencies.sh b/scripts/build-dependencies.sh new file mode 100755 index 00000000..5a55754a --- /dev/null +++ b/scripts/build-dependencies.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -euo pipefail + +source /tmp/build-env.sh + +echo "Building dependencies for target: $TARGET" + +cargo build \ + --target "$TARGET" \ + --config "target.$TARGET.linker=\"$LINKER\"" \ + --features odbc-static \ + --profile superoptimized diff --git a/scripts/build-project.sh b/scripts/build-project.sh new file mode 100755 index 00000000..ac595b61 --- /dev/null +++ b/scripts/build-project.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -euo pipefail + +source /tmp/build-env.sh + +echo "Building project for target: $TARGET" + +cargo build \ + --target "$TARGET" \ + --config "target.$TARGET.linker=\"$LINKER\"" \ + --features odbc-static \ + --profile superoptimized + +mv "target/$TARGET/superoptimized/sqlpage" sqlpage.bin diff --git a/scripts/setup-cross-compilation.sh b/scripts/setup-cross-compilation.sh new file mode 100755 index 00000000..83531abb --- /dev/null +++ b/scripts/setup-cross-compilation.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -euo pipefail + +TARGETARCH="$1" +BUILDARCH="$2" +BINDGEN_EXTRA_CLANG_ARGS="" + +apt-get update + +if [ "$TARGETARCH" = "$BUILDARCH" ]; then + TARGET="$(rustup target list --installed | head -n1)" + LINKER="gcc" + apt-get install -y gcc libgcc-s1 make + LIBDIR="/lib/$(gcc -print-multiarch)" +elif [ "$TARGETARCH" = "arm64" ]; then + TARGET="aarch64-unknown-linux-gnu" + LINKER="aarch64-linux-gnu-gcc" + apt-get install -y gcc-aarch64-linux-gnu libgcc-s1-arm64-cross make + LIBDIR="/usr/aarch64-linux-gnu/lib" +elif [ "$TARGETARCH" = "arm" ]; then + TARGET="armv7-unknown-linux-gnueabihf" + LINKER="arm-linux-gnueabihf-gcc" + apt-get install -y gcc-arm-linux-gnueabihf libgcc-s1-armhf-cross make cmake libclang-dev + cargo install --force --locked bindgen-cli + SYSROOT=$(arm-linux-gnueabihf-gcc -print-sysroot) + BINDGEN_EXTRA_CLANG_ARGS="--sysroot=$SYSROOT -I$SYSROOT/usr/include -I$SYSROOT/usr/include/arm-linux-gnueabihf" + LIBDIR="/usr/arm-linux-gnueabihf/lib" +else + echo "Unsupported cross compilation target: $TARGETARCH" + exit 1 +fi + +mkdir -p /tmp/sqlpage-libs +cp "$LIBDIR/libgcc_s.so.1" /tmp/sqlpage-libs/ +rustup target add "$TARGET" + +{ + echo "export TARGET='$TARGET'" + echo "export LINKER='$LINKER'" + if [ -n "$BINDGEN_EXTRA_CLANG_ARGS" ]; then + printf "export BINDGEN_EXTRA_CLANG_ARGS=%q\n" "$BINDGEN_EXTRA_CLANG_ARGS" + fi +} > /tmp/build-env.sh diff --git a/sqlpage/apexcharts.js b/sqlpage/apexcharts.js index 55cae680..a0f46367 100644 --- a/sqlpage/apexcharts.js +++ b/sqlpage/apexcharts.js @@ -1,4 +1,4 @@ -/* !include https://cdn.jsdelivr.net/npm/apexcharts@5.3.0/dist/apexcharts.min.js */ +/* !include https://cdn.jsdelivr.net/npm/apexcharts@5.3.6/dist/apexcharts.min.js */ sqlpage_chart = (() => { function sqlpage_chart() { @@ -12,20 +12,21 @@ sqlpage_chart = (() => { } const tblrColors = [ - ["blue", "#1c31dc", "#3399FF"], - ["red", "#d11414", "#FF6666"], - ["green", "#00CC00", "#66FF66"], - ["pink", "#FF1493", "#FF69B4"], - ["purple", "#ae3ec9", "#c264d9"], - ["orange", "#f76707", "#FFA500"], - ["cyan", "#00CED1", "#40E0D0"], - ["teal", "#008080", "#20B2AA"], - ["yellow", "#FFD700", "#FFFF00"], - ["indigo", "#4B0082", "#6F00FF"], - ["azure", "#007FFF", "#1E90FF"], - ["gray", "#A0A0A0", "#D3D3D3"], + ["blue", "#1c7ed6", "#339af0"], + ["red", "#f03e3e", "#ff6b6b"], + ["green", "#37b24d", "#51cf66"], + ["pink", "#d6336c", "#f06595"], + ["purple", "#ae3ec9", "#cc5de8"], + ["orange", "#f76707", "#ff922b"], + ["cyan", "#1098ad", "#22b8cf"], + ["teal", "#0ca678", "#20c997"], + ["yellow", "#f59f00", "#fcc419"], + ["indigo", "#4263eb", "#5c7cfa"], + ["lime", "#74b816", "#94d82d"], + ["azure", "#339af0", "#339af0"], + ["gray", "#495057", "#adb5bd"], ["black", "#000000", "#000000"], - ["white", "#E9E9E9", "#F5F5F5"], + ["white", "#ffffff", "#f8f9fa"], ]; const colorNames = Object.fromEntries( tblrColors.flatMap(([name, dark, light]) => [ @@ -157,7 +158,7 @@ sqlpage_chart = (() => { dataLabels: { enabled: !!data.labels, dropShadow: { - enabled: false, + enabled: true, color: "var(--tblr-primary-bg-subtle)", }, formatter: diff --git a/sqlpage/sqlpage.css b/sqlpage/sqlpage.css index 2b9a436f..aa34cb87 100644 --- a/sqlpage/sqlpage.css +++ b/sqlpage/sqlpage.css @@ -22,17 +22,6 @@ td > p { margin-bottom: 0 !important; } -/* https://github.com/tabler/tabler/issues/1648 */ -* { - scrollbar-color: var(--tblr-primary) var(--tblr-bg-surface-dark) !important; -} -::-webkit-scrollbar { - width: 4px !important; -} -::-webkit-scrollbar-thumb { - background: var(--tblr-primary) !important; -} - .text-secondary a { color: inherit; text-decoration: underline; diff --git a/sqlpage/sqlpage.js b/sqlpage/sqlpage.js index 46119635..07ccd349 100644 --- a/sqlpage/sqlpage.js +++ b/sqlpage/sqlpage.js @@ -354,8 +354,7 @@ function open_modal_for_hash() { const hash = window.location.hash.substring(1); if (!hash) return; const modal = document.getElementById(hash); - const classes = modal.classList; - if (!modal || !classes.contains("modal")) return; + if (!modal || !modal.classList.contains("modal")) return; const bootstrap_modal = window.tabler.bootstrap.Modal.getOrCreateInstance(modal); bootstrap_modal.show(); diff --git a/sqlpage/templates/carousel.handlebars b/sqlpage/templates/carousel.handlebars index 7d85775e..d65762ed 100644 --- a/sqlpage/templates/carousel.handlebars +++ b/sqlpage/templates/carousel.handlebars @@ -5,7 +5,7 @@
{{title}}
{{/if}} -