diff --git a/README.md b/README.md index b7a2cf0..7f81a4c 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ without Tauri: | -------------------- | --------------- | ---------------- | ------------------- | | SELECT (multiple) | `fetchAll()` | Read pool | Multiple concurrent | | SELECT (single) | `fetchOne()` | Read pool | Multiple concurrent | +| SELECT (paginated) | `fetchPage()` | Read pool | Multiple concurrent | | INSERT/UPDATE/DELETE | `execute()` | Write connection | Serialized | | DDL (CREATE, etc.) | `execute()` | Write connection | Serialized | @@ -268,6 +269,64 @@ if (user) { } ``` +### Pagination + +When working with large result sets, loading all rows at once can cause +performance degradation and excessive memory usage on both the Rust and +TypeScript sides. The plugin provides built-in pagination to fetch data in +fixed-size pages, keeping memory usage bounded and queries fast regardless +of total row count. + +#### Why Keyset Pagination + +The plugin uses keyset (cursor-based) pagination rather than traditional +OFFSET-based pagination. With OFFSET, the database must scan and discard +all skipped rows on every page request, making deeper pages progressively +slower. Keyset pagination uses indexed column values from the last row of +the current page to seek directly to the next page, keeping query time +constant no matter how far you paginate. + +```typescript +import type { KeysetColumn } from '@silvermine/tauri-plugin-sqlite'; + +type Post = { id: number; title: string; category: string; score: number }; + +const keyset: KeysetColumn[] = [ + { name: 'category', direction: 'asc' }, + { name: 'score', direction: 'desc' }, + { name: 'id', direction: 'asc' }, +]; + +// First page +const page = await db.fetchPage( + 'SELECT id, title, category, score FROM posts', + [], + keyset, + 25, +); + +// Next page (forward) — pass the cursor from the previous page +if (page.nextCursor) { + const nextPage = await db.fetchPage( + 'SELECT id, title, category, score FROM posts', + [], + keyset, + 25, + ).after(page.nextCursor); + + // Previous page (backward) — rows are returned in original sort order + const prevPage = await db.fetchPage( + 'SELECT id, title, category, score FROM posts', + [], + keyset, + 25, + ).before(page.nextCursor); +} +``` + +The base query must not contain `ORDER BY` or `LIMIT` clauses — the builder +appends these automatically based on the keyset definition. + ### Transactions For most cases, use `executeTransaction()` to run multiple statements atomically: @@ -338,8 +397,8 @@ Each attached database gets a schema name that acts as a namespace for its tables. **Builder Pattern:** All query methods (`execute`, `executeTransaction`, -`fetchAll`, `fetchOne`) return builders that support `.attach()` for -cross-database operations. +`fetchAll`, `fetchOne`, `fetchPage`) return builders that support `.attach()` +for cross-database operations. ```typescript // Join data from multiple databases @@ -509,6 +568,7 @@ await db.remove(); // Close and DELETE database file(s) - irreversible | `beginInterruptibleTransaction(statements)` | Begin interruptible transaction, returns `InterruptibleTransaction` | | `fetchAll(query, values?)` | Execute SELECT, return all rows | | `fetchOne(query, values?)` | Execute SELECT, return single row or `undefined` | +| `fetchPage(query, values, keyset, pageSize)` | Keyset pagination, returns `FetchPageBuilder` | | `close()` | Close connection, returns `true` if was loaded | | `remove()` | Close and delete database file(s), returns `true` if was loaded | | `observe(tables, config?)` | Enable change observation for tables | @@ -517,12 +577,15 @@ await db.remove(); // Close and DELETE database file(s) - irreversible ### Builder Methods -All query methods (`execute`, `executeTransaction`, `fetchAll`, `fetchOne`) -return builders that are directly awaitable and support method chaining: +All query methods (`execute`, `executeTransaction`, `fetchAll`, `fetchOne`, +`fetchPage`) return builders that are directly awaitable and support method +chaining: | Method | Description | | ------ | ----------- | | `attach(specs)` | Attach databases for cross-database queries, returns `this` | +| `after(cursor)` | Set cursor for forward pagination (`FetchPageBuilder` only), returns `this` | +| `before(cursor)` | Set cursor for backward pagination (`FetchPageBuilder` only), returns `this` | | `await builder` | Execute the query (builders implement `PromiseLike`) | ### InterruptibleTransaction Methods @@ -569,6 +632,19 @@ interface ObserverConfig { captureValues?: boolean; // default: true } +type SortDirection = 'asc' | 'desc'; + +interface KeysetColumn { + name: string; // Column name in the query result set + direction: SortDirection; +} + +interface KeysetPage> { + rows: T[]; + nextCursor: SqlValue[] | null; // Cursor to continue pagination, null when no more pages + hasMore: boolean; +} + type ChangeOperation = 'insert' | 'update' | 'delete'; type ColumnValue = @@ -647,6 +723,47 @@ if let Some(user_data) = user { } ``` +### Pagination (Rust) + +See [Pagination](#pagination) above for background on why the plugin uses +keyset pagination. The Rust API works the same way via `fetch_page`: + +```rust +use sqlx_sqlite_toolkit::pagination::KeysetColumn; + +let keyset = vec![ + KeysetColumn::asc("category"), + KeysetColumn::desc("score"), + KeysetColumn::asc("id"), +]; + +// First page +let page = db.fetch_page( + "SELECT id, title, category, score FROM posts".into(), + vec![], + keyset.clone(), + 25, +).await?; + +// Next page (forward) +if let Some(cursor) = page.next_cursor { + let next = db.fetch_page( + "SELECT id, title, category, score FROM posts".into(), + vec![], + keyset.clone(), + 25, + ).after(cursor.clone()).await?; + + // Previous page (backward) — rows returned in original sort order + let prev = db.fetch_page( + "SELECT id, title, category, score FROM posts".into(), + vec![], + keyset, + 25, + ).before(cursor).await?; +} +``` + ### Simple Transactions Use `execute_transaction()` for atomic execution of multiple statements: @@ -771,6 +888,7 @@ db.remove().await?; // Close and DELETE database file(s) | `begin_interruptible_transaction()` | Begin interruptible transaction (builder) | | `fetch_all(query, values)` | Fetch all rows | | `fetch_one(query, values)` | Fetch single row | +| `fetch_page(query, values, keyset, page_size)` | Keyset pagination (builder, supports `.after()`, `.before()`, `.attach()`) | | `close()` | Close connection | | `remove()` | Close and delete database file(s) | @@ -818,6 +936,18 @@ fn main() { } ``` +## Examples + +Working Tauri demo apps are in the [`examples/`](examples) directory: + + * **[`observer-demo`](examples/observer-demo)** — Real-time change + notifications with live streaming of inserts, updates, and deletes + * **[`pagination-demo`](examples/pagination-demo)** — Keyset pagination + with a virtualized list and performance metrics + +See the [toolkit crate README](crates/sqlx-sqlite-toolkit/README.md#examples) +for setup instructions. + ## Development This project follows diff --git a/api-iife.js b/api-iife.js index 500169e..cf3a7fb 100644 --- a/api-iife.js +++ b/api-iife.js @@ -1 +1 @@ -if("__TAURI__"in window){var __TAURI_PLUGIN_SQLITE__=function(t){"use strict";function e(t,e,s,i){if("function"==typeof e?t!==e||!i:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?i:"a"===s?i.call(t):i?i.value:e.get(t)}function s(t,e,s,i,a){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,s),s}var i,a,n,r;"function"==typeof SuppressedError&&SuppressedError;const h="__TAURI_TO_IPC_KEY__";class c{constructor(t){i.set(this,void 0),a.set(this,0),n.set(this,[]),r.set(this,void 0),s(this,i,t||(()=>{})),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}(t=>{const h=t.index;if("end"in t)return void(h==e(this,a,"f")?this.cleanupCallback():s(this,r,h));const c=t.message;if(h==e(this,a,"f")){for(e(this,i,"f").call(this,c),s(this,a,e(this,a,"f")+1);e(this,a,"f")in e(this,n,"f");){const t=e(this,n,"f")[e(this,a,"f")];e(this,i,"f").call(this,t),delete e(this,n,"f")[e(this,a,"f")],s(this,a,e(this,a,"f")+1)}e(this,a,"f")===e(this,r,"f")&&this.cleanupCallback()}else e(this,n,"f")[h]=c})}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(t){s(this,i,t)}get onmessage(){return e(this,i,"f")}[(i=new WeakMap,a=new WeakMap,n=new WeakMap,r=new WeakMap,h)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[h]()}}async function u(t,e={},s){return window.__TAURI_INTERNALS__.invoke(t,e,s)}class o{constructor(t,e){this._dbPath=t,this._transactionId=e}async read(t,e){return await u("plugin:sqlite|transaction_read",{token:{dbPath:this._dbPath,transactionId:this._transactionId},query:t,values:e??[]})}async continueWith(t){const e=await u("plugin:sqlite|transaction_continue",{token:{dbPath:this._dbPath,transactionId:this._transactionId},action:{type:"Continue",statements:t.map(([t,e])=>({query:t,values:e??[]}))}});return new o(e.dbPath,e.transactionId)}async commit(){await u("plugin:sqlite|transaction_continue",{token:{dbPath:this._dbPath,transactionId:this._transactionId},action:{type:"Commit"}})}async rollback(){await u("plugin:sqlite|transaction_continue",{token:{dbPath:this._dbPath,transactionId:this._transactionId},action:{type:"Rollback"}})}}class l{constructor(t){this._subscriptionId=t}get id(){return this._subscriptionId}async unsubscribe(){return await u("plugin:sqlite|unsubscribe",{subscriptionId:this._subscriptionId})}}class _{constructor(t,e,s,i=[]){this._db=t,this._query=e,this._bindValues=s,this._attached=i}attach(t){return this._attached=t,this}then(t,e){return this._execute().then(t,e)}async _execute(){return await u("plugin:sqlite|fetch_all",{db:this._db.path,query:this._query,values:this._bindValues,attached:this._attached.length>0?this._attached:null})}}class d{constructor(t,e,s,i=[]){this._db=t,this._query=e,this._bindValues=s,this._attached=i}attach(t){return this._attached=t,this}then(t,e){return this._execute().then(t,e)}async _execute(){return await u("plugin:sqlite|fetch_one",{db:this._db.path,query:this._query,values:this._bindValues,attached:this._attached.length>0?this._attached:null})}}class b{constructor(t,e,s,i=[]){this._db=t,this._query=e,this._bindValues=s,this._attached=i}attach(t){return this._attached=t,this}then(t,e){return this._execute().then(t,e)}async _execute(){const[t,e]=await u("plugin:sqlite|execute",{db:this._db.path,query:this._query,values:this._bindValues,attached:this._attached.length>0?this._attached:null});return{lastInsertId:e,rowsAffected:t}}}class p{constructor(t,e,s=[]){this._db=t,this._initialStatements=e,this._attached=s}attach(t){return this._attached=t,this}then(t,e){return this._execute().then(t,e)}async _execute(){const t=await u("plugin:sqlite|begin_interruptible_transaction",{db:this._db.path,initialStatements:this._initialStatements.map(([t,e])=>({query:t,values:e??[]})),attached:this._attached.length>0?this._attached:null});return new o(t.dbPath,t.transactionId)}}class w{constructor(t,e,s=[]){this._db=t,this._statements=e,this._attached=s}attach(t){return this._attached=t,this}then(t,e){return this._execute().then(t,e)}async _execute(){return await u("plugin:sqlite|execute_transaction",{db:this._db.path,statements:this._statements.map(([t,e])=>({query:t,values:e??[]})),attached:this._attached.length>0?this._attached:null})}}class f{constructor(t){this.path=t}static async load(t,e){const s=await u("plugin:sqlite|load",{db:t,customConfig:e});return new f(s)}static get(t){return new f(t)}static async close_all(){await u("plugin:sqlite|close_all")}execute(t,e){return new b(this,t,e??[])}executeTransaction(t){return new w(this,t)}fetchAll(t,e){return new _(this,t,e??[])}fetchOne(t,e){return new d(this,t,e??[])}async observe(t,e){await u("plugin:sqlite|observe",{db:this.path,tables:t,config:e??null})}async subscribe(t,e){const s=new c;s.onmessage=e;const i=await u("plugin:sqlite|subscribe",{db:this.path,tables:t,onEvent:s});return new l(i)}async unobserve(){await u("plugin:sqlite|unobserve",{db:this.path})}async close(){return await u("plugin:sqlite|close",{db:this.path})}async remove(){return await u("plugin:sqlite|remove",{db:this.path})}beginInterruptibleTransaction(t){return new p(this,t)}async getMigrationEvents(){return await u("plugin:sqlite|get_migration_events",{db:this.path})}}return t.InterruptibleTransaction=o,t.Subscription=l,t.default=f,Object.defineProperty(t,"__esModule",{value:!0}),t}({});Object.defineProperty(window.__TAURI__,"sqlite",{value:__TAURI_PLUGIN_SQLITE__})} +if("__TAURI__"in window){var __TAURI_PLUGIN_SQLITE__=function(t){"use strict";function e(t,e,s,i){if("function"==typeof e?t!==e||!i:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?i:"a"===s?i.call(t):i?i.value:e.get(t)}function s(t,e,s,i,a){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,s),s}var i,a,n,h;"function"==typeof SuppressedError&&SuppressedError;const r="__TAURI_TO_IPC_KEY__";class c{constructor(t){i.set(this,void 0),a.set(this,0),n.set(this,[]),h.set(this,void 0),s(this,i,t||(()=>{})),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}(t=>{const r=t.index;if("end"in t)return void(r==e(this,a,"f")?this.cleanupCallback():s(this,h,r));const c=t.message;if(r==e(this,a,"f")){for(e(this,i,"f").call(this,c),s(this,a,e(this,a,"f")+1);e(this,a,"f")in e(this,n,"f");){const t=e(this,n,"f")[e(this,a,"f")];e(this,i,"f").call(this,t),delete e(this,n,"f")[e(this,a,"f")],s(this,a,e(this,a,"f")+1)}e(this,a,"f")===e(this,h,"f")&&this.cleanupCallback()}else e(this,n,"f")[r]=c})}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(t){s(this,i,t)}get onmessage(){return e(this,i,"f")}[(i=new WeakMap,a=new WeakMap,n=new WeakMap,h=new WeakMap,r)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[r]()}}async function u(t,e={},s){return window.__TAURI_INTERNALS__.invoke(t,e,s)}class _{constructor(t,e){this._dbPath=t,this._transactionId=e}async read(t,e){return await u("plugin:sqlite|transaction_read",{token:{dbPath:this._dbPath,transactionId:this._transactionId},query:t,values:e??[]})}async continueWith(t){const e=await u("plugin:sqlite|transaction_continue",{token:{dbPath:this._dbPath,transactionId:this._transactionId},action:{type:"Continue",statements:t.map(([t,e])=>({query:t,values:e??[]}))}});return new _(e.dbPath,e.transactionId)}async commit(){await u("plugin:sqlite|transaction_continue",{token:{dbPath:this._dbPath,transactionId:this._transactionId},action:{type:"Commit"}})}async rollback(){await u("plugin:sqlite|transaction_continue",{token:{dbPath:this._dbPath,transactionId:this._transactionId},action:{type:"Rollback"}})}}class l{constructor(t){this._subscriptionId=t}get id(){return this._subscriptionId}async unsubscribe(){return await u("plugin:sqlite|unsubscribe",{subscriptionId:this._subscriptionId})}}class o{constructor(t,e,s,i=[]){this._db=t,this._query=e,this._bindValues=s,this._attached=i}attach(t){return this._attached=t,this}then(t,e){return this._execute().then(t,e)}async _execute(){return await u("plugin:sqlite|fetch_all",{db:this._db.path,query:this._query,values:this._bindValues,attached:this._attached.length>0?this._attached:null})}}class d{constructor(t,e,s,i=[]){this._db=t,this._query=e,this._bindValues=s,this._attached=i}attach(t){return this._attached=t,this}then(t,e){return this._execute().then(t,e)}async _execute(){return await u("plugin:sqlite|fetch_one",{db:this._db.path,query:this._query,values:this._bindValues,attached:this._attached.length>0?this._attached:null})}}class b{constructor(t,e,s,i,a){this._db=t,this._query=e,this._bindValues=s,this._keyset=i,this._pageSize=a,this._after=null,this._before=null,this._attached=[]}after(t){return this._after=t,this}before(t){return this._before=t,this}attach(t){return this._attached=t,this}then(t,e){return this._execute().then(t,e)}async _execute(){return await u("plugin:sqlite|fetch_page",{db:this._db.path,query:this._query,values:this._bindValues,keyset:this._keyset,pageSize:this._pageSize,after:this._after,before:this._before,attached:this._attached.length>0?this._attached:null})}}class p{constructor(t,e,s,i=[]){this._db=t,this._query=e,this._bindValues=s,this._attached=i}attach(t){return this._attached=t,this}then(t,e){return this._execute().then(t,e)}async _execute(){const[t,e]=await u("plugin:sqlite|execute",{db:this._db.path,query:this._query,values:this._bindValues,attached:this._attached.length>0?this._attached:null});return{lastInsertId:e,rowsAffected:t}}}class f{constructor(t,e,s=[]){this._db=t,this._initialStatements=e,this._attached=s}attach(t){return this._attached=t,this}then(t,e){return this._execute().then(t,e)}async _execute(){const t=await u("plugin:sqlite|begin_interruptible_transaction",{db:this._db.path,initialStatements:this._initialStatements.map(([t,e])=>({query:t,values:e??[]})),attached:this._attached.length>0?this._attached:null});return new _(t.dbPath,t.transactionId)}}class w{constructor(t,e,s=[]){this._db=t,this._statements=e,this._attached=s}attach(t){return this._attached=t,this}then(t,e){return this._execute().then(t,e)}async _execute(){return await u("plugin:sqlite|execute_transaction",{db:this._db.path,statements:this._statements.map(([t,e])=>({query:t,values:e??[]})),attached:this._attached.length>0?this._attached:null})}}class y{constructor(t){this.path=t}static async load(t,e){const s=await u("plugin:sqlite|load",{db:t,customConfig:e});return new y(s)}static get(t){return new y(t)}static async close_all(){await u("plugin:sqlite|close_all")}execute(t,e){return new p(this,t,e??[])}executeTransaction(t){return new w(this,t)}fetchAll(t,e){return new o(this,t,e??[])}fetchOne(t,e){return new d(this,t,e??[])}fetchPage(t,e,s,i){return new b(this,t,e??[],s,i)}async observe(t,e){await u("plugin:sqlite|observe",{db:this.path,tables:t,config:e??null})}async subscribe(t,e){const s=new c;s.onmessage=e;const i=await u("plugin:sqlite|subscribe",{db:this.path,tables:t,onEvent:s});return new l(i)}async unobserve(){await u("plugin:sqlite|unobserve",{db:this.path})}async close(){return await u("plugin:sqlite|close",{db:this.path})}async remove(){return await u("plugin:sqlite|remove",{db:this.path})}beginInterruptibleTransaction(t){return new f(this,t)}async getMigrationEvents(){return await u("plugin:sqlite|get_migration_events",{db:this.path})}}return t.InterruptibleTransaction=_,t.Subscription=l,t.default=y,Object.defineProperty(t,"__esModule",{value:!0}),t}({});Object.defineProperty(window.__TAURI__,"sqlite",{value:__TAURI_PLUGIN_SQLITE__})} diff --git a/crates/sqlx-sqlite-toolkit/README.md b/crates/sqlx-sqlite-toolkit/README.md index dfd513c..51b7bc5 100644 --- a/crates/sqlx-sqlite-toolkit/README.md +++ b/crates/sqlx-sqlite-toolkit/README.md @@ -278,6 +278,24 @@ All errors provide an `error_code()` method returning a machine-readable string: | `INVALID_COLUMN_NAME` | Keyset column name contains invalid characters | | `CONFLICTING_CURSORS` | Both `after` and `before` cursors provided | +## Examples + +Working Tauri apps demonstrating the toolkit's features are in the +[`examples/`](../../examples) directory: + +| App | Description | +| --- | ----------- | +| [`observer-demo`](../../examples/observer-demo) | Real-time change notifications using the observer subsystem — subscribe to table changes and see inserts, updates, and deletes streamed live | +| [`pagination-demo`](../../examples/pagination-demo) | Keyset pagination with a virtualized list — browse large datasets page-by-page with forward/backward navigation and performance metrics | + +Both are Vue 3 + Tauri apps. To run one: + +```bash +cd examples/observer-demo # or pagination-demo +npm install +cargo tauri dev +``` + ## Development ```bash diff --git a/examples/pagination-demo/index.html b/examples/pagination-demo/index.html new file mode 100644 index 0000000..eb06ac1 --- /dev/null +++ b/examples/pagination-demo/index.html @@ -0,0 +1,12 @@ + + + + + + SQLite Pagination Demo + + +
+ + + diff --git a/examples/pagination-demo/package-lock.json b/examples/pagination-demo/package-lock.json new file mode 100644 index 0000000..40d61e2 --- /dev/null +++ b/examples/pagination-demo/package-lock.json @@ -0,0 +1,1758 @@ +{ + "name": "pagination-demo", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pagination-demo", + "version": "0.0.0", + "dependencies": { + "@silvermine/tauri-plugin-sqlite": "file:../../", + "@tauri-apps/api": "2.9.1", + "vue": "3.5.13" + }, + "devDependencies": { + "@tauri-apps/cli": "2.5.0", + "@vitejs/plugin-vue": "5.2.3", + "typescript": "5.8.3", + "vite": "6.3.5", + "vue-tsc": "2.2.8" + } + }, + "../..": { + "name": "@silvermine/tauri-plugin-sqlite", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@rollup/plugin-node-resolve": "16.0.3", + "@rollup/plugin-terser": "0.4.4", + "@rollup/plugin-typescript": "12.3.0", + "@silvermine/eslint-config": "github:silvermine/eslint-config-silvermine#3d0c92ca8d18c4bb66d7b01cf226b1963cbd1319", + "@silvermine/standardization": "2.2.3", + "@silvermine/typescript-config": "1.0.0", + "@tauri-apps/api": "2.9.1", + "eslint": "8.57.1", + "jsdom": "26.1.0", + "npm-run-all": "4.1.5", + "rollup": "4.53.3", + "tslib": "2.8.1", + "typescript": "5.8.3", + "vitest": "3.2.3" + }, + "peerDependencies": { + "@tauri-apps/api": "2.9.1" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@silvermine/tauri-plugin-sqlite": { + "resolved": "../..", + "link": true + }, + "node_modules/@tauri-apps/api": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.9.1.tgz", + "integrity": "sha512-IGlhP6EivjXHepbBic618GOmiWe4URJiIeZFlB7x3czM0yDHHYviH1Xvoiv4FefdkQtn6v7TuwWCRfOGdnVUGw==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, + "node_modules/@tauri-apps/cli": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.5.0.tgz", + "integrity": "sha512-rAtHqG0Gh/IWLjN2zTf3nZqYqbo81oMbqop56rGTjrlWk9pTTAjkqOjSL9XQLIMZ3RbeVjveCqqCA0s8RnLdMg==", + "dev": true, + "license": "Apache-2.0 OR MIT", + "bin": { + "tauri": "tauri.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + }, + "optionalDependencies": { + "@tauri-apps/cli-darwin-arm64": "2.5.0", + "@tauri-apps/cli-darwin-x64": "2.5.0", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.5.0", + "@tauri-apps/cli-linux-arm64-gnu": "2.5.0", + "@tauri-apps/cli-linux-arm64-musl": "2.5.0", + "@tauri-apps/cli-linux-riscv64-gnu": "2.5.0", + "@tauri-apps/cli-linux-x64-gnu": "2.5.0", + "@tauri-apps/cli-linux-x64-musl": "2.5.0", + "@tauri-apps/cli-win32-arm64-msvc": "2.5.0", + "@tauri-apps/cli-win32-ia32-msvc": "2.5.0", + "@tauri-apps/cli-win32-x64-msvc": "2.5.0" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-VuVAeTFq86dfpoBDNYAdtQVLbP0+2EKCHIIhkaxjeoPARR0sLpFHz2zs0PcFU76e+KAaxtEtAJAXGNUc8E1PzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.5.0.tgz", + "integrity": "sha512-hUF01sC06cZVa8+I0/VtsHOk9BbO75rd+YdtHJ48xTdcYaQ5QIwL4yZz9OR1AKBTaUYhBam8UX9Pvd5V2/4Dpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.5.0.tgz", + "integrity": "sha512-LQKqttsK252LlqYyX8R02MinUsfFcy3+NZiJwHFgi5Y3+ZUIAED9cSxJkyNtuY5KMnR4RlpgWyLv4P6akN1xhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.5.0.tgz", + "integrity": "sha512-mTQufsPcpdHg5RW0zypazMo4L55EfeE5snTzrPqbLX4yCK2qalN7+rnP8O8GT06xhp6ElSP/Ku1M2MR297SByQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-rQO1HhRUQqyEaal5dUVOQruTRda/TD36s9kv1hTxZiFuSq3558lsTjAcUEnMAtBcBkps20sbyTJNMT0AwYIk8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.5.0.tgz", + "integrity": "sha512-7oS18FN46yDxyw1zX/AxhLAd7T3GrLj3Ai6s8hZKd9qFVzrAn36ESL7d3G05s8wEtsJf26qjXnVF4qleS3dYsA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.5.0.tgz", + "integrity": "sha512-SG5sFNL7VMmDBdIg3nO3EzNRT306HsiEQ0N90ILe3ZABYAVoPDO/ttpCO37ApLInTzrq/DLN+gOlC/mgZvLw1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-QXDM8zp/6v05PNWju5ELsVwF0VH1n6b5pk2E6W/jFbbiwz80Vs1lACl9pv5kEHkrxBj+aWU/03JzGuIj2g3SkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.5.0.tgz", + "integrity": "sha512-pFSHFK6b+o9y4Un8w0gGLwVyFTZaC3P0kQ7umRt/BLDkzD5RnQ4vBM7CF8BCU5nkwmEBUCZd7Wt3TWZxe41o6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.5.0.tgz", + "integrity": "sha512-EArv1IaRlogdLAQyGlKmEqZqm5RfHCUMhJoedWu7GtdbOMUfSAz6FMX2boE1PtEmNO4An+g188flLeVErrxEKg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.5.0.tgz", + "integrity": "sha512-lj43EFYbnAta8pd9JnUq87o+xRUR0odz+4rixBtTUwUgdRdwQ2V9CzFtsMu6FQKpFQ6mujRK6P1IEwhL6ADRsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz", + "integrity": "sha512-IYSLEQj4LgZZuoVpdSUCw3dIynTWQgPlaRP6iAvMle4My0HdYwr5g5wQAfwOeHQBmYwEkqF70nRpSilr6PoUDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.48", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/language-core": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.8.tgz", + "integrity": "sha512-rrzB0wPGBvcwaSNRriVWdNAbHQWSf0NlGqgKHK5mEkXpefjUlVRP62u03KvwZpvKVjRnBIQ/Lwre+Mx9N6juUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~2.4.11", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^1.0.3", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", + "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", + "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", + "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", + "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "vue": "3.5.13" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", + "license": "MIT" + }, + "node_modules/alien-signals": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz", + "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-tsc": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.8.tgz", + "integrity": "sha512-jBYKBNFADTN+L+MdesNX/TB3XuDSyaWynKMDgR+yCSln0GQ9Tfb7JS2lr46s2LiFUT1WsmfWsSvIElyxzOPqcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "~2.4.11", + "@vue/language-core": "2.2.8" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + } + } +} diff --git a/examples/pagination-demo/package.json b/examples/pagination-demo/package.json new file mode 100644 index 0000000..1c5af65 --- /dev/null +++ b/examples/pagination-demo/package.json @@ -0,0 +1,23 @@ +{ + "name": "pagination-demo", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc --noEmit && vite build", + "tauri": "tauri" + }, + "dependencies": { + "@silvermine/tauri-plugin-sqlite": "file:../../", + "@tauri-apps/api": "2.9.1", + "vue": "3.5.13" + }, + "devDependencies": { + "@tauri-apps/cli": "2.5.0", + "@vitejs/plugin-vue": "5.2.3", + "typescript": "5.8.3", + "vite": "6.3.5", + "vue-tsc": "2.2.8" + } +} diff --git a/examples/pagination-demo/src-tauri/Cargo.toml b/examples/pagination-demo/src-tauri/Cargo.toml new file mode 100644 index 0000000..8f008c4 --- /dev/null +++ b/examples/pagination-demo/src-tauri/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "pagination-demo" +version = "0.0.0" +edition = "2024" +rust-version = "1.89" + +[lib] +name = "pagination_demo_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[dependencies] +tauri = { version = "2.9.3", features = [] } +tauri-plugin-sqlite = { path = "../../.." } + +[build-dependencies] +tauri-build = { version = "2.0.6", features = [] } diff --git a/examples/pagination-demo/src-tauri/build.rs b/examples/pagination-demo/src-tauri/build.rs new file mode 100644 index 0000000..716a42b --- /dev/null +++ b/examples/pagination-demo/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/examples/pagination-demo/src-tauri/capabilities/default.json b/examples/pagination-demo/src-tauri/capabilities/default.json new file mode 100644 index 0000000..b5846aa --- /dev/null +++ b/examples/pagination-demo/src-tauri/capabilities/default.json @@ -0,0 +1,7 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Capability for the main window", + "windows": ["main"], + "permissions": ["sqlite:default"] +} diff --git a/examples/pagination-demo/src-tauri/icons/icon.png b/examples/pagination-demo/src-tauri/icons/icon.png new file mode 100644 index 0000000..a8bd56d Binary files /dev/null and b/examples/pagination-demo/src-tauri/icons/icon.png differ diff --git a/examples/pagination-demo/src-tauri/src/lib.rs b/examples/pagination-demo/src-tauri/src/lib.rs new file mode 100644 index 0000000..624ecb2 --- /dev/null +++ b/examples/pagination-demo/src-tauri/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_sqlite::init()) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/examples/pagination-demo/src-tauri/src/main.rs b/examples/pagination-demo/src-tauri/src/main.rs new file mode 100644 index 0000000..4323d55 --- /dev/null +++ b/examples/pagination-demo/src-tauri/src/main.rs @@ -0,0 +1,6 @@ +// Prevents additional console window on Windows in release +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + pagination_demo_lib::run() +} diff --git a/examples/pagination-demo/src-tauri/tauri.conf.json b/examples/pagination-demo/src-tauri/tauri.conf.json new file mode 100644 index 0000000..048e057 --- /dev/null +++ b/examples/pagination-demo/src-tauri/tauri.conf.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://raw.githubusercontent.com/nicoverbruggen/tauri-v2-schema/main/schema.json", + "productName": "pagination-demo", + "version": "0.0.0", + "identifier": "com.silvermine.paginationdemo", + "build": { + "beforeDevCommand": "npm run dev", + "devUrl": "http://localhost:1421", + "beforeBuildCommand": "npm run build", + "frontendDist": "../dist" + }, + "app": { + "windows": [ + { + "title": "SQLite Pagination Demo", + "width": 1000, + "height": 800 + } + ], + "security": { + "csp": null + } + } +} diff --git a/examples/pagination-demo/src/App.vue b/examples/pagination-demo/src/App.vue new file mode 100644 index 0000000..0539521 --- /dev/null +++ b/examples/pagination-demo/src/App.vue @@ -0,0 +1,377 @@ + + + + + diff --git a/examples/pagination-demo/src/components/ControlPanel.vue b/examples/pagination-demo/src/components/ControlPanel.vue new file mode 100644 index 0000000..d7f3cb3 --- /dev/null +++ b/examples/pagination-demo/src/components/ControlPanel.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/examples/pagination-demo/src/components/PerformancePanel.vue b/examples/pagination-demo/src/components/PerformancePanel.vue new file mode 100644 index 0000000..a35ac94 --- /dev/null +++ b/examples/pagination-demo/src/components/PerformancePanel.vue @@ -0,0 +1,290 @@ + + + + + diff --git a/examples/pagination-demo/src/components/VirtualizedList.vue b/examples/pagination-demo/src/components/VirtualizedList.vue new file mode 100644 index 0000000..0941981 --- /dev/null +++ b/examples/pagination-demo/src/components/VirtualizedList.vue @@ -0,0 +1,381 @@ + + + + + diff --git a/examples/pagination-demo/src/main.ts b/examples/pagination-demo/src/main.ts new file mode 100644 index 0000000..684d042 --- /dev/null +++ b/examples/pagination-demo/src/main.ts @@ -0,0 +1,4 @@ +import { createApp } from 'vue'; +import App from './App.vue'; + +createApp(App).mount('#app'); diff --git a/examples/pagination-demo/src/style.css b/examples/pagination-demo/src/style.css new file mode 100644 index 0000000..79792db --- /dev/null +++ b/examples/pagination-demo/src/style.css @@ -0,0 +1,93 @@ +:root { + --color-bg: #ffffff; + --color-bg-secondary: #f5f5f7; + --color-bg-tertiary: #e8e8ed; + --color-text: #1d1d1f; + --color-text-secondary: #6e6e73; + --color-border: #d2d2d7; + --color-accent: #0071e3; + --color-accent-hover: #0077ed; + --color-keyset: #34c759; + --color-keyset-bg: #e8f9ed; + --color-warning: #ff9500; + --color-warning-bg: #fff4e5; + --color-all: var(--color-warning); + --color-all-bg: var(--color-warning-bg); + --radius: 8px; + --radius-sm: 4px; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.08); + --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace; +} + +@media (prefers-color-scheme: dark) { + :root { + --color-bg: #1c1c1e; + --color-bg-secondary: #2c2c2e; + --color-bg-tertiary: #3a3a3c; + --color-text: #f5f5f7; + --color-text-secondary: #98989d; + --color-border: #48484a; + --color-accent: #0a84ff; + --color-accent-hover: #409cff; + --color-keyset: #30d158; + --color-keyset-bg: #0d3318; + --color-warning: #ff9f0a; + --color-warning-bg: #332200; + --color-all: var(--color-warning); + --color-all-bg: var(--color-warning-bg); + --shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + } +} + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, + sans-serif; + font-size: 14px; + line-height: 1.5; + color: var(--color-text); + background-color: var(--color-bg); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +button { + font-family: inherit; + font-size: 13px; + font-weight: 500; + padding: 10px 16px; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background-color: var(--color-bg-secondary); + color: var(--color-text); + cursor: pointer; + transition: background-color 0.15s ease; + touch-action: manipulation; +} + +button:hover:not(:disabled) { + background-color: var(--color-bg-tertiary); +} + +button:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +input[type="number"] { + font-family: var(--font-mono); + font-size: 13px; + padding: 8px 12px; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background-color: var(--color-bg); + color: var(--color-text); + width: 100px; +} diff --git a/examples/pagination-demo/src/vite-env.d.ts b/examples/pagination-demo/src/vite-env.d.ts new file mode 100644 index 0000000..0fa3a3c --- /dev/null +++ b/examples/pagination-demo/src/vite-env.d.ts @@ -0,0 +1,7 @@ +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue'; + const component: DefineComponent; + export default component; +} diff --git a/examples/pagination-demo/tsconfig.json b/examples/pagination-demo/tsconfig.json new file mode 100644 index 0000000..9548eed --- /dev/null +++ b/examples/pagination-demo/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2021", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2021", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "preserve", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src/**/*.ts", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/pagination-demo/tsconfig.node.json b/examples/pagination-demo/tsconfig.node.json new file mode 100644 index 0000000..2cf8cde --- /dev/null +++ b/examples/pagination-demo/tsconfig.node.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "composite": true, + "moduleDetection": "force", + "emitDeclarationOnly": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/pagination-demo/vite.config.ts b/examples/pagination-demo/vite.config.ts new file mode 100644 index 0000000..672a89c --- /dev/null +++ b/examples/pagination-demo/vite.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; + +export default defineConfig({ + plugins: [vue()], + clearScreen: false, + server: { + host: process.env.TAURI_DEV_HOST || false, + port: 1421, + strictPort: true, + }, + envPrefix: ['VITE_', 'TAURI_'], + build: { + target: ['es2021', 'chrome100', 'safari14'], + minify: !process.env.TAURI_DEBUG ? 'esbuild' : false, + sourcemap: !!process.env.TAURI_DEBUG, + }, +}); diff --git a/guest-js/index.test.ts b/guest-js/index.test.ts index 67a5ccb..39d1ed9 100644 --- a/guest-js/index.test.ts +++ b/guest-js/index.test.ts @@ -10,6 +10,7 @@ import Database, { ColumnValue, TableChange, TableChangeEvent, + KeysetColumn, } from './index'; let lastCmd = '', @@ -45,6 +46,9 @@ beforeEach(() => { if (cmd === 'plugin:sqlite|fetch_all') { return []; } + if (cmd === 'plugin:sqlite|fetch_page') { + return { rows: [], nextCursor: null, hasMore: false }; + } if (cmd === 'plugin:sqlite|fetch_one') { return null; } @@ -197,6 +201,126 @@ describe('Database commands', () => { ]); }); + it('fetch_page first page (no cursor)', async () => { + const keyset: KeysetColumn[] = [ + { name: 'id', direction: 'asc' }, + ]; + + await Database.get('t.db').fetchPage('SELECT * FROM posts', [], keyset, 25); + expect(lastCmd).toBe('plugin:sqlite|fetch_page'); + expect(lastArgs).toMatchObject({ + db: 't.db', + query: 'SELECT * FROM posts', + values: [], + keyset: [ { name: 'id', direction: 'asc' } ], + pageSize: 25, + after: null, + before: null, + attached: null, + }); + }); + + it('fetch_page with cursor via .after()', async () => { + const keyset: KeysetColumn[] = [ + { name: 'id', direction: 'asc' }, + ]; + + await Database.get('t.db') + .fetchPage('SELECT * FROM posts', [], keyset, 25) + .after([ 100 ]); + + expect(lastCmd).toBe('plugin:sqlite|fetch_page'); + expect(lastArgs.after).toEqual([ 100 ]); + expect(lastArgs.before).toBeNull(); + }); + + it('fetch_page with cursor via .before()', async () => { + const keyset: KeysetColumn[] = [ + { name: 'id', direction: 'asc' }, + ]; + + await Database.get('t.db') + .fetchPage('SELECT * FROM posts', [], keyset, 25) + .before([ 50 ]); + + expect(lastCmd).toBe('plugin:sqlite|fetch_page'); + expect(lastArgs.before).toEqual([ 50 ]); + expect(lastArgs.after).toBeNull(); + }); + + it('fetch_page with attached databases', async () => { + const keyset: KeysetColumn[] = [ + { name: 'id', direction: 'asc' }, + ]; + + await Database.get('main.db') + .fetchPage('SELECT * FROM posts', [], keyset, 25) + .attach([ + { + databasePath: 'archive.db', + schemaName: 'archive', + mode: 'readOnly', + }, + ]); + expect(lastCmd).toBe('plugin:sqlite|fetch_page'); + expect(lastArgs.db).toBe('main.db'); + expect(lastArgs.after).toBeNull(); + expect(lastArgs.before).toBeNull(); + expect(lastArgs.attached).toEqual([ + { + databasePath: 'archive.db', + schemaName: 'archive', + mode: 'readOnly', + }, + ]); + }); + + it('fetch_page multi-column keyset with .after()', async () => { + const keyset: KeysetColumn[] = [ + { name: 'category', direction: 'asc' }, + { name: 'score', direction: 'desc' }, + { name: 'id', direction: 'asc' }, + ]; + + await Database.get('t.db') + .fetchPage('SELECT * FROM posts WHERE active = $1', [ true ], keyset, 50) + .after([ 'tech', 95, 42 ]); + + expect(lastCmd).toBe('plugin:sqlite|fetch_page'); + expect(lastArgs.keyset).toEqual([ + { name: 'category', direction: 'asc' }, + { name: 'score', direction: 'desc' }, + { name: 'id', direction: 'asc' }, + ]); + expect(lastArgs.values).toEqual([ true ]); + expect(lastArgs.after).toEqual([ 'tech', 95, 42 ]); + expect(lastArgs.before).toBeNull(); + expect(lastArgs.pageSize).toBe(50); + }); + + it('fetch_page multi-column keyset with .before()', async () => { + const keyset: KeysetColumn[] = [ + { name: 'category', direction: 'asc' }, + { name: 'score', direction: 'desc' }, + { name: 'id', direction: 'asc' }, + ]; + + await Database.get('t.db') + .fetchPage('SELECT * FROM posts WHERE active = $1', [ true ], keyset, 50) + .before([ 'tech', 95, 42 ]); + + expect(lastCmd).toBe('plugin:sqlite|fetch_page'); + expect(lastArgs.keyset).toEqual([ + { name: 'category', direction: 'asc' }, + { name: 'score', direction: 'desc' }, + { name: 'id', direction: 'asc' }, + ]); + expect(lastArgs.values).toEqual([ true ]); + expect(lastArgs.before).toEqual([ 'tech', 95, 42 ]); + expect(lastArgs.after).toBeNull(); + expect(lastArgs.pageSize).toBe(50); + }); + it('close', async () => { await Database.get('t.db').close(); expect(lastCmd).toBe('plugin:sqlite|close'); diff --git a/guest-js/index.ts b/guest-js/index.ts index fbece40..d064953 100644 --- a/guest-js/index.ts +++ b/guest-js/index.ts @@ -264,6 +264,42 @@ export interface MigrationEvent { error?: string; } +// ─── Pagination Types ─── + +/** + * Sort direction for a keyset column. + */ +export type SortDirection = 'asc' | 'desc'; + +/** + * A column in the keyset used for cursor-based pagination. + */ +export interface KeysetColumn { + + /** Column name as it appears in the query result set */ + name: string; + + /** Sort direction for this column */ + direction: SortDirection; +} + +/** + * A page of results from keyset pagination. + * + * @typeParam T - Row type, defaults to `Record` + */ +export interface KeysetPage> { + + /** The rows in this page */ + rows: T[]; + + /** Cursor values to continue pagination, or null if there are no more pages */ + nextCursor: SqlValue[] | null; + + /** Whether there are more rows in the current pagination direction */ + hasMore: boolean; +} + // ─── Observer Types ─── /** @@ -470,6 +506,90 @@ class FetchOneBuilder implements PromiseLike { } } +/** + * Builder for paginated SELECT queries using keyset (cursor-based) pagination + */ +class FetchPageBuilder implements PromiseLike> { + private readonly _db: Database; + private readonly _query: string; + private readonly _bindValues: SqlValue[]; + private readonly _keyset: KeysetColumn[]; + private readonly _pageSize: number; + private _after: SqlValue[] | null; + private _before: SqlValue[] | null; + private _attached: AttachedDatabaseSpec[]; + + public constructor( + db: Database, + query: string, + bindValues: SqlValue[], + keyset: KeysetColumn[], + pageSize: number + ) { + this._db = db; + this._query = query; + this._bindValues = bindValues; + this._keyset = keyset; + this._pageSize = pageSize; + this._after = null; + this._before = null; + this._attached = []; + } + + /** + * Set the cursor for fetching the next page (forward pagination). + * + * Pass the `nextCursor` from a previous `KeysetPage` to fetch the page + * that follows it in the original sort order. + */ + public after(cursor: SqlValue[]): this { + this._after = cursor; + return this; + } + + /** + * Set the cursor for fetching the previous page (backward pagination). + * + * Pass a cursor to fetch the page that precedes it in the original sort + * order. Rows are returned in the original sort order (not reversed). + */ + public before(cursor: SqlValue[]): this { + this._before = cursor; + return this; + } + + /** + * Attach databases for cross-database queries + */ + public attach(specs: AttachedDatabaseSpec[]): this { + this._attached = specs; + return this; + } + + /** + * Make the builder directly awaitable + */ + public then, TResult2 = never>( + onfulfilled?: ((value: KeysetPage) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null + ): PromiseLike { + return this._execute().then(onfulfilled, onrejected); + } + + private async _execute(): Promise> { + return await invoke>('plugin:sqlite|fetch_page', { + db: this._db.path, + query: this._query, + values: this._bindValues, + keyset: this._keyset, + pageSize: this._pageSize, + after: this._after, + before: this._before, + attached: this._attached.length > 0 ? this._attached : null, + }); + } +} + /** * Builder for write queries (INSERT, UPDATE, DELETE) */ @@ -871,6 +991,80 @@ export default class Database { return new FetchOneBuilder(this, query, bindValues ?? []); } + /** + * **fetchPage** + * + * Creates a builder for paginated SELECT queries using keyset (cursor-based) + * pagination. Keyset pagination avoids the performance degradation of + * OFFSET-based pagination on large datasets by using indexed column values + * to seek directly to the next or previous page. + * + * The base query must not contain ORDER BY or LIMIT clauses — the builder + * appends these automatically based on the keyset definition. + * + * SQLite uses `$1`, `$2`, etc. for parameter binding. + * + * @param query - SQL SELECT query (without ORDER BY or LIMIT) + * @param bindValues - Parameter values for the query + * @param keyset - Columns defining the sort order and cursor + * @param pageSize - Number of rows per page + * + * @example + * ```ts + * const keyset: KeysetColumn[] = [ + * { name: 'category', direction: 'asc' }, + * { name: 'score', direction: 'desc' }, + * { name: 'id', direction: 'asc' }, + * ]; + * + * // First page + * const page = await db.fetchPage( + * 'SELECT * FROM posts', + * [], + * keyset, + * 25, + * ); + * + * // Next page (forward) — pass the cursor from the previous page + * if (page.nextCursor) { + * const nextPage = await db.fetchPage( + * 'SELECT * FROM posts', + * [], + * keyset, + * 25, + * ).after(page.nextCursor); + * + * // Previous page (backward) — rows are returned in original sort order + * const prevPage = await db.fetchPage( + * 'SELECT * FROM posts', + * [], + * keyset, + * 25, + * ).before(page.nextCursor); + * } + * + * // With attached database + * const page = await db.fetchPage( + * 'SELECT * FROM posts', + * [], + * keyset, + * 25, + * ).attach([{ + * databasePath: 'archive.db', + * schemaName: 'archive', + * mode: 'readOnly', + * }]); + * ``` + */ + public fetchPage( + query: string, + bindValues: SqlValue[], + keyset: KeysetColumn[], + pageSize: number + ): FetchPageBuilder { + return new FetchPageBuilder(this, query, bindValues, keyset, pageSize); + } + // ─── Observer Methods ─── /**