From ffe60eaf33e90296fe3e85d8d9882e70ea380798 Mon Sep 17 00:00:00 2001 From: bhuvaneshdhakshinmaoorthy Date: Wed, 30 Sep 2026 10:27:18 +0530 Subject: [PATCH 1/2] 1055046: Prepared samples for client and server side for collaborative editing --- client/react/README.md | 179 ++++++++++++++++++ client/react/index.html | 15 ++ client/react/package.json | 29 +++ client/react/src/App.css | 1 + client/react/src/App.tsx | 130 +++++++++++++ client/react/src/SpreadsheetEditorAdapter.ts | 106 +++++++++++ client/react/src/main.tsx | 11 ++ client/react/tsconfig.app.json | 27 +++ client/react/tsconfig.json | 11 ++ client/react/tsconfig.node.json | 24 +++ client/react/vite.config.ts | 6 + license | 10 + .../SpreadsheetCollaborativeAdaptor.cs | 0 .../CollaborativeEditingController.cs | 0 .../Models/SpreadsheetSelectionInfo.cs | 0 server/aspnet-core-redis/Program.cs | 112 +++++++++++ .../Properties/launchSettings.json | 45 +++++ ...eadsheetCollaborativeEditing.Server.csproj | 0 .../appsettings.Development.json | 10 + server/aspnet-core-redis/appsettings.json | 19 ++ .../wwwroot/Files/Sample.xlsx | Bin 0 -> 8649 bytes 21 files changed, 735 insertions(+) create mode 100644 client/react/README.md create mode 100644 client/react/index.html create mode 100644 client/react/package.json create mode 100644 client/react/src/App.css create mode 100644 client/react/src/App.tsx create mode 100644 client/react/src/SpreadsheetEditorAdapter.ts create mode 100644 client/react/src/main.tsx create mode 100644 client/react/tsconfig.app.json create mode 100644 client/react/tsconfig.json create mode 100644 client/react/tsconfig.node.json create mode 100644 client/react/vite.config.ts create mode 100644 license create mode 100644 server/aspnet-core-redis/Adapters/SpreadsheetCollaborativeAdaptor.cs create mode 100644 server/aspnet-core-redis/Controllers/CollaborativeEditingController.cs create mode 100644 server/aspnet-core-redis/Models/SpreadsheetSelectionInfo.cs create mode 100644 server/aspnet-core-redis/Program.cs create mode 100644 server/aspnet-core-redis/Properties/launchSettings.json create mode 100644 server/aspnet-core-redis/SpreadsheetCollaborativeEditing.Server.csproj create mode 100644 server/aspnet-core-redis/appsettings.Development.json create mode 100644 server/aspnet-core-redis/appsettings.json create mode 100644 server/aspnet-core-redis/wwwroot/Files/Sample.xlsx diff --git a/client/react/README.md b/client/react/README.md new file mode 100644 index 0000000..cc6a6e0 --- /dev/null +++ b/client/react/README.md @@ -0,0 +1,179 @@ +# React Spreadsheet Collaborative Editing + +This example demonstrates real-time collaborative editing in the Syncfusion React Spreadsheet. Multiple users can edit the same workbook simultaneously, and the changes are synchronized through the Collaboration Server. + +## Prerequisites + +Before running the React client, ensure that the following software and services are available: + +- Node.js +- Redis Server +- ASP.NET Core Redis Collaboration Server + +The Collaboration Server and Redis Server must be running before starting the React application. + +## Installing dependencies + +Navigate to the React client folder: + +```bash +cd client/react +``` + +Install the required dependencies: + +```bash +npm install +``` + +The `@syncfusion/ej2-collaborator` package is listed separately because the React Spreadsheet package does not install the collaboration client automatically. + +The `@microsoft/signalr` package is required by the collaboration client for SignalR communication with the ASP.NET Core Collaboration Server. + +## Configuring the Collaboration Server URL + +Open `src/App.tsx` and update the following URL when required: + +```ts +const serviceUrl: string = + 'https://appservice-267100-e9c8cxaab6a2dfeu.centralindia-01.azurewebsites.net/'; +``` + +The URL must point to the running ASP.NET Core Collaboration Server. + +## Running the application + +Start the React development server: + +```bash +npm run dev +``` + +Open the URL displayed by Vite. The default URL is: + +```text +http://localhost:5173/ +``` + +## Testing collaborative editing + +When the application is opened without an `id` query parameter, the React client automatically generates a room name and adds it to the URL. + +For example: + +```text +http://localhost:5173/?id=room-name +``` + +Copy the complete URL and open it in another browser window or tab. Clients using the same room ID collaborate on the same workbook. + +The sample uses the following participant name: + +```ts +const currentUser: string = 'John'; +``` + +## How the sample works + +1. The Spreadsheet is initialized with collaborative editing enabled. +2. The `CollaborativeEditingHandler` service is injected into the Spreadsheet. +3. The client obtains or generates a room name from the URL. +4. `SpreadsheetEditorAdapter.loadFromServer` imports the latest workbook and room version from the server. +5. `CollaborationClient` connects to the server using SignalR. +6. The client joins the collaboration room by calling `joinRoomAsync`. +7. Local Spreadsheet actions are sent to the server from the `actionComplete` event. +8. Remote actions received through the collaboration client are applied to the Spreadsheet. + +## Theme configuration + +The Syncfusion Tailwind 3 theme is imported in `src/App.css`: + +```css +@import "@syncfusion/ej2-tailwind3-theme/styles/tailwind3.css"; +``` + +The stylesheet is loaded in `src/App.tsx`: + +```ts +import './App.css'; +``` + +Do not remove the `App.css` import. Without the theme stylesheet, the Spreadsheet output will not render correctly. + +## Project structure + +```text +client/react/ +|-- src/ +| |-- App.css +| |-- App.tsx +| |-- main.tsx +| `-- SpreadsheetEditorAdapter.ts +|-- index.html +|-- package.json +|-- README.md +|-- tsconfig.app.json +|-- tsconfig.json +|-- tsconfig.node.json +`-- vite.config.ts +``` + +## Production build + +Create a production build by running: + +```bash +npm run build +``` + +Preview the production build locally: + +```bash +npm run preview +``` + +## Troubleshooting + +### The collaborator package cannot be resolved + +Ensure `@syncfusion/ej2-collaborator` is present in `package.json`, and then run: + +```bash +npm install +``` + +### The SignalR package cannot be resolved + +Ensure `@microsoft/signalr` is present in `package.json`, and then run: + +```bash +npm install +``` + +### The Spreadsheet output is not styled correctly + +Confirm that `src/App.tsx` contains: + +```ts +import './App.css'; +``` + +Also confirm that `src/App.css` contains: + +```css +@import "@syncfusion/ej2-tailwind3-theme/styles/tailwind3.css"; +``` + +### Clients do not receive synchronized changes + +Verify the following items: + +- Redis Server is running. +- The ASP.NET Core Collaboration Server is running. +- The `serviceUrl` value points to the correct server. +- All clients use the same room ID in the URL. +- The browser console does not contain SignalR connection or CORS errors. + +## License + +This project is licensed under the terms provided in the repository license file. diff --git a/client/react/index.html b/client/react/index.html new file mode 100644 index 0000000..ec86aff --- /dev/null +++ b/client/react/index.html @@ -0,0 +1,15 @@ + + + + + + + React Spreadsheet Collaborative Editing + + + +
+ + + + \ No newline at end of file diff --git a/client/react/package.json b/client/react/package.json new file mode 100644 index 0000000..0cb6355 --- /dev/null +++ b/client/react/package.json @@ -0,0 +1,29 @@ +{ + "name": "react-spreadsheet-collaborative-editing", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "oxlint", + "preview": "vite preview" + }, + "dependencies": { + "@microsoft/signalr": "^9.0.0", + "@syncfusion/ej2-collaborator": "35.1.37", + "@syncfusion/ej2-react-spreadsheet": "35.1.37", + "@syncfusion/ej2-tailwind3-theme": "35.1.37", + "react": "^19.2.8", + "react-dom": "^19.2.8" + }, + "devDependencies": { + "@types/node": "^24.13.3", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.7", + "@vitejs/plugin-react": "^6.1.1", + "oxlint": "^1.81.0", + "typescript": "~6.0.2", + "vite": "^8.3.0" + } +} diff --git a/client/react/src/App.css b/client/react/src/App.css new file mode 100644 index 0000000..5b40715 --- /dev/null +++ b/client/react/src/App.css @@ -0,0 +1 @@ +@import "@syncfusion/ej2-tailwind3-theme/styles/tailwind3.css"; diff --git a/client/react/src/App.tsx b/client/react/src/App.tsx new file mode 100644 index 0000000..e32f1e6 --- /dev/null +++ b/client/react/src/App.tsx @@ -0,0 +1,130 @@ +import { useRef } from 'react'; +import { + CollaborativeEditingHandler, + Inject, + SpreadsheetComponent +} from '@syncfusion/ej2-react-spreadsheet'; +import { CollaborationClient } from '@syncfusion/ej2-collaborator'; +import { SpreadsheetEditorAdapter } from './SpreadsheetEditorAdapter'; +import './App.css'; + +const serviceUrl: string = + 'https://appservice-267100-e9c8cxaab6a2dfeu.centralindia-01.azurewebsites.net/'; + +const currentUser: string = 'John'; + +function getRoomName(): string { + const currentUrl: URL = new URL(window.location.href); + + let roomName: string = ( + currentUrl.searchParams.get('id') || '' + ).trim(); + + if (!roomName) { + roomName = Math.random() + .toString(32) + .slice(2); + + currentUrl.searchParams.set( + 'id', + roomName + ); + + window.history.replaceState( + window.history.state, + '', + currentUrl.pathname + + currentUrl.search + + currentUrl.hash + ); + } + + return roomName; +} + +export default function App() { + const spreadsheetRef = + useRef(null); + + const adapterRef = + useRef(null); + + const clientRef = + useRef(null); + + const initializedRef = + useRef(false); + + const created = async (): Promise => { + const spreadsheet = spreadsheetRef.current; + + if (!spreadsheet || initializedRef.current) { + return; + } + + initializedRef.current = true; + + const roomName: string = getRoomName(); + + const adapter = new SpreadsheetEditorAdapter( + spreadsheet, + serviceUrl, + currentUser + ); + + try { + // Load the latest workbook and room version. + await adapter.loadFromServer( + 'Sample', + roomName + ); + + const client = new CollaborationClient( + adapter, + { + serviceUrl, + connectionType: 'signalr', + currentUser + } + ); + + adapterRef.current = adapter; + clientRef.current = client; + + // Connect to the real-time collaboration room. + await client.joinRoomAsync(roomName); + + console.log( + '[Collaborative Editing] Joined room', + roomName + ); + } catch (error) { + initializedRef.current = false; + + console.error( + '[Collaborative Editing] Failed to join the room.', + error + ); + } + }; + + const actionComplete = (args: unknown): void => { + adapterRef.current?.sendActionToServer(args); + }; + + return ( + + + + ); +} diff --git a/client/react/src/SpreadsheetEditorAdapter.ts b/client/react/src/SpreadsheetEditorAdapter.ts new file mode 100644 index 0000000..4452023 --- /dev/null +++ b/client/react/src/SpreadsheetEditorAdapter.ts @@ -0,0 +1,106 @@ + +import type { + ICollaborationActionData, + ICollaborationProvider +} from '@syncfusion/ej2-collaborator'; +import { SpreadsheetComponent } from '@syncfusion/ej2-react-spreadsheet'; + +interface ImportFileResponse { + sfdt: string; + version: number; +} + +export class SpreadsheetEditorAdapter + implements ICollaborationProvider { + + public currentRoomName: string = ''; + + public constructor( + private spreadsheet: SpreadsheetComponent, + private serviceUrl: string, + private currentUser: string + ) { + this.serviceUrl = serviceUrl.endsWith('/') + ? serviceUrl + : serviceUrl + '/'; + } + + public async loadFromServer( + fileName: string, + roomName: string + ): Promise { + const response: Response = await fetch( + this.serviceUrl + + 'api/CollaborativeEditing/ImportFile', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + fileName, + roomName + }) + } + ); + + if (!response.ok) { + throw new Error( + 'Failed to load the workbook.' + ); + } + + const data: ImportFileResponse = + JSON.parse(await response.text()); + + this.currentRoomName = roomName; + + // Initialize the room, server version, and API URL. + this.spreadsheet + .collaborativeEditingModule + .updateRoomInfo( + roomName, + data.version, + this.serviceUrl + + 'api/CollaborativeEditing/' + ); + + // Set the name displayed for the local participant. + this.spreadsheet + .collaborativeEditingModule + .setLocalUser(this.currentUser); + + // Open the latest synchronized workbook state. + this.spreadsheet.openFromJson({ + file: data.sfdt + }); + } + + public sendActionToServer( + action: unknown + ): void { + if (!action) { + return; + } + + this.spreadsheet + .collaborativeEditingModule + .sendActionToServer(action); + } + + public applyRemoteAction( + action: string, + data: ICollaborationActionData + ): void { + if (!data) { + return; + } + + this.spreadsheet + .collaborativeEditingModule + .applyRemoteAction( + action, + data.payload + ); + } +} diff --git a/client/react/src/main.tsx b/client/react/src/main.tsx new file mode 100644 index 0000000..71ffa42 --- /dev/null +++ b/client/react/src/main.tsx @@ -0,0 +1,11 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import App from './App'; + +createRoot( + document.getElementById('root')! +).render( + + + +); diff --git a/client/react/tsconfig.app.json b/client/react/tsconfig.app.json new file mode 100644 index 0000000..7af66d0 --- /dev/null +++ b/client/react/tsconfig.app.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": [ + "ES2022", + "DOM", + "DOM.Iterable" + ], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": [ + "src" + ] +} diff --git a/client/react/tsconfig.json b/client/react/tsconfig.json new file mode 100644 index 0000000..65f670c --- /dev/null +++ b/client/react/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.node.json" + } + ] +} \ No newline at end of file diff --git a/client/react/tsconfig.node.json b/client/react/tsconfig.node.json new file mode 100644 index 0000000..d1e6e2b --- /dev/null +++ b/client/react/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": [ + "ES2023" + ], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": [ + "vite.config.ts" + ] +} diff --git a/client/react/vite.config.ts b/client/react/vite.config.ts new file mode 100644 index 0000000..5d9373f --- /dev/null +++ b/client/react/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()] +}); \ No newline at end of file diff --git a/license b/license new file mode 100644 index 0000000..4e57cc0 --- /dev/null +++ b/license @@ -0,0 +1,10 @@ +Essential JS 2 library is available under the Syncfusion Essential Studio program, and can be licensed either under the Syncfusion Community License Program or the Syncfusion commercial license. + +To be qualified for the Syncfusion Community License Program you must have a gross revenue of less than one (1) million U.S. dollars ($1,000,000.00 USD) per year and have less than five (5) developers in your organization, and agree to be bound by Syncfusion’s terms and conditions. + +Customers who do not qualify for the community license can contact sales@syncfusion.com for commercial licensing options. + +Under no circumstances can you use this product without (1) either a Community License or a commercial license and (2) without agreeing and abiding by Syncfusion’s license containing all terms and conditions. + +The Syncfusion license that contains the terms and conditions can be found at +https://www.syncfusion.com/content/downloads/syncfusion_license.pdf \ No newline at end of file diff --git a/server/aspnet-core-redis/Adapters/SpreadsheetCollaborativeAdaptor.cs b/server/aspnet-core-redis/Adapters/SpreadsheetCollaborativeAdaptor.cs new file mode 100644 index 0000000..e69de29 diff --git a/server/aspnet-core-redis/Controllers/CollaborativeEditingController.cs b/server/aspnet-core-redis/Controllers/CollaborativeEditingController.cs new file mode 100644 index 0000000..e69de29 diff --git a/server/aspnet-core-redis/Models/SpreadsheetSelectionInfo.cs b/server/aspnet-core-redis/Models/SpreadsheetSelectionInfo.cs new file mode 100644 index 0000000..e69de29 diff --git a/server/aspnet-core-redis/Program.cs b/server/aspnet-core-redis/Program.cs new file mode 100644 index 0000000..7356400 --- /dev/null +++ b/server/aspnet-core-redis/Program.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.ResponseCompression; +using Microsoft.AspNetCore.Http.Features; +using Newtonsoft.Json.Serialization; +using Syncfusion.Licensing; +using Microsoft.Extensions.Hosting; +using Syncfusion.Collaboration.Core.Extensions; +using Syncfusion.Collaboration.Core.Interfaces; + +var builder = WebApplication.CreateBuilder(args); + +// Configuration Setup +var env = builder.Environment; +builder.Configuration.SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables(); + +// Syncfusion License Registration +string licenseKey = string.Empty; +string licenseFilePath = Path.Combine(Directory.GetCurrentDirectory(), "SyncfusionLicense.txt"); +if (File.Exists(licenseFilePath)) +{ + // Assigning Syncfusion LICENSE_KEY from local file + licenseKey = File.ReadAllText(licenseFilePath).Trim(); +} +if (string.IsNullOrEmpty(licenseKey)) +{ + // Access LICENSE_KEY from environment variables + licenseKey = builder.Configuration["SYNCFUSION_LICENSE_KEY"]; +} + +SyncfusionLicenseProvider.RegisterLicense(licenseKey); + +builder.Services.AddCollaborationServer(options => +{ + options.ConnectionString = builder.Configuration.GetConnectionString("Redis"); + options.ConnectionType = CollaborationConnectionType.SignalR; +}); +builder.Services.AddSingleton(); +builder.Services.AddSignalR(); + +// Configure services +var MyAllowSpecificOrigins = "AllowAllOrigins"; +builder.Services.AddCors(options => +{ + options.AddPolicy(name: MyAllowSpecificOrigins, + builder => { + builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + }); +}); + +builder.Services.Configure(options => +{ + options.MultipartBodyLengthLimit = int.MaxValue; + options.ValueLengthLimit = int.MaxValue; +}); + +builder.WebHost.ConfigureKestrel(options => +{ + options.Limits.MaxRequestBodySize = int.MaxValue; +}); + +builder.Services.AddMemoryCache(); +builder.Services.AddMvc(endPoint => endPoint.EnableEndpointRouting = false); +builder.Services.AddEndpointsApiExplorer(); + +builder.Services.Configure(options => +{ + options.Level = System.IO.Compression.CompressionLevel.Optimal; +}); +builder.Services.AddResponseCompression(); + +builder.Services.AddControllers().AddNewtonsoftJson(options => +{ + options.SerializerSettings.ContractResolver = new DefaultContractResolver(); +}); + +// Build app +var app = builder.Build(); + +// Middleware +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error"); + app.UseHsts(); +} +else +{ + app.UseDeveloperExceptionPage(); +} + +app.UseHttpsRedirection(); +app.UseRouting(); +app.UseCors(MyAllowSpecificOrigins); +app.UseAuthorization(); +app.UseResponseCompression(); +app.MapControllers(); +app.MapCollaborationServer(); +app.Run(); diff --git a/server/aspnet-core-redis/Properties/launchSettings.json b/server/aspnet-core-redis/Properties/launchSettings.json new file mode 100644 index 0000000..bb27f4d --- /dev/null +++ b/server/aspnet-core-redis/Properties/launchSettings.json @@ -0,0 +1,45 @@ +{ + "profiles": { + "EJ2SpreadsheetServer": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "api/test", + "environmentVariables": { + "SYNCFUSION_LICENSE_KEY": "", + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7002;http://localhost:5209" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "api/test", + "environmentVariables": { + "SYNCFUSION_LICENSE_KEY": "", + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Container (Dockerfile)": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/api/test", + "environmentVariables": { + "SYNCFUSION_LICENSE_KEY": "", + "ASPNETCORE_URLS": "https://+:443;http://+:80", + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "publishAllPorts": true, + "useSSL": true + } + }, + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:37531", + "sslPort": 44318 + } + } +} \ No newline at end of file diff --git a/server/aspnet-core-redis/SpreadsheetCollaborativeEditing.Server.csproj b/server/aspnet-core-redis/SpreadsheetCollaborativeEditing.Server.csproj new file mode 100644 index 0000000..e69de29 diff --git a/server/aspnet-core-redis/appsettings.Development.json b/server/aspnet-core-redis/appsettings.Development.json new file mode 100644 index 0000000..723c096 --- /dev/null +++ b/server/aspnet-core-redis/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/server/aspnet-core-redis/appsettings.json b/server/aspnet-core-redis/appsettings.json new file mode 100644 index 0000000..00d7350 --- /dev/null +++ b/server/aspnet-core-redis/appsettings.json @@ -0,0 +1,19 @@ +{ + "SYNCFUSION_LICENSE_KEY": "", + "ConnectionStrings": { + "Redis": "redis-276188.redis.cache.windows.net:6380,password=pRIeJHlNcxzuD1Yz1ClZ00Ol3X4H2YdkmAzCaBuDaQY=,ssl=True,abortConnect=False" + }, + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Warning" + } + }, + "Console": { + "LogLevel": { + "Default": "Warning" + } + } + } +} diff --git a/server/aspnet-core-redis/wwwroot/Files/Sample.xlsx b/server/aspnet-core-redis/wwwroot/Files/Sample.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a4b2c7ce0d7067d0191feefd0aa0a8c63908e23a GIT binary patch literal 8649 zcmd5>XIN9|whk>6L0V{Hp(L1uPN+6Y2&O^`AR;OR2oRALEI4BZLeT`l5NZMnNDT-Q z5Cn!Z0tTfdAc!M6I)jK<=Zu0lwz=nS#&gFRuIG8Y_uL;Bo@8h5oo{7-YklAQuJtZ5 zK}i_`0)f;(aDmHSoe4FUd<77wQV9fFxq39-o)sIB5*(=>`q#mWie0V6WPr68m&?nWoM{Za?Q&W-|?kJ;Fe4vpK(ttCNDu2d>E z$4OoUL8h=4c?XM~@OX{$uaJo5gYB4uOpTJvQ%$-`!UBjd5(|9N< zjgZ26m8YpGd&Sa6EfqbF=b?U%$3Ndjpt1};=&@)AUlE?izw+joh0z}gLOAAz0Al)hEtA0evg@bmGz+!8}L_Jd$(1=J#9Ob_fC6Ax}6<&n!8(ib{g-s z>o&XGSZn-kg(*BR=?Otbz{wE7WP;4LEjVdxB)|q|JLquFc5{c95p)lTW>QhOo1Bh` zPTI$$yBfZ%n(FJ5^`$;v`)VYBc;(!+9yrQlYp3$JXLat)o|2u@(`>LZY^a49HdLL^ z4s_R|ZL@{xUg6X4`c;{d&xQtu1DhmiD zaB%$67*gSOxD^MCOb|MQnuxZ+D?y#GAGI6Dl58oB!}1a9E@XSqC>()ko4RH1KaQ1? zY`!0}$I6?B<+w5XQIZV;IK|(Z2q26R{SS=<*vUz9>q$2!2ZtN>csb5yL^fePK5PRI z=7@qLVmT4}5*R5Ib}~DZyDx=pHx2)7j&p8W3TZ>9u)M&bPxm~1WW`t*AdZr*I;iDd zNzu=3C#*>=Tw<-!BRCa0v787ak#y*UQJ+CZKu+|tO+S=zZK@x*eC)(J- z;zh4yK}{`LPzI?G6^gCb}zk7jLAi2vJ;yX>u|%BBm3ZhR%YA`c=U#IR=$gH6>jMjNmm5ZlGnGG-+rP4Hl9N z-eKN8e9`YjrS|KE_+ba46mF@l#ct-WC7We(u1l}W6ynk%}M&8fkk|l)pM}EpZ<{(Cu=Yzs)3MdD=ou~CTpVULQT_I`O5_O)aKgY(L+$MXUW3{k=eVM*~A2 zH}#z{zVcot+4;y(FW)?!LHLhuLr;&>YGd7eb9Zg#8~XO`5mh%F0meV@V=(GV7W^F2 z7m{gh*CvlY*3k9S)^!0sMN)u{(#e)oL1CS4Q6gMvrbc8(eUo>hlP`P;R+%i|AH`S17+P zfn(?lIJ4{)iAW;JNW?t3R==9=^^nAn)H)oelXNj|eTZWl)WhV0{r>&{>y*^o! zmXlgQZ0@s8x`%3}dHHJTj-y|8!@h@K@_p?d>i9r;iu0no&O?+9JWcntygb`Hl9p|i zn*2e@EZoh{8o%-&8&9a2*5rPRmg-X0_`7Y?p;-DZXU!#}mCo-_Rlj1oBPZ5xOGcEp z9M;wh#D%Y^`oj~=Akb1DH#?)pypN4u12;mGf9R5pE8qK-oczk7UkjNKo23g%GMS!T zPUk+rzs{*z&}Q_is#_{5Q2aT+JysTAG?WSY&fj>T4f+)a`une|34St%Hkt0#G!Lpn z9P2v4t9gGDFvBbOP_1|J4S4GcBEjz8LAn(<-G7#+<3ORpk+w-l zxeT|Ghd8(bpmu#{R)|-DPYN54l(z9t$>x$`$w)+PZ9S}Hp(rb=c46>&XONRKs>9hy z#Z*D#MVoATk)9Q2*6M>f%Gifz_p}IFt243%J}~5^^)@DFg;YUAb5vA(6tlh-q`QQb z6DI)NbaL)$%W|h~8qr84Us z*%cpCSW#BEo?Och78U9P}bB^a&e!)hRqqB#hp z|1sRDvC#*k?SFs&0_NAyN(QpG&oKXkhwZqF$Ps|zgFY?-{KYR|{s08tW~C#sr)$%QD@j$xP0E^aOtYK@`%3K~SS-O8XL zq3ye(cr^d%+$jA~g++F|bp4*XMOk0bheiH0PY;$|A)V6$ZU8jwK%Tk;5Y$Bua}+U9 zZG@{dHTGKecBzdr&G^{{AHdneq|=%71+^g6AU=&F9>00_rtGF>)7mC8M-LAd?Wcx; zD0yB5sft+u@h%II9dASHk z8}F+Hz2gSIC<3=z*m_>bDoZUbFDu?ZUsAbCajK{=L?CP+Q+M-4C1n-N=6PUQ2m_16 zvxUO5lykLYep%^$;Br)W)GQ-BK3qA+D%{Zhuzm|wzaTa9Zdq(`*eP@6lyU%NC=73I zi(r>G{j8oq5VQ60wuC#2-Er?9EG%3jJ*z(aEVJUoxd}^0`whE)diGHNC)q*r(ayAi zX9${ka80wlU+_P^KKN*9@B5b~`#I-7+`gzaaVh?&Yt1hYpCUqtM{Mv!pWuxGVR`8J z+0xKfoubO}vJ;pSJDF77xGg?TdMHXNlrjIvitc!)6RL!%-XBe0>w3sv+~{o_TqG>z zkgC6mGvw{bMDg$hC&|q7GX=TluDf}9UPD|~I{-SG`P_&1hCc-))+I*87GG_JJ4!Um z?~QZQ;JVL4L-_@wa;_Zv=u{vi20Ra4D2CQIEYy|2)8`jEXf(+iJ13MrEbKU{Fn2L` z=>{HRfikGjLrK6mEJF|P;}g3$EF;Nq=cNn%{8LijD9`sf2=|AN!k0P;Op~TjY2Pd9 zdYgQ0oZMU8?^o%RKpGbBmjhi`Fx&HYAUGNFf`MYp$nev!TmnyTX^O^LGzg+q43>9`l+ZpL-P783D;lQmfH#MxP@n{v5} z9k(t`L7Zk1qR6@!gu2s2S@*hSU09mQG~K(^PY2_QWn8q)d>neZE4t`peCesKX#SRw z#)BrlOBl~d4d*)wCNC9E7tiatd?+jt7V3vlg@sRPnj006;}avq2@OMpCx6 zmSL3pqx7#VfgKBfI+d$8a16p*Bn+YmP8^ip8tTDf%>kwICqv|#8@UD=i@Csg|6qt5 zyjw&QJu9zNG*NJIR?a)@!@NBw)ywM6K%|1~C!3(C3(Dv2$krp(QZ^kOzUk4Rw#{F( zb|W|215{fI*=e>r=c!s`pjt<4P(j zGdmzLWqZ8PYR%Tec?ZZDoM6}adGk_;N1DdNEGRj zYGVl9|Jd0UY6?yldZm!o4uKeyc0&zgycE}mJ@z_h?$!bA#F}qL-3nHpKmI0`?Y|LT zWS)R>c5*ZB%kOYD{@T0&SQ_g_#6Vs&0vn#phbn5ssoiSmR;C7fb%!sj?RK^k^Sr?i zOG|1ERbpz3tf3E!3{_zDGf1Jxl%wbX>*$sSkLc)$5JF5B{sl*~;6JYOtYam)+b}Al zvtXT<(hANgUn~cOwv(BU4lJwR^$6c6y)hf=4=j_x%y%Zm+QEX@;-a-p;RDDji`gtK zJ>|Cs2XqzG_XQqOMnQ(1eV1d^ky_?Gs&#=1H$}5lm6{J;lKdu&r=U69yCODw4#|%k zX!Sunv+)*gm{XlrTo>rfkve|xO1XJ@-Z?88?yuBioO{Cyr#7$rs#mFYJ=DKHP8z6( zE!N%^cP`r$Zq}cFg}FhcQ{h;XN{~*QtChQHdc#aX%y&zL?bJd-*vG1oH*n=!u>lOj z9rw6yl8nXiV~ay6igGpJnnoQ)*oD6duFtm`_p2Z81sm44_F7%F0{0r$=U?s3udlzF z(^{Vcw)6Mv#2=5C#fqGw$@2%%3Ha)h1soj9m2xaTwzB~Zh3BpIJ<67i@?Vu zM8d6+bZ~{u*{#Crj*eEYP+{2Gu%vgv%Fi#~Y9ZgS)hfU9dXxZo_O+Cd?#Jj`_A@{- zyDHCJ(XF<*G0c{M6nH`d1k(DL_^?vb;-kMkE>e36jmI zo$Ynf_=GLiW`sB@8?g~juZSK%>O<;UBu)UD2yqk#XetiQI3l2ZXf1>r1NBx|(f*3c z;Bz1ST2dmcXxbkJB!6X8&6JRYwZl<_b6N?kRNW>Mla0w z*UYFvhJ+p-hh0^;FqjyVmH?b+kPl;{y_;I7``G)C*q~k(+n-(F1LVp~43pO)5URV! z7dl8u19R`lXfN=c+mAEjvuE@@e;xZi8srL66G^~t&hUCu- zRfjL;I^96^p90D%9Z$sNTBxUy6hA^FPD~JF3X)}WtJ}A|Ook4-j)*-x2C6s~7FrP* z8G;;{2!b*P8Ka?Hoped-hXknTAfm@c&jpm%%?rsB`Plpod??`9xOH+(#4Za zL#GA<=PYz?it{~$PttnCRRT!Syk10D_To7yXC*-`d%x^$oKo)?q@D>A8`(2hA)hNHwTeYff;vb!%5e?9_Uy)*wvKhC%! z_I{knS#(~pS=_n`mVwuyoEE_*IO!oA(T{c!JEIz)DD8EN@5;9HA5=nbO8R^9_yj^O zS1MOUDGJ>0m!MlRVm0Jf#9Gv68GE%|TLD+sDN?$$N+*&6t%xx8OI|_4i%j+@9PqoGoHoC}>}qikj*-ChXY4~Z zy+`vg2aCPJzWxsMcBr9SI_|e=(y;Tt*TF0JknhKHTnj))A5aIc`-l(wQ=;Qpf2oG; zR<3-By8St@Q2&dd`LRa&0}Y)j^y{Q@8<=Cy5<-Bc~iIl3VN zOWIlV$SB$*&})CE_-Pe1tCQuy)Y_}hv%Vk9{wF^Od#k(BhMa>8eQ2OqV^+9F2)jrofm_c>8HpaqVT z6L6$S+jP^$sF(-9b}bZE05w!)%%eZ>bvCr1LKInZ+D4sf0#5EbQ!P9T=xhoh3PhRT zru*crz`Q;)^)>ykvde)5bOcyHx__Q}WMWG6zcY87T^oMf9) z0l4o2$w*2%4-w%yF2d8#PtBsA<0CfO=1A}y_F zUytLAi60o8^G^4>9RnjK;vR0VFwHCx)-V`W7zVl(s2NN-U%H_`b=)5G({O)3(bKIH>IjoSDVrdWUX>SZHC<;IUIr?{cy4$V;?<%&8Fb|>hjnr z83rO|c~Mjn*4u_aqBO$CFLJUVM60_qw_#G!$oO#oF!5GD^;W-w^!nY=8R_VZjmEq& zL4NkS@3_?9|;%<01JcH8v?a|=y_$w(_vRWHVF=`nH>kTtIx=N zD=_y9f|$_;tWpD@1++4^J?O-Q)aZoN{efvo(X74y*4)H4*#E)y{0puHj89nOfXq#RC#Y+fn=8Y#cE_d%TZq*l8= zHmF-jzX^$nGK3u1HNOByJdW&Cw8_2!Zae?onY?dyGbt1XzRAanPT}sSCp5JP*qD5F z8k=&M%4T;eD-jqv$Qw= znnL)^p?Be<{82lFG@W<{^AkmY5?KE<6E}JfAKV*&{wFToq|5|e^s{E5Y=e>jisov{y-kT2luIK0_J{}<@pA8$^O zY;i=K4Q@IBK+B1+#kNpgEq110?l*cB9QfA4Oqfj*~^uXz6B-*uqLx=Jx86aG4Qwc`3;LqVX+>c0wdL`)$QU>7W#oj_Rp#Rx90I` zE31>MPgZ6Of4A~4Qmxf?R;L%A>}&*j*?-{lKa-8sqgONZr_m-Re;WN?e7$=3YFhX- z`~i?K{_*3VNMf~t)!6&Vz-80VBJpYitM~RN163RTveU@~74XW00fI0SI+(u DHtj1x literal 0 HcmV?d00001 From d53b2e5dfe104c0548cad992cfde8f3986624d0a Mon Sep 17 00:00:00 2001 From: bhuvaneshdhakshinmaoorthy Date: Wed, 30 Sep 2026 22:33:50 +0530 Subject: [PATCH 2/2] 1055046: Prepared samples for Angular, Vue, Typescript --- client/angular/.editorconfig | 15 + client/angular/.gitignore | 10 + client/angular/README.md | 95 + client/angular/angular.json | 63 + client/angular/package.json | 31 + client/angular/src/app/app.config.ts | 5 + client/angular/src/app/app.css | 8 + client/angular/src/app/app.html | 3 + client/angular/src/app/app.ts | 11 + .../spreadsheet-editor-adapter.ts | 83 + .../spreadsheet-editor.component.css | 4 + .../spreadsheet-editor.component.html | 7 + .../spreadsheet-editor.component.ts | 91 + client/angular/src/index.html | 12 + client/angular/src/main.ts | 6 + client/angular/src/styles.css | 11 + client/angular/tsconfig.app.json | 9 + client/angular/tsconfig.json | 26 + client/typescript/.gitignore | 6 + client/typescript/README.md | 79 + client/typescript/index.html | 12 + client/typescript/package-lock.json | 1678 ++++++++++++++ client/typescript/package.json | 21 + client/typescript/src/index.ts | 89 + .../src/spreadsheet-editor-adapter.ts | 83 + client/typescript/src/style.css | 11 + client/typescript/tsconfig.json | 19 + client/vue/.gitignore | 6 + client/vue/README.md | 78 + client/vue/index.html | 12 + client/vue/package-lock.json | 2054 +++++++++++++++++ client/vue/package.json | 27 + client/vue/src/App.vue | 102 + client/vue/src/SpreadsheetEditorAdapter.ts | 83 + client/vue/src/main.ts | 5 + client/vue/src/style.css | 8 + client/vue/src/vite-env.d.ts | 1 + client/vue/tsconfig.app.json | 10 + client/vue/tsconfig.json | 7 + client/vue/tsconfig.node.json | 16 + client/vue/vite.config.ts | 6 + 41 files changed, 4903 insertions(+) create mode 100644 client/angular/.editorconfig create mode 100644 client/angular/.gitignore create mode 100644 client/angular/README.md create mode 100644 client/angular/angular.json create mode 100644 client/angular/package.json create mode 100644 client/angular/src/app/app.config.ts create mode 100644 client/angular/src/app/app.css create mode 100644 client/angular/src/app/app.html create mode 100644 client/angular/src/app/app.ts create mode 100644 client/angular/src/components/spreadsheet-editor/spreadsheet-editor-adapter.ts create mode 100644 client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.css create mode 100644 client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.html create mode 100644 client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.ts create mode 100644 client/angular/src/index.html create mode 100644 client/angular/src/main.ts create mode 100644 client/angular/src/styles.css create mode 100644 client/angular/tsconfig.app.json create mode 100644 client/angular/tsconfig.json create mode 100644 client/typescript/.gitignore create mode 100644 client/typescript/README.md create mode 100644 client/typescript/index.html create mode 100644 client/typescript/package-lock.json create mode 100644 client/typescript/package.json create mode 100644 client/typescript/src/index.ts create mode 100644 client/typescript/src/spreadsheet-editor-adapter.ts create mode 100644 client/typescript/src/style.css create mode 100644 client/typescript/tsconfig.json create mode 100644 client/vue/.gitignore create mode 100644 client/vue/README.md create mode 100644 client/vue/index.html create mode 100644 client/vue/package-lock.json create mode 100644 client/vue/package.json create mode 100644 client/vue/src/App.vue create mode 100644 client/vue/src/SpreadsheetEditorAdapter.ts create mode 100644 client/vue/src/main.ts create mode 100644 client/vue/src/style.css create mode 100644 client/vue/src/vite-env.d.ts create mode 100644 client/vue/tsconfig.app.json create mode 100644 client/vue/tsconfig.json create mode 100644 client/vue/tsconfig.node.json create mode 100644 client/vue/vite.config.ts diff --git a/client/angular/.editorconfig b/client/angular/.editorconfig new file mode 100644 index 0000000..150aa39 --- /dev/null +++ b/client/angular/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/client/angular/.gitignore b/client/angular/.gitignore new file mode 100644 index 0000000..5f131d1 --- /dev/null +++ b/client/angular/.gitignore @@ -0,0 +1,10 @@ +/.angular +/.idea +/.vscode/* +!/.vscode/extensions.json +/dist +/node_modules +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.DS_Store diff --git a/client/angular/README.md b/client/angular/README.md new file mode 100644 index 0000000..a16ecae --- /dev/null +++ b/client/angular/README.md @@ -0,0 +1,95 @@ +# Angular Spreadsheet Collaborative Editing + +This example demonstrates real-time collaborative editing in the Syncfusion Angular Spreadsheet. Multiple users can edit the same workbook, and supported actions are synchronized through the Collaboration Server. + +## Prerequisites + +- Node.js +- Redis Server +- ASP.NET Core Redis Collaboration Server, or access to the configured hosted Collaboration Server + +## Install dependencies + +```bash +npm install +``` + +## Configure the Collaboration Server + +The Collaboration Server URL is defined in: + +```text +src/components/spreadsheet-editor/spreadsheet-editor.component.ts +``` + +Update `serviceUrl` when a different Collaboration Server endpoint is used. + +## Run the application + +```bash +npm start +``` + +Open the URL displayed by Angular, normally: + +```text +http://localhost:4200/ +``` + +When the page opens without an `id` query parameter, the client generates a room ID and updates the URL. Open the complete URL in another browser tab or window to join the same collaboration room. + +Example: + +```text +http://localhost:4200/?id=sample-room +``` + +## Project structure + +```text +src/ +|-- app/ +| |-- app.config.ts +| |-- app.css +| |-- app.html +| `-- app.ts +|-- components/ +| `-- spreadsheet-editor/ +| |-- spreadsheet-editor.component.css +| |-- spreadsheet-editor.component.html +| |-- spreadsheet-editor.component.ts +| `-- spreadsheet-editor-adapter.ts +|-- index.html +|-- main.ts +`-- styles.css +``` + +## How it works + +1. The Angular Spreadsheet starts with collaborative editing enabled. +2. The client obtains or generates a room ID from the URL. +3. The adapter imports the latest workbook and room version from the server. +4. `CollaborationClient` connects through SignalR and joins the room. +5. Local Spreadsheet actions are sent from the `actionComplete` event. +6. Remote actions are applied through `SpreadsheetEditorAdapter.applyRemoteAction`. + +## Build + +```bash +npm run build +``` + +## Important packages + +- `@syncfusion/ej2-angular-spreadsheet` +- `@syncfusion/ej2-collaborator` +- `@microsoft/signalr` +- `@syncfusion/ej2-tailwind3-theme` + +## Troubleshooting + +If clients do not synchronize, verify that the Collaboration Server is reachable, Redis is running, all clients use the same room ID, and the browser console has no SignalR or CORS errors. + +## License + +Refer to the license file in the parent repository. diff --git a/client/angular/angular.json b/client/angular/angular.json new file mode 100644 index 0000000..d59dc59 --- /dev/null +++ b/client/angular/angular.json @@ -0,0 +1,63 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "angular-spreadsheet-collaborative-editing": { + "projectType": "application", + "schematics": {}, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "browser": "src/main.ts", + "tsConfig": "tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": ["src/styles.css"] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "1MB", + "maximumError": "2MB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-spreadsheet-collaborative-editing:build:production" + }, + "development": { + "buildTarget": "angular-spreadsheet-collaborative-editing:build:development" + } + }, + "defaultConfiguration": "development" + } + } + } + }, + "cli": { + "analytics": false + } +} diff --git a/client/angular/package.json b/client/angular/package.json new file mode 100644 index 0000000..ebea787 --- /dev/null +++ b/client/angular/package.json @@ -0,0 +1,31 @@ +{ + "name": "angular-spreadsheet-collaborative-editing", + "version": "1.0.0", + "private": true, + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development" + }, + "dependencies": { + "@angular/common": "^22.2.0", + "@angular/compiler": "^22.2.0", + "@angular/core": "^22.2.0", + "@angular/forms": "^22.2.0", + "@angular/platform-browser": "^22.2.0", + "@angular/router": "^22.2.0", + "@microsoft/signalr": "^9.0.0", + "@syncfusion/ej2-angular-spreadsheet": "35.1.37", + "@syncfusion/ej2-collaborator": "35.1.37", + "@syncfusion/ej2-tailwind3-theme": "35.1.37", + "rxjs": "~7.8.0", + "tslib": "^2.3.0" + }, + "devDependencies": { + "@angular/build": "^22.2.0", + "@angular/cli": "^22.2.0", + "@angular/compiler-cli": "^22.2.0", + "typescript": "~6.0.2" + } +} diff --git a/client/angular/src/app/app.config.ts b/client/angular/src/app/app.config.ts new file mode 100644 index 0000000..67fec86 --- /dev/null +++ b/client/angular/src/app/app.config.ts @@ -0,0 +1,5 @@ +import { ApplicationConfig } from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [] +}; diff --git a/client/angular/src/app/app.css b/client/angular/src/app/app.css new file mode 100644 index 0000000..789f20c --- /dev/null +++ b/client/angular/src/app/app.css @@ -0,0 +1,8 @@ +:host { + display: block; + width: 100%; +} + +.app-container { + width: 100%; +} diff --git a/client/angular/src/app/app.html b/client/angular/src/app/app.html new file mode 100644 index 0000000..05963a4 --- /dev/null +++ b/client/angular/src/app/app.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/client/angular/src/app/app.ts b/client/angular/src/app/app.ts new file mode 100644 index 0000000..714660e --- /dev/null +++ b/client/angular/src/app/app.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { SpreadsheetEditorComponent } from '../components/spreadsheet-editor/spreadsheet-editor.component'; + +@Component({ + selector: 'app-root', + standalone: true, + imports: [SpreadsheetEditorComponent], + templateUrl: './app.html', + styleUrl: './app.css' +}) +export class App {} diff --git a/client/angular/src/components/spreadsheet-editor/spreadsheet-editor-adapter.ts b/client/angular/src/components/spreadsheet-editor/spreadsheet-editor-adapter.ts new file mode 100644 index 0000000..db4e92e --- /dev/null +++ b/client/angular/src/components/spreadsheet-editor/spreadsheet-editor-adapter.ts @@ -0,0 +1,83 @@ +import type { + ICollaborationActionData, + ICollaborationProvider +} from '@syncfusion/ej2-collaborator'; +import { SpreadsheetComponent } from '@syncfusion/ej2-angular-spreadsheet'; + +interface ImportFileResponse { + sfdt: string; + version: number; +} + +export class SpreadsheetEditorAdapter + implements ICollaborationProvider { + public currentRoomName: string = ''; + + public constructor( + private spreadsheet: SpreadsheetComponent, + private serviceUrl: string, + private currentUser: string + ) { + this.serviceUrl = serviceUrl.endsWith('/') + ? serviceUrl + : serviceUrl + '/'; + } + + public async loadFromServer( + fileName: string, + roomName: string + ): Promise { + const response: Response = await fetch( + this.serviceUrl + 'api/CollaborativeEditing/ImportFile', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ fileName, roomName }) + } + ); + + if (!response.ok) { + throw new Error('Failed to load the workbook.'); + } + + const data: ImportFileResponse = + JSON.parse(await response.text()); + + this.currentRoomName = roomName; + this.spreadsheet.collaborativeEditingModule.updateRoomInfo( + roomName, + data.version, + this.serviceUrl + 'api/CollaborativeEditing/' + ); + this.spreadsheet.collaborativeEditingModule.setLocalUser( + this.currentUser + ); + this.spreadsheet.openFromJson({ file: data.sfdt }); + } + + public sendActionToServer(action: unknown): void { + if (!action) { + return; + } + + this.spreadsheet.collaborativeEditingModule.sendActionToServer( + action + ); + } + + public applyRemoteAction( + action: string, + data: ICollaborationActionData + ): void { + if (!data) { + return; + } + + this.spreadsheet.collaborativeEditingModule.applyRemoteAction( + action, + data.payload + ); + } +} diff --git a/client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.css b/client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.css new file mode 100644 index 0000000..fe8bbc5 --- /dev/null +++ b/client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.css @@ -0,0 +1,4 @@ +:host { + display: block; + width: 100%; +} diff --git a/client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.html b/client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.html new file mode 100644 index 0000000..2f52023 --- /dev/null +++ b/client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.html @@ -0,0 +1,7 @@ + + diff --git a/client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.ts b/client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.ts new file mode 100644 index 0000000..b074c2b --- /dev/null +++ b/client/angular/src/components/spreadsheet-editor/spreadsheet-editor.component.ts @@ -0,0 +1,91 @@ +import { Component, ViewChild } from '@angular/core'; +import { + CollaborativeEditingHandlerService, + SpreadsheetComponent, + SpreadsheetModule +} from '@syncfusion/ej2-angular-spreadsheet'; +import { CollaborationClient } from '@syncfusion/ej2-collaborator'; +import { SpreadsheetEditorAdapter } from './spreadsheet-editor-adapter'; + +const serviceUrl: string = + 'https://appservice-267100-e9c8cxaab6a2dfeu.centralindia-01.azurewebsites.net/'; +const currentUser: string = 'John'; + +function getRoomName(): string { + const currentUrl: URL = new URL(window.location.href); + let roomName: string = ( + currentUrl.searchParams.get('id') || '' + ).trim(); + + if (!roomName) { + roomName = Math.random().toString(32).slice(2); + currentUrl.searchParams.set('id', roomName); + window.history.replaceState( + window.history.state, + '', + currentUrl.pathname + currentUrl.search + currentUrl.hash + ); + } + + return roomName; +} + +@Component({ + selector: 'app-spreadsheet-editor', + standalone: true, + imports: [SpreadsheetModule], + providers: [CollaborativeEditingHandlerService], + templateUrl: './spreadsheet-editor.component.html', + styleUrl: './spreadsheet-editor.component.css' +}) +export class SpreadsheetEditorComponent { + @ViewChild('spreadsheet') + public spreadsheetObj!: SpreadsheetComponent; + + private adapter: SpreadsheetEditorAdapter | null = null; + private client: CollaborationClient | null = null; + private initialized: boolean = false; + + public async created(): Promise { + if (!this.spreadsheetObj || this.initialized) { + return; + } + + this.initialized = true; + const roomName: string = getRoomName(); + const adapter = new SpreadsheetEditorAdapter( + this.spreadsheetObj, + serviceUrl, + currentUser + ); + + try { + await adapter.loadFromServer('Sample', roomName); + + const client = new CollaborationClient(adapter, { + serviceUrl, + connectionType: 'signalr', + currentUser + }); + + this.adapter = adapter; + this.client = client; + await client.joinRoomAsync(roomName); + + console.log( + '[Collaborative Editing] Joined room', + roomName + ); + } catch (error) { + this.initialized = false; + console.error( + '[Collaborative Editing] Failed to join the room.', + error + ); + } + } + + public actionComplete(args: unknown): void { + this.adapter?.sendActionToServer(args); + } +} diff --git a/client/angular/src/index.html b/client/angular/src/index.html new file mode 100644 index 0000000..c855029 --- /dev/null +++ b/client/angular/src/index.html @@ -0,0 +1,12 @@ + + + + + Angular Spreadsheet Collaborative Editing + + + + + + + diff --git a/client/angular/src/main.ts b/client/angular/src/main.ts new file mode 100644 index 0000000..7fd415e --- /dev/null +++ b/client/angular/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { App } from './app/app'; + +bootstrapApplication(App, appConfig) + .catch((error: unknown) => console.error(error)); diff --git a/client/angular/src/styles.css b/client/angular/src/styles.css new file mode 100644 index 0000000..01a5d10 --- /dev/null +++ b/client/angular/src/styles.css @@ -0,0 +1,11 @@ +@import '../node_modules/@syncfusion/ej2-tailwind3-theme/styles/tailwind3.css'; + +html, +body { + height: 100%; + margin: 0; +} + +body { + font-family: "Segoe UI", Arial, sans-serif; +} diff --git a/client/angular/tsconfig.app.json b/client/angular/tsconfig.app.json new file mode 100644 index 0000000..5b9d3c5 --- /dev/null +++ b/client/angular/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"] +} diff --git a/client/angular/tsconfig.json b/client/angular/tsconfig.json new file mode 100644 index 0000000..452682e --- /dev/null +++ b/client/angular/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "sourceMap": true, + "declaration": false, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "preserve", + "lib": ["ES2022", "dom"] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/client/typescript/.gitignore b/client/typescript/.gitignore new file mode 100644 index 0000000..cc9689e --- /dev/null +++ b/client/typescript/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +.vscode/* +!.vscode/extensions.json +.DS_Store +*.local diff --git a/client/typescript/README.md b/client/typescript/README.md new file mode 100644 index 0000000..6738323 --- /dev/null +++ b/client/typescript/README.md @@ -0,0 +1,79 @@ +# TypeScript Spreadsheet Collaborative Editing + +This example demonstrates real-time collaborative editing in the Syncfusion Essential JS 2 TypeScript Spreadsheet. Multiple users can edit the same workbook, and supported actions are synchronized through the Collaboration Server. + +## Prerequisites + +- Node.js +- Redis Server +- ASP.NET Core Redis Collaboration Server, or access to the configured hosted Collaboration Server + +## Install dependencies + +```bash +npm install +``` + +## Configure the Collaboration Server + +The Collaboration Server URL is defined in `src/index.ts`. Update `serviceUrl` when a different server endpoint is used. + +## Run the application + +```bash +npm run dev +``` + +Open the URL displayed by Vite, normally: + +```text +http://localhost:5173/ +``` + +When the page opens without an `id` query parameter, the client generates a room ID and updates the URL. Open the complete URL in another browser tab or window to join the same collaboration room. + +Example: + +```text +http://localhost:5173/?id=sample-room +``` + +## Project structure + +```text +src/ +|-- index.ts +|-- spreadsheet-editor-adapter.ts +`-- style.css +``` + +## How it works + +1. `CollaborativeEditingHandler` is injected into the Spreadsheet. +2. The Spreadsheet starts with collaborative editing enabled. +3. The client obtains or generates a room ID from the URL. +4. The adapter imports the latest workbook and room version from the server. +5. `CollaborationClient` connects through SignalR and joins the room. +6. Local Spreadsheet actions are sent from `actionComplete`. +7. Remote actions are applied through `SpreadsheetEditorAdapter.applyRemoteAction`. + +## Build + +```bash +npm run build +``` + +## Important packages + +- `@syncfusion/ej2-spreadsheet` +- `@syncfusion/ej2-collaborator` +- `@microsoft/signalr` +- `@syncfusion/ej2-tailwind3-theme` + +## Troubleshooting + +If clients do not synchronize, verify that the Collaboration Server is reachable, Redis is running, all clients use the same room ID, and the browser console has no SignalR or CORS errors. + +## License + +Refer to the license file in the parent repository. diff --git a/client/typescript/index.html b/client/typescript/index.html new file mode 100644 index 0000000..accd0fa --- /dev/null +++ b/client/typescript/index.html @@ -0,0 +1,12 @@ + + + + + + TypeScript Spreadsheet Collaborative Editing + + +
+ + + diff --git a/client/typescript/package-lock.json b/client/typescript/package-lock.json new file mode 100644 index 0000000..8c42d8a --- /dev/null +++ b/client/typescript/package-lock.json @@ -0,0 +1,1678 @@ +{ + "name": "typescript-spreadsheet-collaborative-editing", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "typescript-spreadsheet-collaborative-editing", + "version": "1.0.0", + "dependencies": { + "@microsoft/signalr": "^9.0.0", + "@syncfusion/ej2-collaborator": "35.1.37", + "@syncfusion/ej2-spreadsheet": "35.1.37", + "@syncfusion/ej2-tailwind3-theme": "35.1.37" + }, + "devDependencies": { + "typescript": "~5.9.3", + "vite": "^7.1.9" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@microsoft/signalr": { + "version": "9.0.19", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-9.0.19.tgz", + "integrity": "sha512-hKDxt4nRXvkhhy+Etlhbg0UDQdhk8PsZKlQTnHyCn+bb+Q6gM8RCZLI43WsLH1IQCa7l/G8tX09em1F1LH/C1w==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "eventsource": "^2.0.2", + "fetch-cookie": "^2.0.3", + "node-fetch": "^2.6.7", + "ws": "^7.5.10" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.5.tgz", + "integrity": "sha512-J25QJU+B78T4FhhBsNpLJyVWOi31mwtpcMwywHmOKH65Q9IWGA81gPj+dnwlhU8wktVriYE+tFAaQgrnJRzAZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.5.tgz", + "integrity": "sha512-LDopB3zuZM5Ux9TT2luNEBJW/tYbGU2g1d+VpKk6I+gSKDb+/7sYE6M225gRQt4RbMX6MSwMsVR/phdjVUgRLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.5.tgz", + "integrity": "sha512-wlJEERGfeuHeBavCL2qVnNacOK43NDoZM4sjkeRPymd04OAE9T1zBqDJgmZ+CIsPTYKwdzpUC8vmOw84dwY4Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.5.tgz", + "integrity": "sha512-4nJJGg5jbo2wwPP4JP+LfEBA3bvP8rU9CLuhp7jWvq9sxEyhjQFTFdrqi+/dHEin/pd8jpT0vcehIpnZtmEdcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.5.tgz", + "integrity": "sha512-DrZbyCDF1hneuO6jRbvZ2D7+PIBM6yIwYnJpg2vIk58T+wuFpiaGZrfUr59lDWw45bg+IrpTGLPiNi/Fk4w3Cg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.5.tgz", + "integrity": "sha512-gqfUVMJMB3mehqywxp6hTBFfgtMQykZY19+cfiaYP0toIJLb/1DZRJHVkQQGP13W4TAwfZDWeg1qBcheTRioXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.5.tgz", + "integrity": "sha512-CFmhpvAwzSaWMlN3VN7UtmoTihlZNzoP0juQib5TQRnYUyDV8dXeWOp29sobWAT6gXl/hQgAClLlEiYozQG3OQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.5.tgz", + "integrity": "sha512-Uc9H8eXCOayV6JLTH5bXKMId6qbhNHa818/BgYjm4jrlq3vZquC9cqyvHBw17xy5Mnj5f+I3gFK5JcEf3hSqrw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.5.tgz", + "integrity": "sha512-VcPr/szv/1BFw112Kt//fxulXt/JPqzzidU84iW68L2DdjnOO8QFUv2zTSYBEPHD6movBD4z+bbr5y60GYM7Jw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.5.tgz", + "integrity": "sha512-BnxtJ5/91BrIHYIkGrmjz/lbMhqEHt1dPFqIxIFR+jPn0xVc/oUSCtIT089zfp5ufwGDlYz2UC+Fe1SRBpYFbQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.5.tgz", + "integrity": "sha512-LrYcHZwF+fAMNKHYTOQ5osWM4AZF7YF6D+XtsjDyEvljtt11twc+zHVXBLNEjxVSUnKYsOhvVz4Z213eW02COQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.5.tgz", + "integrity": "sha512-nj7QKQePAAUpCpJHtg0pR0W/b92A9NO17JS3BAQmHDn/yhmkir2p8llrKY9TOhleKIaSzy1JhxS3T9FVld6coA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.5.tgz", + "integrity": "sha512-5ylkX6dWMeBKge9nTU+Rxfb+ZfaCIJ9lRqIFaK0eAMcWp7OJbYnLveLgXmm0VrvuLKb8qIK+mHyH0qu88RM+iA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.5.tgz", + "integrity": "sha512-oHK4ZHYFDKjZviK34I+NwgfbGxgI7ztrNxj2hPTSSNFgeq1a/lEd7dHV2fdGAuTH4Iym3RHJg+vAbWaWG4B7Zg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.5.tgz", + "integrity": "sha512-UcetmHZ6XOXuUByiKZyQmb55ZPr0LABr3Ec/HB9wKZn6CEAFWZkE+hsJErJ9hbPBC7nI0dKuELx7CoV6IM7TMg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.5.tgz", + "integrity": "sha512-C5CmDPQBtvjVo8cgQsBs+w6WB0JLkiixhgi6hVLV11hERWdn/p0XcPU2OUcZzac9BPOFq7SbaHFa8r3SWEysCQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.5.tgz", + "integrity": "sha512-lHVQHJFKsuuxLMi3MQO9XVL8Tje3JR82CzB+QDKC5NWBcsIWuwsn9uIM5e3lBhI+fF1/s63qnyYqsg65+8rV/w==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.5.tgz", + "integrity": "sha512-3W9bTFcQNJn71cSJVM9RKIiZOy8DO/XLDii8Uv/Pm6WKqDRj7JV3ZfuXIEfyuy5LXpIzAbB/1M4Ukp9GKNa7nA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.5.tgz", + "integrity": "sha512-VDC7rRJlee/scpki96GZ27Omf6yU87s1YXwVTpjE5841faVlDYYT565rgfmoR1U0sqL7z5ivQSDjcsF6VRXyBA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.5.tgz", + "integrity": "sha512-z86Ok2p4pTdv5xqCKZsTooO7yBEiaJR/HzU3Wx8RmWsPoLppnMKROhJusQob8B3IE1ghC343kUW9rC2r+Wf3ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.5.tgz", + "integrity": "sha512-IzQmj+xXwQFGhMAMKMQVXkMwMZN3TqkJgAE0nSsqvVwWWciP4AIPMmWRqOQ2GfX7TUDZr+xqGFcBS36CRPGw0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.5.tgz", + "integrity": "sha512-F6qpTaPc9bwBH85kjy0/BLmLSW1uv7AoOXCoRIkg2arlgCYlWYcAbiMkvZuAcaWk9TpCRG//okznLAqLGshkMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.5.tgz", + "integrity": "sha512-igoDsTFhhwECBeGbUuLeIk7t8Y1apa+cs6mDWpx2EZ0ch7oEQgzHbFUXN9euoHekCAQzXdXApAGkV6jznS7tWw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.5.tgz", + "integrity": "sha512-U3teMeMbXFmaM5D+OTJpsOXd+wV/qftIeYF9kBKL4v73641qyJmoXFtA28DQLsnmlyayEsTe72xpLHrArq6vHw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.5.tgz", + "integrity": "sha512-ypfC34F3RKXvCXBglGqGMsUSMKlgwd1HX9AOAlx9RoZZ6GaI42YHVeKpzg3JG+wpBUJYTG+NNZhqbDWL8tBZkw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@syncfusion/ej2-base": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-35.1.37.tgz", + "integrity": "sha512-pg4WMFGbZJ9XoGr7ecBQ4+afWeVh1s0nckwqa1fFe1PK48Szg/gV5rEFXygFsHfratparBVAJogAd8WrWv3pYg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-icons": "~35.1.37" + }, + "bin": { + "syncfusion-license": "bin/syncfusion-license.js" + } + }, + "node_modules/@syncfusion/ej2-buttons": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-buttons/-/ej2-buttons-35.1.37.tgz", + "integrity": "sha512-tLYpbTqfPPNBqVZ8SmwM9yw+gAkBRhD0Rcl+1YcrG/8z43tVqWJdemtOlOItV/GGEQXXhDbX7PKgJs9m3CDkrg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-calculate": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-calculate/-/ej2-calculate-35.1.37.tgz", + "integrity": "sha512-q6Ogf+3vUcDZFOs7cdhFtXwrBmz555dSKnXNPe1QRdOhnZpOYIDYWcy0WlJ9E6zVk8SdvS3hMNKTmVP8DE1lxA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-calendars": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-calendars/-/ej2-calendars-35.1.37.tgz", + "integrity": "sha512-KLYnPjinjjpHU4I4hZwJKWuMm8AYPWnnT7R/kVYQ0fMz4Vt1XrNCcOyByTvSINTDvb4sZeF6a5VTRNL6/i7Xgw==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-lists": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-charts": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-charts/-/ej2-charts-35.1.37.tgz", + "integrity": "sha512-q9ShJ4K7ozYRPqSpENay95eoe4ZBftHogveYTRsJTAKXCrh7tbjxJ9doeKBN+pcdqIBqcK0ecXDw4vPyxRvEtA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-calendars": "~35.1.37", + "@syncfusion/ej2-data": "~35.1.37", + "@syncfusion/ej2-excel-export": "~35.1.37", + "@syncfusion/ej2-navigations": "~35.1.37", + "@syncfusion/ej2-pdf-export": "~35.1.37", + "@syncfusion/ej2-svg-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-collaborator": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-collaborator/-/ej2-collaborator-35.1.37.tgz", + "integrity": "sha512-Ff72o4M1yIw8VtjaiKyicHv7e5jr0jcL3Mak0yrvOCZoLyp4ucu5Zi5f+WHAy9qj4Tgu2WCuhzqRML8tx8G5gA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-compression": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-compression/-/ej2-compression-35.1.37.tgz", + "integrity": "sha512-Lz+5N1iahYLtmyznJ9O+xTq34Mfc7PXFJna28h7PGJ7YNkZxJDLi1KijypJHj+LZfi8NAQsn7MRMnTFfbHxBmQ==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-file-utils": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-data": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-data/-/ej2-data-35.1.37.tgz", + "integrity": "sha512-rjHvWvwOYWCvWhptPe9ldenOKZXPXFer0v2UrbJhSfJ1oC9NxCm+HX1MrfY46SStOz93s3X8XorF2LgQlXRyRA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-dropdowns": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-dropdowns/-/ej2-dropdowns-35.1.37.tgz", + "integrity": "sha512-StvzQPhuFZIQd53PAIWg7gsBiEKNMJuRueDgCNKTUoJo45fuHphShhb4RIwjnM1D6BJqQ1a/7oxeWQYxdaH/CQ==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-data": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-lists": "~35.1.37", + "@syncfusion/ej2-navigations": "~35.1.37", + "@syncfusion/ej2-notifications": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-excel-export": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-excel-export/-/ej2-excel-export-35.1.37.tgz", + "integrity": "sha512-oTjGCkiTpsx06EVbvwrwaTjORucMizuCb/AnfbjGoHVPGfZj74cchHQY+8rmY2uSRmz9nLQIGhc4Ip1JNcWDZg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-compression": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-file-utils": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-file-utils/-/ej2-file-utils-35.1.37.tgz", + "integrity": "sha512-+Psm/DeB0b8DJeo63OU997bSSSL8Lj6s7tnY0CFUb1nXHmJ6pxxGLapf6MbBJYFSD+as1IWk2kFHkmWI1hu5Xg==", + "license": "SEE LICENSE IN license" + }, + "node_modules/@syncfusion/ej2-grids": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-grids/-/ej2-grids-35.1.37.tgz", + "integrity": "sha512-4t3o3VsUoYCes+gIVsYTyb2jbADfMMbGmGbJdeg9D4/Vl7sfvMdM7cWAxOX87oGwxhua29hQw7+8DLZ2Wm1Agg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-calendars": "~35.1.37", + "@syncfusion/ej2-compression": "~35.1.37", + "@syncfusion/ej2-data": "~35.1.37", + "@syncfusion/ej2-dropdowns": "~35.1.37", + "@syncfusion/ej2-excel-export": "~35.1.37", + "@syncfusion/ej2-file-utils": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-lists": "~35.1.37", + "@syncfusion/ej2-navigations": "~35.1.37", + "@syncfusion/ej2-notifications": "~35.1.37", + "@syncfusion/ej2-pdf-export": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37", + "@syncfusion/ej2-querybuilder": "~35.1.37", + "@syncfusion/ej2-splitbuttons": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-icons": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-35.1.37.tgz", + "integrity": "sha512-D7LC3O2OI2Ugc/DLShHvhoEPBEZCT9hj49PjV30CxfJ8ZyVR+EWl/GC3b1q1xMKLiB7ZhDrXBoZgq5mJaeVZuw==", + "license": "SEE LICENSE IN license" + }, + "node_modules/@syncfusion/ej2-inputs": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-inputs/-/ej2-inputs-35.1.37.tgz", + "integrity": "sha512-P4NQI/MQrfAqnpMNuRsxB0GMIXXZ5EIPPIt/oZnq1gAzGbDj7HM4yz4rg6xtiae8t2nMr0QI4cggL2dDKzpfHQ==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37", + "@syncfusion/ej2-splitbuttons": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-interactive-chat": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-interactive-chat/-/ej2-interactive-chat-35.1.37.tgz", + "integrity": "sha512-oq+h77v2DlbQBblBAfT25DKoLUMtBKiFxrlkgpDq2QqTLIhcD0XlrOXO5FKlUsLTMpmV71pZjpyNWyWc4rH+/g==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-dropdowns": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-layouts": "~35.1.37", + "@syncfusion/ej2-markdown-converter": "~35.1.37", + "@syncfusion/ej2-navigations": "~35.1.37", + "@syncfusion/ej2-notifications": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37", + "@syncfusion/ej2-splitbuttons": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-layouts": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-layouts/-/ej2-layouts-35.1.37.tgz", + "integrity": "sha512-5yj54DPYAKLjuIA+pO3IaZryGWbqAfXRZZuyOb35cjOFHoDDeAoQ9x0ocigxUYFv/IOx/6JW3M79Fb3hx7XA8g==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-lists": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-lists/-/ej2-lists-35.1.37.tgz", + "integrity": "sha512-GA5cnNXA+Kpme2nDAxld7x88T1YnoMDvHidTk2CJj/Dp3mwvB2/FScaGxYjidES1Hev0XWvJtgnGPdhI95gE2Q==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-data": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-markdown-converter": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-markdown-converter/-/ej2-markdown-converter-35.1.37.tgz", + "integrity": "sha512-fBtN3+1ACdXIYbzAX35gDcOTtR5VCdjW2QSWaLpO3jhh+NntOrhfM82ntxI+TOdyZYOvtNxt/pQjIp2K/UOGBA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-navigations": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-navigations/-/ej2-navigations-35.1.37.tgz", + "integrity": "sha512-jqpVXZimfqfIPzCf9I7KPB47hVdiouYDnmEHIpayekQlSdMq2qZQJy59sqb8genYL4OR/KnEFJlcIp1mdhJ8Kw==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-data": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-lists": "~35.1.37", + "@syncfusion/ej2-notifications": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-notifications": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-notifications/-/ej2-notifications-35.1.37.tgz", + "integrity": "sha512-UyjQ2Ykx8N1YrikYxrMaK9F7x/u4Ea4ZdCO8rL07binfF+lDEFlPpMoLbiQYatwb8gN++d5UbcyQXs/sYLIYfg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-pdf-export": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-pdf-export/-/ej2-pdf-export-35.1.37.tgz", + "integrity": "sha512-9+2GJBe0vjs0CuIBgQQd7GcQ2CaxCh//OqNlnbGvPO4dfagEj0TUoKpbhIK9Qs5+l1KHvpSgPhQ80LuZBfyHhw==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-compression": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-popups": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-popups/-/ej2-popups-35.1.37.tgz", + "integrity": "sha512-HC3x/sR07YDDc/eS/iZrN0SyJkuAXDS1uRABeN6UdPdkp2o7IOkSOCAyAURMY/dHqpCDblhsnE0LXZ6HwlcQaQ==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-querybuilder": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-querybuilder/-/ej2-querybuilder-35.1.37.tgz", + "integrity": "sha512-h2xUbOt+I88ICtWRhcMrmizfX4fXU9ZfSc2cR/Umn5vSoMJE52AEAQ9iamRiMTJUFAwOyINpdfabr1pO7xngKg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-calendars": "~35.1.37", + "@syncfusion/ej2-dropdowns": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-splitbuttons": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-splitbuttons": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-splitbuttons/-/ej2-splitbuttons-35.1.37.tgz", + "integrity": "sha512-whjKUf7p9grTtDxssx4s2u9gYDysiRHBn658dj6oRMKoYAECdaw5H/WEIcqKoxA6B1hJyUHZ8GE1SbWdV7eSVw==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-spreadsheet": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-spreadsheet/-/ej2-spreadsheet-35.1.37.tgz", + "integrity": "sha512-TEM28i5E17uFG37HpYLaRhpjiyq/k4KxQG6MJMTn3JroZtM2+BUvki6l847xhCdPt1Jrvg8slc0uKI28Cox3XA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-calculate": "~35.1.37", + "@syncfusion/ej2-charts": "~35.1.37", + "@syncfusion/ej2-dropdowns": "~35.1.37", + "@syncfusion/ej2-grids": "~35.1.37", + "@syncfusion/ej2-interactive-chat": "~35.1.37", + "@syncfusion/ej2-navigations": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-svg-base": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-svg-base/-/ej2-svg-base-35.1.37.tgz", + "integrity": "sha512-jgTjelQSmDHETQfXaecRL92tw/i63ijnACZEwp97gDl+bbY8Nc/PjTvaHNjECjuOL2Nt83q4z77OnGjE2zmNJQ==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-tailwind3-theme": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-tailwind3-theme/-/ej2-tailwind3-theme-35.1.37.tgz", + "integrity": "sha512-1Qujjxkeo4pH58CcBHytkqwTuZZRlFeoEfdzyfkvZGjtcg9dH2sOMYVwz6GVs4dRrtm/KJkqeBXMToCMR6bT2g==", + "license": "SEE LICENSE IN license" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-cookie": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.2.0.tgz", + "integrity": "sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==", + "license": "Unlicense", + "dependencies": { + "set-cookie-parser": "^2.4.8", + "tough-cookie": "^4.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.5.tgz", + "integrity": "sha512-KRWwmNLlPw5M7HcdYfm15oBv9n9LPtjzpzCIxS/phwqvPyxHSoKX6Y2YU3pxSPfy0CLquVgsx/j/hBi6OvH1Nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.5", + "@rollup/rollup-android-arm64": "4.63.5", + "@rollup/rollup-darwin-arm64": "4.63.5", + "@rollup/rollup-darwin-x64": "4.63.5", + "@rollup/rollup-freebsd-arm64": "4.63.5", + "@rollup/rollup-freebsd-x64": "4.63.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.5", + "@rollup/rollup-linux-arm-musleabihf": "4.63.5", + "@rollup/rollup-linux-arm64-gnu": "4.63.5", + "@rollup/rollup-linux-arm64-musl": "4.63.5", + "@rollup/rollup-linux-loong64-gnu": "4.63.5", + "@rollup/rollup-linux-loong64-musl": "4.63.5", + "@rollup/rollup-linux-ppc64-gnu": "4.63.5", + "@rollup/rollup-linux-ppc64-musl": "4.63.5", + "@rollup/rollup-linux-riscv64-gnu": "4.63.5", + "@rollup/rollup-linux-riscv64-musl": "4.63.5", + "@rollup/rollup-linux-s390x-gnu": "4.63.5", + "@rollup/rollup-linux-x64-gnu": "4.63.5", + "@rollup/rollup-linux-x64-musl": "4.63.5", + "@rollup/rollup-openbsd-x64": "4.63.5", + "@rollup/rollup-openharmony-arm64": "4.63.5", + "@rollup/rollup-win32-arm64-msvc": "4.63.5", + "@rollup/rollup-win32-ia32-msvc": "4.63.5", + "@rollup/rollup-win32-x64-gnu": "4.63.5", + "@rollup/rollup-win32-x64-msvc": "4.63.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/ws": { + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/client/typescript/package.json b/client/typescript/package.json new file mode 100644 index 0000000..2eb6b1a --- /dev/null +++ b/client/typescript/package.json @@ -0,0 +1,21 @@ +{ + "name": "typescript-spreadsheet-collaborative-editing", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@microsoft/signalr": "^9.0.0", + "@syncfusion/ej2-collaborator": "35.1.37", + "@syncfusion/ej2-spreadsheet": "35.1.37", + "@syncfusion/ej2-tailwind3-theme": "35.1.37" + }, + "devDependencies": { + "typescript": "~5.9.3", + "vite": "^7.1.9" + } +} diff --git a/client/typescript/src/index.ts b/client/typescript/src/index.ts new file mode 100644 index 0000000..84b604d --- /dev/null +++ b/client/typescript/src/index.ts @@ -0,0 +1,89 @@ +import { + CollaborativeEditingHandler, + Spreadsheet +} from '@syncfusion/ej2-spreadsheet'; +import { CollaborationClient } from '@syncfusion/ej2-collaborator'; +import { SpreadsheetEditorAdapter } from './spreadsheet-editor-adapter'; +import './style.css'; + +const serviceUrl: string = + 'https://appservice-267100-e9c8cxaab6a2dfeu.centralindia-01.azurewebsites.net/'; +const currentUser: string = 'John'; + +Spreadsheet.Inject(CollaborativeEditingHandler); + +function getRoomName(): string { + const currentUrl: URL = new URL(window.location.href); + let roomName: string = ( + currentUrl.searchParams.get('id') || '' + ).trim(); + + if (!roomName) { + roomName = Math.random().toString(32).slice(2); + currentUrl.searchParams.set('id', roomName); + window.history.replaceState( + window.history.state, + '', + currentUrl.pathname + currentUrl.search + currentUrl.hash + ); + } + + return roomName; +} + +let adapter: SpreadsheetEditorAdapter | null = null; +let client: CollaborationClient | null = null; +let initialized: boolean = false; + +const spreadsheet: Spreadsheet = new Spreadsheet({ + height: '550px', + enableCollaborativeEditing: true, + created: async (): Promise => { + if (initialized) { + return; + } + + initialized = true; + const roomName: string = getRoomName(); + const spreadsheetAdapter = new SpreadsheetEditorAdapter( + spreadsheet, + serviceUrl, + currentUser + ); + + try { + await spreadsheetAdapter.loadFromServer('Sample', roomName); + + const collaborationClient = new CollaborationClient( + spreadsheetAdapter, + { + serviceUrl, + connectionType: 'signalr', + currentUser + } + ); + + adapter = spreadsheetAdapter; + client = collaborationClient; + await collaborationClient.joinRoomAsync(roomName); + + console.log( + '[Collaborative Editing] Joined room', + roomName + ); + } catch (error) { + initialized = false; + console.error( + '[Collaborative Editing] Failed to join the room.', + error + ); + } + }, + actionComplete: (args: unknown): void => { + adapter?.sendActionToServer(args); + } +}); + +spreadsheet.appendTo('#spreadsheet'); + +void client; diff --git a/client/typescript/src/spreadsheet-editor-adapter.ts b/client/typescript/src/spreadsheet-editor-adapter.ts new file mode 100644 index 0000000..16b703a --- /dev/null +++ b/client/typescript/src/spreadsheet-editor-adapter.ts @@ -0,0 +1,83 @@ +import type { + ICollaborationActionData, + ICollaborationProvider +} from '@syncfusion/ej2-collaborator'; +import type { Spreadsheet } from '@syncfusion/ej2-spreadsheet'; + +interface ImportFileResponse { + sfdt: string; + version: number; +} + +export class SpreadsheetEditorAdapter implements ICollaborationProvider { + public currentRoomName: string = ''; + + public constructor( + private spreadsheet: Spreadsheet, + private serviceUrl: string, + private currentUser: string + ) { + this.serviceUrl = serviceUrl.endsWith('/') + ? serviceUrl + : serviceUrl + '/'; + } + + public async loadFromServer( + fileName: string, + roomName: string + ): Promise { + const response: Response = await fetch( + this.serviceUrl + 'api/CollaborativeEditing/ImportFile', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ fileName, roomName }) + } + ); + + if (!response.ok) { + throw new Error('Failed to load the workbook.'); + } + + const data: ImportFileResponse = JSON.parse( + await response.text() + ); + + this.currentRoomName = roomName; + this.spreadsheet.collaborativeEditingModule.updateRoomInfo( + roomName, + data.version, + this.serviceUrl + 'api/CollaborativeEditing/' + ); + this.spreadsheet.collaborativeEditingModule.setLocalUser( + this.currentUser + ); + this.spreadsheet.openFromJson({ file: data.sfdt }); + } + + public sendActionToServer(action: unknown): void { + if (!action) { + return; + } + + this.spreadsheet.collaborativeEditingModule.sendActionToServer( + action + ); + } + + public applyRemoteAction( + action: string, + data: ICollaborationActionData + ): void { + if (!data) { + return; + } + + this.spreadsheet.collaborativeEditingModule.applyRemoteAction( + action, + data.payload + ); + } +} diff --git a/client/typescript/src/style.css b/client/typescript/src/style.css new file mode 100644 index 0000000..8a9a433 --- /dev/null +++ b/client/typescript/src/style.css @@ -0,0 +1,11 @@ +@import '@syncfusion/ej2-tailwind3-theme/styles/tailwind3.css'; + +html, +body { + height: 100%; + margin: 0; +} + +#spreadsheet { + width: 100%; +} diff --git a/client/typescript/tsconfig.json b/client/typescript/tsconfig.json new file mode 100644 index 0000000..4f2e02c --- /dev/null +++ b/client/typescript/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/client/vue/.gitignore b/client/vue/.gitignore new file mode 100644 index 0000000..cc9689e --- /dev/null +++ b/client/vue/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +.vscode/* +!.vscode/extensions.json +.DS_Store +*.local diff --git a/client/vue/README.md b/client/vue/README.md new file mode 100644 index 0000000..a6e56ad --- /dev/null +++ b/client/vue/README.md @@ -0,0 +1,78 @@ +# Vue Spreadsheet Collaborative Editing + +This example demonstrates real-time collaborative editing in the Syncfusion Vue Spreadsheet. Multiple users can edit the same workbook, and supported actions are synchronized through the Collaboration Server. + +## Prerequisites + +- Node.js +- Redis Server +- ASP.NET Core Redis Collaboration Server, or access to the configured hosted Collaboration Server + +## Install dependencies + +```bash +npm install +``` + +## Configure the Collaboration Server + +The Collaboration Server URL is defined in `src/App.vue`. Update `serviceUrl` when a different server endpoint is used. + +## Run the application + +```bash +npm run dev +``` + +Open the URL displayed by Vite, normally `http://localhost:5173/`. + +When the page opens without an `id` query parameter, the client generates a room ID and updates the URL. Open the complete URL in another browser tab or window to join the same collaboration room. + +Example: + +```text +http://localhost:5173/?id=sample-room +``` + +## Project structure + +```text +src/ +|-- App.vue +|-- SpreadsheetEditorAdapter.ts +|-- main.ts +|-- style.css +`-- vite-env.d.ts +``` + +## How it works + +1. The Vue Spreadsheet starts with collaborative editing enabled. +2. `CollaborativeEditingHandler` is provided to the Spreadsheet. +3. The client obtains or generates a room ID from the URL. +4. The adapter imports the latest workbook and room version from the server. +5. `CollaborationClient` connects through SignalR and joins the room. +6. Local Spreadsheet actions are sent from `actionComplete`. +7. Remote actions are applied through `SpreadsheetEditorAdapter.applyRemoteAction`. + +## Build + +```bash +npm run build +``` + +## Important packages + +- `@syncfusion/ej2-vue-spreadsheet` +- `@syncfusion/ej2-spreadsheet` +- `@syncfusion/ej2-collaborator` +- `@microsoft/signalr` +- `@syncfusion/ej2-tailwind3-theme` + +## Troubleshooting + +If clients do not synchronize, verify that the Collaboration Server is reachable, Redis is running, all clients use the same room ID, and the browser console has no SignalR or CORS errors. + +## License + +Refer to the license file in the parent repository. diff --git a/client/vue/index.html b/client/vue/index.html new file mode 100644 index 0000000..e074791 --- /dev/null +++ b/client/vue/index.html @@ -0,0 +1,12 @@ + + + + + + Vue Spreadsheet Collaborative Editing + + +
+ + + diff --git a/client/vue/package-lock.json b/client/vue/package-lock.json new file mode 100644 index 0000000..a438a9b --- /dev/null +++ b/client/vue/package-lock.json @@ -0,0 +1,2054 @@ +{ + "name": "vue-spreadsheet-collaborative-editing", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vue-spreadsheet-collaborative-editing", + "version": "1.0.0", + "dependencies": { + "@microsoft/signalr": "^9.0.0", + "@syncfusion/ej2-collaborator": "35.1.37", + "@syncfusion/ej2-spreadsheet": "35.1.37", + "@syncfusion/ej2-tailwind3-theme": "35.1.37", + "@syncfusion/ej2-vue-spreadsheet": "35.1.37", + "vue": "^3.5.22" + }, + "devDependencies": { + "@types/node": "^24.7.0", + "@vitejs/plugin-vue": "^6.0.1", + "@vue/tsconfig": "^0.8.1", + "typescript": "~5.9.3", + "vite": "^7.1.9", + "vue-tsc": "^3.1.1" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@microsoft/signalr": { + "version": "9.0.19", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-9.0.19.tgz", + "integrity": "sha512-hKDxt4nRXvkhhy+Etlhbg0UDQdhk8PsZKlQTnHyCn+bb+Q6gM8RCZLI43WsLH1IQCa7l/G8tX09em1F1LH/C1w==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "eventsource": "^2.0.2", + "fetch-cookie": "^2.0.3", + "node-fetch": "^2.6.7", + "ws": "^7.5.10" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.5.tgz", + "integrity": "sha512-J25QJU+B78T4FhhBsNpLJyVWOi31mwtpcMwywHmOKH65Q9IWGA81gPj+dnwlhU8wktVriYE+tFAaQgrnJRzAZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.5.tgz", + "integrity": "sha512-LDopB3zuZM5Ux9TT2luNEBJW/tYbGU2g1d+VpKk6I+gSKDb+/7sYE6M225gRQt4RbMX6MSwMsVR/phdjVUgRLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.5.tgz", + "integrity": "sha512-wlJEERGfeuHeBavCL2qVnNacOK43NDoZM4sjkeRPymd04OAE9T1zBqDJgmZ+CIsPTYKwdzpUC8vmOw84dwY4Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.5.tgz", + "integrity": "sha512-4nJJGg5jbo2wwPP4JP+LfEBA3bvP8rU9CLuhp7jWvq9sxEyhjQFTFdrqi+/dHEin/pd8jpT0vcehIpnZtmEdcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.5.tgz", + "integrity": "sha512-DrZbyCDF1hneuO6jRbvZ2D7+PIBM6yIwYnJpg2vIk58T+wuFpiaGZrfUr59lDWw45bg+IrpTGLPiNi/Fk4w3Cg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.5.tgz", + "integrity": "sha512-gqfUVMJMB3mehqywxp6hTBFfgtMQykZY19+cfiaYP0toIJLb/1DZRJHVkQQGP13W4TAwfZDWeg1qBcheTRioXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.5.tgz", + "integrity": "sha512-CFmhpvAwzSaWMlN3VN7UtmoTihlZNzoP0juQib5TQRnYUyDV8dXeWOp29sobWAT6gXl/hQgAClLlEiYozQG3OQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.5.tgz", + "integrity": "sha512-Uc9H8eXCOayV6JLTH5bXKMId6qbhNHa818/BgYjm4jrlq3vZquC9cqyvHBw17xy5Mnj5f+I3gFK5JcEf3hSqrw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.5.tgz", + "integrity": "sha512-VcPr/szv/1BFw112Kt//fxulXt/JPqzzidU84iW68L2DdjnOO8QFUv2zTSYBEPHD6movBD4z+bbr5y60GYM7Jw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.5.tgz", + "integrity": "sha512-BnxtJ5/91BrIHYIkGrmjz/lbMhqEHt1dPFqIxIFR+jPn0xVc/oUSCtIT089zfp5ufwGDlYz2UC+Fe1SRBpYFbQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.5.tgz", + "integrity": "sha512-LrYcHZwF+fAMNKHYTOQ5osWM4AZF7YF6D+XtsjDyEvljtt11twc+zHVXBLNEjxVSUnKYsOhvVz4Z213eW02COQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.5.tgz", + "integrity": "sha512-nj7QKQePAAUpCpJHtg0pR0W/b92A9NO17JS3BAQmHDn/yhmkir2p8llrKY9TOhleKIaSzy1JhxS3T9FVld6coA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.5.tgz", + "integrity": "sha512-5ylkX6dWMeBKge9nTU+Rxfb+ZfaCIJ9lRqIFaK0eAMcWp7OJbYnLveLgXmm0VrvuLKb8qIK+mHyH0qu88RM+iA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.5.tgz", + "integrity": "sha512-oHK4ZHYFDKjZviK34I+NwgfbGxgI7ztrNxj2hPTSSNFgeq1a/lEd7dHV2fdGAuTH4Iym3RHJg+vAbWaWG4B7Zg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.5.tgz", + "integrity": "sha512-UcetmHZ6XOXuUByiKZyQmb55ZPr0LABr3Ec/HB9wKZn6CEAFWZkE+hsJErJ9hbPBC7nI0dKuELx7CoV6IM7TMg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.5.tgz", + "integrity": "sha512-C5CmDPQBtvjVo8cgQsBs+w6WB0JLkiixhgi6hVLV11hERWdn/p0XcPU2OUcZzac9BPOFq7SbaHFa8r3SWEysCQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.5.tgz", + "integrity": "sha512-lHVQHJFKsuuxLMi3MQO9XVL8Tje3JR82CzB+QDKC5NWBcsIWuwsn9uIM5e3lBhI+fF1/s63qnyYqsg65+8rV/w==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.5.tgz", + "integrity": "sha512-3W9bTFcQNJn71cSJVM9RKIiZOy8DO/XLDii8Uv/Pm6WKqDRj7JV3ZfuXIEfyuy5LXpIzAbB/1M4Ukp9GKNa7nA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.5.tgz", + "integrity": "sha512-VDC7rRJlee/scpki96GZ27Omf6yU87s1YXwVTpjE5841faVlDYYT565rgfmoR1U0sqL7z5ivQSDjcsF6VRXyBA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.5.tgz", + "integrity": "sha512-z86Ok2p4pTdv5xqCKZsTooO7yBEiaJR/HzU3Wx8RmWsPoLppnMKROhJusQob8B3IE1ghC343kUW9rC2r+Wf3ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.5.tgz", + "integrity": "sha512-IzQmj+xXwQFGhMAMKMQVXkMwMZN3TqkJgAE0nSsqvVwWWciP4AIPMmWRqOQ2GfX7TUDZr+xqGFcBS36CRPGw0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.5.tgz", + "integrity": "sha512-F6qpTaPc9bwBH85kjy0/BLmLSW1uv7AoOXCoRIkg2arlgCYlWYcAbiMkvZuAcaWk9TpCRG//okznLAqLGshkMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.5.tgz", + "integrity": "sha512-igoDsTFhhwECBeGbUuLeIk7t8Y1apa+cs6mDWpx2EZ0ch7oEQgzHbFUXN9euoHekCAQzXdXApAGkV6jznS7tWw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.5.tgz", + "integrity": "sha512-U3teMeMbXFmaM5D+OTJpsOXd+wV/qftIeYF9kBKL4v73641qyJmoXFtA28DQLsnmlyayEsTe72xpLHrArq6vHw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.5.tgz", + "integrity": "sha512-ypfC34F3RKXvCXBglGqGMsUSMKlgwd1HX9AOAlx9RoZZ6GaI42YHVeKpzg3JG+wpBUJYTG+NNZhqbDWL8tBZkw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@syncfusion/ej2-base": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-35.1.37.tgz", + "integrity": "sha512-pg4WMFGbZJ9XoGr7ecBQ4+afWeVh1s0nckwqa1fFe1PK48Szg/gV5rEFXygFsHfratparBVAJogAd8WrWv3pYg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-icons": "~35.1.37" + }, + "bin": { + "syncfusion-license": "bin/syncfusion-license.js" + } + }, + "node_modules/@syncfusion/ej2-buttons": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-buttons/-/ej2-buttons-35.1.37.tgz", + "integrity": "sha512-tLYpbTqfPPNBqVZ8SmwM9yw+gAkBRhD0Rcl+1YcrG/8z43tVqWJdemtOlOItV/GGEQXXhDbX7PKgJs9m3CDkrg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-calculate": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-calculate/-/ej2-calculate-35.1.37.tgz", + "integrity": "sha512-q6Ogf+3vUcDZFOs7cdhFtXwrBmz555dSKnXNPe1QRdOhnZpOYIDYWcy0WlJ9E6zVk8SdvS3hMNKTmVP8DE1lxA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-calendars": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-calendars/-/ej2-calendars-35.1.37.tgz", + "integrity": "sha512-KLYnPjinjjpHU4I4hZwJKWuMm8AYPWnnT7R/kVYQ0fMz4Vt1XrNCcOyByTvSINTDvb4sZeF6a5VTRNL6/i7Xgw==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-lists": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-charts": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-charts/-/ej2-charts-35.1.37.tgz", + "integrity": "sha512-q9ShJ4K7ozYRPqSpENay95eoe4ZBftHogveYTRsJTAKXCrh7tbjxJ9doeKBN+pcdqIBqcK0ecXDw4vPyxRvEtA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-calendars": "~35.1.37", + "@syncfusion/ej2-data": "~35.1.37", + "@syncfusion/ej2-excel-export": "~35.1.37", + "@syncfusion/ej2-navigations": "~35.1.37", + "@syncfusion/ej2-pdf-export": "~35.1.37", + "@syncfusion/ej2-svg-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-collaborator": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-collaborator/-/ej2-collaborator-35.1.37.tgz", + "integrity": "sha512-Ff72o4M1yIw8VtjaiKyicHv7e5jr0jcL3Mak0yrvOCZoLyp4ucu5Zi5f+WHAy9qj4Tgu2WCuhzqRML8tx8G5gA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-compression": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-compression/-/ej2-compression-35.1.37.tgz", + "integrity": "sha512-Lz+5N1iahYLtmyznJ9O+xTq34Mfc7PXFJna28h7PGJ7YNkZxJDLi1KijypJHj+LZfi8NAQsn7MRMnTFfbHxBmQ==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-file-utils": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-data": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-data/-/ej2-data-35.1.37.tgz", + "integrity": "sha512-rjHvWvwOYWCvWhptPe9ldenOKZXPXFer0v2UrbJhSfJ1oC9NxCm+HX1MrfY46SStOz93s3X8XorF2LgQlXRyRA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-dropdowns": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-dropdowns/-/ej2-dropdowns-35.1.37.tgz", + "integrity": "sha512-StvzQPhuFZIQd53PAIWg7gsBiEKNMJuRueDgCNKTUoJo45fuHphShhb4RIwjnM1D6BJqQ1a/7oxeWQYxdaH/CQ==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-data": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-lists": "~35.1.37", + "@syncfusion/ej2-navigations": "~35.1.37", + "@syncfusion/ej2-notifications": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-excel-export": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-excel-export/-/ej2-excel-export-35.1.37.tgz", + "integrity": "sha512-oTjGCkiTpsx06EVbvwrwaTjORucMizuCb/AnfbjGoHVPGfZj74cchHQY+8rmY2uSRmz9nLQIGhc4Ip1JNcWDZg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-compression": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-file-utils": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-file-utils/-/ej2-file-utils-35.1.37.tgz", + "integrity": "sha512-+Psm/DeB0b8DJeo63OU997bSSSL8Lj6s7tnY0CFUb1nXHmJ6pxxGLapf6MbBJYFSD+as1IWk2kFHkmWI1hu5Xg==", + "license": "SEE LICENSE IN license" + }, + "node_modules/@syncfusion/ej2-grids": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-grids/-/ej2-grids-35.1.37.tgz", + "integrity": "sha512-4t3o3VsUoYCes+gIVsYTyb2jbADfMMbGmGbJdeg9D4/Vl7sfvMdM7cWAxOX87oGwxhua29hQw7+8DLZ2Wm1Agg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-calendars": "~35.1.37", + "@syncfusion/ej2-compression": "~35.1.37", + "@syncfusion/ej2-data": "~35.1.37", + "@syncfusion/ej2-dropdowns": "~35.1.37", + "@syncfusion/ej2-excel-export": "~35.1.37", + "@syncfusion/ej2-file-utils": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-lists": "~35.1.37", + "@syncfusion/ej2-navigations": "~35.1.37", + "@syncfusion/ej2-notifications": "~35.1.37", + "@syncfusion/ej2-pdf-export": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37", + "@syncfusion/ej2-querybuilder": "~35.1.37", + "@syncfusion/ej2-splitbuttons": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-icons": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-35.1.37.tgz", + "integrity": "sha512-D7LC3O2OI2Ugc/DLShHvhoEPBEZCT9hj49PjV30CxfJ8ZyVR+EWl/GC3b1q1xMKLiB7ZhDrXBoZgq5mJaeVZuw==", + "license": "SEE LICENSE IN license" + }, + "node_modules/@syncfusion/ej2-inputs": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-inputs/-/ej2-inputs-35.1.37.tgz", + "integrity": "sha512-P4NQI/MQrfAqnpMNuRsxB0GMIXXZ5EIPPIt/oZnq1gAzGbDj7HM4yz4rg6xtiae8t2nMr0QI4cggL2dDKzpfHQ==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37", + "@syncfusion/ej2-splitbuttons": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-interactive-chat": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-interactive-chat/-/ej2-interactive-chat-35.1.37.tgz", + "integrity": "sha512-oq+h77v2DlbQBblBAfT25DKoLUMtBKiFxrlkgpDq2QqTLIhcD0XlrOXO5FKlUsLTMpmV71pZjpyNWyWc4rH+/g==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-dropdowns": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-layouts": "~35.1.37", + "@syncfusion/ej2-markdown-converter": "~35.1.37", + "@syncfusion/ej2-navigations": "~35.1.37", + "@syncfusion/ej2-notifications": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37", + "@syncfusion/ej2-splitbuttons": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-layouts": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-layouts/-/ej2-layouts-35.1.37.tgz", + "integrity": "sha512-5yj54DPYAKLjuIA+pO3IaZryGWbqAfXRZZuyOb35cjOFHoDDeAoQ9x0ocigxUYFv/IOx/6JW3M79Fb3hx7XA8g==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-lists": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-lists/-/ej2-lists-35.1.37.tgz", + "integrity": "sha512-GA5cnNXA+Kpme2nDAxld7x88T1YnoMDvHidTk2CJj/Dp3mwvB2/FScaGxYjidES1Hev0XWvJtgnGPdhI95gE2Q==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-data": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-markdown-converter": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-markdown-converter/-/ej2-markdown-converter-35.1.37.tgz", + "integrity": "sha512-fBtN3+1ACdXIYbzAX35gDcOTtR5VCdjW2QSWaLpO3jhh+NntOrhfM82ntxI+TOdyZYOvtNxt/pQjIp2K/UOGBA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-navigations": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-navigations/-/ej2-navigations-35.1.37.tgz", + "integrity": "sha512-jqpVXZimfqfIPzCf9I7KPB47hVdiouYDnmEHIpayekQlSdMq2qZQJy59sqb8genYL4OR/KnEFJlcIp1mdhJ8Kw==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-data": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-lists": "~35.1.37", + "@syncfusion/ej2-notifications": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-notifications": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-notifications/-/ej2-notifications-35.1.37.tgz", + "integrity": "sha512-UyjQ2Ykx8N1YrikYxrMaK9F7x/u4Ea4ZdCO8rL07binfF+lDEFlPpMoLbiQYatwb8gN++d5UbcyQXs/sYLIYfg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-pdf-export": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-pdf-export/-/ej2-pdf-export-35.1.37.tgz", + "integrity": "sha512-9+2GJBe0vjs0CuIBgQQd7GcQ2CaxCh//OqNlnbGvPO4dfagEj0TUoKpbhIK9Qs5+l1KHvpSgPhQ80LuZBfyHhw==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-compression": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-popups": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-popups/-/ej2-popups-35.1.37.tgz", + "integrity": "sha512-HC3x/sR07YDDc/eS/iZrN0SyJkuAXDS1uRABeN6UdPdkp2o7IOkSOCAyAURMY/dHqpCDblhsnE0LXZ6HwlcQaQ==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-querybuilder": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-querybuilder/-/ej2-querybuilder-35.1.37.tgz", + "integrity": "sha512-h2xUbOt+I88ICtWRhcMrmizfX4fXU9ZfSc2cR/Umn5vSoMJE52AEAQ9iamRiMTJUFAwOyINpdfabr1pO7xngKg==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-buttons": "~35.1.37", + "@syncfusion/ej2-calendars": "~35.1.37", + "@syncfusion/ej2-dropdowns": "~35.1.37", + "@syncfusion/ej2-inputs": "~35.1.37", + "@syncfusion/ej2-splitbuttons": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-splitbuttons": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-splitbuttons/-/ej2-splitbuttons-35.1.37.tgz", + "integrity": "sha512-whjKUf7p9grTtDxssx4s2u9gYDysiRHBn658dj6oRMKoYAECdaw5H/WEIcqKoxA6B1hJyUHZ8GE1SbWdV7eSVw==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-popups": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-spreadsheet": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-spreadsheet/-/ej2-spreadsheet-35.1.37.tgz", + "integrity": "sha512-TEM28i5E17uFG37HpYLaRhpjiyq/k4KxQG6MJMTn3JroZtM2+BUvki6l847xhCdPt1Jrvg8slc0uKI28Cox3XA==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-calculate": "~35.1.37", + "@syncfusion/ej2-charts": "~35.1.37", + "@syncfusion/ej2-dropdowns": "~35.1.37", + "@syncfusion/ej2-grids": "~35.1.37", + "@syncfusion/ej2-interactive-chat": "~35.1.37", + "@syncfusion/ej2-navigations": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-svg-base": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-svg-base/-/ej2-svg-base-35.1.37.tgz", + "integrity": "sha512-jgTjelQSmDHETQfXaecRL92tw/i63ijnACZEwp97gDl+bbY8Nc/PjTvaHNjECjuOL2Nt83q4z77OnGjE2zmNJQ==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-tailwind3-theme": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-tailwind3-theme/-/ej2-tailwind3-theme-35.1.37.tgz", + "integrity": "sha512-1Qujjxkeo4pH58CcBHytkqwTuZZRlFeoEfdzyfkvZGjtcg9dH2sOMYVwz6GVs4dRrtm/KJkqeBXMToCMR6bT2g==", + "license": "SEE LICENSE IN license" + }, + "node_modules/@syncfusion/ej2-vue-base": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-vue-base/-/ej2-vue-base-35.1.37.tgz", + "integrity": "sha512-/S1JqCYTD4VS4vtmDr0HgZCV7IWCTw2O2TdTwRfCMHUbtIZ8QSPg0CRjcPFgH4cXtCgUgGRbMr6sSOButl2Pug==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37" + } + }, + "node_modules/@syncfusion/ej2-vue-spreadsheet": { + "version": "35.1.37", + "resolved": "https://registry.npmjs.org/@syncfusion/ej2-vue-spreadsheet/-/ej2-vue-spreadsheet-35.1.37.tgz", + "integrity": "sha512-TZlBx1qAf0t89djagCozYyEVnhq81PeVQtnRgmnCJ7pUo7+OJCMZOXJRilno6SNSK6XkhGm1ryZ0wRL8GTamew==", + "license": "SEE LICENSE IN license", + "dependencies": { + "@syncfusion/ej2-base": "~35.1.37", + "@syncfusion/ej2-spreadsheet": "35.1.37", + "@syncfusion/ej2-vue-base": "~35.1.37" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.19.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.0.tgz", + "integrity": "sha512-zY+5tKxXdhGh1PYI0ac+7juvEu4OI6vWtVVoj5i2m42jxAY1U+zHGt6QCyOFwykdP62sM3MJ9stoYYUw5aCWew==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.9.tgz", + "integrity": "sha512-rD/MORlhaZMlXWW0rEn4FB4wMinWC0z/D7Ye160S5+Rs1mC8ZDAdcDp4SjKfUhG5t8q1ktcPVw4xuTkPRlzrSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.43.tgz", + "integrity": "sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.43", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.43.tgz", + "integrity": "sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.43.tgz", + "integrity": "sha512-FCbrG3XNCRl+js3huuKx4IVHBLTvMkJhVepjbxSPu1gn4yWLaYtGNQdjJGZaMytXB6qb76qQDDmDSLy/vkmleQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.43", + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-ssr": "3.5.43", + "@vue/shared": "3.5.43", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.28", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.43.tgz", + "integrity": "sha512-GF62orf7KiJX9RqrHNGrYBudsQGD0OhJ5nDs90O8UiDuS40+YMYomiXu6w6EuvtXuRDc3MSNis3EaaSKAVSWpg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/language-core": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.11.tgz", + "integrity": "sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.2.1", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.4" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.43.tgz", + "integrity": "sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.43.tgz", + "integrity": "sha512-hU6U6VnVhBGQDpvlnnDlIB8ZGJBiOcgk2lh/0InltHiz3D8oSkluvuvY+do1G2H3+udeKFsmaBlgVYP7gXQzEw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.43.tgz", + "integrity": "sha512-Bb2Jc0YjjJdMt1SJmb9b2L/IWd3I8lIT9x9eS/xvvP9CiVgna0ffua74xKRmt4/uSJ+0r4iN8ex1jrqkhQGWQw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.43", + "@vue/runtime-core": "3.5.43", + "@vue/shared": "3.5.43", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.43.tgz", + "integrity": "sha512-l2Ygjv9NehV94PSBxNWsAHC0j/eIIKbn92mBuWXAPGLnn6HfJ6MH5ubsd+Nk0YoZ5FRuxWI1P2VSoh+dbPQhCQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.43.tgz", + "integrity": "sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.8.1.tgz", + "integrity": "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/alien-signals": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-cookie": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.2.0.tgz", + "integrity": "sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==", + "license": "Unlicense", + "dependencies": { + "set-cookie-parser": "^2.4.8", + "tough-cookie": "^4.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.5.tgz", + "integrity": "sha512-KRWwmNLlPw5M7HcdYfm15oBv9n9LPtjzpzCIxS/phwqvPyxHSoKX6Y2YU3pxSPfy0CLquVgsx/j/hBi6OvH1Nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.5", + "@rollup/rollup-android-arm64": "4.63.5", + "@rollup/rollup-darwin-arm64": "4.63.5", + "@rollup/rollup-darwin-x64": "4.63.5", + "@rollup/rollup-freebsd-arm64": "4.63.5", + "@rollup/rollup-freebsd-x64": "4.63.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.5", + "@rollup/rollup-linux-arm-musleabihf": "4.63.5", + "@rollup/rollup-linux-arm64-gnu": "4.63.5", + "@rollup/rollup-linux-arm64-musl": "4.63.5", + "@rollup/rollup-linux-loong64-gnu": "4.63.5", + "@rollup/rollup-linux-loong64-musl": "4.63.5", + "@rollup/rollup-linux-ppc64-gnu": "4.63.5", + "@rollup/rollup-linux-ppc64-musl": "4.63.5", + "@rollup/rollup-linux-riscv64-gnu": "4.63.5", + "@rollup/rollup-linux-riscv64-musl": "4.63.5", + "@rollup/rollup-linux-s390x-gnu": "4.63.5", + "@rollup/rollup-linux-x64-gnu": "4.63.5", + "@rollup/rollup-linux-x64-musl": "4.63.5", + "@rollup/rollup-openbsd-x64": "4.63.5", + "@rollup/rollup-openharmony-arm64": "4.63.5", + "@rollup/rollup-win32-arm64-msvc": "4.63.5", + "@rollup/rollup-win32-ia32-msvc": "4.63.5", + "@rollup/rollup-win32-x64-gnu": "4.63.5", + "@rollup/rollup-win32-x64-msvc": "4.63.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.43.tgz", + "integrity": "sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-sfc": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/server-renderer": "3.5.43", + "@vue/shared": "3.5.43" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-tsc": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.11.tgz", + "integrity": "sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.3.11" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/ws": { + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/client/vue/package.json b/client/vue/package.json new file mode 100644 index 0000000..282e3ef --- /dev/null +++ b/client/vue/package.json @@ -0,0 +1,27 @@ +{ + "name": "vue-spreadsheet-collaborative-editing", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@microsoft/signalr": "^9.0.0", + "@syncfusion/ej2-collaborator": "35.1.37", + "@syncfusion/ej2-spreadsheet": "35.1.37", + "@syncfusion/ej2-tailwind3-theme": "35.1.37", + "@syncfusion/ej2-vue-spreadsheet": "35.1.37", + "vue": "^3.5.22" + }, + "devDependencies": { + "@types/node": "^24.7.0", + "@vitejs/plugin-vue": "^6.0.1", + "@vue/tsconfig": "^0.8.1", + "typescript": "~5.9.3", + "vite": "^7.1.9", + "vue-tsc": "^3.1.1" + } +} diff --git a/client/vue/src/App.vue b/client/vue/src/App.vue new file mode 100644 index 0000000..6c04bbf --- /dev/null +++ b/client/vue/src/App.vue @@ -0,0 +1,102 @@ + + + diff --git a/client/vue/src/SpreadsheetEditorAdapter.ts b/client/vue/src/SpreadsheetEditorAdapter.ts new file mode 100644 index 0000000..16b703a --- /dev/null +++ b/client/vue/src/SpreadsheetEditorAdapter.ts @@ -0,0 +1,83 @@ +import type { + ICollaborationActionData, + ICollaborationProvider +} from '@syncfusion/ej2-collaborator'; +import type { Spreadsheet } from '@syncfusion/ej2-spreadsheet'; + +interface ImportFileResponse { + sfdt: string; + version: number; +} + +export class SpreadsheetEditorAdapter implements ICollaborationProvider { + public currentRoomName: string = ''; + + public constructor( + private spreadsheet: Spreadsheet, + private serviceUrl: string, + private currentUser: string + ) { + this.serviceUrl = serviceUrl.endsWith('/') + ? serviceUrl + : serviceUrl + '/'; + } + + public async loadFromServer( + fileName: string, + roomName: string + ): Promise { + const response: Response = await fetch( + this.serviceUrl + 'api/CollaborativeEditing/ImportFile', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ fileName, roomName }) + } + ); + + if (!response.ok) { + throw new Error('Failed to load the workbook.'); + } + + const data: ImportFileResponse = JSON.parse( + await response.text() + ); + + this.currentRoomName = roomName; + this.spreadsheet.collaborativeEditingModule.updateRoomInfo( + roomName, + data.version, + this.serviceUrl + 'api/CollaborativeEditing/' + ); + this.spreadsheet.collaborativeEditingModule.setLocalUser( + this.currentUser + ); + this.spreadsheet.openFromJson({ file: data.sfdt }); + } + + public sendActionToServer(action: unknown): void { + if (!action) { + return; + } + + this.spreadsheet.collaborativeEditingModule.sendActionToServer( + action + ); + } + + public applyRemoteAction( + action: string, + data: ICollaborationActionData + ): void { + if (!data) { + return; + } + + this.spreadsheet.collaborativeEditingModule.applyRemoteAction( + action, + data.payload + ); + } +} diff --git a/client/vue/src/main.ts b/client/vue/src/main.ts new file mode 100644 index 0000000..8dd6bc1 --- /dev/null +++ b/client/vue/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue'; +import App from './App.vue'; +import './style.css'; + +createApp(App).mount('#app'); diff --git a/client/vue/src/style.css b/client/vue/src/style.css new file mode 100644 index 0000000..997bf1a --- /dev/null +++ b/client/vue/src/style.css @@ -0,0 +1,8 @@ +@import '@syncfusion/ej2-tailwind3-theme/styles/tailwind3.css'; + +html, +body, +#app { + height: 100%; + margin: 0; +} diff --git a/client/vue/src/vite-env.d.ts b/client/vue/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/client/vue/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/client/vue/tsconfig.app.json b/client/vue/tsconfig.app.json new file mode 100644 index 0000000..7357af2 --- /dev/null +++ b/client/vue/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "strict": true, + "noEmit": true + } +} diff --git a/client/vue/tsconfig.json b/client/vue/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/client/vue/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/client/vue/tsconfig.node.json b/client/vue/tsconfig.node.json new file mode 100644 index 0000000..3950b4b --- /dev/null +++ b/client/vue/tsconfig.node.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/client/vue/vite.config.ts b/client/vue/vite.config.ts new file mode 100644 index 0000000..0a4f24e --- /dev/null +++ b/client/vue/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; + +export default defineConfig({ + plugins: [vue()] +});