diff --git a/Directory.Packages.props b/Directory.Packages.props index 0a3051b2ee..e4505d2ba3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -19,10 +19,10 @@ - - - - + + + + diff --git a/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/post.md b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/post.md index 49c4f51e60..5d739b1588 100644 --- a/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/post.md +++ b/docs/en/Community-Articles/2026-06-18-deep-dive-9-workflows/post.md @@ -30,8 +30,12 @@ If the team always builds a package after an application service change, that sh Workflows give ABP Studio a place to encode those repeatable steps. +_**Choose a Workflow and Scope before sending your prompt:**_ + ![Opening the workflow settings panel in ABP Studio](workflows-openning.png) +_**The selected workflow can be configured through Workflow Settings, where you can create, edit, and manage reusable workflows for common development tasks:**_ + ![ABP AI Agent workflow settings](workflow-settings.png) For me, the value is not only automation. It is also consistency. diff --git a/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/Post.md b/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/Post.md new file mode 100644 index 0000000000..2fd61ac23e --- /dev/null +++ b/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/Post.md @@ -0,0 +1,645 @@ +Multi-tenancy sounds simple until the first real requirement lands: tenant-specific data isolation, host-only features, separate databases for a few big customers, and an admin panel that still feels like one product. + +ABP Framework gives you most of the plumbing out of the box, but the important part is knowing which pieces to enable, which defaults to trust, and where teams usually get into trouble. This article walks through a practical implementation approach for ABP Framework v8+ and the latest branch, covering shared database, separate database, and hybrid setups. + +## What ABP Multi-Tenancy Actually Gives You + +ABP's multi-tenancy support is not just a `TenantId` convention. It includes: + +- tenant context management +- automatic data filtering for multi-tenant entities +- tenant resolution from web requests +- host vs tenant side separation +- permission scoping by tenancy side +- tenant-aware connection string resolution +- tenant management infrastructure + +The first switch is explicit. + +```csharp +Configure(options => +{ + options.IsEnabled = true; +}); +``` + +Technically, multi-tenancy is disabled by default, although ABP startup templates usually enable it for you. + +ABP models two sides: + +- Host: the system owner, platform operator, or SaaS provider +- Tenant: the customer using the system + +A `TenantId` value of `null` typically means the data belongs to the host side. + +## Choose the Right Database Architecture First + +Before writing entities or resolvers, decide how tenant data will be stored. This choice affects migrations, operations, support cost, and sometimes your pricing model. + +### 1. Shared Database + +All tenants share the same database and tables. Isolation is enforced with `TenantId` and ABP's built-in data filters. + +Why teams choose it: + +- simplest deployment model +- lowest infrastructure cost +- easiest to operate in early-stage SaaS products +- one migration pipeline + +Trade-offs: + +- large tables grow quickly +- indexing becomes more important +- noisy-neighbor performance is more likely +- stricter discipline is required to avoid cross-tenant mistakes + +This is usually the best default unless you already know you need stronger isolation. + +### 2. Separate Database per Tenant + +Each tenant gets its own database. Host data is usually kept in a central database, while tenant-specific data goes to per-tenant databases. + +Why teams choose it: + +- stronger data isolation +- easier tenant-specific backup and restore +- cleaner compliance story +- large tenants can scale independently + +Trade-offs: + +- more provisioning logic +- more migration complexity +- more operational overhead +- onboarding a tenant is no longer just inserting a row + +### 3. Hybrid Model + +Some tenants use the shared database, while others get dedicated databases. + +This is often the most realistic long-term model: + +- small customers stay in shared infrastructure +- enterprise customers get isolated databases +- you can promote selected tenants later + +Trade-offs: + +- highest implementation and operational complexity +- migrations and seeding need stronger discipline +- debugging environment-specific issues becomes harder + + + +![Generated illustration](inline-1.png) + +## Implement Tenant-Aware Entities Correctly + +In ABP, tenant-scoped entities implement `IMultiTenant`. + +```csharp +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +public class Product : AggregateRoot, IMultiTenant +{ + public Guid? TenantId { get; set; } + public string Name { get; private set; } + public decimal Price { get; private set; } + + private Product() + { + } + + public Product(Guid id, string name, decimal price, Guid? tenantId) + : base(id) + { + TenantId = tenantId; + Name = name; + Price = price; + } +} +``` + +Once an entity implements `IMultiTenant`, ABP automatically filters queries according to the current tenant. + +That means this kind of repository call is already tenant-aware in normal application flow: + +```csharp +var products = await _productRepository.GetListAsync(); +``` + +### The nullable `TenantId` detail matters + +`TenantId` is nullable by design because host-owned data is valid in ABP. + +That is useful, but also easy to misuse. + +If an entity is truly tenant-only, do not casually allow `TenantId = null`. Enforce the rule in your constructor, factory method, or domain service. + +Example: + +```csharp +public Order(Guid id, Guid tenantId, string orderNo) : base(id) +{ + TenantId = tenantId; + OrderNo = orderNo; +} + +public Guid? TenantId { get; private set; } +public string OrderNo { get; private set; } +``` + +For tenant-only aggregates, this small constraint prevents a surprising number of data leakage bugs. + +## Use `ICurrentTenant` for Context-Aware Logic + +`ICurrentTenant` is the central service for reading or temporarily changing tenant context. + +```csharp +public class ProductAppService : ApplicationService +{ + public async Task GetTenantInfoAsync() + { + if (CurrentTenant.IsAvailable) + { + return $"TenantId: {CurrentTenant.Id}, Name: {CurrentTenant.Name}"; + } + + return "Host context"; + } +} +``` + +The more interesting capability is context switching. + +```csharp +using (_currentTenant.Change(tenantId)) +{ + var count = await _productRepository.GetCountAsync(); +} +``` + +This is useful for: + +- background jobs that process one tenant at a time +- host-side reporting across tenants +- tenant seeding during onboarding +- maintenance tasks and migrations + +### A practical warning + +Switching tenant context is powerful. It is also a common source of subtle bugs when developers mix host and tenant operations in the same method. Keep tenant context scopes short and obvious. + +## How Tenant Resolution Works in ABP + +ABP determines the active tenant through a chain of tenant resolvers. Out of the box, the default contributors are checked in this order: + +1. Current user claims +2. Query string, using `__tenant` by default +3. Route value +4. Header +5. Cookie + +In practice, this means a request can become tenant-aware even before your application service runs. + +### Default key configuration + +If you want to change the default `__tenant` key: + +```csharp +Configure(options => +{ + options.TenantKey = "tenant"; +}); +``` + +This is fine, but if you have a frontend client, especially Angular, the client must use the same tenant key. Otherwise the backend and frontend silently disagree about tenant resolution. + +### Domain and subdomain based resolution + +ABP also supports domain or subdomain-based tenant resolution. + +```csharp +Configure(options => +{ + options.AddDomainTenantResolver("{0}.myapp.com"); +}); +``` + +This is usually the cleanest user experience for SaaS applications because the tenant is implied by the hostname. + +Use it when: + +- each tenant has a branded subdomain +- the URL should define tenant context naturally +- you want fewer explicit tenant parameters in requests + +Be careful with: + +- reverse proxies and forwarded headers +- wildcard DNS and TLS certificates +- authentication server issuer validation in wildcard domain scenarios +- local development setup + +If you use OpenIddict or token validation with wildcard domains, make sure issuer validation is configured for that pattern. This is one of the most common production surprises in subdomain-based multi-tenant setups. + +### Fallback tenant + +ABP can also use a fallback tenant. + +That can be convenient in development or in a constrained deployment model, but it comes with an important trade-off: you effectively reduce or hide host context behavior. Use fallback tenants deliberately, not as a shortcut for resolver problems. + + + +![Generated illustration](inline-2.png) + +## Custom Tenant Resolvers for Real Projects + +Sooner or later, one tenant comes from a gateway header, another from a custom route pattern, and a third from a legacy integration. + +ABP allows custom tenant resolvers by implementing a contributor. + +```csharp +using System.Threading.Tasks; +using Volo.Abp.MultiTenancy; + +public class XTenantHeaderResolveContributor : TenantResolveContributorBase +{ + public const string HeaderName = "X-Tenant-Code"; + + public override string Name => "XTenantHeader"; + + public override Task ResolveAsync(ITenantResolveContext context) + { + var httpContext = context.GetHttpContext(); + var tenantCode = httpContext?.Request.Headers[HeaderName].ToString(); + + if (!tenantCode.IsNullOrWhiteSpace()) + { + context.Handled = true; + context.TenantIdOrName = tenantCode; + } + + return Task.CompletedTask; + } +} +``` + +Then register it in tenant resolve options. + +The main rule here is simple: prefer one primary strategy. A long resolver chain with multiple overlapping sources makes support harder. + +## Configure `DbContext` for Host and Tenant Sides + +When you move beyond a single shared database, `DbContext` design becomes a core architecture decision. + +ABP supports defining which side a context belongs to: + +- `Both` +- `Host` +- `Tenant` + +This matters when you want host-only tables to stay out of tenant databases, or when tenant databases should contain only selected modules. + +### Why this matters + +Suppose your host side includes tenant management, audit administration, and platform billing, but tenant databases should only include business tables and tenant-facing identity data. + +If you blindly configure every module in every context, your tenant databases will accumulate tables they should never have had. + +### Practical approach + +For a shared database setup, one `DbContext` with `Both` is often enough. + +For separate or hybrid databases, a common approach is: + +- one host/shared `DbContext` +- one tenant-only `DbContext` +- selective module configuration per context + +The important implementation detail is not just the side flag. It is also controlling which `builder.ConfigureXyz()` calls are applied in each context. + +For example, do not configure host-only modules in the tenant-only context. + +## Shared Database Setup: The Best Starting Point + +If you are implementing multi-tenancy for the first time in ABP, start with the shared database model unless you have a strong reason not to. + +A practical setup looks like this: + +1. Enable multi-tenancy +2. Make tenant-owned entities implement `IMultiTenant` +3. Use standard ABP repositories +4. Resolve tenant from user, subdomain, or request key +5. Keep host-owned data with `TenantId = null` +6. Define permissions with proper tenancy sides + +Example entity creation inside a tenant context: + +```csharp +public class ProductManager : DomainService +{ + public async Task CreateAsync(string name, decimal price) + { + var product = new Product( + GuidGenerator.Create(), + name, + price, + CurrentTenant.Id + ); + + return await _productRepository.InsertAsync(product); + } +} +``` + +This works well because ABP naturally fills the application flow with tenant context. + +### Performance tips for shared database mode + +As tenant count grows: + +- index `TenantId` on large tables +- include `TenantId` in common query patterns +- monitor large shared tables early +- be careful with cross-tenant reporting queries +- verify all custom SQL is tenant-aware + +ABP helps with filtering, but it does not replace database design. + +## Separate Database per Tenant in ABP + +This is where ABP becomes especially useful, because it can resolve the active tenant and then use tenant-specific connection strings. + +The Tenant Management module stores tenant metadata, including optional connection strings. + +At a high level, the flow is: + +1. Resolve the current tenant +2. Load tenant configuration +3. Determine the right connection string +4. Build the `DbContext` against the host or tenant database +5. Apply data filters inside that database scope as needed + +### What is available out of the box + +ABP supports the architecture and connection-string-based separation. + +Version-wise, the latest ABP docs reflect improved support in open source for separate database per tenant. However, managing tenant connection strings from the UI remains tied to SaaS/PRO features. In open source, teams often provide this through custom admin screens, configuration management, or provisioning services. + +### What changes operationally + +With per-tenant databases, you now need a plan for: + +- database creation during tenant onboarding +- migrations for new and existing tenant databases +- tenant-specific seeding +- backups and restore procedures +- monitoring failed or drifted tenant databases + +This is the real cost of stronger isolation. + + + +![Generated illustration](inline-3.png) + +## Hybrid Multi-Tenancy: Shared by Default, Dedicated When Needed + +Hybrid architecture is often the most business-friendly model. + +A common pattern looks like this: + +- default all new tenants to shared database +- move larger or regulated tenants to dedicated databases +- keep host/platform data in a central database + +This lets you defer infrastructure cost until a tenant actually needs isolation. + +The challenge is not whether ABP supports it. It does. The challenge is operational consistency: + +- how a tenant is promoted from shared to dedicated +- how data is moved safely +- how migrations stay aligned across both models +- how support engineers know which storage model a tenant uses + +If you choose hybrid, document the lifecycle, not just the code. + +## Tenant Management, Onboarding, and Connection Strings + +ABP's Tenant Management module is the starting point for tenant administration. + +It gives you tenant records and a standard place to store metadata. In more advanced solutions, that metadata is often extended with: + +- edition or plan +- onboarding status +- provisioning result +- custom domains +- support tier +- infrastructure notes + +For separate database scenarios, onboarding usually means more than creating a tenant row. It often includes: + +1. create tenant record +2. assign connection string if needed +3. create database or schema +4. run migrations +5. seed tenant data +6. create admin user +7. confirm domain or resolver setup + +Treat onboarding as a workflow, not a controller action. + +## Permissions and Authorization in a Multi-Tenant App + +ABP permissions can be scoped with `MultiTenancySides`. + +That is important because host users and tenant users often should not even see the same capabilities. + +Example definition: + +```csharp +context.AddGroup(MyPermissions.GroupName) + .AddPermission( + MyPermissions.HostDashboard, + multiTenancySide: MultiTenancySides.Host + ) + .AddPermission( + MyPermissions.TenantDashboard, + multiTenancySide: MultiTenancySides.Tenant + ); +``` + +This is one of the easiest wins in ABP multi-tenancy. Use it early. + +### Why it matters in practice + +Without side-aware permission definitions: + +- host-only menus can appear in tenant UI +- tenant-only features can leak into host administration +- tests become confusing because behavior differs by login context + +Also remember that usernames can collide across tenants. That is normal in multi-tenant identity models. What matters is the combination of user identity and tenant context. + +## Migrations and Data Seeding Without Regret + +Multi-tenant EF Core migrations are straightforward in theory and messy in real systems if you skip the design phase. + +### Shared database + +This is simplest: + +- one database +- one main migration flow +- host and tenant data usually seeded into the same database with different contexts or `TenantId` semantics + +### Separate or hybrid databases + +Now you need to answer: + +- which context owns which schema +- which migration runs against host DB +- which migration runs against tenant DBs +- when new tenants receive schema updates +- how failed migrations are retried + +### Seeding strategy + +A practical model is: + +- seed host-level data in the host database +- seed tenant defaults when a tenant is created +- perform tenant seeding inside `CurrentTenant.Change(tenantId)` scopes where appropriate + +Example: + +```csharp +using (_currentTenant.Change(tenantId)) +{ + await _dataSeeder.SeedAsync(new DataSeedContext(tenantId)); +} +``` + +That keeps seeding logic tenant-aware and consistent with the rest of the application. + +## Common Pitfalls That Break Multi-Tenancy + +Most ABP multi-tenancy bugs are not framework bugs. They are design mistakes. + +### 1. Tenant-only entity accidentally allows host ownership + +If `TenantId` stays nullable for a strictly tenant-owned entity, host-side records can slip in. That often leads to confusing query behavior and data mixing. + +### 2. Custom SQL bypasses tenant filtering + +ABP filters repository and LINQ queries for `IMultiTenant` entities. Your raw SQL does not magically become safe. Always include tenant scope explicitly when writing custom SQL. + +### 3. Host-only modules end up in tenant databases + +This usually happens when all module mappings are copied into every `DbContext`. Be intentional about which modules are configured where. + +### 4. Resolver strategy is inconsistent + +For example: + +- frontend sends `tenant` +- backend expects `__tenant` +- API gateway injects a header +- auth claims still refer to a different tenant source + +You can spend hours debugging what is really just inconsistent tenant resolution. + +### 5. Subdomain authentication is not fully configured + +Wildcard domains, issuer validation, proxy headers, and cookie domains all need a coherent setup. Subdomain multi-tenancy is elegant, but only after it is fully wired. + +### 6. Shared database performance is ignored too long + +If every large table relies on `TenantId` filters, indexing and query shape matter. This usually becomes painful gradually, then suddenly. + +## When to Use Shared, Separate, or Hybrid + +### Use shared database when + +- you are building a standard SaaS product +- operational simplicity matters most +- tenants are relatively small +- strict physical isolation is not required +- you want the fastest path to production + +### Use separate databases when + +- customers require stronger isolation +- you need tenant-level backup and restore +- data volume varies significantly between tenants +- some tenants need independent scaling or maintenance windows +- compliance requirements push you there + +### Use hybrid when + +- most tenants fit shared infrastructure +- a few enterprise tenants need dedicated storage +- you want to defer cost while preserving an upgrade path +- your team can handle extra migration and operational complexity + +### When NOT to over-engineer it + +Do not start with hybrid just because it sounds flexible. + +If you are early-stage and do not yet have hard isolation requirements, shared database with good tenant discipline is usually the better engineering decision. + +## A Practical Implementation Plan + +If you want a sane rollout path, use this order: + +### Phase 1: Enable and model multi-tenancy + +- enable `AbpMultiTenancyOptions` +- implement `IMultiTenant` on tenant-owned entities +- review aggregate rules around nullable `TenantId` +- define host vs tenant permissions correctly + +### Phase 2: Pick one tenant resolution strategy + +- prefer subdomain or authenticated user claim for web apps +- keep request key resolution for APIs or development +- make frontend and backend tenant key configuration consistent + +### Phase 3: Start with shared database + +- launch with shared DB unless requirements force separation +- add indexes and monitoring early +- verify custom queries are tenant-safe + +### Phase 4: Prepare for separation only where needed + +- isolate `DbContext` boundaries cleanly +- separate host-only module configuration from tenant-only configuration +- design onboarding and migration workflows +- add support for per-tenant connection strings when the business actually needs it + +This path keeps your first release simple without blocking future isolation models. + +## Final Thoughts + +ABP Framework removes a lot of the repetitive work in multi-tenant .NET applications, but it does not remove architectural choices. You still need to decide how tenants are resolved, where data lives, which modules belong to which side, and how strict your isolation really needs to be. + +The best ABP multi-tenancy setups are usually boring in the right places: + +- one clear tenant resolution strategy +- strict entity rules +- explicit host vs tenant boundaries +- a simple default database model +- operational workflows designed before enterprise tenants arrive + +That is what keeps a multi-tenant system maintainable after the demo phase. + +## TL;DR + +- Enable ABP multi-tenancy explicitly, then model tenant-owned entities with `IMultiTenant` and disciplined `TenantId` rules. +- Start with a shared database unless you already need stronger isolation, compliance, or tenant-level scaling. +- Use `ICurrentTenant` and a clear tenant resolution strategy to keep application logic predictable. +- For separate or hybrid databases, control `DbContext` boundaries, module mappings, migrations, and onboarding workflows carefully. +- Define permissions with `MultiTenancySides` so host and tenant experiences stay clean and secure. \ No newline at end of file diff --git a/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/cover.png b/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/cover.png new file mode 100644 index 0000000000..b0e2b14a68 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/cover.png differ diff --git a/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/inline-1.png b/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/inline-1.png new file mode 100644 index 0000000000..b0be708000 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/inline-1.png differ diff --git a/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/inline-2.png b/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/inline-2.png new file mode 100644 index 0000000000..0483d13091 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/inline-2.png differ diff --git a/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/inline-3.png b/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/inline-3.png new file mode 100644 index 0000000000..6e12a4f4d8 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-22-how-to-implement-multitenancy-with-abp-framework/inline-3.png differ diff --git a/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/Post.md b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/Post.md new file mode 100644 index 0000000000..be87f21390 --- /dev/null +++ b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/Post.md @@ -0,0 +1,607 @@ +Background jobs are one of those features that look simple at first and become operationally important very quickly. Sending emails, generating reports, syncing with third-party APIs, cleaning expired data, and processing imports should not block your HTTP requests. + +ABP gives you a clean abstraction for background jobs, and Hangfire gives you a production-friendly execution engine with persistence, retries, queues, and a dashboard. The useful part is that you can keep your application code aligned with ABP’s abstractions while swapping in Hangfire as the actual runner. + +In this article, I’ll walk through how to implement background jobs with ABP and Hangfire, when to use each piece, and where teams usually get tripped up. + +## Why use Hangfire instead of ABP's default background job manager? + +ABP already has a built-in background job system, and it is perfectly fine for simple cases. But it helps to understand what you are trading. + +### ABP default job manager + +By default, ABP background jobs are: + +- Enqueued through `IBackgroundJobManager` +- Executed in-process +- FIFO-oriented +- Single-threaded by default +- Retried automatically with increasing delays +- Stored through ABP's background job store + +This is good when: + +- Your app is small or moderate in workload +- You want minimal setup +- You do not need a dashboard +- You do not need advanced queue management + +### Hangfire integration + +When you add `Volo.Abp.BackgroundJobs.HangFire`, ABP can keep the same `IBackgroundJobManager` programming model, but Hangfire becomes the execution backend. + +That gives you: + +- Durable job storage +- Better operational visibility through the Hangfire dashboard +- Multiple worker servers +- Queue-based processing +- Recurring jobs and scheduling features +- A mature retry and monitoring model + +In practice, Hangfire is the better choice when background processing is part of the actual system design, not just a convenience. + +### Quick comparison + +Use ABP default when: + +- You want the simplest possible setup +- Background jobs are low volume +- A single app instance is enough +- You do not need a dashboard or queue controls + +Use Hangfire when: + +- You need reliability across restarts +- You run multiple instances +- You need recurring jobs or queue isolation +- You want to inspect failures and retries visually +- Background processing is operationally important + + + +![Generated illustration](inline-1.png) + +## Defining a background job in ABP + +The nice part of ABP is that your job code does not need to know about Hangfire. + +Start with a job arguments class: + +```csharp +public class EmailSendingArgs +{ + public string To { get; set; } = string.Empty; + public string Subject { get; set; } = string.Empty; + public string Body { get; set; } = string.Empty; +} +``` + +Then create the job itself: + +```csharp +using System.Threading.Tasks; +using Volo.Abp.BackgroundJobs; +using Volo.Abp.DependencyInjection; + +public class EmailSendingJob : AsyncBackgroundJob, ITransientDependency +{ + private readonly IEmailSender _emailSender; + + public EmailSendingJob(IEmailSender emailSender) + { + _emailSender = emailSender; + } + + public override async Task ExecuteAsync(EmailSendingArgs args) + { + await _emailSender.SendAsync( + args.To, + args.Subject, + args.Body + ); + } +} +``` + +This job works with ABP’s job abstraction regardless of whether the runtime backend is the default implementation or Hangfire. + +To enqueue it: + +```csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.BackgroundJobs; + +public class NotificationAppService : ApplicationService +{ + private readonly IBackgroundJobManager _backgroundJobManager; + + public NotificationAppService(IBackgroundJobManager backgroundJobManager) + { + _backgroundJobManager = backgroundJobManager; + } + + public async Task QueueWelcomeEmailAsync(string email) + { + await _backgroundJobManager.EnqueueAsync( + new EmailSendingArgs + { + To = email, + Subject = "Welcome", + Body = "Your account is ready." + }, + priority: BackgroundJobPriority.Normal, + delay: TimeSpan.FromMinutes(1) + ); + } +} +``` + +A few practical notes: + +- `delay` is useful for short deferrals and back-office workflows. +- `priority` is part of ABP’s abstraction. How it maps operationally depends on the provider. +- Keep argument objects small and serializable. +- Do not pass EF entities or large object graphs into jobs. + +## Setting up Hangfire in an ABP application + +To integrate Hangfire, install the package and wire it into your ABP module. + +### 1. Add the package + +Using ABP CLI: + +```bash +abp add-package Volo.Abp.BackgroundJobs.HangFire +``` + +Or with NuGet: + +```bash +Install-Package Volo.Abp.BackgroundJobs.HangFire +``` + +### 2. Add the module dependency + +Typically this goes into your host module, such as `HttpApiHostModule`: + +```csharp +using Volo.Abp.BackgroundJobs.Hangfire; + +[DependsOn( + typeof(AbpBackgroundJobsHangfireModule) +)] +public class MyProjectHttpApiHostModule : AbpModule +{ +} +``` + +### 3. Configure Hangfire services + +In `ConfigureServices`: + +```csharp +using Hangfire; +using Microsoft.Extensions.Configuration; + +public override void ConfigureServices(ServiceConfigurationContext context) +{ + var configuration = context.Services.GetConfiguration(); + + context.Services.AddHangfire(config => + { + config.UseSqlServerStorage( + configuration.GetConnectionString("Default") + ); + }); +} +``` + +If you use PostgreSQL, Redis, or another Hangfire storage provider, configure that instead. The storage decision matters because all servers that process jobs must share the same backing store. + +### 4. Enable the Hangfire dashboard + +In `OnApplicationInitialization`: + +```csharp +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +public override void OnApplicationInitialization(ApplicationInitializationContext context) +{ + var app = context.GetApplicationBuilder(); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.UseAbpHangfireDashboard(); +} +``` + +The dashboard middleware should be added after authentication and authorization middleware. + +At this point, jobs enqueued through `IBackgroundJobManager` should use Hangfire as long as the integration is correctly activated. + + + +![Generated illustration](inline-2.png) + +## End-to-end example: offloading a report export + +A common use case is exporting a report that may take several seconds or minutes. + +Instead of generating the file during the HTTP request: + +- Save an export request record +- Enqueue a background job +- Let the job generate the file +- Notify the user when it is ready + +### Arguments + +```csharp +public class ReportExportJobArgs +{ + public Guid ExportRequestId { get; set; } + public Guid UserId { get; set; } +} +``` + +### Job implementation + +```csharp +using System.Threading.Tasks; +using Volo.Abp.BackgroundJobs; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Uow; + +public class ReportExportJob : AsyncBackgroundJob, ITransientDependency +{ + private readonly IReportExportAppService _reportExportAppService; + + public ReportExportJob(IReportExportAppService reportExportAppService) + { + _reportExportAppService = reportExportAppService; + } + + public override async Task ExecuteAsync(ReportExportJobArgs args) + { + await _reportExportAppService.GenerateAsync(args.ExportRequestId, args.UserId); + } +} +``` + +### Enqueue from app service + +```csharp +public async Task RequestExportAsync() +{ + var exportRequestId = GuidGenerator.Create(); + + await _backgroundJobManager.EnqueueAsync( + new ReportExportJobArgs + { + ExportRequestId = exportRequestId, + UserId = CurrentUser.GetId() + } + ); + + return exportRequestId; +} +``` + +This pattern scales much better than holding open a web request while doing CPU-heavy or IO-heavy work. + +## Retries, exceptions, and cancellation + +ABP and Hangfire both care about retries, but you should still design jobs carefully. + +### How ABP behaves + +With ABP background jobs: + +- Unhandled exceptions trigger retries +- Retry intervals increase over time +- Default implementation uses exponential backoff behavior +- Jobs may eventually time out or be marked abandoned depending on configuration + +### What this means for your code + +A job should be: + +- Idempotent whenever possible +- Safe to retry +- Explicit about transient vs permanent failures + +For example, sending the same payment capture twice is dangerous. Sending the same “your report is ready” notification twice is annoying but manageable. Design around the difference. + +### Cancellation handling + +If you use `ICancellationTokenProvider`, be deliberate. If cancellation means “try again later,” let the exception flow. If cancellation means “stop and do not retry,” return gracefully. + +Example: + +```csharp +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.BackgroundJobs; +using Volo.Abp.Threading; + +public class DataSyncJob : AsyncBackgroundJob +{ + private readonly ICancellationTokenProvider _cancellationTokenProvider; + + public DataSyncJob(ICancellationTokenProvider cancellationTokenProvider) + { + _cancellationTokenProvider = cancellationTokenProvider; + } + + public override async Task ExecuteAsync(int args) + { + var cancellationToken = _cancellationTokenProvider.Token; + + cancellationToken.ThrowIfCancellationRequested(); + + await Task.Delay(500, cancellationToken); + } +} +``` + +### Practical guidance + +- Keep jobs short and composable +- Persist progress if the job is large +- Use domain/application services inside jobs instead of putting business logic directly into the job class +- Log enough context to diagnose retries and failures + +## Recurring jobs and periodic work + +Not every background task is a one-time job. + +There are two different patterns: + +- Background jobs: one-off, delayed, or fire-and-forget work +- Background workers: periodic or recurring work + +In ABP, recurring processing is usually modeled with background workers rather than standard background jobs. + +### When to use a worker instead of a job + +Use a worker when you need: + +- A scheduled cleanup task +- A recurring sync with another system +- Polling behavior +- A cron-like schedule + +### Hangfire-backed recurring worker + +With Hangfire integration, you can derive from `HangfireBackgroundWorkerBase` and provide a cron expression. + +```csharp +using System.Threading.Tasks; +using Volo.Abp.BackgroundWorkers.Hangfire; + +public class ExpiredSessionsCleanupWorker : HangfireBackgroundWorkerBase +{ + private readonly ISessionCleanupService _sessionCleanupService; + + public ExpiredSessionsCleanupWorker(ISessionCleanupService sessionCleanupService) + { + _sessionCleanupService = sessionCleanupService; + + RecurringJobId = "expired-sessions-cleanup"; + CronExpression = "0 * * * *"; + } + + public override async Task DoWorkAsync() + { + await _sessionCleanupService.CleanupAsync(); + } +} +``` + +A few details matter here: + +- `RecurringJobId` should be stable and unique. +- `CronExpression` controls the schedule. +- Hangfire recurring scheduling is minute-based in normal use, so do not expect second-level precision. + +### Background jobs vs background workers + +A simple rule: + +- If a user action creates work to do later, use a background job. +- If the system itself needs to run something on a schedule, use a background worker. + + + +![Generated illustration](inline-3.png) + +## Queue isolation and scaling across multiple instances + +Once you run more than one application instance, background processing becomes an architecture concern rather than a coding detail. + +### Shared storage is required + +If multiple nodes are going to process Hangfire jobs, they must share the same Hangfire storage. + +Typical setups include: + +- Multiple web instances + one shared SQL Server storage +- Web instances enqueueing jobs + dedicated worker instances processing them +- Separate deployment slots or services sharing the same Hangfire backend + +### Disabling execution on some nodes + +Sometimes you want your web app to enqueue jobs but not execute them. + +ABP supports this: + +```csharp +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.BackgroundJobs; + +public override void ConfigureServices(ServiceConfigurationContext context) +{ + Configure(options => + { + options.IsJobExecutionEnabled = false; + }); +} +``` + +This is useful when: + +- You run dedicated worker processes +- You want predictable resource allocation +- You do not want front-end nodes competing for background work + +### Queue prefixing in clustered environments + +If multiple applications share the same Hangfire storage, isolate queues intentionally. + +For Hangfire integration in ABP, use `AbpHangfireOptions.DefaultQueuePrefix` to avoid queue collisions between different applications or environments. + +That matters more than teams expect. Without isolation, staging and production can end up looking at the same queues if storage is misconfigured. + +### Queue routing + +Hangfire supports multiple queues, and ABP’s Hangfire integration can route jobs based on conventions or attributes. + +In some scenarios, you may want specific jobs to go to specific queues, for example: + +- `emails` +- `exports` +- `integration` +- `critical` + +This is especially helpful when one queue can become noisy and starve more important work. + + + +![Generated illustration](inline-4.png) + +## Securing the Hangfire dashboard + +The Hangfire dashboard is extremely useful, but it is also an operations surface. Do not expose it casually. + +ABP provides authorization support for the dashboard via `AbpHangfireAuthorizationFilter`. + +A typical setup is to: + +- Require authentication +- Restrict by permission or role +- Optionally consider tenant-specific access rules + +Example: + +```csharp +app.UseAbpHangfireDashboard("/hangfire", new DashboardOptions +{ + Authorization = new[] + { + new AbpHangfireAuthorizationFilter(requiredPermissionName: "Administration.Hangfire") + } +}); +``` + +Even if your app is internal, treat the dashboard like an admin area: + +- Put it behind authorization +- Avoid exposing it publicly without network restrictions +- Audit who can retry or inspect jobs + +## Common pitfalls and behavior differences + +This is the part that usually saves the most time. + +### 1. Jobs still land in `AbpBackgroundJob` instead of Hangfire + +If Hangfire is not properly activated, ABP may continue using its native background job storage and you will see jobs in the `AbpBackgroundJob` table instead of Hangfire storage. + +Check these first: + +- The `Volo.Abp.BackgroundJobs.HangFire` package is installed +- `AbpBackgroundJobsHangfireModule` is added in `[DependsOn]` +- `AddHangfire(...)` is configured correctly +- The application starts with the expected module graph + +If any of those are missing, you may think you are using Hangfire while you are actually still on the default provider. + +### 2. Passing large or complex objects into jobs + +Keep job args small. Prefer identifiers over rich objects. + +Good: + +- `OrderId` +- `UserId` +- `ExportRequestId` + +Bad: + +- Full EF entities +- Large DTO graphs +- Objects with lazy-loading behavior or runtime-only state + +### 3. Non-idempotent job logic + +Retries will happen. If running the same job twice can corrupt data, redesign the workflow. + +Common fixes: + +- Add a processed flag +- Use unique constraints where appropriate +- Check prior execution status before applying side effects +- Make external calls with idempotency keys when supported + +### 4. Assuming recurring jobs run with exact timing + +Hangfire recurring jobs are cron-based and typically evaluated on minute boundaries. That is fine for most scheduled business work, but it is not a real-time scheduler. + +### 5. Ignoring queue isolation in multi-app environments + +If several apps share one Hangfire store, queue naming and prefixing must be explicit. Otherwise, one application can accidentally process another application's jobs. + +## When to use / When NOT to use ABP + Hangfire + +### Use ABP + Hangfire when + +- You want ABP-friendly job abstractions with a stronger execution backend +- You need operational visibility and retry inspection +- You run multiple instances or worker nodes +- You have recurring background tasks +- Your jobs are part of business-critical workflows + +### Do NOT use it when + +- The work must complete synchronously before responding to the user +- The task is so trivial that plain in-memory processing is enough +- You need event streaming rather than job scheduling +- You need ultra-low-latency real-time processing with very tight timing guarantees + +For many line-of-business systems, ABP + Hangfire hits a very practical middle ground: easy enough to implement, strong enough to operate. + +## A production-minded implementation checklist + +Before shipping, verify these points: + +- Jobs are enqueued through `IBackgroundJobManager` unless you explicitly need Hangfire-specific APIs +- Job arguments are small and serializable +- Job logic is retry-safe and preferably idempotent +- Hangfire storage is shared by all processing nodes +- Dashboard access is restricted +- Queue names or prefixes are isolated per app/environment +- Long-running jobs are split into manageable steps where possible +- You know which nodes execute jobs and which only enqueue them + +## TL;DR + +- ABP gives you a clean background job abstraction; Hangfire gives you the production-grade execution engine. +- Keep using `IBackgroundJobManager` for most jobs so your application code stays provider-independent. +- Use background jobs for one-off work and Hangfire-backed background workers for recurring tasks. +- In multi-instance deployments, shared storage, queue isolation, and dashboard security are not optional. +- If jobs still go to `AbpBackgroundJob`, your Hangfire integration is probably not fully activated. \ No newline at end of file diff --git a/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/cover.png b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/cover.png new file mode 100644 index 0000000000..933dd3399c Binary files /dev/null and b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/cover.png differ diff --git a/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-1.png b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-1.png new file mode 100644 index 0000000000..62cec256e6 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-1.png differ diff --git a/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-2.png b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-2.png new file mode 100644 index 0000000000..1d04df4a76 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-2.png differ diff --git a/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-3.png b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-3.png new file mode 100644 index 0000000000..a22d1c5d24 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-3.png differ diff --git a/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-4.png b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-4.png new file mode 100644 index 0000000000..04444db117 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-22-implementing-background-jobs-with-abp-and-hangfire/inline-4.png differ diff --git a/docs/en/Community-Articles/2026-06-23-deep-dive-index/Post.md b/docs/en/Community-Articles/2026-06-23-deep-dive-index/Post.md new file mode 100644 index 0000000000..b3672e120c --- /dev/null +++ b/docs/en/Community-Articles/2026-06-23-deep-dive-index/Post.md @@ -0,0 +1,41 @@ +# Deep Dive on ABP AI Agent: The Complete Series + +ABP Studio is a development platform built around ABP Framework. With the introduction of **ABP AI Coding Agent**, it became something more: a platform where an AI agent works inside the same environment you already use to build, run, monitor, and ship ABP solutions. + +![ABP Studio with ABP AI Agent](abp-studio-new-design.png) + +General-purpose AI coding tools are excellent for horizontal, file-shaped work. They read source files, edit them, and run shell commands. But ABP solutions are **system-shaped**, not just file-shaped. A typical ABP solution is split across multiple modules and layers with strict dependency rules, composed of many runnable units (HTTP services, gateways, identity servers, background workers, Docker containers), and built on a strong set of conventions: aggregate roots, repositories, application services, DTOs, permissions, localization, event bus, distributed cache, and background jobs. + +A generic agent has none of that vocabulary. It does not know what a module is, which project is the Domain layer, or that an `ApplicationService` should not depend on a `DbContext` directly. It cannot start your microservices, gateway, and auth server together. It cannot tell you that the latest edit caused a runtime exception in the Identity service, because it has no concept of a running application. + +ABP AI Coding Agent was built to close exactly that gap. The agent is born inside a platform that already understands modules, run profiles, builds, migrations, proxies, Docker containers, monitoring, and Git workflows, and it uses every one of them. + +We wrote a **nine-part deep dive series** to explain how each part of this system works, not as a product tour, but as a practical look at the decisions, controls, and architecture behind the experience. + +## The Series + +1. **[Agent, Plan and Ask Modes](https://abp.io/community/articles/deep-dive-on-abp-ai-agent-1-agent-plan-and-ask-modes-62wteg9t)** — The three interaction modes that control how much action the agent is allowed to take: **Ask** for understanding (read-only), **Plan** for designing the approach before editing, and **Agent** for full implementation with builds, tools, and iteration. + +2. **[Supported AI Models + Usage Recommendations](https://abp.io/community/articles/deep-dive-on-abp-ai-agent-2-supported-ai-models-in-abp-3krbc7yc)** — How ABP Studio separates models by role (main, research, browser, text processor, Git review) and why treating model selection as a practical decision based on the task leads to a better balance of capability, speed, and cost. + +3. **[Rules, Skills and Lessons](https://abp.io/community/articles/deep-dive-on-abp-ai-agent-3-rules-skills-and-lessons-ai6kxubt)** — The three mechanisms that give the agent solution-specific memory: **Rules** (always-on conventions), **Skills** (on-demand procedures), and **Lessons** (corrections the agent records and carries forward). + +4. **[Integrated ABP Studio Tools](https://abp.io/community/articles/deep-dive-on-abp-ai-agent-4-integrated-abp-studio-tools-be2xa2om)** — The tools that connect the agent to ABP Studio's runtime environment: monitoring (exceptions, logs, requests), applications, containers, tasks, and build actions, with a practical walkthrough showing the difference between debugging with and without tool access. + +5. **[MCP (Model Context Protocol)](https://abp.io/community/articles/deep-dive-on-abp-ai-agent-5-mcp-model-context-protocol-trb9o4ev)** — How MCP extends the agent beyond the solution boundary to reach external systems like Prometheus, SEO analyzers, or documentation services, with per-tool enable/disable controls and stdio/HTTP server support. + +6. **[ABP Studio Git Integration](https://abp.io/community/articles/deep-dive-on-abp-ai-agent-6-abp-studio-git-integration-09tr41ec)** — The full Git loop inside ABP Studio: branching, diffing, AI-generated commit messages, AI code review on staged changes, GitHub issue context for starting tasks, and pull request feedback for addressing reviewer comments. + +7. **[Scopes](https://abp.io/community/articles/deep-dive-on-abp-ai-agent-7-scopes-tfqtkdzu)** — How AI Scopes restrict the agent's working area to specific modules, packages, or solution areas, reducing unrelated exploration, preventing accidental edits, and making diffs easier to review. + +8. **[Parallel Agent Execution](https://abp.io/community/articles/deep-dive-on-abp-ai-agent-8-parallel-agent-execution-1o0cik6g)** — Running multiple agent sessions at the same time, each with its own mode, model, scope, and workflow, plus read-only research subagents that fan out inside a single session. + +9. **[Workflows](https://abp.io/community/articles/deep-dive-on-abp-ai-agent-9-workflows-7jo1adb1)** — Repeatable before/after steps that wrap agent runs: start containers, build packages, add migrations, generate proxies, restart applications, and run validation tasks, so the agent focuses on the code change while the platform handles the deterministic parts. + +## The Bigger Picture + +Each article focuses on one feature, but the real value comes from how they work together. + +Modes decide how much action the agent takes. Models decide which brain handles the work. Rules, Skills, and Lessons shape what the agent knows. Tools and MCP extend what it can reach. Scopes define where it can work. Workflows define what happens around the work. Git Integration makes the result reviewable and recoverable. Parallel Execution lets multiple tasks move forward at the same time. + +That is the ABP AI Coding Agent experience: **not a single AI button, but a set of controls built into a platform that already understands how ABP solutions are developed, run, and maintained.** diff --git a/docs/en/Community-Articles/2026-06-23-deep-dive-index/abp-studio-new-design.png b/docs/en/Community-Articles/2026-06-23-deep-dive-index/abp-studio-new-design.png new file mode 100644 index 0000000000..d74add4b2d Binary files /dev/null and b/docs/en/Community-Articles/2026-06-23-deep-dive-index/abp-studio-new-design.png differ diff --git a/docs/en/Community-Articles/2026-06-23-deep-dive-index/cover.png b/docs/en/Community-Articles/2026-06-23-deep-dive-index/cover.png new file mode 100644 index 0000000000..9d0d5f5987 Binary files /dev/null and b/docs/en/Community-Articles/2026-06-23-deep-dive-index/cover.png differ diff --git a/docs/en/framework/infrastructure/object-to-object-mapping.md b/docs/en/framework/infrastructure/object-to-object-mapping.md index 5dcf526266..d8484a277e 100644 --- a/docs/en/framework/infrastructure/object-to-object-mapping.md +++ b/docs/en/framework/infrastructure/object-to-object-mapping.md @@ -254,8 +254,8 @@ public partial class UserToUserDtoMapper : TwoWayMapperBase public override partial UserDto Map(User source); public override partial void Map(User source, UserDto destination); - public override partial User ReverseMap(UserDto destination); - public override partial void ReverseMap(UserDto destination, User source); + public override partial User ReverseMap(UserDto source); + public override partial void ReverseMap(UserDto source, User destination); } ```` @@ -280,15 +280,15 @@ public partial class UserToUserDtoMapper : TwoWayMapperBase //TODO: Perform actions after the mapping } - public override partial User ReverseMap(UserDto destination); - public override partial void ReverseMap(UserDto destination, User source); + public override partial User ReverseMap(UserDto source); + public override partial void ReverseMap(UserDto source, User destination); - public override partial void BeforeReverseMap(UserDto destination) + public override partial void BeforeReverseMap(UserDto source) { //TODO: Perform actions before the reverse mapping } - public override partial void AfterReverseMap(UserDto destination, User source) + public override partial void AfterReverseMap(UserDto source, User destination) { //TODO: Perform actions after the reverse mapping } diff --git a/docs/en/others/penetration-test-report.md b/docs/en/others/penetration-test-report.md index 8ca58a57c9..beee9caf04 100644 --- a/docs/en/others/penetration-test-report.md +++ b/docs/en/others/penetration-test-report.md @@ -7,7 +7,7 @@ # ABP Penetration Test Report -The ABP Commercial MVC `v10.4.0` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was scanned on a local HTTPS address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document. +The ABP Commercial MVC `v10.4.1` application template has been tested against security vulnerabilities by the [OWASP ZAP v2.14.0](https://www.zaproxy.org/) tool. The demo web application was scanned on a local HTTPS address. The below alerts have been reported by the pentest tool. These alerts are sorted by the risk level as high, medium, and low. The informational alerts are not mentioned in this document. Many of these alerts are **false-positive**, meaning the vulnerability scanner detected these issues, but they are not exploitable. It's clearly explained for each false-positive alert why this alert is a false-positive. diff --git a/docs/en/package-version-changes.md b/docs/en/package-version-changes.md index 3bdaf00a17..8049ffa986 100644 --- a/docs/en/package-version-changes.md +++ b/docs/en/package-version-changes.md @@ -7,6 +7,15 @@ # Package Version Changes +## 10.5.0-rc.4 + +| Package | Old Version | New Version | PR | +|---------|-------------|-------------|-----| +| Blazorise | 2.1.3 | 2.2.1 | #25683 | +| Blazorise.Components | 2.1.3 | 2.2.1 | #25683 | +| Blazorise.DataGrid | 2.1.3 | 2.2.1 | #25683 | +| Blazorise.Snackbar | 2.1.3 | 2.2.1 | #25683 | + ## 10.5.0-rc.1 | Package | Old Version | New Version | PR | diff --git a/docs/en/release-info/migration-guides/AutoMapper-To-Mapperly.md b/docs/en/release-info/migration-guides/AutoMapper-To-Mapperly.md index f19449d04f..ddae40e1d0 100644 --- a/docs/en/release-info/migration-guides/AutoMapper-To-Mapperly.md +++ b/docs/en/release-info/migration-guides/AutoMapper-To-Mapperly.md @@ -95,8 +95,8 @@ public partial class OrganizationUnitRoleToOrganizationUnitRoleDtoMapper : TwoWa public override partial OrganizationUnitRoleDto Map(OrganizationUnitRole source); public override partial void Map(OrganizationUnitRole source, OrganizationUnitRoleDto destination); - public override partial OrganizationUnitRole ReverseMap(OrganizationUnitRoleDto destination); - public override partial void ReverseMap(OrganizationUnitRoleDto destination, OrganizationUnitRole source); + public override partial OrganizationUnitRole ReverseMap(OrganizationUnitRoleDto source); + public override partial void ReverseMap(OrganizationUnitRoleDto source, OrganizationUnitRole destination); } [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] @@ -306,8 +306,8 @@ public partial class OrganizationUnitRoleToOrganizationUnitRoleDtoMapper : TwoWa public override partial OrganizationUnitRoleDto Map(OrganizationUnitRole source); public override partial void Map(OrganizationUnitRole source, OrganizationUnitRoleDto destination); - public override partial OrganizationUnitRole ReverseMap(OrganizationUnitRoleDto destination); - public override partial void ReverseMap(OrganizationUnitRoleDto destination, OrganizationUnitRole source); + public override partial OrganizationUnitRole ReverseMap(OrganizationUnitRoleDto source); + public override partial void ReverseMap(OrganizationUnitRoleDto source, OrganizationUnitRole destination); } [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] diff --git a/docs/en/solution-templates/layered-web-application/deployment/deployment-iis.md b/docs/en/solution-templates/layered-web-application/deployment/deployment-iis.md index f84b221bb0..047f8bb079 100644 --- a/docs/en/solution-templates/layered-web-application/deployment/deployment-iis.md +++ b/docs/en/solution-templates/layered-web-application/deployment/deployment-iis.md @@ -134,7 +134,7 @@ You can execute this command in your command prompt. ````bash cd Desktop # or another path -mkcert -pkcs12 auth.sample api.sample web.sample # Replace with your domain names +mkcert -pkcs12 {{ if Tiered == "Yes" }}auth.sample api.sample web.sample{{ else if UI == "NG" || UI == "Blazor" }}www.sample app.sample{{ else }}www.sample{{ end }} # Replace with your domain names ```` Rename the created file extension to ".pfx" @@ -147,19 +147,33 @@ Import the certificate to IIS Add domain names to hosts file(in Windows: `C:\Windows\System32\drivers\etc\hosts`, in Linux and macOS: `/etc/hosts`). +{{ if Tiered == "Yes" }} + > For example, in a tiered MVC project. + ````json 127.0.0.1 auth.sample 127.0.0.1 api.sample 127.0.0.1 web.sample ```` +{{ else }} + +````json +127.0.0.1 www.sample{{ if UI == "NG" || UI == "Blazor" }} +127.0.0.1 app.sample{{ end }} +```` + +{{ end }} + ## Publish the Application(s) On IIS ### Update the appsettings Update the appsettings according to your project type and domain names. +{{ if Tiered == "Yes" }} + > For example, in a tiered MVC project. ````json @@ -237,6 +251,37 @@ Update the appsettings according to your project type and domain names. } ```` +{{ else }} + +> In a non-tiered solution there is a single application that also acts as the authentication server, so `App:SelfUrl` and `AuthServer:Authority` point to the same URL. + +````json +//{{ if UI == "NG" || UI == "Blazor" }}HttpApi.Host{{ else if UI == "BlazorServer" }}Blazor{{ else }}Web{{ end }} +{ + "App": { + "SelfUrl": "https://www.sample",{{ if UI == "NG" || UI == "Blazor" }} + "CorsOrigins": "https://app.sample", + "RedirectAllowedUrls": "https://app.sample",{{ else if UI == "BlazorServer" }} + "RedirectAllowedUrls": "https://www.sample",{{ end }} + "DisablePII": "false"{{ if UI == "NG" || UI == "Blazor" }}, + "HealthCheckUrl": "/health-status"{{ end }} + }, + "ConnectionStrings": { + "Default": "Server=volo.sample;Database=Sample;User Id=sa;Password=1q2w3E**;TrustServerCertificate=true" + }, + "AuthServer": { + "Authority": "https://www.sample", + "RequireHttpsMetadata": "true"{{ if UI == "NG" || UI == "Blazor" }}, + "SwaggerClientId": "Sample_Swagger"{{ end }} + }, + "StringEncryption": { + "DefaultPassPhrase": "f9uRkTLdtAZLmlh3" + } +} +```` + +{{ end }} + ### Copy the .pfx file You need to copy pfx file from ./src/{{ if Tiered == "Yes" }}AuthServer{{ else if UI == "NG" || UI == "Blazor" }}HttpApi.Host{{ else if UI == "BlazorServer" }}Blazor{{ else }}Web{{ end }} to ./publish/{{ if Tiered == "Yes" }}authserver{{ else if UI == "NG" || UI == "Blazor" }}apihost{{ else if UI == "BlazorServer" }}blazor{{ else }}web{{ end }} folder. diff --git a/docs/en/studio/release-notes.md b/docs/en/studio/release-notes.md index f6c9e276f2..e5c9a1f474 100644 --- a/docs/en/studio/release-notes.md +++ b/docs/en/studio/release-notes.md @@ -9,7 +9,25 @@ This document contains **brief release notes** for each ABP Studio release. Release notes only include **major features** and **visible enhancements**. Therefore, they don't include all the development done in the related version. -## 3.0.4 (2026-06-03) Latest +## 3.0.6 (2026-06-18) Latest + +* Stabilize Domain.Tests across platforms and fix ReactNative test regression +* [Linux-x64 Support] Align Linux publish with RID-based packaging +* Fix GitHub integration problem on production caused +* Use deferred UI scheduler and marshal CEF updates + +## 3.0.5 (2026-06-17) + +* Set `DOTNET_INSTALL_DIR` on self-hosted runners in template build workflow +* Fix mudblazor template delete conditions for dashboard pages +* Replace modern template AppUser bulk seed backfill with admin bootstrap +* Fix login button URL in app-nolayers Blazor.WebAssembly template +* Improve `#blazor-error-ui` visibility on Blazor templates +* Remove leftover `github-mud-test` source and npm rc patch from templates +* Improve modern React template error handling +* Remove sample CRUD from modern microservice template + +## 3.0.4 (2026-06-03) * Blazor UI Selector: Added a built-in choice between MudBlazor and Blazorise for Blazor solution templates * React Native Updates: Upgraded Expo to the latest version, improved keyboard handling, and ensured UI consistency diff --git a/docs/en/studio/version-mapping.md b/docs/en/studio/version-mapping.md index d254004d4f..443c2a2ee5 100644 --- a/docs/en/studio/version-mapping.md +++ b/docs/en/studio/version-mapping.md @@ -11,7 +11,7 @@ This document provides a general overview of the relationship between various ve | **ABP Studio Version** | **ABP Version of Startup Template** | |------------------------|---------------------------| -| 3.0.4 | 10.4.1 | +| 3.0.4 - 3.0.6 | 10.4.1 | | 3.0.3 | 10.4.0 | | 2.2.7 - 3.0.2 | 10.3.0 | | 2.2.5 - 2.2.6 | 10.2.0 | diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index 9da7e7454f..0f80fe219d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Antiforgery; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Filters; @@ -216,6 +217,27 @@ public class AbpAspNetCoreMvcModule : AbpModule }); ConfigureRouteBasedCulture(context); + DecorateAntiforgery(context); + } + + protected virtual void DecorateAntiforgery(ServiceConfigurationContext context) + { + // Wrap the registered IAntiforgery (DefaultAntiforgery from AddAntiforgery by default) with + // AbpAntiforgery so every antiforgery entry point goes through the claim normalization. + // Only an implementation-type registration is wrapped; a custom factory/instance registration of + // IAntiforgery is left as-is. + var descriptor = context.Services.LastOrDefault(d => d.ServiceType == typeof(IAntiforgery)); + if (descriptor?.ImplementationType == null) + { + return; + } + + context.Services.Replace(ServiceDescriptor.Describe( + typeof(IAntiforgery), + sp => new AbpAntiforgery( + (IAntiforgery)ActivatorUtilities.CreateInstance(sp, descriptor.ImplementationType), + sp.GetRequiredService>()), + descriptor.Lifetime)); } protected virtual void ConfigureRouteBasedCulture(ServiceConfigurationContext context) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiForgeryClaimsPrincipalNormalizer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiForgeryClaimsPrincipalNormalizer.cs new file mode 100644 index 0000000000..9275737245 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiForgeryClaimsPrincipalNormalizer.cs @@ -0,0 +1,66 @@ +using System; +using System.Linq; +using System.Security.Claims; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Security.Claims; + +namespace Volo.Abp.AspNetCore.Mvc.AntiForgery; + +public class AbpAntiForgeryClaimsPrincipalNormalizer : IAbpAntiForgeryClaimsPrincipalNormalizer, ITransientDependency +{ + public const string UserIdClaimIssuer = "AbpAntiForgery"; + + protected virtual string NormalizedIssuer => UserIdClaimIssuer; + + public virtual ClaimsPrincipal Normalize(ClaimsPrincipal principal) + { + var normalized = new ClaimsPrincipal(); + + foreach (var identity in principal.Identities) + { + normalized.AddIdentity(NormalizeIdentity(identity)); + } + + return normalized; + } + + protected virtual ClaimsIdentity NormalizeIdentity(ClaimsIdentity identity) + { + return new ClaimsIdentity( + identity.Claims.Select(NormalizeClaim), + identity.AuthenticationType, + identity.NameClaimType, + identity.RoleClaimType) + { + Actor = identity.Actor, + BootstrapContext = identity.BootstrapContext, + Label = identity.Label + }; + } + + protected virtual Claim NormalizeClaim(Claim claim) + { + var newClaim = new Claim( + claim.Type, + claim.Value, + claim.ValueType, + IsUserIdentifierClaim(claim.Type) ? NormalizedIssuer : claim.Issuer, + claim.OriginalIssuer); + + foreach (var property in claim.Properties) + { + newClaim.Properties[property.Key] = property.Value; + } + + return newClaim; + } + + // The claim types DefaultClaimUidExtractor inspects, in priority order, to build the antiforgery user id. + protected virtual bool IsUserIdentifierClaim(string claimType) + { + return string.Equals(claimType, AbpClaimTypes.UserId, StringComparison.Ordinal) || + string.Equals(claimType, "sub", StringComparison.Ordinal) || + string.Equals(claimType, ClaimTypes.NameIdentifier, StringComparison.Ordinal) || + string.Equals(claimType, ClaimTypes.Upn, StringComparison.Ordinal); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiForgeryOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiForgeryOptions.cs index 0c2cf8884c..f57a38d57a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiForgeryOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiForgeryOptions.cs @@ -24,6 +24,13 @@ public class AbpAntiForgeryOptions /// public bool AutoValidate { get; set; } = true; + /// + /// Normalizes the user id claim issuer before generating/validating the antiforgery token, so the + /// same user produces the same token identifier under both cookie and bearer authentication. + /// Default value: true. + /// + public bool NormalizeUserIdClaimIssuer { get; set; } = true; + /// /// A predicate to filter types to auto-validate. /// Return true to select the type to validate. diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiforgery.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiforgery.cs new file mode 100644 index 0000000000..22c4900379 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiforgery.cs @@ -0,0 +1,101 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Antiforgery; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Volo.Abp.AspNetCore.Mvc.AntiForgery; + +// Wraps the framework IAntiforgery so the antiforgery token's per-user identifier is computed against a +// normalized principal on every entry point (generation and validation, controllers and Razor Pages, +// ABP and built-in filters, cookie and bearer). This keeps the same user consistent across schemes whose +// user id claim carries a different issuer (e.g. "LOCAL AUTHORITY" for the Identity cookie vs. the token +// authority for a validated JWT or an OIDC cookie). +public class AbpAntiforgery : IAntiforgery +{ + protected IAntiforgery Inner { get; } + + protected AbpAntiForgeryOptions Options { get; } + + public AbpAntiforgery( + IAntiforgery inner, + IOptions options) + { + Inner = inner; + Options = options.Value; + } + + public virtual AntiforgeryTokenSet GetAndStoreTokens(HttpContext httpContext) + { + return WithNormalizedUser(httpContext, () => Inner.GetAndStoreTokens(httpContext)); + } + + public virtual AntiforgeryTokenSet GetTokens(HttpContext httpContext) + { + return WithNormalizedUser(httpContext, () => Inner.GetTokens(httpContext)); + } + + public virtual Task IsRequestValidAsync(HttpContext httpContext) + { + return WithNormalizedUserAsync(httpContext, () => Inner.IsRequestValidAsync(httpContext)); + } + + public virtual Task ValidateRequestAsync(HttpContext httpContext) + { + return WithNormalizedUserAsync(httpContext, async () => + { + await Inner.ValidateRequestAsync(httpContext); + return true; + }); + } + + public virtual void SetCookieTokenAndHeader(HttpContext httpContext) + { + WithNormalizedUser(httpContext, () => + { + Inner.SetCookieTokenAndHeader(httpContext); + return true; + }); + } + + protected virtual T WithNormalizedUser(HttpContext httpContext, Func action) + { + if (!Options.NormalizeUserIdClaimIssuer) + { + return action(); + } + + var normalizer = httpContext.RequestServices.GetRequiredService(); + var originalPrincipal = httpContext.User; + httpContext.User = normalizer.Normalize(originalPrincipal); + try + { + return action(); + } + finally + { + httpContext.User = originalPrincipal; + } + } + + protected virtual async Task WithNormalizedUserAsync(HttpContext httpContext, Func> action) + { + if (!Options.NormalizeUserIdClaimIssuer) + { + return await action(); + } + + var normalizer = httpContext.RequestServices.GetRequiredService(); + var originalPrincipal = httpContext.User; + httpContext.User = normalizer.Normalize(originalPrincipal); + try + { + return await action(); + } + finally + { + httpContext.User = originalPrincipal; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/IAbpAntiForgeryClaimsPrincipalNormalizer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/IAbpAntiForgeryClaimsPrincipalNormalizer.cs new file mode 100644 index 0000000000..bd2a581b5f --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AntiForgery/IAbpAntiForgeryClaimsPrincipalNormalizer.cs @@ -0,0 +1,10 @@ +using System.Security.Claims; + +namespace Volo.Abp.AspNetCore.Mvc.AntiForgery; + +public interface IAbpAntiForgeryClaimsPrincipalNormalizer +{ + // Returns a copy of the principal whose user identifier claims carry a stable issuer, so the + // antiforgery token's per-user identifier is the same across authentication schemes. + ClaimsPrincipal Normalize(ClaimsPrincipal principal); +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 464981cb3c..bd5e1e82a0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -175,7 +175,8 @@ public class AspNetCoreApiDescriptionModelProvider : IApiDescriptionModelProvide GetSupportedVersions(controllerType, method, setting), allowAnonymous, authorizeModels, - implementFrom + implementFrom, + GetReturnValueContentTypes(apiDescription) ) ); @@ -199,6 +200,27 @@ public class AspNetCoreApiDescriptionModelProvider : IApiDescriptionModelProvide } } + private static List? GetReturnValueContentTypes(ApiDescription apiDescription) + { + var preferred = apiDescription.SupportedResponseTypes + .FirstOrDefault(x => x.StatusCode == 200 && x.ApiResponseFormats.Any()) + ?? apiDescription.SupportedResponseTypes + .FirstOrDefault(x => x.StatusCode is >= 200 and < 300 && x.ApiResponseFormats.Any()); + + if (preferred == null) + { + return null; + } + + var contentTypes = preferred.ApiResponseFormats + .Select(f => f.MediaType) + .Where(m => !string.IsNullOrWhiteSpace(m)) + .Distinct() + .ToList(); + + return contentTypes.Count > 0 ? contentTypes : null; + } + private static List GetSupportedVersions(Type controllerType, MethodInfo method, ConventionalControllerSetting? setting) { diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs index 0f2a606038..e54734139c 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs @@ -7,7 +7,7 @@ public class HubConfigList : List { public void AddOrUpdate(Action? configAction = null) { - AddOrUpdate(typeof(THub)); + AddOrUpdate(typeof(THub), configAction); } public void AddOrUpdate(Type hubType, Action? configAction = null) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs index 257e12d0dc..84fe8324de 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs @@ -169,7 +169,15 @@ public class AbpIoSourceCodeStore : ISourceCodeStore, ITransientDependency if (!string.IsNullOrWhiteSpace(templateSource) && !IsNetworkSource(templateSource)) { Logger.LogInformation("Using local " + type + ": " + name + ", version: " + version); - return new TemplateFile(File.ReadAllBytes(Path.Combine(templateSource, name + "-" + version + ".zip")), + + // A local template source can be a direct ".zip" file path or a folder that contains the "{name}-{version}.zip" file. + var localTemplateFilePath = templateSource.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) + ? templateSource + : Path.Combine(templateSource, name.Replace("/", ".").EnsureEndsWith('-') + version + ".zip"); + + Logger.LogInformation("Using local template file: " + localTemplateFilePath); + + return new TemplateFile(File.ReadAllBytes(localTemplateFilePath), version, latestVersion, nugetVersion); } @@ -188,16 +196,6 @@ public class AbpIoSourceCodeStore : ISourceCodeStore, ITransientDependency return new TemplateFile(File.ReadAllBytes(localCacheFile), version, latestVersion, nugetVersion); } - if (!skipCache && !templateSource.IsNullOrWhiteSpace() && type == SourceCodeTypes.Template) - { - var templateFilePath = templateSource.EndsWith(".zip") - ? templateSource - : Path.Combine(templateSource, name.Replace("/", ".").EnsureEndsWith('-') + version + ".zip"); - - Logger.LogInformation("Using cached template: " + name + ", version: " + version + " from template source: " + templateFilePath); - return new TemplateFile(File.ReadAllBytes(templateFilePath), version, latestVersion, nugetVersion); - } - Logger.LogInformation("Downloading " + type + ": " + name + ", version: " + version); var fileContent = await DownloadSourceCodeContentAsync( diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateProjectBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateProjectBuilder.cs index 4a06557831..1a27ae84d0 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateProjectBuilder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateProjectBuilder.cs @@ -69,6 +69,7 @@ public class TemplateProjectBuilder : IProjectBuilder, ITransientDependency args.Version, args.TemplateSource, args.ExtraProperties.ContainsKey(NewCommand.Options.Preview.Long), + skipCache: args.SkipCache, trustUserVersion: args.TrustUserVersion ); diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs index 107008deea..0b7a21c71e 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs @@ -108,8 +108,7 @@ public class ClientProxyBase : ITransientDependency { var responseContent = await RequestAsync(requestContext); - if (typeof(T) == typeof(IRemoteStreamContent) || - typeof(T) == typeof(RemoteStreamContent)) + if (typeof(T) == typeof(IRemoteStreamContent) || typeof(T) == typeof(RemoteStreamContent)) { /* returning a class that holds a reference to response * content just to be sure that GC does not dispose of @@ -127,7 +126,8 @@ public class ClientProxyBase : ITransientDependency var stringContent = await responseContent.ReadAsStringAsync(); if (typeof(T) == typeof(string)) { - return (T)(object)stringContent; + var unwrapped = UnwrapStringResponse(stringContent, responseContent.Headers?.ContentType?.MediaType); + return (T)(object)unwrapped!; } if (stringContent.IsNullOrWhiteSpace()) @@ -139,6 +139,39 @@ public class ClientProxyBase : ITransientDependency } } + protected virtual string? UnwrapStringResponse(string body, string? contentType) + { + if (body.IsNullOrEmpty() || contentType.IsNullOrWhiteSpace()) + { + return body; + } + + if (!IsJsonMediaType(NormalizeMediaType(contentType!))) + { + return body; + } + + try + { + var parsed = JsonSerializer.Deserialize(body); + return parsed ?? string.Empty; + } + catch + { + return body; + } + } + + protected static string NormalizeMediaType(string mediaType) + { + if (mediaType.IsNullOrWhiteSpace()) + { + return string.Empty; + } + var semi = mediaType.IndexOf(';'); + return (semi < 0 ? mediaType : mediaType.Substring(0, semi)).Trim().ToLowerInvariant(); + } + protected virtual async Task RequestAsync(ClientProxyRequestContext requestContext) { var clientConfig = ClientOptions.Value.HttpClientProxies.GetOrDefault(requestContext.ServiceType) ?? throw new AbpException($"Could not get HttpClientProxyConfig for {requestContext.ServiceType.FullName}."); @@ -326,14 +359,7 @@ public class ClientProxyBase : ITransientDependency HttpRequestMessage requestMessage, ApiVersionInfo apiVersion) { - //API Version - if (!apiVersion.Version.IsNullOrEmpty()) - { - //TODO: What about other media types? - requestMessage.Headers.Add("accept", $"{MimeTypes.Text.Plain}; v={apiVersion.Version}"); - requestMessage.Headers.Add("accept", $"{MimeTypes.Application.Json}; v={apiVersion.Version}"); - requestMessage.Headers.Add("api-version", apiVersion.Version); - } + AddAcceptHeaders(action, requestMessage, apiVersion); //Header parameters var headers = action.Parameters.Where(p => p.BindingSourceId == ParameterBindingSources.Header).ToArray(); @@ -378,6 +404,57 @@ public class ClientProxyBase : ITransientDependency } } + protected virtual void AddAcceptHeaders( + ActionApiDescriptionModel action, + HttpRequestMessage requestMessage, + ApiVersionInfo apiVersion) + { + var acceptForReturn = GetAcceptForActionReturn(action); + var versionSuffix = apiVersion.Version.IsNullOrEmpty() ? string.Empty : $"; v={apiVersion.Version}"; + + if (!acceptForReturn.IsNullOrEmpty()) + { + requestMessage.Headers.Add("accept", acceptForReturn + versionSuffix); + } + else + { + requestMessage.Headers.Add("accept", MimeTypes.Text.Plain + versionSuffix); + requestMessage.Headers.Add("accept", MimeTypes.Application.Json + versionSuffix); + } + + if (!apiVersion.Version.IsNullOrEmpty()) + { + requestMessage.Headers.Add("api-version", apiVersion.Version); + } + } + + protected virtual string? GetAcceptForActionReturn(ActionApiDescriptionModel action) + { + if (action.ReturnValue.IsRemoteStream || + action.ReturnValue.Type == typeof(IRemoteStreamContent).FullName || + action.ReturnValue.Type == typeof(RemoteStreamContent).FullName) + { + return MimeTypes.Application.OctetStream; + } + + var contentTypes = action.ReturnValue.ContentTypes; + if (contentTypes == null || contentTypes.Count == 0) + { + return null; + } + + var normalized = contentTypes.Select(NormalizeMediaType).ToList(); + + return normalized.FirstOrDefault(IsJsonMediaType) ?? normalized[0]; + } + + private static bool IsJsonMediaType(string normalizedMediaType) + { + return normalizedMediaType.Equals(MimeTypes.Application.Json, StringComparison.OrdinalIgnoreCase) || + normalizedMediaType.Equals("text/json", StringComparison.OrdinalIgnoreCase) || + normalizedMediaType.EndsWith("+json", StringComparison.OrdinalIgnoreCase); + } + protected virtual StringSegment RemoveQuotes(StringSegment input) { if (!StringSegment.IsNullOrEmpty(input) && input.Length >= 2 && input[0] == '"' && input[input.Length - 1] == '"') diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs index 7650e40f88..e01b39c94d 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs @@ -45,7 +45,7 @@ public class ActionApiDescriptionModel } - public static ActionApiDescriptionModel Create([NotNull] string uniqueName, [NotNull] MethodInfo method, [NotNull] string url, string? httpMethod, [NotNull] IList supportedVersions, bool? allowAnonymous = null, IList? authorizeDatas = null, string? implementFrom = null) + public static ActionApiDescriptionModel Create([NotNull] string uniqueName, [NotNull] MethodInfo method, [NotNull] string url, string? httpMethod, [NotNull] IList supportedVersions, bool? allowAnonymous = null, IList? authorizeDatas = null, string? implementFrom = null, IList? returnValueContentTypes = null) { Check.NotNull(uniqueName, nameof(uniqueName)); Check.NotNull(method, nameof(method)); @@ -58,7 +58,7 @@ public class ActionApiDescriptionModel Name = method.Name, Url = url, HttpMethod = httpMethod, - ReturnValue = ReturnValueApiDescriptionModel.Create(method.ReturnType), + ReturnValue = ReturnValueApiDescriptionModel.Create(method.ReturnType, returnValueContentTypes), Parameters = new List(), ParametersOnMethod = method .GetParameters() diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs index e77d2f7fea..25b2fb149a 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using Volo.Abp.Content; using Volo.Abp.Reflection; using Volo.Abp.Threading; @@ -13,19 +15,30 @@ public class ReturnValueApiDescriptionModel public string? Summary { get; set; } + public IList? ContentTypes { get; set; } + + public bool IsRemoteStream { get; set; } + public ReturnValueApiDescriptionModel() { } - public static ReturnValueApiDescriptionModel Create(Type type) + public static ReturnValueApiDescriptionModel Create(Type type, IList? contentTypes = null) { var unwrappedType = AsyncHelper.UnwrapTask(type); return new ReturnValueApiDescriptionModel { Type = TypeHelper.GetFullNameHandlingNullableAndGenerics(unwrappedType), - TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(unwrappedType) + TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(unwrappedType), + ContentTypes = contentTypes, + IsRemoteStream = IsRemoteStreamType(unwrappedType) }; } + + private static bool IsRemoteStreamType(Type type) + { + return type == typeof(IRemoteStreamContent) || type == typeof(RemoteStreamContent); + } } diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs index 18af3e0661..8e15d85191 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs @@ -135,10 +135,9 @@ public class JQueryProxyScriptGenerator : IProxyScriptGenerator, ITransientDepen AddAjaxCallParameters(script, action); - var ajaxParamsIsFromForm = action.Parameters.Any(x => x.BindingSourceId == ParameterBindingSources.Form); - var dataType = action.ReturnValue.Type == ReturnValueApiDescriptionModel.Create(typeof(string)).Type - ? "{ dataType: 'text' }, " - : string.Empty; + var hasFormFile = action.Parameters.Any(x => x.BindingSourceId == ParameterBindingSources.FormFile); + var ajaxParamsIsFromForm = !hasFormFile && action.Parameters.Any(x => x.BindingSourceId == ParameterBindingSources.Form); + var dataType = GetJQueryDataTypeAndAcceptOverride(action); script.AppendLine(ajaxParamsIsFromForm ? " }, $.extend(true, {}, " + dataType + "{ contentType: 'application/x-www-form-urlencoded; charset=UTF-8' }, ajaxParams)));" : " }, " + dataType + "ajaxParams));"); @@ -146,6 +145,54 @@ public class JQueryProxyScriptGenerator : IProxyScriptGenerator, ITransientDepen script.AppendLine(" };"); } + private static string GetJQueryDataTypeAndAcceptOverride(ActionApiDescriptionModel action) + { + if (action.ReturnValue.IsRemoteStream) + { + return string.Empty; + } + + var contentTypes = action.ReturnValue.ContentTypes; + var isStringReturn = action.ReturnValue.Type == ReturnValueApiDescriptionModel.Create(typeof(string)).Type; + + if (contentTypes is { Count: > 0 }) + { + var normalized = contentTypes.Select(NormalizeMediaType).ToList(); + + var firstJsonShaped = normalized.FirstOrDefault(IsJsonMediaType); + if (firstJsonShaped != null) + { + return "{ dataType: 'json', headers: { Accept: '" + firstJsonShaped + "' } }, "; + } + + if (normalized.All(ct => ct.StartsWith("text/", StringComparison.OrdinalIgnoreCase))) + { + return "{ dataType: 'text', headers: { Accept: '" + normalized[0] + "' } }, "; + } + + return "{ headers: { Accept: '" + normalized[0] + "' } }, "; + } + + return isStringReturn ? "{ dataType: 'text' }, " : string.Empty; + } + + private static bool IsJsonMediaType(string normalizedMediaType) + { + return normalizedMediaType.Equals("application/json", StringComparison.OrdinalIgnoreCase) || + normalizedMediaType.Equals("text/json", StringComparison.OrdinalIgnoreCase) || + normalizedMediaType.EndsWith("+json", StringComparison.OrdinalIgnoreCase); + } + + private static string NormalizeMediaType(string mediaType) + { + if (string.IsNullOrWhiteSpace(mediaType)) + { + return string.Empty; + } + var semi = mediaType.IndexOf(';'); + return (semi < 0 ? mediaType : mediaType.Substring(0, semi)).Trim().ToLowerInvariant(); + } + private static string FindBestApiVersion(ActionApiDescriptionModel action) { //var configuredVersion = GetConfiguredApiVersion(); //TODO: Implement @@ -184,6 +231,20 @@ public class JQueryProxyScriptGenerator : IProxyScriptGenerator, ITransientDepen script.Append(" headers: " + headers); } + var firstFileParam = action.Parameters.FirstOrDefault(p => p.BindingSourceId == ParameterBindingSources.FormFile); + if (firstFileParam != null) + { + var fileVar = ProxyScriptingJsFuncHelper.NormalizeJsVariableName(firstFileParam.NameOnMethod.ToCamelCase()); + script.AppendLine(","); + script.Append(" data: " + fileVar + ","); + script.AppendLine(); + script.Append(" processData: false,"); + script.AppendLine(); + script.Append(" contentType: false"); + script.AppendLine(); + return; + } + var body = ProxyScriptingHelper.GenerateBody(action); if (!body.IsNullOrEmpty()) { diff --git a/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/IAbpMapperlyMapper.cs b/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/IAbpMapperlyMapper.cs index eac0f6644c..80efb49f4c 100644 --- a/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/IAbpMapperlyMapper.cs +++ b/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/IAbpMapperlyMapper.cs @@ -13,11 +13,11 @@ public interface IAbpMapperlyMapper public interface IAbpReverseMapperlyMapper : IAbpMapperlyMapper { - TSource ReverseMap(TDestination destination); + TSource ReverseMap(TDestination source); - void ReverseMap(TDestination destination, TSource source); + void ReverseMap(TDestination source, TSource destination); - void BeforeReverseMap(TDestination destination); + void BeforeReverseMap(TDestination source); - void AfterReverseMap(TDestination destination, TSource source); + void AfterReverseMap(TDestination source, TSource destination); } diff --git a/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperBase.cs b/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperBase.cs index 39d9dce995..f49b2f14c9 100644 --- a/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperBase.cs +++ b/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperBase.cs @@ -18,15 +18,15 @@ public abstract class MapperBase : IAbpMapperlyMapper : MapperBase, IAbpReverseMapperlyMapper { - public abstract TSource ReverseMap(TDestination destination); + public abstract TSource ReverseMap(TDestination source); - public abstract void ReverseMap(TDestination destination, TSource source); + public abstract void ReverseMap(TDestination source, TSource destination); - public virtual void BeforeReverseMap(TDestination destination) + public virtual void BeforeReverseMap(TDestination source) { } - public virtual void AfterReverseMap(TDestination destination, TSource source) + public virtual void AfterReverseMap(TDestination source, TSource destination) { } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiForgeryClaimsPrincipalNormalizer_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiForgeryClaimsPrincipalNormalizer_Tests.cs new file mode 100644 index 0000000000..7a2393586c --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiForgeryClaimsPrincipalNormalizer_Tests.cs @@ -0,0 +1,263 @@ +using System; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Antiforgery; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Security.Claims; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.AntiForgery; + +public class AbpAntiForgeryClaimsPrincipalNormalizer_Tests +{ + private const string CookieIssuer = "LOCAL AUTHORITY"; + private const string BearerIssuer = "https://localhost:44361/"; + private const string UserId = "3a0e6f1c-1111-2222-3333-444455556666"; + private const string AntiForgeryHeaderName = "RequestVerificationToken"; + private const string AntiForgeryCookieName = "AF"; + + [Fact] + public void Normalize_should_set_a_constant_issuer_on_user_identifier_claims_only() + { + var usernameClaim = new Claim("preferred_username", "admin", ClaimValueTypes.String, CookieIssuer); + usernameClaim.Properties["test-property"] = "test-value"; + + var principal = new ClaimsPrincipal(new ClaimsIdentity( + new[] + { + new Claim("sub", UserId, ClaimValueTypes.String, CookieIssuer), + new Claim(ClaimTypes.NameIdentifier, UserId, ClaimValueTypes.String, CookieIssuer), + usernameClaim + }, + "Identity.Application")); + + var normalized = new AbpAntiForgeryClaimsPrincipalNormalizer().Normalize(principal); + + normalized.FindFirst("sub")!.Issuer.ShouldBe(AbpAntiForgeryClaimsPrincipalNormalizer.UserIdClaimIssuer); + normalized.FindFirst(ClaimTypes.NameIdentifier)!.Issuer.ShouldBe(AbpAntiForgeryClaimsPrincipalNormalizer.UserIdClaimIssuer); + + // value and OriginalIssuer are kept; only Issuer changes + normalized.FindFirst("sub")!.Value.ShouldBe(UserId); + normalized.FindFirst("sub")!.OriginalIssuer.ShouldBe(CookieIssuer); + + // non-identifier claims and their properties are untouched + var normalizedUsername = normalized.FindFirst("preferred_username")!; + normalizedUsername.Issuer.ShouldBe(CookieIssuer); + normalizedUsername.Properties["test-property"].ShouldBe("test-value"); + + // the original principal is not mutated + principal.FindFirst("sub")!.Issuer.ShouldBe(CookieIssuer); + } + + [Fact] + public void Normalize_should_preserve_identity_metadata() + { + var actor = new ClaimsIdentity(new[] { new Claim(AbpClaimTypes.UserId, "actor-id") }, "Actor"); + var principal = new ClaimsPrincipal(new ClaimsIdentity( + new[] { new Claim("sub", UserId, ClaimValueTypes.String, BearerIssuer) }, + "Identity.Application") + { + Actor = actor, + BootstrapContext = "raw-token", + Label = "my-label" + }); + + var normalized = new AbpAntiForgeryClaimsPrincipalNormalizer().Normalize(principal); + var normalizedIdentity = (ClaimsIdentity)normalized.Identity!; + + // identity metadata that the antiforgery claim uid does not use is still preserved on the copy + normalizedIdentity.Actor.ShouldBeSameAs(actor); + normalizedIdentity.BootstrapContext.ShouldBe("raw-token"); + normalizedIdentity.Label.ShouldBe("my-label"); + // and the user id claim issuer was still normalized + normalized.FindFirst("sub")!.Issuer.ShouldBe(AbpAntiForgeryClaimsPrincipalNormalizer.UserIdClaimIssuer); + } + + [Fact] + public async Task Token_should_validate_for_the_same_cookie_principal_through_the_decorator() + { + // The common server-rendered case: a token generated and validated for the same cookie principal. + // Guards that wrapping IAntiforgery does not break the basic flow every page POST relies on. + var (antiforgery, serviceProvider) = CreateDecoratedAntiforgery(normalize: true); + + var cookiePrincipal = CreatePrincipal("Identity.Application", CookieIssuer); + var (cookieToken, requestToken) = GenerateToken(antiforgery, serviceProvider, cookiePrincipal); + + var isValid = await ValidateAsync(antiforgery, serviceProvider, cookiePrincipal, cookieToken, requestToken); + + isValid.ShouldBeTrue(); + } + + [Fact] + public async Task Token_issued_under_one_scheme_should_validate_under_another_when_normalization_is_enabled() + { + var (antiforgery, serviceProvider) = CreateDecoratedAntiforgery(normalize: true); + + var cookiePrincipal = CreatePrincipal("Identity.Application", CookieIssuer); + var (cookieToken, requestToken) = GenerateToken(antiforgery, serviceProvider, cookiePrincipal); + + var bearerPrincipal = CreatePrincipal("AuthenticationTypes.Federation", BearerIssuer); + var isValid = await ValidateAsync(antiforgery, serviceProvider, bearerPrincipal, cookieToken, requestToken); + + isValid.ShouldBeTrue(); + } + + [Fact] + public async Task Token_issued_under_one_scheme_should_fail_under_another_when_normalization_is_disabled() + { + var (antiforgery, serviceProvider) = CreateDecoratedAntiforgery(normalize: false); + + var cookiePrincipal = CreatePrincipal("Identity.Application", CookieIssuer); + var (cookieToken, requestToken) = GenerateToken(antiforgery, serviceProvider, cookiePrincipal); + + var bearerPrincipal = CreatePrincipal("AuthenticationTypes.Federation", BearerIssuer); + var isValid = await ValidateAsync(antiforgery, serviceProvider, bearerPrincipal, cookieToken, requestToken); + + isValid.ShouldBeFalse(); + } + + [Fact] + public async Task Token_should_validate_when_the_cookie_principal_user_id_issuer_is_not_local_authority() + { + // Tiered/OIDC templates back the cookie with an OIDC principal whose user id issuer is the token + // authority. Because the decorator normalizes both generation and validation (Razor Pages validate + // through the same decorated IAntiforgery), the per-user identifier still matches. + var (antiforgery, serviceProvider) = CreateDecoratedAntiforgery(normalize: true); + + var oidcCookiePrincipal = CreatePrincipal("Identity.Application", BearerIssuer); + var (cookieToken, requestToken) = GenerateToken(antiforgery, serviceProvider, oidcCookiePrincipal); + + var isValid = await ValidateAsync(antiforgery, serviceProvider, oidcCookiePrincipal, cookieToken, requestToken); + + isValid.ShouldBeTrue(); + } + + [Fact] + public async Task Token_should_validate_across_schemes_when_principal_has_both_sub_and_name_identifier() + { + // The extractor picks "sub" before NameIdentifier and a principal can carry both, so the + // normalization must cover the claim actually picked. + var (antiforgery, serviceProvider) = CreateDecoratedAntiforgery(normalize: true); + + var cookiePrincipal = CreatePrincipalWithSubAndNameIdentifier("Identity.Application", CookieIssuer); + var (cookieToken, requestToken) = GenerateToken(antiforgery, serviceProvider, cookiePrincipal); + + var bearerPrincipal = CreatePrincipalWithSubAndNameIdentifier("AuthenticationTypes.Federation", BearerIssuer); + var isValid = await ValidateAsync(antiforgery, serviceProvider, bearerPrincipal, cookieToken, requestToken); + + isValid.ShouldBeTrue(); + } + + [Fact] + public async Task Decorator_should_restore_the_original_principal_after_each_call() + { + var (antiforgery, serviceProvider) = CreateDecoratedAntiforgery(normalize: true); + + var originalPrincipal = CreatePrincipal("AuthenticationTypes.Federation", BearerIssuer); + var httpContext = new DefaultHttpContext { User = originalPrincipal, RequestServices = serviceProvider }; + + antiforgery.GetAndStoreTokens(httpContext); + + httpContext.User.ShouldBeSameAs(originalPrincipal); + httpContext.User.FindFirst(AbpClaimTypes.UserId)!.Issuer.ShouldBe(BearerIssuer); + + httpContext.Request.Headers["Cookie"] = $"{AntiForgeryCookieName}=invalid"; + await antiforgery.IsRequestValidAsync(httpContext); + + httpContext.User.ShouldBeSameAs(originalPrincipal); + httpContext.User.FindFirst(AbpClaimTypes.UserId)!.Issuer.ShouldBe(BearerIssuer); + } + + [Fact] + public async Task Decorator_should_not_normalize_when_disabled() + { + var (antiforgery, serviceProvider) = CreateDecoratedAntiforgery(normalize: false); + + var principal = CreatePrincipal("Identity.Application", CookieIssuer); + var httpContext = new DefaultHttpContext { User = principal, RequestServices = serviceProvider }; + + antiforgery.GetAndStoreTokens(httpContext); + httpContext.User.ShouldBeSameAs(principal); + + httpContext.Request.Headers["Cookie"] = $"{AntiForgeryCookieName}=invalid"; + await antiforgery.IsRequestValidAsync(httpContext); + httpContext.User.ShouldBeSameAs(principal); + } + + private static ClaimsPrincipal CreatePrincipal(string authenticationType, string userIdClaimIssuer) + { + return new ClaimsPrincipal(new ClaimsIdentity( + new[] + { + new Claim(AbpClaimTypes.UserId, UserId, ClaimValueTypes.String, userIdClaimIssuer), + new Claim("preferred_username", "admin", ClaimValueTypes.String, userIdClaimIssuer) + }, + authenticationType, + "preferred_username", + AbpClaimTypes.Role)); + } + + private static ClaimsPrincipal CreatePrincipalWithSubAndNameIdentifier(string authenticationType, string issuer) + { + return new ClaimsPrincipal(new ClaimsIdentity( + new[] + { + new Claim("sub", UserId, ClaimValueTypes.String, issuer), + new Claim(ClaimTypes.NameIdentifier, UserId, ClaimValueTypes.String, issuer), + new Claim("preferred_username", "admin", ClaimValueTypes.String, issuer) + }, + authenticationType, + "preferred_username", + AbpClaimTypes.Role)); + } + + private static (IAntiforgery antiforgery, IServiceProvider serviceProvider) CreateDecoratedAntiforgery(bool normalize) + { + var services = new ServiceCollection(); + services.AddLogging(); + services.AddDataProtection(); + services.AddAntiforgery(options => + { + options.Cookie.Name = AntiForgeryCookieName; + options.HeaderName = AntiForgeryHeaderName; + }); + services.AddTransient(); + + var serviceProvider = services.BuildServiceProvider(); + + var antiforgery = new AbpAntiforgery( + serviceProvider.GetRequiredService(), + Microsoft.Extensions.Options.Options.Create(new AbpAntiForgeryOptions { NormalizeUserIdClaimIssuer = normalize })); + + return (antiforgery, serviceProvider); + } + + private static (string cookieToken, string requestToken) GenerateToken( + IAntiforgery antiforgery, IServiceProvider serviceProvider, ClaimsPrincipal user) + { + var httpContext = new DefaultHttpContext { User = user, RequestServices = serviceProvider }; + var tokenSet = antiforgery.GetAndStoreTokens(httpContext); + return (ExtractCookieToken(httpContext), tokenSet.RequestToken!); + } + + private static async Task ValidateAsync( + IAntiforgery antiforgery, IServiceProvider serviceProvider, ClaimsPrincipal user, string cookieToken, string requestToken) + { + var httpContext = new DefaultHttpContext { User = user, RequestServices = serviceProvider }; + httpContext.Request.Headers["Cookie"] = $"{AntiForgeryCookieName}={cookieToken}"; + httpContext.Request.Headers[AntiForgeryHeaderName] = requestToken; + + return await antiforgery.IsRequestValidAsync(httpContext); + } + + private static string ExtractCookieToken(HttpContext httpContext) + { + var setCookie = httpContext.Response.Headers.SetCookie.ToString(); + var prefix = AntiForgeryCookieName + "="; + var start = setCookie.IndexOf(prefix, StringComparison.Ordinal) + prefix.Length; + var end = setCookie.IndexOf(';', start); + return end < 0 ? setCookie.Substring(start) : setCookie.Substring(start, end - start); + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiforgery_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiforgery_Tests.cs new file mode 100644 index 0000000000..9ac20ed4d6 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AntiForgery/AbpAntiforgery_Tests.cs @@ -0,0 +1,182 @@ +using System; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Antiforgery; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Security.Claims; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.AntiForgery; + +public class AbpAntiforgery_Tests +{ + private const string BearerIssuer = "https://localhost:44361/"; + private const string UserId = "3a0e6f1c-1111-2222-3333-444455556666"; + + [Fact] + public Task GetAndStoreTokens_should_normalize_the_user_and_restore_it() => + Should_normalize_then_restore( + (antiforgery, httpContext) => { antiforgery.GetAndStoreTokens(httpContext); return Task.CompletedTask; }, + inner => inner.UserSeenByGetAndStoreTokens); + + [Fact] + public Task GetTokens_should_normalize_the_user_and_restore_it() => + Should_normalize_then_restore( + (antiforgery, httpContext) => { antiforgery.GetTokens(httpContext); return Task.CompletedTask; }, + inner => inner.UserSeenByGetTokens); + + [Fact] + public Task IsRequestValidAsync_should_normalize_the_user_and_restore_it() => + Should_normalize_then_restore( + (antiforgery, httpContext) => antiforgery.IsRequestValidAsync(httpContext), + inner => inner.UserSeenByIsRequestValid); + + [Fact] + public Task ValidateRequestAsync_should_normalize_the_user_and_restore_it() => + Should_normalize_then_restore( + (antiforgery, httpContext) => antiforgery.ValidateRequestAsync(httpContext), + inner => inner.UserSeenByValidateRequest); + + [Fact] + public Task SetCookieTokenAndHeader_should_normalize_the_user_and_restore_it() => + Should_normalize_then_restore( + (antiforgery, httpContext) => { antiforgery.SetCookieTokenAndHeader(httpContext); return Task.CompletedTask; }, + inner => inner.UserSeenBySetCookieTokenAndHeader); + + [Fact] + public void Should_delegate_the_result_to_the_inner_antiforgery() + { + var inner = new RecordingAntiforgery(); + var antiforgery = new AbpAntiforgery(inner, CreateOptions(normalize: true)); + var httpContext = CreateHttpContext(CreatePrincipal(BearerIssuer), withNormalizer: true); + + var tokenSet = antiforgery.GetAndStoreTokens(httpContext); + + tokenSet.RequestToken.ShouldBe(RecordingAntiforgery.RequestToken); + tokenSet.CookieToken.ShouldBe(RecordingAntiforgery.CookieToken); + } + + [Fact] + public void Should_not_normalize_when_disabled() + { + var inner = new RecordingAntiforgery(); + var antiforgery = new AbpAntiforgery(inner, CreateOptions(normalize: false)); + var original = CreatePrincipal(BearerIssuer); + var httpContext = CreateHttpContext(original, withNormalizer: true); + + antiforgery.GetAndStoreTokens(httpContext); + + // the inner saw the original (un-normalized) principal + inner.UserSeenByGetAndStoreTokens.ShouldBeSameAs(original); + inner.UserSeenByGetAndStoreTokens!.FindFirst(AbpClaimTypes.UserId)!.Issuer.ShouldBe(BearerIssuer); + httpContext.User.ShouldBeSameAs(original); + } + + [Fact] + public void Should_not_resolve_the_normalizer_service_when_disabled() + { + // the normalizer is intentionally not registered; the disabled fast-path must not touch RequestServices + var inner = new RecordingAntiforgery(); + var antiforgery = new AbpAntiforgery(inner, CreateOptions(normalize: false)); + var httpContext = CreateHttpContext(CreatePrincipal(BearerIssuer), withNormalizer: false); + + Should.NotThrow(() => antiforgery.GetAndStoreTokens(httpContext)); + } + + private static async Task Should_normalize_then_restore( + Func invoke, + Func userSeenByInner) + { + var inner = new RecordingAntiforgery(); + var antiforgery = new AbpAntiforgery(inner, CreateOptions(normalize: true)); + var original = CreatePrincipal(BearerIssuer); + var httpContext = CreateHttpContext(original, withNormalizer: true); + + await invoke(antiforgery, httpContext); + + // the inner ran against the normalized principal + userSeenByInner(inner)!.FindFirst(AbpClaimTypes.UserId)!.Issuer + .ShouldBe(AbpAntiForgeryClaimsPrincipalNormalizer.UserIdClaimIssuer); + // the original principal is restored after the call + httpContext.User.ShouldBeSameAs(original); + } + + private static Microsoft.Extensions.Options.IOptions CreateOptions(bool normalize) + { + return Microsoft.Extensions.Options.Options.Create( + new AbpAntiForgeryOptions { NormalizeUserIdClaimIssuer = normalize }); + } + + private static HttpContext CreateHttpContext(ClaimsPrincipal user, bool withNormalizer) + { + var services = new ServiceCollection(); + if (withNormalizer) + { + services.AddTransient(); + } + + return new DefaultHttpContext + { + User = user, + RequestServices = services.BuildServiceProvider() + }; + } + + private static ClaimsPrincipal CreatePrincipal(string userIdClaimIssuer) + { + return new ClaimsPrincipal(new ClaimsIdentity( + new[] + { + new Claim(AbpClaimTypes.UserId, UserId, ClaimValueTypes.String, userIdClaimIssuer) + }, + "AuthenticationTypes.Federation")); + } + + private sealed class RecordingAntiforgery : IAntiforgery + { + public const string RequestToken = "test-request-token"; + public const string CookieToken = "test-cookie-token"; + + public ClaimsPrincipal? UserSeenByGetAndStoreTokens { get; private set; } + public ClaimsPrincipal? UserSeenByGetTokens { get; private set; } + public ClaimsPrincipal? UserSeenByIsRequestValid { get; private set; } + public ClaimsPrincipal? UserSeenByValidateRequest { get; private set; } + public ClaimsPrincipal? UserSeenBySetCookieTokenAndHeader { get; private set; } + + public AntiforgeryTokenSet GetAndStoreTokens(HttpContext httpContext) + { + UserSeenByGetAndStoreTokens = httpContext.User; + return CreateTokenSet(); + } + + public AntiforgeryTokenSet GetTokens(HttpContext httpContext) + { + UserSeenByGetTokens = httpContext.User; + return CreateTokenSet(); + } + + public Task IsRequestValidAsync(HttpContext httpContext) + { + UserSeenByIsRequestValid = httpContext.User; + return Task.FromResult(true); + } + + public Task ValidateRequestAsync(HttpContext httpContext) + { + UserSeenByValidateRequest = httpContext.User; + return Task.CompletedTask; + } + + public void SetCookieTokenAndHeader(HttpContext httpContext) + { + UserSeenBySetCookieTokenAndHeader = httpContext.User; + } + + private static AntiforgeryTokenSet CreateTokenSet() + { + return new AntiforgeryTokenSet(RequestToken, CookieToken, "RequestVerificationToken", "RequestVerificationToken"); + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/HubConfigList_Tests.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/HubConfigList_Tests.cs new file mode 100644 index 0000000000..0265388ab2 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/HubConfigList_Tests.cs @@ -0,0 +1,32 @@ +using System.Linq; +using Shouldly; +using Volo.Abp.AspNetCore.SignalR.SampleHubs; +using Xunit; + +namespace Volo.Abp.AspNetCore.SignalR; + +public class HubConfigList_Tests +{ + [Fact] + public void Should_Apply_ConfigAction_For_Generic_Overload() + { + var list = new HubConfigList(); + + list.AddOrUpdate(config => config.RoutePattern = "/custom-route"); + + var hubConfig = list.Single(c => c.HubType == typeof(RegularHub)); + hubConfig.RoutePattern.ShouldBe("/custom-route"); + } + + [Fact] + public void Should_Update_Existing_Hub_For_Generic_Overload() + { + var list = new HubConfigList(); + + list.AddOrUpdate(); + list.AddOrUpdate(config => config.RoutePattern = "/custom-route"); + + list.Count.ShouldBe(1); + list.Single(c => c.HubType == typeof(RegularHub)).RoutePattern.ShouldBe("/custom-route"); + } +} diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase_ContentTypes_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase_ContentTypes_Tests.cs new file mode 100644 index 0000000000..c30fbb0d43 --- /dev/null +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase_ContentTypes_Tests.cs @@ -0,0 +1,275 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using Shouldly; +using Volo.Abp.Content; +using Volo.Abp.Http.Modeling; +using Xunit; + +namespace Volo.Abp.Http.Client.ClientProxying; + +public class ClientProxyBase_GetAcceptForActionReturn_Tests +{ + [Fact] + public void IRemoteStreamContent_Should_Pick_OctetStream_Even_When_ContentTypes_Include_Json() + { + var action = BuildAction( + returnType: typeof(IRemoteStreamContent).FullName!, + contentTypes: new[] { "application/json", "text/plain", "text/json" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("application/octet-stream"); + } + + [Fact] + public void RemoteStreamContent_Concrete_Type_Should_Pick_OctetStream() + { + var action = BuildAction( + returnType: typeof(RemoteStreamContent).FullName!, + contentTypes: null); + + InvokeGetAcceptForActionReturn(action).ShouldBe("application/octet-stream"); + } + + [Fact] + public void Json_In_ContentTypes_Should_Pick_Json() + { + var action = BuildAction( + returnType: "System.String", + contentTypes: new[] { "text/plain", "application/json", "text/json" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("application/json"); + } + + [Fact] + public void Only_Text_ContentTypes_Should_Pick_TextPlain() + { + var action = BuildAction( + returnType: "System.String", + contentTypes: new[] { "text/plain", "text/csv" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("text/plain"); + } + + [Fact] + public void Empty_Or_Null_ContentTypes_Should_Return_Null() + { + InvokeGetAcceptForActionReturn(BuildAction("System.Int32", null)).ShouldBeNull(); + InvokeGetAcceptForActionReturn(BuildAction("System.Int32", new string[0])).ShouldBeNull(); + } + + [Fact] + public void Mixed_Text_And_Octet_Stream_Should_Echo_First_Content_Type() + { + var action = BuildAction( + returnType: "System.String", + contentTypes: new[] { "text/plain", "application/octet-stream" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("text/plain"); + } + + [Fact] + public void JsonV2_Variant_Should_Still_Pick_Json() + { + var action = BuildAction( + returnType: "System.String", + contentTypes: new[] { "application/json; charset=utf-8" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("application/json"); + } + + [Fact] + public void Single_TextHtml_Should_Echo_Back_TextHtml() + { + var action = BuildAction( + returnType: "System.String", + contentTypes: new[] { "text/html" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("text/html"); + } + + [Fact] + public void OctetStream_Only_With_ObjectReturn_Should_Echo_OctetStream() + { + var action = BuildAction( + returnType: "My.Project.UserDto", + contentTypes: new[] { "application/octet-stream" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("application/octet-stream"); + } + + [Fact] + public void ApplicationXml_Only_Should_Echo_Back_Xml_Instead_Of_Legacy_Pair() + { + var action = BuildAction( + returnType: "My.Project.SoapEnvelope", + contentTypes: new[] { "application/xml" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("application/xml"); + } + + [Fact] + public void Case_Insensitive_Json_Match() + { + var action = BuildAction( + returnType: "System.String", + contentTypes: new[] { "APPLICATION/JSON" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("application/json"); + } + + [Fact] + public void Json_With_Charset_Parameter_Should_Still_Pick_Json() + { + var action = BuildAction( + returnType: "System.String", + contentTypes: new[] { "application/json; charset=utf-8" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("application/json"); + } + + [Fact] + public void Text_With_Charset_Parameter_Should_Still_Pick_TextPlain() + { + var action = BuildAction( + returnType: "System.String", + contentTypes: new[] { "text/plain ; charset=utf-8 " }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("text/plain"); + } + + [Fact] + public void Text_Json_Should_Echo_Back_Text_Json() + { + var action = BuildAction( + returnType: "System.String", + contentTypes: new[] { "text/json" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("text/json"); + } + + [Fact] + public void Application_Problem_Json_Should_Echo_Back_The_Plus_Json_Variant() + { + var action = BuildAction( + returnType: "System.String", + contentTypes: new[] { "application/problem+json" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("application/problem+json"); + } + + [Fact] + public void Vendor_Plus_Json_Should_Echo_Back_The_Plus_Json_Variant() + { + var action = BuildAction( + returnType: "System.String", + contentTypes: new[] { "application/vnd.api+json" }); + + InvokeGetAcceptForActionReturn(action).ShouldBe("application/vnd.api+json"); + } + + [Fact] + public void IsRemoteStream_Flag_True_Should_Pick_OctetStream_Regardless_Of_TypeName() + { + var action = BuildAction( + returnType: "My.Project.CustomStream", + contentTypes: new[] { "application/json" }); + action.ReturnValue.IsRemoteStream = true; + + InvokeGetAcceptForActionReturn(action).ShouldBe("application/octet-stream"); + } + + private static string? InvokeGetAcceptForActionReturn(ActionApiDescriptionModel action) + { + var proxy = new TestableClientProxy(); + return proxy.PublicGetAcceptForActionReturn(action); + } + + private static ActionApiDescriptionModel BuildAction(string returnType, IList? contentTypes) + { + return new ActionApiDescriptionModel + { + UniqueName = "Sample", + Name = "Sample", + HttpMethod = "GET", + Url = "api/test", + SupportedVersions = new List(), + ParametersOnMethod = new List(), + Parameters = new List(), + ReturnValue = new ReturnValueApiDescriptionModel + { + Type = returnType, + TypeSimple = returnType, + ContentTypes = contentTypes + }, + AuthorizeDatas = new List() + }; + } + + [Fact] + public void AddHeaders_With_ApiVersion_Should_Combine_OctetStream_Accept_With_Version_Suffix() + { + var action = BuildAction( + returnType: typeof(IRemoteStreamContent).FullName!, + contentTypes: new[] { "application/json", "text/plain" }); + + var headers = InvokeAddHeadersAndCollectAccept(action, version: "2.0"); + + headers.ShouldContain("application/octet-stream; v=2.0"); + headers.ShouldNotContain(h => h == "text/plain; v=2.0"); + headers.ShouldNotContain(h => h == "application/json; v=2.0"); + } + + [Fact] + public void AddHeaders_Without_ApiVersion_Should_Emit_OctetStream_For_Stream_Returns() + { + var action = BuildAction( + returnType: typeof(IRemoteStreamContent).FullName!, + contentTypes: new[] { "application/json" }); + + var headers = InvokeAddHeadersAndCollectAccept(action, version: null); + + headers.ShouldContain("application/octet-stream"); + } + + [Fact] + public void AddHeaders_Without_ContentType_Metadata_Should_Fall_Back_To_Text_And_Json_Pair() + { + var action = BuildAction(returnType: "System.Int32", contentTypes: null); + + var headers = InvokeAddHeadersAndCollectAccept(action, version: "1.0"); + + headers.ShouldContain("text/plain; v=1.0"); + headers.ShouldContain("application/json; v=1.0"); + } + + [Fact] + public void AddHeaders_Without_ApiVersion_And_Without_ContentType_Metadata_Should_Emit_Unversioned_Text_Json_Pair() + { + var action = BuildAction(returnType: "System.Int32", contentTypes: null); + + var headers = InvokeAddHeadersAndCollectAccept(action, version: null); + + headers.ShouldContain("text/plain"); + headers.ShouldContain("application/json"); + headers.ShouldNotContain(h => h.Contains("; v=")); + } + + private static IList InvokeAddHeadersAndCollectAccept(ActionApiDescriptionModel action, string? version) + { + var proxy = new TestableClientProxy(); + var message = new HttpRequestMessage(HttpMethod.Get, "http://localhost/x"); + var apiVersion = new ApiVersionInfo("HeaderModelBinding", version ?? string.Empty); + proxy.PublicAddAcceptHeaders(action, message, apiVersion); + return message.Headers.Accept.Select(a => a.ToString()).ToList(); + } + + private sealed class TestableClientProxy : ClientProxyBase + { + public string? PublicGetAcceptForActionReturn(ActionApiDescriptionModel action) + => GetAcceptForActionReturn(action); + + public void PublicAddAcceptHeaders(ActionApiDescriptionModel action, HttpRequestMessage requestMessage, ApiVersionInfo apiVersion) + => AddAcceptHeaders(action, requestMessage, apiVersion); + } +} diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder_FormData_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder_FormData_Tests.cs new file mode 100644 index 0000000000..12b453f4f4 --- /dev/null +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/Client/ClientProxying/ClientProxyRequestPayloadBuilder_FormData_Tests.cs @@ -0,0 +1,804 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Content; +using Volo.Abp.Http.Modeling; +using Volo.Abp.Http.ProxyScripting.Generators; +using Volo.Abp.Json; +using Volo.Abp.Timing; +using Xunit; +using MicrosoftOptions = Microsoft.Extensions.Options.Options; + +namespace Volo.Abp.Http.Client.ClientProxying; + +public class ClientProxyRequestPayloadBuilder_FormData_Tests +{ + private readonly ClientProxyRequestPayloadBuilder _builder; + private readonly IJsonSerializer _jsonSerializer = new StubJsonSerializer(); + private static readonly ApiVersionInfo NoApiVersion = new("Query", "1.0"); + + public ClientProxyRequestPayloadBuilder_FormData_Tests() + { + var services = new ServiceCollection(); + var scopeFactory = services.BuildServiceProvider().GetRequiredService(); + var options = MicrosoftOptions.Create(new AbpHttpClientProxyingOptions()); + _builder = new ClientProxyRequestPayloadBuilder(scopeFactory, options, new TestClock()); + } + + [Fact] + public async Task Direct_IRemoteStreamContent_Param_Should_Produce_Single_StreamContent_Part() + { + var action = BuildAction(parameters: new[] + { + FormFileParam(name: "file", nameOnMethod: "file"), + }); + var stream = MakeStream("hello-direct"); + var args = new Dictionary + { + ["file"] = new RemoteStreamContent(stream, "demo.txt", "text/plain"), + }; + + var content = await InvokeAsync(action, args); + + var multipart = content.ShouldBeOfType(); + var parts = multipart.ToList(); + parts.Count.ShouldBe(1); + parts[0].Headers.ContentType!.MediaType.ShouldBe("text/plain"); + (await parts[0].ReadAsStringAsync()).ShouldBe("hello-direct"); + } + + [Fact] + public async Task Dto_With_IRemoteStreamContent_Property_Should_Flatten_To_Name_Plus_File_Parts() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Name", nameOnMethod: "input"), + FormFileParam(name: "File", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestUploadDto + { + Name = "Alice", + File = new RemoteStreamContent(MakeStream("hello-single"), "single.txt", "text/plain"), + }, + }; + + var content = await InvokeAsync(action, args); + + var multipart = content.ShouldBeOfType(); + var parts = multipart.ToList(); + parts.Count.ShouldBe(2); + await AssertStringPart(parts[0], "Name", "Alice"); + await AssertStreamPart(parts[1], "File", "hello-single", "text/plain"); + } + + [Fact] + public async Task Dto_With_IEnumerable_IRemoteStreamContent_Should_Emit_One_Part_Per_Stream() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Label", nameOnMethod: "input"), + FormFileParam(name: "Files", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestUploadFilesDto + { + Label = "batch", + Files = new[] + { + new RemoteStreamContent(MakeStream("a-content"), "a.txt", "text/plain"), + new RemoteStreamContent(MakeStream("b-content"), "b.txt", "text/csv"), + }, + }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(3); + await AssertStringPart(parts[0], "Label", "batch"); + await AssertStreamPart(parts[1], "Files", "a-content", "text/plain"); + await AssertStreamPart(parts[2], "Files", "b-content", "text/csv"); + } + + [Fact] + public async Task Nested_Dto_With_Child_File_Path_Should_Be_Reflected_Via_Dotted_Name() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Outer", nameOnMethod: "input"), + FormFileParam(name: "Child.File", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestNestedUploadDto + { + Outer = "outerVal", + Child = new TestNestedChildDto + { + File = new RemoteStreamContent(MakeStream("hello-nested"), "nested.txt", "text/plain"), + }, + }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(2); + await AssertStringPart(parts[0], "Outer", "outerVal"); + await AssertStreamPart(parts[1], "Child.File", "hello-nested", "text/plain"); + } + + [Fact] + public async Task Form_Only_Action_Without_FormFile_Should_Still_Produce_Multipart_With_String_Parts() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Name", nameOnMethod: "input"), + FormParam(name: "Tag", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestUploadDto { Name = "Alice", Tag = "T1" }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(2); + await AssertStringPart(parts[0], "Name", "Alice"); + await AssertStringPart(parts[1], "Tag", "T1"); + } + + [Fact] + public async Task Body_Binding_Wins_Over_Form_And_Returns_Json_StringContent() + { + var action = BuildAction(parameters: new[] + { + new ParameterApiDescriptionModel + { + Name = "input", + NameOnMethod = "input", + Type = typeof(TestUploadDto).FullName!, + TypeSimple = "dto", + BindingSourceId = ParameterBindingSources.Body, + }, + }); + var args = new Dictionary + { + ["input"] = new TestUploadDto { Name = "Alice" }, + }; + + var content = await InvokeAsync(action, args); + + var stringContent = content.ShouldBeOfType(); + stringContent.Headers.ContentType!.MediaType.ShouldBe("application/json"); + var body = await stringContent.ReadAsStringAsync(); + body.ShouldContain("\"Name\":\"Alice\""); + } + + [Fact] + public async Task No_Form_Or_Body_Params_Should_Return_Null_Content() + { + var action = BuildAction(parameters: new[] + { + new ParameterApiDescriptionModel + { + Name = "id", + NameOnMethod = "id", + Type = "System.Int32", + TypeSimple = "int", + BindingSourceId = ParameterBindingSources.Path, + }, + }); + var args = new Dictionary { ["id"] = 42 }; + + var content = await InvokeAsync(action, args); + + content.ShouldBeNull(); + } + + [Fact] + public async Task FormFile_With_Null_Value_Should_Be_Skipped_Not_Throw() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Name", nameOnMethod: "input"), + FormFileParam(name: "File", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestUploadDto { Name = "Alice", File = null }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(1); + await AssertStringPart(parts[0], "Name", "Alice"); + } + + [Fact] + public async Task Three_Level_Nested_Dto_Should_Resolve_Outer_Inner_File_Via_Dotted_Path() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Outer", nameOnMethod: "input"), + FormFileParam(name: "Inner.Child.File", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestThreeLevelDto + { + Outer = "outerVal", + Inner = new TestThreeLevelMiddleDto + { + Child = new TestNestedChildDto + { + File = new RemoteStreamContent(MakeStream("hello-3-levels"), "deep.txt", "text/plain"), + }, + }, + }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(2); + await AssertStringPart(parts[0], "Outer", "outerVal"); + await AssertStreamPart(parts[1], "Inner.Child.File", "hello-3-levels", "text/plain"); + } + + [Fact] + public async Task File_With_UTF8_FileName_Should_Survive_To_Content_Disposition_FileName_Star() + { + var action = BuildAction(parameters: new[] + { + FormFileParam(name: "file", nameOnMethod: "file"), + }); + var args = new Dictionary + { + ["file"] = new RemoteStreamContent(MakeStream("hello-utf8"), "中文-文件名.txt", "text/plain"), + }; + + var content = await InvokeAsync(action, args); + + var part = content.ShouldBeOfType().Single(); + var disposition = part.Headers.ContentDisposition; + disposition.ShouldNotBeNull(); + disposition!.FileNameStar.ShouldBe("中文-文件名.txt"); + (await part.ReadAsStringAsync()).ShouldBe("hello-utf8"); + } + + [Fact] + public async Task Dto_Treated_As_Body_When_Not_Registered_Should_Serialize_As_Json_With_File_Field_Embedded() + { + var action = BuildAction(parameters: new[] + { + new ParameterApiDescriptionModel + { + Name = "input", + NameOnMethod = "input", + Type = typeof(TestUploadDto).FullName!, + TypeSimple = "dto", + BindingSourceId = ParameterBindingSources.Body, + }, + }); + var args = new Dictionary + { + ["input"] = new TestUploadDto + { + Name = "Alice", + File = new RemoteStreamContent(MakeStream("ignored"), "x.txt", "text/plain"), + }, + }; + + var content = await InvokeAsync(action, args); + + var stringContent = content.ShouldBeOfType(); + stringContent.Headers.ContentType!.MediaType.ShouldBe("application/json"); + var body = await stringContent.ReadAsStringAsync(); + body.ShouldContain("\"Name\":\"Alice\""); + body.ShouldContain("\"File\""); + } + + [Fact] + public async Task Dto_With_Both_Stream_Property_And_Stream_Collection_Should_Emit_All_Parts() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Label", nameOnMethod: "input"), + FormFileParam(name: "Main", nameOnMethod: "input"), + FormFileParam(name: "Extras", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestMixedUploadDto + { + Label = "combo", + Main = new RemoteStreamContent(MakeStream("main-body"), "main.txt", "text/plain"), + Extras = new[] + { + new RemoteStreamContent(MakeStream("extra-a"), "ea.txt", "text/csv"), + new RemoteStreamContent(MakeStream("extra-b"), "eb.txt", "text/plain"), + }, + }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(4); + await AssertStringPart(parts[0], "Label", "combo"); + await AssertStreamPart(parts[1], "Main", "main-body", "text/plain"); + await AssertStreamPart(parts[2], "Extras", "extra-a", "text/csv"); + await AssertStreamPart(parts[3], "Extras", "extra-b", "text/plain"); + } + + [Fact] + public async Task Two_Different_Upload_Actions_On_Same_Builder_Should_Not_Pollute_Each_Other() + { + var actionA = BuildAction(parameters: new[] + { + FormParam(name: "Name", nameOnMethod: "input"), + FormFileParam(name: "File", nameOnMethod: "input"), + }); + var actionB = BuildAction(parameters: new[] + { + FormParam(name: "Label", nameOnMethod: "input"), + FormFileParam(name: "Files", nameOnMethod: "input"), + }); + + var argsA = new Dictionary + { + ["input"] = new TestUploadDto { Name = "A", File = new RemoteStreamContent(MakeStream("aa"), "a.txt", "text/plain") }, + }; + var argsB = new Dictionary + { + ["input"] = new TestUploadFilesDto + { + Label = "B", + Files = new[] { new RemoteStreamContent(MakeStream("bb"), "b.txt", "text/csv") }, + }, + }; + + var contentA = (await InvokeAsync(actionA, argsA)).ShouldBeOfType().ToList(); + var contentB = (await InvokeAsync(actionB, argsB)).ShouldBeOfType().ToList(); + + await AssertStringPart(contentA[0], "Name", "A"); + await AssertStreamPart(contentA[1], "File", "aa", "text/plain"); + await AssertStringPart(contentB[0], "Label", "B"); + await AssertStreamPart(contentB[1], "Files", "bb", "text/csv"); + } + + [Fact] + public async Task Inherited_Dto_With_File_Property_On_Base_Class_Should_Resolve_Via_Reflection() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Name", nameOnMethod: "input"), + FormFileParam(name: "File", nameOnMethod: "input"), + FormParam(name: "ChildOnly", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestInheritedUploadDto + { + Name = "inherited", + File = new RemoteStreamContent(MakeStream("from-base"), "base.txt", "text/plain"), + ChildOnly = "extra", + }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(3); + await AssertStringPart(parts[0], "Name", "inherited"); + await AssertStreamPart(parts[1], "File", "from-base", "text/plain"); + await AssertStringPart(parts[2], "ChildOnly", "extra"); + } + + [Fact] + public async Task Record_Dto_With_File_Property_Should_Resolve_Via_Reflection() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Name", nameOnMethod: "input"), + FormFileParam(name: "File", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestRecordUploadDto( + Name: "Diana", + File: new RemoteStreamContent(MakeStream("from-record"), "r.txt", "text/plain")), + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(2); + await AssertStringPart(parts[0], "Name", "Diana"); + await AssertStreamPart(parts[1], "File", "from-record", "text/plain"); + } + + [Fact] + public async Task Dto_With_DateTime_Enum_And_Nullable_Struct_Fields_Should_Round_Trip_As_String_Parts() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "When", nameOnMethod: "input"), + FormParam(name: "Status", nameOnMethod: "input"), + FormParam(name: "Quantity", nameOnMethod: "input"), + FormFileParam(name: "File", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestPrimitiveUploadDto + { + When = new DateTime(2025, 6, 1, 12, 34, 56, DateTimeKind.Utc), + Status = TestStatus.Active, + Quantity = 7, + File = new RemoteStreamContent(MakeStream("primitives"), "p.txt", "text/plain"), + }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(4); + (await parts[0].ReadAsStringAsync()).ShouldStartWith("2025-06-01T12:34:56"); + (await parts[1].ReadAsStringAsync()).ShouldBe("Active"); + (await parts[2].ReadAsStringAsync()).ShouldBe("7"); + await AssertStreamPart(parts[3], "File", "primitives", "text/plain"); + } + + [Fact] + public async Task Dto_With_Nullable_Struct_Field_Set_To_Null_Should_Skip_The_Part_Entirely() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Quantity", nameOnMethod: "input"), + FormFileParam(name: "File", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestPrimitiveUploadDto + { + Quantity = null, + File = new RemoteStreamContent(MakeStream("null-qty"), "n.txt", "text/plain"), + }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(1); + await AssertStreamPart(parts[0], "File", "null-qty", "text/plain"); + } + + [Fact] + public async Task Polymorphic_Dto_With_Derived_Type_Should_Reflect_Properties_From_Concrete_Runtime_Type() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Name", nameOnMethod: "input"), + FormParam(name: "ExtraField", nameOnMethod: "input"), + FormFileParam(name: "File", nameOnMethod: "input"), + }); + TestUploadDtoBase polymorphic = new TestPolymorphicDerivedDto + { + Name = "poly", + ExtraField = "derived-only", + File = new RemoteStreamContent(MakeStream("from-derived"), "d.txt", "text/plain"), + }; + var args = new Dictionary { ["input"] = polymorphic }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(3); + await AssertStringPart(parts[0], "Name", "poly"); + await AssertStringPart(parts[1], "ExtraField", "derived-only"); + await AssertStreamPart(parts[2], "File", "from-derived", "text/plain"); + } + + [Fact] + public async Task Generic_Dto_Closed_Over_Concrete_Type_Should_Reflect_Open_Generic_Property() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Payload", nameOnMethod: "input"), + FormFileParam(name: "File", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestGenericUploadDto + { + Payload = "closed-string", + File = new RemoteStreamContent(MakeStream("g.txt-body"), "g.txt", "text/plain"), + }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(2); + await AssertStringPart(parts[0], "Payload", "closed-string"); + await AssertStreamPart(parts[1], "File", "g.txt-body", "text/plain"); + } + + [Fact] + public async Task Generic_Dto_With_Integer_Payload_Should_Convert_Via_ConvertValueToString() + { + var action = BuildAction(parameters: new[] + { + FormParam(name: "Payload", nameOnMethod: "input"), + FormFileParam(name: "File", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["input"] = new TestGenericUploadDto + { + Payload = 42, + File = new RemoteStreamContent(MakeStream("int-payload"), "i.txt", "text/plain"), + }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(2); + await AssertStringPart(parts[0], "Payload", "42"); + } + + [Fact] + public async Task Underlying_Caller_Stream_Is_Wrapped_Without_Buffering_So_Retry_Sees_Drained_Source() + { + // Pin pass-through behaviour: HttpClient retry that re-reads `sourceStream` + // observes an empty body (no internal buffering). Opting in to buffering + // later would double-allocate for large uploads. + var action = BuildAction(parameters: new[] + { + FormFileParam(name: "file", nameOnMethod: "file"), + }); + var sourceStream = MakeStream("retry-payload"); + var args = new Dictionary + { + ["file"] = new RemoteStreamContent(sourceStream, "r.txt", "text/plain"), + }; + + var content = await InvokeAsync(action, args); + + var part = content.ShouldBeOfType().Single(); + sourceStream.Position.ShouldBe(0); + + (await part.ReadAsStringAsync()).ShouldBe("retry-payload"); + + sourceStream.Position.ShouldBe(sourceStream.Length); + } + + [Fact] + public async Task Action_With_No_Parameters_Should_Return_Null_Content_Without_Throwing() + { + var action = BuildAction(parameters: Array.Empty()); + var args = new Dictionary(); + + var content = await InvokeAsync(action, args); + + content.ShouldBeNull(); + } + + [Fact] + public async Task Path_Plus_Form_Plus_FormFile_Should_Skip_Path_And_Emit_Multipart_Only() + { + var action = BuildAction(parameters: new[] + { + new ParameterApiDescriptionModel + { + Name = "id", + NameOnMethod = "id", + Type = "System.Int32", + TypeSimple = "int", + BindingSourceId = ParameterBindingSources.Path, + }, + FormParam(name: "Name", nameOnMethod: "input"), + FormFileParam(name: "File", nameOnMethod: "input"), + }); + var args = new Dictionary + { + ["id"] = 7, + ["input"] = new TestUploadDto + { + Name = "Bob", + File = new RemoteStreamContent(MakeStream("path-mixed"), "p.txt", "text/plain"), + }, + }; + + var content = await InvokeAsync(action, args); + + var parts = content.ShouldBeOfType().ToList(); + parts.Count.ShouldBe(2); + await AssertStringPart(parts[0], "Name", "Bob"); + await AssertStreamPart(parts[1], "File", "path-mixed", "text/plain"); + } + + private Task InvokeAsync(ActionApiDescriptionModel action, IReadOnlyDictionary args) + => _builder.BuildContentAsync(action, args, _jsonSerializer, NoApiVersion); + + private static ActionApiDescriptionModel BuildAction(ParameterApiDescriptionModel[] parameters) + => new() + { + UniqueName = "TestAction", + Name = "TestAction", + HttpMethod = "POST", + Url = "api/test", + SupportedVersions = new List(), + ParametersOnMethod = new List(), + Parameters = parameters.ToList(), + ReturnValue = new ReturnValueApiDescriptionModel + { + Type = "System.String", + TypeSimple = "string", + }, + AuthorizeDatas = new List(), + }; + + private static ParameterApiDescriptionModel FormParam(string name, string nameOnMethod) + => new() + { + Name = name, + NameOnMethod = nameOnMethod, + Type = "System.String", + TypeSimple = "string", + BindingSourceId = ParameterBindingSources.Form, + }; + + private static ParameterApiDescriptionModel FormFileParam(string name, string nameOnMethod) + => new() + { + Name = name, + NameOnMethod = nameOnMethod, + Type = typeof(IRemoteStreamContent).FullName!, + TypeSimple = "stream", + BindingSourceId = ParameterBindingSources.FormFile, + }; + + private static MemoryStream MakeStream(string text) + { + var ms = new MemoryStream(); + ms.Write(Encoding.UTF8.GetBytes(text)); + ms.Position = 0; + return ms; + } + + private static async Task AssertStringPart(HttpContent part, string expectedName, string expectedValue) + { + var disposition = part.Headers.ContentDisposition; + disposition.ShouldNotBeNull(); + disposition!.Name!.Trim('"').ShouldBe(expectedName); + (await part.ReadAsStringAsync()).ShouldBe(expectedValue); + } + + private static async Task AssertStreamPart(HttpContent part, string expectedName, string expectedBody, string expectedContentType) + { + var disposition = part.Headers.ContentDisposition; + disposition.ShouldNotBeNull(); + disposition!.Name!.Trim('"').ShouldBe(expectedName); + part.Headers.ContentType!.MediaType.ShouldBe(expectedContentType); + (await part.ReadAsStringAsync()).ShouldBe(expectedBody); + } + + private class TestUploadDto + { + public string? Name { get; set; } + public string? Tag { get; set; } + public IRemoteStreamContent? File { get; set; } + } + + private class TestUploadFilesDto + { + public string? Label { get; set; } + public IEnumerable? Files { get; set; } + } + + private class TestNestedUploadDto + { + public string? Outer { get; set; } + public TestNestedChildDto? Child { get; set; } + } + + private class TestNestedChildDto + { + public IRemoteStreamContent? File { get; set; } + } + + private class TestThreeLevelDto + { + public string? Outer { get; set; } + public TestThreeLevelMiddleDto? Inner { get; set; } + } + + private class TestThreeLevelMiddleDto + { + public TestNestedChildDto? Child { get; set; } + } + + private class TestMixedUploadDto + { + public string? Label { get; set; } + public IRemoteStreamContent? Main { get; set; } + public IEnumerable? Extras { get; set; } + } + + private class TestUploadDtoBase + { + public string? Name { get; set; } + public IRemoteStreamContent? File { get; set; } + } + + private class TestInheritedUploadDto : TestUploadDtoBase + { + public string? ChildOnly { get; set; } + } + + private record TestRecordUploadDto(string Name, IRemoteStreamContent File); + + private class TestPrimitiveUploadDto + { + public DateTime When { get; set; } + public TestStatus Status { get; set; } + public int? Quantity { get; set; } + public IRemoteStreamContent? File { get; set; } + } + + private enum TestStatus + { + Pending = 0, + Active = 1, + Done = 2, + } + + private class TestPolymorphicDerivedDto : TestUploadDtoBase + { + public string? ExtraField { get; set; } + } + + private class TestGenericUploadDto + { + public T? Payload { get; set; } + public IRemoteStreamContent? File { get; set; } + } + + private class StubJsonSerializer : IJsonSerializer + { + public string Serialize(object obj, bool camelCase = true, bool indented = false) + => System.Text.Json.JsonSerializer.Serialize(obj); + + public T Deserialize(string jsonString, bool camelCase = true) + => System.Text.Json.JsonSerializer.Deserialize(jsonString)!; + + public object Deserialize(Type type, string jsonString, bool camelCase = true) + => System.Text.Json.JsonSerializer.Deserialize(jsonString, type)!; + } + + private class TestClock : IClock + { + public DateTime Now => DateTime.UtcNow; + public DateTimeKind Kind => DateTimeKind.Utc; + public bool SupportsMultipleTimezone => false; + public DateTime Normalize(DateTime dateTime) => dateTime; + public DateTime ConvertToUserTime(DateTime utcDateTime) => utcDateTime; + public DateTimeOffset ConvertToUserTime(DateTimeOffset dateTimeOffset) => dateTimeOffset; + public DateTime ConvertToUtc(DateTime dateTime) => dateTime; + } +} diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs index 21786155bf..fdf279e1ce 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/IRegularTestController.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Volo.Abp.Content; namespace Volo.Abp.Http.DynamicProxying; @@ -8,6 +9,26 @@ public interface IRegularTestController { Task IncrementValueAsync(int value); + Task GetPlainStringAsync(); + + Task GetProducesJsonStringAsync(); + + Task GetProducesTextStringAsync(); + + Task GetNullStringAsync(); + + Task GetProducesJsonNullStringAsync(); + + Task GetEmptyStringAsync(); + + Task GetEscapedStringAsync(); + + Task DownloadIconAsync(); + + Task GetReferenceTypeObjectAsync(); + + Task GetByteArrayAsync(); + Task GetException1Async(); Task GetException2Async(); diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PeopleAppServiceClientProxy_ReturnContentTypes_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PeopleAppServiceClientProxy_ReturnContentTypes_Tests.cs new file mode 100644 index 0000000000..b89692eb90 --- /dev/null +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PeopleAppServiceClientProxy_ReturnContentTypes_Tests.cs @@ -0,0 +1,84 @@ +using System.IO; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Http.Client; +using Volo.Abp.TestApp.Application; +using Xunit; + +namespace Volo.Abp.Http.DynamicProxying; + +public class PeopleAppServiceClientProxy_ReturnContentTypes_Tests : AbpHttpClientTestBase +{ + private readonly IPeopleAppService _peopleAppService; + + public PeopleAppServiceClientProxy_ReturnContentTypes_Tests() + { + _peopleAppService = ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task EchoStatusAsync_Should_Return_Plain_String_Without_Quotes() + { + var status = await _peopleAppService.EchoStatusAsync(); + status.ShouldBe("Open"); + status.StartsWith("\"").ShouldBeFalse(); + status.EndsWith("\"").ShouldBeFalse(); + } + + [Fact] + public async Task EchoStatusWithProducesJsonAsync_Should_Return_Plain_String_Without_Quotes() + { + var status = await _peopleAppService.EchoStatusWithProducesJsonAsync(); + status.ShouldBe("Open"); + status.StartsWith("\"").ShouldBeFalse(); + status.EndsWith("\"").ShouldBeFalse(); + } + + [Fact] + public async Task GetBinaryImageAsync_Should_Return_Real_Binary_Not_Json_Metadata() + { + using var content = await _peopleAppService.GetBinaryImageAsync(); + using var ms = new MemoryStream(); + await content.GetStream().CopyToAsync(ms); + var bytes = ms.ToArray(); + + content.FileName.ShouldBe("tiny.png"); + content.ContentType.ShouldStartWith("image/png"); + bytes.Length.ShouldBeGreaterThan(8); + + bytes[0].ShouldBe((byte)0x89); + bytes[1].ShouldBe((byte)0x50); + bytes[2].ShouldBe((byte)0x4E); + bytes[3].ShouldBe((byte)0x47); + } + + [Fact] + public async Task ThrowFromStringAsync_Should_Surface_Server_Exception_To_Client() + { + await Should.ThrowAsync( + () => _peopleAppService.ThrowFromStringAsync() + ); + } + + [Fact] + public async Task DownloadAsync_Should_Still_Work() + { + using var content = await _peopleAppService.DownloadAsync(); + using var reader = new StreamReader(content.GetStream()); + var text = await reader.ReadToEndAsync(); + text.ShouldBe("DownloadAsync"); + content.FileName.ShouldBe("download.rtf"); + content.ContentType.ShouldStartWith("application/rtf"); + } + + [Fact] + public async Task UploadAsync_String_Return_Should_Stay_Unquoted() + { + using var ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("hello")); + var result = await _peopleAppService.UploadAsync( + new Content.RemoteStreamContent(ms, "upload.txt", "text/plain")); + result.ShouldBe("hello:text/plain:upload.txt"); + result.StartsWith("\"").ShouldBeFalse(); + } +} diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs index 46b355090b..1d0864991b 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Content; namespace Volo.Abp.Http.DynamicProxying; @@ -20,6 +23,82 @@ public class RegularTestController : AbpController, IRegularTestController return Task.FromResult(value + 1); } + [HttpGet] + [Route("plain-string")] + public Task GetPlainStringAsync() + { + return Task.FromResult("Open"); + } + + [HttpGet] + [Route("produces-json-string")] + [Produces("application/json")] + public Task GetProducesJsonStringAsync() + { + return Task.FromResult("Open"); + } + + [HttpGet] + [Route("produces-text-string")] + [Produces("text/plain")] + public Task GetProducesTextStringAsync() + { + return Task.FromResult("Open"); + } + + [HttpGet] + [Route("null-string")] + public Task GetNullStringAsync() + { + return Task.FromResult(null!); + } + + [HttpGet] + [Route("produces-json-null-string")] + [Produces("application/json")] + public Task GetProducesJsonNullStringAsync() + { + return Task.FromResult(null!); + } + + [HttpGet] + [Route("empty-string")] + public Task GetEmptyStringAsync() + { + return Task.FromResult(string.Empty); + } + + [HttpGet] + [Route("escaped-string")] + [Produces("application/json")] + public Task GetEscapedStringAsync() + { + return Task.FromResult("a\"b\\c\nd"); + } + + [HttpGet] + [Route("download-icon")] + public Task DownloadIconAsync() + { + var bytes = Encoding.UTF8.GetBytes("ICON-BYTES"); + return Task.FromResult( + new RemoteStreamContent(new MemoryStream(bytes), "icon.bin", "application/octet-stream")); + } + + [HttpGet] + [Route("reference-type-object")] + public Task GetReferenceTypeObjectAsync() + { + return Task.FromResult(new Car { Year = 1999, Model = "BMW" }); + } + + [HttpGet] + [Route("byte-array")] + public Task GetByteArrayAsync() + { + return Task.FromResult(new byte[] { 1, 2, 3, 4 }); + } + [HttpGet] [Route("get-exception1")] public Task GetException1Async() diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_ReturnContentTypes_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_ReturnContentTypes_Tests.cs new file mode 100644 index 0000000000..92c8b5b23c --- /dev/null +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestControllerClientProxy_ReturnContentTypes_Tests.cs @@ -0,0 +1,103 @@ +using System.IO; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Xunit; + +namespace Volo.Abp.Http.DynamicProxying; + +public class RegularTestControllerClientProxy_ReturnContentTypes_Tests : AbpHttpClientTestBase +{ + private readonly IRegularTestController _controller; + + public RegularTestControllerClientProxy_ReturnContentTypes_Tests() + { + _controller = ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task GetPlainStringAsync_Should_Return_Unwrapped_String() + { + var result = await _controller.GetPlainStringAsync(); + result.ShouldBe("Open"); + result.StartsWith("\"").ShouldBeFalse(); + } + + [Fact] + public async Task GetProducesJsonStringAsync_Should_Strip_Json_Quotes() + { + var result = await _controller.GetProducesJsonStringAsync(); + result.ShouldBe("Open"); + result.StartsWith("\"").ShouldBeFalse(); + result.EndsWith("\"").ShouldBeFalse(); + } + + [Fact] + public async Task GetProducesTextStringAsync_Should_Return_Raw_Text_Body() + { + var result = await _controller.GetProducesTextStringAsync(); + result.ShouldBe("Open"); + result.StartsWith("\"").ShouldBeFalse(); + } + + [Fact] + public async Task GetNullStringAsync_Should_Return_Default_Or_Empty() + { + var result = await _controller.GetNullStringAsync(); + (result == null || result == string.Empty).ShouldBeTrue(); + } + + [Fact] + public async Task GetEmptyStringAsync_Should_Return_Empty() + { + var result = await _controller.GetEmptyStringAsync(); + (result == null || result == string.Empty).ShouldBeTrue(); + } + + [Fact] + public async Task GetProducesJsonNullStringAsync_Should_Not_Return_Literal_Null() + { + var result = await _controller.GetProducesJsonNullStringAsync(); + result.ShouldNotBe("null"); + (result == null || result == string.Empty).ShouldBeTrue(); + } + + [Fact] + public async Task GetEscapedStringAsync_Should_Decode_Escaped_Characters() + { + var result = await _controller.GetEscapedStringAsync(); + result.ShouldBe("a\"b\\c\nd"); + } + + [Fact] + public async Task DownloadIconAsync_Should_Return_Binary_Bytes() + { + using var content = await _controller.DownloadIconAsync(); + using var ms = new MemoryStream(); + await content.GetStream().CopyToAsync(ms); + ms.ToArray().ShouldBe(System.Text.Encoding.UTF8.GetBytes("ICON-BYTES")); + content.FileName.ShouldBe("icon.bin"); + } + + [Fact] + public async Task GetReferenceTypeObjectAsync_Should_Not_Be_Wrapped_As_RemoteStreamContent() + { + var result = await _controller.GetReferenceTypeObjectAsync(); + result.ShouldNotBeNull(); + result.ShouldNotBeAssignableTo(); + } + + [Fact] + public async Task GetByteArrayAsync_Should_Round_Trip_Bytes() + { + var bytes = await _controller.GetByteArrayAsync(); + bytes.ShouldBe(new byte[] { 1, 2, 3, 4 }); + } + + [Fact] + public async Task Existing_IncrementValueAsync_Regression_Should_Still_Work() + { + var result = await _controller.IncrementValueAsync(41); + result.ShouldBe(42); + } +} diff --git a/framework/test/Volo.Abp.Http.Tests/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel_Tests.cs b/framework/test/Volo.Abp.Http.Tests/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel_Tests.cs new file mode 100644 index 0000000000..d3c3197937 --- /dev/null +++ b/framework/test/Volo.Abp.Http.Tests/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel_Tests.cs @@ -0,0 +1,279 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Content; +using Volo.Abp.Http.Modeling; +using Xunit; + +namespace Volo.Abp.Http.Modeling; + +public class ReturnValueApiDescriptionModel_Tests +{ + [Fact] + public void Create_Without_ContentTypes_Should_Leave_Property_Null() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(string)); + + model.ShouldNotBeNull(); + model.TypeSimple.ShouldBe("string"); + model.ContentTypes.ShouldBeNull(); + } + + [Fact] + public void Create_With_ContentTypes_Should_Populate_The_Property() + { + var model = ReturnValueApiDescriptionModel.Create( + typeof(string), + new[] { "application/json", "text/plain" }); + + model.ContentTypes.ShouldNotBeNull(); + model.ContentTypes!.ShouldBe(new[] { "application/json", "text/plain" }); + } +} + +public class ReturnValueApiDescriptionModel_IsRemoteStream_Tests +{ + [Fact] + public void Direct_IRemoteStreamContent_Should_Be_True() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(IRemoteStreamContent)); + model.IsRemoteStream.ShouldBeTrue(); + } + + [Fact] + public void Concrete_RemoteStreamContent_Should_Be_True() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(RemoteStreamContent)); + model.IsRemoteStream.ShouldBeTrue(); + } + + [Fact] + public void Custom_Subclass_Of_IRemoteStreamContent_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(MyCustomStreamContent)); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void Task_Of_IRemoteStreamContent_Should_Be_True_After_UnwrapTask() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(Task)); + model.IsRemoteStream.ShouldBeTrue(); + } + + [Fact] + public void Task_Of_Custom_Stream_Subclass_Should_Be_False_After_UnwrapTask() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(Task)); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void IRemoteStreamContent_Array_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(IRemoteStreamContent[])); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void Concrete_RemoteStreamContent_Array_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(RemoteStreamContent[])); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void List_Of_IRemoteStreamContent_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(List)); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void IEnumerable_Of_IRemoteStreamContent_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(IEnumerable)); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void IReadOnlyCollection_Of_IRemoteStreamContent_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(IReadOnlyCollection)); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void Plain_String_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(string)); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void Int_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(int)); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void Plain_Dto_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(PlainDto)); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void Dto_Containing_IRemoteStreamContent_Property_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(DtoWithStream)); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void Dto_Inheriting_From_Type_With_Stream_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(DtoInheritingStream)); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void Byte_Array_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(byte[])); + model.IsRemoteStream.ShouldBeFalse(); + } + + [Fact] + public void Dictionary_Should_Be_False() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(Dictionary)); + model.IsRemoteStream.ShouldBeFalse(); + } + + private class MyCustomStreamContent : IRemoteStreamContent + { + public string? FileName => null; + public string? ContentType => null; + public long? ContentLength => null; + public Stream GetStream() => Stream.Null; + public void Dispose() { } + } + + private class PlainDto + { + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + } + + private class DtoWithStream + { + public string FileName { get; set; } = string.Empty; + public IRemoteStreamContent? File { get; set; } + } + + private class DtoInheritingStream : PlainDto + { + public IRemoteStreamContent? Stream { get; set; } + } +} + +public class ReturnValueApiDescriptionModel_BackwardsCompat_Tests +{ + [Fact] + public void Deserializing_Json_Without_ContentTypes_Field_Should_Leave_It_Null() + { + var json = """ + { + "type": "System.String", + "typeSimple": "string" + } + """; + + var model = System.Text.Json.JsonSerializer.Deserialize( + json, + new System.Text.Json.JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + + model.ShouldNotBeNull(); + model!.Type.ShouldBe("System.String"); + model.TypeSimple.ShouldBe("string"); + model.ContentTypes.ShouldBeNull(); + } + + [Fact] + public void Deserializing_Json_With_ContentTypes_Field_Should_Populate_It() + { + var json = """ + { + "type": "System.String", + "typeSimple": "string", + "contentTypes": ["application/json", "text/plain"] + } + """; + + var model = System.Text.Json.JsonSerializer.Deserialize( + json, + new System.Text.Json.JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + + model!.ContentTypes.ShouldNotBeNull(); + model.ContentTypes!.ShouldBe(new[] { "application/json", "text/plain" }); + } + + [Fact] + public void Serializing_With_Null_ContentTypes_Should_Emit_Null_Or_Omit() + { + var model = ReturnValueApiDescriptionModel.Create(typeof(string)); + var json = System.Text.Json.JsonSerializer.Serialize(model); + + var deserialized = System.Text.Json.JsonSerializer.Deserialize( + json, + new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + deserialized!.ContentTypes.ShouldBeNull(); + } +} + +public class ActionApiDescriptionModel_Tests +{ + [Fact] + public void Create_Should_Propagate_ReturnValueContentTypes() + { + var method = typeof(ActionApiDescriptionModel_Tests).GetMethod(nameof(SampleMethod))!; + var model = ActionApiDescriptionModel.Create( + uniqueName: "SampleMethod", + method: method, + url: "api/test/sample", + httpMethod: "GET", + supportedVersions: new[] { "1.0" }, + allowAnonymous: true, + authorizeDatas: null, + implementFrom: null, + returnValueContentTypes: new[] { "application/octet-stream" }); + + model.ReturnValue.ContentTypes.ShouldNotBeNull(); + model.ReturnValue.ContentTypes!.ShouldBe(new[] { "application/octet-stream" }); + } + + [Fact] + public void Create_Without_ReturnValueContentTypes_Should_Leave_Null() + { + var method = typeof(ActionApiDescriptionModel_Tests).GetMethod(nameof(SampleMethod))!; + var model = ActionApiDescriptionModel.Create( + uniqueName: "SampleMethod", + method: method, + url: "api/test/sample", + httpMethod: "GET", + supportedVersions: new[] { "1.0" }); + + model.ReturnValue.ContentTypes.ShouldBeNull(); + } + + public string SampleMethod() => string.Empty; +} diff --git a/framework/test/Volo.Abp.Http.Tests/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator_ContentTypes_Tests.cs b/framework/test/Volo.Abp.Http.Tests/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator_ContentTypes_Tests.cs new file mode 100644 index 0000000000..a7c63f8270 --- /dev/null +++ b/framework/test/Volo.Abp.Http.Tests/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator_ContentTypes_Tests.cs @@ -0,0 +1,266 @@ +#nullable enable +using System.Collections.Generic; +using Microsoft.Extensions.Options; +using Shouldly; +using Volo.Abp.Http.Modeling; +using Volo.Abp.Http.ProxyScripting.Generators; +using Volo.Abp.Http.ProxyScripting.Generators.JQuery; +using Xunit; + +namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery; + +public class JQueryProxyScriptGenerator_ContentTypes_Tests +{ + private readonly JQueryProxyScriptGenerator _generator = new( + Microsoft.Extensions.Options.Options.Create(new DynamicJavaScriptProxyOptions())); + + [Fact] + public void Should_Emit_Json_DataType_And_Accept_When_ContentTypes_Contain_Json() + { + var script = _generator.CreateScript(BuildAppModel( + returnType: "System.String", + contentTypes: new[] { "text/plain", "application/json", "text/json" })); + + script.ShouldContain("dataType: 'json'"); + script.ShouldContain("Accept: 'application/json'"); + script.ShouldNotContain("dataType: 'text'"); + } + + [Fact] + public void Should_Emit_Text_DataType_And_Accept_When_ContentTypes_Only_Text() + { + var script = _generator.CreateScript(BuildAppModel( + returnType: "System.String", + contentTypes: new[] { "text/plain", "text/csv" })); + + script.ShouldContain("dataType: 'text'"); + script.ShouldContain("Accept: 'text/plain'"); + } + + [Fact] + public void Should_Fallback_To_Legacy_Text_When_Return_Is_String_And_ContentTypes_Null() + { + var script = _generator.CreateScript(BuildAppModel( + returnType: "System.String", + contentTypes: null)); + + // Legacy behavior preserved: string return → dataType: 'text' (no Accept override) + script.ShouldContain("dataType: 'text'"); + script.ShouldNotContain("Accept:"); + } + + [Fact] + public void Should_Emit_No_DataType_For_Non_String_Return_Without_ContentTypes() + { + var script = _generator.CreateScript(BuildAppModel( + returnType: "System.Int32", + contentTypes: null)); + + script.ShouldNotContain("dataType: 'text'"); + script.ShouldNotContain("dataType: 'json'"); + } + + [Fact] + public void Should_Not_Emit_Json_DataType_When_Only_Binary_ContentTypes() + { + var script = _generator.CreateScript(BuildAppModel( + returnType: "System.Byte[]", + contentTypes: new[] { "application/octet-stream", "image/png" })); + + // jQuery dataType doesn't have a clean "blob" — fall through to no override + script.ShouldNotContain("dataType: 'json'"); + script.ShouldNotContain("dataType: 'text'"); + } + + [Fact] + public void Should_Prefer_Json_When_Json_Present_Even_With_Other_Types() + { + var script = _generator.CreateScript(BuildAppModel( + returnType: "My.Project.UserDto", + contentTypes: new[] { "application/xml", "application/json", "text/html" })); + + script.ShouldContain("dataType: 'json'"); + script.ShouldContain("Accept: 'application/json'"); + } + + [Fact] + public void Case_Insensitive_Json_Detection() + { + var script = _generator.CreateScript(BuildAppModel( + returnType: "System.String", + contentTypes: new[] { "APPLICATION/JSON" })); + + script.ShouldContain("dataType: 'json'"); + } + + [Fact] + public void IsRemoteStream_Should_Skip_DataType_Override_To_Avoid_JSON_Metadata_Regression() + { + // IRemoteStreamContent: ABP advertises application/json in formatter list, but + // forcing dataType:'json' here would make the server JSON-serialise the stream. + var model = BuildAppModel( + returnType: "Volo.Abp.Content.IRemoteStreamContent", + contentTypes: new[] { "text/plain", "application/json", "text/json" }, + isRemoteStream: true); + + var script = _generator.CreateScript(model); + + script.ShouldNotContain("dataType: 'json'"); + script.ShouldNotContain("Accept: 'application/json'"); + } + + [Fact] + public void Multipart_Upload_Should_Emit_FormData_Body_With_ProcessData_And_ContentType_False() + { + var script = _generator.CreateScript(BuildUploadModel( + uploadParameters: new[] + { + ("Name", "input", ParameterBindingSources.Form), + ("File", "input", ParameterBindingSources.FormFile), + })); + + script.ShouldContain("data: input"); + script.ShouldContain("processData: false"); + script.ShouldContain("contentType: false"); + script.ShouldNotContain("contentType: 'application/x-www-form-urlencoded"); + } + + [Fact] + public void Multipart_Upload_Should_Use_NameOnMethod_As_Data_Variable() + { + var script = _generator.CreateScript(BuildUploadModel( + uploadParameters: new[] + { + ("file", "file", ParameterBindingSources.FormFile), + })); + + script.ShouldContain("data: file"); + script.ShouldContain("processData: false"); + script.ShouldContain("contentType: false"); + } + + [Fact] + public void Plain_Form_Action_Without_FormFile_Should_Still_Emit_UrlEncoded_ContentType() + { + var script = _generator.CreateScript(BuildUploadModel( + uploadParameters: new[] + { + ("Name", "input", ParameterBindingSources.Form), + })); + + script.ShouldContain("contentType: 'application/x-www-form-urlencoded; charset=UTF-8'"); + script.ShouldNotContain("processData: false"); + script.ShouldNotContain("contentType: false"); + } + + [Fact] + public void Multipart_Upload_Should_Skip_FormPostData_And_Body_Generation() + { + var script = _generator.CreateScript(BuildUploadModel( + uploadParameters: new[] + { + ("Name", "input", ParameterBindingSources.Form), + ("File", "input", ParameterBindingSources.FormFile), + })); + + script.ShouldNotContain("'Name=' + "); + script.ShouldNotContain("JSON.stringify"); + } + + [Fact] + public void Multiple_Direct_FormFile_Params_Should_Forward_Only_First_Var_Known_Limitation() + { + var script = _generator.CreateScript(BuildUploadModel( + uploadParameters: new[] + { + ("file1", "file1", ParameterBindingSources.FormFile), + ("file2", "file2", ParameterBindingSources.FormFile), + })); + + script.ShouldContain("data: file1"); + script.ShouldNotContain("data: file2"); + script.ShouldNotContain("$.merge(file1, file2)"); + } + + private static ApplicationApiDescriptionModel BuildAppModel(string returnType, IList? contentTypes, bool isRemoteStream = false) + { + var model = ApplicationApiDescriptionModel.Create(); + var module = model.GetOrAddModule("app", "Default"); + var controller = module.GetOrAddController( + name: "TestController", + groupName: null, + isRemoteService: true, + isIntegrationService: false, + apiVersion: null, + type: typeof(object)); + + var action = new ActionApiDescriptionModel + { + UniqueName = "DoSomethingAsync", + Name = "DoSomethingAsync", + HttpMethod = "GET", + Url = "api/test/do-something", + SupportedVersions = new List(), + ParametersOnMethod = new List(), + Parameters = new List(), + ReturnValue = new ReturnValueApiDescriptionModel + { + Type = returnType, + TypeSimple = returnType, + ContentTypes = contentTypes, + IsRemoteStream = isRemoteStream, + }, + AuthorizeDatas = new List(), + }; + controller.AddAction("DoSomethingAsync", action); + + return model; + } + + private static ApplicationApiDescriptionModel BuildUploadModel( + (string Name, string NameOnMethod, string BindingSourceId)[] uploadParameters) + { + var model = ApplicationApiDescriptionModel.Create(); + var module = model.GetOrAddModule("app", "Default"); + var controller = module.GetOrAddController( + name: "TestController", + groupName: null, + isRemoteService: true, + isIntegrationService: false, + apiVersion: null, + type: typeof(object)); + + var parameters = new List(); + foreach (var (name, nameOnMethod, binding) in uploadParameters) + { + parameters.Add(new ParameterApiDescriptionModel + { + Name = name, + NameOnMethod = nameOnMethod, + Type = "System.String", + TypeSimple = "string", + BindingSourceId = binding, + }); + } + + var action = new ActionApiDescriptionModel + { + UniqueName = "UploadAsync", + Name = "UploadAsync", + HttpMethod = "POST", + Url = "api/test/upload", + SupportedVersions = new List(), + ParametersOnMethod = new List(), + Parameters = parameters, + ReturnValue = new ReturnValueApiDescriptionModel + { + Type = "System.Void", + TypeSimple = "void", + }, + AuthorizeDatas = new List(), + }; + controller.AddAction("UploadAsync", action); + + return model; + } +} diff --git a/framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpReverseMapperly_Tests.cs b/framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpReverseMapperly_Tests.cs index 5d166f7dc8..6dc7b5e2a0 100644 --- a/framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpReverseMapperly_Tests.cs +++ b/framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpReverseMapperly_Tests.cs @@ -31,18 +31,18 @@ public partial class MyReverseClassMapper : TwoWayMapperBase Task GetParamsFromQueryAsync(GetParamsInput input); Task GetParamsFromFormAsync(GetParamsInput input); + + Task EchoStatusAsync(); + + Task EchoStatusWithProducesJsonAsync(); + + Task GetBinaryImageAsync(); + + Task ThrowFromStringAsync(); } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs index a3920ec8ca..5eb9370754 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Shouldly; +using Volo.Abp; using Volo.Abp.Application.Dtos; using Volo.Abp.TestApp.Domain; using Volo.Abp.Domain.Repositories; @@ -94,6 +95,30 @@ public class PeopleAppService : CrudAppService, IPeople return new RemoteStreamContent(memoryStream, "download.rtf", "application/rtf"); } + public Task EchoStatusAsync() + { + return Task.FromResult("Open"); + } + + [Produces("application/json")] + public Task EchoStatusWithProducesJsonAsync() + { + return Task.FromResult("Open"); + } + + public Task GetBinaryImageAsync() + { + var bytes = Convert.FromBase64String( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="); + return Task.FromResult( + new RemoteStreamContent(new MemoryStream(bytes), "tiny.png", "image/png")); + } + + public Task ThrowFromStringAsync() + { + throw new BusinessException("TestApp.StringEndpointBoom", "string endpoint failed"); + } + public async Task UploadAsync(IRemoteStreamContent streamContent) { using (var reader = new StreamReader(streamContent.GetStream())) diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json index 59ac7ca94b..ea01f3ed3e 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/package.json @@ -3,8 +3,8 @@ "name": "asp.net", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.3", - "@abp/prismjs": "~10.5.0-rc.3", - "@abp/highlight.js": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.4", + "@abp/prismjs": "~10.5.0-rc.4", + "@abp/highlight.js": "~10.5.0-rc.4" } } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock index a1f6b130b1..32c8a72692 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/yarn.lock @@ -2,204 +2,204 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" - integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== - dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" - "@abp/bootstrap" "~10.5.0-rc.3" - "@abp/bootstrap-datepicker" "~10.5.0-rc.3" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" - "@abp/datatables.net-bs5" "~10.5.0-rc.3" - "@abp/font-awesome" "~10.5.0-rc.3" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" - "@abp/lodash" "~10.5.0-rc.3" - "@abp/luxon" "~10.5.0-rc.3" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" - "@abp/moment" "~10.5.0-rc.3" - "@abp/select2" "~10.5.0-rc.3" - "@abp/sweetalert2" "~10.5.0-rc.3" - "@abp/timeago" "~10.5.0-rc.3" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" - integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.4.tgz#b14d0142a2b7d37e4b6d1ff700e495ff7561891a" + integrity sha512-i0AC6ZSYTxK42psQc1VbyHVouKHGii80E0Eax42jGoTlim8D1kNgetO+8+Pltg/5rL0HJViWrWPwyG7mBipA2w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.4" + "@abp/bootstrap" "~10.5.0-rc.4" + "@abp/bootstrap-datepicker" "~10.5.0-rc.4" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.4" + "@abp/datatables.net-bs5" "~10.5.0-rc.4" + "@abp/font-awesome" "~10.5.0-rc.4" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.4" + "@abp/lodash" "~10.5.0-rc.4" + "@abp/luxon" "~10.5.0-rc.4" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.4" + "@abp/moment" "~10.5.0-rc.4" + "@abp/select2" "~10.5.0-rc.4" + "@abp/sweetalert2" "~10.5.0-rc.4" + "@abp/timeago" "~10.5.0-rc.4" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.4.tgz#79822aadec8133133201d59a1c3dc6b7ca226c1f" + integrity sha512-149kt2gvpdG/rUn6Z44YF3m2Bsz5Z8mZfEDXwt7x+MjB84cXbm16sVcKdSkPkg2mXcLycJYpMGG/cgSlH+FvHQ== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" - integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== +"@abp/bootstrap-datepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.4.tgz#66d04f31a8d021d151452afc4fcbc7315d89758e" + integrity sha512-EMcZlWJWU+Ypbw3TGraC8K9hM3+HWLhmEz+0vj6yRajmP5MczJfCyxt4AODaPEmf4uRV3WQ3wV0OjHZdmG+QhQ== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" - integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.4.tgz#1aa514548b762d0b3bd628a29edd59585510532b" + integrity sha512-TnEg+k6XVWROtni7sXCBkouBjjDLmN7C0lP4XcUztSSHGo7l+r+cl+pi1iSOuHdRX304/CO+3PZUBKYku3A3+A== dependencies: - "@abp/moment" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.4" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" - integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== +"@abp/bootstrap@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.4.tgz#591b5a9da1f4fcf48c9a419f4acb985ddd48d876" + integrity sha512-aJtjmCaLg+TSrA/PL1X4d6AYsIv/8AHprDFJ6cXTsZkQTYjyTZUfjw15JHA+mLe6BionsGOtsXryid29IbX2Mg== dependencies: - "@abp/core" "~10.5.0-rc.3" - "@abp/popper.js" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" + "@abp/popper.js" "~10.5.0-rc.4" bootstrap "^5.3.8" -"@abp/clipboard@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.3.tgz#b4d7eae0554a7872fb497a1c156dbd70b23954a1" - integrity sha512-B7hU650JeKPNbOWrYDLipuxLvnAgsj3Q+IyqxwrdS1JUWajQpu30d5c5t9y28w//5xXqyp5xoWoEzDOg66/8Lw== +"@abp/clipboard@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.4.tgz#3d48c5e461997c50f532a8af6394d90de2a80c42" + integrity sha512-V77e/wptcMJbF3Zhki1/jGVAzlbd5W/Q0wWEa3xiSJqa97g8VmjNB+Kzz6FoTzAUHT2oBJllGO8d2+nhmqLl4g== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" clipboard "^2.0.11" -"@abp/core@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" - integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== +"@abp/core@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.4.tgz#1a5aec4eb83daf1400150d7c0c4dc81c7265ae49" + integrity sha512-AA4NdfAzA7zLWmAdS5dCjVshyidTn/bEMqlhfIOtWOnp3nEjdtURhmz6yA22MM6YAuKkda9iE1OtHu+jMEG3vQ== dependencies: - "@abp/utils" "~10.5.0-rc.3" + "@abp/utils" "~10.5.0-rc.4" -"@abp/datatables.net-bs5@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" - integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== +"@abp/datatables.net-bs5@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.4.tgz#ec26b8a22d58880755f3593ac635b0daf082c886" + integrity sha512-f5WEXTEQC8S73Hz2QpCMkrh9vyFJifQ2Q9Jp6r0iLRURpBItzliUsSHcDAhdo3oPtuH9HOumQvISH4Y8S+EPzQ== dependencies: - "@abp/datatables.net" "~10.5.0-rc.3" + "@abp/datatables.net" "~10.5.0-rc.4" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" - integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== +"@abp/datatables.net@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.4.tgz#46254de6e712640f12058e2f167083b3011e2c1b" + integrity sha512-i81nFF+Qevs602t0zZWAhQWBpCV9FBHixwWQmVDCfyhDYJbiMLUBPcscIqF7EIv1tz/dEdW2GofvEzIMcffbAg== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" - integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== +"@abp/font-awesome@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.4.tgz#d7a8e4d32f84bfbe46c55dc93ad33f185e0f3848" + integrity sha512-guGsjSWTlUYtrvNlekEL3qEgEp1HeABnNEQdQF5yyukHXoOTC0pR/aPTmMiKHTiYfqpc3LoQMv+TU+ZAVQrU4w== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/highlight.js@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-10.5.0-rc.3.tgz#ceffb929de26eec5903149a4eb3b64900ad9f7f4" - integrity sha512-crb7LwCqMzt0wX5hC4zuIVgML+9ljDJNjh6kCnf5N5LhWH8vsBf+8dwdcp8Jm9/XE/1rOtXN3938lkFo9F/0Qw== +"@abp/highlight.js@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-10.5.0-rc.4.tgz#591a78f06b86ba3c46ab548e4380c486252562ef" + integrity sha512-FA7U9B559I0wug29E30vvNYw9fxjv3mVvq4ashftw7V5H9oQcztUbaDe+nOeSdPLwI8YQRa/0QdXky4QmsdDQQ== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@highlightjs/cdn-assets" "~11.11.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" - integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.4.tgz#93bb721e8dfff9a824d44980f6ce859c8c3866bb" + integrity sha512-U1b+2lnfdNQIVBUM8CjbP/EJaLDZ7EHrvWeJFn0aJYHglV8Jqka4+jPu0Be8NP1NqyQQSrhtqdf8axp742COmQ== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.3" + "@abp/jquery-validation" "~10.5.0-rc.4" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" - integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== +"@abp/jquery-validation@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.4.tgz#b90ceb992b9c0a7fc4f8faccaaaa628c259f028e" + integrity sha512-0qYfsAwhcatyiJpeZejmG3DgOAoCbWQcHOUM0Bgxg8gh+vcuzXu//956SBJqbkeTJiaeY4z8uULaQZpAiF+ZRQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" - integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== +"@abp/jquery@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.4.tgz#623d7be22842b37505795e15069f0993f7c417eb" + integrity sha512-IATc1SVqRPd3xDCPHQZh1GaMgGGcVmxUYFDo/+t6c768iLCwLpJPuoNebrBSeN+z9+opbuZrFLNdwWQ+Ch72QA== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" - integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== +"@abp/lodash@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.4.tgz#284d4ebf314cfb1d96e66e8994f22b44055e4130" + integrity sha512-rswQ/laMhlu1MA+51CcIbQR1iZcOdSwF2HzxWccCqsaBVCOWy4G9TIj1gk2QfQSYMkBl6OWNprOea7XKhaSfzw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" - integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== +"@abp/luxon@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.4.tgz#c35120586c16afbafdefbb0a4b1cefb3e7e97690" + integrity sha512-Mk5oMzecFf5v35/EW2FvVYmQX16giT8J/QpeqyaPWbp0UptOSYrA7E+gHoJGvs3SvY24KkPzTfdcaWzCyV6zIg== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" - integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.4.tgz#f7e215c48012306d449544be35b384face75caa8" + integrity sha512-nu00haqvL68AnGIX0enLy3XvhDq72/ws7f+oh8Fr5gPPtywI1fzMnkXIGoXewE/5wcD7Fs/hHjJorFlX0yWOgw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" - integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== +"@abp/moment@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.4.tgz#4d02915b3650b8afe1160ab91c09d274bd9108d6" + integrity sha512-6giU879bnFltkefbb/it4bWxBfZn+QDzDgP1aPC4xNIa134ZXzkcSMRlMyUBa/96OZ6mmmYo/6wLFZLCAsOPZw== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" - integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== +"@abp/popper.js@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.4.tgz#4a74c6e1f72da2ba8f67b33856196558a5efff72" + integrity sha512-oJ4pDrVCgu0RadsdwlppGN3tPzMymQ5DWhqK6CKEeir4lBFubKDe1NAhawZ4qGtIXKFWIbE30prFg11pf/zj9Q== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@popperjs/core" "^2.11.8" -"@abp/prismjs@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.3.tgz#c2a0816f5a6d0ae7a6ae56687bc7d62bef89f707" - integrity sha512-1YsVUx3GZID5H3GlUFljO8gEnKSDigbDsfX2zD/Kue3vCYnlq0Ysrx+jesNAvLP/UO5JBMPiAzr8xHU+KV5q6g== +"@abp/prismjs@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.4.tgz#d130f9e872eef068da63497b53ef1823347dc403" + integrity sha512-epb+8m21G8glvjHh2NgzO6kK3JDgliwXmfyIvRBg2/dUyovCyQ9A4V9jOA0VfaqTHz7sl0liM7M66/gP3XiGhA== dependencies: - "@abp/clipboard" "~10.5.0-rc.3" - "@abp/core" "~10.5.0-rc.3" + "@abp/clipboard" "~10.5.0-rc.4" + "@abp/core" "~10.5.0-rc.4" prismjs "^1.30.0" -"@abp/select2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" - integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== +"@abp/select2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.4.tgz#87448d8b1d2ec97c0cb97e1c08db3ba2618e29f7" + integrity sha512-64utn6ikY5q71TIfO9qhXWpy0pGrj1YEpAdO/q3qYoFYf9RDz9mSFd2cJ8Noasph/CmZSKFK3YwPcHFkSeLTYQ== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" - integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== +"@abp/sweetalert2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.4.tgz#aa667ee6edb6b008961a463f7f477a4671d28381" + integrity sha512-+/OKRL2hzohXryUD2rZO7aFLL7G7A300/QK82BKnYSXr7fh2wsVEC9g4/DXnaoygad7k5KS3NWUMIpEL21ROpw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" - integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== +"@abp/timeago@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.4.tgz#02b27e97c20b6b226cbdaa43d7bfb367218a626f" + integrity sha512-XCopnK5kphD6+OQ6+RHsgZHL7WtaXBbiYHpQmo7JHr9iVBwvLmWSzJZMIbrfZTczkhHk2VEwMQWSUwnJsWCWvQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" - integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== +"@abp/utils@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.4.tgz#3bddb096f7537b69c08c2c81c99607519b58c200" + integrity sha512-jYNIF72jXBYHWxrHTe3Na5yhPm1e+b76U0PpA2chJ+Qs6LHWdwQXGGvplQO3KrInrA+dNqrWzera7a1N9U1WhQ== dependencies: just-compare "^2.3.0" diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json index ab16ef4cb2..723fa049d0 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package.json @@ -3,8 +3,8 @@ "name": "asp.net", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3", - "@abp/prismjs": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.4", + "@abp/prismjs": "~10.5.0-rc.4" }, "devDependencies": {} } diff --git a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock index e30ecb505c..e36895c5fe 100644 --- a/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock +++ b/modules/basic-theme/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/yarn.lock @@ -2,203 +2,203 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" - integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" - -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" - integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== - dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" - "@abp/bootstrap" "~10.5.0-rc.3" - "@abp/bootstrap-datepicker" "~10.5.0-rc.3" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" - "@abp/datatables.net-bs5" "~10.5.0-rc.3" - "@abp/font-awesome" "~10.5.0-rc.3" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" - "@abp/lodash" "~10.5.0-rc.3" - "@abp/luxon" "~10.5.0-rc.3" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" - "@abp/moment" "~10.5.0-rc.3" - "@abp/select2" "~10.5.0-rc.3" - "@abp/sweetalert2" "~10.5.0-rc.3" - "@abp/timeago" "~10.5.0-rc.3" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" - integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.4.tgz#c24c2528ab89c12876d3051c7af3e4487573eba6" + integrity sha512-f/B88/lhr1aGWW8FNgqvqTHWtCL8psc11cna0I4dwbfk0vAOxsoMKoksX7tLK2spxm5y4j7ZET0c+DehLlMHuw== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.4" + +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.4.tgz#b14d0142a2b7d37e4b6d1ff700e495ff7561891a" + integrity sha512-i0AC6ZSYTxK42psQc1VbyHVouKHGii80E0Eax42jGoTlim8D1kNgetO+8+Pltg/5rL0HJViWrWPwyG7mBipA2w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.4" + "@abp/bootstrap" "~10.5.0-rc.4" + "@abp/bootstrap-datepicker" "~10.5.0-rc.4" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.4" + "@abp/datatables.net-bs5" "~10.5.0-rc.4" + "@abp/font-awesome" "~10.5.0-rc.4" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.4" + "@abp/lodash" "~10.5.0-rc.4" + "@abp/luxon" "~10.5.0-rc.4" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.4" + "@abp/moment" "~10.5.0-rc.4" + "@abp/select2" "~10.5.0-rc.4" + "@abp/sweetalert2" "~10.5.0-rc.4" + "@abp/timeago" "~10.5.0-rc.4" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.4.tgz#79822aadec8133133201d59a1c3dc6b7ca226c1f" + integrity sha512-149kt2gvpdG/rUn6Z44YF3m2Bsz5Z8mZfEDXwt7x+MjB84cXbm16sVcKdSkPkg2mXcLycJYpMGG/cgSlH+FvHQ== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" - integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== +"@abp/bootstrap-datepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.4.tgz#66d04f31a8d021d151452afc4fcbc7315d89758e" + integrity sha512-EMcZlWJWU+Ypbw3TGraC8K9hM3+HWLhmEz+0vj6yRajmP5MczJfCyxt4AODaPEmf4uRV3WQ3wV0OjHZdmG+QhQ== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" - integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.4.tgz#1aa514548b762d0b3bd628a29edd59585510532b" + integrity sha512-TnEg+k6XVWROtni7sXCBkouBjjDLmN7C0lP4XcUztSSHGo7l+r+cl+pi1iSOuHdRX304/CO+3PZUBKYku3A3+A== dependencies: - "@abp/moment" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.4" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" - integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== +"@abp/bootstrap@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.4.tgz#591b5a9da1f4fcf48c9a419f4acb985ddd48d876" + integrity sha512-aJtjmCaLg+TSrA/PL1X4d6AYsIv/8AHprDFJ6cXTsZkQTYjyTZUfjw15JHA+mLe6BionsGOtsXryid29IbX2Mg== dependencies: - "@abp/core" "~10.5.0-rc.3" - "@abp/popper.js" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" + "@abp/popper.js" "~10.5.0-rc.4" bootstrap "^5.3.8" -"@abp/clipboard@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.3.tgz#b4d7eae0554a7872fb497a1c156dbd70b23954a1" - integrity sha512-B7hU650JeKPNbOWrYDLipuxLvnAgsj3Q+IyqxwrdS1JUWajQpu30d5c5t9y28w//5xXqyp5xoWoEzDOg66/8Lw== +"@abp/clipboard@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.4.tgz#3d48c5e461997c50f532a8af6394d90de2a80c42" + integrity sha512-V77e/wptcMJbF3Zhki1/jGVAzlbd5W/Q0wWEa3xiSJqa97g8VmjNB+Kzz6FoTzAUHT2oBJllGO8d2+nhmqLl4g== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" clipboard "^2.0.11" -"@abp/core@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" - integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== +"@abp/core@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.4.tgz#1a5aec4eb83daf1400150d7c0c4dc81c7265ae49" + integrity sha512-AA4NdfAzA7zLWmAdS5dCjVshyidTn/bEMqlhfIOtWOnp3nEjdtURhmz6yA22MM6YAuKkda9iE1OtHu+jMEG3vQ== dependencies: - "@abp/utils" "~10.5.0-rc.3" + "@abp/utils" "~10.5.0-rc.4" -"@abp/datatables.net-bs5@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" - integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== +"@abp/datatables.net-bs5@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.4.tgz#ec26b8a22d58880755f3593ac635b0daf082c886" + integrity sha512-f5WEXTEQC8S73Hz2QpCMkrh9vyFJifQ2Q9Jp6r0iLRURpBItzliUsSHcDAhdo3oPtuH9HOumQvISH4Y8S+EPzQ== dependencies: - "@abp/datatables.net" "~10.5.0-rc.3" + "@abp/datatables.net" "~10.5.0-rc.4" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" - integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== +"@abp/datatables.net@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.4.tgz#46254de6e712640f12058e2f167083b3011e2c1b" + integrity sha512-i81nFF+Qevs602t0zZWAhQWBpCV9FBHixwWQmVDCfyhDYJbiMLUBPcscIqF7EIv1tz/dEdW2GofvEzIMcffbAg== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" - integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== +"@abp/font-awesome@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.4.tgz#d7a8e4d32f84bfbe46c55dc93ad33f185e0f3848" + integrity sha512-guGsjSWTlUYtrvNlekEL3qEgEp1HeABnNEQdQF5yyukHXoOTC0pR/aPTmMiKHTiYfqpc3LoQMv+TU+ZAVQrU4w== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" - integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.4.tgz#93bb721e8dfff9a824d44980f6ce859c8c3866bb" + integrity sha512-U1b+2lnfdNQIVBUM8CjbP/EJaLDZ7EHrvWeJFn0aJYHglV8Jqka4+jPu0Be8NP1NqyQQSrhtqdf8axp742COmQ== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.3" + "@abp/jquery-validation" "~10.5.0-rc.4" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" - integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== +"@abp/jquery-validation@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.4.tgz#b90ceb992b9c0a7fc4f8faccaaaa628c259f028e" + integrity sha512-0qYfsAwhcatyiJpeZejmG3DgOAoCbWQcHOUM0Bgxg8gh+vcuzXu//956SBJqbkeTJiaeY4z8uULaQZpAiF+ZRQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" - integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== +"@abp/jquery@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.4.tgz#623d7be22842b37505795e15069f0993f7c417eb" + integrity sha512-IATc1SVqRPd3xDCPHQZh1GaMgGGcVmxUYFDo/+t6c768iLCwLpJPuoNebrBSeN+z9+opbuZrFLNdwWQ+Ch72QA== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" - integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== +"@abp/lodash@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.4.tgz#284d4ebf314cfb1d96e66e8994f22b44055e4130" + integrity sha512-rswQ/laMhlu1MA+51CcIbQR1iZcOdSwF2HzxWccCqsaBVCOWy4G9TIj1gk2QfQSYMkBl6OWNprOea7XKhaSfzw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" - integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== +"@abp/luxon@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.4.tgz#c35120586c16afbafdefbb0a4b1cefb3e7e97690" + integrity sha512-Mk5oMzecFf5v35/EW2FvVYmQX16giT8J/QpeqyaPWbp0UptOSYrA7E+gHoJGvs3SvY24KkPzTfdcaWzCyV6zIg== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" - integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.4.tgz#f7e215c48012306d449544be35b384face75caa8" + integrity sha512-nu00haqvL68AnGIX0enLy3XvhDq72/ws7f+oh8Fr5gPPtywI1fzMnkXIGoXewE/5wcD7Fs/hHjJorFlX0yWOgw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" - integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== +"@abp/moment@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.4.tgz#4d02915b3650b8afe1160ab91c09d274bd9108d6" + integrity sha512-6giU879bnFltkefbb/it4bWxBfZn+QDzDgP1aPC4xNIa134ZXzkcSMRlMyUBa/96OZ6mmmYo/6wLFZLCAsOPZw== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" - integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== +"@abp/popper.js@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.4.tgz#4a74c6e1f72da2ba8f67b33856196558a5efff72" + integrity sha512-oJ4pDrVCgu0RadsdwlppGN3tPzMymQ5DWhqK6CKEeir4lBFubKDe1NAhawZ4qGtIXKFWIbE30prFg11pf/zj9Q== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@popperjs/core" "^2.11.8" -"@abp/prismjs@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.3.tgz#c2a0816f5a6d0ae7a6ae56687bc7d62bef89f707" - integrity sha512-1YsVUx3GZID5H3GlUFljO8gEnKSDigbDsfX2zD/Kue3vCYnlq0Ysrx+jesNAvLP/UO5JBMPiAzr8xHU+KV5q6g== +"@abp/prismjs@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.4.tgz#d130f9e872eef068da63497b53ef1823347dc403" + integrity sha512-epb+8m21G8glvjHh2NgzO6kK3JDgliwXmfyIvRBg2/dUyovCyQ9A4V9jOA0VfaqTHz7sl0liM7M66/gP3XiGhA== dependencies: - "@abp/clipboard" "~10.5.0-rc.3" - "@abp/core" "~10.5.0-rc.3" + "@abp/clipboard" "~10.5.0-rc.4" + "@abp/core" "~10.5.0-rc.4" prismjs "^1.30.0" -"@abp/select2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" - integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== +"@abp/select2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.4.tgz#87448d8b1d2ec97c0cb97e1c08db3ba2618e29f7" + integrity sha512-64utn6ikY5q71TIfO9qhXWpy0pGrj1YEpAdO/q3qYoFYf9RDz9mSFd2cJ8Noasph/CmZSKFK3YwPcHFkSeLTYQ== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" - integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== +"@abp/sweetalert2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.4.tgz#aa667ee6edb6b008961a463f7f477a4671d28381" + integrity sha512-+/OKRL2hzohXryUD2rZO7aFLL7G7A300/QK82BKnYSXr7fh2wsVEC9g4/DXnaoygad7k5KS3NWUMIpEL21ROpw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" - integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== +"@abp/timeago@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.4.tgz#02b27e97c20b6b226cbdaa43d7bfb367218a626f" + integrity sha512-XCopnK5kphD6+OQ6+RHsgZHL7WtaXBbiYHpQmo7JHr9iVBwvLmWSzJZMIbrfZTczkhHk2VEwMQWSUwnJsWCWvQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" - integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== +"@abp/utils@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.4.tgz#3bddb096f7537b69c08c2c81c99607519b58c200" + integrity sha512-jYNIF72jXBYHWxrHTe3Na5yhPm1e+b76U0PpA2chJ+Qs6LHWdwQXGGvplQO3KrInrA+dNqrWzera7a1N9U1WhQ== dependencies: just-compare "^2.3.0" diff --git a/modules/blogging/app/Volo.BloggingTestApp/package.json b/modules/blogging/app/Volo.BloggingTestApp/package.json index 283ecc7146..005d340712 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/package.json +++ b/modules/blogging/app/Volo.BloggingTestApp/package.json @@ -3,7 +3,7 @@ "name": "volo.blogtestapp", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3", - "@abp/blogging": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.4", + "@abp/blogging": "~10.5.0-rc.4" } } diff --git a/modules/blogging/app/Volo.BloggingTestApp/yarn.lock b/modules/blogging/app/Volo.BloggingTestApp/yarn.lock index 406e3c95da..7d8c134605 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/yarn.lock +++ b/modules/blogging/app/Volo.BloggingTestApp/yarn.lock @@ -2,229 +2,229 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" - integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" - -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" - integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== - dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" - "@abp/bootstrap" "~10.5.0-rc.3" - "@abp/bootstrap-datepicker" "~10.5.0-rc.3" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" - "@abp/datatables.net-bs5" "~10.5.0-rc.3" - "@abp/font-awesome" "~10.5.0-rc.3" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" - "@abp/lodash" "~10.5.0-rc.3" - "@abp/luxon" "~10.5.0-rc.3" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" - "@abp/moment" "~10.5.0-rc.3" - "@abp/select2" "~10.5.0-rc.3" - "@abp/sweetalert2" "~10.5.0-rc.3" - "@abp/timeago" "~10.5.0-rc.3" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" - integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.4.tgz#c24c2528ab89c12876d3051c7af3e4487573eba6" + integrity sha512-f/B88/lhr1aGWW8FNgqvqTHWtCL8psc11cna0I4dwbfk0vAOxsoMKoksX7tLK2spxm5y4j7ZET0c+DehLlMHuw== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.4" + +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.4.tgz#b14d0142a2b7d37e4b6d1ff700e495ff7561891a" + integrity sha512-i0AC6ZSYTxK42psQc1VbyHVouKHGii80E0Eax42jGoTlim8D1kNgetO+8+Pltg/5rL0HJViWrWPwyG7mBipA2w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.4" + "@abp/bootstrap" "~10.5.0-rc.4" + "@abp/bootstrap-datepicker" "~10.5.0-rc.4" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.4" + "@abp/datatables.net-bs5" "~10.5.0-rc.4" + "@abp/font-awesome" "~10.5.0-rc.4" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.4" + "@abp/lodash" "~10.5.0-rc.4" + "@abp/luxon" "~10.5.0-rc.4" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.4" + "@abp/moment" "~10.5.0-rc.4" + "@abp/select2" "~10.5.0-rc.4" + "@abp/sweetalert2" "~10.5.0-rc.4" + "@abp/timeago" "~10.5.0-rc.4" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.4.tgz#79822aadec8133133201d59a1c3dc6b7ca226c1f" + integrity sha512-149kt2gvpdG/rUn6Z44YF3m2Bsz5Z8mZfEDXwt7x+MjB84cXbm16sVcKdSkPkg2mXcLycJYpMGG/cgSlH+FvHQ== dependencies: ansi-colors "^4.1.3" -"@abp/blogging@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/blogging/-/blogging-10.5.0-rc.3.tgz#796d10b12a16d6b48ea5f9c53ea26a490199eef7" - integrity sha512-+7RV4CchmnzNPydsUgpr4bY3Er4SA0AiJhvoeTZQknBCo7oF7uY8OiqeVzWGj/iDodkreTN/x3QP1HjVQVOTXA== +"@abp/blogging@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/blogging/-/blogging-10.5.0-rc.4.tgz#c3decde27feaaff1abd4290a1b350800340aa7e0" + integrity sha512-z5NetRKM/Luv9GoidsGYgnCAqHVRZz52TUhQAvavpmM42QySmur4N8v0bAoT5oyMPgT4qXkAJKpaw7MflF3mtA== dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" - "@abp/owl.carousel" "~10.5.0-rc.3" - "@abp/prismjs" "~10.5.0-rc.3" - "@abp/tui-editor" "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.4" + "@abp/owl.carousel" "~10.5.0-rc.4" + "@abp/prismjs" "~10.5.0-rc.4" + "@abp/tui-editor" "~10.5.0-rc.4" -"@abp/bootstrap-datepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" - integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== +"@abp/bootstrap-datepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.4.tgz#66d04f31a8d021d151452afc4fcbc7315d89758e" + integrity sha512-EMcZlWJWU+Ypbw3TGraC8K9hM3+HWLhmEz+0vj6yRajmP5MczJfCyxt4AODaPEmf4uRV3WQ3wV0OjHZdmG+QhQ== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" - integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.4.tgz#1aa514548b762d0b3bd628a29edd59585510532b" + integrity sha512-TnEg+k6XVWROtni7sXCBkouBjjDLmN7C0lP4XcUztSSHGo7l+r+cl+pi1iSOuHdRX304/CO+3PZUBKYku3A3+A== dependencies: - "@abp/moment" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.4" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" - integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== +"@abp/bootstrap@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.4.tgz#591b5a9da1f4fcf48c9a419f4acb985ddd48d876" + integrity sha512-aJtjmCaLg+TSrA/PL1X4d6AYsIv/8AHprDFJ6cXTsZkQTYjyTZUfjw15JHA+mLe6BionsGOtsXryid29IbX2Mg== dependencies: - "@abp/core" "~10.5.0-rc.3" - "@abp/popper.js" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" + "@abp/popper.js" "~10.5.0-rc.4" bootstrap "^5.3.8" -"@abp/clipboard@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.3.tgz#b4d7eae0554a7872fb497a1c156dbd70b23954a1" - integrity sha512-B7hU650JeKPNbOWrYDLipuxLvnAgsj3Q+IyqxwrdS1JUWajQpu30d5c5t9y28w//5xXqyp5xoWoEzDOg66/8Lw== +"@abp/clipboard@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.4.tgz#3d48c5e461997c50f532a8af6394d90de2a80c42" + integrity sha512-V77e/wptcMJbF3Zhki1/jGVAzlbd5W/Q0wWEa3xiSJqa97g8VmjNB+Kzz6FoTzAUHT2oBJllGO8d2+nhmqLl4g== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" clipboard "^2.0.11" -"@abp/core@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" - integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== +"@abp/core@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.4.tgz#1a5aec4eb83daf1400150d7c0c4dc81c7265ae49" + integrity sha512-AA4NdfAzA7zLWmAdS5dCjVshyidTn/bEMqlhfIOtWOnp3nEjdtURhmz6yA22MM6YAuKkda9iE1OtHu+jMEG3vQ== dependencies: - "@abp/utils" "~10.5.0-rc.3" + "@abp/utils" "~10.5.0-rc.4" -"@abp/datatables.net-bs5@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" - integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== +"@abp/datatables.net-bs5@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.4.tgz#ec26b8a22d58880755f3593ac635b0daf082c886" + integrity sha512-f5WEXTEQC8S73Hz2QpCMkrh9vyFJifQ2Q9Jp6r0iLRURpBItzliUsSHcDAhdo3oPtuH9HOumQvISH4Y8S+EPzQ== dependencies: - "@abp/datatables.net" "~10.5.0-rc.3" + "@abp/datatables.net" "~10.5.0-rc.4" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" - integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== +"@abp/datatables.net@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.4.tgz#46254de6e712640f12058e2f167083b3011e2c1b" + integrity sha512-i81nFF+Qevs602t0zZWAhQWBpCV9FBHixwWQmVDCfyhDYJbiMLUBPcscIqF7EIv1tz/dEdW2GofvEzIMcffbAg== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" - integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== +"@abp/font-awesome@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.4.tgz#d7a8e4d32f84bfbe46c55dc93ad33f185e0f3848" + integrity sha512-guGsjSWTlUYtrvNlekEL3qEgEp1HeABnNEQdQF5yyukHXoOTC0pR/aPTmMiKHTiYfqpc3LoQMv+TU+ZAVQrU4w== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" - integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.4.tgz#93bb721e8dfff9a824d44980f6ce859c8c3866bb" + integrity sha512-U1b+2lnfdNQIVBUM8CjbP/EJaLDZ7EHrvWeJFn0aJYHglV8Jqka4+jPu0Be8NP1NqyQQSrhtqdf8axp742COmQ== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.3" + "@abp/jquery-validation" "~10.5.0-rc.4" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" - integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== +"@abp/jquery-validation@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.4.tgz#b90ceb992b9c0a7fc4f8faccaaaa628c259f028e" + integrity sha512-0qYfsAwhcatyiJpeZejmG3DgOAoCbWQcHOUM0Bgxg8gh+vcuzXu//956SBJqbkeTJiaeY4z8uULaQZpAiF+ZRQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" - integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== +"@abp/jquery@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.4.tgz#623d7be22842b37505795e15069f0993f7c417eb" + integrity sha512-IATc1SVqRPd3xDCPHQZh1GaMgGGcVmxUYFDo/+t6c768iLCwLpJPuoNebrBSeN+z9+opbuZrFLNdwWQ+Ch72QA== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" - integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== +"@abp/lodash@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.4.tgz#284d4ebf314cfb1d96e66e8994f22b44055e4130" + integrity sha512-rswQ/laMhlu1MA+51CcIbQR1iZcOdSwF2HzxWccCqsaBVCOWy4G9TIj1gk2QfQSYMkBl6OWNprOea7XKhaSfzw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" - integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== +"@abp/luxon@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.4.tgz#c35120586c16afbafdefbb0a4b1cefb3e7e97690" + integrity sha512-Mk5oMzecFf5v35/EW2FvVYmQX16giT8J/QpeqyaPWbp0UptOSYrA7E+gHoJGvs3SvY24KkPzTfdcaWzCyV6zIg== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" - integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.4.tgz#f7e215c48012306d449544be35b384face75caa8" + integrity sha512-nu00haqvL68AnGIX0enLy3XvhDq72/ws7f+oh8Fr5gPPtywI1fzMnkXIGoXewE/5wcD7Fs/hHjJorFlX0yWOgw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" - integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== +"@abp/moment@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.4.tgz#4d02915b3650b8afe1160ab91c09d274bd9108d6" + integrity sha512-6giU879bnFltkefbb/it4bWxBfZn+QDzDgP1aPC4xNIa134ZXzkcSMRlMyUBa/96OZ6mmmYo/6wLFZLCAsOPZw== dependencies: moment "^2.30.1" -"@abp/owl.carousel@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/owl.carousel/-/owl.carousel-10.5.0-rc.3.tgz#632dd138458040080128c539a4b98b5df7532c44" - integrity sha512-4XEc52wiN6sBFtt028pN8fz7Xjgx/23lBC6W4iTk2J2oeZXWkTWMszbIgMXEvAO5+wXzCPIbu1/kRLjgSp6gSQ== +"@abp/owl.carousel@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/owl.carousel/-/owl.carousel-10.5.0-rc.4.tgz#f6a30590475b5f67b656de1ee6aed12ec58af255" + integrity sha512-kkQyi6dvw6rfz6DRseNZS0aazofzwIREyHyZ6KjJivUsHlb8tlIuIrIGNjO46SzvtV+vE+neJFg5chLEi2HtQQ== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" owl.carousel "^2.3.4" -"@abp/popper.js@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" - integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== +"@abp/popper.js@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.4.tgz#4a74c6e1f72da2ba8f67b33856196558a5efff72" + integrity sha512-oJ4pDrVCgu0RadsdwlppGN3tPzMymQ5DWhqK6CKEeir4lBFubKDe1NAhawZ4qGtIXKFWIbE30prFg11pf/zj9Q== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@popperjs/core" "^2.11.8" -"@abp/prismjs@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.3.tgz#c2a0816f5a6d0ae7a6ae56687bc7d62bef89f707" - integrity sha512-1YsVUx3GZID5H3GlUFljO8gEnKSDigbDsfX2zD/Kue3vCYnlq0Ysrx+jesNAvLP/UO5JBMPiAzr8xHU+KV5q6g== +"@abp/prismjs@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.4.tgz#d130f9e872eef068da63497b53ef1823347dc403" + integrity sha512-epb+8m21G8glvjHh2NgzO6kK3JDgliwXmfyIvRBg2/dUyovCyQ9A4V9jOA0VfaqTHz7sl0liM7M66/gP3XiGhA== dependencies: - "@abp/clipboard" "~10.5.0-rc.3" - "@abp/core" "~10.5.0-rc.3" + "@abp/clipboard" "~10.5.0-rc.4" + "@abp/core" "~10.5.0-rc.4" prismjs "^1.30.0" -"@abp/select2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" - integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== +"@abp/select2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.4.tgz#87448d8b1d2ec97c0cb97e1c08db3ba2618e29f7" + integrity sha512-64utn6ikY5q71TIfO9qhXWpy0pGrj1YEpAdO/q3qYoFYf9RDz9mSFd2cJ8Noasph/CmZSKFK3YwPcHFkSeLTYQ== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" - integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== +"@abp/sweetalert2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.4.tgz#aa667ee6edb6b008961a463f7f477a4671d28381" + integrity sha512-+/OKRL2hzohXryUD2rZO7aFLL7G7A300/QK82BKnYSXr7fh2wsVEC9g4/DXnaoygad7k5KS3NWUMIpEL21ROpw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" - integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== +"@abp/timeago@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.4.tgz#02b27e97c20b6b226cbdaa43d7bfb367218a626f" + integrity sha512-XCopnK5kphD6+OQ6+RHsgZHL7WtaXBbiYHpQmo7JHr9iVBwvLmWSzJZMIbrfZTczkhHk2VEwMQWSUwnJsWCWvQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" timeago "^1.6.7" -"@abp/tui-editor@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-10.5.0-rc.3.tgz#3039865c149cea98dfedace000c4b392dc0680ed" - integrity sha512-SrwvvLH4GTJg9basCsu1cjmdy7BweDUAmZg0Zy8zZH1ry68FrV7AqGUujopssXkRooP6W/0VRpobbAXEyAAwzw== +"@abp/tui-editor@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-10.5.0-rc.4.tgz#e4d8359e11fadb84ccc9d1e3445f5a3370fd4656" + integrity sha512-hDC1gGPF6PtRWWkaSY0lu/WcpwnPSirG7394PNLKOXBuKoJss9pCJWbTkJYavz3wWOtOe/cwhuv29vg6q2+p6g== dependencies: - "@abp/jquery" "~10.5.0-rc.3" - "@abp/prismjs" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" + "@abp/prismjs" "~10.5.0-rc.4" -"@abp/utils@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" - integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== +"@abp/utils@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.4.tgz#3bddb096f7537b69c08c2c81c99607519b58c200" + integrity sha512-jYNIF72jXBYHWxrHTe3Na5yhPm1e+b76U0PpA2chJ+Qs6LHWdwQXGGvplQO3KrInrA+dNqrWzera7a1N9U1WhQ== dependencies: just-compare "^2.3.0" diff --git a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json index 6917ce16f8..d05ad638f3 100644 --- a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json +++ b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package.json @@ -3,6 +3,6 @@ "name": "client-simulation-web", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.4" } } diff --git a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock index 820ecf8a71..d5580eb529 100644 --- a/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock +++ b/modules/client-simulation/demo/Volo.ClientSimulation.Demo/yarn.lock @@ -2,186 +2,186 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" - integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.4.tgz#c24c2528ab89c12876d3051c7af3e4487573eba6" + integrity sha512-f/B88/lhr1aGWW8FNgqvqTHWtCL8psc11cna0I4dwbfk0vAOxsoMKoksX7tLK2spxm5y4j7ZET0c+DehLlMHuw== dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.4" -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" - integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.4.tgz#b14d0142a2b7d37e4b6d1ff700e495ff7561891a" + integrity sha512-i0AC6ZSYTxK42psQc1VbyHVouKHGii80E0Eax42jGoTlim8D1kNgetO+8+Pltg/5rL0HJViWrWPwyG7mBipA2w== dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" - "@abp/bootstrap" "~10.5.0-rc.3" - "@abp/bootstrap-datepicker" "~10.5.0-rc.3" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" - "@abp/datatables.net-bs5" "~10.5.0-rc.3" - "@abp/font-awesome" "~10.5.0-rc.3" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" - "@abp/lodash" "~10.5.0-rc.3" - "@abp/luxon" "~10.5.0-rc.3" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" - "@abp/moment" "~10.5.0-rc.3" - "@abp/select2" "~10.5.0-rc.3" - "@abp/sweetalert2" "~10.5.0-rc.3" - "@abp/timeago" "~10.5.0-rc.3" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" - integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.4" + "@abp/bootstrap" "~10.5.0-rc.4" + "@abp/bootstrap-datepicker" "~10.5.0-rc.4" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.4" + "@abp/datatables.net-bs5" "~10.5.0-rc.4" + "@abp/font-awesome" "~10.5.0-rc.4" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.4" + "@abp/lodash" "~10.5.0-rc.4" + "@abp/luxon" "~10.5.0-rc.4" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.4" + "@abp/moment" "~10.5.0-rc.4" + "@abp/select2" "~10.5.0-rc.4" + "@abp/sweetalert2" "~10.5.0-rc.4" + "@abp/timeago" "~10.5.0-rc.4" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.4.tgz#79822aadec8133133201d59a1c3dc6b7ca226c1f" + integrity sha512-149kt2gvpdG/rUn6Z44YF3m2Bsz5Z8mZfEDXwt7x+MjB84cXbm16sVcKdSkPkg2mXcLycJYpMGG/cgSlH+FvHQ== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" - integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== +"@abp/bootstrap-datepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.4.tgz#66d04f31a8d021d151452afc4fcbc7315d89758e" + integrity sha512-EMcZlWJWU+Ypbw3TGraC8K9hM3+HWLhmEz+0vj6yRajmP5MczJfCyxt4AODaPEmf4uRV3WQ3wV0OjHZdmG+QhQ== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" - integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.4.tgz#1aa514548b762d0b3bd628a29edd59585510532b" + integrity sha512-TnEg+k6XVWROtni7sXCBkouBjjDLmN7C0lP4XcUztSSHGo7l+r+cl+pi1iSOuHdRX304/CO+3PZUBKYku3A3+A== dependencies: - "@abp/moment" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.4" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" - integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== +"@abp/bootstrap@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.4.tgz#591b5a9da1f4fcf48c9a419f4acb985ddd48d876" + integrity sha512-aJtjmCaLg+TSrA/PL1X4d6AYsIv/8AHprDFJ6cXTsZkQTYjyTZUfjw15JHA+mLe6BionsGOtsXryid29IbX2Mg== dependencies: - "@abp/core" "~10.5.0-rc.3" - "@abp/popper.js" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" + "@abp/popper.js" "~10.5.0-rc.4" bootstrap "^5.3.8" -"@abp/core@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" - integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== +"@abp/core@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.4.tgz#1a5aec4eb83daf1400150d7c0c4dc81c7265ae49" + integrity sha512-AA4NdfAzA7zLWmAdS5dCjVshyidTn/bEMqlhfIOtWOnp3nEjdtURhmz6yA22MM6YAuKkda9iE1OtHu+jMEG3vQ== dependencies: - "@abp/utils" "~10.5.0-rc.3" + "@abp/utils" "~10.5.0-rc.4" -"@abp/datatables.net-bs5@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" - integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== +"@abp/datatables.net-bs5@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.4.tgz#ec26b8a22d58880755f3593ac635b0daf082c886" + integrity sha512-f5WEXTEQC8S73Hz2QpCMkrh9vyFJifQ2Q9Jp6r0iLRURpBItzliUsSHcDAhdo3oPtuH9HOumQvISH4Y8S+EPzQ== dependencies: - "@abp/datatables.net" "~10.5.0-rc.3" + "@abp/datatables.net" "~10.5.0-rc.4" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" - integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== +"@abp/datatables.net@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.4.tgz#46254de6e712640f12058e2f167083b3011e2c1b" + integrity sha512-i81nFF+Qevs602t0zZWAhQWBpCV9FBHixwWQmVDCfyhDYJbiMLUBPcscIqF7EIv1tz/dEdW2GofvEzIMcffbAg== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" - integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== +"@abp/font-awesome@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.4.tgz#d7a8e4d32f84bfbe46c55dc93ad33f185e0f3848" + integrity sha512-guGsjSWTlUYtrvNlekEL3qEgEp1HeABnNEQdQF5yyukHXoOTC0pR/aPTmMiKHTiYfqpc3LoQMv+TU+ZAVQrU4w== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" - integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.4.tgz#93bb721e8dfff9a824d44980f6ce859c8c3866bb" + integrity sha512-U1b+2lnfdNQIVBUM8CjbP/EJaLDZ7EHrvWeJFn0aJYHglV8Jqka4+jPu0Be8NP1NqyQQSrhtqdf8axp742COmQ== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.3" + "@abp/jquery-validation" "~10.5.0-rc.4" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" - integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== +"@abp/jquery-validation@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.4.tgz#b90ceb992b9c0a7fc4f8faccaaaa628c259f028e" + integrity sha512-0qYfsAwhcatyiJpeZejmG3DgOAoCbWQcHOUM0Bgxg8gh+vcuzXu//956SBJqbkeTJiaeY4z8uULaQZpAiF+ZRQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" - integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== +"@abp/jquery@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.4.tgz#623d7be22842b37505795e15069f0993f7c417eb" + integrity sha512-IATc1SVqRPd3xDCPHQZh1GaMgGGcVmxUYFDo/+t6c768iLCwLpJPuoNebrBSeN+z9+opbuZrFLNdwWQ+Ch72QA== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" - integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== +"@abp/lodash@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.4.tgz#284d4ebf314cfb1d96e66e8994f22b44055e4130" + integrity sha512-rswQ/laMhlu1MA+51CcIbQR1iZcOdSwF2HzxWccCqsaBVCOWy4G9TIj1gk2QfQSYMkBl6OWNprOea7XKhaSfzw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" - integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== +"@abp/luxon@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.4.tgz#c35120586c16afbafdefbb0a4b1cefb3e7e97690" + integrity sha512-Mk5oMzecFf5v35/EW2FvVYmQX16giT8J/QpeqyaPWbp0UptOSYrA7E+gHoJGvs3SvY24KkPzTfdcaWzCyV6zIg== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" - integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.4.tgz#f7e215c48012306d449544be35b384face75caa8" + integrity sha512-nu00haqvL68AnGIX0enLy3XvhDq72/ws7f+oh8Fr5gPPtywI1fzMnkXIGoXewE/5wcD7Fs/hHjJorFlX0yWOgw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" - integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== +"@abp/moment@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.4.tgz#4d02915b3650b8afe1160ab91c09d274bd9108d6" + integrity sha512-6giU879bnFltkefbb/it4bWxBfZn+QDzDgP1aPC4xNIa134ZXzkcSMRlMyUBa/96OZ6mmmYo/6wLFZLCAsOPZw== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" - integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== +"@abp/popper.js@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.4.tgz#4a74c6e1f72da2ba8f67b33856196558a5efff72" + integrity sha512-oJ4pDrVCgu0RadsdwlppGN3tPzMymQ5DWhqK6CKEeir4lBFubKDe1NAhawZ4qGtIXKFWIbE30prFg11pf/zj9Q== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@popperjs/core" "^2.11.8" -"@abp/select2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" - integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== +"@abp/select2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.4.tgz#87448d8b1d2ec97c0cb97e1c08db3ba2618e29f7" + integrity sha512-64utn6ikY5q71TIfO9qhXWpy0pGrj1YEpAdO/q3qYoFYf9RDz9mSFd2cJ8Noasph/CmZSKFK3YwPcHFkSeLTYQ== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" - integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== +"@abp/sweetalert2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.4.tgz#aa667ee6edb6b008961a463f7f477a4671d28381" + integrity sha512-+/OKRL2hzohXryUD2rZO7aFLL7G7A300/QK82BKnYSXr7fh2wsVEC9g4/DXnaoygad7k5KS3NWUMIpEL21ROpw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" - integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== +"@abp/timeago@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.4.tgz#02b27e97c20b6b226cbdaa43d7bfb367218a626f" + integrity sha512-XCopnK5kphD6+OQ6+RHsgZHL7WtaXBbiYHpQmo7JHr9iVBwvLmWSzJZMIbrfZTczkhHk2VEwMQWSUwnJsWCWvQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" - integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== +"@abp/utils@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.4.tgz#3bddb096f7537b69c08c2c81c99607519b58c200" + integrity sha512-jYNIF72jXBYHWxrHTe3Na5yhPm1e+b76U0PpA2chJ+Qs6LHWdwQXGGvplQO3KrInrA+dNqrWzera7a1N9U1WhQ== dependencies: just-compare "^2.3.0" diff --git a/modules/cms-kit/angular/package.json b/modules/cms-kit/angular/package.json index eba25d9d5e..4e7074e872 100644 --- a/modules/cms-kit/angular/package.json +++ b/modules/cms-kit/angular/package.json @@ -15,11 +15,11 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~10.5.0-rc.3", - "@abp/ng.identity": "~10.5.0-rc.3", - "@abp/ng.setting-management": "~10.5.0-rc.3", - "@abp/ng.tenant-management": "~10.5.0-rc.3", - "@abp/ng.theme.basic": "~10.5.0-rc.3", + "@abp/ng.account": "~10.5.0-rc.4", + "@abp/ng.identity": "~10.5.0-rc.4", + "@abp/ng.setting-management": "~10.5.0-rc.4", + "@abp/ng.tenant-management": "~10.5.0-rc.4", + "@abp/ng.theme.basic": "~10.5.0-rc.4", "@angular/animations": "~10.0.0", "@angular/common": "~10.0.0", "@angular/compiler": "~10.0.0", diff --git a/modules/cms-kit/angular/projects/cms-kit/package.json b/modules/cms-kit/angular/projects/cms-kit/package.json index ccb0d5c466..1f5633b582 100644 --- a/modules/cms-kit/angular/projects/cms-kit/package.json +++ b/modules/cms-kit/angular/projects/cms-kit/package.json @@ -4,8 +4,8 @@ "peerDependencies": { "@angular/common": "^9.1.11", "@angular/core": "^9.1.11", - "@abp/ng.core": ">=10.5.0-rc.3", - "@abp/ng.theme.shared": ">=10.5.0-rc.3" + "@abp/ng.core": ">=10.5.0-rc.4", + "@abp/ng.theme.shared": ">=10.5.0-rc.4" }, "dependencies": { "tslib": "^2.0.0" diff --git a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json index f77ffb012e..90ad3d4154 100644 --- a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json +++ b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/package.json @@ -3,6 +3,6 @@ "name": "my-app-identityserver", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.4" } } diff --git a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock index 820ecf8a71..d5580eb529 100644 --- a/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock +++ b/modules/cms-kit/host/Volo.CmsKit.IdentityServer/yarn.lock @@ -2,186 +2,186 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" - integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.4.tgz#c24c2528ab89c12876d3051c7af3e4487573eba6" + integrity sha512-f/B88/lhr1aGWW8FNgqvqTHWtCL8psc11cna0I4dwbfk0vAOxsoMKoksX7tLK2spxm5y4j7ZET0c+DehLlMHuw== dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.4" -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" - integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.4.tgz#b14d0142a2b7d37e4b6d1ff700e495ff7561891a" + integrity sha512-i0AC6ZSYTxK42psQc1VbyHVouKHGii80E0Eax42jGoTlim8D1kNgetO+8+Pltg/5rL0HJViWrWPwyG7mBipA2w== dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" - "@abp/bootstrap" "~10.5.0-rc.3" - "@abp/bootstrap-datepicker" "~10.5.0-rc.3" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" - "@abp/datatables.net-bs5" "~10.5.0-rc.3" - "@abp/font-awesome" "~10.5.0-rc.3" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" - "@abp/lodash" "~10.5.0-rc.3" - "@abp/luxon" "~10.5.0-rc.3" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" - "@abp/moment" "~10.5.0-rc.3" - "@abp/select2" "~10.5.0-rc.3" - "@abp/sweetalert2" "~10.5.0-rc.3" - "@abp/timeago" "~10.5.0-rc.3" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" - integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.4" + "@abp/bootstrap" "~10.5.0-rc.4" + "@abp/bootstrap-datepicker" "~10.5.0-rc.4" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.4" + "@abp/datatables.net-bs5" "~10.5.0-rc.4" + "@abp/font-awesome" "~10.5.0-rc.4" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.4" + "@abp/lodash" "~10.5.0-rc.4" + "@abp/luxon" "~10.5.0-rc.4" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.4" + "@abp/moment" "~10.5.0-rc.4" + "@abp/select2" "~10.5.0-rc.4" + "@abp/sweetalert2" "~10.5.0-rc.4" + "@abp/timeago" "~10.5.0-rc.4" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.4.tgz#79822aadec8133133201d59a1c3dc6b7ca226c1f" + integrity sha512-149kt2gvpdG/rUn6Z44YF3m2Bsz5Z8mZfEDXwt7x+MjB84cXbm16sVcKdSkPkg2mXcLycJYpMGG/cgSlH+FvHQ== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" - integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== +"@abp/bootstrap-datepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.4.tgz#66d04f31a8d021d151452afc4fcbc7315d89758e" + integrity sha512-EMcZlWJWU+Ypbw3TGraC8K9hM3+HWLhmEz+0vj6yRajmP5MczJfCyxt4AODaPEmf4uRV3WQ3wV0OjHZdmG+QhQ== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" - integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.4.tgz#1aa514548b762d0b3bd628a29edd59585510532b" + integrity sha512-TnEg+k6XVWROtni7sXCBkouBjjDLmN7C0lP4XcUztSSHGo7l+r+cl+pi1iSOuHdRX304/CO+3PZUBKYku3A3+A== dependencies: - "@abp/moment" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.4" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" - integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== +"@abp/bootstrap@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.4.tgz#591b5a9da1f4fcf48c9a419f4acb985ddd48d876" + integrity sha512-aJtjmCaLg+TSrA/PL1X4d6AYsIv/8AHprDFJ6cXTsZkQTYjyTZUfjw15JHA+mLe6BionsGOtsXryid29IbX2Mg== dependencies: - "@abp/core" "~10.5.0-rc.3" - "@abp/popper.js" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" + "@abp/popper.js" "~10.5.0-rc.4" bootstrap "^5.3.8" -"@abp/core@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" - integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== +"@abp/core@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.4.tgz#1a5aec4eb83daf1400150d7c0c4dc81c7265ae49" + integrity sha512-AA4NdfAzA7zLWmAdS5dCjVshyidTn/bEMqlhfIOtWOnp3nEjdtURhmz6yA22MM6YAuKkda9iE1OtHu+jMEG3vQ== dependencies: - "@abp/utils" "~10.5.0-rc.3" + "@abp/utils" "~10.5.0-rc.4" -"@abp/datatables.net-bs5@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" - integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== +"@abp/datatables.net-bs5@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.4.tgz#ec26b8a22d58880755f3593ac635b0daf082c886" + integrity sha512-f5WEXTEQC8S73Hz2QpCMkrh9vyFJifQ2Q9Jp6r0iLRURpBItzliUsSHcDAhdo3oPtuH9HOumQvISH4Y8S+EPzQ== dependencies: - "@abp/datatables.net" "~10.5.0-rc.3" + "@abp/datatables.net" "~10.5.0-rc.4" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" - integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== +"@abp/datatables.net@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.4.tgz#46254de6e712640f12058e2f167083b3011e2c1b" + integrity sha512-i81nFF+Qevs602t0zZWAhQWBpCV9FBHixwWQmVDCfyhDYJbiMLUBPcscIqF7EIv1tz/dEdW2GofvEzIMcffbAg== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" - integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== +"@abp/font-awesome@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.4.tgz#d7a8e4d32f84bfbe46c55dc93ad33f185e0f3848" + integrity sha512-guGsjSWTlUYtrvNlekEL3qEgEp1HeABnNEQdQF5yyukHXoOTC0pR/aPTmMiKHTiYfqpc3LoQMv+TU+ZAVQrU4w== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" - integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.4.tgz#93bb721e8dfff9a824d44980f6ce859c8c3866bb" + integrity sha512-U1b+2lnfdNQIVBUM8CjbP/EJaLDZ7EHrvWeJFn0aJYHglV8Jqka4+jPu0Be8NP1NqyQQSrhtqdf8axp742COmQ== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.3" + "@abp/jquery-validation" "~10.5.0-rc.4" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" - integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== +"@abp/jquery-validation@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.4.tgz#b90ceb992b9c0a7fc4f8faccaaaa628c259f028e" + integrity sha512-0qYfsAwhcatyiJpeZejmG3DgOAoCbWQcHOUM0Bgxg8gh+vcuzXu//956SBJqbkeTJiaeY4z8uULaQZpAiF+ZRQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" - integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== +"@abp/jquery@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.4.tgz#623d7be22842b37505795e15069f0993f7c417eb" + integrity sha512-IATc1SVqRPd3xDCPHQZh1GaMgGGcVmxUYFDo/+t6c768iLCwLpJPuoNebrBSeN+z9+opbuZrFLNdwWQ+Ch72QA== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" - integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== +"@abp/lodash@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.4.tgz#284d4ebf314cfb1d96e66e8994f22b44055e4130" + integrity sha512-rswQ/laMhlu1MA+51CcIbQR1iZcOdSwF2HzxWccCqsaBVCOWy4G9TIj1gk2QfQSYMkBl6OWNprOea7XKhaSfzw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" - integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== +"@abp/luxon@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.4.tgz#c35120586c16afbafdefbb0a4b1cefb3e7e97690" + integrity sha512-Mk5oMzecFf5v35/EW2FvVYmQX16giT8J/QpeqyaPWbp0UptOSYrA7E+gHoJGvs3SvY24KkPzTfdcaWzCyV6zIg== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" - integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.4.tgz#f7e215c48012306d449544be35b384face75caa8" + integrity sha512-nu00haqvL68AnGIX0enLy3XvhDq72/ws7f+oh8Fr5gPPtywI1fzMnkXIGoXewE/5wcD7Fs/hHjJorFlX0yWOgw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" - integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== +"@abp/moment@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.4.tgz#4d02915b3650b8afe1160ab91c09d274bd9108d6" + integrity sha512-6giU879bnFltkefbb/it4bWxBfZn+QDzDgP1aPC4xNIa134ZXzkcSMRlMyUBa/96OZ6mmmYo/6wLFZLCAsOPZw== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" - integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== +"@abp/popper.js@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.4.tgz#4a74c6e1f72da2ba8f67b33856196558a5efff72" + integrity sha512-oJ4pDrVCgu0RadsdwlppGN3tPzMymQ5DWhqK6CKEeir4lBFubKDe1NAhawZ4qGtIXKFWIbE30prFg11pf/zj9Q== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@popperjs/core" "^2.11.8" -"@abp/select2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" - integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== +"@abp/select2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.4.tgz#87448d8b1d2ec97c0cb97e1c08db3ba2618e29f7" + integrity sha512-64utn6ikY5q71TIfO9qhXWpy0pGrj1YEpAdO/q3qYoFYf9RDz9mSFd2cJ8Noasph/CmZSKFK3YwPcHFkSeLTYQ== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" - integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== +"@abp/sweetalert2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.4.tgz#aa667ee6edb6b008961a463f7f477a4671d28381" + integrity sha512-+/OKRL2hzohXryUD2rZO7aFLL7G7A300/QK82BKnYSXr7fh2wsVEC9g4/DXnaoygad7k5KS3NWUMIpEL21ROpw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" - integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== +"@abp/timeago@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.4.tgz#02b27e97c20b6b226cbdaa43d7bfb367218a626f" + integrity sha512-XCopnK5kphD6+OQ6+RHsgZHL7WtaXBbiYHpQmo7JHr9iVBwvLmWSzJZMIbrfZTczkhHk2VEwMQWSUwnJsWCWvQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" - integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== +"@abp/utils@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.4.tgz#3bddb096f7537b69c08c2c81c99607519b58c200" + integrity sha512-jYNIF72jXBYHWxrHTe3Na5yhPm1e+b76U0PpA2chJ+Qs6LHWdwQXGGvplQO3KrInrA+dNqrWzera7a1N9U1WhQ== dependencies: just-compare "^2.3.0" diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json b/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json index 144454257f..1f5589b59e 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.4" } } diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock index 820ecf8a71..d5580eb529 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Host/yarn.lock @@ -2,186 +2,186 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" - integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.4.tgz#c24c2528ab89c12876d3051c7af3e4487573eba6" + integrity sha512-f/B88/lhr1aGWW8FNgqvqTHWtCL8psc11cna0I4dwbfk0vAOxsoMKoksX7tLK2spxm5y4j7ZET0c+DehLlMHuw== dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.4" -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" - integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.4.tgz#b14d0142a2b7d37e4b6d1ff700e495ff7561891a" + integrity sha512-i0AC6ZSYTxK42psQc1VbyHVouKHGii80E0Eax42jGoTlim8D1kNgetO+8+Pltg/5rL0HJViWrWPwyG7mBipA2w== dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" - "@abp/bootstrap" "~10.5.0-rc.3" - "@abp/bootstrap-datepicker" "~10.5.0-rc.3" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" - "@abp/datatables.net-bs5" "~10.5.0-rc.3" - "@abp/font-awesome" "~10.5.0-rc.3" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" - "@abp/lodash" "~10.5.0-rc.3" - "@abp/luxon" "~10.5.0-rc.3" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" - "@abp/moment" "~10.5.0-rc.3" - "@abp/select2" "~10.5.0-rc.3" - "@abp/sweetalert2" "~10.5.0-rc.3" - "@abp/timeago" "~10.5.0-rc.3" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" - integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.4" + "@abp/bootstrap" "~10.5.0-rc.4" + "@abp/bootstrap-datepicker" "~10.5.0-rc.4" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.4" + "@abp/datatables.net-bs5" "~10.5.0-rc.4" + "@abp/font-awesome" "~10.5.0-rc.4" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.4" + "@abp/lodash" "~10.5.0-rc.4" + "@abp/luxon" "~10.5.0-rc.4" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.4" + "@abp/moment" "~10.5.0-rc.4" + "@abp/select2" "~10.5.0-rc.4" + "@abp/sweetalert2" "~10.5.0-rc.4" + "@abp/timeago" "~10.5.0-rc.4" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.4.tgz#79822aadec8133133201d59a1c3dc6b7ca226c1f" + integrity sha512-149kt2gvpdG/rUn6Z44YF3m2Bsz5Z8mZfEDXwt7x+MjB84cXbm16sVcKdSkPkg2mXcLycJYpMGG/cgSlH+FvHQ== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" - integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== +"@abp/bootstrap-datepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.4.tgz#66d04f31a8d021d151452afc4fcbc7315d89758e" + integrity sha512-EMcZlWJWU+Ypbw3TGraC8K9hM3+HWLhmEz+0vj6yRajmP5MczJfCyxt4AODaPEmf4uRV3WQ3wV0OjHZdmG+QhQ== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" - integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.4.tgz#1aa514548b762d0b3bd628a29edd59585510532b" + integrity sha512-TnEg+k6XVWROtni7sXCBkouBjjDLmN7C0lP4XcUztSSHGo7l+r+cl+pi1iSOuHdRX304/CO+3PZUBKYku3A3+A== dependencies: - "@abp/moment" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.4" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" - integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== +"@abp/bootstrap@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.4.tgz#591b5a9da1f4fcf48c9a419f4acb985ddd48d876" + integrity sha512-aJtjmCaLg+TSrA/PL1X4d6AYsIv/8AHprDFJ6cXTsZkQTYjyTZUfjw15JHA+mLe6BionsGOtsXryid29IbX2Mg== dependencies: - "@abp/core" "~10.5.0-rc.3" - "@abp/popper.js" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" + "@abp/popper.js" "~10.5.0-rc.4" bootstrap "^5.3.8" -"@abp/core@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" - integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== +"@abp/core@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.4.tgz#1a5aec4eb83daf1400150d7c0c4dc81c7265ae49" + integrity sha512-AA4NdfAzA7zLWmAdS5dCjVshyidTn/bEMqlhfIOtWOnp3nEjdtURhmz6yA22MM6YAuKkda9iE1OtHu+jMEG3vQ== dependencies: - "@abp/utils" "~10.5.0-rc.3" + "@abp/utils" "~10.5.0-rc.4" -"@abp/datatables.net-bs5@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" - integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== +"@abp/datatables.net-bs5@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.4.tgz#ec26b8a22d58880755f3593ac635b0daf082c886" + integrity sha512-f5WEXTEQC8S73Hz2QpCMkrh9vyFJifQ2Q9Jp6r0iLRURpBItzliUsSHcDAhdo3oPtuH9HOumQvISH4Y8S+EPzQ== dependencies: - "@abp/datatables.net" "~10.5.0-rc.3" + "@abp/datatables.net" "~10.5.0-rc.4" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" - integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== +"@abp/datatables.net@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.4.tgz#46254de6e712640f12058e2f167083b3011e2c1b" + integrity sha512-i81nFF+Qevs602t0zZWAhQWBpCV9FBHixwWQmVDCfyhDYJbiMLUBPcscIqF7EIv1tz/dEdW2GofvEzIMcffbAg== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" - integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== +"@abp/font-awesome@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.4.tgz#d7a8e4d32f84bfbe46c55dc93ad33f185e0f3848" + integrity sha512-guGsjSWTlUYtrvNlekEL3qEgEp1HeABnNEQdQF5yyukHXoOTC0pR/aPTmMiKHTiYfqpc3LoQMv+TU+ZAVQrU4w== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" - integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.4.tgz#93bb721e8dfff9a824d44980f6ce859c8c3866bb" + integrity sha512-U1b+2lnfdNQIVBUM8CjbP/EJaLDZ7EHrvWeJFn0aJYHglV8Jqka4+jPu0Be8NP1NqyQQSrhtqdf8axp742COmQ== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.3" + "@abp/jquery-validation" "~10.5.0-rc.4" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" - integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== +"@abp/jquery-validation@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.4.tgz#b90ceb992b9c0a7fc4f8faccaaaa628c259f028e" + integrity sha512-0qYfsAwhcatyiJpeZejmG3DgOAoCbWQcHOUM0Bgxg8gh+vcuzXu//956SBJqbkeTJiaeY4z8uULaQZpAiF+ZRQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" - integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== +"@abp/jquery@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.4.tgz#623d7be22842b37505795e15069f0993f7c417eb" + integrity sha512-IATc1SVqRPd3xDCPHQZh1GaMgGGcVmxUYFDo/+t6c768iLCwLpJPuoNebrBSeN+z9+opbuZrFLNdwWQ+Ch72QA== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" jquery "~3.7.1" -"@abp/lodash@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" - integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== +"@abp/lodash@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.4.tgz#284d4ebf314cfb1d96e66e8994f22b44055e4130" + integrity sha512-rswQ/laMhlu1MA+51CcIbQR1iZcOdSwF2HzxWccCqsaBVCOWy4G9TIj1gk2QfQSYMkBl6OWNprOea7XKhaSfzw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" - integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== +"@abp/luxon@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.4.tgz#c35120586c16afbafdefbb0a4b1cefb3e7e97690" + integrity sha512-Mk5oMzecFf5v35/EW2FvVYmQX16giT8J/QpeqyaPWbp0UptOSYrA7E+gHoJGvs3SvY24KkPzTfdcaWzCyV6zIg== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" - integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.4.tgz#f7e215c48012306d449544be35b384face75caa8" + integrity sha512-nu00haqvL68AnGIX0enLy3XvhDq72/ws7f+oh8Fr5gPPtywI1fzMnkXIGoXewE/5wcD7Fs/hHjJorFlX0yWOgw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/moment@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" - integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== +"@abp/moment@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.4.tgz#4d02915b3650b8afe1160ab91c09d274bd9108d6" + integrity sha512-6giU879bnFltkefbb/it4bWxBfZn+QDzDgP1aPC4xNIa134ZXzkcSMRlMyUBa/96OZ6mmmYo/6wLFZLCAsOPZw== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" - integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== +"@abp/popper.js@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.4.tgz#4a74c6e1f72da2ba8f67b33856196558a5efff72" + integrity sha512-oJ4pDrVCgu0RadsdwlppGN3tPzMymQ5DWhqK6CKEeir4lBFubKDe1NAhawZ4qGtIXKFWIbE30prFg11pf/zj9Q== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@popperjs/core" "^2.11.8" -"@abp/select2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" - integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== +"@abp/select2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.4.tgz#87448d8b1d2ec97c0cb97e1c08db3ba2618e29f7" + integrity sha512-64utn6ikY5q71TIfO9qhXWpy0pGrj1YEpAdO/q3qYoFYf9RDz9mSFd2cJ8Noasph/CmZSKFK3YwPcHFkSeLTYQ== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" select2 "^4.0.13" -"@abp/sweetalert2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" - integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== +"@abp/sweetalert2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.4.tgz#aa667ee6edb6b008961a463f7f477a4671d28381" + integrity sha512-+/OKRL2hzohXryUD2rZO7aFLL7G7A300/QK82BKnYSXr7fh2wsVEC9g4/DXnaoygad7k5KS3NWUMIpEL21ROpw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" - integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== +"@abp/timeago@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.4.tgz#02b27e97c20b6b226cbdaa43d7bfb367218a626f" + integrity sha512-XCopnK5kphD6+OQ6+RHsgZHL7WtaXBbiYHpQmo7JHr9iVBwvLmWSzJZMIbrfZTczkhHk2VEwMQWSUwnJsWCWvQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" timeago "^1.6.7" -"@abp/utils@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" - integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== +"@abp/utils@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.4.tgz#3bddb096f7537b69c08c2c81c99607519b58c200" + integrity sha512-jYNIF72jXBYHWxrHTe3Na5yhPm1e+b76U0PpA2chJ+Qs6LHWdwQXGGvplQO3KrInrA+dNqrWzera7a1N9U1WhQ== dependencies: just-compare "^2.3.0" diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json index 596144bcf9..e141e199f8 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3", - "@abp/cms-kit": "10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.4", + "@abp/cms-kit": "10.5.0-rc.4" } } diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock index a0a698dd92..a2f15f92bf 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/yarn.lock @@ -2,298 +2,298 @@ # yarn lockfile v1 -"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.3.tgz#ea2352806b9123e8468ff46c83789ca712d3c456" - integrity sha512-bWT71ZvFG4SnVTVqapfUwVfXb9tHrLE7BpCHWul5ebM8hY6BV4AFlZ49qBjgg1cw2/fLbfpNEZJco7T81PvSeQ== - dependencies: - "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.3" - -"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.3.tgz#19c770ee556a360e508a061e1bba3d7ebd0189cf" - integrity sha512-CL3awTP+RzMCUVnyjPjs9JyQmgHrh1131oymJcOsQAmtikOqrlxGqFSXj/FUa3sOd3E70BYcdtKaCOkqDsb0MQ== - dependencies: - "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.3" - "@abp/bootstrap" "~10.5.0-rc.3" - "@abp/bootstrap-datepicker" "~10.5.0-rc.3" - "@abp/bootstrap-daterangepicker" "~10.5.0-rc.3" - "@abp/datatables.net-bs5" "~10.5.0-rc.3" - "@abp/font-awesome" "~10.5.0-rc.3" - "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.3" - "@abp/lodash" "~10.5.0-rc.3" - "@abp/luxon" "~10.5.0-rc.3" - "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.3" - "@abp/moment" "~10.5.0-rc.3" - "@abp/select2" "~10.5.0-rc.3" - "@abp/sweetalert2" "~10.5.0-rc.3" - "@abp/timeago" "~10.5.0-rc.3" - -"@abp/aspnetcore.mvc.ui@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.3.tgz#f4c7f9f15c8127d0d289c68ef577ec4cb09b7e3f" - integrity sha512-Bo+v7W75Y6XyRBK1p9eXkFgDGju9D3ktiWsVO+ifDWcx0Mc4fUxVkoLIwBzBIdkw28lw6HbKaUkf50mtnl+D8Q== +"@abp/aspnetcore.mvc.ui.theme.basic@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-10.5.0-rc.4.tgz#c24c2528ab89c12876d3051c7af3e4487573eba6" + integrity sha512-f/B88/lhr1aGWW8FNgqvqTHWtCL8psc11cna0I4dwbfk0vAOxsoMKoksX7tLK2spxm5y4j7ZET0c+DehLlMHuw== + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "~10.5.0-rc.4" + +"@abp/aspnetcore.mvc.ui.theme.shared@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-10.5.0-rc.4.tgz#b14d0142a2b7d37e4b6d1ff700e495ff7561891a" + integrity sha512-i0AC6ZSYTxK42psQc1VbyHVouKHGii80E0Eax42jGoTlim8D1kNgetO+8+Pltg/5rL0HJViWrWPwyG7mBipA2w== + dependencies: + "@abp/aspnetcore.mvc.ui" "~10.5.0-rc.4" + "@abp/bootstrap" "~10.5.0-rc.4" + "@abp/bootstrap-datepicker" "~10.5.0-rc.4" + "@abp/bootstrap-daterangepicker" "~10.5.0-rc.4" + "@abp/datatables.net-bs5" "~10.5.0-rc.4" + "@abp/font-awesome" "~10.5.0-rc.4" + "@abp/jquery-validation-unobtrusive" "~10.5.0-rc.4" + "@abp/lodash" "~10.5.0-rc.4" + "@abp/luxon" "~10.5.0-rc.4" + "@abp/malihu-custom-scrollbar-plugin" "~10.5.0-rc.4" + "@abp/moment" "~10.5.0-rc.4" + "@abp/select2" "~10.5.0-rc.4" + "@abp/sweetalert2" "~10.5.0-rc.4" + "@abp/timeago" "~10.5.0-rc.4" + +"@abp/aspnetcore.mvc.ui@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-10.5.0-rc.4.tgz#79822aadec8133133201d59a1c3dc6b7ca226c1f" + integrity sha512-149kt2gvpdG/rUn6Z44YF3m2Bsz5Z8mZfEDXwt7x+MjB84cXbm16sVcKdSkPkg2mXcLycJYpMGG/cgSlH+FvHQ== dependencies: ansi-colors "^4.1.3" -"@abp/bootstrap-datepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.3.tgz#8ab0c12082f2627843bcb4dc4999e04f9e3ded20" - integrity sha512-Hcr2Pdb8vjTWltn0VPnugrtBcYCUj+BSJXi/bw449ANF3eqnUbNw6T+H25uxuHWOIrI0BeezuU0w2iaHhvjIBA== +"@abp/bootstrap-datepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-datepicker/-/bootstrap-datepicker-10.5.0-rc.4.tgz#66d04f31a8d021d151452afc4fcbc7315d89758e" + integrity sha512-EMcZlWJWU+Ypbw3TGraC8K9hM3+HWLhmEz+0vj6yRajmP5MczJfCyxt4AODaPEmf4uRV3WQ3wV0OjHZdmG+QhQ== dependencies: bootstrap-datepicker "^1.10.1" -"@abp/bootstrap-daterangepicker@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.3.tgz#f32c23d8c4afe497a2f478b614122d2929eda26f" - integrity sha512-bSGQwChBXZqa1LZBpKwUFaB7BSWSoksvlTnMwapzJj0foUMC4o9TvFHFFugq8WhtS1f7YrnBI+GD5S9AZ3JTeQ== +"@abp/bootstrap-daterangepicker@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap-daterangepicker/-/bootstrap-daterangepicker-10.5.0-rc.4.tgz#1aa514548b762d0b3bd628a29edd59585510532b" + integrity sha512-TnEg+k6XVWROtni7sXCBkouBjjDLmN7C0lP4XcUztSSHGo7l+r+cl+pi1iSOuHdRX304/CO+3PZUBKYku3A3+A== dependencies: - "@abp/moment" "~10.5.0-rc.3" + "@abp/moment" "~10.5.0-rc.4" bootstrap-daterangepicker "^3.1.0" -"@abp/bootstrap@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.3.tgz#d947ae0c748c63326d08c1b6ae7502132b3547b8" - integrity sha512-9G6+ilOda+HUeVaUv4l2JzMAZEm8K1JyRrBcq/EKbePslnruicyyN0YjHOygDujMLlOFgO4o6+lPY/nUyMSRFQ== +"@abp/bootstrap@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-10.5.0-rc.4.tgz#591b5a9da1f4fcf48c9a419f4acb985ddd48d876" + integrity sha512-aJtjmCaLg+TSrA/PL1X4d6AYsIv/8AHprDFJ6cXTsZkQTYjyTZUfjw15JHA+mLe6BionsGOtsXryid29IbX2Mg== dependencies: - "@abp/core" "~10.5.0-rc.3" - "@abp/popper.js" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" + "@abp/popper.js" "~10.5.0-rc.4" bootstrap "^5.3.8" -"@abp/clipboard@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.3.tgz#b4d7eae0554a7872fb497a1c156dbd70b23954a1" - integrity sha512-B7hU650JeKPNbOWrYDLipuxLvnAgsj3Q+IyqxwrdS1JUWajQpu30d5c5t9y28w//5xXqyp5xoWoEzDOg66/8Lw== +"@abp/clipboard@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/clipboard/-/clipboard-10.5.0-rc.4.tgz#3d48c5e461997c50f532a8af6394d90de2a80c42" + integrity sha512-V77e/wptcMJbF3Zhki1/jGVAzlbd5W/Q0wWEa3xiSJqa97g8VmjNB+Kzz6FoTzAUHT2oBJllGO8d2+nhmqLl4g== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" clipboard "^2.0.11" -"@abp/cms-kit.admin@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/cms-kit.admin/-/cms-kit.admin-10.5.0-rc.3.tgz#74e8aed088a2721041e479ab305da1b42fd46207" - integrity sha512-+PNQ2PyhM5UYqyoNujG+HlpfTsOi/6nhbjOs1zH1ZBsGyUP5Rg144erkMYYQryouAj4I5kPvxV+VTtuqRzbl0A== +"@abp/cms-kit.admin@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/cms-kit.admin/-/cms-kit.admin-10.5.0-rc.4.tgz#75a922a15ea76865df8a36b61a6b3a49711ad2d1" + integrity sha512-OlLZ8NgG1UC+zQfoP8o4VLqCcslapgwxrPhbCzs0JjiBJ+yr+89ZbSQq+vLaHS9UlSNgtkGI7nH62UDijuz1DA== dependencies: - "@abp/codemirror" "~10.5.0-rc.3" - "@abp/jstree" "~10.5.0-rc.3" - "@abp/markdown-it" "~10.5.0-rc.3" - "@abp/slugify" "~10.5.0-rc.3" - "@abp/tui-editor" "~10.5.0-rc.3" - "@abp/uppy" "~10.5.0-rc.3" + "@abp/codemirror" "~10.5.0-rc.4" + "@abp/jstree" "~10.5.0-rc.4" + "@abp/markdown-it" "~10.5.0-rc.4" + "@abp/slugify" "~10.5.0-rc.4" + "@abp/tui-editor" "~10.5.0-rc.4" + "@abp/uppy" "~10.5.0-rc.4" -"@abp/cms-kit.public@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/cms-kit.public/-/cms-kit.public-10.5.0-rc.3.tgz#d07b202a762b8f29106958ccb8f56bb6596c5ee0" - integrity sha512-pNjILNysxOOrJw8dqKmp813KsWI9nHhiGWYICQdisg18c2fy0TICcnWHCI1yW/yah9YybHkFRntC3DEKuPBM2A== +"@abp/cms-kit.public@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/cms-kit.public/-/cms-kit.public-10.5.0-rc.4.tgz#c56440257773490aae6ea25b4ceea0bcf2983dea" + integrity sha512-GwCoJ5/3+Oto2xqU/rysdg2z0G3VMgMhe/7y0XxPdBE+pxKaYzA6qurDrP2Hj9h06EzV97+vWCMHhkvkvPrFtA== dependencies: - "@abp/highlight.js" "~10.5.0-rc.3" - "@abp/star-rating-svg" "~10.5.0-rc.3" + "@abp/highlight.js" "~10.5.0-rc.4" + "@abp/star-rating-svg" "~10.5.0-rc.4" -"@abp/cms-kit@10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/cms-kit/-/cms-kit-10.5.0-rc.3.tgz#addedc04c98661cf544491174d0d4cd4fb0fc5c6" - integrity sha512-HrNwsArlnSylt2wmO2ZhCdnS8DN7Y/qic6RdLwpKdhPqi/d1HovRGLZDVRDxrRPw8L52fEOtdBXJXs0KSLikRg== +"@abp/cms-kit@10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/cms-kit/-/cms-kit-10.5.0-rc.4.tgz#ee87be54f59f15d7b6cffe94bce78b01f57d95f6" + integrity sha512-sLAQZwsUlN42EOA6lJOIihJ8gx96tv2f2qhOk6yDMClYlwpZ26hW6w2fz+QO+WNmu8FTFSIDNvmab6P9Y18S+w== dependencies: - "@abp/cms-kit.admin" "~10.5.0-rc.3" - "@abp/cms-kit.public" "~10.5.0-rc.3" + "@abp/cms-kit.admin" "~10.5.0-rc.4" + "@abp/cms-kit.public" "~10.5.0-rc.4" -"@abp/codemirror@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/codemirror/-/codemirror-10.5.0-rc.3.tgz#310e203f3d201db886341d4a725ef85198923add" - integrity sha512-NDxrxO3knC26GF+sVWmf8WmsjYPmWbpvu/b0/1qj3SXITQN5MNzURxvV4WpZ24dTSK4S/t8pg2VINgu5l7BbnA== +"@abp/codemirror@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/codemirror/-/codemirror-10.5.0-rc.4.tgz#855a7c16da6ca36ad855bfa9051b742494f8fe23" + integrity sha512-Hy5MqK5LRiITm63MW8nCX6MudV3xUdHmp/vOhMOQbMZI/Xhjd1yL865VvzHiUSUJOHTV4XNBgdT6NLBCKNqbzQ== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@codemirror/lang-css" "^6.0.0" "@codemirror/lang-javascript" "^6.0.0" "@codemirror/state" "^6.0.0" "@codemirror/view" "^6.0.0" codemirror "^6.0.2" -"@abp/core@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.3.tgz#9b14f264ec6eeb74d8e116cec9e3f823e5c1d4d4" - integrity sha512-Ti4w7erMXQlv22Bgppr3TpmPpPa3mWupXVyf/ZoE0q88rWC3twRpqPCV6ziXMesRgR/GpN49xa0fcW+MDe02wA== +"@abp/core@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-10.5.0-rc.4.tgz#1a5aec4eb83daf1400150d7c0c4dc81c7265ae49" + integrity sha512-AA4NdfAzA7zLWmAdS5dCjVshyidTn/bEMqlhfIOtWOnp3nEjdtURhmz6yA22MM6YAuKkda9iE1OtHu+jMEG3vQ== dependencies: - "@abp/utils" "~10.5.0-rc.3" + "@abp/utils" "~10.5.0-rc.4" -"@abp/datatables.net-bs5@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.3.tgz#cf6bd77c189907f9e1683adb070b6965ca5a7c80" - integrity sha512-pKfvVlZgx3k3aRLUn8fU/kxgOBulwx6kKm0plnTPshG0MOA4v1CTAldFgZQsJ2XI2GYX26UO1SC244TArsFOug== +"@abp/datatables.net-bs5@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-10.5.0-rc.4.tgz#ec26b8a22d58880755f3593ac635b0daf082c886" + integrity sha512-f5WEXTEQC8S73Hz2QpCMkrh9vyFJifQ2Q9Jp6r0iLRURpBItzliUsSHcDAhdo3oPtuH9HOumQvISH4Y8S+EPzQ== dependencies: - "@abp/datatables.net" "~10.5.0-rc.3" + "@abp/datatables.net" "~10.5.0-rc.4" datatables.net-bs5 "^2.3.4" -"@abp/datatables.net@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.3.tgz#f550317bd7540100412ba72a379d91a96b80cb41" - integrity sha512-PRkJoR4WYw9N/Pab5Gai2FcqHBEOUA3GGNMx0c/Gslrad2Ahctmtri1KWvO+aGOfzBUNLsMDYJfldpHiZR0OIg== +"@abp/datatables.net@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-10.5.0-rc.4.tgz#46254de6e712640f12058e2f167083b3011e2c1b" + integrity sha512-i81nFF+Qevs602t0zZWAhQWBpCV9FBHixwWQmVDCfyhDYJbiMLUBPcscIqF7EIv1tz/dEdW2GofvEzIMcffbAg== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" datatables.net "^2.3.4" -"@abp/font-awesome@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.3.tgz#6c7f5e6f3702ab6a8c5288d59efeb0ca014b9e30" - integrity sha512-pIWsLlubLXxl+XVxNYcvtUOd1iDNlE/rOFqfMAt/7hBXWrAvnYXuCyweWWvrXTLGj5pUfEdMk8hJjXCZqoWCuw== +"@abp/font-awesome@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-10.5.0-rc.4.tgz#d7a8e4d32f84bfbe46c55dc93ad33f185e0f3848" + integrity sha512-guGsjSWTlUYtrvNlekEL3qEgEp1HeABnNEQdQF5yyukHXoOTC0pR/aPTmMiKHTiYfqpc3LoQMv+TU+ZAVQrU4w== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@fortawesome/fontawesome-free" "^7.0.1" -"@abp/highlight.js@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-10.5.0-rc.3.tgz#ceffb929de26eec5903149a4eb3b64900ad9f7f4" - integrity sha512-crb7LwCqMzt0wX5hC4zuIVgML+9ljDJNjh6kCnf5N5LhWH8vsBf+8dwdcp8Jm9/XE/1rOtXN3938lkFo9F/0Qw== +"@abp/highlight.js@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/highlight.js/-/highlight.js-10.5.0-rc.4.tgz#591a78f06b86ba3c46ab548e4380c486252562ef" + integrity sha512-FA7U9B559I0wug29E30vvNYw9fxjv3mVvq4ashftw7V5H9oQcztUbaDe+nOeSdPLwI8YQRa/0QdXky4QmsdDQQ== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@highlightjs/cdn-assets" "~11.11.1" -"@abp/jquery-validation-unobtrusive@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.3.tgz#59aabe284b8d3dbaaf2a35178eab08fc3cdd2ecc" - integrity sha512-K2aVIFLENcm/o21IuXk0Iy9xGI2sOWUqYWoToKH0IDJvwFqbep/xyPpjYCRXkMt/6Haq2kcru1P6Xy5Vym+Vfw== +"@abp/jquery-validation-unobtrusive@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-10.5.0-rc.4.tgz#93bb721e8dfff9a824d44980f6ce859c8c3866bb" + integrity sha512-U1b+2lnfdNQIVBUM8CjbP/EJaLDZ7EHrvWeJFn0aJYHglV8Jqka4+jPu0Be8NP1NqyQQSrhtqdf8axp742COmQ== dependencies: - "@abp/jquery-validation" "~10.5.0-rc.3" + "@abp/jquery-validation" "~10.5.0-rc.4" jquery-validation-unobtrusive "^4.0.0" -"@abp/jquery-validation@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.3.tgz#84cbb192cd90dd5b31f3c88a0a5017eea1c552f7" - integrity sha512-gUaSYkJaakNA9VC3nysFiX4NK3rJ38DzVZR9siMtqt0+kPAb5jySt7xf0CV1CNgOOlbbeefbZclDivQPIiOTzw== +"@abp/jquery-validation@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-10.5.0-rc.4.tgz#b90ceb992b9c0a7fc4f8faccaaaa628c259f028e" + integrity sha512-0qYfsAwhcatyiJpeZejmG3DgOAoCbWQcHOUM0Bgxg8gh+vcuzXu//956SBJqbkeTJiaeY4z8uULaQZpAiF+ZRQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" jquery-validation "^1.21.0" -"@abp/jquery@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.3.tgz#333b97ca19a3ee40ed5da8ed7ad407b3e7b8358a" - integrity sha512-/as9TRCpE9xHgGgdm2GNNHWE/F/rm8C9j4/9i7BkrX27lsKQDK0ulVyJ6qwEjkFN/OGSA+5TyQD1h+E5s42APw== +"@abp/jquery@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-10.5.0-rc.4.tgz#623d7be22842b37505795e15069f0993f7c417eb" + integrity sha512-IATc1SVqRPd3xDCPHQZh1GaMgGGcVmxUYFDo/+t6c768iLCwLpJPuoNebrBSeN+z9+opbuZrFLNdwWQ+Ch72QA== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" jquery "~3.7.1" -"@abp/jstree@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/jstree/-/jstree-10.5.0-rc.3.tgz#70b139caa5bb695a6596e270f277161dbd76be51" - integrity sha512-o1gAXT1A50e8RFUnZ9cDM6VkvSz73hrAV6ftPHj0cOjlEDSnWiHp4mjjLgk9ZHEDHRWhOiB/DK+Xxf5R7APSWg== +"@abp/jstree@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/jstree/-/jstree-10.5.0-rc.4.tgz#f1c99fbda6fb18d356cf14fe9da83a867ecbbaa5" + integrity sha512-VYdsmmIqPJd1aHR3XWDQ7FKl35i7t+O4x5PBUT0/jFwHqJSlNSRcl77vQULTltr4uYOt8SE6xIu3l9CVb/DLMw== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" jstree "^3.3.17" -"@abp/lodash@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.3.tgz#2842e31372d745f7e2b4c19dd973a889ad328de5" - integrity sha512-+DKOjSPwtoU+8nwgFAyXmczLVxwTmboek1FZWs303eUGKALVkVW8hLFGXVmYiswMo9qXqrWU/oTu3YcGx+V2nQ== +"@abp/lodash@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-10.5.0-rc.4.tgz#284d4ebf314cfb1d96e66e8994f22b44055e4130" + integrity sha512-rswQ/laMhlu1MA+51CcIbQR1iZcOdSwF2HzxWccCqsaBVCOWy4G9TIj1gk2QfQSYMkBl6OWNprOea7XKhaSfzw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" lodash "^4.18.1" -"@abp/luxon@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.3.tgz#97c02dac4c80f3cc1ba20dea24a0c553810e148d" - integrity sha512-H7If3E9T/nICq6d7VrI4jptTON+m0i5MZPWti3jbmrvvukKbTef0TQkHOzFy6HM/P3cChospbSIXBAJJjtzTug== +"@abp/luxon@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/luxon/-/luxon-10.5.0-rc.4.tgz#c35120586c16afbafdefbb0a4b1cefb3e7e97690" + integrity sha512-Mk5oMzecFf5v35/EW2FvVYmQX16giT8J/QpeqyaPWbp0UptOSYrA7E+gHoJGvs3SvY24KkPzTfdcaWzCyV6zIg== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" luxon "^3.7.2" -"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.3.tgz#7053c8c0bfd80d58f981dcfd4479ac3f41ef106d" - integrity sha512-DuF877PsZJItCi6zsEPjtv0njGTCtecrOsjRW389s6+q0ZoCa/yxTxA3nqr2I0c2ApGpjaypuY7ta7yrKNqXAA== +"@abp/malihu-custom-scrollbar-plugin@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-10.5.0-rc.4.tgz#f7e215c48012306d449544be35b384face75caa8" + integrity sha512-nu00haqvL68AnGIX0enLy3XvhDq72/ws7f+oh8Fr5gPPtywI1fzMnkXIGoXewE/5wcD7Fs/hHjJorFlX0yWOgw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" malihu-custom-scrollbar-plugin "^3.1.5" -"@abp/markdown-it@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/markdown-it/-/markdown-it-10.5.0-rc.3.tgz#5e1f2d069139b30628d11c2eed37fdaaed6b93c3" - integrity sha512-bgFsY3HkLqNF682Bxlhi9m/VwDNXq1D3UR9cXyP/ucaJNsHwyZuoAr3PK1NkhoBV3jjxsqE6ft8cQaH1xfLUXA== +"@abp/markdown-it@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/markdown-it/-/markdown-it-10.5.0-rc.4.tgz#e05ef3bbef1857aeb0933dd3fd9d6814e2c04821" + integrity sha512-iVUKOqN1FXZwS2hQ63IE1RpDEogGsbhrsWGern2XxGU2PrIMfQxRfqrBBd70AF9ETL8abTV+o6tSajG6pm2N1w== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" markdown-it "^14.1.0" -"@abp/moment@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.3.tgz#64886fddc8eff5a8bfcb0888de86c8b7186de33a" - integrity sha512-c8G2NHqNh0U9ca9Cbfaxq/zcS0vShQXjK0sXtrmoR1WjOYHQMam+nS63neLmUV4FRe3kpNbQ6+CyH6A0v4AIug== +"@abp/moment@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/moment/-/moment-10.5.0-rc.4.tgz#4d02915b3650b8afe1160ab91c09d274bd9108d6" + integrity sha512-6giU879bnFltkefbb/it4bWxBfZn+QDzDgP1aPC4xNIa134ZXzkcSMRlMyUBa/96OZ6mmmYo/6wLFZLCAsOPZw== dependencies: moment "^2.30.1" -"@abp/popper.js@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.3.tgz#47227a677923876b2642091cea199455195686ec" - integrity sha512-HrRKcQbqT7IddjXwsbPk2jrHbERwY9pxmvxwQnnCy1BKsNzhdKrj0h14QpxaX268cBcwKRQ/GWSvj56QiF6xhQ== +"@abp/popper.js@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/popper.js/-/popper.js-10.5.0-rc.4.tgz#4a74c6e1f72da2ba8f67b33856196558a5efff72" + integrity sha512-oJ4pDrVCgu0RadsdwlppGN3tPzMymQ5DWhqK6CKEeir4lBFubKDe1NAhawZ4qGtIXKFWIbE30prFg11pf/zj9Q== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" "@popperjs/core" "^2.11.8" -"@abp/prismjs@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.3.tgz#c2a0816f5a6d0ae7a6ae56687bc7d62bef89f707" - integrity sha512-1YsVUx3GZID5H3GlUFljO8gEnKSDigbDsfX2zD/Kue3vCYnlq0Ysrx+jesNAvLP/UO5JBMPiAzr8xHU+KV5q6g== +"@abp/prismjs@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/prismjs/-/prismjs-10.5.0-rc.4.tgz#d130f9e872eef068da63497b53ef1823347dc403" + integrity sha512-epb+8m21G8glvjHh2NgzO6kK3JDgliwXmfyIvRBg2/dUyovCyQ9A4V9jOA0VfaqTHz7sl0liM7M66/gP3XiGhA== dependencies: - "@abp/clipboard" "~10.5.0-rc.3" - "@abp/core" "~10.5.0-rc.3" + "@abp/clipboard" "~10.5.0-rc.4" + "@abp/core" "~10.5.0-rc.4" prismjs "^1.30.0" -"@abp/select2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.3.tgz#f117e0c51440ecc33bb32852547a3dd411deca38" - integrity sha512-2xrIgVBAjSV3d2bnW3JKqzG/TBq4cT1atioXT9WzAvK78H8jIF28LA7uM7iYIHgyRWcIdYQqzvuy170ts/aHPA== +"@abp/select2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-10.5.0-rc.4.tgz#87448d8b1d2ec97c0cb97e1c08db3ba2618e29f7" + integrity sha512-64utn6ikY5q71TIfO9qhXWpy0pGrj1YEpAdO/q3qYoFYf9RDz9mSFd2cJ8Noasph/CmZSKFK3YwPcHFkSeLTYQ== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" select2 "^4.0.13" -"@abp/slugify@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/slugify/-/slugify-10.5.0-rc.3.tgz#437ddb0b56c966a9a0f60ea641636685438b2cca" - integrity sha512-fsIEV6PD3tViuBLc0CGuUcK4sLjsxRB4yaGC2vOBd1evfBB3Dy39ZN0+OJOoJS8oFLBTpb32tIR2bz7xgw+2xg== +"@abp/slugify@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/slugify/-/slugify-10.5.0-rc.4.tgz#247c6bdc9cf2e6056809e1f3c5dfe7267af54e55" + integrity sha512-s9Nr7husDQtFAwaB2aY2b/65JwcylJvUuOUQtLR0NitxJfLq7RhfxxVWR9+0PeHiGzU9osbaE66fXse+vo4+sg== dependencies: slugify "^1.6.6" -"@abp/star-rating-svg@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/star-rating-svg/-/star-rating-svg-10.5.0-rc.3.tgz#62990b9af71292f72531d16bf5aa6853f363d629" - integrity sha512-r0KFzqK0MO57YKY0vdVk8ST2T9PwXXpIUmGTT7wRf8d83pSmogEDxw8AYusb3tbi5A1YvzdtRZTglqYuLuCc6g== +"@abp/star-rating-svg@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/star-rating-svg/-/star-rating-svg-10.5.0-rc.4.tgz#e6cbd11e8aa4c5308ed2b6a79424f30f66c9a68e" + integrity sha512-RV2lKxblJ2DtR9+sdhdTsJGGvVGITQu6adPCXcSBkHB8yRWF93K7WayTvNLJV3XwAzJWBLp1GyMQtX9X/wXAzQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" star-rating-svg "^3.5.0" -"@abp/sweetalert2@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.3.tgz#94e0a0dad4eed969282865371813151591e36fec" - integrity sha512-kqTMrrQ0QLqPZ9ref3IXmMWZM3ZYapTSqc4Ci7vIXEh3HNNPxk4qqkMPQWBEggeAZaDbRldycT47lGURu8f+Yw== +"@abp/sweetalert2@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-10.5.0-rc.4.tgz#aa667ee6edb6b008961a463f7f477a4671d28381" + integrity sha512-+/OKRL2hzohXryUD2rZO7aFLL7G7A300/QK82BKnYSXr7fh2wsVEC9g4/DXnaoygad7k5KS3NWUMIpEL21ROpw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" sweetalert2 "^11.23.0" -"@abp/timeago@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.3.tgz#40a5dc466791dd114e97c8c0665e77425ac5458d" - integrity sha512-SpVkKvwBGsiAHSZ+yAm6YsJcpuwJ0U5qPU89JgmUT+NxS6+VwyELF166IzJYS+N3mq/bM7UR/jlwctHUIOaYPA== +"@abp/timeago@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-10.5.0-rc.4.tgz#02b27e97c20b6b226cbdaa43d7bfb367218a626f" + integrity sha512-XCopnK5kphD6+OQ6+RHsgZHL7WtaXBbiYHpQmo7JHr9iVBwvLmWSzJZMIbrfZTczkhHk2VEwMQWSUwnJsWCWvQ== dependencies: - "@abp/jquery" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" timeago "^1.6.7" -"@abp/tui-editor@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-10.5.0-rc.3.tgz#3039865c149cea98dfedace000c4b392dc0680ed" - integrity sha512-SrwvvLH4GTJg9basCsu1cjmdy7BweDUAmZg0Zy8zZH1ry68FrV7AqGUujopssXkRooP6W/0VRpobbAXEyAAwzw== +"@abp/tui-editor@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/tui-editor/-/tui-editor-10.5.0-rc.4.tgz#e4d8359e11fadb84ccc9d1e3445f5a3370fd4656" + integrity sha512-hDC1gGPF6PtRWWkaSY0lu/WcpwnPSirG7394PNLKOXBuKoJss9pCJWbTkJYavz3wWOtOe/cwhuv29vg6q2+p6g== dependencies: - "@abp/jquery" "~10.5.0-rc.3" - "@abp/prismjs" "~10.5.0-rc.3" + "@abp/jquery" "~10.5.0-rc.4" + "@abp/prismjs" "~10.5.0-rc.4" -"@abp/uppy@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/uppy/-/uppy-10.5.0-rc.3.tgz#5b5a98c7bb59b991be51b96fc3ff63e85484d730" - integrity sha512-LlJRY7PrMCZnFuQLueD++s+HXlwkVJBririh0qU0eODsHmPRmRP+xMk83Ulp9d7Kw5SHUNaqz7j19kyhZs/kDw== +"@abp/uppy@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/uppy/-/uppy-10.5.0-rc.4.tgz#64b9d40290e7f2d4c17d6be026bd3a9afc2201af" + integrity sha512-vbb5BNcXWIz6riPjj4bPPDdMvU5jeCHc3Lr02tWI9376xRF0+fI/o9g/RsnmlTQGBrELuCoJY2z+Meye/DFNRw== dependencies: - "@abp/core" "~10.5.0-rc.3" + "@abp/core" "~10.5.0-rc.4" uppy "^5.1.2" -"@abp/utils@~10.5.0-rc.3": - version "10.5.0-rc.3" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.3.tgz#bb6704f710737682651bee2d768ec6246201207e" - integrity sha512-bfK6SjS2fhLnqy+9jbrHrmgyS6lQ8ppX/CZzcuz/qZV0tE8veQlPgmyy7IEZtfPmDFKKz3mLP/hWcd/7n7UUOA== +"@abp/utils@~10.5.0-rc.4": + version "10.5.0-rc.4" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-10.5.0-rc.4.tgz#3bddb096f7537b69c08c2c81c99607519b58c200" + integrity sha512-jYNIF72jXBYHWxrHTe3Na5yhPm1e+b76U0PpA2chJ+Qs6LHWdwQXGGvplQO3KrInrA+dNqrWzera7a1N9U1WhQ== dependencies: just-compare "^2.3.0" diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/CmsKitAdminWebMappers.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/CmsKitAdminWebMappers.cs index 99b07a65fc..fe9dadfd66 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Web/CmsKitAdminWebMappers.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Web/CmsKitAdminWebMappers.cs @@ -19,8 +19,8 @@ public partial class BlogFeatureInputDtoToBlogFeatureViewModelMapper : TwoWayMap public override partial FeaturesModalModel.BlogFeatureViewModel Map(BlogFeatureInputDto source); public override partial void Map(BlogFeatureInputDto source, FeaturesModalModel.BlogFeatureViewModel destination); - public override partial BlogFeatureInputDto ReverseMap(FeaturesModalModel.BlogFeatureViewModel destination); - public override partial void ReverseMap(FeaturesModalModel.BlogFeatureViewModel destination, BlogFeatureInputDto source); + public override partial BlogFeatureInputDto ReverseMap(FeaturesModalModel.BlogFeatureViewModel source); + public override partial void ReverseMap(FeaturesModalModel.BlogFeatureViewModel source, BlogFeatureInputDto destination); } [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] @@ -29,8 +29,8 @@ public partial class BlogFeatureDtoToBlogFeatureViewModelMapper : TwoWayMapperBa public override partial FeaturesModalModel.BlogFeatureViewModel Map(BlogFeatureDto source); public override partial void Map(BlogFeatureDto source, FeaturesModalModel.BlogFeatureViewModel destination); - public override partial BlogFeatureDto ReverseMap(FeaturesModalModel.BlogFeatureViewModel destination); - public override partial void ReverseMap(FeaturesModalModel.BlogFeatureViewModel destination, BlogFeatureDto source); + public override partial BlogFeatureDto ReverseMap(FeaturesModalModel.BlogFeatureViewModel source); + public override partial void ReverseMap(FeaturesModalModel.BlogFeatureViewModel source, BlogFeatureDto destination); } [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] @@ -39,8 +39,8 @@ public partial class CreateBlogPostDtoToCreateBlogPostViewModelMapper : TwoWayMa public override partial CreateModel.CreateBlogPostViewModel Map(CreateBlogPostDto source); public override partial void Map(CreateBlogPostDto source, CreateModel.CreateBlogPostViewModel destination); - public override partial CreateBlogPostDto ReverseMap(CreateModel.CreateBlogPostViewModel destination); - public override partial void ReverseMap(CreateModel.CreateBlogPostViewModel destination, CreateBlogPostDto source); + public override partial CreateBlogPostDto ReverseMap(CreateModel.CreateBlogPostViewModel source); + public override partial void ReverseMap(CreateModel.CreateBlogPostViewModel source, CreateBlogPostDto destination); } [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] @@ -49,8 +49,8 @@ public partial class TagUpdateDtoToTagEditViewModelMapper : TwoWayMapperBase=10.5.0-rc.3", - "@abp/ng.theme.shared": ">=10.5.0-rc.3" + "@abp/ng.core": ">=10.5.0-rc.4", + "@abp/ng.theme.shared": ">=10.5.0-rc.4" }, "dependencies": { "chart.js": "^3.5.1", diff --git a/npm/ng-packs/packages/core/package.json b/npm/ng-packs/packages/core/package.json index 249b5d4170..fd14c801f6 100644 --- a/npm/ng-packs/packages/core/package.json +++ b/npm/ng-packs/packages/core/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.core", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/utils": "~10.5.0-rc.3", + "@abp/utils": "~10.5.0-rc.4", "just-clone": "^6.0.0", "just-compare": "^2.0.0", "ts-toolbelt": "^9.0.0", diff --git a/npm/ng-packs/packages/core/src/lib/services/rest.service.ts b/npm/ng-packs/packages/core/src/lib/services/rest.service.ts index a2d45e2f12..4121eaa75a 100644 --- a/npm/ng-packs/packages/core/src/lib/services/rest.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/rest.service.ts @@ -1,7 +1,7 @@ -import { HttpClient, HttpParameterCodec, HttpParams, HttpRequest } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParameterCodec, HttpParams, HttpRequest } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; -import { catchError } from 'rxjs/operators'; +import { Observable, from, of, throwError } from 'rxjs'; +import { catchError, map, switchMap } from 'rxjs/operators'; import { ExternalHttpClient } from '../clients/http.client'; import { ABP } from '../models/common'; import { Rest } from '../models/rest'; @@ -39,7 +39,10 @@ export class RestService { api = api || this.getApiFromStore(config.apiName); const { method, params, ...options } = request; const { observe = Rest.Observe.Body, skipHandleError, responseType = Rest.ResponseType.JSON } = config; + const effectiveResponseType = + ((request as Rest.Request).responseType as Rest.ResponseType | undefined) ?? responseType; const url = this.removeDuplicateSlashes(api + request.url); + const headers = this.ensureAcceptHeader(options.headers, effectiveResponseType); const httpClient: HttpClient = this.getHttpClient(config.skipAddingHeader); return httpClient @@ -50,13 +53,111 @@ export class RestService { params: this.getParams(params, config.httpParamEncoder), }), ...options, + ...(headers && { headers }), } as any) - .pipe(catchError(err => (skipHandleError ? throwError(() => err) : this.handleError(err)))); + .pipe( + catchError(err => { + if (skipHandleError) { + return throwError(() => err); + } + return this.normalizeErrorBody(err, effectiveResponseType).pipe( + switchMap(normalizedErr => this.handleError(normalizedErr)), + ); + }), + ); } private getHttpClient(isExternal: boolean) { return isExternal ? this.externalHttp : this.http; } + private ensureAcceptHeader( + headers: Rest.Request['headers'], + responseType: Rest.ResponseType, + ): Rest.Request['headers'] | undefined { + const accept = this.getAcceptForResponseType(responseType); + if (!accept) return headers; + if (this.hasAcceptHeader(headers)) return headers; + if (headers instanceof HttpHeaders) { + return headers.set('Accept', accept); + } + return { ...(headers || {}), Accept: accept }; + } + + private hasAcceptHeader(headers: Rest.Request['headers']): boolean { + if (!headers) return false; + if (headers instanceof HttpHeaders) return headers.has('Accept'); + return Object.keys(headers).some(key => key.toLowerCase() === 'accept'); + } + + private getAcceptForResponseType(responseType: Rest.ResponseType): string | null { + switch (responseType) { + case Rest.ResponseType.Blob: + case Rest.ResponseType.ArrayBuffer: + return 'application/octet-stream'; + default: + return null; + } + } + + private normalizeErrorBody(err: any, responseType: Rest.ResponseType): Observable { + if (!err || responseType === Rest.ResponseType.JSON) { + return of(err); + } + + if (typeof err.error === 'string') { + this.tryParseJsonErrorText(err, err.error); + return of(err); + } + + if (typeof Blob !== 'undefined' && err.error instanceof Blob) { + return from(this.readBlobAsText(err.error)).pipe( + map((text: string) => { + this.tryParseJsonErrorText(err, text); + return err; + }), + catchError(() => of(err)), + ); + } + + return of(err); + } + + private readBlobAsText(blob: Blob): Promise { + if (typeof (blob as any).text === 'function') { + return (blob as any).text(); + } + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(typeof reader.result === 'string' ? reader.result : ''); + reader.onerror = () => reject(reader.error); + reader.readAsText(blob); + }); + } + + private tryParseJsonErrorText(err: any, text: string): void { + if (!text || text.length === 0) return; + let parsed: any; + try { + parsed = JSON.parse(text); + } catch { + return; + } + if (this.looksLikeAbpErrorEnvelope(parsed)) { + err.error = parsed; + } + } + + private looksLikeAbpErrorEnvelope(parsed: any): boolean { + if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return false; + const inner = parsed.error; + if (!inner || typeof inner !== 'object') return false; + return ( + typeof inner.code === 'string' || + typeof inner.message === 'string' || + Array.isArray(inner.validationErrors) + ); + } + private getParams(params: Rest.Params, encoder?: HttpParameterCodec): HttpParams { const filteredParams = Object.entries(params).reduce((acc, [key, value]) => { if (isUndefinedOrEmptyString(value)) return acc; diff --git a/npm/ng-packs/packages/core/src/lib/tests/rest.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/rest.service.spec.ts index 9ac795c24f..3009b0d6dc 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/rest.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/rest.service.spec.ts @@ -1,3 +1,4 @@ +import { HttpErrorResponse, HttpHeaders } from '@angular/common/http'; import { createHttpFactory, HttpMethod, SpectatorHttp, SpyObject } from '@ngneat/spectator/vitest'; import { OAuthService } from 'angular-oauth2-oidc'; import { of, throwError } from 'rxjs'; @@ -130,6 +131,367 @@ describe('HttpClient testing', () => { spectator.flushAll([req], [throwError('Testing error')]); }); + test('should set Accept: application/octet-stream when config.responseType is blob', () => { + spectator.service + .request({ method: HttpMethod.GET, url: '/file' }, { responseType: Rest.ResponseType.Blob }) + .subscribe(); + const req = spectator.expectOne(api + '/file', HttpMethod.GET); + expect(req.request.headers.get('Accept')).toEqual('application/octet-stream'); + expect(req.request.responseType).toEqual('blob'); + }); + + test('should set Accept based on request-level responseType (generator path)', () => { + spectator.service + .request({ method: HttpMethod.GET, url: '/file', responseType: 'blob' }) + .subscribe(); + const req = spectator.expectOne(api + '/file', HttpMethod.GET); + expect(req.request.headers.get('Accept')).toEqual('application/octet-stream'); + expect(req.request.responseType).toEqual('blob'); + }); + + test('should set Accept: application/octet-stream when responseType is arraybuffer', () => { + spectator.service + .request( + { method: HttpMethod.GET, url: '/binary' }, + { responseType: Rest.ResponseType.ArrayBuffer }, + ) + .subscribe(); + const req = spectator.expectOne(api + '/binary', HttpMethod.GET); + expect(req.request.headers.get('Accept')).toEqual('application/octet-stream'); + }); + + test('should NOT add Accept for text responseType (left to schematic / caller)', () => { + spectator.service + .request({ method: HttpMethod.GET, url: '/text' }, { responseType: Rest.ResponseType.Text }) + .subscribe(); + const req = spectator.expectOne(api + '/text', HttpMethod.GET); + expect(req.request.headers.has('Accept')).toBe(false); + }); + + test('should not override caller-supplied Accept header (plain object)', () => { + spectator.service + .request( + { method: HttpMethod.GET, url: '/file', headers: { Accept: 'image/png' } }, + { responseType: Rest.ResponseType.Blob }, + ) + .subscribe(); + const req = spectator.expectOne(api + '/file', HttpMethod.GET); + expect(req.request.headers.get('Accept')).toEqual('image/png'); + }); + + test('should not override caller-supplied Accept header (HttpHeaders)', () => { + spectator.service + .request( + { + method: HttpMethod.GET, + url: '/file', + headers: new HttpHeaders({ Accept: 'image/jpeg' }), + }, + { responseType: Rest.ResponseType.Blob }, + ) + .subscribe(); + const req = spectator.expectOne(api + '/file', HttpMethod.GET); + expect(req.request.headers.get('Accept')).toEqual('image/jpeg'); + }); + + test('should preserve caller-supplied non-Accept headers and add Accept', () => { + spectator.service + .request( + { method: HttpMethod.GET, url: '/file', headers: { 'X-Custom': '1' } }, + { responseType: Rest.ResponseType.Blob }, + ) + .subscribe(); + const req = spectator.expectOne(api + '/file', HttpMethod.GET); + expect(req.request.headers.get('Accept')).toEqual('application/octet-stream'); + expect(req.request.headers.get('X-Custom')).toEqual('1'); + }); + + test('should not add Accept header for default JSON responseType', () => { + spectator.service.request({ method: HttpMethod.GET, url: '/json' }).subscribe(); + const req = spectator.expectOne(api + '/json', HttpMethod.GET); + expect(req.request.headers.has('Accept')).toBe(false); + }); + + test('should NOT unwrap error body when skipHandleError is true', async () => { + const spy = vi.spyOn(httpErrorReporter, 'reportError'); + + const completion = new Promise((resolve, reject) => { + spectator.service + .request( + { method: HttpMethod.GET, url: '/text' }, + { responseType: Rest.ResponseType.Text, skipHandleError: true }, + ) + .pipe( + catchError(err => { + try { + expect(spy).toHaveBeenCalledTimes(0); + expect(typeof err.error).toBe('string'); + expect(err.error).toBe('{"error":{"code":"X","message":"y"}}'); + resolve(); + } catch (e) { + reject(e); + } + return of(null); + }), + ) + .subscribe(); + }); + + const req = spectator.expectOne(api + '/text', HttpMethod.GET); + req.flush('{"error":{"code":"X","message":"y"}}', { + status: 500, + statusText: 'Internal Server Error', + }); + + await completion; + }); + + test('should unwrap ABP validationErrors envelope in text mode', async () => { + const spy = vi.spyOn(httpErrorReporter, 'reportError'); + + const completion = new Promise((resolve, reject) => { + spectator.service + .request({ method: HttpMethod.GET, url: '/text' }, { responseType: Rest.ResponseType.Text }) + .pipe( + catchError(() => { + try { + const errArg: any = spy.mock.calls[0][0]; + expect(typeof errArg.error).toBe('object'); + expect(errArg.error.error.message).toBe('Validation failed'); + expect(errArg.error.error.validationErrors).toHaveLength(1); + resolve(); + } catch (e) { + reject(e); + } + return of(null); + }), + ) + .subscribe(); + }); + + const req = spectator.expectOne(api + '/text', HttpMethod.GET); + req.flush( + '{"error":{"message":"Validation failed","validationErrors":[{"message":"Required","members":["Name"]}]}}', + { status: 400, statusText: 'Bad Request' }, + ); + + await completion; + }); + + test('should unwrap ABP envelope that carries only validationErrors (no message / code)', async () => { + const completion = new Promise((resolve, reject) => { + spectator.service + .request({ method: HttpMethod.GET, url: '/text' }, { responseType: Rest.ResponseType.Text }) + .pipe( + catchError(err => { + try { + expect(typeof err.error).toBe('object'); + expect(err.error.error.validationErrors).toHaveLength(1); + resolve(); + } catch (e) { + reject(e); + } + return of(null); + }), + ) + .subscribe(); + }); + + const req = spectator.expectOne(api + '/text', HttpMethod.GET); + req.flush( + '{"error":{"validationErrors":[{"message":"Required","members":["Email"]}]}}', + { status: 400, statusText: 'Bad Request' }, + ); + + await completion; + }); + + test('should leave non-ABP-envelope JSON body alone in text mode', async () => { + const completion = new Promise((resolve, reject) => { + spectator.service + .request({ method: HttpMethod.GET, url: '/text' }, { responseType: Rest.ResponseType.Text }) + .pipe( + catchError(err => { + try { + expect(typeof err.error).toBe('string'); + expect(err.error).toBe('{"foo":"bar"}'); + resolve(); + } catch (e) { + reject(e); + } + return of(null); + }), + ) + .subscribe(); + }); + + const req = spectator.expectOne(api + '/text', HttpMethod.GET); + req.flush('{"foo":"bar"}', { status: 500, statusText: 'err' }); + + await completion; + }); + + test('should unwrap JSON-encoded error body in text mode for HttpErrorReporter', async () => { + const spy = vi.spyOn(httpErrorReporter, 'reportError'); + + const completion = new Promise((resolve, reject) => { + spectator.service + .request( + { method: HttpMethod.GET, url: '/text' }, + { responseType: Rest.ResponseType.Text }, + ) + .pipe( + catchError(() => { + try { + expect(spy).toHaveBeenCalledTimes(1); + const errArg: any = spy.mock.calls[0][0]; + expect(errArg.error).toEqual({ + error: { code: 'AbpAuthorization.001', message: 'forbidden' }, + }); + resolve(); + } catch (e) { + reject(e); + } + return of(null); + }), + ) + .subscribe(); + }); + + const req = spectator.expectOne(api + '/text', HttpMethod.GET); + req.flush('{"error":{"code":"AbpAuthorization.001","message":"forbidden"}}', { + status: 403, + statusText: 'Forbidden', + }); + + await completion; + }); + + test('should unwrap ABP error envelope from a Blob body in blob mode', async () => { + // HttpTestingController doesn't deliver Blob in HttpErrorResponse; call the helper directly. + const err: any = { + error: new Blob( + ['{"error":{"code":"AbpAuthorization.002","message":"forbidden-blob"}}'], + { type: 'application/json' }, + ), + }; + + const normalized: any = await (spectator.service as any) + .normalizeErrorBody(err, Rest.ResponseType.Blob) + .toPromise(); + + expect(normalized.error).toEqual({ + error: { code: 'AbpAuthorization.002', message: 'forbidden-blob' }, + }); + }); + + test('should leave non-JSON Blob body alone in blob mode', async () => { + const blob = new Blob([new Uint8Array([0xff, 0xd8, 0xff])], { type: 'image/jpeg' }); + const err: any = { error: blob }; + + const normalized: any = await (spectator.service as any) + .normalizeErrorBody(err, Rest.ResponseType.Blob) + .toPromise(); + + expect(normalized.error).toBe(blob); + }); + + test('should swallow Blob.text() rejection and keep the original error in blob mode', async () => { + const fakeBlob = { + text: () => Promise.reject(new Error('boom')), + } as unknown as Blob; + const err: any = { error: fakeBlob, message: 'original' }; + + Object.setPrototypeOf(fakeBlob, Blob.prototype); + + const normalized: any = await (spectator.service as any) + .normalizeErrorBody(err, Rest.ResponseType.Blob) + .toPromise(); + + expect(normalized).toBe(err); + expect(normalized.error).toBe(fakeBlob); + }); + + test('should leave non-JSON error body alone in text mode', async () => { + const spy = vi.spyOn(httpErrorReporter, 'reportError'); + + const completion = new Promise((resolve, reject) => { + spectator.service + .request( + { method: HttpMethod.GET, url: '/text' }, + { responseType: Rest.ResponseType.Text }, + ) + .pipe( + catchError(() => { + try { + const errArg: any = spy.mock.calls[0][0]; + expect(errArg.error).toBe('plain error text'); + resolve(); + } catch (e) { + reject(e); + } + return of(null); + }), + ) + .subscribe(); + }); + + const req = spectator.expectOne(api + '/text', HttpMethod.GET); + req.flush('plain error text', { status: 500, statusText: 'Internal Server Error' }); + + await completion; + }); + + test('should send Accept header emitted by schematic verbatim (json scenario)', () => { + spectator.service + .request({ + method: HttpMethod.GET, + url: '/status', + headers: { Accept: 'application/json' }, + }) + .subscribe(); + const req = spectator.expectOne(api + '/status', HttpMethod.GET); + expect(req.request.headers.get('Accept')).toEqual('application/json'); + }); + + test('should send Accept header emitted by schematic verbatim (text scenario)', () => { + spectator.service + .request({ method: HttpMethod.GET, url: '/csv', headers: { Accept: 'text/plain' } }) + .subscribe(); + const req = spectator.expectOne(api + '/csv', HttpMethod.GET); + expect(req.request.headers.get('Accept')).toEqual('text/plain'); + }); + + test('should leave JSON error body alone in json mode (no double-parse)', async () => { + const spy = vi.spyOn(httpErrorReporter, 'reportError'); + + const completion = new Promise((resolve, reject) => { + spectator.service + .request({ method: HttpMethod.GET, url: '/json' }) + .pipe( + catchError(() => { + try { + const errArg: any = spy.mock.calls[0][0]; + expect(errArg.error).toEqual({ error: { code: 'X', message: 'y' } }); + resolve(); + } catch (e) { + reject(e); + } + return of(null); + }), + ) + .subscribe(); + }); + + const req = spectator.expectOne(api + '/json', HttpMethod.GET); + req.flush( + { error: { code: 'X', message: 'y' } }, + { status: 403, statusText: 'Forbidden' }, + ); + + await completion; + }); + test('should remove the duplicate slashes', () => { spectator.service .request({ method: HttpMethod.GET, url: '//test', params: { id: 1 } }) diff --git a/npm/ng-packs/packages/feature-management/package.json b/npm/ng-packs/packages/feature-management/package.json index dc60396b66..60768d9968 100644 --- a/npm/ng-packs/packages/feature-management/package.json +++ b/npm/ng-packs/packages/feature-management/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.feature-management", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.4", "tslib": "^2.0.0" }, "peerDependencies": { diff --git a/npm/ng-packs/packages/generators/package.json b/npm/ng-packs/packages/generators/package.json index ca377f820a..04c83a14b2 100644 --- a/npm/ng-packs/packages/generators/package.json +++ b/npm/ng-packs/packages/generators/package.json @@ -1,6 +1,6 @@ { "name": "@abp/nx.generators", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "homepage": "https://abp.io", "generators": "./generators.json", "type": "commonjs", diff --git a/npm/ng-packs/packages/identity/package.json b/npm/ng-packs/packages/identity/package.json index de8fefc31c..03100c6063 100644 --- a/npm/ng-packs/packages/identity/package.json +++ b/npm/ng-packs/packages/identity/package.json @@ -1,15 +1,15 @@ { "name": "@abp/ng.identity", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.components": "~10.5.0-rc.3", - "@abp/ng.permission-management": "~10.5.0-rc.3", - "@abp/ng.theme.shared": "~10.5.0-rc.3", + "@abp/ng.components": "~10.5.0-rc.4", + "@abp/ng.permission-management": "~10.5.0-rc.4", + "@abp/ng.theme.shared": "~10.5.0-rc.4", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/oauth/package.json b/npm/ng-packs/packages/oauth/package.json index 04a7ff826e..6080f634cf 100644 --- a/npm/ng-packs/packages/oauth/package.json +++ b/npm/ng-packs/packages/oauth/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.oauth", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.core": "~10.5.0-rc.3", - "@abp/utils": "~10.5.0-rc.3", + "@abp/ng.core": "~10.5.0-rc.4", + "@abp/utils": "~10.5.0-rc.4", "angular-oauth2-oidc": "^20.0.0", "just-clone": "^6.0.0", "just-compare": "^2.0.0", diff --git a/npm/ng-packs/packages/permission-management/package.json b/npm/ng-packs/packages/permission-management/package.json index 85edab5b82..bf1e8ebe58 100644 --- a/npm/ng-packs/packages/permission-management/package.json +++ b/npm/ng-packs/packages/permission-management/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.permission-management", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "~10.5.0-rc.3", + "@abp/ng.theme.shared": "~10.5.0-rc.4", "tslib": "^2.0.0" }, "peerDependencies": { diff --git a/npm/ng-packs/packages/schematics/package.json b/npm/ng-packs/packages/schematics/package.json index 9250064323..788dada681 100644 --- a/npm/ng-packs/packages/schematics/package.json +++ b/npm/ng-packs/packages/schematics/package.json @@ -1,6 +1,6 @@ { "name": "@abp/ng.schematics", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "author": "", "schematics": "./collection.json", "dependencies": { diff --git a/npm/ng-packs/packages/schematics/src/commands/api/files-service/proxy/__namespace@dir__/__name@kebab__.service.ts.template b/npm/ng-packs/packages/schematics/src/commands/api/files-service/proxy/__namespace@dir__/__name@kebab__.service.ts.template index c445286162..22443a5a88 100644 --- a/npm/ng-packs/packages/schematics/src/commands/api/files-service/proxy/__namespace@dir__/__name@kebab__.service.ts.template +++ b/npm/ng-packs/packages/schematics/src/commands/api/files-service/proxy/__namespace@dir__/__name@kebab__.service.ts.template @@ -11,15 +11,17 @@ export class <%= name %>Service { <% const isBlob = body.isBlobMethod() ; const responseType = isBlob ? "Blob":body.responseType; + const httpResponseType = body.httpResponseType; + const acceptHeader = body.acceptHeader; %> <%= camel(signature.name) %> = (<%= serializeParameters(signature.parameters) %>) => this.restService.request<<%= body.requestType %>, <%= responseType %>>({ method: '<%= body.method %>',<% - if (body.responseType === 'string') { %> - responseType: 'text',<% } %><% - if (isBlob) { %> - responseType: 'blob',<% } %> + if (httpResponseType && httpResponseType !== 'json') { %> + responseType: '<%= httpResponseType %>',<% } %><% + if (acceptHeader) { %> + headers: { Accept: '<%= acceptHeader %>' },<% } %> url: <%= body.url %>,<% if (body.dictParamVar && !body.params.length) { %> params: <%= body.dictParamVar %>,<% } %><% diff --git a/npm/ng-packs/packages/schematics/src/enums/binding-source-id.ts b/npm/ng-packs/packages/schematics/src/enums/binding-source-id.ts index 308b8dfc32..91c1901e09 100644 --- a/npm/ng-packs/packages/schematics/src/enums/binding-source-id.ts +++ b/npm/ng-packs/packages/schematics/src/enums/binding-source-id.ts @@ -3,5 +3,6 @@ export enum eBindingSourceId { Model = 'ModelBinding', Path = 'Path', Query = 'Query', + Form = 'Form', FormFile = 'FormFile', } diff --git a/npm/ng-packs/packages/schematics/src/models/api-definition.ts b/npm/ng-packs/packages/schematics/src/models/api-definition.ts index 70dca68dd5..3e6458e91d 100644 --- a/npm/ng-packs/packages/schematics/src/models/api-definition.ts +++ b/npm/ng-packs/packages/schematics/src/models/api-definition.ts @@ -68,7 +68,7 @@ export interface Action { supportedVersions: string[]; parametersOnMethod: ParameterInSignature[]; parameters: ParameterInBody[]; - returnValue: TypeDef; + returnValue: ReturnValueDef; } export interface ParameterInSignature { @@ -100,6 +100,11 @@ export interface TypeDef { typeSimple: string; } +export interface ReturnValueDef extends TypeDef { + contentTypes?: string[]; + isRemoteStream?: boolean; +} + export interface TypeWithEnum { isEnum: boolean; type: string; diff --git a/npm/ng-packs/packages/schematics/src/models/method.ts b/npm/ng-packs/packages/schematics/src/models/method.ts index 404fa9d233..c160359770 100644 --- a/npm/ng-packs/packages/schematics/src/models/method.ts +++ b/npm/ng-packs/packages/schematics/src/models/method.ts @@ -45,6 +45,8 @@ export class Body { responseTypeWithNamespace: string; requestType = 'any'; responseType: string; + httpResponseType?: 'json' | 'text' | 'blob' | 'arraybuffer'; + acceptHeader?: string; url: string; registerActionParameter = (param: ParameterInBody) => { @@ -85,6 +87,9 @@ export class Body { } isBlobMethod() { + if (this.httpResponseType === 'blob') { + return true; + } return VOLO_REMOTE_STREAM_CONTENT.some(x => x === this.responseTypeWithNamespace); } diff --git a/npm/ng-packs/packages/schematics/src/tests/action-to-body-mapper.spec.ts b/npm/ng-packs/packages/schematics/src/tests/action-to-body-mapper.spec.ts new file mode 100644 index 0000000000..9e35e9ef8c --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/tests/action-to-body-mapper.spec.ts @@ -0,0 +1,846 @@ +import { readFileSync } from 'fs'; +import { join } from 'path'; +import { describe, expect, test } from 'vitest'; +import { eBindingSourceId } from '../enums'; +import { Action } from '../models'; +import { + createActionToBodyMapper, + createActionToMethodMapper, + createActionToSignatureMapper, +} from '../utils/service'; + +const TEMPLATE_PATH = join( + __dirname, + '..', + 'commands', + 'api', + 'files-service', + 'proxy', + '__namespace@dir__', + '__name@kebab__.service.ts.template', +); + +function buildAction(overrides: Partial): Action { + return { + uniqueName: 'GetStatusAsync', + name: 'GetStatus', + httpMethod: 'GET', + url: 'api/app/test-service/status', + supportedVersions: [], + parametersOnMethod: [], + parameters: [], + returnValue: { type: 'System.String', typeSimple: 'string' }, + ...overrides, + } as Action; +} + +describe('createActionToBodyMapper — string return value', () => { + const mapBody = createActionToBodyMapper(); + + test('without contentTypes falls back to text mode (legacy behavior)', () => { + const body = mapBody(buildAction({})); + + expect(body.responseType).toBe('string'); + expect(body.httpResponseType).toBe('text'); + expect(body.acceptHeader).toBeUndefined(); + }); + + test('with contentTypes containing application/json picks json + Accept: application/json', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['application/json', 'text/plain'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('json'); + expect(body.acceptHeader).toBe('application/json'); + }); + + test('with only text/* contentTypes picks text + Accept: text/plain', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['text/plain', 'text/csv'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('text'); + expect(body.acceptHeader).toBe('text/plain'); + }); +}); + +describe('createActionToBodyMapper — IRemoteStreamContent return value', () => { + const mapBody = createActionToBodyMapper(); + + test('always picks blob + Accept: application/octet-stream regardless of contentTypes', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'Volo.Abp.Content.IRemoteStreamContent', + typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('blob'); + expect(body.acceptHeader).toBe('application/octet-stream'); + expect(body.isBlobMethod()).toBe(true); + }); + + test('binary-only contentTypes picks blob and echoes back the actual binary media type', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.Byte[]', + typeSimple: 'byte[]', + contentTypes: ['application/pdf'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('blob'); + expect(body.acceptHeader).toBe('application/pdf'); + }); +}); + +describe('createActionToBodyMapper — other return values', () => { + const mapBody = createActionToBodyMapper(); + + test('object return without contentTypes has no httpResponseType / acceptHeader (defaults to json)', () => { + const body = mapBody( + buildAction({ + returnValue: { type: 'My.Project.UserDto', typeSimple: 'My.Project.UserDto' }, + } as Partial), + ); + + expect(body.httpResponseType).toBeUndefined(); + expect(body.acceptHeader).toBeUndefined(); + }); + + test('void return has no httpResponseType / acceptHeader', () => { + const body = mapBody( + buildAction({ + returnValue: { type: 'System.Void', typeSimple: 'void' }, + } as Partial), + ); + + expect(body.httpResponseType).toBeUndefined(); + expect(body.acceptHeader).toBeUndefined(); + }); + + test('registers a query parameter via the binding source', () => { + const body = mapBody( + buildAction({ + parameters: [ + { + nameOnMethod: 'id', + name: 'id', + jsonName: null, + type: 'System.Guid', + typeSimple: 'string', + isOptional: false, + defaultValue: null, + constraintTypes: null, + bindingSourceId: eBindingSourceId.Query, + descriptorName: '', + }, + ], + }), + ); + + expect(body.params).toEqual(['id']); + }); +}); + +describe('createActionToBodyMapper — IsRemoteStream backend flag', () => { + const mapBody = createActionToBodyMapper(); + + test('isRemoteStream=true forces blob even if Type is a custom subclass name', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'My.Project.CustomStreamContent', + typeSimple: 'My.Project.CustomStreamContent', + isRemoteStream: true, + contentTypes: ['text/plain', 'application/json'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('blob'); + expect(body.acceptHeader).toBe('application/octet-stream'); + expect(body.isBlobMethod()).toBe(true); + }); + + test('[Volo.Abp.Content.IRemoteStreamContent] (real ABP square-bracket form) must NOT pick blob and degrades responseType to any[]', () => { + // ABP serialises collections as `[T]` (not `T[]`) — pin the on-the-wire shape. + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.Collections.Generic.IList', + typeSimple: '[Volo.Abp.Content.IRemoteStreamContent]', + }, + } as Partial), + ); + + expect(body.httpResponseType).toBeUndefined(); + expect(body.acceptHeader).toBeUndefined(); + expect(body.isBlobMethod()).toBe(false); + expect(body.responseType).toBe('any[]'); + }); + + test('IRemoteStreamContent[] array return must NOT pick blob (server falls back to JSON metadata)', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'Volo.Abp.Content.IRemoteStreamContent[]', + typeSimple: 'Volo.Abp.Content.IRemoteStreamContent[]', + }, + } as Partial), + ); + + expect(body.httpResponseType).toBeUndefined(); + expect(body.acceptHeader).toBeUndefined(); + expect(body.isBlobMethod()).toBe(false); + expect(body.responseType).toBe('any[]'); + }); + + test('isRemoteStream=false with stream-content type-name still detected by type name (legacy)', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'Volo.Abp.Content.IRemoteStreamContent', + typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('blob'); + expect(body.acceptHeader).toBe('application/octet-stream'); + }); +}); + +describe('createActionToBodyMapper — +json suffix detection', () => { + const mapBody = createActionToBodyMapper(); + + test('application/problem+json echoes back as Accept (decoder stays json)', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['application/problem+json'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('json'); + expect(body.acceptHeader).toBe('application/problem+json'); + }); + + test('text/json echoes back as text/json (decoder stays json)', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['text/json'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('json'); + expect(body.acceptHeader).toBe('text/json'); + }); + + test('application/vnd.api+json echoes back as Accept (decoder stays json)', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['application/vnd.api+json'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('json'); + expect(body.acceptHeader).toBe('application/vnd.api+json'); + }); +}); + +describe('createActionToBodyMapper — expanded binary whitelist', () => { + const mapBody = createActionToBodyMapper(); + + test.each([ + ['application/wasm'], + ['font/woff2'], + ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'], + ['application/vnd.ms-excel'], + ['application/vnd.oasis.opendocument.spreadsheet'], + ['application/x-msdownload'], + ['application/rtf'], + ['application/x-rar-compressed'], + ['application/x-bzip2'], + ['application/x-iso9660-image'], + ['application/java-archive'], + ['application/epub+zip'], + ['model/gltf-binary'], + ])('"%s" picked as blob', mediaType => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.Byte[]', + typeSimple: 'byte[]', + contentTypes: [mediaType], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('blob'); + }); +}); + +describe('createActionToBodyMapper — contentTypes precedence and edge cases', () => { + const mapBody = createActionToBodyMapper(); + + test('isBlobMethod() type detection wins over json in contentTypes', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'Volo.Abp.Content.RemoteStreamContent', + typeSimple: 'Volo.Abp.Content.RemoteStreamContent', + contentTypes: ['application/json', 'text/plain'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('blob'); + expect(body.acceptHeader).toBe('application/octet-stream'); + }); + + test('IRemoteStreamContent[] array falls through to defaults (server returns JSON metadata, not binary)', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'Volo.Abp.Content.IRemoteStreamContent[]', + typeSimple: 'Volo.Abp.Content.IRemoteStreamContent[]', + }, + } as Partial), + ); + + expect(body.httpResponseType).toBeUndefined(); + expect(body.acceptHeader).toBeUndefined(); + }); + + test('case-insensitive json detection (APPLICATION/JSON)', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['APPLICATION/JSON'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('json'); + expect(body.acceptHeader).toBe('application/json'); + }); + + test('image/* contentTypes alone picks blob and echoes back the first image type', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.Byte[]', + typeSimple: 'byte[]', + contentTypes: ['image/png', 'image/jpeg'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('blob'); + expect(body.acceptHeader).toBe('image/png'); + }); + + test('video/* and audio/* picked as blob', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.Byte[]', + typeSimple: 'byte[]', + contentTypes: ['video/mp4', 'audio/mpeg'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('blob'); + }); + + test('application/pdf contentTypes picked as blob', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.Byte[]', + typeSimple: 'byte[]', + contentTypes: ['application/pdf'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('blob'); + }); + + test('empty contentTypes falls through to legacy string→text behavior', () => { + const body = mapBody( + buildAction({ + returnValue: { type: 'System.String', typeSimple: 'string', contentTypes: [] }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('text'); + expect(body.acceptHeader).toBeUndefined(); + }); + + test('mixed text/* and application/json picks json and echoes the first json-shaped media type', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['text/json', 'text/plain', 'application/json'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('json'); + expect(body.acceptHeader).toBe('text/json'); + }); + + test('contentTypes with json-suffix variants still picks json', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['application/json; charset=utf-8'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('json'); + }); + + test('non-string non-blob type with contentTypes containing json defaults appropriately', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'My.Project.UserDto', + typeSimple: 'My.Project.UserDto', + contentTypes: ['application/json'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('json'); + expect(body.acceptHeader).toBe('application/json'); + }); + + test('json contentType with charset parameter is normalized', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['application/json; charset=utf-8'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('json'); + expect(body.acceptHeader).toBe('application/json'); + }); + + test('text contentType with charset parameter is normalized', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['text/plain ; charset=utf-8 '], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('text'); + expect(body.acceptHeader).toBe('text/plain'); + }); + + test('mixed text/plain (with charset) and application/json picks json after normalize', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['text/plain; charset=utf-8', 'application/json; charset=utf-8'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('json'); + }); + + test('text/csv only (custom text format) picks text and echoes the content type back', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'System.String', + typeSimple: 'string', + contentTypes: ['text/csv'], + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('text'); + expect(body.acceptHeader).toBe('text/csv'); + }); +}); + +describe('createActionToBodyMapper — backward compatibility', () => { + const mapBody = createActionToBodyMapper(); + + test('legacy api-definition without contentTypes works (string)', () => { + const body = mapBody( + buildAction({ + returnValue: { type: 'System.String', typeSimple: 'string' } as Partial['returnValue'], + } as Partial), + ); + + expect(body.httpResponseType).toBe('text'); + expect(body.acceptHeader).toBeUndefined(); + }); + + test('legacy api-definition without contentTypes works (IRemoteStreamContent)', () => { + const body = mapBody( + buildAction({ + returnValue: { + type: 'Volo.Abp.Content.IRemoteStreamContent', + typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', + }, + } as Partial), + ); + + expect(body.httpResponseType).toBe('blob'); + expect(body.acceptHeader).toBe('application/octet-stream'); + }); + + test('legacy api-definition without contentTypes works (object)', () => { + const body = mapBody( + buildAction({ + returnValue: { type: 'My.Project.UserDto', typeSimple: 'My.Project.UserDto' }, + } as Partial), + ); + + expect(body.httpResponseType).toBeUndefined(); + expect(body.acceptHeader).toBeUndefined(); + }); + + test('void / no return preserves legacy behavior', () => { + const body = mapBody( + buildAction({ + returnValue: { type: 'System.Void', typeSimple: 'void' }, + } as Partial), + ); + + expect(body.httpResponseType).toBeUndefined(); + expect(body.acceptHeader).toBeUndefined(); + }); +}); + +describe('proxy service template emission', () => { + const template = readFileSync(TEMPLATE_PATH, 'utf8'); + + test('reads body.httpResponseType and body.acceptHeader from body', () => { + expect(template).toContain('body.httpResponseType'); + expect(template).toContain('body.acceptHeader'); + }); + + test('emits Accept header conditional', () => { + expect(template).toMatch(/headers:\s*\{\s*Accept:/); + }); + + test('emits responseType only for non-json httpResponseType', () => { + expect(template).toMatch(/httpResponseType\s*&&\s*httpResponseType\s*!==\s*'json'/); + }); +}); + +describe('createActionToBodyMapper — multipart FormData uploads', () => { + const mapBody = createActionToBodyMapper(); + + test('DTO with one IRemoteStreamContent property collapses to FormData body using the method arg name', () => { + const body = mapBody( + buildAction({ + httpMethod: 'POST', + parameters: [ + { name: 'Name', nameOnMethod: 'input', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.Form } as any, + { name: 'File', nameOnMethod: 'input', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial), + ); + + expect(body.body).toBe('input'); + expect(body.params).toEqual([]); + }); + + test('DTO with IEnumerable collapses to FormData body using the method arg name', () => { + const body = mapBody( + buildAction({ + httpMethod: 'POST', + parameters: [ + { name: 'Label', nameOnMethod: 'input', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.Form } as any, + { name: 'Files', nameOnMethod: 'input', type: 'System.Collections.Generic.IEnumerable', typeSimple: '[Volo.Abp.Content.IRemoteStreamContent]', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial), + ); + + expect(body.body).toBe('input'); + expect(body.params).toEqual([]); + }); + + test('nested DTO with IRemoteStreamContent collapses to FormData body using the method arg name', () => { + const body = mapBody( + buildAction({ + httpMethod: 'POST', + parameters: [ + { name: 'Outer', nameOnMethod: 'input', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.Form } as any, + { name: 'Child.File', nameOnMethod: 'input', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial), + ); + + expect(body.body).toBe('input'); + expect(body.params).toEqual([]); + }); + + test('direct IRemoteStreamContent parameter uses its own method arg name as the FormData body', () => { + const body = mapBody( + buildAction({ + httpMethod: 'POST', + parameters: [ + { name: 'file', nameOnMethod: 'file', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial), + ); + + expect(body.body).toBe('file'); + }); + + test('Path + upload-DTO mix keeps the path parameter while binding the upload to FormData', () => { + const body = mapBody( + buildAction({ + httpMethod: 'POST', + url: 'api/upload/{id}', + parameters: [ + { name: 'id', nameOnMethod: 'id', type: 'System.Int32', typeSimple: 'number', bindingSourceId: eBindingSourceId.Path } as any, + { name: 'Name', nameOnMethod: 'input', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.Form } as any, + { name: 'File', nameOnMethod: 'input', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial), + ); + + expect(body.body).toBe('input'); + expect(body.url).toBe("`/api/upload/${id}`"); + }); + + test('actions without FormFile parameters are not affected', () => { + const body = mapBody( + buildAction({ + httpMethod: 'POST', + parameters: [ + { name: 'Name', nameOnMethod: 'name', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.Body } as any, + ], + } as Partial), + ); + + expect(body.body).toBe('name'); + }); +}); + +describe('createActionToBodyMapper — multipart upload params regression', () => { + const mapBody = createActionToBodyMapper(); + + test('AppService convention ModelBinding non-file field sharing the upload arg is dropped from query params', () => { + const body = mapBody( + buildAction({ + httpMethod: 'POST', + url: 'api/app/proxy-demo-test/upload-single', + parameters: [ + { nameOnMethod: 'input', name: 'Name', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.ModelBinding } as any, + { nameOnMethod: 'input', name: 'File', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial), + ); + expect(body.body).toBe('input'); + expect(body.params.join(',')).not.toContain('name'); + }); + + test('Path param on a separate method arg stays in URL even when sibling form fields share the upload arg', () => { + const body = mapBody( + buildAction({ + httpMethod: 'POST', + url: 'api/proxy-demo/media/upload-with-path/{id}', + parameters: [ + { nameOnMethod: 'id', name: 'id', type: 'System.Int32', typeSimple: 'int', bindingSourceId: eBindingSourceId.Path } as any, + { nameOnMethod: 'input', name: 'Name', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.ModelBinding } as any, + { nameOnMethod: 'input', name: 'File', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial), + ); + expect(body.body).toBe('input'); + expect(body.url).toContain('${id}'); + expect(body.params.join(',')).not.toContain('name'); + }); + + test('Multiple direct FormFile method args: body forwards only the first var (known limitation, mirrors jQuery)', () => { + const body = mapBody( + buildAction({ + httpMethod: 'POST', + url: 'api/proxy-demo/media/upload-two-direct', + parameters: [ + { nameOnMethod: 'file1', name: 'file1', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + { nameOnMethod: 'file2', name: 'file2', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial), + ); + expect(body.body).toBe('file1'); + }); +}); + +describe('createActionToSignatureMapper — multipart upload signature collapse', () => { + const mapSignature = createActionToSignatureMapper(); + + test('FormFile DTO method arg collapses to FormData type', () => { + const sig = mapSignature(buildAction({ + httpMethod: 'POST', + url: 'api/app/proxy-demo-test/upload-single', + parametersOnMethod: [ + { name: 'input', type: 'AbpProxyDemo.UploadDto', typeAsString: 'AbpProxyDemo.UploadDto', typeSimple: 'AbpProxyDemo.UploadDto', isOptional: false, defaultValue: null } as any, + ], + parameters: [ + { nameOnMethod: 'input', name: 'Name', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.ModelBinding } as any, + { nameOnMethod: 'input', name: 'File', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial)); + const types = sig.parameters.map((p: any) => `${p.name}:${p.type}`); + expect(types).toContain('input:FormData'); + expect(types[types.length - 1]).toBe('config:Partial'); + }); + + test('Path arg keeps its primitive type while upload arg becomes FormData', () => { + const sig = mapSignature(buildAction({ + httpMethod: 'POST', + url: 'api/proxy-demo/media/upload-with-path/{id}', + parametersOnMethod: [ + { name: 'id', type: 'System.Int32', typeAsString: 'System.Int32', typeSimple: 'number', isOptional: false, defaultValue: null } as any, + { name: 'input', type: 'AbpProxyDemo.UploadDto', typeAsString: 'AbpProxyDemo.UploadDto', typeSimple: 'AbpProxyDemo.UploadDto', isOptional: false, defaultValue: null } as any, + ], + parameters: [ + { nameOnMethod: 'id', name: 'id', type: 'System.Int32', typeSimple: 'int', bindingSourceId: eBindingSourceId.Path } as any, + { nameOnMethod: 'input', name: 'Name', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.ModelBinding } as any, + { nameOnMethod: 'input', name: 'File', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial)); + const types = sig.parameters.map((p: any) => `${p.name}:${p.type}`); + expect(types).toEqual([ + 'id:number', + 'input:FormData', + 'config:Partial', + ]); + }); + + test('Multiple direct IRemoteStreamContent method args each become FormData independently', () => { + const sig = mapSignature(buildAction({ + httpMethod: 'POST', + url: 'api/proxy-demo/media/upload-two-direct', + parametersOnMethod: [ + { name: 'file1', type: 'Volo.Abp.Content.IRemoteStreamContent', typeAsString: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', isOptional: false, defaultValue: null } as any, + { name: 'file2', type: 'Volo.Abp.Content.IRemoteStreamContent', typeAsString: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', isOptional: false, defaultValue: null } as any, + ], + parameters: [ + { nameOnMethod: 'file1', name: 'file1', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + { nameOnMethod: 'file2', name: 'file2', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial)); + const types = sig.parameters.map((p: any) => `${p.name}:${p.type}`); + expect(types).toEqual([ + 'file1:FormData', + 'file2:FormData', + 'config:Partial', + ]); + }); + + test('Non-upload action signature stays untouched (regression guard)', () => { + const sig = mapSignature(buildAction({ + httpMethod: 'GET', + url: 'api/app/proxy-demo-test/get-item-by-id', + parametersOnMethod: [ + { name: 'id', type: 'System.Int32', typeAsString: 'System.Int32', typeSimple: 'number', isOptional: false, defaultValue: null } as any, + ], + parameters: [ + { nameOnMethod: 'id', name: 'id', type: 'System.Int32', typeSimple: 'int', bindingSourceId: eBindingSourceId.Path } as any, + ], + } as Partial)); + const types = sig.parameters.map((p: any) => `${p.name}:${p.type}`); + expect(types).toEqual(['id:number', 'config:Partial']); + }); +}); + +describe('createActionToMethodMapper — signature + body wired together for upload actions', () => { + const mapMethod = createActionToMethodMapper(); + + test('Upload action produces both FormData signature and FormData body in one pass', () => { + const method = mapMethod(buildAction({ + httpMethod: 'POST', + url: 'api/app/proxy-demo-test/upload-single', + parametersOnMethod: [ + { name: 'input', type: 'AbpProxyDemo.UploadDto', typeAsString: 'AbpProxyDemo.UploadDto', typeSimple: 'AbpProxyDemo.UploadDto', isOptional: false, defaultValue: null } as any, + ], + parameters: [ + { nameOnMethod: 'input', name: 'Name', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.ModelBinding } as any, + { nameOnMethod: 'input', name: 'File', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial)); + const sigTypes = method.signature.parameters.map((p: any) => `${p.name}:${p.type}`); + expect(sigTypes).toContain('input:FormData'); + expect(method.body.body).toBe('input'); + expect(method.body.params.join(',')).not.toContain('name'); + }); + + test('Query + ModelBinding + FormFile mix preserves Query as URL param and drops upload-arg fields', () => { + const method = mapMethod(buildAction({ + httpMethod: 'POST', + url: 'api/proxy-demo/media/upload-with-query', + parametersOnMethod: [ + { name: 'tag', type: 'System.String', typeAsString: 'System.String', typeSimple: 'string', isOptional: false, defaultValue: null } as any, + { name: 'input', type: 'AbpProxyDemo.UploadDto', typeAsString: 'AbpProxyDemo.UploadDto', typeSimple: 'AbpProxyDemo.UploadDto', isOptional: false, defaultValue: null } as any, + ], + parameters: [ + { nameOnMethod: 'tag', name: 'tag', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.Query } as any, + { nameOnMethod: 'input', name: 'Name', type: 'System.String', typeSimple: 'string', bindingSourceId: eBindingSourceId.ModelBinding } as any, + { nameOnMethod: 'input', name: 'File', type: 'Volo.Abp.Content.IRemoteStreamContent', typeSimple: 'Volo.Abp.Content.IRemoteStreamContent', bindingSourceId: eBindingSourceId.FormFile } as any, + ], + } as Partial)); + const sigTypes = method.signature.parameters.map((p: any) => `${p.name}:${p.type}`); + expect(sigTypes).toEqual(['tag:string', 'input:FormData', 'config:Partial']); + expect(method.body.body).toBe('input'); + expect(method.body.params).toContain('tag'); + expect(method.body.params.join(',')).not.toContain('name'); + }); +}); diff --git a/npm/ng-packs/packages/schematics/src/tests/proxy-service-template-render.spec.ts b/npm/ng-packs/packages/schematics/src/tests/proxy-service-template-render.spec.ts new file mode 100644 index 0000000000..04b070ae61 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/tests/proxy-service-template-render.spec.ts @@ -0,0 +1,495 @@ +import { readFileSync } from 'fs'; +import { join } from 'path'; +import { template as lodashTemplate } from 'lodash'; +import { describe, expect, test } from 'vitest'; + +/** + * Smoke test that actually renders the proxy `.service.ts.template` against + * representative body configurations and asserts the emitted code matches + * what the runtime contract requires. + * + * This catches template-syntax / control-flow regressions that a string + * `toContain` check on the template source would silently let through. + */ + +const TEMPLATE_PATH = join( + __dirname, + '..', + 'commands', + 'api', + 'files-service', + 'proxy', + '__namespace@dir__', + '__name@kebab__.service.ts.template', +); + +const TEMPLATE_SRC = readFileSync(TEMPLATE_PATH, 'utf8'); + +function render(context: Record): string { + const compiled = lodashTemplate(TEMPLATE_SRC, { + imports: { + camel: (s: string) => s.charAt(0).toLowerCase() + s.slice(1), + serializeParameters: (params: Array<{ name: string; type: string; default?: string }>) => + params.map(p => `${p.name}: ${p.type}`).join(', '), + }, + }); + return compiled(context); +} + +function buildContext(body: Partial) { + return { + apiName: 'Default', + name: 'Sample', + namespace: 'app', + imports: [ + { keyword: 'import', specifiers: ['RestService', 'Rest'], path: '@abp/ng.core' }, + { keyword: 'import', specifiers: ['Injectable', 'inject'], path: '@angular/core' }, + ], + methods: [ + { + body: makeBody(body), + signature: { + name: 'GetSampleAsync', + parameters: [], + }, + }, + ], + }; +} + +interface MockBody { + method: string; + url: string; + responseType: string; + responseTypeWithNamespace: string; + httpResponseType?: string; + acceptHeader?: string; + body?: string; + params: string[]; + dictParamVar?: string; + requestType: string; + isBlobMethod(): boolean; +} + +function makeBody(overrides: Partial): MockBody { + return { + method: 'GET', + url: "'/api/sample'", + responseType: 'any', + responseTypeWithNamespace: 'any', + httpResponseType: undefined, + acceptHeader: undefined, + body: undefined, + params: [], + dictParamVar: undefined, + requestType: 'any', + isBlobMethod: () => false, + ...overrides, + }; +} + +describe('proxy service template — rendered output', () => { + test('default JSON body emits no responseType and no Accept header', () => { + const output = render(buildContext({ + responseType: 'MyDto', + responseTypeWithNamespace: 'My.Project.MyDto', + })); + + expect(output).toContain("method: 'GET'"); + expect(output).not.toContain('responseType:'); + expect(output).not.toContain('headers:'); + }); + + test('json httpResponseType emits Accept but no responseType (default is json)', () => { + const output = render(buildContext({ + responseType: 'string', + responseTypeWithNamespace: 'string', + httpResponseType: 'json', + acceptHeader: 'application/json', + })); + + expect(output).toContain("headers: { Accept: 'application/json' }"); + expect(output).not.toContain('responseType:'); + }); + + test('text httpResponseType emits both responseType and Accept', () => { + const output = render(buildContext({ + responseType: 'string', + responseTypeWithNamespace: 'string', + httpResponseType: 'text', + acceptHeader: 'text/plain', + })); + + expect(output).toContain("responseType: 'text'"); + expect(output).toContain("headers: { Accept: 'text/plain' }"); + }); + + test('blob (IRemoteStreamContent) emits Blob return type + responseType + Accept', () => { + const output = render(buildContext({ + responseType: 'Volo.Abp.Content.IRemoteStreamContent', + responseTypeWithNamespace: 'Volo.Abp.Content.IRemoteStreamContent', + isBlobMethod: () => true, + httpResponseType: 'blob', + acceptHeader: 'application/octet-stream', + })); + + expect(output).toContain("responseType: 'blob'"); + expect(output).toContain('Blob>'); + expect(output).toContain("headers: { Accept: 'application/octet-stream' }"); + }); + + test('IRemoteStreamContent[] degradation renders any[] return type and does not reference IRemoteStreamContent', () => { + const output = render(buildContext({ + responseType: 'any[]', + responseTypeWithNamespace: '[Volo.Abp.Content.IRemoteStreamContent]', + isBlobMethod: () => false, + httpResponseType: undefined, + acceptHeader: undefined, + })); + + expect(output).toContain('any[]'); + expect(output).not.toContain('IRemoteStreamContent'); + expect(output).not.toContain("responseType: 'blob'"); + }); + + test('arraybuffer httpResponseType emits responseType', () => { + const output = render(buildContext({ + responseType: 'ArrayBuffer', + responseTypeWithNamespace: 'ArrayBuffer', + httpResponseType: 'arraybuffer', + acceptHeader: 'application/octet-stream', + })); + + expect(output).toContain("responseType: 'arraybuffer'"); + expect(output).toContain("headers: { Accept: 'application/octet-stream' }"); + }); + + test('no acceptHeader → no headers line', () => { + const output = render(buildContext({ + responseType: 'string', + responseTypeWithNamespace: 'string', + httpResponseType: 'text', + acceptHeader: undefined, + })); + + expect(output).toContain("responseType: 'text'"); + expect(output).not.toContain('headers:'); + }); + + test('rendered service code is valid TypeScript-shaped (closing braces / semicolons)', () => { + const output = render(buildContext({ + responseType: 'string', + responseTypeWithNamespace: 'string', + httpResponseType: 'json', + acceptHeader: 'application/json', + })); + + expect(output).toContain('@Injectable({'); + expect(output).toContain('providedIn: \'root\''); + expect(output).toContain('export class SampleService'); + expect(output).toContain('this.restService.request true } }, + { name: 'any[] degradation', body: { responseType: 'any[]', responseTypeWithNamespace: '[Volo.Abp.Content.IRemoteStreamContent]' } }, + { name: 'xml Accept only', body: { responseType: 'any', responseTypeWithNamespace: 'any', acceptHeader: 'application/xml' } }, + ])('rendered service compiles cleanly under real ts.Program ($name)', ({ body }) => { + const ts = require('typescript'); + const ctx = buildContext(body as Partial); + ctx.methods[0].signature.parameters = [{ name: 'config', type: 'Record' } as any]; + const output = render(ctx); + + const abpStub = ` + declare module '@abp/ng.core' { + export namespace Rest { + export interface Config { + apiName?: string; + observe?: any; + skipHandleError?: boolean; + responseType?: string; + [key: string]: any; + } + export type Observe = any; + } + export class RestService { + request(req: any, config?: any): import('rxjs').Observable; + } + } + `; + const domStub = 'declare class Blob { constructor(parts?: any[], options?: any); }\n'; + const angularCoreStub = ` + declare module '@angular/core' { + export function Injectable(opts?: any): ClassDecorator; + export function inject(token: { new (...args: any[]): T }): T; + export function inject(token: any): T; + } + `; + const rxjsStub = ` + declare module 'rxjs' { + export class Observable { subscribe(...args: any[]): unknown; } + } + `; + + const ambient = abpStub + angularCoreStub + rxjsStub + domStub; + const sources: Record = { + '/proxy/sample.service.ts': output, + '/proxy/ambient.d.ts': ambient, + }; + + const compilerOptions: any = { + target: ts.ScriptTarget.ES2020, + module: ts.ModuleKind.ES2020, + moduleResolution: ts.ModuleResolutionKind.NodeJs, + experimentalDecorators: true, + emitDecoratorMetadata: true, + strict: true, + noEmit: true, + skipLibCheck: true, + }; + + const baseHost = ts.createCompilerHost(compilerOptions, true); + const host: any = { + ...baseHost, + getSourceFile: (fileName: string, languageVersion: any, onError: any) => { + if (sources[fileName]) { + return ts.createSourceFile(fileName, sources[fileName], languageVersion, true); + } + return baseHost.getSourceFile(fileName, languageVersion, onError); + }, + fileExists: (fileName: string) => + sources[fileName] != null || baseHost.fileExists(fileName), + readFile: (fileName: string) => + sources[fileName] ?? baseHost.readFile(fileName), + }; + + const program = ts.createProgram(Object.keys(sources), compilerOptions, host); + const errors = ts + .getPreEmitDiagnostics(program) + .filter((d: any) => + d.category === ts.DiagnosticCategory.Error && + d.code !== 6053, + ); + + if (errors.length) { + const messages = errors + .map((d: any) => { + const where = d.file + ? (() => { + const p = d.file.getLineAndCharacterOfPosition(d.start ?? 0); + const lineText = d.file.text.split('\n')[p.line]; + return `${d.file.fileName}:${p.line + 1}:${p.character + 1}\n>>> ${lineText}\n>>> ${' '.repeat(p.character)}^`; + })() + : '(no file)'; + return `[${where}] TS${d.code}: ${ts.flattenDiagnosticMessageText(d.messageText, '\n')}`; + }) + .join('\n---\n'); + throw new Error(`Generated proxy did not compile:\n${output}\n=== diagnostics ===\n${messages}`); + } + expect(errors).toHaveLength(0); + }); + + test.each([ + { + name: 'DTO upload — single FormData arg', + signatureParams: [ + { name: 'input', type: 'FormData' }, + { name: 'config', type: 'Record' }, + ], + bodyOverrides: { method: 'POST', url: "'/api/test/upload-single'", body: 'input' }, + shouldContain: ['input: FormData', 'body: input'], + }, + { + name: 'direct upload — FormData arg with custom name', + signatureParams: [ + { name: 'file', type: 'FormData' }, + { name: 'config', type: 'Record' }, + ], + bodyOverrides: { method: 'POST', url: "'/api/test/upload-direct'", body: 'file' }, + shouldContain: ['file: FormData', 'body: file'], + }, + { + name: 'path + upload mixed — id stays in URL, FormData becomes body', + signatureParams: [ + { name: 'id', type: 'number' }, + { name: 'input', type: 'FormData' }, + { name: 'config', type: 'Record' }, + ], + bodyOverrides: { method: 'POST', url: '`/api/test/upload-with-path/${id}`', body: 'input' }, + shouldContain: ['id: number', 'input: FormData', 'body: input'], + }, + { + name: 'query + upload mixed — tag in params, FormData in body', + signatureParams: [ + { name: 'tag', type: 'string' }, + { name: 'input', type: 'FormData' }, + { name: 'config', type: 'Record' }, + ], + bodyOverrides: { + method: 'POST', + url: "'/api/test/upload-with-query'", + params: ['tag'], + body: 'input', + }, + shouldContain: ['tag: string', 'input: FormData', 'params: { tag }', 'body: input'], + }, + ])('upload action signature collapses to FormData ($name)', ({ signatureParams, bodyOverrides, shouldContain }) => { + const ts = require('typescript'); + const ctx = buildContext({ + responseType: 'string', + responseTypeWithNamespace: 'string', + ...bodyOverrides, + } as Partial); + ctx.methods[0].signature.parameters = signatureParams as any; + const output = render(ctx); + + for (const fragment of shouldContain) { + expect(output).toContain(fragment); + } + expect(output).not.toContain('JSON.stringify'); + + const abpStub = ` + declare module '@abp/ng.core' { + export namespace Rest { + export interface Config { + apiName?: string; + observe?: any; + skipHandleError?: boolean; + responseType?: string; + [key: string]: any; + } + export type Observe = any; + } + export class RestService { + request(req: any, config?: any): import('rxjs').Observable; + } + } + `; + const angularCoreStub = ` + declare module '@angular/core' { + export function Injectable(opts?: any): ClassDecorator; + export function inject(token: { new (...args: any[]): T }): T; + export function inject(token: any): T; + } + `; + const rxjsStub = ` + declare module 'rxjs' { + export class Observable { subscribe(...args: any[]): unknown; } + } + `; + const domStub = ` + declare class Blob { constructor(parts?: any[], options?: any); } + declare class FormData { + constructor(); + append(name: string, value: string | Blob, fileName?: string): void; + get(name: string): any; + } + `; + const ambient = abpStub + angularCoreStub + rxjsStub + domStub; + const sources: Record = { + '/proxy/sample.service.ts': output, + '/proxy/ambient.d.ts': ambient, + }; + const compilerOptions: any = { + target: ts.ScriptTarget.ES2020, + module: ts.ModuleKind.ES2020, + moduleResolution: ts.ModuleResolutionKind.NodeJs, + experimentalDecorators: true, + emitDecoratorMetadata: true, + strict: true, + noEmit: true, + skipLibCheck: true, + }; + const baseHost = ts.createCompilerHost(compilerOptions, true); + const host: any = { + ...baseHost, + getSourceFile: (fileName: string, languageVersion: any, onError: any) => + sources[fileName] + ? ts.createSourceFile(fileName, sources[fileName], languageVersion, true) + : baseHost.getSourceFile(fileName, languageVersion, onError), + fileExists: (fileName: string) => + sources[fileName] != null || baseHost.fileExists(fileName), + readFile: (fileName: string) => + sources[fileName] ?? baseHost.readFile(fileName), + }; + const program = ts.createProgram(Object.keys(sources), compilerOptions, host); + const errors = ts + .getPreEmitDiagnostics(program) + .filter((d: any) => d.category === ts.DiagnosticCategory.Error && d.code !== 6053); + if (errors.length) { + const messages = errors + .map((d: any) => { + const where = d.file + ? (() => { + const p = d.file.getLineAndCharacterOfPosition(d.start ?? 0); + const lineText = d.file.text.split('\n')[p.line]; + return `${d.file.fileName}:${p.line + 1}:${p.character + 1}\n>>> ${lineText}\n>>> ${' '.repeat(p.character)}^`; + })() + : '(no file)'; + return `[${where}] TS${d.code}: ${ts.flattenDiagnosticMessageText(d.messageText, '\n')}`; + }) + .join('\n---\n'); + throw new Error(`Upload action proxy did not compile:\n${output}\n=== diagnostics ===\n${messages}`); + } + expect(errors).toHaveLength(0); + }); + + test('rendered upload service forwards FormData to restService.request at runtime', () => { + const ts = require('typescript'); + const ctx = buildContext({ + method: 'POST', + url: "'/api/upload-runtime'", + responseType: 'string', + responseTypeWithNamespace: 'string', + body: 'input', + }); + ctx.methods[0].signature.parameters = [ + { name: 'input', type: 'FormData' }, + { name: 'config', type: 'Record' }, + ] as any; + const output = render(ctx); + + const stripped = output + .replace(/^import .*?;\s*$/gm, '') + .replace(/@Injectable\(\{[\s\S]*?\}\)\s*\n/g, '') + .replace(/private restService = inject\(RestService\);/, 'restService;') + .replace(/this\.restService\.request<[^>]+,\s*[^>]+>/g, 'this.restService.request'); + + const transpiled = ts.transpileModule(stripped, { + compilerOptions: { + target: ts.ScriptTarget.ES2020, + module: ts.ModuleKind.CommonJS, + experimentalDecorators: true, + }, + }).outputText; + + const restMockCalls: Array<{ body: any; method: string; url: string; headers?: any }> = []; + const restMock = { + request: (req: any /* , _config: any */) => { + restMockCalls.push(req); + return { subscribe: () => undefined }; + }, + }; + + const vm = require('vm'); + const sandbox: Record = { exports: {} }; + vm.createContext(sandbox); + vm.runInContext(transpiled + '\nexports.SampleService = SampleService;', sandbox); + const ServiceCls = sandbox.exports.SampleService; + const instance = new ServiceCls(); + instance.restService = restMock; + + const GlobalFormData = (globalThis as any).FormData; + const formData = typeof GlobalFormData === 'function' + ? new GlobalFormData() + : { __isFormData: true, append: () => undefined }; + instance.getSampleAsync(formData, { apiName: 'Default' }); + + expect(restMockCalls).toHaveLength(1); + expect(restMockCalls[0].body).toBe(formData); + expect(restMockCalls[0].method).toBe('POST'); + }); +}); diff --git a/npm/ng-packs/packages/schematics/src/utils/service.ts b/npm/ng-packs/packages/schematics/src/utils/service.ts index 8fe4aae91a..1c808c9d3b 100644 --- a/npm/ng-packs/packages/schematics/src/utils/service.ts +++ b/npm/ng-packs/packages/schematics/src/utils/service.ts @@ -96,15 +96,135 @@ export function createActionToBodyMapper() { responseType = adaptType(normalizedType); } } + if (isRemoteStreamContentArray(returnValue.typeSimple)) { + responseType = 'any[]'; + } const responseTypeWithNamespace = returnValue.typeSimple; - const body = new Body({ method: httpMethod, responseType, url, responseTypeWithNamespace }); + const { httpResponseType, acceptHeader } = resolveHttpResponseAndAccept( + responseType, + responseTypeWithNamespace, + returnValue.contentTypes, + returnValue.isRemoteStream, + ); + const body = new Body({ + method: httpMethod, + responseType, + url, + responseTypeWithNamespace, + httpResponseType, + acceptHeader, + }); - parameters.forEach(body.registerActionParameter); + const uploadMethodArgNames = new Set( + parameters + .filter(p => p.bindingSourceId === eBindingSourceId.FormFile) + .map(p => p.nameOnMethod), + ); + if (uploadMethodArgNames.size > 0) { + body.body = camelizeHyphen([...uploadMethodArgNames][0]); + parameters + .filter(p => { + if (uploadMethodArgNames.has(p.nameOnMethod)) { + return false; + } + return ( + p.bindingSourceId !== eBindingSourceId.Form && + p.bindingSourceId !== eBindingSourceId.FormFile + ); + }) + .forEach(body.registerActionParameter); + } else { + parameters.forEach(body.registerActionParameter); + } return body; }; } +function normalizeMediaType(mediaType: string): string { + const semi = mediaType.indexOf(';'); + return (semi < 0 ? mediaType : mediaType.slice(0, semi)).trim().toLowerCase(); +} + +function isJsonMediaType(normalized: string): boolean { + return normalized === 'application/json' || normalized === 'text/json' || normalized.endsWith('+json'); +} + +function isBinaryMediaType(mediaType: string): boolean { + const m = normalizeMediaType(mediaType); + if ( + m === 'application/octet-stream' || + m === 'application/pdf' || + m === 'application/zip' || + m === 'application/x-zip-compressed' || + m === 'application/gzip' || + m === 'application/x-tar' || + m === 'application/x-7z-compressed' || + m === 'application/wasm' || + m === 'application/x-msdownload' || + m === 'application/x-msdos-program' || + m === 'application/rtf' || + m === 'application/x-rar-compressed' || + m === 'application/x-rar' || + m === 'application/x-bzip2' || + m === 'application/x-iso9660-image' || + m === 'application/x-apple-diskimage' || + m === 'application/java-archive' || + m === 'application/epub+zip' || + m === 'model/gltf-binary' + ) { + return true; + } + if (m.startsWith('image/') || m.startsWith('video/') || m.startsWith('audio/') || m.startsWith('font/')) { + return true; + } + // Office / OpenDocument / generic vnd.* (excluding +json / +xml which are structured text) + if ( + m.startsWith('application/vnd.openxmlformats-') || + m.startsWith('application/vnd.ms-') || + m.startsWith('application/vnd.oasis.opendocument.') + ) { + return true; + } + if (m.startsWith('application/vnd.') && !m.endsWith('+json') && !m.endsWith('+xml')) { + return true; + } + return false; +} + +function resolveHttpResponseAndAccept( + responseType: string, + responseTypeWithNamespace: string, + contentTypes: string[] | undefined, + isRemoteStreamFlag: boolean | undefined, +): { httpResponseType?: 'json' | 'text' | 'blob' | 'arraybuffer'; acceptHeader?: string } { + if (isRemoteStreamFlag || isRemoteStreamContent(responseTypeWithNamespace)) { + return { httpResponseType: 'blob', acceptHeader: 'application/octet-stream' }; + } + + if (contentTypes && contentTypes.length > 0) { + const normalized = contentTypes.map(normalizeMediaType); + + const firstJsonShaped = normalized.find(isJsonMediaType); + if (firstJsonShaped) { + return { httpResponseType: 'json', acceptHeader: firstJsonShaped }; + } + if (normalized.every(ct => ct.startsWith('text/'))) { + return { httpResponseType: 'text', acceptHeader: normalized[0] }; + } + if (normalized.every(isBinaryMediaType)) { + return { httpResponseType: 'blob', acceptHeader: normalized[0] }; + } + return { acceptHeader: normalized[0] }; + } + + if (responseType === 'string') { + return { httpResponseType: 'text' }; + } + + return {}; +} + export function createActionToSignatureMapper() { const adaptType = createTypeAdapter(); @@ -118,7 +238,16 @@ export function createActionToSignatureMapper() { ...(versionParameter ? [versionParameter] : []), ]; + const uploadMethodArgNames = new Set( + (action.parameters ?? []) + .filter(p => p.bindingSourceId === eBindingSourceId.FormFile) + .map(p => p.nameOnMethod), + ); + signature.parameters = parameters.map(p => { + if (uploadMethodArgNames.has(p.name)) { + return new Property({ name: p.name, type: 'FormData' }); + } const isFormData = isRemoteStreamContent(p.type); const isFormArray = isRemoteStreamContentArray(p.type); if (isFormData || isFormArray) { @@ -146,13 +275,18 @@ export function isRemoteStreamContent(type: string) { } export function isRemoteStreamContentArray(type: string) { - // Check for array types like Volo.Abp.Content.IRemoteStreamContent[] if (VOLO_REMOTE_STREAM_CONTENT.map(x => `${x}[]`).some(x => x === type)) { return true; } - // Check for collection types like List, IEnumerable, ICollection, Collection, IList - // This matches any generic type from System.Collections.Generic that implements IEnumerable + // ABP serialises collections as `[T]` (see ApiTypeNameHelper.GetSimpleTypeName). + if (type.startsWith('[') && type.endsWith(']')) { + const inner = type.slice(1, -1); + if (VOLO_REMOTE_STREAM_CONTENT.includes(inner)) { + return true; + } + } + if (isCollectionType(type)) { const { generics } = extractGenerics(type); if (generics.length > 0 && VOLO_REMOTE_STREAM_CONTENT.includes(generics[0])) { diff --git a/npm/ng-packs/packages/setting-management/package.json b/npm/ng-packs/packages/setting-management/package.json index 9166b38a9b..134b30605c 100644 --- a/npm/ng-packs/packages/setting-management/package.json +++ b/npm/ng-packs/packages/setting-management/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.setting-management", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.components": "~10.5.0-rc.3", - "@abp/ng.theme.shared": "~10.5.0-rc.3", + "@abp/ng.components": "~10.5.0-rc.4", + "@abp/ng.theme.shared": "~10.5.0-rc.4", "tslib": "^2.0.0" }, "peerDependencies": { diff --git a/npm/ng-packs/packages/tenant-management/package.json b/npm/ng-packs/packages/tenant-management/package.json index 4e1b06c290..7a8067eb09 100644 --- a/npm/ng-packs/packages/tenant-management/package.json +++ b/npm/ng-packs/packages/tenant-management/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.tenant-management", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.feature-management": "~10.5.0-rc.3", - "@abp/ng.theme.shared": "~10.5.0-rc.3", + "@abp/ng.feature-management": "~10.5.0-rc.4", + "@abp/ng.theme.shared": "~10.5.0-rc.4", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/theme-basic/package.json b/npm/ng-packs/packages/theme-basic/package.json index 5eac6adb43..0b3280e954 100644 --- a/npm/ng-packs/packages/theme-basic/package.json +++ b/npm/ng-packs/packages/theme-basic/package.json @@ -1,14 +1,14 @@ { "name": "@abp/ng.theme.basic", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.account.core": "~10.5.0-rc.3", - "@abp/ng.theme.shared": "~10.5.0-rc.3", + "@abp/ng.account.core": "~10.5.0-rc.4", + "@abp/ng.theme.shared": "~10.5.0-rc.4", "tslib": "^2.0.0" }, "publishConfig": { diff --git a/npm/ng-packs/packages/theme-shared/package.json b/npm/ng-packs/packages/theme-shared/package.json index 9cf2894626..d909f06569 100644 --- a/npm/ng-packs/packages/theme-shared/package.json +++ b/npm/ng-packs/packages/theme-shared/package.json @@ -1,13 +1,13 @@ { "name": "@abp/ng.theme.shared", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "homepage": "https://abp.io", "repository": { "type": "git", "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.core": "~10.5.0-rc.3", + "@abp/ng.core": "~10.5.0-rc.4", "@fortawesome/fontawesome-free": "^6.0.0", "@ng-bootstrap/ng-bootstrap": "~20.0.0", "@ngx-validate/core": "^0.2.0", diff --git a/npm/packs/anchor-js/package.json b/npm/packs/anchor-js/package.json index 14c41d93eb..67529e4171 100644 --- a/npm/packs/anchor-js/package.json +++ b/npm/packs/anchor-js/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/anchor-js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "anchor-js": "^5.0.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/aspnetcore.components.server.basictheme/package.json b/npm/packs/aspnetcore.components.server.basictheme/package.json index 00637d758b..6352e20059 100644 --- a/npm/packs/aspnetcore.components.server.basictheme/package.json +++ b/npm/packs/aspnetcore.components.server.basictheme/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/aspnetcore.components.server.basictheme", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/aspnetcore.components.server.theming": "~10.5.0-rc.3" + "@abp/aspnetcore.components.server.theming": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.components.server.theming/package.json b/npm/packs/aspnetcore.components.server.theming/package.json index cbf24167df..863062439c 100644 --- a/npm/packs/aspnetcore.components.server.theming/package.json +++ b/npm/packs/aspnetcore.components.server.theming/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/aspnetcore.components.server.theming", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/bootstrap": "~10.5.0-rc.3", - "@abp/font-awesome": "~10.5.0-rc.3" + "@abp/bootstrap": "~10.5.0-rc.4", + "@abp/font-awesome": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json b/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json index ee5021df43..26266841f5 100644 --- a/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json +++ b/npm/packs/aspnetcore.mvc.ui.theme.basic/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/aspnetcore.mvc.ui.theme.basic", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json b/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json index 3b11314d4e..985d38c9a8 100644 --- a/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json +++ b/npm/packs/aspnetcore.mvc.ui.theme.shared/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/aspnetcore.mvc.ui.theme.shared", "repository": { "type": "git", @@ -10,20 +10,20 @@ "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui": "~10.5.0-rc.3", - "@abp/bootstrap": "~10.5.0-rc.3", - "@abp/bootstrap-datepicker": "~10.5.0-rc.3", - "@abp/bootstrap-daterangepicker": "~10.5.0-rc.3", - "@abp/datatables.net-bs5": "~10.5.0-rc.3", - "@abp/font-awesome": "~10.5.0-rc.3", - "@abp/jquery-validation-unobtrusive": "~10.5.0-rc.3", - "@abp/lodash": "~10.5.0-rc.3", - "@abp/luxon": "~10.5.0-rc.3", - "@abp/malihu-custom-scrollbar-plugin": "~10.5.0-rc.3", - "@abp/moment": "~10.5.0-rc.3", - "@abp/select2": "~10.5.0-rc.3", - "@abp/sweetalert2": "~10.5.0-rc.3", - "@abp/timeago": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui": "~10.5.0-rc.4", + "@abp/bootstrap": "~10.5.0-rc.4", + "@abp/bootstrap-datepicker": "~10.5.0-rc.4", + "@abp/bootstrap-daterangepicker": "~10.5.0-rc.4", + "@abp/datatables.net-bs5": "~10.5.0-rc.4", + "@abp/font-awesome": "~10.5.0-rc.4", + "@abp/jquery-validation-unobtrusive": "~10.5.0-rc.4", + "@abp/lodash": "~10.5.0-rc.4", + "@abp/luxon": "~10.5.0-rc.4", + "@abp/malihu-custom-scrollbar-plugin": "~10.5.0-rc.4", + "@abp/moment": "~10.5.0-rc.4", + "@abp/select2": "~10.5.0-rc.4", + "@abp/sweetalert2": "~10.5.0-rc.4", + "@abp/timeago": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/aspnetcore.mvc.ui/package-lock.json b/npm/packs/aspnetcore.mvc.ui/package-lock.json index d4e696b179..394d8eec2c 100644 --- a/npm/packs/aspnetcore.mvc.ui/package-lock.json +++ b/npm/packs/aspnetcore.mvc.ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "@abp/aspnetcore.mvc.ui", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "lockfileVersion": 1, "requires": true, "packages": { diff --git a/npm/packs/aspnetcore.mvc.ui/package.json b/npm/packs/aspnetcore.mvc.ui/package.json index 359428439f..cbb0dd79f6 100644 --- a/npm/packs/aspnetcore.mvc.ui/package.json +++ b/npm/packs/aspnetcore.mvc.ui/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/aspnetcore.mvc.ui", "repository": { "type": "git", diff --git a/npm/packs/blogging/package.json b/npm/packs/blogging/package.json index c68e6ffe22..3e4472d63b 100644 --- a/npm/packs/blogging/package.json +++ b/npm/packs/blogging/package.json @@ -1,14 +1,14 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/blogging", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.3", - "@abp/owl.carousel": "~10.5.0-rc.3", - "@abp/prismjs": "~10.5.0-rc.3", - "@abp/tui-editor": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.shared": "~10.5.0-rc.4", + "@abp/owl.carousel": "~10.5.0-rc.4", + "@abp/prismjs": "~10.5.0-rc.4", + "@abp/tui-editor": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/bootstrap-datepicker/package.json b/npm/packs/bootstrap-datepicker/package.json index ff12c51cfe..1e464c4d13 100644 --- a/npm/packs/bootstrap-datepicker/package.json +++ b/npm/packs/bootstrap-datepicker/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/bootstrap-datepicker", "repository": { "type": "git", diff --git a/npm/packs/bootstrap-daterangepicker/package.json b/npm/packs/bootstrap-daterangepicker/package.json index 2757b14687..cd8a2cd980 100644 --- a/npm/packs/bootstrap-daterangepicker/package.json +++ b/npm/packs/bootstrap-daterangepicker/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/bootstrap-daterangepicker", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/moment": "~10.5.0-rc.3", + "@abp/moment": "~10.5.0-rc.4", "bootstrap-daterangepicker": "^3.1.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/bootstrap/package.json b/npm/packs/bootstrap/package.json index 3fa5cb0400..d483018ad4 100644 --- a/npm/packs/bootstrap/package.json +++ b/npm/packs/bootstrap/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/bootstrap", "repository": { "type": "git", @@ -10,8 +10,8 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", - "@abp/popper.js": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", + "@abp/popper.js": "~10.5.0-rc.4", "bootstrap": "^5.3.8" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/chart.js/package.json b/npm/packs/chart.js/package.json index ee66b1be44..ac5561e752 100644 --- a/npm/packs/chart.js/package.json +++ b/npm/packs/chart.js/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/chart.js", "publishConfig": { "access": "public" diff --git a/npm/packs/clipboard/package.json b/npm/packs/clipboard/package.json index 5b6993cd53..8e08f7d04f 100644 --- a/npm/packs/clipboard/package.json +++ b/npm/packs/clipboard/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/clipboard", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "clipboard": "^2.0.11" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/cms-kit.admin/package.json b/npm/packs/cms-kit.admin/package.json index 7d57b03bfc..3bd43a7364 100644 --- a/npm/packs/cms-kit.admin/package.json +++ b/npm/packs/cms-kit.admin/package.json @@ -1,16 +1,16 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/cms-kit.admin", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/codemirror": "~10.5.0-rc.3", - "@abp/jstree": "~10.5.0-rc.3", - "@abp/markdown-it": "~10.5.0-rc.3", - "@abp/slugify": "~10.5.0-rc.3", - "@abp/tui-editor": "~10.5.0-rc.3", - "@abp/uppy": "~10.5.0-rc.3" + "@abp/codemirror": "~10.5.0-rc.4", + "@abp/jstree": "~10.5.0-rc.4", + "@abp/markdown-it": "~10.5.0-rc.4", + "@abp/slugify": "~10.5.0-rc.4", + "@abp/tui-editor": "~10.5.0-rc.4", + "@abp/uppy": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/cms-kit.public/package.json b/npm/packs/cms-kit.public/package.json index a4ed9b143a..0fc89b1647 100644 --- a/npm/packs/cms-kit.public/package.json +++ b/npm/packs/cms-kit.public/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/cms-kit.public", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/highlight.js": "~10.5.0-rc.3", - "@abp/star-rating-svg": "~10.5.0-rc.3" + "@abp/highlight.js": "~10.5.0-rc.4", + "@abp/star-rating-svg": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/cms-kit/package.json b/npm/packs/cms-kit/package.json index 7807ecfb76..a4577e7264 100644 --- a/npm/packs/cms-kit/package.json +++ b/npm/packs/cms-kit/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/cms-kit", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/cms-kit.admin": "~10.5.0-rc.3", - "@abp/cms-kit.public": "~10.5.0-rc.3" + "@abp/cms-kit.admin": "~10.5.0-rc.4", + "@abp/cms-kit.public": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/codemirror/package.json b/npm/packs/codemirror/package.json index 90dbcf8193..80a28ece2a 100644 --- a/npm/packs/codemirror/package.json +++ b/npm/packs/codemirror/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/codemirror", "publishConfig": { "access": "public" @@ -8,7 +8,7 @@ "build": "node ./scripts/build.js" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "@codemirror/lang-css": "^6.0.0", "@codemirror/lang-javascript": "^6.0.0", "@codemirror/state": "^6.0.0", diff --git a/npm/packs/core/package.json b/npm/packs/core/package.json index 78728f1f4e..acf428b30c 100644 --- a/npm/packs/core/package.json +++ b/npm/packs/core/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/core", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/utils": "~10.5.0-rc.3" + "@abp/utils": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/cropperjs/package.json b/npm/packs/cropperjs/package.json index 42ca25e3c7..6d990f12a2 100644 --- a/npm/packs/cropperjs/package.json +++ b/npm/packs/cropperjs/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/cropperjs", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "cropperjs": "^1.6.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/datatables.net-bs4/package.json b/npm/packs/datatables.net-bs4/package.json index d887f587ca..7e1ad9c642 100644 --- a/npm/packs/datatables.net-bs4/package.json +++ b/npm/packs/datatables.net-bs4/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/datatables.net-bs4", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/datatables.net": "~10.5.0-rc.3", + "@abp/datatables.net": "~10.5.0-rc.4", "datatables.net-bs4": "^2.3.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/datatables.net-bs5/package.json b/npm/packs/datatables.net-bs5/package.json index ce6cd4ef69..f4f9f07c72 100644 --- a/npm/packs/datatables.net-bs5/package.json +++ b/npm/packs/datatables.net-bs5/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/datatables.net-bs5", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/datatables.net": "~10.5.0-rc.3", + "@abp/datatables.net": "~10.5.0-rc.4", "datatables.net-bs5": "^2.3.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/datatables.net/package.json b/npm/packs/datatables.net/package.json index ada4be0e82..4690d34bdf 100644 --- a/npm/packs/datatables.net/package.json +++ b/npm/packs/datatables.net/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/datatables.net", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.3", + "@abp/jquery": "~10.5.0-rc.4", "datatables.net": "^2.3.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/docs/package.json b/npm/packs/docs/package.json index 009e459c1b..f018d3d86b 100644 --- a/npm/packs/docs/package.json +++ b/npm/packs/docs/package.json @@ -1,15 +1,15 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/docs", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/anchor-js": "~10.5.0-rc.3", - "@abp/clipboard": "~10.5.0-rc.3", - "@abp/malihu-custom-scrollbar-plugin": "~10.5.0-rc.3", - "@abp/popper.js": "~10.5.0-rc.3", - "@abp/prismjs": "~10.5.0-rc.3" + "@abp/anchor-js": "~10.5.0-rc.4", + "@abp/clipboard": "~10.5.0-rc.4", + "@abp/malihu-custom-scrollbar-plugin": "~10.5.0-rc.4", + "@abp/popper.js": "~10.5.0-rc.4", + "@abp/prismjs": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/flag-icon-css/package.json b/npm/packs/flag-icon-css/package.json index 26553ba07a..9f528afd87 100644 --- a/npm/packs/flag-icon-css/package.json +++ b/npm/packs/flag-icon-css/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/flag-icon-css", "publishConfig": { "access": "public" diff --git a/npm/packs/flag-icons/package.json b/npm/packs/flag-icons/package.json index ca33fe8cc9..fd5029a29b 100644 --- a/npm/packs/flag-icons/package.json +++ b/npm/packs/flag-icons/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/flag-icons", "publishConfig": { "access": "public" diff --git a/npm/packs/font-awesome/package.json b/npm/packs/font-awesome/package.json index f86a333be5..a66800a42d 100644 --- a/npm/packs/font-awesome/package.json +++ b/npm/packs/font-awesome/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/font-awesome", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "@fortawesome/fontawesome-free": "^7.0.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/highlight.js/package.json b/npm/packs/highlight.js/package.json index 226a5764c2..b0ecd9ec37 100644 --- a/npm/packs/highlight.js/package.json +++ b/npm/packs/highlight.js/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/highlight.js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "@highlightjs/cdn-assets": "~11.11.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery-form/package.json b/npm/packs/jquery-form/package.json index 34057c9667..f7696aa07d 100644 --- a/npm/packs/jquery-form/package.json +++ b/npm/packs/jquery-form/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/jquery-form", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.3", + "@abp/jquery": "~10.5.0-rc.4", "jquery-form": "^4.3.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery-validation-unobtrusive/package.json b/npm/packs/jquery-validation-unobtrusive/package.json index c6ab79e4fd..8017a0aa8a 100644 --- a/npm/packs/jquery-validation-unobtrusive/package.json +++ b/npm/packs/jquery-validation-unobtrusive/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/jquery-validation-unobtrusive", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery-validation": "~10.5.0-rc.3", + "@abp/jquery-validation": "~10.5.0-rc.4", "jquery-validation-unobtrusive": "^4.0.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery-validation/package.json b/npm/packs/jquery-validation/package.json index ae7e938b24..4faeb19de5 100644 --- a/npm/packs/jquery-validation/package.json +++ b/npm/packs/jquery-validation/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/jquery-validation", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.3", + "@abp/jquery": "~10.5.0-rc.4", "jquery-validation": "^1.21.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jquery/package.json b/npm/packs/jquery/package.json index 0cc8c01c75..8dc3b6c915 100644 --- a/npm/packs/jquery/package.json +++ b/npm/packs/jquery/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/jquery", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "jquery": "~3.7.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/jstree/package.json b/npm/packs/jstree/package.json index 66fb8c5a1f..3d7930d89e 100644 --- a/npm/packs/jstree/package.json +++ b/npm/packs/jstree/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/jstree", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.3", + "@abp/jquery": "~10.5.0-rc.4", "jstree": "^3.3.17" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/lodash/package.json b/npm/packs/lodash/package.json index d1a565c618..2f4246b215 100644 --- a/npm/packs/lodash/package.json +++ b/npm/packs/lodash/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/lodash", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "lodash": "^4.18.1" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/luxon/package.json b/npm/packs/luxon/package.json index 383c86c109..b889cc6221 100644 --- a/npm/packs/luxon/package.json +++ b/npm/packs/luxon/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/luxon", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "luxon": "^3.7.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/malihu-custom-scrollbar-plugin/package.json b/npm/packs/malihu-custom-scrollbar-plugin/package.json index 311750c55b..6b69f665ea 100644 --- a/npm/packs/malihu-custom-scrollbar-plugin/package.json +++ b/npm/packs/malihu-custom-scrollbar-plugin/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/malihu-custom-scrollbar-plugin", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "malihu-custom-scrollbar-plugin": "^3.1.5" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/markdown-it/package.json b/npm/packs/markdown-it/package.json index 1b5c8afe54..599481e3d5 100644 --- a/npm/packs/markdown-it/package.json +++ b/npm/packs/markdown-it/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/markdown-it", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "markdown-it": "^14.1.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/moment/package.json b/npm/packs/moment/package.json index 05186f6af0..2269ba5e70 100644 --- a/npm/packs/moment/package.json +++ b/npm/packs/moment/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/moment", "repository": { "type": "git", diff --git a/npm/packs/owl.carousel/package.json b/npm/packs/owl.carousel/package.json index b1124b4fe4..1ad15d35b9 100644 --- a/npm/packs/owl.carousel/package.json +++ b/npm/packs/owl.carousel/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/owl.carousel", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "owl.carousel": "^2.3.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/popper.js/package.json b/npm/packs/popper.js/package.json index d45a46d17a..44fbb64f71 100644 --- a/npm/packs/popper.js/package.json +++ b/npm/packs/popper.js/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/popper.js", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "@popperjs/core": "^2.11.8" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/prismjs/package.json b/npm/packs/prismjs/package.json index 04c041763b..b8a76a5dcd 100644 --- a/npm/packs/prismjs/package.json +++ b/npm/packs/prismjs/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/prismjs", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/clipboard": "~10.5.0-rc.3", - "@abp/core": "~10.5.0-rc.3", + "@abp/clipboard": "~10.5.0-rc.4", + "@abp/core": "~10.5.0-rc.4", "prismjs": "^1.30.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/qrcode/package.json b/npm/packs/qrcode/package.json index c4157c6a7e..af3a9f3cec 100644 --- a/npm/packs/qrcode/package.json +++ b/npm/packs/qrcode/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/qrcode", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3" + "@abp/core": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/select2/package.json b/npm/packs/select2/package.json index 5b4b979289..fd45b18212 100644 --- a/npm/packs/select2/package.json +++ b/npm/packs/select2/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/select2", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "select2": "^4.0.13" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/signalr/package.json b/npm/packs/signalr/package.json index e932121445..3c6c2e4c63 100644 --- a/npm/packs/signalr/package.json +++ b/npm/packs/signalr/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/signalr", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "@microsoft/signalr": "~9.0.6" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/slugify/package.json b/npm/packs/slugify/package.json index 981d48547d..967223bd90 100644 --- a/npm/packs/slugify/package.json +++ b/npm/packs/slugify/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/slugify", "publishConfig": { "access": "public" diff --git a/npm/packs/star-rating-svg/package.json b/npm/packs/star-rating-svg/package.json index 5dc84acd3f..feb1143115 100644 --- a/npm/packs/star-rating-svg/package.json +++ b/npm/packs/star-rating-svg/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/star-rating-svg", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.3", + "@abp/jquery": "~10.5.0-rc.4", "star-rating-svg": "^3.5.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/sweetalert2/package.json b/npm/packs/sweetalert2/package.json index f7d736b4f2..cfd432c329 100644 --- a/npm/packs/sweetalert2/package.json +++ b/npm/packs/sweetalert2/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/sweetalert2", "publishConfig": { "access": "public" @@ -10,7 +10,7 @@ "directory": "npm/packs/sweetalert2" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "sweetalert2": "^11.23.0" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/timeago/package.json b/npm/packs/timeago/package.json index 45d3b438f7..56e0f5d4a7 100644 --- a/npm/packs/timeago/package.json +++ b/npm/packs/timeago/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/timeago", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.3", + "@abp/jquery": "~10.5.0-rc.4", "timeago": "^1.6.7" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/toastr/package.json b/npm/packs/toastr/package.json index 7607765044..464a521889 100644 --- a/npm/packs/toastr/package.json +++ b/npm/packs/toastr/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/toastr", "repository": { "type": "git", @@ -10,7 +10,7 @@ "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.3", + "@abp/jquery": "~10.5.0-rc.4", "toastr": "^2.1.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/tui-editor/package.json b/npm/packs/tui-editor/package.json index 98bd47dacc..ff05857994 100644 --- a/npm/packs/tui-editor/package.json +++ b/npm/packs/tui-editor/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/tui-editor", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/jquery": "~10.5.0-rc.3", - "@abp/prismjs": "~10.5.0-rc.3" + "@abp/jquery": "~10.5.0-rc.4", + "@abp/prismjs": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/uppy/package.json b/npm/packs/uppy/package.json index 9cd28abe98..7d14ef304f 100644 --- a/npm/packs/uppy/package.json +++ b/npm/packs/uppy/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/uppy", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "uppy": "^5.1.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/utils/package.json b/npm/packs/utils/package.json index 8d691ee210..05454fd035 100644 --- a/npm/packs/utils/package.json +++ b/npm/packs/utils/package.json @@ -1,6 +1,6 @@ { "name": "@abp/utils", - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "scripts": { "prepublishOnly": "yarn install --ignore-scripts && node prepublish.js", "ng": "ng", diff --git a/npm/packs/vee-validate/package.json b/npm/packs/vee-validate/package.json index 097a23fef3..fadfdd7c2e 100644 --- a/npm/packs/vee-validate/package.json +++ b/npm/packs/vee-validate/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/vee-validate", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/vue": "~10.5.0-rc.3", + "@abp/vue": "~10.5.0-rc.4", "vee-validate": "~3.4.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/npm/packs/virtual-file-explorer/package.json b/npm/packs/virtual-file-explorer/package.json index c2425a5d46..f4dc9c5894 100644 --- a/npm/packs/virtual-file-explorer/package.json +++ b/npm/packs/virtual-file-explorer/package.json @@ -1,12 +1,12 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/virtual-file-explorer", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/clipboard": "~10.5.0-rc.3", - "@abp/prismjs": "~10.5.0-rc.3" + "@abp/clipboard": "~10.5.0-rc.4", + "@abp/prismjs": "~10.5.0-rc.4" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", "homepage": "https://abp.io", diff --git a/npm/packs/vue/package.json b/npm/packs/vue/package.json index b9c4328665..8526cf4d84 100644 --- a/npm/packs/vue/package.json +++ b/npm/packs/vue/package.json @@ -1,5 +1,5 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/vue", "publishConfig": { "access": "public" diff --git a/npm/packs/zxcvbn/package.json b/npm/packs/zxcvbn/package.json index 457cb6e177..3903b75cb6 100644 --- a/npm/packs/zxcvbn/package.json +++ b/npm/packs/zxcvbn/package.json @@ -1,11 +1,11 @@ { - "version": "10.5.0-rc.3", + "version": "10.5.0-rc.4", "name": "@abp/zxcvbn", "publishConfig": { "access": "public" }, "dependencies": { - "@abp/core": "~10.5.0-rc.3", + "@abp/core": "~10.5.0-rc.4", "zxcvbn": "^4.4.2" }, "gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", diff --git a/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip b/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip index fa9b0bd48f..8a9e4cb5ff 100644 Binary files a/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip and b/source-code/Volo.Abp.Account.SourceCode/Volo.Abp.Account.SourceCode.zip differ diff --git a/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip b/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip index bd293c24b0..d062887bf8 100644 Binary files a/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip and b/source-code/Volo.Abp.AuditLogging.SourceCode/Volo.Abp.AuditLogging.SourceCode.zip differ diff --git a/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip b/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip index c453c1e72d..61c13ab16d 100644 Binary files a/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip and b/source-code/Volo.Abp.BackgroundJobs.SourceCode/Volo.Abp.BackgroundJobs.SourceCode.zip differ diff --git a/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip b/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip index 0d42237c66..97546410fd 100644 Binary files a/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip and b/source-code/Volo.Abp.BlobStoring.Database.SourceCode/Volo.Abp.BlobStoring.Database.SourceCode.zip differ diff --git a/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip b/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip index c4996b8087..54e3f092e6 100644 Binary files a/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip and b/source-code/Volo.Abp.FeatureManagement.SourceCode/Volo.Abp.FeatureManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip b/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip index 5e9293a300..045005bcc5 100644 Binary files a/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip and b/source-code/Volo.Abp.Identity.SourceCode/Volo.Abp.Identity.SourceCode.zip differ diff --git a/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip b/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip index b3266e534b..7e61366da7 100644 Binary files a/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip and b/source-code/Volo.Abp.IdentityServer.SourceCode/Volo.Abp.IdentityServer.SourceCode.zip differ diff --git a/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip b/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip index 453c7a6202..865072066d 100644 Binary files a/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip and b/source-code/Volo.Abp.OpenIddict.SourceCode/Volo.Abp.OpenIddict.SourceCode.zip differ diff --git a/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip b/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip index 39a13f6a6d..04cffb8dad 100644 Binary files a/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip and b/source-code/Volo.Abp.PermissionManagement.SourceCode/Volo.Abp.PermissionManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip b/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip index a5757cedfb..3266b8601b 100644 Binary files a/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip and b/source-code/Volo.Abp.SettingManagement.SourceCode/Volo.Abp.SettingManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip b/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip index 3e440fe227..43877dd17c 100644 Binary files a/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip and b/source-code/Volo.Abp.TenantManagement.SourceCode/Volo.Abp.TenantManagement.SourceCode.zip differ diff --git a/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip b/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip index 09b67ef9de..b6c5e16d72 100644 Binary files a/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip and b/source-code/Volo.Abp.Users.SourceCode/Volo.Abp.Users.SourceCode.zip differ diff --git a/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip b/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip index 00c5d91b52..3e897de8bc 100644 Binary files a/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip and b/source-code/Volo.Abp.VirtualFileExplorer.SourceCode/Volo.Abp.VirtualFileExplorer.SourceCode.zip differ diff --git a/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip b/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip index 2111dc86db..58ce1124cf 100644 Binary files a/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip and b/source-code/Volo.Blogging.SourceCode/Volo.Blogging.SourceCode.zip differ diff --git a/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip b/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip index 49de9a94c6..3397cb6b24 100644 Binary files a/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip and b/source-code/Volo.CmsKit.SourceCode/Volo.CmsKit.SourceCode.zip differ diff --git a/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip b/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip index 8f65d7123b..8da20f0b89 100644 Binary files a/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip and b/source-code/Volo.Docs.SourceCode/Volo.Docs.SourceCode.zip differ diff --git a/templates/app-nolayers/angular/package.json b/templates/app-nolayers/angular/package.json index bf77fa3ddc..8180543a2a 100644 --- a/templates/app-nolayers/angular/package.json +++ b/templates/app-nolayers/angular/package.json @@ -12,15 +12,15 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~10.5.0-rc.3", - "@abp/ng.components": "~10.5.0-rc.3", - "@abp/ng.core": "~10.5.0-rc.3", - "@abp/ng.identity": "~10.5.0-rc.3", - "@abp/ng.oauth": "~10.5.0-rc.3", - "@abp/ng.setting-management": "~10.5.0-rc.3", - "@abp/ng.tenant-management": "~10.5.0-rc.3", - "@abp/ng.theme.lepton-x": "~5.5.0-rc.3", - "@abp/ng.theme.shared": "~10.5.0-rc.3", + "@abp/ng.account": "~10.5.0-rc.4", + "@abp/ng.components": "~10.5.0-rc.4", + "@abp/ng.core": "~10.5.0-rc.4", + "@abp/ng.identity": "~10.5.0-rc.4", + "@abp/ng.oauth": "~10.5.0-rc.4", + "@abp/ng.setting-management": "~10.5.0-rc.4", + "@abp/ng.tenant-management": "~10.5.0-rc.4", + "@abp/ng.theme.lepton-x": "~5.5.0-rc.4", + "@abp/ng.theme.shared": "~10.5.0-rc.4", "@angular/animations": "~22.0.1", "@angular/aria": "~22.0.0", "@angular/common": "~22.0.1", @@ -37,7 +37,7 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@abp/ng.schematics": "~10.5.0-rc.3", + "@abp/ng.schematics": "~10.5.0-rc.4", "@angular-eslint/builder": "~21.2.0", "@angular-eslint/eslint-plugin": "~21.2.0", "@angular-eslint/eslint-plugin-template": "~21.2.0", diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json index 810abfcc24..ef9079556d 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server.Mongo/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3", - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.4", + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json index ef9e246b0c..23277d57c4 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.Server/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.4" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json index fa15c8e0b3..2f78c870cc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server.Mongo/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json index fa15c8e0b3..2f78c870cc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Blazor.WebAssembly/Server/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json index fa15c8e0b3..2f78c870cc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host.Mongo/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json index fa15c8e0b3..2f78c870cc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json index fa15c8e0b3..2f78c870cc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc.Mongo/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4" } } diff --git a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json index fa15c8e0b3..2f78c870cc 100644 --- a/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json +++ b/templates/app-nolayers/aspnet-core/MyCompanyName.MyProjectName.Mvc/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4" } } diff --git a/templates/app/angular/package.json b/templates/app/angular/package.json index e4be98db02..172782629e 100644 --- a/templates/app/angular/package.json +++ b/templates/app/angular/package.json @@ -12,15 +12,15 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~10.5.0-rc.3", - "@abp/ng.components": "~10.5.0-rc.3", - "@abp/ng.core": "~10.5.0-rc.3", - "@abp/ng.identity": "~10.5.0-rc.3", - "@abp/ng.oauth": "~10.5.0-rc.3", - "@abp/ng.setting-management": "~10.5.0-rc.3", - "@abp/ng.tenant-management": "~10.5.0-rc.3", - "@abp/ng.theme.lepton-x": "~5.5.0-rc.3", - "@abp/ng.theme.shared": "~10.5.0-rc.3", + "@abp/ng.account": "~10.5.0-rc.4", + "@abp/ng.components": "~10.5.0-rc.4", + "@abp/ng.core": "~10.5.0-rc.4", + "@abp/ng.identity": "~10.5.0-rc.4", + "@abp/ng.oauth": "~10.5.0-rc.4", + "@abp/ng.setting-management": "~10.5.0-rc.4", + "@abp/ng.tenant-management": "~10.5.0-rc.4", + "@abp/ng.theme.lepton-x": "~5.5.0-rc.4", + "@abp/ng.theme.shared": "~10.5.0-rc.4", "@angular/animations": "~22.0.1", "@angular/aria": "~22.0.0", "@angular/common": "~22.0.1", @@ -37,7 +37,7 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@abp/ng.schematics": "~10.5.0-rc.3", + "@abp/ng.schematics": "~10.5.0-rc.4", "@angular-eslint/builder": "~21.2.0", "@angular-eslint/eslint-plugin": "~21.2.0", "@angular-eslint/eslint-plugin-template": "~21.2.0", diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json index 770f6e9be2..747427d820 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.AuthServer/package.json @@ -3,6 +3,6 @@ "name": "my-app-authserver", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json index ef9e246b0c..23277d57c4 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server.Tiered/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.4" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json index ef9e246b0c..23277d57c4 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.Server/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.4" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json index ef9e246b0c..23277d57c4 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp.Tiered/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.4" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json index ef9e246b0c..23277d57c4 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor.WebApp/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3", - "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4", + "@abp/aspnetcore.components.server.leptonxlitetheme": "~5.5.0-rc.4" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json index fa15c8e0b3..2f78c870cc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json index fa15c8e0b3..2f78c870cc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4" } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json index fa15c8e0b3..2f78c870cc 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.leptonxlite": "~5.5.0-rc.4" } } diff --git a/templates/module/angular/package.json b/templates/module/angular/package.json index 431b1d6686..071537835e 100644 --- a/templates/module/angular/package.json +++ b/templates/module/angular/package.json @@ -13,15 +13,15 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "~10.5.0-rc.3", - "@abp/ng.components": "~10.5.0-rc.3", - "@abp/ng.core": "~10.5.0-rc.3", - "@abp/ng.identity": "~10.5.0-rc.3", - "@abp/ng.oauth": "~10.5.0-rc.3", - "@abp/ng.setting-management": "~10.5.0-rc.3", - "@abp/ng.tenant-management": "~10.5.0-rc.3", - "@abp/ng.theme.basic": "~10.5.0-rc.3", - "@abp/ng.theme.shared": "~10.5.0-rc.3", + "@abp/ng.account": "~10.5.0-rc.4", + "@abp/ng.components": "~10.5.0-rc.4", + "@abp/ng.core": "~10.5.0-rc.4", + "@abp/ng.identity": "~10.5.0-rc.4", + "@abp/ng.oauth": "~10.5.0-rc.4", + "@abp/ng.setting-management": "~10.5.0-rc.4", + "@abp/ng.tenant-management": "~10.5.0-rc.4", + "@abp/ng.theme.basic": "~10.5.0-rc.4", + "@abp/ng.theme.shared": "~10.5.0-rc.4", "@angular/animations": "~22.0.0", "@angular/aria": "~22.0.0", "@angular/common": "~22.0.0", @@ -37,16 +37,16 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@abp/ng.schematics": "~10.5.0-rc.3", - "@angular-eslint/builder": "~22.0.0", - "@angular-eslint/eslint-plugin": "~22.0.0", - "@angular-eslint/eslint-plugin-template": "~22.0.0", - "@angular-eslint/schematics": "~22.0.0", - "@angular-eslint/template-parser": "~22.0.0", - "@angular/build": "~22.0.0", - "@angular/cli": "~22.0.0", - "@angular/compiler-cli": "~22.0.0", - "@angular/language-service": "~22.0.0", + "@abp/ng.schematics": "~10.5.0-rc.4", + "@angular-eslint/builder": "~21.2.0", + "@angular-eslint/eslint-plugin": "~21.2.0", + "@angular-eslint/eslint-plugin-template": "~21.2.0", + "@angular-eslint/schematics": "~21.2.0", + "@angular-eslint/template-parser": "~21.2.0", + "@angular/build": "~21.2.0", + "@angular/cli": "~21.2.0", + "@angular/compiler-cli": "~21.2.0", + "@angular/language-service": "~21.2.0", "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "7.16.0", "@typescript-eslint/parser": "7.16.0", diff --git a/templates/module/angular/projects/my-project-name/package.json b/templates/module/angular/projects/my-project-name/package.json index db7e763e6a..40431fb776 100644 --- a/templates/module/angular/projects/my-project-name/package.json +++ b/templates/module/angular/projects/my-project-name/package.json @@ -4,8 +4,8 @@ "peerDependencies": { "@angular/common": "~21.2.0", "@angular/core": "~21.2.0", - "@abp/ng.core": "~10.5.0-rc.3", - "@abp/ng.theme.shared": "~10.5.0-rc.3" + "@abp/ng.core": "~10.5.0-rc.4", + "@abp/ng.theme.shared": "~10.5.0-rc.4" }, "dependencies": { "tslib": "^2.1.0" diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json index 6d67129b14..6edec00b4b 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.AuthServer/package.json @@ -3,6 +3,6 @@ "name": "my-app-authserver", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.4" } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json index 05ada78062..c3c70e1e2f 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Blazor.Server.Host/package.json @@ -3,7 +3,7 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3", - "@abp/aspnetcore.components.server.basictheme": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.4", + "@abp/aspnetcore.components.server.basictheme": "~10.5.0-rc.4" } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json index f87780dd17..9c85db4a27 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Host/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.4" } } diff --git a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json index f87780dd17..9c85db4a27 100644 --- a/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json +++ b/templates/module/aspnet-core/host/MyCompanyName.MyProjectName.Web.Unified/package.json @@ -3,6 +3,6 @@ "name": "my-app", "private": true, "dependencies": { - "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.3" + "@abp/aspnetcore.mvc.ui.theme.basic": "~10.5.0-rc.4" } }