diff --git a/.gitea/workflows/api-dev.yaml b/.gitea/workflows/api-dev.yaml new file mode 100644 index 0000000..e49551f --- /dev/null +++ b/.gitea/workflows/api-dev.yaml @@ -0,0 +1,22 @@ +name: Build and Test API +on: + push: + paths: + - 'finnow-api/**' + - '.gitea/workflows/api-dev.yaml' + branches-ignore: + - main +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dlang-community/setup-dlang@v2 + with: + compiler: ldc-latest + - name: Test + run: dub test + working-directory: ./finnow-api + - name: Build + run: dub build --build=release + working-directory: ./finnow-api diff --git a/.gitea/workflows/api.yaml b/.gitea/workflows/api.yaml index b0e1dea..1256e6b 100644 --- a/.gitea/workflows/api.yaml +++ b/.gitea/workflows/api.yaml @@ -4,6 +4,8 @@ on: paths: - 'finnow-api/**' - '.gitea/workflows/api.yaml' + branches: + - main jobs: build-and-deploy: runs-on: ubuntu-latest diff --git a/.gitea/workflows/web-app-dev.yaml b/.gitea/workflows/web-app-dev.yaml new file mode 100644 index 0000000..e6013e3 --- /dev/null +++ b/.gitea/workflows/web-app-dev.yaml @@ -0,0 +1,24 @@ +name: Build Web App +on: + push: + paths: + - 'web-app/**' + - '.gitea/workflows/web-app-dev.yaml' + branches-ignore: + - main +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22.x' + - name: Enable Corepack + run: corepack enable + - name: Install Dependencies + run: yarn install --immutable + working-directory: ./web-app + - name: Build + run: yarn build + working-directory: ./web-app diff --git a/.gitea/workflows/web-app.yaml b/.gitea/workflows/web-app.yaml index 1a83423..29cfe6b 100644 --- a/.gitea/workflows/web-app.yaml +++ b/.gitea/workflows/web-app.yaml @@ -4,6 +4,8 @@ on: paths: - 'web-app/**' - '.gitea/workflows/web-app.yaml' + branches: + - main jobs: build-and-deploy: runs-on: ubuntu-latest diff --git a/finnow-api/dub.json b/finnow-api/dub.json index fd42f97..be65806 100644 --- a/finnow-api/dub.json +++ b/finnow-api/dub.json @@ -4,6 +4,7 @@ ], "copyright": "Copyright © 2024, Andrew Lalis", "dependencies": { + "cronexp": ">=0.1.0-beta3 <0.2.0-0", "d2sqlite3": "~>1.0", "handy-http-starter": "~>1.6", "jwt4d": "~>0.0.2", diff --git a/finnow-api/dub.selections.json b/finnow-api/dub.selections.json index a584455..39aac10 100644 --- a/finnow-api/dub.selections.json +++ b/finnow-api/dub.selections.json @@ -2,12 +2,13 @@ "fileVersion": 1, "versions": { "asdf": "0.8.0", + "cronexp": "0.1.0-beta3", "d2sqlite3": "1.0.0", "dxml": "0.4.5", - "handy-http-data": "1.3.0", + "handy-http-data": "1.3.2", "handy-http-handlers": "1.3.0", - "handy-http-primitives": "1.8.1", - "handy-http-starter": "1.7.0", + "handy-http-primitives": "1.10.0", + "handy-http-starter": "1.7.1", "handy-http-transport": "1.10.1", "handy-http-websockets": "1.2.0", "jwt4d": "0.0.2", @@ -15,7 +16,7 @@ "mir-core": "1.7.4", "openssl": "3.3.4", "path-matcher": "1.2.0", - "photon": "0.18.11", + "photon": "0.18.12", "scheduled": "1.4.0", "secured": "3.0.0", "sharded-map": "2.7.0", diff --git a/finnow-api/source/account/data_impl_sqlite.d b/finnow-api/source/account/data_impl_sqlite.d index 1e46eed..119ee4c 100644 --- a/finnow-api/source/account/data_impl_sqlite.d +++ b/finnow-api/source/account/data_impl_sqlite.d @@ -202,12 +202,12 @@ SQL", private AccountHistoryValueRecordItemResponse fetchValueRecordHistoryItem(in BaseHistoryItem item) { return util.sqlite.findOne( db, - "SELECT vr.id, vr.type, vr.value, vr.currency FROM history_item_linked_value_record h " ~ + "SELECT vr.id, vr.type, vr.value, vr.currency, vr.timestamp FROM history_item_linked_value_record h " ~ "LEFT JOIN account_value_record vr ON vr.id = h.value_record_id " ~ "WHERE h.item_id = ?", (row) { auto obj = new AccountHistoryValueRecordItemResponse(); - obj.timestamp = item.timestamp; + obj.timestamp = row.peek!string(4); obj.type = item.type; obj.valueRecordId = row.peek!ulong(0); obj.valueRecordType = row.peek!string(1); @@ -222,13 +222,14 @@ SQL", private AccountHistoryJournalEntryItemResponse fetchJournalEntryHistoryItem(in BaseHistoryItem item) { return util.sqlite.findOne( db, - "SELECT je.type, je.amount, je.currency, tx.id, tx.description FROM history_item_linked_journal_entry h " ~ + "SELECT je.type, je.amount, je.currency, tx.id, tx.description, tx.timestamp " ~ + "FROM history_item_linked_journal_entry h " ~ "LEFT JOIN account_journal_entry je ON je.id = h.journal_entry_id " ~ "LEFT JOIN \"transaction\" tx ON tx.id = je.transaction_id " ~ "WHERE h.item_id = ?", (row) { auto obj = new AccountHistoryJournalEntryItemResponse(); - obj.timestamp = item.timestamp; + obj.timestamp = row.peek!string(5); obj.type = item.type; obj.journalEntryType = row.peek!string(0); obj.amount = row.peek!ulong(1); diff --git a/finnow-api/source/account/dto.d b/finnow-api/source/account/dto.d index 445e60b..5871e44 100644 --- a/finnow-api/source/account/dto.d +++ b/finnow-api/source/account/dto.d @@ -5,12 +5,9 @@ import account.model; import attachment.data; import attachment.dto; import util.money; -import util.data : serializeOptional; /// The data the API provides for an Account entity. struct AccountResponse { - import asdf : serdeTransformOut; - ulong id; string createdAt; bool archived; @@ -19,7 +16,6 @@ struct AccountResponse { string name; Currency currency; string description; - @serdeTransformOut!serializeOptional Optional!long currentBalance; static AccountResponse of(in Account account, Optional!long currentBalance) { @@ -37,6 +33,14 @@ struct AccountResponse { } } +/// A limited set of account data, usually for use in other responses. +struct SimpleAccountResponse { + ulong id; + string name; + string type; + string numberSuffix; +} + // The data provided by a user to create a new account. struct AccountCreationPayload { string type; diff --git a/finnow-api/source/analytics/data.d b/finnow-api/source/analytics/data.d index 6020b29..ba83e1b 100644 --- a/finnow-api/source/analytics/data.d +++ b/finnow-api/source/analytics/data.d @@ -2,7 +2,6 @@ module analytics.data; import std.datetime; import handy_http_primitives : Optional; -import asdf : serdeTransformOut; import util.money; import util.data; @@ -36,7 +35,6 @@ struct CategorySpendData { ulong categoryId; string categoryName; string categoryColor; - @serdeTransformOut!serializeOptional Optional!ulong parentCategoryId; long amount; } diff --git a/finnow-api/source/analytics/modules/balances.d b/finnow-api/source/analytics/modules/balances.d index 7ab596b..cfc0dd1 100644 --- a/finnow-api/source/analytics/modules/balances.d +++ b/finnow-api/source/analytics/modules/balances.d @@ -9,7 +9,6 @@ import std.algorithm; import std.array; import std.conv; import slf4d; -import asdf; import profile.data; import profile.model; diff --git a/finnow-api/source/api_mapping.d b/finnow-api/source/api_mapping.d index 1fc4ccf..3e43b6b 100644 --- a/finnow-api/source/api_mapping.d +++ b/finnow-api/source/api_mapping.d @@ -25,7 +25,7 @@ HttpRequestHandler mapApiHandlers(string webOrigin) { publicHandler.registerHandlers!(auth.api_public); // Dev endpoint for sample data: REMOVE BEFORE DEPLOYING!!! - // h.map(HttpMethod.POST, "/sample-data", &sampleDataEndpoint); + publicHandler.addMapping(HttpMethod.POST, "/api/sample-data", HttpRequestHandler.of(&sampleDataEndpoint)); // Authenticated endpoints: import auth.api; diff --git a/finnow-api/source/attachment/data.d b/finnow-api/source/attachment/data.d index 30413f4..ba866f6 100644 --- a/finnow-api/source/attachment/data.d +++ b/finnow-api/source/attachment/data.d @@ -9,6 +9,7 @@ interface AttachmentRepository { Attachment[] findAllByLinkedEntity(string subquery, ulong entityId); Attachment[] findAllByTransactionId(ulong transactionId); Attachment[] findAllByValueRecordId(ulong valueRecordId); + Attachment[] findAllByTransactionDraftId(ulong draftId); ulong save(SysTime uploadedAt, string filename, string contentType, in ubyte[] content); void remove(ulong id); Optional!(ubyte[]) getContent(ulong id); diff --git a/finnow-api/source/attachment/data_impl_sqlite.d b/finnow-api/source/attachment/data_impl_sqlite.d index 96ec5fc..144f4fb 100644 --- a/finnow-api/source/attachment/data_impl_sqlite.d +++ b/finnow-api/source/attachment/data_impl_sqlite.d @@ -45,6 +45,13 @@ class SqliteAttachmentRepository : AttachmentRepository { valueRecordId ); } + + Attachment[] findAllByTransactionDraftId(ulong draftId) { + return findAllByLinkedEntity( + "SELECT attachment_id FROM transaction_draft_attachment WHERE draft_id = ?", + draftId + ); + } ulong save(SysTime uploadedAt, string filename, string contentType, in ubyte[] content) { util.sqlite.update( diff --git a/finnow-api/source/profile/data.d b/finnow-api/source/profile/data.d index 6186fc0..a38e108 100644 --- a/finnow-api/source/profile/data.d +++ b/finnow-api/source/profile/data.d @@ -53,6 +53,8 @@ interface ProfileDataSource { TransactionCategoryRepository getTransactionCategoryRepository(); TransactionTagRepository getTransactionTagRepository(); TransactionRepository getTransactionRepository(); + TransactionDraftRepository getTransactionDraftRepository(); + RecurringTransactionRepository getRecurringTransactionRepository(); AnalyticsRepository getAnalyticsRepository(); @@ -93,6 +95,12 @@ version(unittest) { TransactionRepository getTransactionRepository() { throw new Exception("Not implemented"); } + TransactionDraftRepository getTransactionDraftRepository() { + throw new Exception("Not implemented"); + } + RecurringTransactionRepository getRecurringTransactionRepository() { + throw new Exception("Not implemented"); + } AnalyticsRepository getAnalyticsRepository() { throw new Exception("Not implemented"); } diff --git a/finnow-api/source/profile/data_impl_sqlite.d b/finnow-api/source/profile/data_impl_sqlite.d index 07220de..046709a 100644 --- a/finnow-api/source/profile/data_impl_sqlite.d +++ b/finnow-api/source/profile/data_impl_sqlite.d @@ -185,7 +185,7 @@ class SqlitePropertiesRepository : PropertiesRepository { } private const SCHEMA = import("sql/schema.sql"); -private const uint SCHEMA_VERSION = 1; +private const uint SCHEMA_VERSION = 2; private const SCHEMA_VERSION_PROPERTY = "database-schema-version"; /** @@ -215,6 +215,8 @@ class SqliteProfileDataSource : ProfileDataSource { TransactionCategoryRepository transactionCategoryRepo; TransactionTagRepository transactionTagRepo; TransactionRepository transactionRepo; + TransactionDraftRepository transactionDraftRepo; + RecurringTransactionRepository recurringTransactionRepo; AnalyticsRepository analyticsRepo; this(string path) { @@ -297,6 +299,20 @@ class SqliteProfileDataSource : ProfileDataSource { return transactionRepo; } + TransactionDraftRepository getTransactionDraftRepository() { + if (transactionDraftRepo is null) { + transactionDraftRepo = new SqliteTransactionDraftRepository(db); + } + return transactionDraftRepo; + } + + RecurringTransactionRepository getRecurringTransactionRepository() { + if (recurringTransactionRepo is null) { + recurringTransactionRepo = new SqliteRecurringTransactionRepository(db); + } + return recurringTransactionRepo; + } + AnalyticsRepository getAnalyticsRepository() { if (analyticsRepo is null) { analyticsRepo = new SqliteAnalyticsRepository(db); @@ -322,7 +338,8 @@ class SqliteProfileDataSource : ProfileDataSource { if (currentVersion == SCHEMA_VERSION) return; static const migrations = [ - import("sql/migrations/1.sql") + import("sql/migrations/1.sql"), + import("sql/migrations/2.sql") ]; static if (migrations.length != SCHEMA_VERSION) { static assert(false, "Schema version doesn't match the list of defined migrations."); diff --git a/finnow-api/source/scheduled_jobs.d b/finnow-api/source/scheduled_jobs.d index adb5c40..cac7741 100644 --- a/finnow-api/source/scheduled_jobs.d +++ b/finnow-api/source/scheduled_jobs.d @@ -11,6 +11,7 @@ void startScheduledJobs() { ); JobScheduler jobScheduler = new TaskPoolScheduler(); + jobScheduler.addJob(() { // Clear old analytics data from profiles. import profile.data; @@ -38,5 +39,32 @@ void startScheduledJobs() { ); }, analyticsSchedule); + + // Add a scheduled job to regularly check for and create recurring transaction drafts. + jobScheduler.addJob(() { + import profile.data; + import profile.data_impl_sqlite; + import profile.model; + import transaction.dto; + import transaction.data; + import util.pagination; + import std.stdio; + import std.datetime; + import cronexp; + FileSystemProfileRepository.doForAllUserProfiles((Profile profile, ProfileRepository profileRepo) { + writefln!"Recurring transaction check: %s / %s"(profile.username, profile.name); + ProfileDataSource ds = profileRepo.getDataSource(profile); + RecurringTransactionRepository rtRepo = ds.getRecurringTransactionRepository(); + Page!(RecurringTransactionResponse) result = rtRepo.findAll(PageRequest.unpaged()); + foreach (rt; result.items) { + writeln(rt.scheduleExpr); + DateTime now = cast(DateTime) Clock.currTime(); + auto cron = CronExpr(rt.scheduleExpr); + writefln!"scheduleExpr = %s -> %s"(rt.scheduleExpr, cron.getNext(now)); + // TODO: Figure out how to actually create the transactions! + } + }); + }, new FixedIntervalSchedule(minutes(1), Clock.currTime(UTC()))); + jobScheduler.start(); } \ No newline at end of file diff --git a/finnow-api/source/transaction/api.d b/finnow-api/source/transaction/api.d index b6d4cbd..1486fff 100644 --- a/finnow-api/source/transaction/api.d +++ b/finnow-api/source/transaction/api.d @@ -185,7 +185,7 @@ struct CategoryPayload { string name; string description; string color; - Nullable!ulong parentId; + Optional!ulong parentId; } @PostMapping(PROFILE_PATH ~ "/categories") @@ -215,3 +215,95 @@ void handleDeleteCategory(ref ServerHttpRequest request, ref ServerHttpResponse private ulong getCategoryId(in ServerHttpRequest request) { return getPathParamOrThrow!ulong(request, "categoryId"); } + +// Drafts & Templates & Recurring Transactions + +immutable DEFAULT_DRAFT_PAGE = PageRequest(1, 10, [Sort("draft.id", SortDir.DESC)]); + +@GetMapping(PROFILE_PATH ~ "/transaction-drafts") +void handleGetDrafts(ref ServerHttpRequest request, ref ServerHttpResponse response) { + ProfileDataSource ds = getProfileDataSource(request); + PageRequest pr = PageRequest.parse(request, DEFAULT_DRAFT_PAGE); + Page!TransactionDraftListItem page = getDrafts(ds, pr); + writeJsonBody(response, page); +} + +@GetMapping(PROFILE_PATH ~ "/transaction-drafts/:draftId:ulong") +void handleGetDraft(ref ServerHttpRequest request, ref ServerHttpResponse response) { + ProfileDataSource ds = getProfileDataSource(request); + TransactionDraftResponse draft = getDraft(ds, getDraftId(request)); + import asdf : serializeToJson; + string jsonStr = serializeToJson(draft); + response.writeBodyString(jsonStr, "application/json"); +} + +@PostMapping(PROFILE_PATH ~ "/transaction-drafts") +void handleAddDraft(ref ServerHttpRequest request, ref ServerHttpResponse response) { + auto fullPayload = parseMultipartFilesAndBody!TransactionDraftPayload(request); + ProfileDataSource ds = getProfileDataSource(request); + TransactionDraftResponse draft = addDraft(ds, fullPayload.payload, fullPayload.files); + import asdf : serializeToJson; + string jsonStr = serializeToJson(draft); + response.writeBodyString(jsonStr, "application/json"); +} + +@PutMapping(PROFILE_PATH ~ "/transaction-drafts/:draftId:ulong") +void handleUpdateDraft(ref ServerHttpRequest request, ref ServerHttpResponse response) { + ProfileDataSource ds = getProfileDataSource(request); + auto fullPayload = parseMultipartFilesAndBody!TransactionDraftPayload(request); + TransactionDraftResponse draft = updateDraft(ds, getDraftId(request), fullPayload.payload, fullPayload.files); + import asdf : serializeToJson; + string jsonStr = serializeToJson(draft); + response.writeBodyString(jsonStr, "application/json"); +} + +@DeleteMapping(PROFILE_PATH ~ "/transaction-drafts/:draftId:ulong") +void handleDeleteDraft(ref ServerHttpRequest request, ref ServerHttpResponse response) { + ProfileDataSource ds = getProfileDataSource(request); + ulong draftId = getDraftId(request); + deleteDraft(ds, draftId); +} + +@GetMapping(PROFILE_PATH ~ "/transaction-drafts/:draftId:ulong/recurring-transactions") +void handleGetDraftRecurringTransactions( + ref ServerHttpRequest request, + ref ServerHttpResponse response +) { + ProfileDataSource ds = getProfileDataSource(request); + ulong draftId = getDraftId(request); + RecurringTransactionResponse[] results = getRecurringTransactionsForDraft(ds, draftId); + writeJsonBody(response, results); +} + +private ulong getDraftId(in ServerHttpRequest request) { + return getPathParamOrThrow!ulong(request, "draftId"); +} + +immutable DEFAULT_RECURRING_TRANSACTIONS_PAGE = PageRequest(1, 10, [Sort("id", SortDir.DESC)]); + +@GetMapping(PROFILE_PATH ~ "/recurring-transactions") +void handleGetRecurringTransactions(ref ServerHttpRequest request, ref ServerHttpResponse response) { + ProfileDataSource ds = getProfileDataSource(request); + PageRequest pr = PageRequest.parse(request, DEFAULT_RECURRING_TRANSACTIONS_PAGE); + Page!RecurringTransactionResponse page = getRecurringTransactions(ds, pr); + writeJsonBody(response, page); +} + +@PostMapping(PROFILE_PATH ~ "/recurring-transactions") +void handlePostRecurringTransaction(ref ServerHttpRequest request, ref ServerHttpResponse response) { + RecurringTransactionPayload payload = readJsonBodyAs!RecurringTransactionPayload(request); + ProfileDataSource ds = getProfileDataSource(request); + RecurringTransactionResponse result = createRecurringTransaction(ds, payload); + writeJsonBody(response, result); +} + +@DeleteMapping(PROFILE_PATH ~ "/recurring-transactions/:recurringTransactionId:ulong") +void handleDeleteRecurringTransaction(ref ServerHttpRequest request, ref ServerHttpResponse response) { + ProfileDataSource ds = getProfileDataSource(request); + ulong id = getRecurringTransactionId(request); + deleteRecurringTransaction(ds, id); +} + +private ulong getRecurringTransactionId(in ServerHttpRequest request) { + return getPathParamOrThrow!ulong(request, "recurringTransactionId"); +} diff --git a/finnow-api/source/transaction/data.d b/finnow-api/source/transaction/data.d index a6c38c8..4466257 100644 --- a/finnow-api/source/transaction/data.d +++ b/finnow-api/source/transaction/data.d @@ -36,6 +36,7 @@ interface TransactionCategoryRepository { ); } +// TODO: Migrate into transaction repo, similar to drafts! interface TransactionTagRepository { string[] findAllByTransactionId(ulong transactionId); void updateTags(ulong transactionId, in string[] tags); @@ -52,3 +53,25 @@ interface TransactionRepository { TransactionDetail update(ulong transactionId, in AddTransactionPayload data); void deleteById(ulong id); } + +interface TransactionDraftRepository { + Page!TransactionDraftListItem findAllDrafts(in PageRequest pr); + Page!TransactionDraftListItem findAllTemplates(in PageRequest pr); + Page!TransactionDraftListItem findAll(in PageRequest pr); + Optional!TransactionDraftResponse findById(ulong id); + TransactionDraftResponse insert(in TransactionDraftPayload data); + void linkAttachment(ulong draftId, ulong attachmentId); + TransactionDraftResponse update(ulong draftId, in TransactionDraftPayload data); + void deleteById(ulong id); + + void updateTags(ulong draftId, in string[] tags); + string[] findAllTags(); +} + +interface RecurringTransactionRepository { + Page!RecurringTransactionResponse findAll(in PageRequest pr); + RecurringTransactionResponse[] findAllByDraftId(ulong draftId); + Optional!RecurringTransactionResponse findById(ulong id); + RecurringTransactionResponse insert(in RecurringTransactionPayload data); + void deleteById(ulong id); +} diff --git a/finnow-api/source/transaction/data_impl_sqlite.d b/finnow-api/source/transaction/data_impl_sqlite.d index 1991cbe..2476caf 100644 --- a/finnow-api/source/transaction/data_impl_sqlite.d +++ b/finnow-api/source/transaction/data_impl_sqlite.d @@ -1,6 +1,7 @@ module transaction.data_impl_sqlite; -import handy_http_primitives : Optional, StringMultiValueMap; +import handy_http_primitives : Optional, StringMultiValueMap, mapIfPresent, toOptional; +import util.data; import std.datetime; import std.typecons; import d2sqlite3; @@ -13,6 +14,8 @@ import util.money; import util.pagination; import util.data; import account.model; +import account.dto; +import attachment.dto; class SqliteTransactionVendorRepository : TransactionVendorRepository { private Database db; @@ -228,7 +231,7 @@ class SqliteTransactionCategoryRepository : TransactionCategoryRepository { import std.typecons; return TransactionCategory( row.peek!ulong(0), - toOptional(row.peek!(Nullable!ulong)(1)), + row.parseOptional!ulong(1), row.peek!string(2), row.peek!string(3), row.peek!string(4) @@ -283,12 +286,8 @@ class SqliteTransactionRepository : TransactionRepository { } Page!TransactionsListItem findAll(in PageRequest pr) { - const pageIdsQuery = "SELECT DISTINCT txn.id FROM \"transaction\" txn " ~ pr.toSql(); - QueryBuilder qb = getBuilderForTransactionsList(); - addSelectsForTransactionsList(qb); - qb.where("txn.id IN (" ~ pageIdsQuery ~ ")"); - string query = qb.build() ~ "\n" ~ pr.toOrderClause(); - TransactionsListItem[] results = util.sqlite.findAllDirect(db, query, &parseListItems); + string query = import("sql/query/get_transactions.sql") ~ "\n" ~ pr.toSql(); + TransactionsListItem[] results = util.sqlite.findAll(db, query, &parseListItem); ulong totalCount = util.sqlite.count(db, "SELECT COUNT(DISTINCT id) FROM \"transaction\""); return Page!(TransactionsListItem).of(results, pr, totalCount); } @@ -328,10 +327,10 @@ class SqliteTransactionRepository : TransactionRepository { qb.conditions = []; qb.argBinders = []; addSelectsForTransactionsList(qb); + qb.groupBy("txn.id"); qb.where("txn.id IN (" ~ idsStr ~ ")"); string query = qb.build() ~ "\n" ~ pr.toOrderClause(); - Statement stmt = db.prepare(query); - auto results = parseListItems(stmt.execute()); + TransactionsListItem[] results = util.sqlite.findAll(db, query, &parseListItem); return Page!TransactionsListItem.of(results, pr, count); } @@ -370,7 +369,7 @@ class SqliteTransactionRepository : TransactionRepository { Optional!TransactionDetail findById(ulong id) { Optional!TransactionDetail item = util.sqlite.findOne( db, - import("sql/get_transaction.sql"), + import("sql/query/get_transaction.sql"), (row) { TransactionDetail item; item.id = row.peek!ulong(0); @@ -383,43 +382,41 @@ class SqliteTransactionRepository : TransactionRepository { Nullable!ulong vendorId = row.peek!(Nullable!ulong)(7); if (!vendorId.isNull) { - item.vendor = Optional!(TransactionDetail.Vendor).of( - TransactionDetail.Vendor( - vendorId.get, - row.peek!string(8), - row.peek!string(9) - )).toNullable; + item.vendor = Optional!TransactionVendor.of(TransactionVendor( + vendorId.get, + row.peek!string(8), + row.peek!string(9) + )); } Nullable!ulong categoryId = row.peek!(Nullable!ulong)(10); if (!categoryId.isNull) { - item.category = Optional!(TransactionDetail.Category).of( - TransactionDetail.Category( - categoryId.get, - row.peek!(Nullable!ulong)(11), - row.peek!string(12), - row.peek!string(13), - row.peek!string(14) - )).toNullable; + item.category = Optional!TransactionCategory.of(TransactionCategory( + categoryId.get, + row.parseOptional!ulong(11), + row.peek!string(12), + row.peek!string(13), + row.peek!string(14) + )); } Nullable!ulong creditedAccountId = row.peek!(Nullable!ulong)(15); if (!creditedAccountId.isNull) { - item.creditedAccount = Optional!(TransactionDetail.Account).of( - TransactionDetail.Account( + item.creditedAccount = Optional!SimpleAccountResponse.of( + SimpleAccountResponse( creditedAccountId.get, row.peek!string(16), row.peek!string(17), row.peek!string(18) - )).toNullable; + )); } Nullable!ulong debitedAccountId = row.peek!(Nullable!ulong)(19); if (!debitedAccountId.isNull) { - item.debitedAccount = Optional!(TransactionDetail.Account).of( - TransactionDetail.Account( + item.debitedAccount = Optional!SimpleAccountResponse.of( + SimpleAccountResponse( debitedAccountId.get, row.peek!string(20), row.peek!string(21), row.peek!string(22) - )).toNullable; + )); } string tagsStr = row.peek!string(23); if (tagsStr !is null && tagsStr.length > 0) { @@ -435,23 +432,23 @@ class SqliteTransactionRepository : TransactionRepository { if (item.isNull) return item; item.value.lineItems = util.sqlite.findAll( db, - import("sql/get_line_items.sql"), + import("sql/query/get_line_items.sql"), (row) { - TransactionDetail.LineItem li; + TransactionLineItemResponse li; li.idx = row.peek!uint(0); li.valuePerItem = row.peek!long(1); li.quantity = row.peek!ulong(2); li.description = row.peek!string(3); - Nullable!ulong categoryId = row.peek!(Nullable!ulong)(4); - if (!categoryId.isNull) { - li.category = Optional!(TransactionDetail.Category).of( - TransactionDetail.Category( - categoryId.get, - row.peek!(Nullable!ulong)(5), + Optional!ulong categoryId = row.parseOptional!ulong(4); + if (categoryId) { + li.category = Optional!TransactionCategory.of( + TransactionCategory( + categoryId.value, + row.parseOptional!ulong(5), row.peek!string(6), row.peek!string(7), row.peek!string(8) - )).toNullable; + )); } return li; }, @@ -470,8 +467,8 @@ class SqliteTransactionRepository : TransactionRepository { data.currencyCode, data.description, data.internalTransfer, - data.vendorId, - data.categoryId + data.vendorId.toNullable(), + data.categoryId.toNullable() ); ulong transactionId = db.lastInsertRowid(); insertLineItems(transactionId, data); @@ -496,8 +493,8 @@ class SqliteTransactionRepository : TransactionRepository { data.currencyCode, data.description, data.internalTransfer, - data.vendorId, - data.categoryId, + data.vendorId.toNullable(), + data.categoryId.toNullable(), transactionId ); // Re-write all line items: @@ -521,97 +518,56 @@ class SqliteTransactionRepository : TransactionRepository { ); } - /** - * Function to parse a list of transaction list items as obtained from the - * `get_transactions.sql` query. Because there are possibly multiple rows - * per transaction, we have to deal with the result range as a whole. - * Params: - * r = The result range to read. - * Returns: The list of transaction list items. - */ - private static TransactionsListItem[] parseListItems(ResultRange r) { - import std.array : appender; - auto app = appender!(TransactionsListItem[]); + private static TransactionsListItem parseListItem(Row row) { TransactionsListItem item; - - /// Helper function that appends the current item to the list, and resets state. - void appendItem() { + item.id = row.peek!ulong(0); + item.timestamp = row.peek!string(1); + item.addedAt = row.peek!string(2); + item.amount = row.peek!ulong(3); + item.currency = Currency.ofCode(row.peek!(string, PeekMode.slice)(4)); + item.description = row.peek!string(5); + item.internalTransfer = row.peek!bool(6); + Optional!ulong vendorId = row.parseOptional!ulong(7); + if (vendorId) { + item.vendor = SimpleVendorResponse( + vendorId.value, + row.peek!string(8) + ).toOptional; + } + Optional!ulong categoryId = row.parseOptional!ulong(9); + if (categoryId) { + item.category = SimpleCategoryResponse( + categoryId.value, + row.peek!string(10), + row.peek!string(11) + ).toOptional; + } + Optional!ulong creditedAccountId = row.parseOptional!ulong(12); + if (creditedAccountId) { + item.creditedAccount = SimpleAccountResponse( + creditedAccountId.value, + row.peek!string(13), + row.peek!string(14), + row.peek!string(15) + ).toOptional; + } + Optional!ulong debitedAccountId = row.parseOptional!ulong(16); + if (debitedAccountId) { + item.debitedAccount = SimpleAccountResponse( + debitedAccountId.value, + row.peek!string(17), + row.peek!string(18), + row.peek!string(19) + ).toOptional; + } + string aggregateTags = row.peek!string(20); + if (aggregateTags !is null) { + import std.string : split; import std.algorithm : sort; + item.tags = aggregateTags.split(","); sort(item.tags); - app ~= item; - item.id = 0; - item.tags = []; - item.vendor = Optional!(TransactionsListItem.Vendor).empty(); - item.category = Optional!(TransactionsListItem.Category).empty(); - item.creditedAccount = Optional!(TransactionsListItem.Account).empty(); - item.debitedAccount = Optional!(TransactionsListItem.Account).empty(); } - - size_t rowCount = 0; - foreach (Row row; r) { - rowCount++; - ulong txnId = row.peek!ulong(0); - if (item.id != txnId) { - // We're parsing a new item. First, append the current one if there is one. - if (item.id != 0) { - appendItem(); - } - - item.id = txnId; - item.timestamp = row.peek!string(1); - item.addedAt = row.peek!string(2); - item.amount = row.peek!ulong(3); - item.currency = Currency.ofCode(row.peek!(string, PeekMode.slice)(4)); - item.description = row.peek!string(5); - item.internalTransfer = row.peek!bool(6); - // Read the nullable Vendor information. - Nullable!ulong vendorId = row.peek!(Nullable!ulong)(7); - if (!vendorId.isNull) { - string vendorName = row.peek!string(8); - item.vendor = Optional!(TransactionsListItem.Vendor).of( - TransactionsListItem.Vendor(vendorId.get, vendorName)); - } - // Read the nullable Category information. - Nullable!ulong categoryId = row.peek!(Nullable!ulong)(9); - if (!categoryId.isNull) { - string categoryName = row.peek!string(10); - string categoryColor = row.peek!string(11); - item.category = Optional!(TransactionsListItem.Category).of( - TransactionsListItem.Category(categoryId.get, categoryName, categoryColor)); - } - // Read the nullable creditedAccount. - Nullable!ulong creditedAccountId = row.peek!(Nullable!ulong)(12); - if (!creditedAccountId.isNull) { - ulong id = creditedAccountId.get; - string name = row.peek!string(13); - string type = row.peek!string(14); - string suffix = row.peek!string(15); - item.creditedAccount = Optional!(TransactionsListItem.Account).of( - TransactionsListItem.Account(id, name, type, suffix)); - } - // Read the nullable debitedAccount. - Nullable!ulong debitedAccountId = row.peek!(Nullable!ulong)(16); - if (!debitedAccountId.isNull) { - ulong id = debitedAccountId.get; - string name = row.peek!string(17); - string type = row.peek!string(18); - string suffix = row.peek!string(19); - item.debitedAccount = Optional!(TransactionsListItem.Account).of( - TransactionsListItem.Account(id, name, type, suffix)); - } - } - - // Read multi-row properties, like tags, to the current item. - string tag = row.peek!string(20); - if (tag !is null) { - item.tags ~= tag; - } - } - // If there's one last cached item, append it to the list. - if (item.id != 0) { - appendItem(); - } - return app[]; + return item; } private void insertLineItems(ulong transactionId, in AddTransactionPayload data) { @@ -624,7 +580,7 @@ class SqliteTransactionRepository : TransactionRepository { lineItem.valuePerItem, lineItem.quantity, lineItem.description, - lineItem.categoryId + lineItem.categoryId.toNullable() ); } } @@ -664,6 +620,348 @@ class SqliteTransactionRepository : TransactionRepository { .select("account_debit.name") .select("account_debit.type") .select("account_debit.number_suffix") - .select("tags.tag"); + .select("GROUP_CONCAT(tags.tag)"); + } +} + +class SqliteTransactionDraftRepository : TransactionDraftRepository { + private Database db; + this(Database db) { + this.db = db; + } + + Page!TransactionDraftListItem findAllDrafts(in PageRequest pr) { + return findAllInternal(pr, DraftType.DRAFT.toOptional); + } + + Page!TransactionDraftListItem findAllTemplates(in PageRequest pr) { + return findAllInternal(pr, DraftType.TEMPLATE.toOptional); + } + + Page!TransactionDraftListItem findAll(in PageRequest pr) { + return findAllInternal(pr, Optional!DraftType.empty()); + } + + private static enum DraftType { + DRAFT, + TEMPLATE + } + + private Page!TransactionDraftListItem findAllInternal(in PageRequest pr, Optional!DraftType type) { + QueryBuilder qb = getBuilderForDraftsList(); + addSelectsForDraftsList(qb); + qb.groupBy("draft.id"); + if (type) { + if (type.value == DraftType.DRAFT) { + qb.where("template_name IS NULL"); + } else { + qb.where("template_name IS NOT NULL"); + } + } + string query = qb.build() ~ "\n" ~ pr.toSql(); + TransactionDraftListItem[] results = util.sqlite.findAll(db, query, &parseDraftListItem); + ulong totalCount = util.sqlite.count(db, "SELECT COUNT(DISTINCT id) FROM transaction_draft"); + return Page!(TransactionDraftListItem).of(results, pr, totalCount); + } + + Optional!TransactionDraftResponse findById(ulong id) { + // First fetch the draft list item (contains all basic properties). + QueryBuilder qb = getBuilderForDraftsList(); + addSelectsForDraftsList(qb); + qb.groupBy("draft.id"); + qb.where("draft.id = ?"); + string query = qb.build(); + Optional!TransactionDraftListItem li = util.sqlite.findOne(db, query, &parseDraftListItem, id); + if (li.isNull) return Optional!TransactionDraftResponse.empty(); + TransactionDraftListItem draft = li.value; + // Then fetch line items. + TransactionDraftResponse response; + response.id = draft.id; + response.addedAt = draft.addedAt; + response.templateName = draft.templateName; + response.timestamp = draft.timestamp; + response.amount = draft.amount; + response.currency = draft.currency; + response.description = draft.description; + response.internalTransfer = draft.internalTransfer; + response.vendor = draft.vendor; + response.category = draft.category; + response.creditedAccount = draft.creditedAccount; + response.debitedAccount = draft.debitedAccount; + response.tags = li.value.tags; + response.lineItems = util.sqlite.findAll( + db, + import("sql/query/get_line_items_draft.sql"), + (row) { + TransactionLineItemResponse item; + item.idx = row.peek!uint(0); + item.valuePerItem = row.peek!long(1); + item.quantity = row.peek!ulong(2); + item.description = row.peek!string(3); + Optional!ulong categoryId = row.parseOptional!ulong(4); + if (categoryId) { + item.category = Optional!TransactionCategory.of( + TransactionCategory( + categoryId.value, + row.parseOptional!ulong(5), + row.peek!string(6), + row.peek!string(7), + row.peek!string(8) + )); + } + return item; + }, + draft.id + ); + // Return the response, excluding attachments (they are fetched using the attachment repo). + return Optional!TransactionDraftResponse.of(response); + } + + TransactionDraftResponse insert(in TransactionDraftPayload data) { + util.sqlite.update( + db, + import("sql/insert_transaction_draft.sql"), + Clock.currTime(UTC()).toISOExtString(), + data.templateName.toNullable(), + data.timestamp.toNullable(), + data.amount.toNullable(), + data.currencyCode.toNullable(), + data.description.toNullable(), + data.internalTransfer.toNullable(), + data.vendorId.toNullable(), + data.categoryId.toNullable(), + data.creditedAccountId.toNullable(), + data.debitedAccountId.toNullable() + ); + ulong draftId = db.lastInsertRowid(); + insertLineItems(draftId, data); + return findById(draftId).orElseThrow(); + } + + void linkAttachment(ulong draftId, ulong attachmentId) { + util.sqlite.update( + db, + "INSERT INTO transaction_draft_attachment (draft_id, attachment_id) VALUES (?, ?)", + draftId, + attachmentId + ); + } + + TransactionDraftResponse update(ulong draftId, in TransactionDraftPayload data) { + util.sqlite.update( + db, + import("sql/update_transaction_draft.sql"), + data.templateName.toNullable(), + data.timestamp.toNullable(), + data.amount.toNullable(), + data.currencyCode.toNullable(), + data.description.toNullable(), + data.internalTransfer.toNullable(), + data.vendorId.toNullable(), + data.categoryId.toNullable(), + data.creditedAccountId.toNullable(), + data.debitedAccountId.toNullable(), + draftId + ); + // Re-write all line items: + util.sqlite.update( + db, + "DELETE FROM transaction_draft_line_item WHERE draft_id = ?", + draftId + ); + insertLineItems(draftId, data); + return findById(draftId).orElseThrow(); + } + + void deleteById(ulong id) { + util.sqlite.update( + db, + "DELETE FROM attachment WHERE id IN ( + SELECT attachment_id FROM transaction_draft_attachment WHERE draft_id = ? + )", + id + ); + util.sqlite.deleteById(db, "transaction_draft", id); + } + + void updateTags(ulong draftId, in string[] tags) { + util.sqlite.update( + db, + "DELETE FROM transaction_draft_tag WHERE draft_id = ?", + draftId + ); + foreach (tag; tags) { + util.sqlite.update( + db, + "INSERT INTO transaction_draft_tag (draft_id, tag) VALUES (?, ?)", + draftId, tag + ); + } + } + + string[] findAllTags() { + return util.sqlite.findAll( + db, + "SELECT DISTINCT tag FROM transaction_draft_tag ORDER BY tag", + r => r.peek!string(0) + ); + } + + + + private QueryBuilder getBuilderForDraftsList() { + return QueryBuilder("transaction_draft draft") + .join("LEFT JOIN transaction_vendor vendor ON vendor.id = draft.vendor_id") + .join("LEFT JOIN transaction_category category ON category.id = draft.category_id") + .join("LEFT JOIN account account_credit ON account_credit.id = draft.credited_account_id") + .join("LEFT JOIN account account_debit ON account_debit.id = draft.debited_account_id") + .join("LEFT JOIN transaction_draft_tag tags ON tags.draft_id = draft.id"); + } + + private void addSelectsForDraftsList(ref QueryBuilder qb) { + qb + .select("draft.id") + .select("draft.added_at") + .select("draft.template_name") + .select("draft.timestamp") + .select("draft.amount")// 5 + .select("draft.currency") + .select("draft.description") + .select("draft.internal_transfer") + .select("vendor.id") + .select("vendor.name")// 10 + .select("category.id") + .select("category.name") + .select("category.color") + .select("account_credit.id") + .select("account_credit.name")// 15 + .select("account_credit.type") + .select("account_credit.number_suffix") + .select("account_debit.id") + .select("account_debit.name") + .select("account_debit.type")// 20 + .select("account_debit.number_suffix") + .select("group_concat(tags.tag)"); + } + + private static TransactionDraftListItem parseDraftListItem(Row row) { + TransactionDraftListItem item; + item.id = row.peek!ulong(0); + item.addedAt = row.peek!string(1); + item.templateName = row.parseOptional!string(2); + item.timestamp = row.parseOptional!string(3); + item.amount = row.parseOptional!ulong(4); + item.currency = row.parseOptional!(string, PeekMode.slice)(5) + .mapIfPresent!(s => Currency.ofCode(s)); + item.description = row.parseOptional!string(6); + item.internalTransfer = row.parseOptional!bool(7); + Optional!ulong vendorId = row.parseOptional!ulong(8); + if (vendorId) { + item.vendor = SimpleVendorResponse( + vendorId.value, + row.peek!string(9) + ).toOptional; + } + Optional!ulong categoryId = row.parseOptional!ulong(10); + if (categoryId) { + item.category = SimpleCategoryResponse( + categoryId.value, + row.peek!string(11), + row.peek!string(12), + ).toOptional; + } + Optional!ulong creditedAccountId = row.parseOptional!ulong(13); + if (creditedAccountId) { + item.creditedAccount = SimpleAccountResponse( + creditedAccountId.value, + row.peek!string(14), + row.peek!string(15), + row.peek!string(16) + ).toOptional; + } + Optional!ulong debitedAccountId = row.parseOptional!ulong(17); + if (debitedAccountId) { + item.debitedAccount = SimpleAccountResponse( + debitedAccountId.value, + row.peek!string(18), + row.peek!string(19), + row.peek!string(20) + ).toOptional; + } + string aggregateTags = row.peek!string(21); + if (aggregateTags !is null) { + import std.string : split; + item.tags = aggregateTags.split(","); + } + import std.algorithm : sort; + sort(item.tags); + return item; + } + + private void insertLineItems(ulong draftId, in TransactionDraftPayload payload) { + foreach (size_t idx, lineItem; payload.lineItems) { + util.sqlite.update( + db, + import("sql/insert_line_item_draft.sql"), + draftId, + idx, + lineItem.valuePerItem, + lineItem.quantity, + lineItem.description, + lineItem.categoryId.toNullable() + ); + } + } +} + +class SqliteRecurringTransactionRepository : RecurringTransactionRepository { + private Database db; + this(Database db) { + this.db = db; + } + + Page!RecurringTransactionResponse findAll(in PageRequest pr) { + string query = "SELECT * FROM recurring_transaction " ~ pr.toSql(); + RecurringTransactionResponse[] results = util.sqlite.findAll(db, query, &parseRecurringTransaction); + ulong totalCount = util.sqlite.count(db, "SELECT COUNT(DISTINCT id) FROM recurring_transaction"); + return Page!(RecurringTransactionResponse).of(results, pr, totalCount); + } + + RecurringTransactionResponse[] findAllByDraftId(ulong draftId) { + string query = "SELECT * FROM recurring_transaction WHERE draft_id = ? ORDER BY id"; + return util.sqlite.findAll(db, query, &parseRecurringTransaction, draftId); + } + + Optional!RecurringTransactionResponse findById(ulong id) { + return util.sqlite.findOne( + db, + "SELECT * FROM recurring_transaction WHERE id = ?", + &parseRecurringTransaction, + id + ); + } + + RecurringTransactionResponse insert(in RecurringTransactionPayload data) { + util.sqlite.update( + db, + "INSERT INTO recurring_transaction " ~ + "(draft_id, schedule_expr) " ~ + "VALUES (?, ?)", + data.draftId, + data.scheduleExpr + ); + return findById(db.lastInsertRowid()).orElseThrow(); + } + + void deleteById(ulong id) { + util.sqlite.deleteById(db, "recurring_transaction", id); + } + + private static RecurringTransactionResponse parseRecurringTransaction(Row row) { + return RecurringTransactionResponse( + row.peek!ulong(0), + row.peek!ulong(1), + row.peek!string(2) + ); } } diff --git a/finnow-api/source/transaction/dto.d b/finnow-api/source/transaction/dto.d index 694626e..9f620a9 100644 --- a/finnow-api/source/transaction/dto.d +++ b/finnow-api/source/transaction/dto.d @@ -1,14 +1,36 @@ module transaction.dto; import handy_http_primitives : Optional; -import asdf : serdeTransformOut; import std.typecons; -import transaction.model : TransactionCategory; +import transaction.model : TransactionCategory, TransactionVendor; import attachment.dto; +import account.dto; import util.data; import util.money; +/// A simple selection of vendor data to be included in other responses. +struct SimpleVendorResponse { + ulong id; + string name; +} + +/// A simple selection of category data to be included in other responses. +struct SimpleCategoryResponse { + ulong id; + string name; + string color; +} + +/// Response data for a transaction's line item. +struct TransactionLineItemResponse { + uint idx; + long valuePerItem; + ulong quantity; + string description; + Optional!TransactionCategory category; +} + /// The transaction data provided when a list of transactions is requested. struct TransactionsListItem { ulong id; @@ -18,33 +40,11 @@ struct TransactionsListItem { Currency currency; string description; bool internalTransfer; - @serdeTransformOut!serializeOptional - Optional!Vendor vendor; - @serdeTransformOut!serializeOptional - Optional!Category category; - @serdeTransformOut!serializeOptional - Optional!Account creditedAccount; - @serdeTransformOut!serializeOptional - Optional!Account debitedAccount; + Optional!SimpleVendorResponse vendor; + Optional!SimpleCategoryResponse category; + Optional!SimpleAccountResponse creditedAccount; + Optional!SimpleAccountResponse debitedAccount; string[] tags; - - static struct Account { - ulong id; - string name; - string type; - string numberSuffix; - } - - static struct Vendor { - ulong id; - string name; - } - - static struct Category { - ulong id; - string name; - string color; - } } /// Transaction data provided when fetching a single transaction. @@ -56,42 +56,13 @@ struct TransactionDetail { Currency currency; string description; bool internalTransfer; - Nullable!Vendor vendor; - Nullable!Category category; - Nullable!Account creditedAccount; - Nullable!Account debitedAccount; + Optional!TransactionVendor vendor; + Optional!TransactionCategory category; + Optional!SimpleAccountResponse creditedAccount; + Optional!SimpleAccountResponse debitedAccount; string[] tags; - LineItem[] lineItems; + TransactionLineItemResponse[] lineItems; AttachmentResponse[] attachments; - - static struct Vendor { - ulong id; - string name; - string description; - } - - static struct Category { - ulong id; - Nullable!ulong parentId; - string name; - string description; - string color; - } - - static struct LineItem { - uint idx; - long valuePerItem; - ulong quantity; - string description; - Nullable!Category category; - } - - static struct Account { - ulong id; - string name; - string type; - string numberSuffix; - } } /// Data provided when a new transaction is added by a user. @@ -101,26 +72,25 @@ struct AddTransactionPayload { string currencyCode; string description; bool internalTransfer; - Nullable!ulong vendorId; - Nullable!ulong categoryId; - Nullable!ulong creditedAccountId; - Nullable!ulong debitedAccountId; + Optional!ulong vendorId; + Optional!ulong categoryId; + Optional!ulong creditedAccountId; + Optional!ulong debitedAccountId; string[] tags; - LineItem[] lineItems; + LineItemPayload[] lineItems; ulong[] attachmentIdsToRemove; - static struct LineItem { + static struct LineItemPayload { long valuePerItem; ulong quantity; string description; - Nullable!ulong categoryId; + Optional!ulong categoryId; } } /// Structure for depicting an entire hierarchical tree structure of categories. struct TransactionCategoryTree { ulong id; - @serdeTransformOut!serializeOptional Optional!ulong parentId; string name; string description; @@ -131,7 +101,6 @@ struct TransactionCategoryTree { struct TransactionCategoryResponse { ulong id; - @serdeTransformOut!serializeOptional Optional!ulong parentId; string name; string description; @@ -170,3 +139,82 @@ struct AggregateTransactionData { } CurrencyData[] currencies; } + +/// Response data for drafts as they'd appear in a list, with less data. +struct TransactionDraftListItem { + ulong id; + string addedAt; + Optional!string templateName; + Optional!string timestamp; + Optional!ulong amount; + Optional!Currency currency; + Optional!string description; + Optional!bool internalTransfer; + + Optional!SimpleVendorResponse vendor; + Optional!SimpleCategoryResponse category; + Optional!SimpleAccountResponse creditedAccount; + Optional!SimpleAccountResponse debitedAccount; + + string[] tags; +} + +/// Data representing a draft (or template). +struct TransactionDraftResponse { + ulong id; + string addedAt; + Optional!string templateName; + Optional!string timestamp; + Optional!ulong amount; + Optional!Currency currency; + Optional!string description; + Optional!bool internalTransfer; + + Optional!SimpleVendorResponse vendor; + Optional!SimpleCategoryResponse category; + Optional!SimpleAccountResponse creditedAccount; + Optional!SimpleAccountResponse debitedAccount; + + string[] tags; + TransactionLineItemResponse[] lineItems; + AttachmentResponse[] attachments; +} + +/// Data provided by users when creating or updating drafts. +struct TransactionDraftPayload { + Optional!string templateName; + Optional!string timestamp; + Optional!ulong amount; + Optional!string currencyCode; + Optional!string description; + Optional!bool internalTransfer; + + Optional!ulong vendorId; + Optional!ulong categoryId; + Optional!ulong creditedAccountId; + Optional!ulong debitedAccountId; + + string[] tags; + LineItemPayload[] lineItems; + ulong[] attachmentIdsToRemove; + + static struct LineItemPayload { + long valuePerItem; + ulong quantity; + string description; + Optional!ulong categoryId; + } +} + +// Recurring Transaction stuff: + +struct RecurringTransactionPayload { + ulong draftId; + string scheduleExpr; +} + +struct RecurringTransactionResponse { + ulong id; + ulong draftId; + string scheduleExpr; +} diff --git a/finnow-api/source/transaction/model.d b/finnow-api/source/transaction/model.d index 11c9513..57055b2 100644 --- a/finnow-api/source/transaction/model.d +++ b/finnow-api/source/transaction/model.d @@ -6,9 +6,9 @@ import std.datetime; import util.money; struct TransactionVendor { - immutable ulong id; - immutable string name; - immutable string description; + ulong id; + string name; + string description; } struct TransactionCategory { @@ -20,23 +20,23 @@ struct TransactionCategory { } struct Transaction { - immutable ulong id; + ulong id; /// The time at which the transaction happened. - immutable SysTime timestamp; + SysTime timestamp; /// The time at which the transaction entity was saved. - immutable SysTime addedAt; - immutable ulong amount; - immutable Currency currency; - immutable string description; - immutable Optional!ulong vendorId; - immutable Optional!ulong categoryId; + SysTime addedAt; + ulong amount; + Currency currency; + string description; + Optional!ulong vendorId; + Optional!ulong categoryId; } struct TransactionLineItem { - immutable ulong transactionId; - immutable uint idx; - immutable long valuePerItem; - immutable ulong quantity; - immutable string description; - immutable Optional!ulong categoryId; + ulong transactionId; + uint idx; + long valuePerItem; + ulong quantity; + string description; + Optional!ulong categoryId; } diff --git a/finnow-api/source/transaction/service.d b/finnow-api/source/transaction/service.d index b6fe647..9c6ebf2 100644 --- a/finnow-api/source/transaction/service.d +++ b/finnow-api/source/transaction/service.d @@ -16,15 +16,16 @@ import account.data; import util.money; import util.pagination; import util.data; +import util.validation.transaction; +import util.validation.draft; import attachment.data; import attachment.dto; // Transactions Services Page!TransactionsListItem getTransactions(ProfileDataSource ds, in PageRequest pageRequest) { - Page!TransactionsListItem page = ds.getTransactionRepository() + return ds.getTransactionRepository() .findAll(pageRequest); - return page; } TransactionDetail getTransaction(ProfileDataSource ds, ulong transactionId) { @@ -46,6 +47,7 @@ TransactionDetail addTransaction(ProfileDataSource ds, in AddTransactionPayload AttachmentRepository attachmentRepo = ds.getAttachmentRepository(); validateTransactionPayload(vendorRepo, categoryRepo, accountRepo, payload); + SysTime now = Clock.currTime(UTC()); SysTime timestamp = SysTime.fromISOExtString(payload.timestamp, UTC()); // Add the transaction: @@ -53,20 +55,20 @@ TransactionDetail addTransaction(ProfileDataSource ds, in AddTransactionPayload ds.doTransaction(() { TransactionDetail txn = txnRepo.insert(payload); AccountJournalEntryRepository jeRepo = ds.getAccountJournalEntryRepository(); - if (!payload.creditedAccountId.isNull) { + if (payload.creditedAccountId) { jeRepo.insert( timestamp, - payload.creditedAccountId.get, + payload.creditedAccountId.value, txn.id, txn.amount, AccountJournalEntryType.CREDIT, txn.currency ); } - if (!payload.debitedAccountId.isNull) { + if (payload.debitedAccountId) { jeRepo.insert( timestamp, - payload.debitedAccountId.get, + payload.debitedAccountId.value, txn.id, txn.amount, AccountJournalEntryType.DEBIT, @@ -75,7 +77,7 @@ TransactionDetail addTransaction(ProfileDataSource ds, in AddTransactionPayload } TransactionTagRepository tagRepo = ds.getTransactionTagRepository(); tagRepo.updateTags(txn.id, payload.tags); - updateAttachments(txn.id, timestamp, payload.attachmentIdsToRemove, files, attachmentRepo, txnRepo); + updateAttachments(txn.id, now, payload.attachmentIdsToRemove, files, attachmentRepo, txnRepo); txnId = txn.id; }); return getTransaction(ds, txnId); @@ -96,6 +98,7 @@ TransactionDetail updateTransaction( validateTransactionPayload(vendorRepo, categoryRepo, accountRepo, payload); SysTime timestamp = SysTime.fromISOExtString(payload.timestamp, UTC()); + SysTime now = Clock.currTime(UTC()); const TransactionDetail prev = transactionRepo.findById(transactionId) .orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND)); @@ -105,7 +108,7 @@ TransactionDetail updateTransaction( TransactionDetail curr = transactionRepo.update(transactionId, payload); updateLinkedAccountJournalEntries(prev, curr, payload, ds, timestamp); tagRepo.updateTags(transactionId, payload.tags); - updateAttachments(curr.id, timestamp, payload.attachmentIdsToRemove, files, attachmentRepo, transactionRepo); + updateAttachments(curr.id, now, payload.attachmentIdsToRemove, files, attachmentRepo, transactionRepo); }); return getTransaction(ds, transactionId); } @@ -120,45 +123,45 @@ private void updateLinkedAccountJournalEntries( AccountJournalEntryRepository jeRepo = ds.getAccountJournalEntryRepository(); const bool amountOrCurrencyChanged = prev.amount != curr.amount || prev.currency.code != curr.currency.code; const bool updateCreditEntry = amountOrCurrencyChanged || ( - (prev.creditedAccount.isNull && !payload.creditedAccountId.isNull) || - (!prev.creditedAccount.isNull && payload.creditedAccountId.isNull) || + (!prev.creditedAccount && payload.creditedAccountId) || + (prev.creditedAccount && !payload.creditedAccountId) || ( - !prev.creditedAccount.isNull && - !payload.creditedAccountId.isNull && - prev.creditedAccount.get.id != payload.creditedAccountId.get + prev.creditedAccount && + payload.creditedAccountId && + prev.creditedAccount.value.id != payload.creditedAccountId.value ) ); const bool updateDebitEntry = amountOrCurrencyChanged || ( - (prev.debitedAccount.isNull && !payload.creditedAccountId.isNull) || - (!prev.debitedAccount.isNull && payload.debitedAccountId.isNull) || + (!prev.debitedAccount && payload.creditedAccountId) || + (prev.debitedAccount && !payload.debitedAccountId) || ( - !prev.debitedAccount.isNull && - !payload.debitedAccountId.isNull && - prev.debitedAccount.get.id != payload.debitedAccountId.get + prev.debitedAccount && + payload.debitedAccountId && + prev.debitedAccount.value.id != payload.debitedAccountId.value ) ); // Update journal entries if necessary: - if (updateCreditEntry && !prev.creditedAccount.isNull) { - jeRepo.deleteByAccountIdAndTransactionId(prev.creditedAccount.get.id, prev.id); + if (updateCreditEntry && prev.creditedAccount) { + jeRepo.deleteByAccountIdAndTransactionId(prev.creditedAccount.value.id, prev.id); } - if (updateCreditEntry && !payload.creditedAccountId.isNull) { + if (updateCreditEntry && payload.creditedAccountId) { jeRepo.insert( timestamp, - payload.creditedAccountId.get, + payload.creditedAccountId.value, curr.id, curr.amount, AccountJournalEntryType.CREDIT, curr.currency ); } - if (updateDebitEntry && !prev.debitedAccount.isNull) { - jeRepo.deleteByAccountIdAndTransactionId(prev.debitedAccount.get.id, prev.id); + if (updateDebitEntry && prev.debitedAccount) { + jeRepo.deleteByAccountIdAndTransactionId(prev.debitedAccount.value.id, prev.id); } - if (updateDebitEntry && !payload.debitedAccountId.isNull) { + if (updateDebitEntry && payload.debitedAccountId) { jeRepo.insert( timestamp, - payload.debitedAccountId.get, + payload.debitedAccountId.value, curr.id, curr.amount, AccountJournalEntryType.DEBIT, @@ -181,76 +184,6 @@ void deleteTransaction(ProfileDataSource ds, ulong transactionId) { }); } -private void validateTransactionPayload( - TransactionVendorRepository vendorRepo, - TransactionCategoryRepository categoryRepo, - AccountRepository accountRepo, - in AddTransactionPayload payload -) { - if (payload.creditedAccountId.isNull && payload.debitedAccountId.isNull) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "At least one account must be linked."); - } - if ( - !payload.creditedAccountId.isNull && - !payload.debitedAccountId.isNull && - payload.creditedAccountId.get == payload.debitedAccountId.get - ) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Cannot link the same account as both credit and debit."); - } - if (payload.amount == 0) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Amount should be greater than 0."); - } - SysTime now = Clock.currTime(UTC()); - SysTime timestamp; - try { - timestamp = SysTime.fromISOExtString(payload.timestamp, UTC()); - } catch (TimeException e) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Invalid timestamp format. Expected ISO-8601 datetime."); - } - if (timestamp > now) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Cannot create transaction in the future."); - } - if (!payload.vendorId.isNull && !vendorRepo.existsById(payload.vendorId.get)) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Vendor doesn't exist."); - } - if (!payload.categoryId.isNull && !categoryRepo.existsById(payload.categoryId.get)) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Category doesn't exist."); - } - if (!payload.creditedAccountId.isNull && !accountRepo.existsById(payload.creditedAccountId.get)) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Credited account doesn't exist."); - } - if (!payload.debitedAccountId.isNull && !accountRepo.existsById(payload.debitedAccountId.get)) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Debited account doesn't exist."); - } - foreach (tag; payload.tags) { - import std.regex; - auto r = ctRegex!(`^[a-z0-9-_]{3,32}$`); - if (!matchFirst(tag, r)) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Invalid tag: \"" ~ tag ~ "\"."); - } - } - if (payload.lineItems.length > 0) { - long lineItemsTotal = 0; - foreach (lineItem; payload.lineItems) { - if (!lineItem.categoryId.isNull && !categoryRepo.existsById(lineItem.categoryId.get)) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Line item's category doesn't exist."); - } - if (lineItem.quantity == 0) { - throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Line item's quantity should greater than zero."); - } - for (ulong i = 0; i < lineItem.quantity; i++) { - lineItemsTotal += lineItem.valuePerItem; - } - } - if (lineItemsTotal != payload.amount) { - throw new HttpStatusException( - HttpStatus.BAD_REQUEST, - "Total of all line items doesn't equal the transaction's total." - ); - } - } -} - /** * Helper function to add / remove attachments for a transaction. */ @@ -454,7 +387,7 @@ TransactionCategoryResponse createCategory(ProfileDataSource ds, in CategoryPayl if (repo.existsByName(payload.name)) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Name already in use."); } - if (!payload.parentId.isNull && !repo.existsById(payload.parentId.get)) { + if (payload.parentId && !repo.existsById(payload.parentId.value)) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Invalid parent id."); } import std.regex; @@ -463,7 +396,7 @@ TransactionCategoryResponse createCategory(ProfileDataSource ds, in CategoryPayl throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Invalid color hex string."); } auto category = repo.insert( - toOptional(payload.parentId), + payload.parentId, payload.name, payload.description, payload.color @@ -481,7 +414,7 @@ TransactionCategoryResponse updateCategory(ProfileDataSource ds, ulong categoryI if (payload.name != prev.name && repo.existsByName(payload.name)) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Name already in use."); } - if (!payload.parentId.isNull && !repo.existsById(payload.parentId.get)) { + if (payload.parentId && !repo.existsById(payload.parentId.value)) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Invalid parent id."); } TransactionCategory curr = repo.updateById( @@ -489,7 +422,7 @@ TransactionCategoryResponse updateCategory(ProfileDataSource ds, ulong categoryI payload.name, payload.description, payload.color, - toOptional!ulong(payload.parentId) + payload.parentId ); return TransactionCategoryResponse.of(curr); } @@ -497,3 +430,124 @@ TransactionCategoryResponse updateCategory(ProfileDataSource ds, ulong categoryI void deleteCategory(ProfileDataSource ds, ulong categoryId) { ds.getTransactionCategoryRepository().deleteById(categoryId); } + +// Draft services + +Page!TransactionDraftListItem getDrafts(ProfileDataSource ds, in PageRequest pr) { + return ds.getTransactionDraftRepository().findAll(pr); +} + +TransactionDraftResponse getDraft(ProfileDataSource ds, ulong draftId) { + return ds.getTransactionDraftRepository() + .findById(draftId) + // Populate the list of attachments for the draft. + .mapIfPresent!((draft) { + import std.algorithm : map; + import std.array : array; + draft.attachments = ds.getAttachmentRepository() + .findAllByTransactionDraftId(draft.id) + .map!(AttachmentResponse.of) + .array; + return draft; + }) + .orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND)); +} + +TransactionDraftResponse addDraft(ProfileDataSource ds, in TransactionDraftPayload payload, in MultipartFile[] files) { + TransactionDraftRepository draftRepo = ds.getTransactionDraftRepository(); + AttachmentRepository attachmentRepo = ds.getAttachmentRepository(); + + validateDraftPayload( + ds.getTransactionVendorRepository(), + ds.getTransactionCategoryRepository(), + ds.getAccountRepository(), + payload + ); + SysTime now = Clock.currTime(UTC()); + + ulong draftId; + ds.doTransaction(() { + TransactionDraftResponse draft = draftRepo.insert(payload); + draftRepo.updateTags(draft.id, payload.tags); + updateDraftAttachments(draft.id, now, payload.attachmentIdsToRemove, files, attachmentRepo, draftRepo); + draftId = draft.id; + }); + return getDraft(ds, draftId); +} + +TransactionDraftResponse updateDraft( + ProfileDataSource ds, + ulong draftId, + in TransactionDraftPayload payload, + in MultipartFile[] files +) { + TransactionDraftRepository draftRepo = ds.getTransactionDraftRepository(); + AttachmentRepository attachmentRepo = ds.getAttachmentRepository(); + + validateDraftPayload( + ds.getTransactionVendorRepository(), + ds.getTransactionCategoryRepository(), + ds.getAccountRepository(), + payload + ); + SysTime now = Clock.currTime(UTC()); + + ds.doTransaction(() { + draftRepo.update(draftId, payload); + draftRepo.updateTags(draftId, payload.tags); + updateDraftAttachments(draftId, now, payload.attachmentIdsToRemove, files, attachmentRepo, draftRepo); + }); + return getDraft(ds, draftId); +} + +void deleteDraft(ProfileDataSource ds, ulong draftId) { + TransactionDraftRepository draftRepo = ds.getTransactionDraftRepository(); + AttachmentRepository attachmentRepo = ds.getAttachmentRepository(); + TransactionDraftResponse draft = draftRepo.findById(draftId) + .orElseThrow(() => new HttpStatusException(HttpStatus.NOT_FOUND)); + ds.doTransaction(() { + // First delete all attachments. + foreach (a; attachmentRepo.findAllByTransactionDraftId(draft.id)) { + attachmentRepo.remove(a.id); + } + draftRepo.deleteById(draft.id); + }); +} + +private void updateDraftAttachments( + ulong draftId, + SysTime timestamp, + in ulong[] attachmentIdsToRemove, + in MultipartFile[] attachmentsToAdd, + AttachmentRepository attachmentRepo, + TransactionDraftRepository draftRepo +) { + foreach (file; attachmentsToAdd) { + ulong attachmentId = attachmentRepo.save(timestamp, file.name, file.contentType, file.content); + draftRepo.linkAttachment(draftId, attachmentId); + } + foreach (idToRemove; attachmentIdsToRemove) { + attachmentRepo.remove(idToRemove); + } +} + +// Recurring Transactions: + +Page!RecurringTransactionResponse getRecurringTransactions(ProfileDataSource ds, PageRequest pr) { + return ds.getRecurringTransactionRepository().findAll(pr); +} + +RecurringTransactionResponse createRecurringTransaction(ProfileDataSource ds, in RecurringTransactionPayload payload) { + return ds.getRecurringTransactionRepository() + .insert(payload); +} + +void deleteRecurringTransaction(ProfileDataSource ds, ulong id) { + ds.getRecurringTransactionRepository() + .deleteById(id); +} + +RecurringTransactionResponse[] getRecurringTransactionsForDraft(ProfileDataSource ds, ulong draftId) { + return ds.getRecurringTransactionRepository() + .findAllByDraftId(draftId); +} diff --git a/finnow-api/source/util/data.d b/finnow-api/source/util/data.d index 7b82347..c4152c4 100644 --- a/finnow-api/source/util/data.d +++ b/finnow-api/source/util/data.d @@ -21,13 +21,6 @@ Nullable!T toNullable(T)(Optional!T value) { } } -auto serializeOptional(T)(Optional!T value) { - if (value.isNull) { - return Nullable!T(); - } - return Nullable!T(value.value); -} - T getPathParamOrThrow(T)(in ServerHttpRequest req, string name) { import handy_http_handlers.path_handler; import std.conv : to, ConvException; diff --git a/finnow-api/source/util/sample_data.d b/finnow-api/source/util/sample_data.d index a44e0b1..0ef6e92 100644 --- a/finnow-api/source/util/sample_data.d +++ b/finnow-api/source/util/sample_data.d @@ -1,7 +1,7 @@ module util.sample_data; import slf4d; -import handy_http_primitives : Optional, mapIfPresent; +import handy_http_primitives : Optional, mapIfPresent, toOptional; import auth; import profile; @@ -143,10 +143,10 @@ void generateRandomTransactions(ProfileDataSource ds) { AddTransactionPayload data; data.timestamp = timestamp.toISOExtString(); if (uniform01() < 0.7) { - data.vendorId = Optional!ulong.of(choice(vendors).id).toNullable; + data.vendorId = Optional!ulong.of(choice(vendors).id); } if (uniform01() < 0.8) { - data.categoryId = Optional!ulong.of(choice(categories).id).toNullable; + data.categoryId = Optional!ulong.of(choice(categories).id); } // Randomly choose an account to credit / debit the transaction to. @@ -162,11 +162,11 @@ void generateRandomTransactions(ProfileDataSource ds) { } } if (uniform01() < 0.5) { - data.creditedAccountId = Optional!ulong.of(primaryAccount.id).toNullable; - if (secondaryAccountId) data.debitedAccountId = secondaryAccountId.toNullable; + data.creditedAccountId = Optional!ulong.of(primaryAccount.id); + if (secondaryAccountId) data.debitedAccountId = secondaryAccountId; } else { - data.debitedAccountId = Optional!ulong.of(primaryAccount.id).toNullable; - if (secondaryAccountId) data.creditedAccountId = secondaryAccountId.toNullable; + data.debitedAccountId = Optional!ulong.of(primaryAccount.id); + if (secondaryAccountId) data.creditedAccountId = secondaryAccountId; } // Randomly choose some tags to add. @@ -185,25 +185,25 @@ void generateRandomTransactions(ProfileDataSource ds) { if (uniform01 < 0.5) { long lineItemTotal = 0; foreach (n; 1..uniform(1, 20)) { - AddTransactionPayload.LineItem item; + AddTransactionPayload.LineItemPayload item; item.valuePerItem = uniform(1, 10_000); item.quantity = uniform(1, 5); lineItemTotal += item.quantity * item.valuePerItem; item.description = "Sample item " ~ n.to!string; if (uniform01 < 0.5) { TransactionCategory category = choice(categories); - item.categoryId = category.id; + item.categoryId = Optional!ulong.of(category.id); } data.lineItems ~= item; } long diff = data.amount - lineItemTotal; // Add one final line item that adds up to the transaction total. if (diff != 0) { - data.lineItems ~= AddTransactionPayload.LineItem( + data.lineItems ~= AddTransactionPayload.LineItemPayload( diff, 1, "Last item which reconciles line items total with transaction amount.", - Nullable!ulong.init + Optional!ulong.empty() ); } } diff --git a/finnow-api/source/util/sqlite.d b/finnow-api/source/util/sqlite.d index 5324d88..60d0755 100644 --- a/finnow-api/source/util/sqlite.d +++ b/finnow-api/source/util/sqlite.d @@ -192,11 +192,35 @@ immutable(ubyte[]) parseBlob(Row row, size_t idx) { return row.peek!(ubyte[], PeekMode.slice)(idx).idup; } +/** + * Reads an Optional primitive value from a result row. + * Params: + * row = The row to read from. + * idx = The column index in the row to read. + * Returns: An optional containing the data, if present. + */ +Optional!T parseOptional(T, PeekMode mode = PeekMode.copy)(Row row, size_t idx) { + import std.typecons : Nullable; + import std.traits : isSomeString; + static if (isSomeString!T) { + Nullable!T n = row.peek!(Nullable!T, mode)(idx); + } else { + Nullable!T n = row.peek!(Nullable!T)(idx); + } + if (n.isNull) return Optional!(T).empty(); + static if (isSomeString!T) { + // If the string value is null, return empty as well. + if (n.get() is null) return Optional!(T).empty(); + } + return Optional!(T).of(n.get()); +} + struct QueryBuilder { string fromTable; string[] selections; string[] joins; string[] conditions; + string[] groupings; void delegate(ref Statement, ref int)[] argBinders; this(string fromTable) { @@ -223,6 +247,11 @@ struct QueryBuilder { return this; } + ref groupBy(string grouping) { + groupings ~= grouping; + return this; + } + string build() const { import std.algorithm : map; import std.string : join; @@ -240,6 +269,10 @@ struct QueryBuilder { app ~= "\nWHERE\n"; app ~= conditions.map!(s => " " ~ s).join(" AND\n"); } + if (groupings.length > 0) { + app ~= "\nGROUP BY\n"; + app ~= groupings.map!(s => " " ~ s).join(",\n"); + } return app[]; } diff --git a/finnow-api/source/util/validation/common.d b/finnow-api/source/util/validation/common.d new file mode 100644 index 0000000..e688398 --- /dev/null +++ b/finnow-api/source/util/validation/common.d @@ -0,0 +1,46 @@ +module util.validation.common; + +import handy_http_primitives.optional; +import std.datetime; + +struct ValidationError { + string field; + string message; +} + +interface ValidationRule(T) { + Optional!ValidationError validate(in T payload); +} + +ValidationError[] applyValidationRules(T)(ValidationRule!(T)[] rules, in T payload) { + import std.array; + auto app = appender!(ValidationError[]); + foreach (rule; rules) { + auto result = rule.validate(payload); + if (result) app ~= result.value; + } + return app[]; +} + +// Helper functions: + +void validateTags(in string[] tags) { + import std.regex; + import handy_http_primitives: HttpStatus, HttpStatusException; + foreach (tag; tags) { + import std.regex; + auto r = ctRegex!(`^[a-z0-9-_]{3,32}$`); + if (!matchFirst(tag, r)) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Invalid tag: \"" ~ tag ~ "\"."); + } + } +} + +SysTime validateTimestampFormat(string timestampStr) { + import handy_http_primitives: HttpStatus, HttpStatusException; + try { + return SysTime.fromISOExtString(timestampStr, UTC()); + } catch (TimeException e) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Invalid timestamp format. Expected ISO-8601 datetime."); + } +} diff --git a/finnow-api/source/util/validation/draft.d b/finnow-api/source/util/validation/draft.d new file mode 100644 index 0000000..4cabe79 --- /dev/null +++ b/finnow-api/source/util/validation/draft.d @@ -0,0 +1,59 @@ +module util.validation.draft; + +import handy_http_primitives; +import std.datetime; + +import transaction.data; +import transaction.dto; +import util.validation.common; +import account.data; + +void validateDraftPayload( + TransactionVendorRepository vendorRepo, + TransactionCategoryRepository categoryRepo, + AccountRepository accountRepo, + in TransactionDraftPayload payload +) { + if (payload.amount && !payload.currencyCode) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Currency is required when saving an amount."); + } + if (payload.amount && payload.amount.value == 0) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Amount should be greater than 0."); + } + if (payload.timestamp) { + validateTimestampFormat(payload.timestamp.value); + } + if (payload.vendorId && !vendorRepo.existsById(payload.vendorId.value)) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Vendor doesn't exist."); + } + if (payload.categoryId && !categoryRepo.existsById(payload.categoryId.value)) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Category doesn't exist."); + } + if (payload.creditedAccountId && !accountRepo.existsById(payload.creditedAccountId.value)) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Credited account doesn't exist."); + } + if (payload.debitedAccountId && !accountRepo.existsById(payload.debitedAccountId.value)) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Debited account doesn't exist."); + } + validateTags(payload.tags); + if (payload.lineItems.length > 0) { + long lineItemsTotal = 0; + foreach (lineItem; payload.lineItems) { + if (lineItem.categoryId && !categoryRepo.existsById(lineItem.categoryId.value)) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Line item's category doesn't exist."); + } + if (lineItem.quantity == 0) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Line item's quantity should greater than zero."); + } + for (ulong i = 0; i < lineItem.quantity; i++) { + lineItemsTotal += lineItem.valuePerItem; + } + } + if (payload.amount && lineItemsTotal != payload.amount.value) { + throw new HttpStatusException( + HttpStatus.BAD_REQUEST, + "Total of all line items doesn't equal the transaction's total." + ); + } + } +} \ No newline at end of file diff --git a/finnow-api/source/util/validation/transaction.d b/finnow-api/source/util/validation/transaction.d new file mode 100644 index 0000000..f9fa566 --- /dev/null +++ b/finnow-api/source/util/validation/transaction.d @@ -0,0 +1,78 @@ +module util.validation.transaction; + +import handy_http_primitives; +import std.array; +import std.datetime; + +import util.validation.common; +import transaction.dto; +import transaction.data; +import account.data; + +// class AtLeastOneLinkedAccountRule : ValidationRule!AddTransactionPayload { +// Optional!ValidationError validate(in AddTransactionPayload payload) { +// if (!payload.creditedAccountId && !payload.debitedAccountId) { +// return ValidationError("creditedAccountId", "At least one account must be linked.").toOptional(); +// } +// return Optional!ValidationError.empty(); +// } +// } + +void validateTransactionPayload( + TransactionVendorRepository vendorRepo, + TransactionCategoryRepository categoryRepo, + AccountRepository accountRepo, + in AddTransactionPayload payload +) { + if (!payload.creditedAccountId && !payload.debitedAccountId) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "At least one account must be linked."); + } + if ( + payload.creditedAccountId && + payload.debitedAccountId && + payload.creditedAccountId.value == payload.debitedAccountId.value + ) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Cannot link the same account as both credit and debit."); + } + if (payload.amount == 0) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Amount should be greater than 0."); + } + SysTime now = Clock.currTime(UTC()); + SysTime timestamp = validateTimestampFormat(payload.timestamp); + if (timestamp > now) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Cannot create transaction in the future."); + } + if (payload.vendorId && !vendorRepo.existsById(payload.vendorId.value)) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Vendor doesn't exist."); + } + if (payload.categoryId && !categoryRepo.existsById(payload.categoryId.value)) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Category doesn't exist."); + } + if (payload.creditedAccountId && !accountRepo.existsById(payload.creditedAccountId.value)) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Credited account doesn't exist."); + } + if (payload.debitedAccountId && !accountRepo.existsById(payload.debitedAccountId.value)) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Debited account doesn't exist."); + } + validateTags(payload.tags); + if (payload.lineItems.length > 0) { + long lineItemsTotal = 0; + foreach (lineItem; payload.lineItems) { + if (lineItem.categoryId && !categoryRepo.existsById(lineItem.categoryId.value)) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Line item's category doesn't exist."); + } + if (lineItem.quantity == 0) { + throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Line item's quantity should greater than zero."); + } + for (ulong i = 0; i < lineItem.quantity; i++) { + lineItemsTotal += lineItem.valuePerItem; + } + } + if (lineItemsTotal != payload.amount) { + throw new HttpStatusException( + HttpStatus.BAD_REQUEST, + "Total of all line items doesn't equal the transaction's total." + ); + } + } +} diff --git a/finnow-api/sql/insert_line_item_draft.sql b/finnow-api/sql/insert_line_item_draft.sql new file mode 100644 index 0000000..94a0a8f --- /dev/null +++ b/finnow-api/sql/insert_line_item_draft.sql @@ -0,0 +1,8 @@ +INSERT INTO transaction_draft_line_item ( + draft_id, + idx, + value_per_item, + quantity, + description, + category_id +) VALUES (?, ?, ?, ?, ?, ?) \ No newline at end of file diff --git a/finnow-api/sql/insert_transaction_draft.sql b/finnow-api/sql/insert_transaction_draft.sql new file mode 100644 index 0000000..3061be1 --- /dev/null +++ b/finnow-api/sql/insert_transaction_draft.sql @@ -0,0 +1,13 @@ +INSERT INTO transaction_draft ( + added_at, + template_name, + timestamp, + amount, + currency, + description, + internal_transfer, + vendor_id, + category_id, + credited_account_id, + debited_account_id +) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) \ No newline at end of file diff --git a/finnow-api/sql/migrations/2.sql b/finnow-api/sql/migrations/2.sql new file mode 100644 index 0000000..38b37b5 --- /dev/null +++ b/finnow-api/sql/migrations/2.sql @@ -0,0 +1,74 @@ +CREATE TABLE transaction_draft ( + id INTEGER PRIMARY KEY, + added_at TEXT NOT NULL, + template_name TEXT, + timestamp TEXT, + amount INTEGER, + currency TEXT, + description TEXT, + internal_transfer BOOLEAN DEFAULT FALSE, + vendor_id INTEGER, + category_id INTEGER, + credited_account_id INTEGER, + debited_account_id INTEGER, + CONSTRAINT fk_transaction_draft_vendor + FOREIGN KEY (vendor_id) REFERENCES transaction_vendor(id) + ON UPDATE CASCADE ON DELETE SET NULL, + CONSTRAINT fk_transaction_draft_category + FOREIGN KEY (category_id) REFERENCES transaction_category(id) + ON UPDATE CASCADE ON DELETE SET NULL, + CONSTRAINT fk_transaction_draft_credited_account + FOREIGN KEY (credited_account_id) REFERENCES account(id) + ON UPDATE CASCADE ON DELETE SET NULL, + CONSTRAINT fk_transaction_draft_debited_account + FOREIGN KEY (debited_account_id) REFERENCES account(id) + ON UPDATE CASCADE ON DELETE SET NULL, + CONSTRAINT ck_transaction_amount_positive + CHECK (amount IS NULL OR amount > 0) +); + +CREATE TABLE transaction_draft_tag ( + draft_id INTEGER NOT NULL, + tag TEXT NOT NULL, + CONSTRAINT pk_transaction_draft_tag PRIMARY KEY (draft_id, tag), + CONSTRAINT fk_transaction_draft_tag_draft + FOREIGN KEY (draft_id) REFERENCES transaction_draft(id) + ON UPDATE CASCADE ON DELETE CASCADE +); + +CREATE TABLE transaction_draft_attachment ( + draft_id INTEGER NOT NULL, + attachment_id INTEGER NOT NULL, + PRIMARY KEY (draft_id, attachment_id), + CONSTRAINT fk_transaction_draft_attachment_transaction + FOREIGN KEY (draft_id) REFERENCES transaction_draft(id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT fk_transaction_draft_attachment_attachment + FOREIGN KEY (attachment_id) REFERENCES attachment(id) + ON UPDATE CASCADE ON DELETE CASCADE +); + +CREATE TABLE transaction_draft_line_item ( + draft_id INTEGER NOT NULL, + idx INTEGER NOT NULL DEFAULT 0, + value_per_item INTEGER NOT NULL, + quantity INTEGER NOT NULL DEFAULT 1, + description TEXT NOT NULL, + category_id INTEGER, + CONSTRAINT pk_transaction_draft_line_item PRIMARY KEY (draft_id, idx), + CONSTRAINT fk_transaction_draft_line_item_transaction + FOREIGN KEY (draft_id) REFERENCES transaction_draft(id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT fk_transaction_draft_line_item_category + FOREIGN KEY (category_id) REFERENCES transaction_category(id) + ON UPDATE CASCADE ON DELETE SET NULL +); + +CREATE TABLE recurring_transaction ( + id INTEGER PRIMARY KEY, + draft_id INTEGER NOT NULL, + schedule_expr TEXT NOT NULL, + CONSTRAINT fk_recurring_transaction_draft + FOREIGN KEY (draft_id) REFERENCES transaction_draft(id) + ON UPDATE CASCADE ON DELETE CASCADE +); \ No newline at end of file diff --git a/finnow-api/sql/get_line_items.sql b/finnow-api/sql/query/get_line_items.sql similarity index 94% rename from finnow-api/sql/get_line_items.sql rename to finnow-api/sql/query/get_line_items.sql index 135e8c0..f1ecddf 100644 --- a/finnow-api/sql/get_line_items.sql +++ b/finnow-api/sql/query/get_line_items.sql @@ -3,11 +3,13 @@ i.idx, i.value_per_item, i.quantity, i.description, + i.category_id, category.parent_id, category.name, category.description, category.color + FROM transaction_line_item i LEFT JOIN transaction_category category ON category.id = i.category_id diff --git a/finnow-api/sql/query/get_line_items_draft.sql b/finnow-api/sql/query/get_line_items_draft.sql new file mode 100644 index 0000000..ed6c59b --- /dev/null +++ b/finnow-api/sql/query/get_line_items_draft.sql @@ -0,0 +1,17 @@ +SELECT +i.idx, +i.value_per_item, +i.quantity, +i.description, + +i.category_id, +category.parent_id, +category.name, +category.description, +category.color + +FROM transaction_draft_line_item i +LEFT JOIN transaction_category category + ON category.id = i.category_id +WHERE i.draft_id = ? +ORDER BY idx; \ No newline at end of file diff --git a/finnow-api/sql/get_transaction.sql b/finnow-api/sql/query/get_transaction.sql similarity index 100% rename from finnow-api/sql/get_transaction.sql rename to finnow-api/sql/query/get_transaction.sql diff --git a/finnow-api/sql/query/get_transaction_draft.sql b/finnow-api/sql/query/get_transaction_draft.sql new file mode 100644 index 0000000..e69de29 diff --git a/finnow-api/sql/query/get_transactions.sql b/finnow-api/sql/query/get_transactions.sql new file mode 100644 index 0000000..7b81ed2 --- /dev/null +++ b/finnow-api/sql/query/get_transactions.sql @@ -0,0 +1,38 @@ +SELECT + txn.id, + txn.timestamp, + txn.added_at, + txn.amount, + txn.currency, + txn.description, + txn.internal_transfer, + + txn.vendor_id, + vendor.name, + + txn.category_id, + category.name, + category.color, + + account_credit.id, + account_credit.name, + account_credit.type, + account_credit.number_suffix, + + account_debit.id, + account_debit.name, + account_debit.type, + account_debit.number_suffix, + + GROUP_CONCAT(tags.tag) +FROM "transaction" txn +LEFT JOIN transaction_vendor vendor ON vendor.id = txn.vendor_id +LEFT JOIN transaction_category category ON category.id = txn.category_id +LEFT JOIN account_journal_entry j_credit + ON j_credit.transaction_id = txn.id AND UPPER(j_credit.type) = 'CREDIT' +LEFT JOIN account account_credit ON account_credit.id = j_credit.account_id +LEFT JOIN account_journal_entry j_debit + ON j_debit.transaction_id = txn.id AND UPPER(j_debit.type) = 'DEBIT' +LEFT JOIN account account_debit ON account_debit.id = j_debit.account_id +LEFT JOIN transaction_tag tags ON tags.transaction_id = txn.id +GROUP BY txn.id \ No newline at end of file diff --git a/finnow-api/sql/schema.sql b/finnow-api/sql/schema.sql index dfb24c7..31a3320 100644 --- a/finnow-api/sql/schema.sql +++ b/finnow-api/sql/schema.sql @@ -1,4 +1,9 @@ --- This schema is included at compile-time into source/profile/data_impl_sqlite.d SqliteProfileDataSource +-- This schema is included at compile-time into +-- source/profile/data_impl_sqlite.d SqliteProfileDataSource +-- ---------------------------------------------------------------------------- +-- This is the full current schema for Finnow's database. Tables and statements +-- are defined in the order in which they're executed to build a new database +-- for a newly-created profile. -- Basic/Utility Entities @@ -218,3 +223,82 @@ CREATE TABLE history_item_linked_journal_entry ( FOREIGN KEY (journal_entry_id) REFERENCES account_journal_entry(id) ON UPDATE CASCADE ON DELETE CASCADE ); + +-- Drafts / Templates / Recurring Transactions +-- Generally, draft tables are copies of their normal transaction counterparts, +-- with looser nullability constraints in some cases. + +CREATE TABLE transaction_draft ( + id INTEGER PRIMARY KEY, + added_at TEXT NOT NULL, + template_name TEXT, + timestamp TEXT, + amount INTEGER, + currency TEXT, + description TEXT, + internal_transfer BOOLEAN DEFAULT FALSE, + vendor_id INTEGER, + category_id INTEGER, + credited_account_id INTEGER, + debited_account_id INTEGER, + CONSTRAINT fk_transaction_draft_vendor + FOREIGN KEY (vendor_id) REFERENCES transaction_vendor(id) + ON UPDATE CASCADE ON DELETE SET NULL, + CONSTRAINT fk_transaction_draft_category + FOREIGN KEY (category_id) REFERENCES transaction_category(id) + ON UPDATE CASCADE ON DELETE SET NULL, + CONSTRAINT fk_transaction_draft_credited_account + FOREIGN KEY (credited_account_id) REFERENCES account(id) + ON UPDATE CASCADE ON DELETE SET NULL, + CONSTRAINT fk_transaction_draft_debited_account + FOREIGN KEY (debited_account_id) REFERENCES account(id) + ON UPDATE CASCADE ON DELETE SET NULL, + CONSTRAINT ck_transaction_amount_positive + CHECK (amount IS NULL OR amount > 0) +); + +CREATE TABLE transaction_draft_tag ( + draft_id INTEGER NOT NULL, + tag TEXT NOT NULL, + CONSTRAINT pk_transaction_draft_tag PRIMARY KEY (draft_id, tag), + CONSTRAINT fk_transaction_draft_tag_draft + FOREIGN KEY (draft_id) REFERENCES transaction_draft(id) + ON UPDATE CASCADE ON DELETE CASCADE +); + +CREATE TABLE transaction_draft_attachment ( + draft_id INTEGER NOT NULL, + attachment_id INTEGER NOT NULL, + PRIMARY KEY (draft_id, attachment_id), + CONSTRAINT fk_transaction_draft_attachment_transaction + FOREIGN KEY (draft_id) REFERENCES transaction_draft(id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT fk_transaction_draft_attachment_attachment + FOREIGN KEY (attachment_id) REFERENCES attachment(id) + ON UPDATE CASCADE ON DELETE CASCADE +); + +CREATE TABLE transaction_draft_line_item ( + draft_id INTEGER NOT NULL, + idx INTEGER NOT NULL DEFAULT 0, + value_per_item INTEGER NOT NULL, + quantity INTEGER NOT NULL DEFAULT 1, + description TEXT NOT NULL, + category_id INTEGER, + CONSTRAINT pk_transaction_draft_line_item PRIMARY KEY (draft_id, idx), + CONSTRAINT fk_transaction_draft_line_item_transaction + FOREIGN KEY (draft_id) REFERENCES transaction_draft(id) + ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT fk_transaction_draft_line_item_category + FOREIGN KEY (category_id) REFERENCES transaction_category(id) + ON UPDATE CASCADE ON DELETE SET NULL +); + +CREATE TABLE recurring_transaction ( + id INTEGER PRIMARY KEY, + draft_id INTEGER NOT NULL, + schedule_expr TEXT NOT NULL, + CONSTRAINT fk_recurring_transaction_draft + FOREIGN KEY (draft_id) REFERENCES transaction_draft(id) + ON UPDATE CASCADE ON DELETE CASCADE +); diff --git a/finnow-api/sql/update_transaction_draft.sql b/finnow-api/sql/update_transaction_draft.sql new file mode 100644 index 0000000..61c59d6 --- /dev/null +++ b/finnow-api/sql/update_transaction_draft.sql @@ -0,0 +1,13 @@ +UPDATE transaction_draft +SET + template_name = ?, + timestamp = ?, + amount = ?, + currency = ?, + description = ?, + internal_transfer = ?, + vendor_id = ?, + category_id = ?, + credited_account_id = ?, + debited_account_id = ? +WHERE id = ? \ No newline at end of file diff --git a/web-app/package.json b/web-app/package.json index e221218..a42b96f 100644 --- a/web-app/package.json +++ b/web-app/package.json @@ -25,6 +25,7 @@ "@idle-observer/vue3": "^0.2.0", "chart.js": "^4.5.1", "chartjs-adapter-date-fns": "^3.0.0", + "cron-parser": "^5.6.1", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "pinia": "^3.0.4", diff --git a/web-app/src/api/account.ts b/web-app/src/api/account.ts index fcc57c4..2ba5c7a 100644 --- a/web-app/src/api/account.ts +++ b/web-app/src/api/account.ts @@ -59,6 +59,13 @@ export interface Account { currentBalance: number | null } +export interface SimpleAccountResponse { + id: number + name: string + type: string + numberSuffix: string +} + export interface AccountCreationPayload { type: string numberSuffix: string diff --git a/web-app/src/api/transaction.ts b/web-app/src/api/transaction.ts index ff74b2f..1644cc8 100644 --- a/web-app/src/api/transaction.ts +++ b/web-app/src/api/transaction.ts @@ -1,8 +1,28 @@ +import type { SimpleAccountResponse } from './account' import type { Attachment } from './attachment' import { ApiClient } from './base' import type { Currency } from './data' import { type Page, type PageRequest } from './pagination' +export interface SimpleVendorResponse { + id: number + name: string +} + +export interface SimpleCategoryResponse { + id: number + name: string + color: string +} + +export interface TransactionLineItemResponse { + idx: number + valuePerItem: number + quantity: number + description: string + category: TransactionCategory | null +} + export interface TransactionVendor { id: number name: string @@ -47,10 +67,10 @@ export interface TransactionsListItem { currency: Currency description: string internalTransfer: boolean - vendor: TransactionsListItemVendor | null - category: TransactionsListItemCategory | null - creditedAccount: TransactionsListItemAccount | null - debitedAccount: TransactionsListItemAccount | null + vendor: SimpleVendorResponse | null + category: SimpleCategoryResponse | null + creditedAccount: SimpleAccountResponse | null + debitedAccount: SimpleAccountResponse | null tags: string[] } @@ -82,28 +102,13 @@ export interface TransactionDetail { internalTransfer: boolean vendor: TransactionVendor | null category: TransactionCategory | null - creditedAccount: TransactionDetailAccount | null - debitedAccount: TransactionDetailAccount | null + creditedAccount: SimpleAccountResponse | null + debitedAccount: SimpleAccountResponse | null tags: string[] - lineItems: TransactionDetailLineItem[] + lineItems: TransactionLineItemResponse[] attachments: Attachment[] } -export interface TransactionDetailAccount { - id: number - name: string - type: string - numberSuffix: string -} - -export interface TransactionDetailLineItem { - idx: number - valuePerItem: number - quantity: number - description: string - category: TransactionCategory | null -} - export interface AddTransactionPayload { timestamp: string amount: number @@ -144,6 +149,67 @@ export interface AggregateTransactionData { currencies: AggregateTransactionCurrencyData[] } +export interface TransactionDraftListItem { + id: number + addedAt: string + templateName: string | null + timestamp: string | null + amount: number | null + currency: Currency | null + description: string | null + internalTransfer: boolean | null + vendor: SimpleVendorResponse | null + category: SimpleCategoryResponse | null + creditedAccount: SimpleAccountResponse | null + debitedAccount: SimpleAccountResponse | null + tags: string[] +} + +export interface TransactionDraftResponse { + id: number + addedAt: string + templateName: string | null + timestamp: string | null + amount: number | null + currency: Currency | null + description: string | null + internalTransfer: boolean | null + vendor: SimpleVendorResponse | null + category: SimpleCategoryResponse | null + creditedAccount: SimpleAccountResponse | null + debitedAccount: SimpleAccountResponse | null + tags: string[] + lineItems: TransactionLineItemResponse[] + attachments: Attachment[] +} + +export interface TransactionDraftPayload { + templateName: string | null + timestamp: string | null + amount: number | null + currencyCode: string | null + description: string | null + internalTransfer: boolean | null + vendorId: number | null + categoryId: number | null + creditedAccountId: number | null + debitedAccountId: number | null + tags: string[] + lineItems: AddTransactionPayloadLineItem[] + attachmentIdsToRemove: number[] +} + +export interface RecurringTransactionPayload { + draftId: number + scheduleExpr: string +} + +export interface RecurringTransactionResponse { + id: number + draftId: number + scheduleExpr: string +} + export class TransactionApiClient extends ApiClient { readonly path: string @@ -277,4 +343,77 @@ export class TransactionApiClient extends ApiClient { getAllTags(): Promise { return super.getJson(this.path + '/transaction-tags') } + + // Drafts: + + getDrafts( + paginationOptions: PageRequest | undefined = undefined, + ): Promise> { + return super.getJsonPage(this.path + '/transaction-drafts', paginationOptions) + } + + getTemplateDrafts( + paginationOptions: PageRequest | undefined = undefined, + ): Promise> { + const params = new URLSearchParams() + params.append('template', 'true') + if (paginationOptions !== undefined) { + params.append('page', paginationOptions.page + '') + params.append('size', paginationOptions.size + '') + for (const sort of paginationOptions.sorts) { + params.append('sort', sort.attribute + ',' + sort.dir) + } + } + return super.getJson(this.path + '/transaction-drafts?' + params.toString()) + } + + getDraft(id: number): Promise { + return super.getJson(this.path + '/transaction-drafts/' + id) + } + + addDraft(data: TransactionDraftPayload, files: File[] = []): Promise { + const formData = new FormData() + formData.append('payload', JSON.stringify(data)) + for (const file of files) { + formData.append('file', file) + } + return super.postFormData(this.path + '/transaction-drafts', formData) + } + + updateDraft( + id: number, + data: TransactionDraftPayload, + files: File[] = [], + ): Promise { + const formData = new FormData() + formData.append('payload', JSON.stringify(data)) + for (const file of files) { + formData.append('file', file) + } + return super.putFormData(this.path + '/transaction-drafts/' + id, formData) + } + + deleteDraft(id: number): Promise { + return super.delete(this.path + '/transaction-drafts/' + id) + } + + getRecurringTransactionsForDraft(draftId: number): Promise { + return super.getJson(`${this.path}/transaction-drafts/${draftId}/recurring-transactions`) + } + + getRecurringTransactions( + paginationOptions: PageRequest | undefined = undefined, + ): Promise> { + return super.getJsonPage(this.path + '/recurring-transactions', paginationOptions) + } + + createRecurringTransaction( + data: RecurringTransactionPayload, + ): Promise { + return super.postJson(this.path + '/recurring-transactions', data) + } + + deleteRecurringTransaction(id: number): Promise { + return super.delete(`${this.path}/recurring-transactions/${id}`) + } } diff --git a/web-app/src/components/AddRecurringTransactionModal.vue b/web-app/src/components/AddRecurringTransactionModal.vue new file mode 100644 index 0000000..7a01c3f --- /dev/null +++ b/web-app/src/components/AddRecurringTransactionModal.vue @@ -0,0 +1,91 @@ + + diff --git a/web-app/src/components/LineItemCard.vue b/web-app/src/components/LineItemCard.vue index 423f326..e700ce9 100644 --- a/web-app/src/components/LineItemCard.vue +++ b/web-app/src/components/LineItemCard.vue @@ -1,10 +1,10 @@ + + diff --git a/web-app/src/pages/forms/EditAccountPage.vue b/web-app/src/pages/EditAccountPage.vue similarity index 100% rename from web-app/src/pages/forms/EditAccountPage.vue rename to web-app/src/pages/EditAccountPage.vue diff --git a/web-app/src/pages/TransactionDraftPage.vue b/web-app/src/pages/TransactionDraftPage.vue new file mode 100644 index 0000000..a641c5e --- /dev/null +++ b/web-app/src/pages/TransactionDraftPage.vue @@ -0,0 +1,260 @@ + + diff --git a/web-app/src/pages/UserHomePage.vue b/web-app/src/pages/UserHomePage.vue index 3a2b285..aaebb22 100644 --- a/web-app/src/pages/UserHomePage.vue +++ b/web-app/src/pages/UserHomePage.vue @@ -2,12 +2,14 @@ import ProfileModule from './home/ProfileModule.vue' import AccountsModule from './home/AccountsModule.vue' import TransactionsModule from './home/TransactionsModule.vue' +import DraftsModule from './home/DraftsModule.vue' diff --git a/web-app/src/pages/forms/EditTransactionPage.vue b/web-app/src/pages/forms/EditTransactionPage.vue deleted file mode 100644 index f0bc757..0000000 --- a/web-app/src/pages/forms/EditTransactionPage.vue +++ /dev/null @@ -1,429 +0,0 @@ - - - diff --git a/web-app/src/pages/home/DraftsModule.vue b/web-app/src/pages/home/DraftsModule.vue new file mode 100644 index 0000000..a48aa96 --- /dev/null +++ b/web-app/src/pages/home/DraftsModule.vue @@ -0,0 +1,50 @@ + + diff --git a/web-app/src/pages/transaction-editor/EditTransactionPage.vue b/web-app/src/pages/transaction-editor/EditTransactionPage.vue new file mode 100644 index 0000000..eef9665 --- /dev/null +++ b/web-app/src/pages/transaction-editor/EditTransactionPage.vue @@ -0,0 +1,250 @@ + + + diff --git a/web-app/src/pages/transaction-editor/util.ts b/web-app/src/pages/transaction-editor/util.ts new file mode 100644 index 0000000..3f2d4d6 --- /dev/null +++ b/web-app/src/pages/transaction-editor/util.ts @@ -0,0 +1,493 @@ +import type { Attachment } from '@/api/attachment' +import { floatMoneyToInteger, integerMoneyToFloat, type Currency } from '@/api/data' +import { getSelectedProfile } from '@/api/profile' +import { + TransactionApiClient, + type AddTransactionPayload, + type TransactionDetail, + type TransactionDraftPayload, + type TransactionDraftResponse, + type TransactionLineItemResponse, + type TransactionVendor, +} from '@/api/transaction' +import { getDatetimeLocalValueForNow } from '@/util/time' +import type { RouteLocation, Router } from 'vue-router' + +/** + * The set of all form fields on the transaction editor page. Note that some + * fields may only be used in certain contexts. + */ +export interface TransactionEditorFormFields { + timestamp: string | null + amount: number | null + templateName: string | null // Only for drafts, not transactions. + currency: Currency | null + description: string | null + internalTransfer: boolean | null + vendor: TransactionVendor | null + categoryId: number | null + creditedAccountId: number | null + debitedAccountId: number | null + lineItems: TransactionLineItemResponse[] + tags: string[] + attachmentsToUpload: File[] + removedAttachmentIds: number[] + existingAttachments: Attachment[] +} + +export function defaultEmptyFormFields(): TransactionEditorFormFields { + return { + timestamp: null, + amount: null, + templateName: null, + currency: null, + description: null, + internalTransfer: null, + vendor: null, + categoryId: null, + creditedAccountId: null, + debitedAccountId: null, + lineItems: [], + tags: [], + attachmentsToUpload: [], + removedAttachmentIds: [], + existingAttachments: [], + } +} + +export interface TransactionEditorAction { + name: string + disabled: boolean + callback: ( + formData: TransactionEditorFormFields, + route: RouteLocation, + router: Router, + ) => Promise +} + +/** + * Base class for transaction editor contexts. + */ +export interface TransactionEditorContextBase { + isFormDataValid(formData: TransactionEditorFormFields): boolean + areChangesPresent(formData: TransactionEditorFormFields): boolean + initializeFormFields( + queryParams: Record, + ): TransactionEditorFormFields + getActions(formData: TransactionEditorFormFields): TransactionEditorAction[] +} + +/** + * Editor context that's used when the user starts editing a new transaction, + * not related to any saved transaction or draft. + */ +export class NewTransactionEditorContext implements TransactionEditorContextBase { + isFormDataValid(formData: TransactionEditorFormFields): boolean { + return ( + isFormDataValidForDraftSave(formData) || isFormDataValidForTransactionSubmission(formData) + ) + } + + areChangesPresent(formData: TransactionEditorFormFields): boolean { + return ( + formData.timestamp !== null || + formData.amount !== null || + formData.templateName !== null || + formData.currency !== null || + formData.description !== null || + formData.internalTransfer !== null || + formData.vendor !== null || + formData.creditedAccountId !== null || + formData.debitedAccountId !== null || + formData.lineItems.length > 0 || + formData.tags.length > 0 || + formData.attachmentsToUpload.length > 0 + ) + } + + initializeFormFields( + queryParams: Record, + ): TransactionEditorFormFields { + const fields = defaultEmptyFormFields() + fields.timestamp = getDatetimeLocalValueForNow() + if ('credited-account' in queryParams) { + fields.creditedAccountId = parseInt(queryParams['credited-account'] as string) + } + if ('debited-account' in queryParams) { + fields.debitedAccountId = parseInt(queryParams['debited-account'] as string) + } + return fields + } + + getActions(formData: TransactionEditorFormFields): TransactionEditorAction[] { + return [ + { + name: 'Save', + disabled: !( + this.areChangesPresent(formData) && isFormDataValidForTransactionSubmission(formData) + ), + callback: async (formData, route, router) => { + const api = new TransactionApiClient(getSelectedProfile(route)) + // Assume that form data is valid! + const data = toTransactionPayload(formData) + const txn = await api.addTransaction(data, formData.attachmentsToUpload) + await router.replace(`/profiles/${getSelectedProfile(route)}/transactions/${txn.id}`) + }, + }, + { + name: 'Save Draft', + disabled: !this.areChangesPresent(formData) || !isFormDataValidForDraftSave(formData), + callback: async (formData, route, router) => { + const api = new TransactionApiClient(getSelectedProfile(route)) + const data = toDraftPayload(formData) + const draft = await api.addDraft(data, formData.attachmentsToUpload) + await router.replace( + `/profiles/${getSelectedProfile(route)}/transaction-drafts/${draft.id}`, + ) + }, + }, + { + name: 'Cancel', + disabled: false, + callback: async (_formData, route, router) => { + await goBackOrHome(router, route) + }, + }, + ] + } +} + +/** + * Editor context for when the user is editing a transaction. + */ +export class TransactionEditorContext implements TransactionEditorContextBase { + private existingTransaction: TransactionDetail + + constructor(existingTransaction: TransactionDetail) { + this.existingTransaction = existingTransaction + } + + isFormDataValid(formData: TransactionEditorFormFields): boolean { + return isFormDataValidForTransactionSubmission(formData) + } + + areChangesPresent(formData: TransactionEditorFormFields): boolean { + const tx: TransactionDetail = this.existingTransaction + const tagsChanged = + formData.tags.every((t) => tx.tags.includes(t)) && + tx.tags.every((t) => formData.tags.includes(t)) + const lineItemsChanged = + JSON.stringify(formData.lineItems) !== JSON.stringify(formData.lineItems) + const attachmentsChanged = + formData.attachmentsToUpload.length > 0 || formData.removedAttachmentIds.length > 0 + + const timestampChanged = new Date(formData.timestamp ?? 0).toISOString() !== tx.timestamp + const amountChanged = + floatMoneyToInteger(formData.amount ?? 0, formData.currency ?? tx.currency) !== tx.amount + const currencyChanged = formData.currency?.code !== tx.currency.code + const descriptionChanged = formData.description !== tx.description + const internalTransferChanged = formData.internalTransfer !== tx.internalTransfer + const vendorChanged = formData.vendor?.id !== tx.vendor?.id + const categoryChanged = formData.categoryId !== (tx.category?.id ?? null) + const creditedAccountChanged = formData.creditedAccountId !== (tx.creditedAccount?.id ?? null) + const debitedAccountChanged = formData.debitedAccountId !== (tx.debitedAccount?.id ?? null) + + return ( + tagsChanged || + lineItemsChanged || + attachmentsChanged || + timestampChanged || + amountChanged || + currencyChanged || + descriptionChanged || + internalTransferChanged || + vendorChanged || + categoryChanged || + creditedAccountChanged || + debitedAccountChanged + ) + } + + initializeFormFields(): TransactionEditorFormFields { + const tx = this.existingTransaction + return { + timestamp: getLocalDateTimeStringFromUTCTimestamp(tx.timestamp), + amount: integerMoneyToFloat(tx.amount, tx.currency), + templateName: null, + currency: tx.currency, + description: tx.description, + internalTransfer: tx.internalTransfer, + vendor: tx.vendor ?? null, + categoryId: tx.category?.id ?? null, + creditedAccountId: tx.creditedAccount?.id ?? null, + debitedAccountId: tx.debitedAccount?.id ?? null, + lineItems: [...tx.lineItems], + tags: [...tx.tags], + attachmentsToUpload: [], + removedAttachmentIds: [], + existingAttachments: [...tx.attachments], + } + } + + getActions(formData: TransactionEditorFormFields): TransactionEditorAction[] { + return [ + { + name: 'Save', + disabled: !this.areChangesPresent(formData) || !this.isFormDataValid(formData), + callback: async (formData, route, router) => { + const api = new TransactionApiClient(getSelectedProfile(route)) + // Assume that form data is valid! + const data = toTransactionPayload(formData) + const txn = await api.updateTransaction( + this.existingTransaction.id, + data, + formData.attachmentsToUpload, + ) + await router.replace(`/profiles/${getSelectedProfile(route)}/transactions/${txn.id}`) + }, + }, + { + name: 'Cancel', + disabled: false, + callback: async (_formData, route, router) => { + await goBackOrHome(router, route) + }, + }, + ] + } +} + +/** + * Editor context for when the user is editing an existing draft. + */ +export class DraftEditorContext implements TransactionEditorContextBase { + private existingDraft: TransactionDraftResponse + + constructor(existingDraft: TransactionDraftResponse) { + this.existingDraft = existingDraft + } + + isFormDataValid(formData: TransactionEditorFormFields): boolean { + const result = + (formData.amount === null || formData.currency !== null) && + (formData.amount === null || formData.amount > 0) && + (formData.creditedAccountId === null || + formData.debitedAccountId === null || + formData.creditedAccountId !== formData.debitedAccountId) && + (formData.templateName === null || formData.templateName.length <= 32) + console.log('Checking draft editor valid:', formData, result) + return result + } + + areChangesPresent(): boolean { + return true + } + + initializeFormFields(): TransactionEditorFormFields { + const d = this.existingDraft + const fields = defaultEmptyFormFields() + if (d.templateName !== null && d.templateName.length > 0) { + fields.templateName = d.templateName + } + if (d.timestamp !== null) { + fields.timestamp = getLocalDateTimeStringFromUTCTimestamp(d.timestamp) + } + if (d.amount !== null && d.currency !== null) { + fields.currency = d.currency + fields.amount = integerMoneyToFloat(d.amount, d.currency) + } + fields.description = d.description + fields.internalTransfer = d.internalTransfer + if (d.vendor !== null) { + fields.vendor = { + id: d.vendor.id, + name: d.vendor.name, + description: '', + } + // TODO: Update TransactionDraftResponse format to include full vendor data. + } + if (d.category !== null) { + fields.categoryId = d.category.id + } + if (d.creditedAccount !== null) { + fields.creditedAccountId = d.creditedAccount.id + } + if (d.debitedAccount !== null) { + fields.debitedAccountId = d.debitedAccount.id + } + fields.lineItems = [...d.lineItems] + fields.tags = [...d.tags] + fields.existingAttachments = [...d.attachments] + return fields + } + + getActions(formData: TransactionEditorFormFields): TransactionEditorAction[] { + return [ + { + name: 'Save Draft', + disabled: !this.areChangesPresent() || !this.isFormDataValid(formData), + callback: async (formData, route, router) => { + const api = new TransactionApiClient(getSelectedProfile(route)) + const data = toDraftPayload(formData) + const draft = await api.updateDraft( + this.existingDraft.id, + data, + formData.attachmentsToUpload, + ) + await router.replace( + `/profiles/${getSelectedProfile(route)}/transaction-drafts/${draft.id}`, + ) + }, + }, + { + name: 'Submit Transaction', + disabled: !this.areChangesPresent() || !isFormDataValidForTransactionSubmission(formData), + callback: async (formData, route, router) => { + const api = new TransactionApiClient(getSelectedProfile(route)) + // First call the normal "Save" callback from the NewTransactionEditorContext. + const tec = new NewTransactionEditorContext() + const saveTxnAction = tec.getActions(formData).find((a) => a.name === 'Save')! + await saveTxnAction.callback(formData, route, router) + // Then if this is not a template draft, delete it. + if (formData.templateName === null || formData.templateName.length === 0) { + await api.deleteDraft(this.existingDraft.id) + } + }, + }, + { + name: 'Cancel', + disabled: false, + callback: async (_formData, route, router) => { + await goBackOrHome(router, route) + }, + }, + ] + } +} + +// Helper functions below here! + +/** + * Obtains an editor context by determining what the user is doing based on the + * route they've navigated to. + * @param route The current route (which tells us what intent the user has). + * @returns A promise that resolves to an editor context. + */ +export async function loadEditorContextFromRoute( + route: RouteLocation, +): Promise { + const transactionApi = new TransactionApiClient(getSelectedProfile(route)) + if (route.name === 'edit-transaction') { + const transactionIdStr = route.params.id + if (transactionIdStr && typeof transactionIdStr === 'string') { + const transactionId = parseInt(transactionIdStr) + const existingTransaction = await transactionApi.getTransaction(transactionId) + return new TransactionEditorContext(existingTransaction) + } + } else if (route.name === 'edit-draft') { + const draftIdStr = route.params.id + if (draftIdStr && typeof draftIdStr === 'string') { + const draftId = parseInt(draftIdStr) + const existingDraft = await transactionApi.getDraft(draftId) + return new DraftEditorContext(existingDraft) + } + } + + return new NewTransactionEditorContext() +} + +function getLocalDateTimeStringFromUTCTimestamp(timestamp: string) { + const date = new Date(timestamp) + date.setMilliseconds(0) + const timezoneOffset = new Date().getTimezoneOffset() * 60_000 + return new Date(date.getTime() - timezoneOffset).toISOString().slice(0, -1) +} + +function toDraftPayload(formData: TransactionEditorFormFields): TransactionDraftPayload { + if (typeof formData.amount === 'string') { + formData.amount = null + } + if (formData.amount !== null && formData.currency !== null) { + formData.amount = floatMoneyToInteger(formData.amount, formData.currency) + } else { + formData.amount = null + formData.currency = null + } + let isoTimestamp = null + if (formData.timestamp !== null && formData.timestamp.length > 0) { + isoTimestamp = new Date(formData.timestamp).toISOString() + } + return { + templateName: formData.templateName, + timestamp: isoTimestamp, + amount: formData.amount, + currencyCode: formData.currency?.code ?? null, + description: formData.description, + internalTransfer: formData.internalTransfer, + vendorId: formData.vendor?.id ?? null, + categoryId: formData.categoryId, + creditedAccountId: formData.creditedAccountId, + debitedAccountId: formData.debitedAccountId, + tags: [...formData.tags], + lineItems: [...formData.lineItems].map((li) => { + return { + valuePerItem: li.valuePerItem, + quantity: li.quantity, + description: li.description, + categoryId: li.category?.id ?? null, + } + }), + attachmentIdsToRemove: [...formData.removedAttachmentIds], + } +} + +function toTransactionPayload(formData: TransactionEditorFormFields): AddTransactionPayload { + const payload: AddTransactionPayload = { + timestamp: new Date(formData.timestamp!).toISOString(), + amount: floatMoneyToInteger(formData.amount!, formData.currency!), + currencyCode: formData.currency!.code, + description: formData.description ?? '', + internalTransfer: formData.internalTransfer ?? false, + vendorId: formData.vendor?.id ?? null, + categoryId: formData.categoryId, + creditedAccountId: formData.creditedAccountId, + debitedAccountId: formData.debitedAccountId, + tags: formData.tags, + lineItems: formData.lineItems.map((li) => { + return { ...li, categoryId: li.category?.id ?? null } + }), + attachmentIdsToRemove: formData.removedAttachmentIds, + } + return payload +} + +async function goBackOrHome(router: Router, route: RouteLocation) { + if (window.history.length > 0) { + await router.back() + } else { + await router.replace(`/profiles/${getSelectedProfile(route)}`) + } +} + +function isFormDataValidForTransactionSubmission(formData: TransactionEditorFormFields): boolean { + return ( + formData.timestamp !== null && + formData.timestamp.length > 0 && + formData.amount !== null && + formData.amount > 0 && + formData.currency !== null && + formData.description !== null && + formData.description.length > 0 && + (formData.creditedAccountId !== null || formData.debitedAccountId !== null) && + formData.creditedAccountId !== formData.debitedAccountId + ) +} + +function isFormDataValidForDraftSave(formData: TransactionEditorFormFields): boolean { + return ( + formData.amount !== null && + formData.amount > 0 && + formData.timestamp !== null && + formData.currency !== null + ) +} diff --git a/web-app/src/router/index.ts b/web-app/src/router/index.ts index 2e665a9..286150a 100644 --- a/web-app/src/router/index.ts +++ b/web-app/src/router/index.ts @@ -44,7 +44,7 @@ const router = createRouter({ }, { path: 'accounts/:id/edit', - component: () => import('@/pages/forms/EditAccountPage.vue'), + component: () => import('@/pages/EditAccountPage.vue'), meta: { title: 'Edit Account' }, }, { @@ -54,7 +54,7 @@ const router = createRouter({ }, { path: 'add-account', - component: () => import('@/pages/forms/EditAccountPage.vue'), + component: () => import('@/pages/EditAccountPage.vue'), meta: { title: 'Add Account' }, }, { @@ -63,20 +63,32 @@ const router = createRouter({ meta: { title: 'Transaction' }, }, { + path: 'transaction-drafts/:id', + component: () => import('@/pages/TransactionDraftPage.vue'), + meta: { title: 'Draft' }, + }, + { + name: 'edit-transaction', path: 'transactions/:id/edit', - component: () => import('@/pages/forms/EditTransactionPage.vue'), + component: () => import('@/pages/transaction-editor/EditTransactionPage.vue'), meta: { title: 'Edit Transaction' }, }, + { + path: 'add-transaction', + component: () => import('@/pages/transaction-editor/EditTransactionPage.vue'), + meta: { title: 'Add Transaction' }, + }, + { + name: 'edit-draft', + path: 'transaction-drafts/:id/edit', + component: () => import('@/pages/transaction-editor/EditTransactionPage.vue'), + meta: { title: 'Edit Draft' }, + }, { path: 'transactions/search', component: () => import('@/pages/TransactionSearchPage.vue'), meta: { title: 'Search Transactions' }, }, - { - path: 'add-transaction', - component: () => import('@/pages/forms/EditTransactionPage.vue'), - meta: { title: 'Add Transaction' }, - }, { path: 'vendors', component: () => import('@/pages/VendorsPage.vue'), diff --git a/web-app/yarn.lock b/web-app/yarn.lock index 37e2502..13a5715 100644 --- a/web-app/yarn.lock +++ b/web-app/yarn.lock @@ -4,7 +4,7 @@ "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz" integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== dependencies: "@babel/helper-validator-identifier" "^7.28.5" @@ -13,12 +13,12 @@ "@babel/compat-data@^7.28.6": version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz" integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== "@babel/core@^7.23.0": version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz" integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== dependencies: "@babel/code-frame" "^7.29.0" @@ -39,7 +39,7 @@ "@babel/generator@^7.28.6", "@babel/generator@^7.29.0": version "7.29.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz" integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== dependencies: "@babel/parser" "^7.29.0" @@ -50,14 +50,14 @@ "@babel/helper-annotate-as-pure@^7.27.3": version "7.27.3" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz" integrity sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg== dependencies: "@babel/types" "^7.27.3" "@babel/helper-compilation-targets@^7.28.6": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz" integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== dependencies: "@babel/compat-data" "^7.28.6" @@ -68,7 +68,7 @@ "@babel/helper-create-class-features-plugin@^7.28.6": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz#611ff5482da9ef0db6291bcd24303400bca170fb" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz" integrity sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" @@ -81,12 +81,12 @@ "@babel/helper-globals@^7.28.0": version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" + resolved "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz" integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== "@babel/helper-member-expression-to-functions@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz#f3e07a10be37ed7a63461c63e6929575945a6150" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz" integrity sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg== dependencies: "@babel/traverse" "^7.28.5" @@ -94,7 +94,7 @@ "@babel/helper-module-imports@^7.27.1", "@babel/helper-module-imports@^7.28.6": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz" integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== dependencies: "@babel/traverse" "^7.28.6" @@ -102,7 +102,7 @@ "@babel/helper-module-transforms@^7.28.6": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz" integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== dependencies: "@babel/helper-module-imports" "^7.28.6" @@ -111,19 +111,19 @@ "@babel/helper-optimise-call-expression@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz" integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw== dependencies: "@babel/types" "^7.27.1" "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.28.6": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz" integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug== "@babel/helper-replace-supers@^7.28.6": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz#94aa9a1d7423a00aead3f204f78834ce7d53fe44" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz" integrity sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg== dependencies: "@babel/helper-member-expression-to-functions" "^7.28.5" @@ -132,7 +132,7 @@ "@babel/helper-skip-transparent-expression-wrappers@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz" integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg== dependencies: "@babel/traverse" "^7.27.1" @@ -140,22 +140,22 @@ "@babel/helper-string-parser@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== "@babel/helper-validator-identifier@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== "@babel/helper-validator-option@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz" integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== "@babel/helpers@^7.28.6": version "7.29.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.29.2.tgz#9cfbccb02b8e229892c0b07038052cc1a8709c49" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz" integrity sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw== dependencies: "@babel/template" "^7.28.6" @@ -163,14 +163,14 @@ "@babel/parser@^7.28.0", "@babel/parser@^7.28.4", "@babel/parser@^7.28.5", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0", "@babel/parser@^7.29.2": version "7.29.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.2.tgz#58bd50b9a7951d134988a1ae177a35ef9a703ba1" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz" integrity sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA== dependencies: "@babel/types" "^7.29.0" "@babel/plugin-proposal-decorators@^7.23.0": version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.0.tgz#d159f26f78740e47bf3ef075882b155b2d54ca81" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.0.tgz" integrity sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA== dependencies: "@babel/helper-create-class-features-plugin" "^7.28.6" @@ -179,42 +179,42 @@ "@babel/plugin-syntax-decorators@^7.28.6": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz#8c3293a0fef033e4c786b35ce1e159fc1d676153" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz" integrity sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA== dependencies: "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-import-attributes@^7.22.5": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz#b71d5914665f60124e133696f17cd7669062c503" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz" integrity sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw== dependencies: "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx@^7.27.1": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz#f8ca28bbd84883b5fea0e447c635b81ba73997ee" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz" integrity sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w== dependencies: "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-typescript@^7.28.6": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz#c7b2ddf1d0a811145b1de800d1abd146af92e3a2" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz" integrity sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A== dependencies: "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-typescript@^7.22.15": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz#1e93d96da8adbefdfdade1d4956f73afa201a158" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz" integrity sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" @@ -225,7 +225,7 @@ "@babel/template@^7.27.2", "@babel/template@^7.28.6": version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz" integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== dependencies: "@babel/code-frame" "^7.28.6" @@ -234,7 +234,7 @@ "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.5", "@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0": version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz" integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== dependencies: "@babel/code-frame" "^7.29.0" @@ -247,49 +247,27 @@ "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.5", "@babel/types@^7.28.6", "@babel/types@^7.29.0": version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz" integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== dependencies: "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.28.5" -"@emnapi/core@^1.7.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.9.1.tgz#2143069c744ca2442074f8078462e51edd63c7bd" - integrity sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA== - dependencies: - "@emnapi/wasi-threads" "1.2.0" - tslib "^2.4.0" - -"@emnapi/runtime@^1.7.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.9.1.tgz#115ff2a0d589865be6bd8e9d701e499c473f2a8d" - integrity sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA== - dependencies: - tslib "^2.4.0" - -"@emnapi/wasi-threads@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz#a19d9772cc3d195370bf6e2a805eec40aa75e18e" - integrity sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg== - dependencies: - tslib "^2.4.0" - "@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": version "4.9.1" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz" integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== dependencies: eslint-visitor-keys "^3.4.3" "@eslint-community/regexpp@^4.12.2": version "4.12.2" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz" integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== "@eslint/config-array@^0.23.3": version "0.23.3" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.23.3.tgz#3f4a93dd546169c09130cbd10f2415b13a20a219" + resolved "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.3.tgz" integrity sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw== dependencies: "@eslint/object-schema" "^3.0.3" @@ -298,26 +276,26 @@ "@eslint/config-helpers@^0.5.3": version "0.5.3" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.5.3.tgz#721fe6bbb90d74b0c80d6ff2428e5bbcb002becb" + resolved "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.3.tgz" integrity sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw== dependencies: "@eslint/core" "^1.1.1" "@eslint/core@^1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-1.1.1.tgz#450f3d2be2d463ccd51119544092256b4e88df32" + resolved "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz" integrity sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ== dependencies: "@types/json-schema" "^7.0.15" "@eslint/object-schema@^3.0.3": version "3.0.3" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.3.tgz#5bf671e52e382e4adc47a9906f2699374637db6b" + resolved "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.3.tgz" integrity sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ== "@eslint/plugin-kit@^0.6.1": version "0.6.1" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz#eb9e6689b56ce8bc1855bb33090e63f3fc115e8e" + resolved "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz" integrity sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ== dependencies: "@eslint/core" "^1.1.1" @@ -325,14 +303,14 @@ "@floating-ui/core@^1.7.5": version "1.7.5" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.7.5.tgz#d4af157a03330af5a60e69da7a4692507ada0622" + resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz" integrity sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ== dependencies: "@floating-ui/utils" "^0.2.11" "@floating-ui/dom@^1.7.5": version "1.7.6" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.6.tgz#f915bba5abbb177e1f227cacee1b4d0634b187bf" + resolved "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz" integrity sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ== dependencies: "@floating-ui/core" "^1.7.5" @@ -340,12 +318,12 @@ "@floating-ui/utils@^0.2.10", "@floating-ui/utils@^0.2.11": version "0.2.11" - resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.11.tgz#a269e055e40e2f45873bae9d1a2fdccbd314ea3f" + resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz" integrity sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg== "@floating-ui/vue@1.1.10": version "1.1.10" - resolved "https://registry.yarnpkg.com/@floating-ui/vue/-/vue-1.1.10.tgz#cae3ff9a1410219fc3da6dd6475cc92dd5281488" + resolved "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.1.10.tgz" integrity sha512-vdf8f6rHnFPPLRsmL4p12wYl+Ux4mOJOkjzKEMYVnwdf7UFdvBtHlLvQyx8iKG5vhPRbDRgZxdtpmyigDPjzYg== dependencies: "@floating-ui/dom" "^1.7.5" @@ -354,43 +332,43 @@ "@fortawesome/fontawesome-common-types@7.2.0": version "7.2.0" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.2.0.tgz#ae54831ab5974bc1a64e8b07410f2da2b4abf87f" + resolved "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.2.0.tgz" integrity sha512-IpR0bER9FY25p+e7BmFH25MZKEwFHTfRAfhOyJubgiDnoJNsSvJ7nigLraHtp4VOG/cy8D7uiV0dLkHOne5Fhw== "@fortawesome/fontawesome-svg-core@^7.2.0": version "7.2.0" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.2.0.tgz#813550a5e8946a798e170d3f7331a685f8f8a550" + resolved "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.2.0.tgz" integrity sha512-6639htZMjEkwskf3J+e6/iar+4cTNM9qhoWuRfj9F3eJD6r7iCzV1SWnQr2Mdv0QT0suuqU8BoJCZUyCtP9R4Q== dependencies: "@fortawesome/fontawesome-common-types" "7.2.0" "@fortawesome/free-regular-svg-icons@^7.2.0": version "7.2.0" - resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-7.2.0.tgz#9f2f0af03b851e8b261514dd495cf30653de2fbd" + resolved "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-7.2.0.tgz" integrity sha512-iycmlN51EULlQ4D/UU9WZnHiN0CvjJ2TuuCrAh+1MVdzD+4ViKYH2deNAll4XAAYlZa8WAefHR5taSK8hYmSMw== dependencies: "@fortawesome/fontawesome-common-types" "7.2.0" "@fortawesome/free-solid-svg-icons@^7.2.0": version "7.2.0" - resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.2.0.tgz#9c761f7ab393e281f750c54d9ed1ebbcd4d581fa" + resolved "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.2.0.tgz" integrity sha512-YTVITFGN0/24PxzXrwqCgnyd7njDuzp5ZvaCx5nq/jg55kUYd94Nj8UTchBdBofi/L0nwRfjGOg0E41d2u9T1w== dependencies: "@fortawesome/fontawesome-common-types" "7.2.0" "@fortawesome/vue-fontawesome@^3.1.3": version "3.1.3" - resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.1.3.tgz#3184bc26cd5cf20904e7e937e9c68860039c6dab" + resolved "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.1.3.tgz" integrity sha512-OHHUTLPEzdwP8kcYIzhioUdUOjZ4zzmi+midwa4bqscza4OJCOvTKJEHkXNz8PgZ23kWci1HkKVX0bm8f9t9gQ== "@humanfs/core@^0.19.1": version "0.19.1" - resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + resolved "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz" integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== "@humanfs/node@^0.16.6": version "0.16.7" - resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.7.tgz#822cb7b3a12c5a240a24f621b5a2413e27a45f26" + resolved "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz" integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== dependencies: "@humanfs/core" "^0.19.1" @@ -398,29 +376,29 @@ "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": version "0.4.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" + resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz" integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== "@idle-observer/core@0.2.0": version "0.2.0" - resolved "https://registry.yarnpkg.com/@idle-observer/core/-/core-0.2.0.tgz#4f022cf9347dacb15d8c0e1d082d7e1dc2009e6f" + resolved "https://registry.npmjs.org/@idle-observer/core/-/core-0.2.0.tgz" integrity sha512-mr8dedtzGUGMo38oP4+gDGq/oGjY0f9aCddsCcOm5XZTDE5ALSL3zyvXT8+eOjMrwOVLkDXt4BoC7A9U6ImANw== "@idle-observer/vue3@^0.2.0": version "0.2.0" - resolved "https://registry.yarnpkg.com/@idle-observer/vue3/-/vue3-0.2.0.tgz#1f60cd3837304ce4a7baad5598b4757de1f602a2" + resolved "https://registry.npmjs.org/@idle-observer/vue3/-/vue3-0.2.0.tgz" integrity sha512-amI/uRRcHIdOI5x7wLGxGK3ewaBcljsIwoXQ16sDCnnHqAg8zwa9H1PMb3QzaRqH5b5Ck2MX0YOY6+pd2EEohQ== dependencies: "@idle-observer/core" "0.2.0" "@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz" integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" @@ -428,7 +406,7 @@ "@jridgewell/remapping@^2.3.5": version "2.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + resolved "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz" integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== dependencies: "@jridgewell/gen-mapping" "^0.3.5" @@ -436,17 +414,17 @@ "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5": version "1.5.5" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": version "0.3.31" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" @@ -454,21 +432,19 @@ "@kurkle/color@^0.3.0": version "0.3.4" - resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.4.tgz#4d4ff677e1609214fc71c580125ddddd86abcabf" + resolved "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz" integrity sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w== "@napi-rs/wasm-runtime@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz#c3705ab549d176b8dc5172723d6156c3dc426af2" - integrity sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A== + version "1.1.6" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz#ed33806d0f9be98dc76d0c3d4fd872fda701b5d5" + integrity sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg== dependencies: - "@emnapi/core" "^1.7.1" - "@emnapi/runtime" "^1.7.1" - "@tybys/wasm-util" "^0.10.1" + "@tybys/wasm-util" "^0.10.3" "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -476,12 +452,12 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -489,17 +465,17 @@ "@oxc-project/types@=0.122.0": version "0.122.0" - resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.122.0.tgz#2f4e77a3b183c87b2a326affd703ef71ba836601" + resolved "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz" integrity sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA== "@pkgr/core@^0.2.9": version "0.2.9" - resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" + resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz" integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== "@polka/url@^1.0.0-next.24": version "1.0.0-next.29" - resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.29.tgz#5a40109a1ab5f84d6fd8fc928b19f367cbe7e7b1" + resolved "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz" integrity sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww== "@rolldown/binding-android-arm64@1.0.0-rc.12": @@ -549,12 +525,12 @@ "@rolldown/binding-linux-x64-gnu@1.0.0-rc.12": version "1.0.0-rc.12" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz#f7d71d97f6bd43198596b26dc2cb364586e12673" + resolved "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz" integrity sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg== "@rolldown/binding-linux-x64-musl@1.0.0-rc.12": version "1.0.0-rc.12" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz#a2ca737f01b0ad620c4c404ca176ea3e3ad804c3" + resolved "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz" integrity sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig== "@rolldown/binding-openharmony-arm64@1.0.0-rc.12": @@ -581,56 +557,56 @@ "@rolldown/pluginutils@1.0.0-rc.12": version "1.0.0-rc.12" - resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz#74163aec62fa51cee18d62709483963dceb3f6dc" + resolved "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz" integrity sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw== "@rolldown/pluginutils@1.0.0-rc.2": version "1.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz#10324e74cb3396cb7b616042ea7e9e6aa7d8d458" + resolved "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz" integrity sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw== "@rollup/rollup-linux-x64-gnu@4.57.1": version "4.57.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz#78c16eef9520bd10e1ea7a112593bb58e2842622" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz" integrity sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg== "@tsconfig/node22@^22.0.5": version "22.0.5" - resolved "https://registry.yarnpkg.com/@tsconfig/node22/-/node22-22.0.5.tgz#56be9f34ccffd3aa85f9cc7365ad9765d5829adb" + resolved "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.5.tgz" integrity sha512-hLf2ld+sYN/BtOJjHUWOk568dvjFQkHnLNa6zce25GIH+vxKfvTgm3qpaH6ToF5tu/NN0IH66s+Bb5wElHrLcw== -"@tybys/wasm-util@^0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414" - integrity sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg== +"@tybys/wasm-util@^0.10.3": + version "0.10.3" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.3.tgz#015cba9e9dd47ce14d03d2a8c5d547bfb169665d" + integrity sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg== dependencies: tslib "^2.4.0" "@types/esrecurse@^4.3.1": version "4.3.1" - resolved "https://registry.yarnpkg.com/@types/esrecurse/-/esrecurse-4.3.1.tgz#6f636af962fbe6191b830bd676ba5986926bccec" + resolved "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz" integrity sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw== "@types/estree@^1.0.6", "@types/estree@^1.0.8": version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== "@types/json-schema@^7.0.15": version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/node@^22.19.15": version "22.19.15" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.19.15.tgz#6091d99fdf7c08cb57dc8b1345d407ba9a1df576" + resolved "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz" integrity sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg== dependencies: undici-types "~6.21.0" "@typescript-eslint/eslint-plugin@8.57.2": version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz#ad0dcefeca9c2ecbe09f730d478063666aee010b" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz" integrity sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w== dependencies: "@eslint-community/regexpp" "^4.12.2" @@ -644,7 +620,7 @@ "@typescript-eslint/parser@8.57.2": version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.57.2.tgz#b819955e39f976c0d4f95b5ed67fe22f85cd6898" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz" integrity sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA== dependencies: "@typescript-eslint/scope-manager" "8.57.2" @@ -655,7 +631,7 @@ "@typescript-eslint/project-service@8.57.2": version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.57.2.tgz#dfbc7777f9f633f2b06b558cda3836e76f856e3c" + resolved "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz" integrity sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw== dependencies: "@typescript-eslint/tsconfig-utils" "^8.57.2" @@ -664,7 +640,7 @@ "@typescript-eslint/scope-manager@8.57.2": version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz#734dcde40677f430b5d963108337295bdbc09dae" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz" integrity sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw== dependencies: "@typescript-eslint/types" "8.57.2" @@ -672,12 +648,12 @@ "@typescript-eslint/tsconfig-utils@8.57.2", "@typescript-eslint/tsconfig-utils@^8.57.2": version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz#cf82dc11e884103ec13188a7352591efaa1a887e" + resolved "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz" integrity sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw== "@typescript-eslint/type-utils@8.57.2": version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz#3ec65a94e73776252991a3cf0a15d220734c28f5" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz" integrity sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg== dependencies: "@typescript-eslint/types" "8.57.2" @@ -688,12 +664,12 @@ "@typescript-eslint/types@8.57.2", "@typescript-eslint/types@^8.57.2": version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.57.2.tgz#efe0da4c28b505ed458f113aa960dce2c5c671f4" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz" integrity sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA== "@typescript-eslint/typescript-estree@8.57.2": version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz#432e61a6cf2ab565837da387e5262c159672abea" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz" integrity sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA== dependencies: "@typescript-eslint/project-service" "8.57.2" @@ -708,7 +684,7 @@ "@typescript-eslint/utils@8.57.2", "@typescript-eslint/utils@^8.56.0": version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.57.2.tgz#46a8974c24326fb8899486728428a0f1a3115014" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz" integrity sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg== dependencies: "@eslint-community/eslint-utils" "^4.9.1" @@ -718,7 +694,7 @@ "@typescript-eslint/visitor-keys@8.57.2": version "8.57.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz#a5c9605774247336c0412beb7dc288ab2a07c11e" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz" integrity sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw== dependencies: "@typescript-eslint/types" "8.57.2" @@ -726,26 +702,26 @@ "@vitejs/plugin-vue@^6.0.5": version "6.0.5" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz#20ebb46c4da069753d9cfb1309c4334213cc3f7b" + resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz" integrity sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg== dependencies: "@rolldown/pluginutils" "1.0.0-rc.2" "@volar/language-core@2.4.28": version "2.4.28" - resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-2.4.28.tgz#c21f365a91c1dffe8bd7264fd491770c8d74fef3" + resolved "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz" integrity sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ== dependencies: "@volar/source-map" "2.4.28" "@volar/source-map@2.4.28": version "2.4.28" - resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-2.4.28.tgz#b40254e8c96199e5f1e0796777c593c617ad270e" + resolved "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz" integrity sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ== "@volar/typescript@2.4.28": version "2.4.28" - resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-2.4.28.tgz#83f86356e84eb101b8081a44c104f2f2ced8411f" + resolved "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz" integrity sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw== dependencies: "@volar/language-core" "2.4.28" @@ -754,7 +730,7 @@ "@vue-macros/common@^3.1.1": version "3.1.2" - resolved "https://registry.yarnpkg.com/@vue-macros/common/-/common-3.1.2.tgz#6b5f71ea219732fc955fdcfb15a95a417b87a0fe" + resolved "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.2.tgz" integrity sha512-h9t4ArDdniO9ekYHAD95t9AZcAbb19lEGK+26iAjUODOIJKmObDNBSe4+6ELQAA3vtYiFPPBtHh7+cQCKi3Dng== dependencies: "@vue/compiler-sfc" "^3.5.22" @@ -765,12 +741,12 @@ "@vue/babel-helper-vue-transform-on@1.5.0": version "1.5.0" - resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz#b7e99d37eeb144d7b9757d7a1f40cd977fde748a" + resolved "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz" integrity sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA== "@vue/babel-plugin-jsx@^1.1.5": version "1.5.0" - resolved "https://registry.yarnpkg.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz#1b988b497cb1f79725da94463e75cebe60b72e70" + resolved "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz" integrity sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw== dependencies: "@babel/helper-module-imports" "^7.27.1" @@ -785,7 +761,7 @@ "@vue/babel-plugin-resolve-type@1.5.0": version "1.5.0" - resolved "https://registry.yarnpkg.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz#6881d7b1478e9fc0ea4bb08aaad1f4d206655568" + resolved "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz" integrity sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w== dependencies: "@babel/code-frame" "^7.27.1" @@ -796,7 +772,7 @@ "@vue/compiler-core@3.5.31": version "3.5.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.31.tgz#db20d99eb3e8e9ce3c008b8cc79bdb7dab3dfe61" + resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.31.tgz" integrity sha512-k/ueL14aNIEy5Onf0OVzR8kiqF/WThgLdFhxwa4e/KF/0qe38IwIdofoSWBTvvxQOesaz6riAFAUaYjoF9fLLQ== dependencies: "@babel/parser" "^7.29.2" @@ -807,7 +783,7 @@ "@vue/compiler-dom@3.5.31", "@vue/compiler-dom@^3.3.4", "@vue/compiler-dom@^3.5.0": version "3.5.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.31.tgz#7d4b5688a8daef1513ee18f566ea129bded36ff7" + resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.31.tgz" integrity sha512-BMY/ozS/xxjYqRFL+tKdRpATJYDTTgWSo0+AJvJNg4ig+Hgb0dOsHPXvloHQ5hmlivUqw1Yt2pPIqp4e0v1GUw== dependencies: "@vue/compiler-core" "3.5.31" @@ -815,7 +791,7 @@ "@vue/compiler-sfc@3.5.31", "@vue/compiler-sfc@^3.5.18", "@vue/compiler-sfc@^3.5.22": version "3.5.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.31.tgz#ab3670bc81f0bf60ccd0766b16042ad5b334d821" + resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.31.tgz" integrity sha512-M8wpPgR9UJ8MiRGjppvx9uWJfLV7A/T+/rL8s/y3QG3u0c2/YZgff3d6SuimKRIhcYnWg5fTfDMlz2E6seUW8Q== dependencies: "@babel/parser" "^7.29.2" @@ -830,7 +806,7 @@ "@vue/compiler-ssr@3.5.31": version "3.5.31" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.31.tgz#bc68bb14cedab04ff5230460badfca983de5391b" + resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.31.tgz" integrity sha512-h0xIMxrt/LHOvJKMri+vdYT92BrK3HFLtDqq9Pr/lVVfE4IyKZKvWf0vJFW10Yr6nX02OR4MkJwI0c1HDa1hog== dependencies: "@vue/compiler-dom" "3.5.31" @@ -838,21 +814,21 @@ "@vue/devtools-api@^7.7.7": version "7.7.9" - resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz#999dbea50da6b00cf59a1336f11fdc2b43d9e063" + resolved "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz" integrity sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g== dependencies: "@vue/devtools-kit" "^7.7.9" "@vue/devtools-api@^8.0.6": version "8.1.1" - resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-8.1.1.tgz#367caf696349b1a49c655939ce605f4c96e30357" + resolved "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.1.1.tgz" integrity sha512-bsDMJ07b3GN1puVwJb/fyFnj/U2imyswK5UQVLZwVl7O05jDrt6BHxeG5XffmOOdasOj/bOmIjxJvGPxU7pcqw== dependencies: "@vue/devtools-kit" "^8.1.1" "@vue/devtools-core@^8.1.1": version "8.1.1" - resolved "https://registry.yarnpkg.com/@vue/devtools-core/-/devtools-core-8.1.1.tgz#f8a64f7d34bc545a3cd40b59efe7e665b6a9eeba" + resolved "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.1.1.tgz" integrity sha512-bCCsSABp1/ot4j8xJEycM6Mtt2wbuucfByr6hMgjbYhrtlscOJypZKvy8f1FyWLYrLTchB5Qz216Lm92wfbq0A== dependencies: "@vue/devtools-kit" "^8.1.1" @@ -860,7 +836,7 @@ "@vue/devtools-kit@^7.7.9": version "7.7.9" - resolved "https://registry.yarnpkg.com/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz#bc218a815616e8987df7ab3e10fc1fb3b8706c58" + resolved "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz" integrity sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA== dependencies: "@vue/devtools-shared" "^7.7.9" @@ -873,7 +849,7 @@ "@vue/devtools-kit@^8.1.1": version "8.1.1" - resolved "https://registry.yarnpkg.com/@vue/devtools-kit/-/devtools-kit-8.1.1.tgz#f205563eab389099d5181706b5b98e2d6fd2489d" + resolved "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.1.1.tgz" integrity sha512-gVBaBv++i+adg4JpH71k9ppl4soyR7Y2McEqO5YNgv0BI1kMZ7BDX5gnwkZ5COYgiCyhejZG+yGNrBAjj6Coqg== dependencies: "@vue/devtools-shared" "^8.1.1" @@ -883,19 +859,19 @@ "@vue/devtools-shared@^7.7.9": version "7.7.9" - resolved "https://registry.yarnpkg.com/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz#fa4c096b744927081a7dda5fcf05f34b1ae6ca14" + resolved "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz" integrity sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA== dependencies: rfdc "^1.4.1" "@vue/devtools-shared@^8.1.1": version "8.1.1" - resolved "https://registry.yarnpkg.com/@vue/devtools-shared/-/devtools-shared-8.1.1.tgz#daaa16243d2f5eaa50f7e34902d2bb9e98235b52" + resolved "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.1.1.tgz" integrity sha512-+h4ttmJYl/txpxHKaoZcaKpC+pvckgLzIDiSQlaQ7kKthKh8KuwoLW2D8hPJEnqKzXOvu15UHEoGyngAXCz0EQ== "@vue/eslint-config-prettier@^10.2.0": version "10.2.0" - resolved "https://registry.yarnpkg.com/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz#49a5ed571acb81820a216e6d88ebf1f3def321d0" + resolved "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz" integrity sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw== dependencies: eslint-config-prettier "^10.0.1" @@ -903,7 +879,7 @@ "@vue/eslint-config-typescript@^14.7.0": version "14.7.0" - resolved "https://registry.yarnpkg.com/@vue/eslint-config-typescript/-/eslint-config-typescript-14.7.0.tgz#76f0122f39447e8596ef83a771ce54ee0bd51380" + resolved "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-14.7.0.tgz" integrity sha512-iegbMINVc+seZ/QxtzWiOBozctrHiF2WvGedruu2EbLujg9VuU0FQiNcN2z1ycuaoKKpF4m2qzB5HDEMKbxtIg== dependencies: "@typescript-eslint/utils" "^8.56.0" @@ -913,7 +889,7 @@ "@vue/language-core@3.2.6": version "3.2.6" - resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-3.2.6.tgz#afe49fe5f2009eb0adaf80af72b01e09de2ba0fc" + resolved "https://registry.npmjs.org/@vue/language-core/-/language-core-3.2.6.tgz" integrity sha512-xYYYX3/aVup576tP/23sEUpgiEnujrENaoNRbaozC1/MA9I6EGFQRJb4xrt/MmUCAGlxTKL2RmT8JLTPqagCkg== dependencies: "@volar/language-core" "2.4.28" @@ -926,14 +902,14 @@ "@vue/reactivity@3.5.31": version "3.5.31" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.31.tgz#c8deaab09bd26185d3153d3e4447e2c6590a6608" + resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.31.tgz" integrity sha512-DtKXxk9E/KuVvt8VxWu+6Luc9I9ETNcqR1T1oW1gf02nXaZ1kuAx58oVu7uX9XxJR0iJCro6fqBLw9oSBELo5g== dependencies: "@vue/shared" "3.5.31" "@vue/runtime-core@3.5.31": version "3.5.31" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.31.tgz#bbe07e3bda17caf3ca2ba289bdd2d22badf3dce4" + resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.31.tgz" integrity sha512-AZPmIHXEAyhpkmN7aWlqjSfYynmkWlluDNPHMCZKFHH+lLtxP/30UJmoVhXmbDoP1Ng0jG0fyY2zCj1PnSSA6Q== dependencies: "@vue/reactivity" "3.5.31" @@ -941,7 +917,7 @@ "@vue/runtime-dom@3.5.31": version "3.5.31" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.31.tgz#3fe39a7bbbbf3ef2cdd6c51f8a1ea63d13959ec6" + resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.31.tgz" integrity sha512-xQJsNRmGPeDCJq/u813tyonNgWBFjzfVkBwDREdEWndBnGdHLHgkwNBQxLtg4zDrzKTEcnikUy1UUNecb3lJ6g== dependencies: "@vue/reactivity" "3.5.31" @@ -951,7 +927,7 @@ "@vue/server-renderer@3.5.31": version "3.5.31" - resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.31.tgz#d5dbc14dedc37315d197d0a846ef817e02257778" + resolved "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.31.tgz" integrity sha512-GJuwRvMcdZX/CriUnyIIOGkx3rMV3H6sOu0JhdKbduaeCji6zb60iOGMY7tFoN24NfsUYoFBhshZtGxGpxO4iA== dependencies: "@vue/compiler-ssr" "3.5.31" @@ -959,27 +935,27 @@ "@vue/shared@3.5.31", "@vue/shared@^3.5.0", "@vue/shared@^3.5.18": version "3.5.31" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.31.tgz#83276e1d450fea7d20dd15c3bbafbea5aada122d" + resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.5.31.tgz" integrity sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw== "@vue/tsconfig@^0.7.0": version "0.7.0" - resolved "https://registry.yarnpkg.com/@vue/tsconfig/-/tsconfig-0.7.0.tgz#67044c847b7a137b8cbfd6b23104c36dbaf80d1d" + resolved "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.7.0.tgz" integrity sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg== acorn-jsx@^5.3.2: version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn@^8.16.0: version "8.16.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz" integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== ajv@^6.14.0: version "6.14.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.14.0.tgz#fd067713e228210636ebb08c60bd3765d6dbe73a" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz" integrity sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw== dependencies: fast-deep-equal "^3.1.1" @@ -989,34 +965,34 @@ ajv@^6.14.0: alien-signals@^3.0.0: version "3.1.2" - resolved "https://registry.yarnpkg.com/alien-signals/-/alien-signals-3.1.2.tgz#26e623e3ed81e401df1a7c503f726e2288a4fa02" + resolved "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.2.tgz" integrity sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw== ansi-escapes@^7.0.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.3.0.tgz#5395bb74b2150a4a1d6e3c2565f4aeca78d28627" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz" integrity sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg== dependencies: environment "^1.0.0" ansi-regex@^6.2.2: version "6.2.2" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz" integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== ansi-styles@^6.2.1, ansi-styles@^6.2.3: version "6.2.3" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz" integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== ansis@^4.1.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/ansis/-/ansis-4.2.0.tgz#2e6e61c46b11726ac67f78785385618b9e658780" + resolved "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz" integrity sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig== ast-kit@^2.1.2, ast-kit@^2.1.3: version "2.2.0" - resolved "https://registry.yarnpkg.com/ast-kit/-/ast-kit-2.2.0.tgz#6d9a298acefef5bdfc5a0fa51d94d1334ef2e671" + resolved "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz" integrity sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw== dependencies: "@babel/parser" "^7.28.5" @@ -1024,7 +1000,7 @@ ast-kit@^2.1.2, ast-kit@^2.1.3: ast-walker-scope@^0.8.3: version "0.8.3" - resolved "https://registry.yarnpkg.com/ast-walker-scope/-/ast-walker-scope-0.8.3.tgz#f516c42669f3b77e1473a78e5e9d3c5b2e7c1e8e" + resolved "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.8.3.tgz" integrity sha512-cbdCP0PGOBq0ASG+sjnKIoYkWMKhhz+F/h9pRexUdX2Hd38+WOlBkRKlqkGOSm0YQpcFMQBJeK4WspUAkwsEdg== dependencies: "@babel/parser" "^7.28.4" @@ -1032,41 +1008,41 @@ ast-walker-scope@^0.8.3: balanced-match@^4.0.2: version "4.0.4" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz" integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== baseline-browser-mapping@^2.9.0: version "2.10.11" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.11.tgz#957bea71ccc2e9854287c2575a037d36b3a94b73" + resolved "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.11.tgz" integrity sha512-DAKrHphkJyiGuau/cFieRYhcTFeK/lBuD++C7cZ6KZHbMhBrisoi+EvhQ5RZrIfV5qwsW8kgQ07JIC+MDJRAhg== birpc@^2.3.0, birpc@^2.4.0, birpc@^2.6.1: version "2.9.0" - resolved "https://registry.yarnpkg.com/birpc/-/birpc-2.9.0.tgz#b59550897e4cd96a223e2a6c1475b572236ed145" + resolved "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz" integrity sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw== boolbase@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== brace-expansion@^5.0.2: version "5.0.5" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz" integrity sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ== dependencies: balanced-match "^4.0.2" braces@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" browserslist@^4.24.0: version "4.28.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz" integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== dependencies: baseline-browser-mapping "^2.9.0" @@ -1077,45 +1053,45 @@ browserslist@^4.24.0: bundle-name@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + resolved "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz" integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== dependencies: run-applescript "^7.0.0" caniuse-lite@^1.0.30001759: version "1.0.30001781" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz#344b47c03eb8168b79c3c158b872bcfbdd02a400" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz" integrity sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw== chart.js@^4.5.1: version "4.5.1" - resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.5.1.tgz#19dd1a9a386a3f6397691672231cb5fc9c052c35" + resolved "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz" integrity sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw== dependencies: "@kurkle/color" "^0.3.0" chartjs-adapter-date-fns@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz#c25f63c7f317c1f96f9a7c44bd45eeedb8a478e5" + resolved "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz" integrity sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg== chokidar@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-5.0.0.tgz#949c126a9238a80792be9a0265934f098af369a5" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz" integrity sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw== dependencies: readdirp "^5.0.0" cli-cursor@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz" integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== dependencies: restore-cursor "^5.0.0" cli-truncate@^5.0.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-5.2.0.tgz#c8e72aaca8339c773d128c36e0a17c6315b694eb" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz" integrity sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw== dependencies: slice-ansi "^8.0.0" @@ -1123,39 +1099,46 @@ cli-truncate@^5.0.0: colorette@^2.0.20: version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== commander@^14.0.3: version "14.0.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.3.tgz#425d79b48f9af82fcd9e4fc1ea8af6c5ec07bbc2" + resolved "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz" integrity sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw== confbox@^0.1.8: version "0.1.8" - resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.8.tgz#820d73d3b3c82d9bd910652c5d4d599ef8ff8b06" + resolved "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz" integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== confbox@^0.2.2: version "0.2.4" - resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.2.4.tgz#592e7be71f882a4a874e3c88f0ac1ef6f7da1ce5" + resolved "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz" integrity sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ== convert-source-map@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== copy-anything@^4: version "4.0.5" - resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-4.0.5.tgz#16cabafd1ea4bb327a540b750f2b4df522825aea" + resolved "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz" integrity sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA== dependencies: is-what "^5.2.0" +cron-parser@^5.6.1: + version "5.6.1" + resolved "https://registry.npmjs.org/cron-parser/-/cron-parser-5.6.1.tgz" + integrity sha512-QBm4o1PwZiuY7KFbVvW7FLC8bozy7YWzv+Fz6KRS7sQghzcbDZCGxr/Bc5b6TQreAoSwuWVP491dIcK0THCX6A== + dependencies: + luxon "^3.7.2" + cross-spawn@^7.0.6: version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" @@ -1164,44 +1147,44 @@ cross-spawn@^7.0.6: cssesc@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== csstype@^3.2.3: version "3.2.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz" integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== date-fns-tz@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-3.2.0.tgz#647dc56d38ac33a3e37b65e9d5c4cda5af5e58e6" + resolved "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-3.2.0.tgz" integrity sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ== date-fns@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14" + resolved "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz" integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3: version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" deep-is@^0.1.3: version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== default-browser-id@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.1.tgz#f7a7ccb8f5104bf8e0f71ba3b1ccfa5eafdb21e8" + resolved "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz" integrity sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q== default-browser@^5.2.1: version "5.5.0" - resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.5.0.tgz#2792e886f2422894545947cc80e1a444496c5976" + resolved "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz" integrity sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw== dependencies: bundle-name "^4.1.0" @@ -1209,57 +1192,57 @@ default-browser@^5.2.1: define-lazy-prop@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz" integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== detect-libc@^2.0.3: version "2.1.2" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz" integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== electron-to-chromium@^1.5.263: version "1.5.328" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz#d24ce55f1aa5e4a3b877c1b315a0ab40e9498cc8" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz" integrity sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w== emoji-regex@^10.3.0: version "10.6.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.6.0.tgz#bf3d6e8f7f8fd22a65d9703475bc0147357a6b0d" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz" integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== entities@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-7.0.1.tgz#26e8a88889db63417dcb9a1e79a3f1bc92b5976b" + resolved "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz" integrity sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA== environment@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" + resolved "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz" integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== error-stack-parser-es@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz#e6a1655dd12f39bb3a85bf4c7088187d78740327" + resolved "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz" integrity sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA== escalade@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-prettier@^10.0.1: version "10.1.8" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz#15734ce4af8c2778cc32f0b01b37b0b5cd1ecb97" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz" integrity sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w== eslint-plugin-prettier@^5.2.2: version "5.5.5" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz#9eae11593faa108859c26f9a9c367d619a0769c0" + resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz" integrity sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw== dependencies: prettier-linter-helpers "^1.0.1" @@ -1267,7 +1250,7 @@ eslint-plugin-prettier@^5.2.2: eslint-plugin-vue@10.8.0: version "10.8.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-10.8.0.tgz#a856d73b53ba5f4b2b4bb6cd8051ca7ac65cb21d" + resolved "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.8.0.tgz" integrity sha512-f1J/tcbnrpgC8suPN5AtdJ5MQjuXbSU9pGRSSYAuF3SHoiYCOdEX6O22pLaRyLHXvDcOe+O5ENgc1owQ587agA== dependencies: "@eslint-community/eslint-utils" "^4.4.0" @@ -1279,7 +1262,7 @@ eslint-plugin-vue@10.8.0: "eslint-scope@^8.2.0 || ^9.0.0", eslint-scope@^9.1.2: version "9.1.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-9.1.2.tgz#b9de6ace2fab1cff24d2e58d85b74c8fcea39802" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz" integrity sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ== dependencies: "@types/esrecurse" "^4.3.1" @@ -1289,17 +1272,17 @@ eslint-plugin-vue@10.8.0: eslint-visitor-keys@^3.4.3: version "3.4.3" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== "eslint-visitor-keys@^4.2.0 || ^5.0.0", eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz" integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== eslint@10.1.0: version "10.1.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.1.0.tgz#9ca98e654e642ab2e1af6d1e9d8613857ac341b4" + resolved "https://registry.npmjs.org/eslint/-/eslint-10.1.0.tgz" integrity sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA== dependencies: "@eslint-community/eslint-utils" "^4.8.0" @@ -1335,7 +1318,7 @@ eslint@10.1.0: "espree@^10.3.0 || ^11.0.0", espree@^11.2.0: version "11.2.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-11.2.0.tgz#01d5e47dc332aaba3059008362454a8cc34ccaa5" + resolved "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz" integrity sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw== dependencies: acorn "^8.16.0" @@ -1344,56 +1327,56 @@ eslint@10.1.0: esquery@^1.6.0, esquery@^1.7.0: version "1.7.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz" integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== estree-walker@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== eventemitter3@^5.0.1: version "5.0.4" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.4.tgz#a86d66170433712dde814707ac52b5271ceb1feb" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz" integrity sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== exsolve@^1.0.7: version "1.0.8" - resolved "https://registry.yarnpkg.com/exsolve/-/exsolve-1.0.8.tgz#7f5e34da61cd1116deda5136e62292c096f50613" + resolved "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz" integrity sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-diff@^1.1.2: version "1.3.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== fast-glob@^3.3.3: version "3.3.3" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -1404,43 +1387,43 @@ fast-glob@^3.3.3: fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastq@^1.6.0: version "1.20.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.20.1.tgz#ca750a10dc925bc8b18839fd203e3ef4b3ced675" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz" integrity sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw== dependencies: reusify "^1.0.4" fdir@^6.5.0: version "6.5.0" - resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== file-entry-cache@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz" integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== dependencies: flat-cache "^4.0.0" fill-range@^7.1.1: version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" find-up@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" @@ -1448,7 +1431,7 @@ find-up@^5.0.0: flat-cache@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz" integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== dependencies: flatted "^3.2.9" @@ -1456,7 +1439,7 @@ flat-cache@^4.0.0: flatted@^3.2.9: version "3.4.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz" integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== fsevents@~2.3.3: @@ -1466,166 +1449,166 @@ fsevents@~2.3.3: gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-east-asian-width@^1.0.0, get-east-asian-width@^1.3.1, get-east-asian-width@^1.5.0: version "1.5.0" - resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz#ce7008fe345edcf5497a6f557cfa54bc318a9ce7" + resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz" integrity sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA== glob-parent@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob-parent@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" hookable@^5.5.3: version "5.5.3" - resolved "https://registry.yarnpkg.com/hookable/-/hookable-5.5.3.tgz#6cfc358984a1ef991e2518cb9ed4a778bbd3215d" + resolved "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz" integrity sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ== husky@^9.1.7: version "9.1.7" - resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" + resolved "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz" integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== ignore@^5.2.0: version "5.3.2" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== ignore@^7.0.5: version "7.0.5" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + resolved "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz" integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== is-docker@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz" integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^5.0.0, is-fullwidth-code-point@^5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz#046b2a6d4f6b156b2233d3207d4b5a9783999b98" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz" integrity sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ== dependencies: get-east-asian-width "^1.3.1" is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-inside-container@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + resolved "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz" integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== dependencies: is-docker "^3.0.0" is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-what@^5.2.0: version "5.5.0" - resolved "https://registry.yarnpkg.com/is-what/-/is-what-5.5.0.tgz#a3031815757cfe1f03fed990bf6355a2d3f628c4" + resolved "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz" integrity sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw== is-wsl@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.1.tgz#327897b26832a3eb117da6c27492d04ca132594f" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz" integrity sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw== dependencies: is-inside-container "^1.0.0" isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isexe@^3.1.1: version "3.1.5" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.5.tgz#42e368f68d5e10dadfee4fda7b550bc2d8892dc9" + resolved "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz" integrity sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w== jiti@^2.6.1: version "2.6.1" - resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92" + resolved "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz" integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== jsesc@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== json-buffer@3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-parse-even-better-errors@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz#d3f67bd5925e81d3e31aa466acc821c8375cec43" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz" integrity sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json5@^2.2.3: version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== keyv@^4.5.4: version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: json-buffer "3.0.1" kolorist@^1.8.0: version "1.8.0" - resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c" + resolved "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz" integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== levn@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -1668,12 +1651,12 @@ lightningcss-linux-arm64-musl@1.32.0: lightningcss-linux-x64-gnu@1.32.0: version "1.32.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz#0b7803af4eb21cfd38dd39fe2abbb53c7dd091f6" + resolved "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz" integrity sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA== lightningcss-linux-x64-musl@1.32.0: version "1.32.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz#88dc8ba865ddddb1ac5ef04b0f161804418c163b" + resolved "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz" integrity sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg== lightningcss-win32-arm64-msvc@1.32.0: @@ -1688,7 +1671,7 @@ lightningcss-win32-x64-msvc@1.32.0: lightningcss@^1.32.0: version "1.32.0" - resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.32.0.tgz#b85aae96486dcb1bf49a7c8571221273f4f1e4a9" + resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz" integrity sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ== dependencies: detect-libc "^2.0.3" @@ -1707,7 +1690,7 @@ lightningcss@^1.32.0: lint-staged@^16.4.0: version "16.4.0" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-16.4.0.tgz#a00b0e3abff59239cef6d7d9341e8f8473308e23" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz" integrity sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw== dependencies: commander "^14.0.3" @@ -1719,7 +1702,7 @@ lint-staged@^16.4.0: listr2@^9.0.5: version "9.0.5" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-9.0.5.tgz#92df7c4416a6da630eb9ef46da469b70de97b316" + resolved "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz" integrity sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g== dependencies: cli-truncate "^5.0.0" @@ -1731,7 +1714,7 @@ listr2@^9.0.5: local-pkg@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-1.1.2.tgz#c03d208787126445303f8161619dc701afa4abb5" + resolved "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz" integrity sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A== dependencies: mlly "^1.7.4" @@ -1740,14 +1723,14 @@ local-pkg@^1.1.2: locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" log-update@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4" + resolved "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz" integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== dependencies: ansi-escapes "^7.0.0" @@ -1758,38 +1741,43 @@ log-update@^6.1.0: lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" +luxon@^3.7.2: + version "3.7.2" + resolved "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz" + integrity sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew== + magic-string-ast@^1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/magic-string-ast/-/magic-string-ast-1.0.3.tgz#51ef7832fd5c70a0188fb94627caa3b8c74ff9bf" + resolved "https://registry.npmjs.org/magic-string-ast/-/magic-string-ast-1.0.3.tgz" integrity sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA== dependencies: magic-string "^0.30.19" magic-string@^0.30.19, magic-string@^0.30.21, magic-string@^0.30.4: version "0.30.21" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz" integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== dependencies: "@jridgewell/sourcemap-codec" "^1.5.5" memorystream@^0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + resolved "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz" integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== merge2@^1.3.0: version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== micromatch@^4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" @@ -1797,24 +1785,24 @@ micromatch@^4.0.8: mimic-function@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== minimatch@^10.2.2, minimatch@^10.2.4: version "10.2.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz" integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg== dependencies: brace-expansion "^5.0.2" mitt@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + resolved "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz" integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== mlly@^1.7.4, mlly@^1.8.0: version "1.8.2" - resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.8.2.tgz#e7f7919a82d13b174405613117249a3f449d78bb" + resolved "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz" integrity sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA== dependencies: acorn "^8.16.0" @@ -1824,42 +1812,42 @@ mlly@^1.7.4, mlly@^1.8.0: mrmime@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.1.tgz#bc3e87f7987853a54c9850eeb1f1078cd44adddc" + resolved "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz" integrity sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ== ms@^2.1.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== muggle-string@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.4.1.tgz#3b366bd43b32f809dc20659534dd30e7c8a0d328" + resolved "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz" integrity sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ== nanoid@^3.3.11: version "3.3.11" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== node-releases@^2.0.27: version "2.0.36" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.36.tgz#99fd6552aaeda9e17c4713b57a63964a2e325e9d" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz" integrity sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA== npm-normalize-package-bin@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz#df79e70cd0a113b77c02d1fe243c96b8e618acb1" + resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz" integrity sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w== npm-run-all2@^8.0.4: version "8.0.4" - resolved "https://registry.yarnpkg.com/npm-run-all2/-/npm-run-all2-8.0.4.tgz#bcc070fd0cdb8d45496ec875d99a659a112e3f74" + resolved "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.4.tgz" integrity sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA== dependencies: ansi-styles "^6.2.1" @@ -1873,26 +1861,26 @@ npm-run-all2@^8.0.4: nth-check@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== dependencies: boolbase "^1.0.0" ohash@^2.0.11: version "2.0.11" - resolved "https://registry.yarnpkg.com/ohash/-/ohash-2.0.11.tgz#60b11e8cff62ca9dee88d13747a5baa145f5900b" + resolved "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz" integrity sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ== onetime@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + resolved "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz" integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== dependencies: mimic-function "^5.0.0" open@^10.2.0: version "10.2.0" - resolved "https://registry.yarnpkg.com/open/-/open-10.2.0.tgz#b9d855be007620e80b6fb05fac98141fe62db73c" + resolved "https://registry.npmjs.org/open/-/open-10.2.0.tgz" integrity sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA== dependencies: default-browser "^5.2.1" @@ -1902,7 +1890,7 @@ open@^10.2.0: optionator@^0.9.3: version "0.9.4" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz" integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== dependencies: deep-is "^0.1.3" @@ -1914,78 +1902,78 @@ optionator@^0.9.3: p-limit@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" path-browserify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz" integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== pathe@^2.0.1, pathe@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== perfect-debounce@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz#9c2e8bc30b169cc984a58b7d5b28049839591d2a" + resolved "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz" integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== perfect-debounce@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-2.1.0.tgz#e7078e38f231cb191855c3136a4423aef725d261" + resolved "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz" integrity sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g== picocolors@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.3.1: version "2.3.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz" integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== picomatch@^4.0.2, picomatch@^4.0.3, picomatch@^4.0.4: version "4.0.4" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz" integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== pidtree@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz" integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== pinia@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/pinia/-/pinia-3.0.4.tgz#75dde12784a61e34c1fa6abcd13c1a1061c360c0" + resolved "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz" integrity sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw== dependencies: "@vue/devtools-api" "^7.7.7" pkg-types@^1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.3.1.tgz#bd7cc70881192777eef5326c19deb46e890917df" + resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz" integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== dependencies: confbox "^0.1.8" @@ -1994,7 +1982,7 @@ pkg-types@^1.3.1: pkg-types@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-2.3.0.tgz#037f2c19bd5402966ff6810e32706558cb5b5726" + resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz" integrity sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig== dependencies: confbox "^0.2.2" @@ -2003,7 +1991,7 @@ pkg-types@^2.3.0: postcss-selector-parser@^7.1.0: version "7.1.1" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz#e75d2e0d843f620e5df69076166f4e16f891cb9f" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz" integrity sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg== dependencies: cssesc "^3.0.0" @@ -2011,7 +1999,7 @@ postcss-selector-parser@^7.1.0: postcss@^8.5.8: version "8.5.8" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.8.tgz#6230ecc8fb02e7a0f6982e53990937857e13f399" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz" integrity sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg== dependencies: nanoid "^3.3.11" @@ -2020,39 +2008,39 @@ postcss@^8.5.8: prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier-linter-helpers@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz#6a31f88a4bad6c7adda253de12ba4edaea80ebcd" + resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz" integrity sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg== dependencies: fast-diff "^1.1.2" prettier@3.8.1: version "3.8.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" + resolved "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz" integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== punycode@^2.1.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== quansync@^0.2.11: version "0.2.11" - resolved "https://registry.yarnpkg.com/quansync/-/quansync-0.2.11.tgz#f9c3adda2e1272e4f8cf3f1457b04cbdb4ee692a" + resolved "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz" integrity sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA== queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== read-package-json-fast@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz#8ccbc05740bb9f58264f400acc0b4b4eee8d1b39" + resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz" integrity sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg== dependencies: json-parse-even-better-errors "^4.0.0" @@ -2060,12 +2048,12 @@ read-package-json-fast@^4.0.0: readdirp@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-5.0.0.tgz#fbf1f71a727891d685bb1786f9ba74084f6e2f91" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz" integrity sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ== restore-cursor@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz" integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== dependencies: onetime "^7.0.0" @@ -2073,17 +2061,17 @@ restore-cursor@^5.0.0: reusify@^1.0.4: version "1.1.0" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz" integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== rfdc@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== rolldown@1.0.0-rc.12: version "1.0.0-rc.12" - resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.0.0-rc.12.tgz#e226fa74a4c21c71a13f8e44f778f81d58853ad5" + resolved "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz" integrity sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A== dependencies: "@oxc-project/types" "=0.122.0" @@ -2107,56 +2095,56 @@ rolldown@1.0.0-rc.12: run-applescript@^7.0.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.1.0.tgz#2e9e54c4664ec3106c5b5630e249d3d6595c4911" + resolved "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz" integrity sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q== run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" scule@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/scule/-/scule-1.3.0.tgz#6efbd22fd0bb801bdcc585c89266a7d2daa8fbd3" + resolved "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz" integrity sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g== semver@^6.3.1: version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.6.3, semver@^7.7.3: version "7.7.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + resolved "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz" integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.7.3: version "1.8.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz" integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== signal-exit@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== sirv@^3.0.1, sirv@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-3.0.2.tgz#f775fccf10e22a40832684848d636346f41cd970" + resolved "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz" integrity sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g== dependencies: "@polka/url" "^1.0.0-next.24" @@ -2165,7 +2153,7 @@ sirv@^3.0.1, sirv@^3.0.2: slice-ansi@^7.1.0: version "7.1.2" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.2.tgz#adf7be70aa6d72162d907cd0e6d5c11f507b5403" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz" integrity sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w== dependencies: ansi-styles "^6.2.1" @@ -2173,7 +2161,7 @@ slice-ansi@^7.1.0: slice-ansi@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-8.0.0.tgz#22d0b66d18bc5c57f488bfcf36cbde3bef731537" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz" integrity sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg== dependencies: ansi-styles "^6.2.3" @@ -2181,22 +2169,22 @@ slice-ansi@^8.0.0: source-map-js@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== speakingurl@^14.0.1: version "14.0.1" - resolved "https://registry.yarnpkg.com/speakingurl/-/speakingurl-14.0.1.tgz#f37ec8ddc4ab98e9600c1c9ec324a8c48d772a53" + resolved "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz" integrity sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ== string-argv@^0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== string-width@^7.0.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + resolved "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz" integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== dependencies: emoji-regex "^10.3.0" @@ -2205,7 +2193,7 @@ string-width@^7.0.0: string-width@^8.2.0: version "8.2.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.2.0.tgz#bdb6a9bd6d7800db635adae96cdb0443fec56c42" + resolved "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz" integrity sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw== dependencies: get-east-asian-width "^1.5.0" @@ -2213,33 +2201,33 @@ string-width@^8.2.0: strip-ansi@^7.1.0, strip-ansi@^7.1.2: version "7.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz" integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== dependencies: ansi-regex "^6.2.2" superjson@^2.2.2: version "2.2.6" - resolved "https://registry.yarnpkg.com/superjson/-/superjson-2.2.6.tgz#a223a3a988172a5f9656e2063fe5f733af40d099" + resolved "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz" integrity sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA== dependencies: copy-anything "^4" synckit@^0.11.12: version "0.11.12" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.12.tgz#abe74124264fbc00a48011b0d98bdc1cffb64a7b" + resolved "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz" integrity sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ== dependencies: "@pkgr/core" "^0.2.9" tinyexec@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.0.4.tgz#6c60864fe1d01331b2f17c6890f535d7e5385408" + resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz" integrity sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw== tinyglobby@^0.2.15: version "0.2.15" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz" integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== dependencies: fdir "^6.5.0" @@ -2247,19 +2235,19 @@ tinyglobby@^0.2.15: to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" totalist@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + resolved "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz" integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== ts-api-utils@^2.4.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1" + resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz" integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== tslib@^2.4.0: @@ -2269,14 +2257,14 @@ tslib@^2.4.0: type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" typescript-eslint@^8.56.0: version "8.57.2" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.57.2.tgz#d64c6648dda5b15176708701537ab0b55ba3c83d" + resolved "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz" integrity sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A== dependencies: "@typescript-eslint/eslint-plugin" "8.57.2" @@ -2286,22 +2274,22 @@ typescript-eslint@^8.56.0: typescript@~5.8.0: version "5.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz" integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== ufo@^1.6.3: version "1.6.3" - resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.6.3.tgz#799666e4e88c122a9659805e30b9dc071c3aed4f" + resolved "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz" integrity sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q== undici-types@~6.21.0: version "6.21.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz" integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== unplugin-utils@^0.3.0, unplugin-utils@^0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/unplugin-utils/-/unplugin-utils-0.3.1.tgz#ef2873670a6a2a21bd2c9d31307257cc863a709c" + resolved "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz" integrity sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog== dependencies: pathe "^2.0.3" @@ -2309,7 +2297,7 @@ unplugin-utils@^0.3.0, unplugin-utils@^0.3.1: unplugin@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-3.0.0.tgz#01e40c474bf74d363744f4cb262569d26dd9bb43" + resolved "https://registry.npmjs.org/unplugin/-/unplugin-3.0.0.tgz" integrity sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg== dependencies: "@jridgewell/remapping" "^2.3.5" @@ -2318,7 +2306,7 @@ unplugin@^3.0.0: update-browserslist-db@^1.2.0: version "1.2.3" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz" integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== dependencies: escalade "^3.2.0" @@ -2326,19 +2314,19 @@ update-browserslist-db@^1.2.0: uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" util-deprecate@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== vite-dev-rpc@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz#a54be63cc4dbb127bce1360e4b12d9038087c204" + resolved "https://registry.npmjs.org/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz" integrity sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A== dependencies: birpc "^2.4.0" @@ -2346,12 +2334,12 @@ vite-dev-rpc@^1.1.0: vite-hot-client@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/vite-hot-client/-/vite-hot-client-2.1.0.tgz#88f8469875e0121eae2f460cbf35cb528c049961" + resolved "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.1.0.tgz" integrity sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ== vite-plugin-inspect@^11.3.3: version "11.3.3" - resolved "https://registry.yarnpkg.com/vite-plugin-inspect/-/vite-plugin-inspect-11.3.3.tgz#2b9c4db9574c59ebcf9647b37bb4eb5c5596b3be" + resolved "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-11.3.3.tgz" integrity sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA== dependencies: ansis "^4.1.0" @@ -2366,7 +2354,7 @@ vite-plugin-inspect@^11.3.3: vite-plugin-vue-devtools@^8.1.0: version "8.1.1" - resolved "https://registry.yarnpkg.com/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-8.1.1.tgz#bf2fb3cf6e4bfadc174c01936ddc8a5ccc561ee0" + resolved "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-8.1.1.tgz" integrity sha512-9qTpOmZ2vHpvlI9hdVXAQ1Ry4I8GcBArU7aPi0qfIaV7fQIXy0L1nb6X4mFY2Gw0dYshHuLbIl0Ulb572SCjsQ== dependencies: "@vue/devtools-core" "^8.1.1" @@ -2378,7 +2366,7 @@ vite-plugin-vue-devtools@^8.1.0: vite-plugin-vue-inspector@^5.3.2: version "5.4.0" - resolved "https://registry.yarnpkg.com/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.4.0.tgz#164a8f454f9558a5896164d8495f23910f23250f" + resolved "https://registry.npmjs.org/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.4.0.tgz" integrity sha512-Iq/024CydcE46FZqWPU4t4lw4uYOdLnFSO1RNxJVt2qY9zxIjmnkBqhHnYaReWM82kmNnaXs7OkfgRrV2GEjyw== dependencies: "@babel/core" "^7.23.0" @@ -2393,7 +2381,7 @@ vite-plugin-vue-inspector@^5.3.2: vite@8.0.3: version "8.0.3" - resolved "https://registry.yarnpkg.com/vite/-/vite-8.0.3.tgz#036d9e3b077ff57b128660b3e3a5d2d12bac9b42" + resolved "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz" integrity sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ== dependencies: lightningcss "^1.32.0" @@ -2406,22 +2394,22 @@ vite@8.0.3: vscode-uri@^3.0.8: version "3.1.0" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c" + resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz" integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ== vue-chartjs@^5.3.3: version "5.3.3" - resolved "https://registry.yarnpkg.com/vue-chartjs/-/vue-chartjs-5.3.3.tgz#1ee3e1580bb35779616f881eaf56711aee9e1c85" + resolved "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.3.tgz" integrity sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA== vue-demi@>=0.13.0: version "0.14.10" - resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04" + resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz" integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg== vue-eslint-parser@^10.4.0: version "10.4.0" - resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-10.4.0.tgz#fd7251d0e710a88a6618f50e8a27973bc3c8d69c" + resolved "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.4.0.tgz" integrity sha512-Vxi9pJdbN3ZnVGLODVtZ7y4Y2kzAAE2Cm0CZ3ZDRvydVYxZ6VrnBhLikBsRS+dpwj4Jv4UCv21PTEwF5rQ9WXg== dependencies: debug "^4.4.0" @@ -2433,7 +2421,7 @@ vue-eslint-parser@^10.4.0: vue-router@^5.0.4: version "5.0.4" - resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-5.0.4.tgz#41ffc00bab448c406447115bbb29d49144cc1e7d" + resolved "https://registry.npmjs.org/vue-router/-/vue-router-5.0.4.tgz" integrity sha512-lCqDLCI2+fKVRl2OzXuzdSWmxXFLQRxQbmHugnRpTMyYiT+hNaycV0faqG5FBHDXoYrZ6MQcX87BvbY8mQ20Bg== dependencies: "@babel/generator" "^7.28.6" @@ -2456,7 +2444,7 @@ vue-router@^5.0.4: vue-tsc@^3.2.5: version "3.2.6" - resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-3.2.6.tgz#efeb12a6166d531304d2e1602b550254309be15d" + resolved "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.2.6.tgz" integrity sha512-gYW/kWI0XrwGzd0PKc7tVB/qpdeAkIZLNZb10/InizkQjHjnT8weZ/vBarZoj4kHKbUTZT/bAVgoOr8x4NsQ/Q== dependencies: "@volar/typescript" "2.4.28" @@ -2464,7 +2452,7 @@ vue-tsc@^3.2.5: vue3-select-component@^0.16.0: version "0.16.0" - resolved "https://registry.yarnpkg.com/vue3-select-component/-/vue3-select-component-0.16.0.tgz#98436688e33e902908ca560d7497d379eab4bbcc" + resolved "https://registry.npmjs.org/vue3-select-component/-/vue3-select-component-0.16.0.tgz" integrity sha512-zh1HyCXkUida15T8uG4fStC3R0rX6q9H2Q6j/0t9aduxSdbX32I+f/aZAsQRmhJnQziOlBYak/ivtv9i0nbiqQ== dependencies: "@floating-ui/vue" "1.1.10" @@ -2473,7 +2461,7 @@ vue3-select-component@^0.16.0: vue@^3.5.18: version "3.5.31" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.31.tgz#ff20b2ca7893b4f9ae576a2064dbd3e2f5850118" + resolved "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz" integrity sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q== dependencies: "@vue/compiler-dom" "3.5.31" @@ -2484,31 +2472,31 @@ vue@^3.5.18: webpack-virtual-modules@^0.6.2: version "0.6.2" - resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz#057faa9065c8acf48f24cb57ac0e77739ab9a7e8" + resolved "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz" integrity sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ== which@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" which@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/which/-/which-5.0.0.tgz#d93f2d93f79834d4363c7d0c23e00d07c466c8d6" + resolved "https://registry.npmjs.org/which/-/which-5.0.0.tgz" integrity sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ== dependencies: isexe "^3.1.1" word-wrap@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== wrap-ansi@^9.0.0: version "9.0.2" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz#956832dea9494306e6d209eb871643bb873d7c98" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz" integrity sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== dependencies: ansi-styles "^6.2.1" @@ -2517,27 +2505,27 @@ wrap-ansi@^9.0.0: wsl-utils@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/wsl-utils/-/wsl-utils-0.1.0.tgz#8783d4df671d4d50365be2ee4c71917a0557baab" + resolved "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz" integrity sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw== dependencies: is-wsl "^3.1.0" xml-name-validator@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== yallist@^3.0.2: version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yaml@^2.8.2: version "2.8.3" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.3.tgz#a0d6bd2efb3dd03c59370223701834e60409bd7d" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz" integrity sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==