diff --git a/.github/workflows/fmtk.yml b/.github/workflows/fmtk.yml index e2b17f96b8..aa72d9af28 100644 --- a/.github/workflows/fmtk.yml +++ b/.github/workflows/fmtk.yml @@ -15,7 +15,6 @@ jobs: - uses: actions/setup-node@v4 with: node-version: lts/* - cache: "pnpm" - name: Install FMTK run: | pnpm install factoriomod-debug diff --git a/exp_commands/package.json b/exp_commands/package.json index 1bcf6c28be..bd7e0a20c4 100644 --- a/exp_commands/package.json +++ b/exp_commands/package.json @@ -13,10 +13,10 @@ "node": ">=18" }, "peerDependencies": { - "@clusterio/lib": "catalog:" + "@clusterio/lib": "workspace:^" }, "devDependencies": { - "@clusterio/lib": "catalog:", + "@clusterio/lib": "workspace:^", "@types/node": "catalog:", "typescript": "catalog:", "webpack": "catalog:", diff --git a/exp_groups/controller.ts b/exp_groups/controller.ts index dde5515c8b..6f94b2c036 100644 --- a/exp_groups/controller.ts +++ b/exp_groups/controller.ts @@ -1,282 +1,434 @@ +import { BaseControllerPlugin } from "@clusterio/controller"; import * as lib from "@clusterio/lib"; -import { BaseControllerPlugin, InstanceInfo } from "@clusterio/controller"; +import * as messages from "./messages"; +import * as path from "node:path"; -import { - PermissionStrings, PermissionStringsUpdate, - PermissionGroup, PermissionGroupUpdate, - InstancePermissionGroups, - PermissionInstanceId, - PermissionGroupEditEvent, -} from "./messages"; +export class ControllerPlugin extends BaseControllerPlugin { + groups!: lib.SubscribableDatastore; + roleMappings!: lib.SubscribableDatastore; + manualAssignments!: lib.SubscribableDatastore; + resolvedAssignments!: lib.SubscribableDatastore; -import path from "path"; -import fs from "fs-extra"; + async init() { + const databaseDirectory = this.controller.config.get("controller.database_directory"); -export class ControllerPlugin extends BaseControllerPlugin { - static permissionGroupsPath = "exp_groups.json"; - static userGroupsPath = "exp_user_groups.json"; - - userToGroup: Map = new Map(); // TODO this needs to be per instance - permissionStrings!: Map; - permissionGroups!: Map; - - async init() { - this.controller.handle(PermissionStringsUpdate, this.handlePermissionStringsUpdate.bind(this)); - this.controller.handle(PermissionGroupUpdate, this.handlePermissionGroupUpdate.bind(this)); - this.controller.handle(PermissionGroupEditEvent, this.handlePermissionGroupEditEvent.bind(this)); - this.controller.subscriptions.handle(PermissionStringsUpdate, this.handlePermissionStringsSubscription.bind(this)); - this.controller.subscriptions.handle(PermissionGroupUpdate, this.handlePermissionGroupSubscription.bind(this)); - this.controller.subscriptions.handle(PermissionGroupEditEvent); - this.permissionStrings = new Map([["Global", new PermissionStrings("Global", new Set())]]); - this.permissionGroups = new Map([["Global", new InstancePermissionGroups("Global")]]); - await this.loadData(); - - // Add the default group if missing and add any missing cluster roles - const clusterRoles = [...this.controller.userManager.roles.values()] - for (const instanceGroups of this.permissionGroups.values()) { - const groups = instanceGroups.groups; - const instanceRoles = [...groups.values()].flatMap(group => [...group.roleIds.values()]); - const missingRoles = clusterRoles.filter(role => instanceRoles.includes(role.id)); - const defaultGroup = groups.get("Default"); - if (defaultGroup) { - for (const role of missingRoles) { - defaultGroup.roleIds.add(role.id) - } - } else { - groups.set("Default", new PermissionGroup( - instanceGroups.instanceId, - "Default", - groups.size, - new Set(missingRoles.map(role => role.id)) - )); - } - } - } - - async onControllerConfigFieldChanged(field: string, curr: unknown, prev: unknown) { - if (field === "exp_groups.allow_role_inconsistency") { - // Do something with this.userToGroup - } - } - - async onInstanceConfigFieldChanged(instance: InstanceInfo, field: string, curr: unknown, prev: unknown) { - this.logger.info(`controller::onInstanceConfigFieldChanged ${instance.id} ${field}`); - if (field === "exp_groups.sync_permission_groups") { - const updates = [] - const now = Date.now(); - if (curr) { - // Global sync enabled, we dont need the instance config - const instanceGroups = this.permissionGroups.get(instance.id); - if (instanceGroups) { - this.permissionGroups.delete(instance.id); - for (const group of instanceGroups.groups.values()) { - group.updatedAtMs = now; - group.isDeleted = true; - updates.push(group); - } - } - } else { - // Global sync disabled, make a copy of the global config as a base - const global = this.permissionGroups.get("Global")!; - const oldInstanceGroups = this.permissionGroups.get(instance.id); - const instanceGroups = new InstancePermissionGroups( - instance.id, new Map([...global.groups.values()].map(group => [group.name, group.copy(instance.id)])) - ) - this.permissionGroups.set(instance.id, instanceGroups); - for (const group of instanceGroups.groups.values()) { - group.updatedAtMs = now; - updates.push(group); - } - // If it has an old config (unexpected) then deal with it - if (oldInstanceGroups) { - for (const group of oldInstanceGroups.groups.values()) { - if (!instanceGroups.groups.has(group.name)) { - group.updatedAtMs = now; - group.isDeleted = true; - updates.push(group); - } - } - } - } - // Send the updates to all instances and controls - if (updates.length) { - this.controller.subscriptions.broadcast(new PermissionGroupUpdate(updates)); - } - } - } - - async loadPermissionGroups() { - const file = path.resolve(this.controller.config.get("controller.database_directory"), ControllerPlugin.permissionGroupsPath); - this.logger.verbose(`Loading ${file}`); - try { - const content = await fs.readFile(file, { encoding: "utf8" }); - for (const groupRaw of JSON.parse(content)) { - const group = PermissionGroup.fromJSON(groupRaw); - const instanceGroups = this.permissionGroups.get(group.instanceId); - if (instanceGroups) { - instanceGroups.groups.set(group.name, group); - } else { - this.permissionGroups.set(group.instanceId, - new InstancePermissionGroups(group.instanceId, new Map([[group.name, group]])) - ); - } - }; - - } catch (err: any) { - if (err.code === "ENOENT") { - this.logger.verbose("Creating new permission group database"); - return; - } - throw err; - } - } - - async savePermissionGroups() { - const file = path.resolve(this.controller.config.get("controller.database_directory"), ControllerPlugin.permissionGroupsPath); - this.logger.verbose(`Writing ${file}`); - await lib.safeOutputFile(file, JSON.stringify( - [...this.permissionGroups.values()].flatMap(instanceGroups => [...instanceGroups.groups.values()]) - )); - } - - async loadUserGroups() { - if (!this.controller.config.get("exp_groups.allow_role_inconsistency")) return; - const file = path.resolve(this.controller.config.get("controller.database_directory"), ControllerPlugin.userGroupsPath); - this.logger.verbose(`Loading ${file}`); - try { - const content = await fs.readFile(file, { encoding: "utf8" }); - this.userToGroup = new Map(JSON.parse(content)); - - } catch (err: any) { - if (err.code === "ENOENT") { - this.logger.verbose("Creating new user group database"); - return; - } - throw err; - } - } - - async saveUserGroups() { - if (!this.controller.config.get("exp_groups.allow_role_inconsistency")) return; - const file = path.resolve(this.controller.config.get("controller.database_directory"), ControllerPlugin.userGroupsPath); - this.logger.verbose(`Writing ${file}`); - await lib.safeOutputFile(file, JSON.stringify([...this.permissionGroups.entries()])); - } - - async loadData() { - await Promise.all([ - this.loadPermissionGroups(), - this.loadUserGroups(), - ]) - } - - async onSaveData() { - await Promise.all([ - this.savePermissionGroups(), - this.saveUserGroups(), - ]) - } - - addPermisisonGroup(instanceId: PermissionInstanceId, name: string, permissions = new Set(), silent = false) { - const instanceGroups = this.permissionGroups.get(instanceId); - if (!instanceGroups) { - throw new Error("Instance ID does not exist"); - } - if (instanceGroups.groups.has(name)) { - return instanceGroups.groups.get(name)!; - } - for (const group of instanceGroups.groups.values()) { - group.order += 1; - } - const group = new PermissionGroup(instanceId, name, 0, new Set(), permissions, Date.now(), false); - instanceGroups.groups.set(group.id, group); - if (!silent) { - this.controller.subscriptions.broadcast(new PermissionGroupUpdate([group])); - } - return group; - } - - removePermissionGroup(instanceId: PermissionInstanceId, name: string, silent = false) { - const instanceGroups = this.permissionGroups.get(instanceId); - if (!instanceGroups) { - throw new Error("Instance ID does not exist"); - } - const group = instanceGroups.groups.get(name) - if (!group) { - return null; - } - for (const nextGroup of instanceGroups.groups.values()) { - if (nextGroup.order > group.order) { - nextGroup.order -= 1; - } - } - instanceGroups.groups.delete(group.id); - group.updatedAtMs = Date.now(); - group.isDeleted = true; - if (!silent) { - this.controller.subscriptions.broadcast(new PermissionGroupUpdate([group])); - } - return group; - } - - async handlePermissionGroupEditEvent(event: PermissionGroupEditEvent) { - // TODO - } - - async handlePermissionStringsUpdate(event: PermissionStringsUpdate) { - for (const update of event.updates) { - const global = this.permissionStrings.get("Global")! - this.permissionStrings.set(update.instanceId as number, update) - global.updatedAtMs = Math.max(global.updatedAtMs, update.updatedAtMs) - for (const permission of update.permissions) { - global.permissions.add(permission) - } - // TODO maybe check if changes have happened rather than always pushing updates - this.controller.subscriptions.broadcast(new PermissionStringsUpdate([global, update])) - } - } - - async handlePermissionGroupUpdate(event: PermissionGroupUpdate) { - const updates = []; - for (const group of event.updates) { - const groups = this.permissionGroups.get(group.instanceId); - if (!groups) continue; - const existingGroup = groups.groups.get(group.id); - let update - if (!existingGroup) { - update = this.addPermisisonGroup(group.instanceId, group.name, group.permissions, true); - } else if (group.isDeleted) { - update = this.removePermissionGroup(group.instanceId, group.name, true); - } else { - existingGroup.permissions = group.permissions; - existingGroup.updatedAtMs = Date.now(); - update = existingGroup; - } - if (update) updates.push(update); - } - this.controller.subscriptions.broadcast(new PermissionGroupUpdate(updates)); - } - - async handlePermissionStringsSubscription(request: lib.SubscriptionRequest, src: lib.Address) { - const updates = [ ...this.permissionStrings.values() ] - .filter( - value => value.updatedAtMs > request.lastRequestTimeMs, - ) - return updates.length ? new PermissionStringsUpdate(updates) : null; - } - - async handlePermissionGroupSubscription(request: lib.SubscriptionRequest, src: lib.Address) { - const updates = [ ...this.permissionGroups.values() ] - .flatMap(instanceGroups => [...instanceGroups.groups.values()]) - .filter( - value => value.updatedAtMs > request.lastRequestTimeMs, - ) - if (src.type === lib.Address.instance) { - const instanceUpdates = updates.filter(group => group.instanceId === src.id || group.instanceId === "Global"); - this.logger.info(JSON.stringify(updates)) - this.logger.info(JSON.stringify(instanceUpdates)) - return instanceUpdates.length ? new PermissionGroupUpdate(instanceUpdates) : null; - } - return updates.length ? new PermissionGroupUpdate(updates) : null; - } + this.groups = new lib.SubscribableDatastore( + ...await new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_groups", "groups.json"), + messages.GroupRecord.fromJSON.bind(messages.GroupRecord), + ).bootstrap() + ); + + this.roleMappings = new lib.SubscribableDatastore( + ...await new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_groups", "role_mappings.json"), + messages.RoleMappingRecord.fromJSON.bind(messages.RoleMappingRecord), + ).bootstrap() + ); + + this.manualAssignments = new lib.SubscribableDatastore( + ...await new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_groups", "assignments.json"), + messages.AssignmentRecord.fromJSON.bind(messages.AssignmentRecord), + ).bootstrap() + ); + + this.resolvedAssignments = new lib.SubscribableDatastore(); + + this.controller.subscriptions.handle(messages.GroupUpdatedEvent, this.handleGroupSubscription.bind(this)); + this.controller.subscriptions.handle(messages.RoleMappingUpdatedEvent, this.handleRoleMappingSubscription.bind(this)); + this.controller.subscriptions.handle(messages.ManualAssignmentUpdatedEvent, this.handleManualAssignmentSubscription.bind(this)); + this.controller.subscriptions.handle(messages.ResolvedAssignmentUpdatedEvent, this.handleResolvedAssignmentSubscription.bind(this)); + + this.groups.on("update", this.groupsUpdated.bind(this)); + this.roleMappings.on("update", this.roleMappingsUpdated.bind(this)); + this.manualAssignments.on("update", this.manualAssignmentsUpdated.bind(this)); + this.resolvedAssignments.on("update", this.resolvedAssignmentsUpdated.bind(this)); + + this.controller.handle(messages.GroupCreateRequest, this.handleGroupCreateRequest.bind(this)); + this.controller.handle(messages.GroupUpdateRequest, this.handleGroupUpdateRequest.bind(this)); + this.controller.handle(messages.GroupDeleteRequest, this.handleGroupDeleteRequest.bind(this)); + this.controller.handle(messages.GroupGetRequest, this.handleGroupGetRequest.bind(this)); + this.controller.handle(messages.GroupListRequest, this.handleGroupListRequest.bind(this)); + + this.controller.handle(messages.AssignmentCreateRequest, this.handleAssignmentCreateRequest.bind(this)); + this.controller.handle(messages.AssignmentUpdateRequest, this.handleAssignmentUpdateRequest.bind(this)); + this.controller.handle(messages.AssignmentDeleteRequest, this.handleAssignmentDeleteRequest.bind(this)); + this.controller.handle(messages.AssignmentGetRequest, this.handleAssignmentGetRequest.bind(this)); + this.controller.handle(messages.AssignmentListRequest, this.handleAssignmentListRequest.bind(this)); + + this.controller.handle(messages.RoleMappingCreateRequest, this.handleRoleMappingCreateRequest.bind(this)); + this.controller.handle(messages.RoleMappingUpdateRequest, this.handleRoleMappingUpdateRequest.bind(this)); + this.controller.handle(messages.RoleMappingDeleteRequest, this.handleRoleMappingDeleteRequest.bind(this)); + this.controller.handle(messages.RoleMappingGetRequest, this.handleRoleMappingGetRequest.bind(this)); + this.controller.handle(messages.RoleMappingListRequest, this.handleRoleMappingListRequest.bind(this)); + } + + async onShutdown() { + await Promise.all([ + this.groups.save(), + this.manualAssignments.save(), + this.roleMappings.save(), + ]) + } + + /* + Subscriptions + */ + + async groupsUpdated(groups: messages.GroupRecord[]) { + this.controller.subscriptions.broadcast(new messages.GroupUpdatedEvent(groups)); + + // We need to do extra work if the group was deleted + const deletedGroupIds = groups.filter(g => g.isDeleted).map(g => g.id); + if (!deletedGroupIds.length) { + return; + } + + // Cascade the delete down to affected role mappings + const mappingsToDelete = []; + for (const mapping of this.roleMappings.values()) { + if (deletedGroupIds.includes(mapping.groupId)) { + mappingsToDelete.push(mapping); + } + } + if (mappingsToDelete.length) { + this.roleMappings.deleteMany(mappingsToDelete); + } + + // Cascade the delete down to affected manual assignments + const affectedPlayers = new Set(); + const assignmentsToDelete = []; + for (const assignment of this.manualAssignments.values()) { + if (deletedGroupIds.includes(assignment.groupId)) { + assignmentsToDelete.push(assignment); + affectedPlayers.add(assignment.name); + } + } + if (assignmentsToDelete.length) { + this.manualAssignments.deleteMany(assignmentsToDelete); + } + + // Find all the affected players who were assigned to this group + for (const resolved of this.resolvedAssignments.values()) { + if (deletedGroupIds.includes(resolved.groupId)) { + affectedPlayers.add(resolved.name); + } + } + if (affectedPlayers.size) { + this.resolvedAssignments.setMany(await this.computeResolvedAssignments([...affectedPlayers])); + } + } + + async handleGroupSubscription(request: lib.SubscriptionRequest) { + const groups = [...this.groups.values()] + .filter(group => group.updatedAtMs > request.lastRequestTimeMs); + return groups.length ? new messages.GroupUpdatedEvent(groups) : null; + } + + async roleMappingsUpdated(roleMappings: messages.RoleMappingRecord[]) { + this.controller.subscriptions.broadcast(new messages.RoleMappingUpdatedEvent(roleMappings)); + + // Mappings pointing to a deleted group have already been handled + // But if any are active, then we still must recompute all assignments + let hasActiveGroup = false; + for (const roleMapping of roleMappings) { + const group = this.groups.get(roleMapping.groupId); + if (group && !group.isDeleted) { + hasActiveGroup = true; + break; + } + } + if (!hasActiveGroup) { + return; + } + + // Affected players are those without manual assignments + const affectedPlayers = []; + for (const resolved of this.resolvedAssignments.values()) { + if (!this.manualAssignments.has(resolved.name)) { + affectedPlayers.push(resolved.name); + } + } + if (affectedPlayers.length) { + this.resolvedAssignments.setMany(await this.computeResolvedAssignments(affectedPlayers)); + } + } + + async handleRoleMappingSubscription(request: lib.SubscriptionRequest) { + const mappings = [...this.roleMappings.values()] + .filter(mapping => mapping.updatedAtMs > request.lastRequestTimeMs); + return mappings.length ? new messages.RoleMappingUpdatedEvent(mappings) : null; + } + + async manualAssignmentsUpdated(assignments: messages.AssignmentRecord[]) { + this.controller.subscriptions.broadcast(new messages.ManualAssignmentUpdatedEvent(assignments)); + + // Assignments pointing to a deleted group have already been handled + const affectedPlayers: string[] = []; + for (const assignment of assignments) { + const group = this.groups.get(assignment.groupId); + if (!group || group.isDeleted) continue; + affectedPlayers.push(assignment.name); + } + if (affectedPlayers.length) { + this.resolvedAssignments.setMany(await this.computeResolvedAssignments(affectedPlayers)); + } + } + + async handleManualAssignmentSubscription(request: lib.SubscriptionRequest) { + const assignments = [...this.resolvedAssignments.values()] + .filter(a => a.updatedAtMs > request.lastRequestTimeMs); + return assignments.length ? new messages.ManualAssignmentUpdatedEvent(assignments) : null; + } + + resolvedAssignmentsUpdated(assignments: messages.AssignmentRecord[]) { + this.controller.subscriptions.broadcast( + new messages.ResolvedAssignmentUpdatedEvent(assignments), + assignments.map(assignment => assignment.name), + ); + } + + async handleResolvedAssignmentSubscription(request: lib.SubscriptionRequest) { + // Check for any missing assignments to be computed on demand + const filters = Array.isArray(request.filters) ? request.filters : [request.filters!]; + if (request.filters && filters.length) { + const missing = filters.filter(name => !this.resolvedAssignments.has(name)); + if (missing.length) { + this.resolvedAssignments.setMany(await this.computeResolvedAssignments(missing)); + } + } + + // Filter the assignments + const assignments = (filters.length + ? filters.map(name => this.resolvedAssignments.get(name)).filter(Boolean) + : [...this.resolvedAssignments.values()] + ).filter(a => a.updatedAtMs > request.lastRequestTimeMs); + + return assignments.length ? new messages.ResolvedAssignmentUpdatedEvent(assignments) : null; + } + + /* + Groups + */ + + async handleGroupListRequest() { + return [...this.groups.values()]; + } + + async handleGroupCreateRequest(request: messages.GroupCreateRequest) { + if ([...this.groups.values()].some(g => g.name === request.name)) { + throw new lib.RequestError(`Group with name '${request.name}' already exists`); + } + + let id = Math.random() * 2**31 | 0; + while (this.groups.has(id)) { + id = Math.random() * 2**31 | 0; + } + + const group = new messages.GroupRecord(id, request.name, request.permissions); + this.groups.set(group); + return group; + } + + async handleGroupUpdateRequest(request: messages.GroupUpdateRequest) { + const group = request.group; + if (group.id === undefined || !this.groups.has(group.id)) { + throw new lib.RequestError(`Group with ID ${group.id} does not exist`); + } + + this.groups.set(group); + } + + async handleGroupDeleteRequest(request: messages.GroupDeleteRequest) { + const { groupId } = request; + + const group = this.groups.getMutable(groupId); + if (!group) { + throw new lib.RequestError(`Group with ID ${groupId} does not exist`); + } + + this.groups.delete(group); + } + + async handleGroupGetRequest(request: messages.GroupGetRequest) { + const group = this.groups.get(request.groupId); + if (!group) { + throw new lib.RequestError(`Group with ID ${request.groupId} does not exist`); + } + + return group; + } + + /* + Groups + */ + + async handleAssignmentListRequest() { + return [...this.manualAssignments.values()]; + } + + async handleAssignmentCreateRequest(request: messages.AssignmentCreateRequest) { + const { name, groupId } = request; + if (this.manualAssignments.has(name)) { + throw new lib.RequestError(`Assignment for '${name}' already exists`); + } + + const assignment = new messages.AssignmentRecord(name, groupId); + this.manualAssignments.set(assignment); + return assignment; + } + + async handleAssignmentUpdateRequest(request: messages.AssignmentUpdateRequest) { + const assignment = request.assignment; + if (!this.manualAssignments.has(assignment.name)) { + throw new lib.RequestError(`Assignment for '${assignment.name}' does not exist`); + } + + this.manualAssignments.set(assignment); + } + + async handleAssignmentDeleteRequest(request: messages.AssignmentDeleteRequest) { + const { name } = request; + + const assignment = this.manualAssignments.getMutable(name); + if (!assignment) { + throw new lib.RequestError(`Assignment for '${name}' does not exist`); + } + + this.manualAssignments.delete(assignment); + } + + async handleAssignmentGetRequest(request: messages.AssignmentGetRequest) { + if (request.resolve) { + let assignment = this.resolvedAssignments.get(request.name); + if (!assignment) { + assignment = await this.computeResolvedAssignment(request.name); + this.resolvedAssignments.set(assignment); + } + + return assignment; + } + + const assignment = this.manualAssignments.get(request.name); + if (!assignment) { + throw new lib.RequestError(`Assignment for '${request.name}' does not exist`); + } + + return assignment; + } + + /* + Role mappings + */ + + async handleRoleMappingListRequest() { + return [...this.roleMappings.values()]; + } + + async handleRoleMappingCreateRequest(request: messages.RoleMappingCreateRequest) { + let id = Math.random() * 2**31 | 0; + while (this.roleMappings.has(id)) { + id = Math.random() * 2**31 | 0; + } + + let priority = request.priority; + const existing = new Set([...this.roleMappings.values()].map(m => m.priority)); + while (existing.has(priority)) { + priority++; + } + + const roleMapping = new messages.RoleMappingRecord( + id, new Set(request.roleIds), request.groupId, priority, request.enabled, + ); + + this.roleMappings.set(roleMapping); + return roleMapping; + } + + async handleRoleMappingUpdateRequest(request: messages.RoleMappingUpdateRequest) { + const roleMapping = request.roleMapping; + if (roleMapping.id === undefined || !this.roleMappings.has(roleMapping.id)) { + throw new lib.RequestError(`Role mapping with ID ${roleMapping.id} does not exist`); + } + + const existing = new Set( + [...this.roleMappings.values()] + .filter(m => m.id !== roleMapping.id) + .map(m => m.priority) + ); + + let priority = roleMapping.priority; + while (existing.has(priority)) { + priority++; + } + + this.roleMappings.set(roleMapping); + } + + async handleRoleMappingDeleteRequest(request: messages.RoleMappingDeleteRequest) { + const { id } = request; + + const mapping = this.roleMappings.getMutable(id); + if (!mapping) { + throw new lib.RequestError(`Role mapping with ID ${id} does not exist`); + } + + this.roleMappings.delete(mapping); + } + + async handleRoleMappingGetRequest(request: messages.RoleMappingGetRequest) { + const mapping = this.roleMappings.get(request.id); + if (!mapping) { + throw new lib.RequestError(`Role mapping with ID ${request.id} does not exist`); + } + + return mapping; + } + + /* + Calculating assignments + */ + + async computeResolvedAssignment(playerName: string): Promise { + // 1) Manual override + const manual = this.manualAssignments.get(playerName); + if (manual) { + return manual; + } + + const user = this.controller.users.getByName(playerName); + const userRoles = user?.roleIds ?? new Set(); + + // 2) Role mappings + let best: messages.RoleMappingRecord | null = null; + for (const mapping of this.roleMappings.values()) { + if (!mapping.enabled) continue; + + let matches = true; + for (const roleId of mapping.roleIds) { + if (!userRoles.has(roleId)) { + matches = false; + break; + } + } + + if (!matches) continue; + + if (!best || mapping.priority > best.priority) { + best = mapping; + } + } + + if (best) { + return new messages.AssignmentRecord(playerName, best.groupId); + } + + // 3) Default (deleted assignment, assigns to 'Default' in game) + return new messages.AssignmentRecord(playerName, 0, 0, true); + } + + async computeResolvedAssignments(playerNames: string[]): Promise { + return Promise.all(playerNames.map(name => this.computeResolvedAssignment(name))); + } } diff --git a/exp_groups/index.ts b/exp_groups/index.ts index f5e272b352..524ac7d914 100644 --- a/exp_groups/index.ts +++ b/exp_groups/index.ts @@ -1,84 +1,183 @@ import * as lib from "@clusterio/lib"; -import * as Messages from "./messages"; +import * as messages from "./messages"; + +declare module "@clusterio/lib" { + export interface InstanceConfigFields { + "exp_groups.sync_mode": "enabled" | "disabled" | "bidirectional" + } + export interface ControllerConfigFields { + } +} + +// Group permissions lib.definePermission({ - name: "exp_groups.create_delete_groups", - title: "Create and delete permission groups", - description: "Create and delete permission groups.", + name: "exp_groups.group.get", + title: "Get Groups", + description: "Retrieve a specific Factorio permission group by ID.", + grantByDefault: true, }); - lib.definePermission({ - name: "exp_groups.reorder_groups", - title: "Reorder permission groups", - description: "Reorder groups and link them to user roles.", + name: "exp_groups.group.list", + title: "List Groups", + description: "List all Factorio permission groups.", + grantByDefault: true, }); - lib.definePermission({ - name: "exp_groups.modify_permissions", - title: "Modify permission groups", - description: "Modify game permissions for groups.", + name: "exp_groups.group.subscribe", + title: "Subscribe to Group Updates", + description: "Receive updates when Factorio permission groups change.", + grantByDefault: true, }); - lib.definePermission({ - name: "exp_groups.assign_players", - title: "Change player group", - description: "Change the permission group of a player", + name: "exp_groups.group.create", + title: "Create Groups", + description: "Create new Factorio permission groups.", + grantByDefault: false, }); - lib.definePermission({ - name: "exp_groups.list", - title: "View permission groups", - description: "View permission groups.", + name: "exp_groups.group.update", + title: "Update Groups", + description: "Modify existing Factorio permission groups.", + grantByDefault: false, +}); +lib.definePermission({ + name: "exp_groups.group.delete", + title: "Delete Groups", + description: "Delete Factorio permission groups.", + grantByDefault: false, }); +// Assignment permissions + lib.definePermission({ - name: "exp_groups.list.subscribe", - title: "Subscribe to permission group updates", - description: "Subscribe to permission group updates.", + name: "exp_groups.assignment.get", + title: "Get Assignments", + description: "Retrieve a specific manual group assignment for a player.", + grantByDefault: true, +}); +lib.definePermission({ + name: "exp_groups.assignment.list", + title: "List Assignments", + description: "List all manual group assignments.", + grantByDefault: true, +}); +lib.definePermission({ + name: "exp_groups.assignment.subscribe", + title: "Subscribe to Assignment Updates", + description: "Receive updates when manual group assignments change.", + grantByDefault: true, +}); +lib.definePermission({ + name: "exp_groups.assignment.create", + title: "Create Assignments", + description: "Manually assign players to groups, overriding role mappings.", + grantByDefault: false, +}); +lib.definePermission({ + name: "exp_groups.assignment.update", + title: "Update Assignments", + description: "Modify existing manual group assignments.", + grantByDefault: false, +}); +lib.definePermission({ + name: "exp_groups.assignment.delete", + title: "Delete Assignments", + description: "Remove manual group assignments.", + grantByDefault: false, }); -declare module "@clusterio/lib" { - export interface ControllerConfigFields { - "exp_groups.allow_role_inconsistency": boolean; - } - export interface InstanceConfigFields { - "exp_groups.sync_permission_groups": boolean; - } -} +// Role mapping permissions + +lib.definePermission({ + name: "exp_groups.role_mapping.get", + title: "Get Role Mappings", + description: "Retrieve a specific role mapping rule.", + grantByDefault: true, +}); +lib.definePermission({ + name: "exp_groups.role_mapping.list", + title: "List Role Mappings", + description: "List all role mapping rules.", + grantByDefault: true, +}); +lib.definePermission({ + name: "exp_groups.role_mapping.subscribe", + title: "Subscribe to Role Mapping Updates", + description: "Receive updates when role mapping rules change.", + grantByDefault: true, +}); +lib.definePermission({ + name: "exp_groups.role_mapping.create", + title: "Create Role Mappings", + description: "Create rules that map user roles to Factorio permission groups.", + grantByDefault: false, +}); +lib.definePermission({ + name: "exp_groups.role_mapping.update", + title: "Update Role Mappings", + description: "Modify existing role mapping rules.", + grantByDefault: false, +}); +lib.definePermission({ + name: "exp_groups.role_mapping.delete", + title: "Delete Role Mappings", + description: "Delete role mapping rules.", + grantByDefault: false, +}); export const plugin: lib.PluginDeclaration = { name: "exp_groups", - title: "exp_groups", - description: "Create, modify, and link factorio permission groups to clusterio user roles.", + title: "ExpGaming - Permission Groups", + description: "Clusterio plugin providing syncing of permission groups", - controllerEntrypoint: "./dist/node/controller", - controllerConfigFields: { - "exp_groups.allow_role_inconsistency": { - title: "Allow User Role Inconsistency", - description: "When true, users can be assgined to any group regardless of their roles", - type: "boolean", - initialValue: false, - }, - }, + features: [ + "SavePatching", + "ScriptCommands", + ], + + messages: [ + messages.GroupUpdatedEvent, + messages.ManualAssignmentUpdatedEvent, + messages.ResolvedAssignmentUpdatedEvent, + messages.RoleMappingUpdatedEvent, + + messages.GroupCreateRequest, + messages.GroupUpdateRequest, + messages.GroupDeleteRequest, + messages.GroupGetRequest, + messages.GroupListRequest, + + messages.AssignmentCreateRequest, + messages.AssignmentUpdateRequest, + messages.AssignmentDeleteRequest, + messages.AssignmentGetRequest, + messages.AssignmentListRequest, + + messages.RoleMappingCreateRequest, + messages.RoleMappingUpdateRequest, + messages.RoleMappingDeleteRequest, + messages.RoleMappingGetRequest, + messages.RoleMappingListRequest, + ], instanceEntrypoint: "./dist/node/instance", instanceConfigFields: { - "exp_groups.sync_permission_groups": { - title: "Sync Permission Groups", - description: "When true, the instance cannot deviate from the global group settings and will be hidden from the sellection dropdown.", - type: "boolean", - initialValue: true, + "exp_groups.sync_mode": { + description: "Synchronize permission groups with the controller", + type: "string", + enum: ["disabled", "enabled", "bidirectional"], + initialValue: "bidirectional", }, }, - messages: [ - Messages.PermissionGroupEditEvent, - Messages.PermissionStringsUpdate, - Messages.PermissionGroupUpdate, - ], + controllerEntrypoint: "./dist/node/controller", + controllerConfigFields: { + }, webEntrypoint: "./web", routes: [ - "/exp_groups", - ], + "/permission_groups", + "/permission_groups/:id/view", + ] }; diff --git a/exp_groups/instance.ts b/exp_groups/instance.ts index 5142d933c3..516c34bf0c 100644 --- a/exp_groups/instance.ts +++ b/exp_groups/instance.ts @@ -1,129 +1,171 @@ -import * as lib from "@clusterio/lib"; import { BaseInstancePlugin } from "@clusterio/host"; -import { - PermissionGroup, PermissionGroupEditEvent, PermissionGroupEditType, - PermissionGroupUpdate, PermissionInstanceId, PermissionStrings, PermissionStringsUpdate -} from "./messages"; - -const rconBase = "/sc local Groups = package.loaded['modules/exp_groups/module_exports'];" +import * as lib from "@clusterio/lib"; +import * as messages from "./messages"; -type EditIPC = { - type: PermissionGroupEditType, - changes: string[], - group: string, +export type IpcGroupUpdated = { + group_name: string, + group_id: number | undefined, + permissions: { is_blacklist: boolean, permissions: string[] | undefined }, }; -type CreateIPC = { - group: string, - defiantion: [boolean, string[] | {}] -} +export type IpcGroupDeleted = { + group_name: string, + group_id: number | undefined, +}; -type DeleteIPC = { - group: string, -} +export type IpcPlayerAssignments = { + assignments: Record, +}; export class InstancePlugin extends BaseInstancePlugin { - permissions: Set = new Set(); - permissionGroups = new lib.EventSubscriber(PermissionGroupUpdate, this.instance); - permissionGroupUpdates = new lib.EventSubscriber(PermissionGroupEditEvent, this.instance); - syncId: PermissionInstanceId = this.instance.config.get("exp_groups.sync_permission_groups") ? "Global" : this.instance.id; - - async init() { - this.instance.server.handle("exp_groups-permission_group_edit", this.handleEditIPC.bind(this)); - this.instance.server.handle("exp_groups-permission_group_create", this.handleCreateIPC.bind(this)); - this.instance.server.handle("exp_groups-permission_group_delete", this.handleDeleteIPC.bind(this)); - } - - async onStart() { - // Send the most recent version of the permission string - const permissionsString = await this.sendRcon(rconBase + "rcon.print(Groups.get_actions_json())"); - this.permissions = new Set(JSON.parse(permissionsString)); - this.instance.sendTo("controller", new PermissionStringsUpdate([ - new PermissionStrings(this.instance.id, this.permissions, Date.now()) - ])); - - // Subscribe to get updates for permission groups - this.permissionGroups.subscribe(this.onPermissionGroupsUpdate.bind(this)); - this.permissionGroupUpdates.subscribe(this.onPermissionGroupUpdate.bind(this)); - } - - async onControllerConnectionEvent(event: any) { - this.permissionGroups.handleConnectionEvent(event); - } - - async onInstanceConfigFieldChanged(field: string, curr: unknown, prev: unknown) { - if (field === "exp_groups.sync_permission_groups") { - this.syncId = curr ? "Global" : this.instance.id; - const [snapshot, synced] = this.permissionGroups.getSnapshot(); - if (synced && this.instance.status !== "running") await this.syncPermissionGroups(snapshot.values()); - } - } - - async onPermissionGroupsUpdate(event: PermissionGroupUpdate | null, synced: boolean) { - if (!synced || this.instance.status !== "running" || !event?.updates.length) return; - await this.syncPermissionGroups(event.updates); - } - - async syncPermissionGroups(groups: Iterable) { - const updateCommands = [rconBase]; - for (const group of groups) { - if (group.instanceId === this.syncId && group.updatedAtMs > (this.permissionGroups.values.get(group.id)?.updatedAtMs ?? 0)) { - if (group.isDeleted) { - updateCommands.push(`Groups.destroy_group('${group.name}')`); - } else if (group.permissions.size < this.permissions.size / 2) { - updateCommands.push(`Groups.get_or_create('${group.name}'):from_json('${JSON.stringify([false, [...this.permissions.values()]])}')`); - } else { - const inverted = [...this.permissions.values()].filter(permission => !group.permissions.has(permission)); - updateCommands.push(`Groups.get_or_create('${group.name}'):from_json('${JSON.stringify([true, inverted])}')`); - } - } - } - await this.sendRcon(updateCommands.join(";"), true); - } - - async onPermissionGroupUpdate(event: PermissionGroupEditEvent | null, synced: boolean) { - if (!synced || this.instance.status !== "running" || !event) return; - if (event.src.equals(lib.Address.fromShorthand({ instanceId: this.instance.id }))) return; - const getCmd = `Groups.get_or_create('${event.group}')`; - if (event.type === "add_permissions") { - await this.sendRcon(rconBase + getCmd + `:allow_actions(Groups.json_to_actions('${JSON.stringify(event.changes)}'))`); - } else if (event.type === "remove_permissions") { - await this.sendRcon(rconBase + getCmd + `:disallow_actions(Groups.json_to_actions('${JSON.stringify(event.changes)}'))`); - } else if (event.type === "assign_players") { - await this.sendRcon(rconBase + getCmd + `:add_players(game.json_to_table('${JSON.stringify(event.changes)}'))`); - } - } - - async handleEditIPC(event: EditIPC) { - this.logger.info(JSON.stringify(event)) - this.instance.sendTo("controller", new PermissionGroupEditEvent( - lib.Address.fromShorthand({ instanceId: this.instance.id }), - event.type, event.group, event.changes - )) - } - - async handleCreateIPC(event: CreateIPC) { - this.logger.info(JSON.stringify(event)) - if (!this.permissionGroups.synced) return; - let [defaultAllow, permissionsRaw] = event.defiantion; - if (!Array.isArray(permissionsRaw)) { - permissionsRaw = [] // lua outputs {} for empty arrays - } - const permissions = [...this.permissions.values()] - .filter(permission => defaultAllow !== (permissionsRaw as String[]).includes(permission)); - this.instance.sendTo("controller", new PermissionGroupUpdate([ new PermissionGroup( - this.syncId, event.group, 0, new Set(), new Set(permissions) - ) ])); - } - - async handleDeleteIPC(event: DeleteIPC) { - if (!this.permissionGroups.synced) return; - const group = [...this.permissionGroups.values.values()] - .find(group => group.instanceId === this.syncId && group.name === event.group); - if (group) { - group.updatedAtMs = Date.now(); - group.isDeleted = true; - this.instance.sendTo("controller", new PermissionGroupUpdate([ group ])); - } - } + // Once only, don't send permissions for these groups + // This is used for groups created on this instance that only need the controller generated id + skipSendingPermissions = new Set(); + // This is used for groups updated / deleted on this instance to stop cycles + skipSendingUpdate = new Set(); + // Track known online players so that we only apply assignment updates for them + onlinePlayers = new Set(); + + async init() { + this.instance.handle(messages.GroupUpdatedEvent, this.handleGroupUpdatedEvent.bind(this)); + this.instance.handle(messages.ResolvedAssignmentUpdatedEvent, this.handleResolvedAssignmentUpdatedEvent.bind(this)); + this.instance.server.handle(`exp_group:group_updated`, this.handleGroupUpdatedIPC.bind(this)) + this.instance.server.handle(`exp_group:group_deleted`, this.handleGroupDeletedIPC.bind(this)) + this.instance.server.handle(`exp_group:player_assignments`, this.handlePlayerAssignmentsIPC.bind(this)) + } + + async onInstanceConfigFieldChanged(field: string, curr: unknown, prev: unknown) { + switch(field) { + case "exp_groups.sync_mode": + await this.luaSetEmitEvents(curr == "bidirectional") + break; + } + } + + async onStart() { + // We use Date.now() because we need to manually initialise the groups on the lua side + await this.instance.sendTo("controller", new lib.SubscriptionRequest( + `exp_groups:${messages.GroupUpdatedEvent.name}`, true, Date.now() + )); + const groups = await this.instance.sendTo("controller", new messages.GroupListRequest()) + await this.luaSendInitialGroups(groups); + await this.luaSetEmitEvents(this.instance.config.get("exp_groups.sync_mode") == "bidirectional") + } + + async onPlayerEvent(event: lib.PlayerEvent) { + switch(event.type) { + case "join": + this.onlinePlayers.add(event.name); + await this.subscribePlayerAssignment(event.name); + break; + case "leave": + this.onlinePlayers.delete(event.name); + await this.unsubscribePlayerAssignment(event.name); + break; + } + } + + async handleGroupUpdatedEvent(event: messages.GroupUpdatedEvent) { + for (const group of event.updates) { + await this.luaSendGroupUpdate(group); + } + } + + async handleResolvedAssignmentUpdatedEvent(event: messages.ManualAssignmentUpdatedEvent) { + for (const assignment of event.updates) { + if (this.onlinePlayers.has(assignment.name)) { + await this.luaSendAssignmentUpdate(assignment); + } + } + } + + async handleGroupUpdatedIPC(event: IpcGroupUpdated) { + const permissions = new messages.GroupPermissions( + event.permissions.is_blacklist, + event.permissions.permissions ?? [], + ) + + if (event.group_id === undefined) { + this.skipSendingPermissions.add(event.group_name); + await this.instance.sendTo("controller", + new messages.GroupCreateRequest(event.group_name, permissions), + ); + } else { + this.skipSendingUpdate.add(event.group_id); + await this.instance.sendTo("controller", new messages.GroupUpdateRequest( + new messages.GroupRecord(event.group_id, event.group_name, permissions), + )); + } + } + + async handleGroupDeletedIPC(event: IpcGroupDeleted) { + if (event.group_id === undefined) { + return; + } + this.skipSendingUpdate.add(event.group_id); + await this.instance.sendTo("controller", new messages.GroupDeleteRequest(event.group_id)); + } + + async handlePlayerAssignmentsIPC(event: IpcPlayerAssignments) { + await Promise.all( + Object.entries(event.assignments).map(([playerName, groupId]) => + this.instance.sendTo("controller", + new messages.AssignmentUpdateRequest(new messages.AssignmentRecord(playerName, groupId)), + ) + ) + ); + } + + async subscribePlayerAssignment(playerName: string) { + await this.instance.sendTo("controller", new lib.SubscriptionRequest( + `exp_groups:${messages.ResolvedAssignmentUpdatedEvent.name}`, true, 0, playerName + )); + } + + async unsubscribePlayerAssignment(playerName: string) { + await this.instance.sendTo("controller", new lib.SubscriptionRequest( + `exp_groups:${messages.ResolvedAssignmentUpdatedEvent.name}`, false, 0, playerName + )); + } + + async luaSendInitialGroups(groups: messages.GroupRecord[]) { + if (this.instance.config.get("exp_groups.sync_mode") === "disabled") { + return; + } + await this.luaSend("initialise_groups", groups); + } + + async luaSendGroupUpdate(group: messages.GroupRecord) { + if (this.instance.config.get("exp_groups.sync_mode") === "disabled") { + return; + } + + if (this.skipSendingUpdate.has(group.id)) { + this.skipSendingUpdate.delete(group.id); + return; + } + + const json = group.toJSON(); + if (this.skipSendingPermissions.has(group.name)) { + this.skipSendingPermissions.delete(group.name); + delete (json as any).permissions; + } + + await this.luaSend("receive_group_update", json); + } + + async luaSendAssignmentUpdate(assignment: messages.AssignmentRecord) { + if (this.instance.config.get("exp_groups.sync_mode") === "disabled") { + return; + } + await this.luaSend("receive_assignment_update", assignment); + } + + async luaSetEmitEvents(emitEvents: boolean) { + await this.luaSend("set_emit_events", emitEvents); + } + + async luaSend(receiver: string, json: any) { + await this.instance.sendRcon(`/c exp_groups.${receiver}(helpers.json_to_table[=[${JSON.stringify(json)}]=])`, true) + } } diff --git a/exp_groups/messages.ts b/exp_groups/messages.ts index 9b7337a8a8..b0e5e9860b 100644 --- a/exp_groups/messages.ts +++ b/exp_groups/messages.ts @@ -1,239 +1,692 @@ -import { User, InstanceDetails, IControllerUser, Link, MessageRequest, StringEnum, PermissionError, Address } from "@clusterio/lib"; -import { Type, Static } from "@sinclair/typebox"; - -export const PermissionInstanceIdSchema = Type.Union([InstanceDetails.jsonSchema.properties.id, Type.Literal("Global")]) -export type PermissionInstanceId = InstanceDetails["id"] | "Global" -export type GamePermission = string; // todo: maybe enum this? - -/** - * Data class for permission groups - */ -export class PermissionGroup { - constructor( - public instanceId: PermissionInstanceId, - public name: string, - /** A lower order assumes a lower permission group */ - public order: number = 0, - /** A role will use the highest order group it is apart of */ - public roleIds: User["roleIds"] = new Set(), - public permissions: Set = new Set(), - public updatedAtMs: number = 0, - public isDeleted: boolean = false, - ) { - } - - static jsonSchema = Type.Object({ - instanceId: PermissionInstanceIdSchema, - name: Type.String(), - order: Type.Number(), - roleIds: Type.Array(Type.Number()), - permissions: Type.Array(Type.String()), - updatedAtMs: Type.Optional(Type.Number()), - isDeleted: Type.Optional(Type.Boolean()), - }); - - static fromJSON(json: Static) { - return new this( - json.instanceId, - json.name, - json.order, - new Set(json.roleIds), - new Set(json.permissions), - json.updatedAtMs, - json.isDeleted - ); - } - - toJSON(): Static { - return { - instanceId: this.instanceId, - name: this.name, - order: this.order, - roleIds: [...this.roleIds.values()], - permissions: [...this.permissions.values()], - updatedAtMs: this.updatedAtMs > 0 ? this.updatedAtMs : undefined, - isDeleted: this.isDeleted ? this.isDeleted : undefined, - } - } - - get id() { - return `${this.instanceId}:${this.name}`; - } - - copy(newInstanceId: PermissionInstanceId) { - return new PermissionGroup( - newInstanceId, - this.name, - this.order, - new Set(this.roleIds), - new Set(this.permissions), - Date.now(), - false - ) - } -} - -export class InstancePermissionGroups { - constructor( - public instanceId: PermissionInstanceId, - public groups: Map = new Map(), - ) { - } - - static jsonSchema = Type.Object({ - instanceId: PermissionInstanceIdSchema, - permissionsGroups: Type.Array(PermissionGroup.jsonSchema), - }); - - static fromJSON(json: Static) { - return new InstancePermissionGroups( - json.instanceId, - new Map(json.permissionsGroups.map(group => [group.name, PermissionGroup.fromJSON(group)])), - ); - } - - toJSON() { - return { - instanceId: this.instanceId, - permissionsGroups: [...this.groups.values()], - } - } - - getUserGroup(user: User) { - const groups = [...user.roleIds.values()].map(roleId => - // There will always be one and only one group for each role - [...this.groups.values()].find(group => group.roleIds.has(roleId))! - ); - return groups.reduce((highest, group) => highest.order > group.order ? highest : group); - } - - get id() { - return this.instanceId; - } -} - -export class PermissionGroupUpdate { - declare ["constructor"]: typeof PermissionGroupUpdate; - static type = "event" as const; - static src = ["controller", "instance"] as const; - static dst = ["control", "instance", "controller"] as const; - static plugin = "exp_groups" as const; - static permission = "exp_groups.list.subscribe"; - - constructor( - public updates: PermissionGroup[], - ) { } - - static jsonSchema = Type.Object({ - "updates": Type.Array(PermissionGroup.jsonSchema), - }); - - static fromJSON(json: Static) { - return new this( - json.updates.map(update => PermissionGroup.fromJSON(update)) - ); - } -} - -export type PermissionGroupEditType = "assign_players" | "add_permissions" | "remove_permissions"; - -export class PermissionGroupEditEvent { - declare ["constructor"]: typeof PermissionGroupEditEvent; - static type = "event" as const; - static src = ["instance", "controller"] as const; - static dst = ["control", "instance", "controller"] as const; - static plugin = "exp_groups" as const; - - static permission(user: IControllerUser, message: MessageRequest) { - if (typeof message.data === "object" && message.data !== null) { - const data = message.data as Static; - if (data.type === "add_permissions" || data.type === "remove_permissions") { - user.checkPermission("exp_groups.modify_permissions") - } else if (data.type === "assign_players") { - user.checkPermission("exp_groups.assign_players") - } else { - throw new PermissionError("Permission denied"); - } - }; - } - - constructor( - public src: Address, - public type: PermissionGroupEditType, - public group: string, - public changes: String[], - ) { } - - static jsonSchema = Type.Object({ - "src": Address.jsonSchema, - "type": StringEnum(["assign_players", "add_permissions", "remove_permissions"]), - "group": Type.String(), - "changes": Type.Array(Type.String()), - }); - - static fromJSON(json: Static) { - return new this(Address.fromJSON(json.src), json.type, json.group, json.changes); - } -} - -export class PermissionStrings { - constructor( - public instanceId: PermissionInstanceId, - public permissions: Set, - public updatedAtMs: number = 0, - public isDeleted: boolean = false, - ) { - } - - static jsonSchema = Type.Object({ - instanceId: PermissionInstanceIdSchema, - permissions: Type.Array(Type.String()), - updatedAtMs: Type.Optional(Type.Number()), - isDeleted: Type.Optional(Type.Boolean()), - }); - - static fromJSON(json: Static) { - return new PermissionStrings( - json.instanceId, - new Set(json.permissions), - json.updatedAtMs, - json.isDeleted - ); - } - - toJSON() { - return { - instanceId: this.instanceId, - permissions: [...this.permissions.values()], - updatedAtMs: this.updatedAtMs > 0 ? this.updatedAtMs : undefined, - isDeleted: this.isDeleted ? this.isDeleted : undefined, - } - } - - get id() { - return this.instanceId - } -} - -export class PermissionStringsUpdate { - declare ["constructor"]: typeof PermissionStringsUpdate; - static type = "event" as const; - static src = ["instance", "controller"] as const; - static dst = ["controller", "control"] as const; - static plugin = "exp_groups" as const; - static permission = "exp_groups.list.subscribe"; - - constructor( - public updates: PermissionStrings[], - ) { } - - static jsonSchema = Type.Object({ - "updates": Type.Array(PermissionStrings.jsonSchema), - }); - - static fromJSON(json: Static) { - return new this( - json.updates.map(update => PermissionStrings.fromJSON(update)) - ); - } -} +import * as lib from "@clusterio/lib"; +import { Type, Static } from "@sinclair/typebox"; + +/* + Data records +*/ + +export class GroupPermissions { + constructor( + public isBlacklist: boolean, + public permissions: string[], + ) {} + + static jsonSchema = Type.Object({ + is_blacklist: Type.Boolean(), + permissions: Type.Array(Type.String()), + }); + + toJSON(): Static { + return { + is_blacklist: this.isBlacklist, + permissions: this.permissions, + }; + } + + static fromJSON(json: Static) { + return new this( + json.is_blacklist, + json.permissions, + ); + } +} + +export class GroupRecord { + constructor( + public id: number, + public name: string, + public permissions: GroupPermissions, + public updatedAtMs: number = 0, + public isDeleted: boolean = false, + ) {} + + static jsonSchema = Type.Object({ + id: Type.Integer(), + name: Type.String(), + permissions: GroupPermissions.jsonSchema, + updated_at_ms: Type.Optional(Type.Number()), + is_deleted: Type.Optional(Type.Boolean()), + }); + + toJSON() { + let json: Static = { + id: this.id, + name: this.name, + permissions: this.permissions.toJSON(), + }; + + if (this.updatedAtMs) { + json.updated_at_ms = this.updatedAtMs; + } + + if (this.isDeleted) { + json.is_deleted = true; + } + + return json; + } + + static fromJSON(json: Static) { + return new this( + json.id, + json.name, + GroupPermissions.fromJSON(json.permissions), + json.updated_at_ms ?? 0, + json.is_deleted ?? false, + ); + } +} + +export class AssignmentRecord { + constructor( + public name: string, + public groupId: number, + public updatedAtMs: number = 0, + public isDeleted: boolean = false, + ) {} + + get id() { + return this.name; + } + + static jsonSchema = Type.Object({ + name: Type.String(), + group_id: Type.Integer(), + updated_at_ms: Type.Optional(Type.Number()), + is_deleted: Type.Optional(Type.Boolean()), + }); + + toJSON(): Static { + let json: Static = { + name: this.name, + group_id: this.groupId, + }; + + if (this.updatedAtMs) { + json.updated_at_ms = this.updatedAtMs; + } + + if (this.isDeleted) { + json.is_deleted = true; + } + + return json; + } + + static fromJSON(json: Static) { + return new this( + json.name, + json.group_id, + json.updated_at_ms ?? 0, + json.is_deleted ?? false, + ); + } +} + +export class RoleMappingRecord { + constructor( + public id: number, + public roleIds: Set, + public groupId: number, + public priority: number, + public enabled: boolean, + public updatedAtMs: number = 0, + public isDeleted: boolean = false, + ) {} + + static jsonSchema = Type.Object({ + id: Type.Integer(), + role_ids: Type.Array(Type.Integer()), + group_id: Type.Integer(), + priority: Type.Number(), + enabled: Type.Boolean(), + updated_at_ms: Type.Optional(Type.Number()), + is_deleted: Type.Optional(Type.Boolean()), + }); + + toJSON(): Static { + let json: Static = { + id: this.id, + role_ids: [...this.roleIds], + group_id: this.groupId, + priority: this.priority, + enabled: this.enabled, + }; + + if (this.updatedAtMs) { + json.updated_at_ms = this.updatedAtMs; + } + + if (this.isDeleted) { + json.is_deleted = true; + } + + return json; + } + + static fromJSON(json: Static) { + return new this( + json.id, + new Set(json.role_ids), + json.group_id, + json.priority, + json.enabled, + json.updated_at_ms ?? 0, + json.is_deleted ?? false, + ); + } +} + +/* + Update events +*/ + +export class GroupUpdatedEvent { + declare ["constructor"]: typeof GroupUpdatedEvent; + static plugin = "exp_groups" as const; + static type = "event" as const; + static src = "controller" as const; + static dst = ["control", "instance"] as const; + static permission = "exp_groups.group.subscribe" as const; + + constructor( + public updates: GroupRecord[], + ) {} + + static jsonSchema = Type.Object({ + updates: Type.Array(GroupRecord.jsonSchema), + }); + + toJSON() { + return { updates: this.updates.map(group => group.toJSON()) }; + } + + static fromJSON(json: Static) { + return new this(json.updates.map(group => GroupRecord.fromJSON(group))); + } +} + +export class ManualAssignmentUpdatedEvent { + declare ["constructor"]: typeof ManualAssignmentUpdatedEvent; + static plugin = "exp_groups" as const; + static type = "event" as const; + static src = "controller" as const; + static dst = ["control", "instance"] as const; + static permission = "exp_groups.assignment.subscribe" as const; + + constructor( + public updates: AssignmentRecord[], + ) {} + + static jsonSchema = Type.Object({ + updates: Type.Array(AssignmentRecord.jsonSchema), + }); + + toJSON() { + return { updates: this.updates.map(assignment => assignment.toJSON()) }; + } + + static fromJSON(json: Static) { + return new this(json.updates.map(assignment => AssignmentRecord.fromJSON(assignment))); + } +} + +export class ResolvedAssignmentUpdatedEvent { + declare ["constructor"]: typeof ResolvedAssignmentUpdatedEvent; + static plugin = "exp_groups" as const; + static type = "event" as const; + static src = "controller" as const; + static dst = ["control", "instance"] as const; + static permission = "exp_groups.assignment.subscribe" as const; + + constructor( + public updates: AssignmentRecord[], + ) {} + + static jsonSchema = Type.Object({ + updates: Type.Array(AssignmentRecord.jsonSchema), + }); + + toJSON() { + return { updates: this.updates.map(assignment => assignment.toJSON()) }; + } + + static fromJSON(json: Static) { + return new this(json.updates.map(assignment => AssignmentRecord.fromJSON(assignment))); + } +} + +export class RoleMappingUpdatedEvent { + declare ["constructor"]: typeof RoleMappingUpdatedEvent; + static plugin = "exp_groups" as const; + static type = "event" as const; + static src = "controller" as const; + static dst = "control" as const; + static permission = "exp_groups.role_mapping.subscribe" as const; + + constructor( + public updates: RoleMappingRecord[], + ) {} + + static jsonSchema = Type.Object({ + updates: Type.Array(RoleMappingRecord.jsonSchema), + }); + + toJSON() { + return { updates: this.updates.map(roleMapping => roleMapping.toJSON()) }; + } + + static fromJSON(json: Static) { + return new this(json.updates.map(roleMapping => RoleMappingRecord.fromJSON(roleMapping))); + } +} + +/* + Group requests +*/ + +export class GroupCreateRequest { + declare ["constructor"]: typeof GroupCreateRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.group.create" as const; + static Response = GroupRecord; + + constructor( + public name: string, + public permissions: GroupPermissions, + ) {} + + static jsonSchema = Type.Object({ + name: Type.String(), + permissions: GroupPermissions.jsonSchema, + }); + + toJSON() { + return { + name: this.name, + permissions: this.permissions.toJSON(), + }; + } + + static fromJSON(json: Static) { + return new this( + json.name, + GroupPermissions.fromJSON(json.permissions), + ); + } +} + +export class GroupUpdateRequest { + declare ["constructor"]: typeof GroupUpdateRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.group.update" as const; + + constructor( + public group: GroupRecord, + ) {} + + static jsonSchema = Type.Object({ + group: GroupRecord.jsonSchema, + }); + + toJSON() { + return { + group: this.group.toJSON(), + }; + } + + static fromJSON(json: Static) { + return new this( + GroupRecord.fromJSON(json.group), + ); + } +} + +export class GroupDeleteRequest { + declare ["constructor"]: typeof GroupDeleteRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.group.delete" as const; + + constructor( + public groupId: number, + ) {} + + static jsonSchema = Type.Object({ + group_id: Type.Integer(), + }); + + toJSON() { + return { + group_id: this.groupId, + }; + } + + static fromJSON(json: Static) { + return new this(json.group_id); + } +} + +export class GroupGetRequest { + declare ["constructor"]: typeof GroupGetRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.group.get" as const; + static Response = GroupRecord; + + constructor( + public groupId: number, + ) {} + + static jsonSchema = Type.Object({ + group_id: Type.Integer(), + }); + + toJSON() { + return { + group_id: this.groupId, + }; + } + + static fromJSON(json: Static) { + return new this(json.group_id); + } +} + +export class GroupListRequest { + declare ["constructor"]: typeof GroupListRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.group.list" as const; + static Response = lib.jsonArray(GroupRecord); + + constructor() {} +} + +/* + Assignment requests +*/ + +export class AssignmentCreateRequest { + declare ["constructor"]: typeof AssignmentCreateRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.assignment.create" as const; + static Response = AssignmentRecord; + + constructor( + public name: string, + public groupId: number, + ) {} + + static jsonSchema = Type.Object({ + name: Type.String(), + group_id: Type.Integer(), + }); + + toJSON() { + return { + name: this.name, + group_id: this.groupId, + }; + } + + static fromJSON(json: Static) { + return new this(json.name, json.group_id); + } +} + +export class AssignmentUpdateRequest { + declare ["constructor"]: typeof AssignmentUpdateRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.assignment.update" as const; + + constructor( + public assignment: AssignmentRecord, + ) {} + + static jsonSchema = Type.Object({ + assignment: AssignmentRecord.jsonSchema, + }); + + toJSON() { + return { + assignment: this.assignment.toJSON(), + }; + } + + static fromJSON(json: Static) { + return new this( + AssignmentRecord.fromJSON(json.assignment), + ); + } +} + +export class AssignmentDeleteRequest { + declare ["constructor"]: typeof AssignmentDeleteRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.assignment.delete" as const; + + constructor( + public name: string, + ) {} + + static jsonSchema = Type.Object({ + name: Type.String(), + }); + + toJSON() { + return { + name: this.name, + }; + } + + static fromJSON(json: Static) { + return new this(json.name); + } +} + +export class AssignmentGetRequest { + declare ["constructor"]: typeof AssignmentGetRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.assignment.get" as const; + static Response = AssignmentRecord; + + constructor( + public name: string, + public resolve: boolean = false, + ) {} + + static jsonSchema = Type.Object({ + name: Type.String(), + resolve: Type.Boolean(), + }); + + toJSON() { + return { + name: this.name, + resolve: this.resolve, + }; + } + + static fromJSON(json: Static) { + return new this(json.name, json.resolve); + } +} + +export class AssignmentListRequest { + declare ["constructor"]: typeof AssignmentListRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.assignment.list" as const; + static Response = lib.jsonArray(AssignmentRecord); + + constructor() {} +} + +/* + Role mapping requests +*/ + +export class RoleMappingCreateRequest { + declare ["constructor"]: typeof RoleMappingCreateRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.role_mapping.create" as const; + static Response = RoleMappingRecord; + + constructor( + public roleIds: number[], + public groupId: number, + public priority: number, + public enabled: boolean, + ) {} + + static jsonSchema = Type.Object({ + role_ids: Type.Array(Type.Integer()), + group_id: Type.Integer(), + priority: Type.Number(), + enabled: Type.Boolean(), + }); + + toJSON() { + return { + role_ids: this.roleIds, + group_id: this.groupId, + priority: this.priority, + enabled: this.enabled, + }; + } + + static fromJSON(json: Static) { + return new this( + json.role_ids, + json.group_id, + json.priority, + json.enabled, + ); + } +} + +export class RoleMappingUpdateRequest { + declare ["constructor"]: typeof RoleMappingUpdateRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.role_mapping.update" as const; + + constructor( + public roleMapping: RoleMappingRecord, + ) {} + + static jsonSchema = Type.Object({ + role_mapping: RoleMappingRecord.jsonSchema, + }); + + toJSON() { + return { + role_mapping: this.roleMapping.toJSON(), + }; + } + + static fromJSON(json: Static) { + return new this( + RoleMappingRecord.fromJSON(json.role_mapping), + ); + } +} + +export class RoleMappingDeleteRequest { + declare ["constructor"]: typeof RoleMappingDeleteRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.role_mapping.delete" as const; + + constructor( + public id: number, + ) {} + + static jsonSchema = Type.Object({ + id: Type.Integer(), + }); + + toJSON() { + return { + id: this.id, + }; + } + + static fromJSON(json: Static) { + return new this(json.id); + } +} + +export class RoleMappingGetRequest { + declare ["constructor"]: typeof RoleMappingGetRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.role_mapping.get" as const; + static Response = RoleMappingRecord; + + constructor( + public id: number, + ) {} + + static jsonSchema = Type.Object({ + id: Type.Integer(), + }); + + toJSON() { + return { + id: this.id, + }; + } + + static fromJSON(json: Static) { + return new this(json.id); + } +} + +export class RoleMappingListRequest { + declare ["constructor"]: typeof RoleMappingListRequest; + static plugin = "exp_groups" as const; + static type = "request" as const; + static src = ["control", "instance"] as const; + static dst = "controller" as const; + static permission = "exp_groups.role_mapping.list" as const; + static Response = lib.jsonArray(RoleMappingRecord); + + constructor() {} +} diff --git a/exp_groups/module/control.lua b/exp_groups/module/control.lua index af956afc51..0c61af71db 100644 --- a/exp_groups/module/control.lua +++ b/exp_groups/module/control.lua @@ -1,127 +1,438 @@ +--[[-- ExpGroups +Adds permission group syncing to clusterio +]] + local clusterio_api = require("modules/clusterio/api") -local Global = require("modules/exp_util/global") -local Groups = require("modules/exp_groups") +local compat = require("modules/clusterio/compat") -local pending_updates = {} -Global.register(pending_updates, function(tbl) - pending_updates = tbl -end) +--- Top level module table, contains event handlers and public methods +--- @class ExpGroups +local ExpGroups = {} -local function on_permission_group_added(event) - if not event.player_index then return end - pending_updates[event.group.name] = { - created = true, - sync_all = true, - tick = event.tick, - permissions = {}, - players = {}, - } +--- @class ExpPermissionGroups.GroupPermissions +--- @field is_blacklist boolean +--- @field permissions string[]? + +--- @class ExpPermissionGroups.GroupRecord +--- @field id number +--- @field name string +--- @field permissions ExpPermissionGroups.GroupPermissions? +--- @field is_deleted boolean + +--- @class ExpPermissionGroups.AssignmentRecord +--- @field name string +--- @field group_id number +--- @field is_deleted boolean + +--- @class ExpPermissionGroups.ScriptData +--- @field factorio_to_clusterio_id table +--- @field clusterio_id_to_group table +--- @field dirty_groups table +--- @field dirty_players table +--- @field emit_updates boolean +local script_data = {} + +local function setup_script_data() + if compat.script_data["exp_groups"] == nil then + --- @type ExpPermissionGroups.ScriptData + compat.script_data["exp_groups"] = { + factorio_to_clusterio_id = {}, + clusterio_id_to_group = {}, + dirty_groups = {}, + dirty_players = {}, + emit_updates = false, + } + end + + ExpGroups.on_load() end -local function on_permission_group_deleted(event) - if not event.player_index then return end - local existing = pending_updates[event.group_name] - pending_updates[event.group_name] = nil - if not existing or not existing.created then - clusterio_api.send_json("exp_groups-permission_group_delete", { - group = event.group_name, - }) +--[[ + Helper methods +]] + +--- Get the default factorio permission group +--- @return LuaPermissionGroup +local function get_default_group() + return assert(game.permissions.get_group("Default")) +end + +--- Move all players from one group to another +--- @param group_src LuaPermissionGroup +--- @param group_dst LuaPermissionGroup +local function move_players(group_src, group_dst) + local add_player = group_dst.add_player + for _, player in pairs(group_src.players) do + add_player(player) end end -local function on_permission_group_edited(event) - if not event.player_index then return end - local pending = pending_updates[event.group.name] - if not pending then - pending = { - tick = event.tick, - permissions = {}, - players = {}, - } - pending_updates[event.group.name] = pending +--- Apply an encoded permission definition to a group +--- @param group LuaPermissionGroup +--- @param permissions ExpPermissionGroups.GroupPermissions +local function decode_group_permissions(group, permissions) + -- Construct a hash map for faster lookup + local action_map = {} + for _, input_action_name in pairs(assert(permissions.permissions)) do + action_map[input_action_name] = true end - pending.tick = event.tick - if event.type == "add-permission" then - if not pending.sync_all then - pending.permissions[event.action] = true + -- Apply the whitelist / backlist to the group + local is_blacklist = permissions.is_blacklist + local action_allowed = not is_blacklist + for input_action_name, input_action in pairs(defines.input_action) do + if action_map[input_action_name] then + group.set_allows_action(input_action, action_allowed) + else + group.set_allows_action(input_action, is_blacklist) end - elseif event.type == "remove-permission" then - if not pending.sync_all then - pending.permissions[event.action] = false + end +end + +--- Encode the permissions of a group in a shorthand format +--- @param group LuaPermissionGroup +--- @return ExpPermissionGroups.GroupPermissions +local function encode_group_permissions(group) + local whitelist = {} --- @type string[] + local blacklist = {} --- @type string[] + local whitelist_index = 0 + local blacklist_index = 0 + + -- Construct the whitelist and blacklist + local allows_action = group.allows_action + for input_action_name, input_action in pairs(defines.input_action) do + if allows_action(input_action) then + whitelist[whitelist_index] = input_action_name + whitelist_index = whitelist_index + 1 + else + blacklist[blacklist_index] = input_action_name + blacklist_index = blacklist_index + 1 + end + end + + -- Return the whitelist if it is smaller + if blacklist_index > whitelist_index then + return whitelist_index > 0 + and { is_blacklist = false, permissions = whitelist } + or { is_blacklist = false } + end + + -- Otherwise return the blacklist as it is smaller + return blacklist_index > 0 + and { is_blacklist = true, permissions = blacklist } + or { is_blacklist = true } +end + +--[[ + State handlers +]] + +--- Update the factorio permission group +--- @param group_record ExpPermissionGroups.GroupRecord +local function update_group(group_record) + assert(not group_record.is_deleted) + + -- Try find the group by id and then then name + local group = script_data.clusterio_id_to_group[group_record.id] + if not group then + group = game.permissions.get_group(group_record.name) + end + + -- Create a new group or update the found group + if not group or not group.valid then + group = assert(game.permissions.create_group(group_record.name)) + else + group.name = group_record.name + end + + -- Update the permissions for the group + if group_record.permissions then + decode_group_permissions(group, group_record.permissions) + end + + -- Update the script data + script_data.factorio_to_clusterio_id[group.group_id] = group_record.id + script_data.clusterio_id_to_group[group_record.id] = group +end + +--- Delete the factorio permission group +--- @param group_record ExpPermissionGroups.GroupRecord +local function delete_group(group_record) + assert(group_record.is_deleted) + + local default_group = get_default_group() + local group = script_data.clusterio_id_to_group[group_record.id] + if group then + move_players(group, default_group) + group.destroy() + end +end + +--- Update an assignment by moving the player to their new group +--- @param assignment_record ExpPermissionGroups.AssignmentRecord +local function update_assignment(assignment_record) + assert(not assignment_record.is_deleted) + + local group = script_data.clusterio_id_to_group[assignment_record.group_id] + local player = assert(game.get_player(assignment_record.name)) + if group then + group.add_player(player) + end +end + +--- Clear an assignment by moving the player to the default group +--- @param assignment_record ExpPermissionGroups.AssignmentRecord +local function delete_assignment(assignment_record) + assert(assignment_record.is_deleted) + + local default_group = get_default_group() + local player = assert(game.get_player(assignment_record.name)) + default_group.add_player(player) +end + +--[[ + Public methods +]] + +--- Restore local references to persistent script data after load +function ExpGroups.on_load() + script_data = compat.script_data["exp_groups"] +end + +--- Enable or disable emitting lua changes back to the instance plugin +--- @param enabled boolean? +function ExpGroups.set_emit_events(enabled) + script_data.emit_updates = enabled ~= false +end + +--- Replace local state with expected controller state on startup +--- @param group_records ExpPermissionGroups.GroupRecord[] +function ExpGroups.initialise_groups(group_records) + local _emit_events = script_data.emit_updates + script_data.emit_updates = false + + -- Update all the received groups + local seen_clusterio_ids = {} --- @type table + for _, group_record in pairs(group_records) do + update_group(group_record) + seen_clusterio_ids[group_record.id] = true + end + + -- Cleanup the script data, removing stale group ids + local factorio_to_clusterio_id = script_data.factorio_to_clusterio_id + local clusterio_id_to_group = script_data.clusterio_id_to_group + for factorio_id, clusterio_id in pairs(factorio_to_clusterio_id) do + if not seen_clusterio_ids[clusterio_id] then + factorio_to_clusterio_id[factorio_id] = nil + clusterio_id_to_group[clusterio_id] = nil + end + end + + -- Remove all other groups + local default_group = get_default_group() + local default_group_id = default_group.group_id + for _, group in pairs(game.permissions.groups) do + if not factorio_to_clusterio_id[group.group_id] and group.group_id ~= default_group_id then + move_players(group, default_group) + group.destroy() end - elseif event.type == "enable-all" then - pending.sync_all = true - elseif event.type == "disable-all" then - pending.sync_all = true - elseif event.type == "add-player" then - local player = game.get_player(event.other_player_index) --- @cast player -nil - pending.players[player.name] = true - elseif event.type == "remove-player" then - local player = game.get_player(event.other_player_index) --- @cast player -nil - pending.players[player.name] = nil - elseif event.type == "rename" then - pending.created = true - pending.sync_all = true - local old = pending_updates[event.old_name] - if old then pending.players = old.players end - on_permission_group_deleted{ - tick = event.tick, player_index = event.player_index, group_name = event.old_name, - } end + + script_data.emit_updates = _emit_events +end + +--- Receive an updated version of a group record +--- @param group_record ExpPermissionGroups.GroupRecord +function ExpGroups.receive_group_update(group_record) + local _emit_events = script_data.emit_updates + script_data.emit_updates = false + + if group_record.is_deleted then + delete_group(group_record) + else + update_group(group_record) + end + + script_data.emit_updates = _emit_events +end + +--- Receive an updated version of a assignment record +--- @param assignment_record ExpPermissionGroups.AssignmentRecord +function ExpGroups.receive_assignment_update(assignment_record) + local _emit_events = script_data.emit_updates + script_data.emit_updates = false + + if assignment_record.is_deleted then + delete_assignment(assignment_record) + else + update_assignment(assignment_record) + end + + script_data.emit_updates = _emit_events +end + +--- Get the current script data for debugging purposes +--- @package +function ExpGroups._script_data() + return script_data +end + +--[[ + IPC events +]] + +--- Emit a group update to the instance plugin +--- @param group LuaPermissionGroup +local function emit_group_update(group) + clusterio_api.send_json("exp_group:group_updated", { + group_name = group.name, + group_id = script_data.factorio_to_clusterio_id[group.group_id], + permissions = encode_group_permissions(group), + }) +end + +--- Emit a group deletion to the instance plugin +--- @param group_id number +--- @param group_name string +local function emit_group_delete(group_id, group_name) + clusterio_api.send_json("exp_group:group_deleted", { + group_name = group_name, + group_id = script_data.factorio_to_clusterio_id[group_id], + }) +end + +--- Emit a player assignment to the instance plugin +--- @param assignments table +local function emit_player_assignments(assignments) + clusterio_api.send_json("exp_group:player_assignments", { + assignments = assignments + }) +end + +--[[ + IPC event queuing +]] + +--- Mark a group as changed +--- @param group LuaPermissionGroup +local function mark_group_dirty(group) + script_data.dirty_groups[group.group_id] = group end -local function send_updates() - local tick = game.tick - 600 -- 10 Seconds - local done = {} - for group_name, pending in pairs(pending_updates) do - if pending.tick < tick then - done[group_name] = true - if pending.sync_all then - clusterio_api.send_json("exp_groups-permission_group_create", { - group = group_name, defiantion = Groups.get_group(group_name):to_json(true), - }) - else - if next(pending.players) then - clusterio_api.send_json("exp_groups-permission_group_edit", { - type = "assign_players", group = group_name, changes = table.get_keys(pending.players), - }) - end - local add, remove = {}, {} - for permission, state in pairs(pending.permissions) do - if state then - add[#add + 1] = permission - else - remove[#remove + 1] = permission - end - end - - if next(add) then - clusterio_api.send_json("exp_groups-permission_group_edit", { - type = "add_permissions", group = group_name, changes = Groups.actions_to_names(add), - }) - end - if next(remove) then - clusterio_api.send_json("exp_groups-permission_group_edit", { - type = "remove_permissions", group = group_name, changes = Groups.actions_to_names(remove), - }) - end - end +--- Mark a player as changed +--- @param player LuaPlayer +local function mark_player_dirty(player) + script_data.dirty_players[player.name] = player +end + +--- Flush queued group updates to the instance plugin +local function flush_group_updates() + -- Check if updates should be updated + if not script_data.emit_updates then + script_data.dirty_groups = {} + return + end + + -- Get all the groups and emit their updates + for _, group in pairs(script_data.dirty_groups) do + if group.valid then + emit_group_update(group) end end - for group_name in pairs(done) do - pending_updates[group_name] = nil + script_data.dirty_groups = {} +end + +--- Flush queued player updates to the instance plugin +local function flush_player_updates() + -- Check if updates should be updated + if not script_data.emit_updates then + script_data.dirty_players = {} + return end + + -- Construct the update payload + local assignments = {} --- @type table + for player_name, player in pairs(script_data.dirty_players) do + local group = player.valid and player.permission_group + if group then + assignments[player_name] = script_data.factorio_to_clusterio_id[group.group_id] + end + end + + emit_player_assignments(assignments) + + script_data.dirty_players = {} end -return { - events = { - [defines.events.on_permission_group_added] = on_permission_group_added, - [defines.events.on_permission_group_deleted] = on_permission_group_deleted, - [defines.events.on_permission_group_edited] = on_permission_group_edited, - }, - on_nth_tick = { - [300] = send_updates, - }, +--[[ + Factorio events +]] + +--- Handle clusterio server startup +local function on_server_startup() + setup_script_data() +end + +--- Handle creation of permission groups +--- @param event EventData.on_permission_group_added +local function on_permission_group_added(event) + -- Check if updates should be updated + if not script_data.emit_updates then + return + end + + mark_group_dirty(event.group) +end + +--- Handle deletion of permission groups +--- @param event EventData.on_permission_group_deleted +local function on_permission_group_deleted(event) + -- Check if updates should be updated + if not script_data.emit_updates then + return + end + + emit_group_delete(event.id, event.group_name) +end + +--- Handle edits make to permission groups +--- @param event EventData.on_permission_group_edited +local function on_permission_group_edited(event) + -- Check if updates should be updated + if not script_data.emit_updates then + return + end + + -- Check if this is a player or group event + if event.type == "add-player" or event.type == "remove-player" then + local player = assert(game.get_player(event.other_player_index)) + mark_player_dirty(player) + else + mark_group_dirty(event.group) + end +end + +--- Periodically flush queued changes +local function on_nth_tick_flush() + flush_group_updates() + flush_player_updates() +end + +local e = defines.events + +local events = { + [clusterio_api.events.on_server_startup] = on_server_startup, + [e.on_multiplayer_init] = on_server_startup, + [e.on_permission_group_added] = on_permission_group_added, + [e.on_permission_group_deleted] = on_permission_group_deleted, + [e.on_permission_group_edited] = on_permission_group_edited, } + +local on_nth_tick = { + [300] = on_nth_tick_flush, +} + +ExpGroups.events = events --- @package +ExpGroups.on_nth_tick = on_nth_tick --- @package +return ExpGroups diff --git a/exp_groups/module/globals.lua b/exp_groups/module/globals.lua new file mode 100644 index 0000000000..ce036c2172 --- /dev/null +++ b/exp_groups/module/globals.lua @@ -0,0 +1,11 @@ +--[[ +It is best practice to not expose any globals because all modules share a global environment +However, sometimes you need globals, for example to access functions within rcon commands +Therefore, we advise that this should be the only file in your module to expose globals +Typically this would be your control file as shown in the example below +]] + +--- @diagnostic disable: global-element + +-- Access using `/sc exp_groups.foo()` +exp_groups = require("modules/exp_groups/control") diff --git a/exp_groups/module/module.json b/exp_groups/module/module.json index 391b1a1d60..6d8893a706 100644 --- a/exp_groups/module/module.json +++ b/exp_groups/module/module.json @@ -4,9 +4,9 @@ "control.lua" ], "require": [ + "globals.lua" ], "dependencies": { - "clusterio": "*", - "exp_util": "*" + "clusterio": "*" } } diff --git a/exp_groups/module/module_exports.lua b/exp_groups/module/module_exports.lua index 56518bc83f..47762f6be7 100644 --- a/exp_groups/module/module_exports.lua +++ b/exp_groups/module/module_exports.lua @@ -1,306 +1,3 @@ -local Async = require("modules/exp_util/async") -local table_to_json = helpers.table_to_json -local json_to_table = helpers.json_to_table - ---- Top level module table, contains event handlers and public methods -local Groups = {} - ---- @class ExpGroup : LuaPermissionGroup ---- @field group LuaPermissionGroup The permission group for this group proxy -Groups._prototype = {} - -Groups._metatable = { - __index = setmetatable(Groups._prototype, { - --- @type any Annotation required because otherwise it is typed as 'table' - __index = function(self, key) - return self.group[key] - end, - }), - __class = "ExpGroup", -} - -local action_to_name = {} -for name, action in pairs(defines.input_action) do - action_to_name[action] = name -end - ---- Async Functions --- These are required to allow bypassing edit_permission_group - ---- Add a player to a permission group, requires edit_permission_group ---- @param player LuaPlayer Player to add to the group ---- @param group LuaPermissionGroup Group to add the player to ---- @return boolean # True if successful -local function add_player_to_group(player, group) - return group.add_player(player) -end - ---- Add a players to a permission group, requires edit_permission_group ---- @param players LuaPlayer[] Players to add to the group ---- @param group LuaPermissionGroup Group to add the players to ---- @return boolean # True if successful -local function add_players_to_group(players, group) - local add_player = group.add_player - if not add_player(players[1]) then - return false - end - for i = 2, #players do - add_player(players[i]) - end - - return true -end - --- Async will bypass edit_permission_group but takes at least one tick -local add_player_to_group_async = Async.register(add_player_to_group) -local add_players_to_group_async = Async.register(add_players_to_group) - ---- Static methods for gettings, creating and removing permission groups - ---- Gets the permission group proxy with the given name or group ID. ---- @param group_name string|uint32 The name or id of the permission group ---- @return ExpGroup? -function Groups.get_group(group_name) - local group = game.permissions.get_group(group_name) - if group == nil then return nil end - return setmetatable({ - group = group, - }, Groups._metatable) -end - ---- Gets the permission group proxy for a players group ---- @param player LuaPlayer The player to get the group of ---- @return ExpGroup? -function Groups.get_player_group(player) - local group = player.permission_group - if group == nil then return nil end - return setmetatable({ - group = group, - }, Groups._metatable) -end - ---- Creates a new permission group, requires add_permission_group ---- @param group_name string Name of the group to create ---- @return ExpGroup -function Groups.new_group(group_name) - local group = game.permissions.get_group(group_name) - assert(group == nil, "Group already exists with name: " .. group_name) - group = game.permissions.create_group(group_name) - assert(group ~= nil, "Requires permission add_permission_group") - return setmetatable({ - group = group, - }, Groups._metatable) -end - ---- Get or create a permisison group, must use the group name not the group id ---- @param group_name string Name of the group to create ---- @return ExpGroup -function Groups.get_or_create(group_name) - local group = game.permissions.get_group(group_name) - if group then - return setmetatable({ - group = group, - }, Groups._metatable) - else - group = game.permissions.create_group(group_name) - assert(group ~= nil, "Requires permission add_permission_group") - return setmetatable({ - group = group, - }, Groups._metatable) - end -end - ---- Destory a permission group, moves all players to default group ---- @param group_name string|uint32 The name or id of the permission group to destroy ---- @param move_to_name string|uint32? The name or id of the permission group to move players to -function Groups.destroy_group(group_name, move_to_name) - local group = game.permissions.get_group(group_name) - if group == nil then return end - - local players = group.players - if #players > 0 then - local move_to = game.permissions.get_group(move_to_name or "Default") - for _, player in ipairs(players) do - player.permission_group = move_to - end - end - - local success = group.destroy() - assert(success, "Requires permission delete_permission_group") -end - ---- Prototype methods for modifying and working with permission groups - ---- Add a player to the permission group ---- @param player LuaPlayer The player to add to the group -function Groups._prototype:add_player(player) - if not add_player_to_group(player, self.group) then - add_player_to_group_async(player, self.group) - end -end - ---- Add players to the permission group ---- @param players LuaPlayer[] The player to add to the group -function Groups._prototype:add_players(players) - if not add_players_to_group(players, self.group) then - add_players_to_group_async(players, self.group) - end -end - ---- Move all players to another group ---- @param other_group ExpGroup The group to move players to, default is the Default group -function Groups._prototype:move_players(other_group) - if not add_players_to_group(self.group.players, other_group.group) then - add_players_to_group_async(self.group.players, other_group.group) - end -end - ---- Allow a set of actions for this group ---- @param actions defines.input_action[] Actions to allow ---- @return ExpGroup -function Groups._prototype:allow_actions(actions) - local set_allow = self.group.set_allows_action - for _, action in ipairs(actions) do - set_allow(action, true) - end - - return self -end - ---- Disallow a set of actions for this group ---- @param actions defines.input_action[] Actions to disallow ---- @return ExpGroup -function Groups._prototype:disallow_actions(actions) - local set_allow = self.group.set_allows_action - for _, action in ipairs(actions) do - set_allow(action, false) - end - - return self -end - ---- Reset the allowed state of all actions ---- @param allowed boolean? default true for allow all actions, false to disallow all actions ---- @return ExpGroup -function Groups._prototype:reset(allowed) - local set_allow = self.group.set_allows_action - if allowed == nil then allowed = true end - for _, action in pairs(defines.input_action) do - set_allow(action, allowed) - end - - return self -end - ---- Returns if the group is allowed a given action ---- @param action string|defines.input_action Actions to test ---- @return boolean # True if successful -function Groups._prototype:allows(action) - if type(action) == "string" then - return self.group.allows_action(defines.input_action[action]) - end - return self.group.allows_action(action) -end - ---- Print a message to all players in the group -function Groups._prototype:print(...) - for _, player in ipairs(self.group.players) do - player.print(...) - end -end - ---- Static and Prototype methods for use with IPC - ---- Convert an array of strings into an array of action names ---- @param actions_names string[] An array of action names ---- @return defines.input_action[] -local function names_to_actions(actions_names) - --- @type defines.input_action[], number[], number - local actions, invalid, invalid_i = {}, {}, 1 - for i, action_name in ipairs(actions_names) do - local action = defines.input_action[action_name] --[[ @as defines.input_action? ]] - if action then - actions[i] = action - else - invalid[invalid_i] = i - invalid_i = invalid_i + 1 - end - end - - local last = #actions - for _, i in ipairs(invalid) do - actions[i] = actions[last] - last = last - 1 - end - - return actions -end - ---- Get the action names from the action numbers -function Groups.actions_to_names(actions) - local names = {} - for i, action in ipairs(actions) do - names[i] = action_to_name[action] - end - - return names -end - ---- Get all input actions that are defined -function Groups.get_actions_json() - local rtn, rtn_i = {}, 1 - for name in pairs(defines.input_action) do - rtn[rtn_i] = name - rtn_i = rtn_i + 1 - end - - return table_to_json(rtn) -end - ---- Convert a json string array into an array of input actions ---- @param json string A json string representing a string array of actions ---- @return defines.input_action[] -function Groups.json_to_actions(json) - local tbl = json_to_table(json) - assert(tbl, "Invalid Json String") - --- @cast tbl string[] - return names_to_actions(tbl) -end - ---- Returns the shortest defination of the allowed actions --- The first value of the return can be passed to :reset -function Groups._prototype:to_json(raw) - local allow, disallow = {}, {} - local allow_i, disallow_i = 1, 1 - local allows = self.group.allows_action - for name, action in pairs(defines.input_action) do - if allows(action) then - allow[allow_i] = name - allow_i = allow_i + 1 - else - disallow[disallow_i] = name - disallow_i = disallow_i + 1 - end - end - - if allow_i >= disallow_i then - return raw and { true, disallow } or table_to_json{ true, disallow } - end - return raw and { false, allow } or table_to_json{ false, allow } -end - ---- Restores this group to the state given in a json string ---- @param json string The json string to restore from -function Groups._prototype:from_json(json) - local tbl = json_to_table(json) - assert(tbl and type(tbl[1]) == "boolean" and type(tbl[2]) == "table", "Invalid Json String") - - if tbl[1] then - self:reset(true):disallow_actions(names_to_actions(tbl[2])) - return - end - self:reset(false):allow_actions(names_to_actions(tbl[2])) -end - -return Groups +-- Access the exports from other modules using require("modules/exp_groups") +return require("modules/exp_groups/control") diff --git a/exp_groups/package.json b/exp_groups/package.json index 61dca7f904..05bd95794b 100644 --- a/exp_groups/package.json +++ b/exp_groups/package.json @@ -1,41 +1,49 @@ { - "name": "@expcluster/permission_groups", - "private": true, - "version": "0.0.0", - "description": "Example Description. Package. Change me in package.json", + "name": "@expcluster/permission-groups", + "version": "0.1.0", + "description": "Clusterio plugin providing syncing of permission groups", + "author": "Cooldude2606 ", + "license": "MIT", + "repository": "explosivegaming/ExpCluster", "main": "dist/node/index.js", "scripts": { - "$prepare": "tsc --build && webpack-cli --env production" + "prepare": "tsc --build && webpack-cli --env production" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@clusterio/lib": "catalog:" + "@clusterio/controller": "workspace:^", + "@clusterio/host": "workspace:^", + "@clusterio/lib": "workspace:^", + "@clusterio/web_ui": "workspace:^" }, "devDependencies": { - "@clusterio/lib": "catalog:", - "@clusterio/web_ui": "catalog:", - "@types/fs-extra": "^11.0.4", + "@ant-design/icons": "^6.2.3", + "@clusterio/controller": "workspace:^", + "@clusterio/host": "workspace:^", + "@clusterio/lib": "workspace:^", + "@clusterio/web_ui": "workspace:^", "@types/node": "catalog:", "@types/react": "catalog:", "antd": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "react-router-dom": "catalog:", "typescript": "catalog:", "webpack": "catalog:", "webpack-cli": "catalog:", "webpack-merge": "catalog:" }, "dependencies": { - "@sinclair/typebox": "catalog:", - "fs-extra": "^11.3.3" + "@sinclair/typebox": "catalog:" }, "publishConfig": { "access": "public" }, "keywords": [ "clusterio", + "clusterio-plugin", "factorio" ] -} +} \ No newline at end of file diff --git a/exp_groups/tsconfig.json b/exp_groups/tsconfig.json index be6d4e98a8..303b8f284b 100644 --- a/exp_groups/tsconfig.json +++ b/exp_groups/tsconfig.json @@ -1,7 +1,7 @@ { "files": [], "references": [ - { "path": "./tsconfig.browser.json" }, - { "path": "./tsconfig.node.json" } + { "path": "./tsconfig.node.json" }, + { "path": "./tsconfig.browser.json" } ] } diff --git a/exp_groups/web/components/AssignmentForm.tsx b/exp_groups/web/components/AssignmentForm.tsx new file mode 100644 index 0000000000..41a6a8f297 --- /dev/null +++ b/exp_groups/web/components/AssignmentForm.tsx @@ -0,0 +1,74 @@ +import React, { useContext, useEffect } from "react"; +import { Modal, Form, Select } from "antd"; + +import { ControlContext, useUsers } from "@clusterio/web_ui"; +import * as messages from "../../messages"; +import type { WebPlugin } from ".."; + +export default function AssignmentForm({ open, setOpen, initial }: { + open: boolean, + setOpen: (open: boolean) => void, + initial?: messages.AssignmentRecord, +}) { + const control = useContext(ControlContext); + const plugin = control.plugins.get("exp_groups") as WebPlugin; + + const [groups] = plugin.useGroups(); + const [users] = useUsers(); + + const [form] = Form.useForm(); + + function submit(values: any) { + if (initial) { + control.send(new messages.AssignmentUpdateRequest( + new messages.AssignmentRecord( + values.name, + values.groupId, + ) + )); + } else { + control.send(new messages.AssignmentCreateRequest( + values.name, + values.groupId, + )); + } + + setOpen(false); + } + + useEffect(() => { + if (open) { + form.resetFields(); + form.setFieldsValue(initial); + } + }, [open, initial]); + + return setOpen(false)} + onOk={() => form.submit()} + > +
+ + + + + + + +
+
; +} diff --git a/exp_groups/web/components/AssignmentsTable.tsx b/exp_groups/web/components/AssignmentsTable.tsx new file mode 100644 index 0000000000..680d9f78ee --- /dev/null +++ b/exp_groups/web/components/AssignmentsTable.tsx @@ -0,0 +1,111 @@ +import React, { useContext, useState, useRef } from "react"; +import { Table, Button, Space, Input, InputRef } from "antd"; +import { EditOutlined, SearchOutlined } from "@ant-design/icons"; + +import { ControlContext, useAccount } from "@clusterio/web_ui"; +import { AssignmentDeleteRequest, AssignmentRecord } from "../../messages"; +import type { WebPlugin } from ".."; + +import AssignmentForm from "./AssignmentForm"; +import DeletedConfirm from "./DeleteConfirm"; + +const strcmp = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }).compare; + +export default function AssignmentsTable() { + const control = useContext(ControlContext); + const plugin = control.plugins.get("exp_groups") as WebPlugin; + const account = useAccount(); + + const searchInput = useRef(null); + + const [assignments, assignmentsSynced] = plugin.useAssignments(); + const [groups, groupsSynced] = plugin.useGroups(); + + const [editing, setEditing] = useState(); + const [open, setOpen] = useState(false); + + const assignmentsArray = [...assignments.values()]; + + const groupFilters = [...groups.values()] + .filter(group => assignmentsArray.some(assignment => assignment.groupId === group.id)) + .map(group => ({ text: group.name, value: group.id })) + + return <> + strcmp(a.name, b.name), + filterIcon: (filtered: boolean) => ( + + ), + onFilter: (value, record: AssignmentRecord) => ( + record.name.toLowerCase().includes(String(value).toLowerCase()) + ), + filterDropdownProps: { + onOpenChange: (open: boolean) => open && setTimeout(() => searchInput.current?.select(), 100), + }, + filterDropdown: ({ selectedKeys, setSelectedKeys, confirm, clearFilters }) => ( +
e.stopPropagation()}> + setSelectedKeys([e.target.value])} + onSearch={() => confirm({ closeDropdown: false })} + onClear={() => { + clearFilters?.({ closeDropdown: false }); + confirm({ closeDropdown: true }); + }} + /> +
+ ), + }, + { + title: "Group", + width: "40%", + filters: groupFilters, + onFilter: (value, record: AssignmentRecord) => ( + record.groupId === value + ), + render: (_: any, a: any) => ( + groups.get(a.groupId)?.name ?? a.groupId + ), + }, + ...(account.hasAnyPermission( + "exp_groups.assignment.update", + "exp_groups.assignment.delete", + ) ? [{ + title: "Actions", + width: "10%", + render: (_: any, record: AssignmentRecord) => ( + + {account.hasPermission("exp_groups.assignment.update") && + + + + + )} + +
+ + setName(e.target.value)} /> +
+ +

Permissions

+ + {!defaultModPack?.exportManifest?.assets?.defines && ( + + )} + + {defaultModPack?.exportManifest?.assets?.defines && !definesJson && } + + {definesJson && <> + setSearch(e.target.value)} + style={{ marginBottom: 16 }} + /> + + + {grouped.map(([groupName, permissions]) => ( +
+ + + {groupName} + + + + + + + +
+ {permissions.map(p => ( + + setPermissionState(prev => ({ ...prev, [p]: e.target.checked })) + } + > + {p} + + ))} +
+
+ ))} +
+ } + ; +} diff --git a/exp_groups/web/components/GroupsTable.tsx b/exp_groups/web/components/GroupsTable.tsx new file mode 100644 index 0000000000..1dc711d90f --- /dev/null +++ b/exp_groups/web/components/GroupsTable.tsx @@ -0,0 +1,49 @@ +import React, { useContext, useState } from "react"; +import { Table } from "antd"; +import { useNavigate } from "react-router-dom"; + +import { ControlContext } from "@clusterio/web_ui"; +import { GroupRecord } from "../../messages"; +import type { WebPlugin } from ".."; + +import GroupForm from "./GroupForm"; + +const strcmp = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }).compare; + +export default function GroupsTable() { + const control = useContext(ControlContext); + const plugin = control.plugins.get("exp_groups") as WebPlugin; + const navigate = useNavigate(); + + const [groups, synced] = plugin.useGroups(); + + const [open, setOpen] = useState(false); + + return <> +
strcmp(a.name, b.name), + }, + { + title: "Permissions", + width: "50%", + render: (_: any, g: GroupRecord) => ( + `${g.permissions.permissions.length} ${g.permissions.isBlacklist ? "Disallowed" : "Allowed"}` + ), + }, + ]} + dataSource={[...groups.values()]} + loading={!synced} + rowKey={(g) => g.id} + pagination={false} + onRow={(g) => ({ + onClick: () => navigate(`/permission_groups/${g.id}/view`), + })} + /> + + + ; +} \ No newline at end of file diff --git a/exp_groups/web/components/RoleMappingForm.tsx b/exp_groups/web/components/RoleMappingForm.tsx new file mode 100644 index 0000000000..ee14330134 --- /dev/null +++ b/exp_groups/web/components/RoleMappingForm.tsx @@ -0,0 +1,106 @@ +import React, { useContext, useEffect } from "react"; +import { Modal, Form, Select, InputNumber, Switch, Alert } from "antd"; + +import { ControlContext, useRoles } from "@clusterio/web_ui"; +import { RoleMappingRecord, RoleMappingCreateRequest, RoleMappingUpdateRequest } from "../../messages"; +import type { WebPlugin } from ".."; + +export default function RoleMappingForm({ open, setOpen, initial }: { + open: boolean, + setOpen: (open: boolean) => void, + initial?: RoleMappingRecord, +}) { + const control = useContext(ControlContext); + const plugin = control.plugins.get("exp_groups") as WebPlugin; + + const [groups] = plugin.useGroups(); + const [roles] = useRoles(); + + const [form] = Form.useForm(); + + function submit(values: any) { + if (initial) { + control.send(new RoleMappingUpdateRequest( + new RoleMappingRecord( + initial.id, + new Set(values.roleIds), + values.groupId, + values.priority, + values.enabled, + ) + )); + } else { + control.send(new RoleMappingCreateRequest( + values.roleIds, + values.groupId, + values.priority, + values.enabled, + )); + } + + setOpen(false); + } + + useEffect(() => { + if (open) { + form.resetFields(); + form.setFieldsValue({ + ...initial, + roleIds: initial ? [...initial.roleIds] : [], + }); + } + }, [open, initial]); + + return setOpen(false)} + onOk={() => form.submit()} + > + +
  • Manual assignments always take priority.
  • +
  • Mappings are checked in priority order (highest first).
  • +
  • A mapping applies only if the user has all listed roles.
  • +
  • The first matching mapping assigns the group.
  • +
  • If none match, the player is given all permissions.
  • + + } + /> + +
    + + + + + + + + + + + + + + + + +
    ; +} diff --git a/exp_groups/web/components/RoleMappingsTable.tsx b/exp_groups/web/components/RoleMappingsTable.tsx new file mode 100644 index 0000000000..058774267d --- /dev/null +++ b/exp_groups/web/components/RoleMappingsTable.tsx @@ -0,0 +1,107 @@ +import React, { useContext, useState } from "react"; +import { Table, Button, Space, Tag } from "antd"; +import { EditOutlined } from "@ant-design/icons"; + +import { ControlContext, useAccount, useRoles } from "@clusterio/web_ui"; +import { RoleMappingRecord, RoleMappingDeleteRequest } from "../../messages"; +import type { WebPlugin } from ".."; + +import RoleMappingForm from "./RoleMappingForm"; +import DeletedConfirm from "./DeleteConfirm"; + +export default function RoleMappingsTable() { + const control = useContext(ControlContext); + const plugin = control.plugins.get("exp_groups") as WebPlugin; + const account = useAccount(); + + const [roleMappings, roleMappingsSynced] = plugin.useRoleMappings(); + const [groups, groupsSynced] = plugin.useGroups(); + const [roles, rolesSynced] = useRoles(); + + const [editing, setEditing] = useState(); + const [open, setOpen] = useState(false); + + const roleMappingArray = [...roleMappings.values()]; + + const roleFilters = [...roles.values()] + .filter(role => roleMappingArray.some(mapping => mapping.roleIds.has(role.id))) + .map(role => ({ text: role.name, value: role.id })); + + const groupFilters = [...groups.values()] + .filter(group => roleMappingArray.some(mapping => mapping.groupId === group.id)) + .map(group => ({ text: group.name, value: group.id })) + + return <> +
    ( + record.roleIds.has(value as number) + ), + render: (_: any, record: RoleMappingRecord) => ( + [...record.roleIds].map(id => {roles.get(id)?.name ?? id}) + ), + }, + { + title: "Group", + width: "30%", + filters: groupFilters, + onFilter: (value, record: RoleMappingRecord) => ( + record.groupId === value + ), + render: (_: any, record: RoleMappingRecord) => ( + groups.get(record.groupId)?.name ?? record.groupId + ), + }, + { + title: "Enabled", + width: "10%", + filters: [ + { text: "Enabled", value: true }, + { text: "Disabled", value: false }, + ], + onFilter: (value, record: RoleMappingRecord) => ( + record.enabled === value + ), + render: (_: any, record: RoleMappingRecord) => ( + record.enabled ? "Yes" : "No" + ), + }, + ...(account.hasAnyPermission( + "exp_groups.role_mapping.update", + "exp_groups.role_mapping.delete", + ) ? [{ + title: "Actions", + width: "10%", + render: (_: any, record: any) => ( + + {account.hasPermission("exp_groups.role_mapping.update") && + : undefined + } + /> + + + } + + {account.hasPermission("exp_groups.role_mapping.list") && <> + setRoleMappingOpen(true)}>Create + : undefined + } + /> + + + } + + {account.hasPermission("exp_groups.assignment.list") && <> + setAssignmentOpen(true)}>Create + : undefined + } + /> + + + } + ; +} + +export class WebPlugin extends BaseWebPlugin { + groups = new lib.EventSubscriber(messages.GroupUpdatedEvent, this.control); + assignments = new lib.EventSubscriber(messages.ManualAssignmentUpdatedEvent, this.control); + roleMappings = new lib.EventSubscriber(messages.RoleMappingUpdatedEvent, this.control); + + async init() { + this.pages = [ + { + path: "/permission_groups", + sidebarName: "Permission Groups", + permission: (account => account.hasAnyPermission( + "exp_groups.group.list", + "exp_groups.assignment.list", + "exp_groups.role_mapping.list", + )), + content: , + }, + { + path: "/permission_groups/:id/view", + sidebarPath: "/permission_groups", + permission: "exp_groups.group.get", + content: , + }, + ]; + } + + useGroups() { + const subscribe = useCallback((cb: () => void) => this.groups.subscribe(cb), []); + return useSyncExternalStore(subscribe, () => this.groups.getSnapshot()); + } + + useAssignments() { + const subscribe = useCallback((cb: () => void) => this.assignments.subscribe(cb), []); + return useSyncExternalStore(subscribe, () => this.assignments.getSnapshot()); + } + + useRoleMappings() { + const subscribe = useCallback((cb: () => void) => this.roleMappings.subscribe(cb), []); + return useSyncExternalStore(subscribe, () => this.roleMappings.getSnapshot()); + } +} diff --git a/exp_groups/webpack.config.js b/exp_groups/webpack.config.js index 6c269a9500..b1f0116516 100644 --- a/exp_groups/webpack.config.js +++ b/exp_groups/webpack.config.js @@ -26,6 +26,8 @@ module.exports = (env = {}) => merge(common(env), { "antd": { import: false }, "react": { import: false }, "react-dom": { import: false }, + "react-router": { import: false }, + "react-router-dom": { import: false }, }, }), ], diff --git a/exp_gui/package.json b/exp_gui/package.json index 4e112edbf3..441f7dff45 100644 --- a/exp_gui/package.json +++ b/exp_gui/package.json @@ -13,10 +13,10 @@ "node": ">=18" }, "peerDependencies": { - "@clusterio/lib": "catalog:" + "@clusterio/lib": "workspace:^" }, "devDependencies": { - "@clusterio/lib": "catalog:", + "@clusterio/lib": "workspace:^", "@types/node": "catalog:", "typescript": "catalog:", "webpack": "catalog:", diff --git a/exp_legacy/package.json b/exp_legacy/package.json index 5ab1fc1f26..70d90b2e53 100644 --- a/exp_legacy/package.json +++ b/exp_legacy/package.json @@ -13,10 +13,10 @@ "node": ">=18" }, "peerDependencies": { - "@clusterio/lib": "catalog:" + "@clusterio/lib": "workspace:^" }, "devDependencies": { - "@clusterio/lib": "catalog:", + "@clusterio/lib": "workspace:^", "@types/node": "catalog:", "typescript": "catalog:", "webpack": "catalog:", diff --git a/exp_scenario/controller.ts b/exp_scenario/controller.ts index 110c6c8611..fc761f627c 100644 --- a/exp_scenario/controller.ts +++ b/exp_scenario/controller.ts @@ -1,7 +1,6 @@ import * as lib from "@clusterio/lib"; -import { BaseControllerPlugin, InstanceInfo } from "@clusterio/controller"; +import { BaseControllerPlugin } from "@clusterio/controller"; export class ControllerPlugin extends BaseControllerPlugin { - async init() { - } + } diff --git a/exp_scenario/package.json b/exp_scenario/package.json index 93ba07b1b1..a7abd8c2fc 100644 --- a/exp_scenario/package.json +++ b/exp_scenario/package.json @@ -13,11 +13,11 @@ "node": ">=18" }, "peerDependencies": { - "@clusterio/lib": "catalog:" + "@clusterio/lib": "workspace:^" }, "devDependencies": { - "@clusterio/lib": "catalog:", - "@clusterio/web_ui": "catalog:", + "@clusterio/lib": "workspace:^", + "@clusterio/web_ui": "workspace:^", "@types/node": "catalog:", "@types/react": "catalog:", "antd": "catalog:", diff --git a/exp_scenario/web/~index.tsx b/exp_scenario/web/~index.tsx deleted file mode 100644 index f4d7ff80d0..0000000000 --- a/exp_scenario/web/~index.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { - useContext, useEffect, useState, - useCallback, useSyncExternalStore, -} from "react"; - -// import { -// -// } from "antd"; - -import { - BaseWebPlugin, PageLayout, PageHeader, Control, ControlContext, notifyErrorHandler, -} from "@clusterio/web_ui"; - -import { - PluginExampleEvent, PluginExampleRequest, - ExampleSubscribableUpdate, ExampleSubscribableValue, -} from "../messages"; - -import * as lib from "@clusterio/lib"; - -function MyTemplatePage() { - const control = useContext(ControlContext); - const plugin = control.plugins.get("exp_scenario") as WebPlugin; - const [subscribableData, synced] = plugin.useSubscribableData(); - - return - - Synced: {String(synced)} Data: {JSON.stringify([...subscribableData.values()])} - ; -} - -export class WebPlugin extends BaseWebPlugin { - subscribableData = new lib.EventSubscriber(ExampleSubscribableUpdate, this.control); - - async init() { - this.pages = [ - { - path: "/exp_scenario", - sidebarName: "exp_scenario", - // This permission is client side only, so it must match the permission string of a resource request to be secure - // An undefined value means that the page will always be visible - permission: "exp_scenario.example.permission.subscribe", - content: , - }, - ]; - - this.control.handle(PluginExampleEvent, this.handlePluginExampleEvent.bind(this)); - this.control.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this)); - } - - useSubscribableData() { - const control = useContext(ControlContext); - const subscribe = useCallback((callback: () => void) => this.subscribableData.subscribe(callback), [control]); - return useSyncExternalStore(subscribe, () => this.subscribableData.getSnapshot()); - } - - async handlePluginExampleEvent(event: PluginExampleEvent) { - this.logger.info(JSON.stringify(event)); - } - - async handlePluginExampleRequest(request: PluginExampleRequest) { - this.logger.info(JSON.stringify(request)); - return { - myResponseString: request.myString, - myResponseNumbers: request.myNumberArray, - }; - } -} diff --git a/exp_server_ups/package.json b/exp_server_ups/package.json index b446a746cb..3cd9a76a95 100644 --- a/exp_server_ups/package.json +++ b/exp_server_ups/package.json @@ -1,5 +1,5 @@ { - "name": "@expcluster/server_ups", + "name": "@expcluster/server-ups", "version": "0.1.0", "description": "Clusterio plugin providing in game server ups counter", "author": "Cooldude2606 ", @@ -13,12 +13,12 @@ "node": ">=18" }, "peerDependencies": { - "@clusterio/lib": "catalog:" + "@clusterio/lib": "workspace:^" }, "devDependencies": { + "@clusterio/lib": "workspace:^", "typescript": "catalog:", "@types/node": "catalog:", - "@clusterio/lib": "catalog:", "webpack": "catalog:", "webpack-cli": "catalog:", "webpack-merge": "catalog:" diff --git a/exp_util/package.json b/exp_util/package.json index 4ca7677513..ac95386f84 100644 --- a/exp_util/package.json +++ b/exp_util/package.json @@ -13,10 +13,10 @@ "node": ">=18" }, "peerDependencies": { - "@clusterio/lib": "catalog:" + "@clusterio/lib": "workspace:^" }, "devDependencies": { - "@clusterio/lib": "catalog:", + "@clusterio/lib": "workspace:^", "@types/node": "catalog:", "typescript": "catalog:", "webpack": "catalog:", diff --git a/package.json b/package.json deleted file mode 100644 index 4b00e095a4..0000000000 --- a/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "root", - "private": true, - "files": [], - "scripts": { - "build": "tsc --build", - "watch": "tsc --build --watch" - }, - "devDependencies": { - "typescript": "catalog:" - } -} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index ae212b8820..0000000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,3782 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: false - excludeLinksFromLockfile: false - -catalogs: - default: - '@clusterio/lib': - specifier: 2.0.0-alpha.22b - version: 2.0.0-alpha.22b - '@clusterio/web_ui': - specifier: 2.0.0-alpha.22b - version: 2.0.0-alpha.22b - '@sinclair/typebox': - specifier: ^0.30.4 - version: 0.30.4 - '@types/node': - specifier: ^20.19.29 - version: 20.19.29 - '@types/react': - specifier: 18.2.0 - version: 18.2.0 - antd: - specifier: 5.24.2 - version: 5.24.2 - react: - specifier: 18.2.0 - version: 18.2.0 - react-dom: - specifier: 18.2.0 - version: 18.2.0 - typescript: - specifier: ^5.9.3 - version: 5.9.3 - webpack: - specifier: 5.98.0 - version: 5.98.0 - webpack-cli: - specifier: 5.1.4 - version: 5.1.4 - webpack-merge: - specifier: 5.9.0 - version: 5.9.0 - -importers: - - .: - devDependencies: - typescript: - specifier: 'catalog:' - version: 5.9.3 - - exp_commands: - dependencies: - '@expcluster/lib_util': - specifier: workspace:^ - version: link:../exp_util - '@sinclair/typebox': - specifier: 'catalog:' - version: 0.30.4 - devDependencies: - '@clusterio/lib': - specifier: 'catalog:' - version: 2.0.0-alpha.22b - '@types/node': - specifier: 'catalog:' - version: 20.19.29 - typescript: - specifier: 'catalog:' - version: 5.9.3 - webpack: - specifier: 'catalog:' - version: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-cli: - specifier: 'catalog:' - version: 5.1.4(webpack@5.98.0) - webpack-merge: - specifier: 'catalog:' - version: 5.9.0 - - exp_groups: - dependencies: - '@sinclair/typebox': - specifier: 'catalog:' - version: 0.30.4 - fs-extra: - specifier: ^11.3.3 - version: 11.3.3 - devDependencies: - '@clusterio/lib': - specifier: 'catalog:' - version: 2.0.0-alpha.22b - '@clusterio/web_ui': - specifier: 'catalog:' - version: 2.0.0-alpha.22b - '@types/fs-extra': - specifier: ^11.0.4 - version: 11.0.4 - '@types/node': - specifier: 'catalog:' - version: 20.19.29 - '@types/react': - specifier: 'catalog:' - version: 18.2.0 - antd: - specifier: 'catalog:' - version: 5.24.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: - specifier: 'catalog:' - version: 18.2.0 - react-dom: - specifier: 'catalog:' - version: 18.2.0(react@18.2.0) - typescript: - specifier: 'catalog:' - version: 5.9.3 - webpack: - specifier: 'catalog:' - version: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-cli: - specifier: 'catalog:' - version: 5.1.4(webpack@5.98.0) - webpack-merge: - specifier: 'catalog:' - version: 5.9.0 - - exp_gui: - dependencies: - '@expcluster/lib_util': - specifier: workspace:^ - version: link:../exp_util - '@sinclair/typebox': - specifier: 'catalog:' - version: 0.30.4 - devDependencies: - '@clusterio/lib': - specifier: 'catalog:' - version: 2.0.0-alpha.22b - '@types/node': - specifier: 'catalog:' - version: 20.19.29 - typescript: - specifier: 'catalog:' - version: 5.9.3 - webpack: - specifier: 'catalog:' - version: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-cli: - specifier: 'catalog:' - version: 5.1.4(webpack@5.98.0) - webpack-merge: - specifier: 'catalog:' - version: 5.9.0 - - exp_legacy: - dependencies: - '@expcluster/lib_commands': - specifier: workspace:^ - version: link:../exp_commands - '@expcluster/lib_util': - specifier: workspace:^ - version: link:../exp_util - '@sinclair/typebox': - specifier: 'catalog:' - version: 0.30.4 - devDependencies: - '@clusterio/lib': - specifier: 'catalog:' - version: 2.0.0-alpha.22b - '@types/node': - specifier: 'catalog:' - version: 20.19.29 - typescript: - specifier: 'catalog:' - version: 5.9.3 - webpack: - specifier: 'catalog:' - version: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-cli: - specifier: 'catalog:' - version: 5.1.4(webpack@5.98.0) - webpack-merge: - specifier: 'catalog:' - version: 5.9.0 - - exp_scenario: - dependencies: - '@expcluster/lib_commands': - specifier: workspace:^ - version: link:../exp_commands - '@expcluster/lib_util': - specifier: workspace:^ - version: link:../exp_util - '@sinclair/typebox': - specifier: 'catalog:' - version: 0.30.4 - devDependencies: - '@clusterio/lib': - specifier: 'catalog:' - version: 2.0.0-alpha.22b - '@clusterio/web_ui': - specifier: 'catalog:' - version: 2.0.0-alpha.22b - '@types/node': - specifier: 'catalog:' - version: 20.19.29 - '@types/react': - specifier: 'catalog:' - version: 18.2.0 - antd: - specifier: 'catalog:' - version: 5.24.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: - specifier: 'catalog:' - version: 18.2.0 - react-dom: - specifier: 'catalog:' - version: 18.2.0(react@18.2.0) - typescript: - specifier: 'catalog:' - version: 5.9.3 - webpack: - specifier: 'catalog:' - version: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-cli: - specifier: 'catalog:' - version: 5.1.4(webpack@5.98.0) - webpack-merge: - specifier: 'catalog:' - version: 5.9.0 - - exp_server_ups: - dependencies: - '@expcluster/lib_commands': - specifier: workspace:^ - version: link:../exp_commands - '@expcluster/lib_gui': - specifier: workspace:^ - version: link:../exp_gui - '@expcluster/lib_util': - specifier: workspace:^ - version: link:../exp_util - '@sinclair/typebox': - specifier: 'catalog:' - version: 0.30.4 - devDependencies: - '@clusterio/lib': - specifier: 'catalog:' - version: 2.0.0-alpha.22b - '@types/node': - specifier: 'catalog:' - version: 20.19.29 - typescript: - specifier: 'catalog:' - version: 5.9.3 - webpack: - specifier: 'catalog:' - version: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-cli: - specifier: 'catalog:' - version: 5.1.4(webpack@5.98.0) - webpack-merge: - specifier: 'catalog:' - version: 5.9.0 - - exp_util: - dependencies: - '@sinclair/typebox': - specifier: 'catalog:' - version: 0.30.4 - devDependencies: - '@clusterio/lib': - specifier: 'catalog:' - version: 2.0.0-alpha.22b - '@types/node': - specifier: 'catalog:' - version: 20.19.29 - typescript: - specifier: 'catalog:' - version: 5.9.3 - webpack: - specifier: 'catalog:' - version: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-cli: - specifier: 'catalog:' - version: 5.1.4(webpack@5.98.0) - webpack-merge: - specifier: 'catalog:' - version: 5.9.0 - -packages: - - '@ant-design/colors@7.2.1': - resolution: {integrity: sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==} - - '@ant-design/cssinjs-utils@1.1.3': - resolution: {integrity: sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@ant-design/cssinjs@1.24.0': - resolution: {integrity: sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@ant-design/fast-color@2.0.6': - resolution: {integrity: sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==} - engines: {node: '>=8.x'} - - '@ant-design/icons-svg@4.4.2': - resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} - - '@ant-design/icons@5.6.1': - resolution: {integrity: sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==} - engines: {node: '>=8'} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - '@ant-design/react-slick@1.1.2': - resolution: {integrity: sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==} - peerDependencies: - react: '>=16.9.0' - - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} - engines: {node: '>=6.9.0'} - - '@clusterio/lib@2.0.0-alpha.22b': - resolution: {integrity: sha512-tzuRsAaUBkgWQIhBmvuR4ynP7oxREEHibuLNaWk3mjYl4k4boDdiPFcYhJBrdYoteYVnsS5uH6iY+14Zsepw1w==} - engines: {node: '>=12'} - - '@clusterio/web_ui@2.0.0-alpha.22b': - resolution: {integrity: sha512-NLVNJ77dpDDczngdqgjSWtiZKXGsrW28wM0jIhnAshPlU5VkP2d/0Z/MDLS1/CzkSvHxghNsTosaxiMN6am3mw==} - engines: {node: '>=12'} - - '@colors/colors@1.6.0': - resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} - engines: {node: '>=0.1.90'} - - '@dabh/diagnostics@2.0.8': - resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==} - - '@discoveryjs/json-ext@0.5.7': - resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} - engines: {node: '>=10.0.0'} - - '@emotion/hash@0.8.0': - resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} - - '@emotion/unitless@0.7.5': - resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} - - '@isaacs/balanced-match@4.0.1': - resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} - engines: {node: 20 || >=22} - - '@isaacs/brace-expansion@5.0.0': - resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} - engines: {node: 20 || >=22} - - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/source-map@0.3.11': - resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@rc-component/async-validator@5.1.0': - resolution: {integrity: sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA==} - engines: {node: '>=14.x'} - - '@rc-component/color-picker@2.0.1': - resolution: {integrity: sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/context@1.4.0': - resolution: {integrity: sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/mini-decimal@1.1.0': - resolution: {integrity: sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==} - engines: {node: '>=8.x'} - - '@rc-component/mutate-observer@1.1.0': - resolution: {integrity: sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/portal@1.1.2': - resolution: {integrity: sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/qrcode@1.0.1': - resolution: {integrity: sha512-g8eeeaMyFXVlq8cZUeaxCDhfIYjpao0l9cvm5gFwKXy/Vm1yDWV7h2sjH5jHYzdFedlVKBpATFB1VKMrHzwaWQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/tour@1.15.1': - resolution: {integrity: sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@rc-component/trigger@2.3.1': - resolution: {integrity: sha512-ORENF39PeXTzM+gQEshuk460Z8N4+6DkjpxlpE7Q3gYy1iBpLrx0FOJz3h62ryrJZ/3zCAUIkT1Pb/8hHWpb3A==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - '@remix-run/router@1.23.2': - resolution: {integrity: sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==} - engines: {node: '>=14.0.0'} - - '@sinclair/typebox@0.30.4': - resolution: {integrity: sha512-wFuuDR+O1OAE2GL0q68h1Ty00RE6Ihcixr55A6TU5RCvOUHnwJw9LGuDVg9NxDiAp7m/YJpa+UaOuLAz0ziyOQ==} - - '@so-ric/colorspace@1.1.6': - resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} - - '@swc/core-darwin-arm64@1.15.8': - resolution: {integrity: sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==} - engines: {node: '>=10'} - cpu: [arm64] - os: [darwin] - - '@swc/core-darwin-x64@1.15.8': - resolution: {integrity: sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==} - engines: {node: '>=10'} - cpu: [x64] - os: [darwin] - - '@swc/core-linux-arm-gnueabihf@1.15.8': - resolution: {integrity: sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==} - engines: {node: '>=10'} - cpu: [arm] - os: [linux] - - '@swc/core-linux-arm64-gnu@1.15.8': - resolution: {integrity: sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - - '@swc/core-linux-arm64-musl@1.15.8': - resolution: {integrity: sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - - '@swc/core-linux-x64-gnu@1.15.8': - resolution: {integrity: sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - - '@swc/core-linux-x64-musl@1.15.8': - resolution: {integrity: sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - - '@swc/core-win32-arm64-msvc@1.15.8': - resolution: {integrity: sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==} - engines: {node: '>=10'} - cpu: [arm64] - os: [win32] - - '@swc/core-win32-ia32-msvc@1.15.8': - resolution: {integrity: sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==} - engines: {node: '>=10'} - cpu: [ia32] - os: [win32] - - '@swc/core-win32-x64-msvc@1.15.8': - resolution: {integrity: sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==} - engines: {node: '>=10'} - cpu: [x64] - os: [win32] - - '@swc/core@1.15.8': - resolution: {integrity: sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==} - engines: {node: '>=10'} - peerDependencies: - '@swc/helpers': '>=0.5.17' - peerDependenciesMeta: - '@swc/helpers': - optional: true - - '@swc/counter@0.1.3': - resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - - '@swc/types@0.1.25': - resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} - - '@types/eslint-scope@3.7.7': - resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} - - '@types/eslint@9.6.1': - resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/fs-extra@11.0.4': - resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} - - '@types/glob@7.2.0': - resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/jsonfile@6.1.4': - resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} - - '@types/minimatch@6.0.0': - resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} - deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. - - '@types/node@20.19.29': - resolution: {integrity: sha512-YrT9ArrGaHForBaCNwFjoqJWmn8G1Pr7+BH/vwyLHciA9qT/wSiuOhxGCT50JA5xLvFBd6PIiGkE3afxcPE1nw==} - - '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} - - '@types/react@18.2.0': - resolution: {integrity: sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==} - - '@types/scheduler@0.26.0': - resolution: {integrity: sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA==} - - '@types/triple-beam@1.3.5': - resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - - '@webassemblyjs/ast@1.14.1': - resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} - - '@webassemblyjs/floating-point-hex-parser@1.13.2': - resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} - - '@webassemblyjs/helper-api-error@1.13.2': - resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} - - '@webassemblyjs/helper-buffer@1.14.1': - resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} - - '@webassemblyjs/helper-numbers@1.13.2': - resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} - - '@webassemblyjs/helper-wasm-bytecode@1.13.2': - resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} - - '@webassemblyjs/helper-wasm-section@1.14.1': - resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} - - '@webassemblyjs/ieee754@1.13.2': - resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} - - '@webassemblyjs/leb128@1.13.2': - resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} - - '@webassemblyjs/utf8@1.13.2': - resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} - - '@webassemblyjs/wasm-edit@1.14.1': - resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} - - '@webassemblyjs/wasm-gen@1.14.1': - resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} - - '@webassemblyjs/wasm-opt@1.14.1': - resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} - - '@webassemblyjs/wasm-parser@1.14.1': - resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} - - '@webassemblyjs/wast-printer@1.14.1': - resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} - - '@webpack-cli/configtest@2.1.1': - resolution: {integrity: sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==} - engines: {node: '>=14.15.0'} - peerDependencies: - webpack: 5.x.x - webpack-cli: 5.x.x - - '@webpack-cli/info@2.0.2': - resolution: {integrity: sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==} - engines: {node: '>=14.15.0'} - peerDependencies: - webpack: 5.x.x - webpack-cli: 5.x.x - - '@webpack-cli/serve@2.0.5': - resolution: {integrity: sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==} - engines: {node: '>=14.15.0'} - peerDependencies: - webpack: 5.x.x - webpack-cli: 5.x.x - webpack-dev-server: '*' - peerDependenciesMeta: - webpack-dev-server: - optional: true - - '@xtuc/ieee754@1.2.0': - resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} - - '@xtuc/long@4.2.2': - resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} - - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true - - ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - - ajv-keywords@3.5.2: - resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} - peerDependencies: - ajv: ^6.9.1 - - ajv-keywords@5.1.0: - resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} - peerDependencies: - ajv: ^8.8.2 - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - antd@5.24.2: - resolution: {integrity: sha512-7Z9HsE3ZIK3sE/WuUqii3w7Gl1IJuRL21sDUTtkN95JS5KhRYP8ISv7m/HxsJ3Mn/yxgojBCgLPJ212+Dn+aPw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - array-union@1.0.2: - resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==} - engines: {node: '>=0.10.0'} - - array-uniq@1.0.3: - resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} - engines: {node: '>=0.10.0'} - - assert@2.1.0: - resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} - - async-validator@4.2.5: - resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} - - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - baseline-browser-mapping@2.9.14: - resolution: {integrity: sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==} - hasBin: true - - big.js@5.2.2: - resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} - - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - - browserify-zlib@0.2.0: - resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} - - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - caniuse-lite@1.0.30001764: - resolution: {integrity: sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chrome-trace-event@1.0.4: - resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} - engines: {node: '>=6.0'} - - classnames@2.5.1: - resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - - clean-webpack-plugin@4.0.0: - resolution: {integrity: sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==} - engines: {node: '>=10.0.0'} - peerDependencies: - webpack: '>=4.0.0 <6.0.0' - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - clone-deep@4.0.1: - resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} - engines: {node: '>=6'} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-convert@3.1.3: - resolution: {integrity: sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==} - engines: {node: '>=14.6'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - color-name@2.1.0: - resolution: {integrity: sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==} - engines: {node: '>=12.20'} - - color-string@2.1.4: - resolution: {integrity: sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==} - engines: {node: '>=18'} - - color@5.0.3: - resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==} - engines: {node: '>=18'} - - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - - commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - - compute-scroll-into-view@3.1.1: - resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - copy-to-clipboard@3.3.3: - resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - css-loader@6.11.0: - resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} - engines: {node: '>= 12.13.0'} - peerDependencies: - '@rspack/core': 0.x || 1.x - webpack: ^5.0.0 - peerDependenciesMeta: - '@rspack/core': - optional: true - webpack: - optional: true - - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - - dayjs@1.11.19: - resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - del@4.1.1: - resolution: {integrity: sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==} - engines: {node: '>=6'} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - electron-to-chromium@1.5.267: - resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emojis-list@3.0.0: - resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} - engines: {node: '>= 4'} - - enabled@2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - - enhanced-resolve@5.18.4: - resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} - engines: {node: '>=10.13.0'} - - envinfo@7.21.0: - resolution: {integrity: sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==} - engines: {node: '>=4'} - hasBin: true - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - - fastest-levenshtein@1.0.16: - resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} - engines: {node: '>= 4.9.1'} - - fecha@4.2.3: - resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - - file-loader@6.2.0: - resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - fn.name@1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - - fs-extra@11.3.3: - resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} - engines: {node: '>=14.14'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - generator-function@2.0.1: - resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} - engines: {node: '>= 0.4'} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - glob-to-regexp@0.4.1: - resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - - globby@6.1.0: - resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==} - engines: {node: '>=0.10.0'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - icss-utils@5.1.0: - resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - immediate@3.0.6: - resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - - import-local@3.2.0: - resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} - engines: {node: '>=8'} - hasBin: true - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - interpret@3.1.1: - resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} - engines: {node: '>=10.13.0'} - - is-arguments@1.2.0: - resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-generator-function@1.1.2: - resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} - engines: {node: '>= 0.4'} - - is-nan@1.3.2: - resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} - engines: {node: '>= 0.4'} - - is-path-cwd@2.2.0: - resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} - engines: {node: '>=6'} - - is-path-in-cwd@2.1.0: - resolution: {integrity: sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==} - engines: {node: '>=6'} - - is-path-inside@2.1.0: - resolution: {integrity: sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==} - engines: {node: '>=6'} - - is-plain-object@2.0.4: - resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} - engines: {node: '>=0.10.0'} - - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - isobject@3.0.1: - resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} - engines: {node: '>=0.10.0'} - - jest-worker@27.5.1: - resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} - engines: {node: '>= 10.13.0'} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json2mq@0.2.0: - resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} - - jszip@3.10.1: - resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - klaw@4.1.0: - resolution: {integrity: sha512-1zGZ9MF9H22UnkpVeuaGKOjfA2t6WrfdrJmGjy16ykcjnKQDmHVX+KI477rpbGevz/5FD4MC3xf1oxylBgcaQw==} - engines: {node: '>=14.14.0'} - - kuler@2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - - lie@3.3.0: - resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} - - loader-runner@4.3.1: - resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} - engines: {node: '>=6.11.5'} - - loader-utils@2.0.4: - resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} - engines: {node: '>=8.9.0'} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - logform@2.7.0: - resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} - engines: {node: '>= 12.0.0'} - - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - minimatch@10.1.1: - resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} - engines: {node: 20 || >=22} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - - null-loader@4.0.1: - resolution: {integrity: sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - one-time@1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} - - os-browserify@0.3.0: - resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-is-inside@1.0.2: - resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} - - pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - - pinkie-promise@2.0.1: - resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} - engines: {node: '>=0.10.0'} - - pinkie@2.0.4: - resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} - engines: {node: '>=0.10.0'} - - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - - postcss-modules-extract-imports@3.1.0: - resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - postcss-modules-local-by-default@4.2.0: - resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - postcss-modules-scope@3.2.1: - resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - postcss-modules-values@4.0.0: - resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - postcss-selector-parser@7.1.1: - resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} - engines: {node: '>=4'} - - postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} - engines: {node: ^10 || ^12 || >=14} - - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - - rc-cascader@3.33.1: - resolution: {integrity: sha512-Kyl4EJ7ZfCBuidmZVieegcbFw0RcU5bHHSbtEdmuLYd0fYHCAiYKZ6zon7fWAVyC6rWWOOib0XKdTSf7ElC9rg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-checkbox@3.5.0: - resolution: {integrity: sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-collapse@3.9.0: - resolution: {integrity: sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-dialog@9.6.0: - resolution: {integrity: sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-drawer@7.2.0: - resolution: {integrity: sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-dropdown@4.2.1: - resolution: {integrity: sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==} - peerDependencies: - react: '>=16.11.0' - react-dom: '>=16.11.0' - - rc-field-form@1.44.0: - resolution: {integrity: sha512-el7w87fyDUsca63Y/s8qJcq9kNkf/J5h+iTdqG5WsSHLH0e6Usl7QuYSmSVzJMgtp40mOVZIY/W/QP9zwrp1FA==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-field-form@2.7.1: - resolution: {integrity: sha512-vKeSifSJ6HoLaAB+B8aq/Qgm8a3dyxROzCtKNCsBQgiverpc4kWDQihoUwzUj+zNWJOykwSY4dNX3QrGwtVb9A==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-image@7.11.1: - resolution: {integrity: sha512-XuoWx4KUXg7hNy5mRTy1i8c8p3K8boWg6UajbHpDXS5AlRVucNfTi5YxTtPBTBzegxAZpvuLfh3emXFt6ybUdA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-input-number@9.4.0: - resolution: {integrity: sha512-Tiy4DcXcFXAf9wDhN8aUAyMeCLHJUHA/VA/t7Hj8ZEx5ETvxG7MArDOSE6psbiSCo+vJPm4E3fGN710ITVn6GA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-input@1.7.3: - resolution: {integrity: sha512-A5w4egJq8+4JzlQ55FfQjDnPvOaAbzwC3VLOAdOytyek3TboSOP9qxN+Gifup+shVXfvecBLBbWBpWxmk02SWQ==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - rc-mentions@2.19.1: - resolution: {integrity: sha512-KK3bAc/bPFI993J3necmaMXD2reZTzytZdlTvkeBbp50IGH1BDPDvxLdHDUrpQx2b2TGaVJsn+86BvYa03kGqA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-menu@9.16.1: - resolution: {integrity: sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-motion@2.9.5: - resolution: {integrity: sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-notification@5.6.4: - resolution: {integrity: sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-overflow@1.5.0: - resolution: {integrity: sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-pagination@5.1.0: - resolution: {integrity: sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-picker@4.11.3: - resolution: {integrity: sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==} - engines: {node: '>=8.x'} - peerDependencies: - date-fns: '>= 2.x' - dayjs: '>= 1.x' - luxon: '>= 3.x' - moment: '>= 2.x' - react: '>=16.9.0' - react-dom: '>=16.9.0' - peerDependenciesMeta: - date-fns: - optional: true - dayjs: - optional: true - luxon: - optional: true - moment: - optional: true - - rc-progress@4.0.0: - resolution: {integrity: sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-rate@2.13.1: - resolution: {integrity: sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-resize-observer@1.4.3: - resolution: {integrity: sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-segmented@2.7.1: - resolution: {integrity: sha512-izj1Nw/Dw2Vb7EVr+D/E9lUTkBe+kKC+SAFSU9zqr7WV2W5Ktaa9Gc7cB2jTqgk8GROJayltaec+DBlYKc6d+g==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' - - rc-select@14.16.8: - resolution: {integrity: sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==} - engines: {node: '>=8.x'} - peerDependencies: - react: '*' - react-dom: '*' - - rc-slider@11.1.9: - resolution: {integrity: sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-steps@6.0.1: - resolution: {integrity: sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-switch@4.1.0: - resolution: {integrity: sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-table@7.50.5: - resolution: {integrity: sha512-FDZu8aolhSYd3v9KOc3lZOVAU77wmRRu44R0Wfb8Oj1dXRUsloFaXMSl6f7yuWZUxArJTli7k8TEOX2mvhDl4A==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-tabs@15.5.2: - resolution: {integrity: sha512-Hbqf2IV6k/jPgfMjPtIDmPV0D0C9c/fN4B/fYcoh9qqaUzUZQoK0PYzsV3UaV+3UsmyoYt48p74m/HkLhGTw+w==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-textarea@1.9.0: - resolution: {integrity: sha512-dQW/Bc/MriPBTugj2Kx9PMS5eXCCGn2cxoIaichjbNvOiARlaHdI99j4DTxLl/V8+PIfW06uFy7kjfUIDDKyxQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-tooltip@6.4.0: - resolution: {integrity: sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-tree-select@5.27.0: - resolution: {integrity: sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==} - peerDependencies: - react: '*' - react-dom: '*' - - rc-tree@5.13.1: - resolution: {integrity: sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==} - engines: {node: '>=10.x'} - peerDependencies: - react: '*' - react-dom: '*' - - rc-upload@4.8.1: - resolution: {integrity: sha512-toEAhwl4hjLAI1u8/CgKWt30BR06ulPa4iGQSMvSXoHzO88gPCslxqV/mnn4gJU7PDoltGIC9Eh+wkeudqgHyw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-util@5.44.4: - resolution: {integrity: sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-virtual-list@3.19.2: - resolution: {integrity: sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - react-dom@18.2.0: - resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} - peerDependencies: - react: ^18.2.0 - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - - react-router-dom@6.30.3: - resolution: {integrity: sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' - - react-router@6.30.3: - resolution: {integrity: sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' - - react@18.2.0: - resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} - engines: {node: '>=0.10.0'} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - rechoir@0.8.0: - resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} - engines: {node: '>= 10.13.0'} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - resize-observer-polyfill@1.5.1: - resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} - - resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} - engines: {node: '>= 0.4'} - hasBin: true - - rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - - safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} - - scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} - - schema-utils@3.3.0: - resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} - engines: {node: '>= 10.13.0'} - - schema-utils@4.3.3: - resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} - engines: {node: '>= 10.13.0'} - - scroll-into-view-if-needed@3.1.0: - resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} - - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} - hasBin: true - - serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - - shallow-clone@3.0.1: - resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} - engines: {node: '>=8'} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - source-list-map@2.0.1: - resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - - stream-browserify@3.0.0: - resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} - - string-convert@0.2.1: - resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - style-loader@3.3.4: - resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 - - stylis@4.3.6: - resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - swc-loader@0.2.7: - resolution: {integrity: sha512-nwYWw3Fh9ame3Rtm7StS9SBLpHRRnYcK7bnpF3UKZmesAK0gw2/ADvlURFAINmPvKtDLzp+GBiP9yLoEjg6S9w==} - peerDependencies: - '@swc/core': ^1.2.147 - webpack: '>=2' - - tapable@2.3.0: - resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} - engines: {node: '>=6'} - - terser-webpack-plugin@5.3.16: - resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@swc/core': '*' - esbuild: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - esbuild: - optional: true - uglify-js: - optional: true - - terser@5.44.1: - resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} - engines: {node: '>=10'} - hasBin: true - - text-hex@1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - - throttle-debounce@5.0.2: - resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} - engines: {node: '>=12.22'} - - toggle-selection@1.0.6: - resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} - - triple-beam@1.4.1: - resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} - engines: {node: '>= 14.0.0'} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - - watchpack@2.5.0: - resolution: {integrity: sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==} - engines: {node: '>=10.13.0'} - - webpack-cli@5.1.4: - resolution: {integrity: sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==} - engines: {node: '>=14.15.0'} - hasBin: true - peerDependencies: - '@webpack-cli/generators': '*' - webpack: 5.x.x - webpack-bundle-analyzer: '*' - webpack-dev-server: '*' - peerDependenciesMeta: - '@webpack-cli/generators': - optional: true - webpack-bundle-analyzer: - optional: true - webpack-dev-server: - optional: true - - webpack-manifest-plugin@5.0.1: - resolution: {integrity: sha512-xTlX7dC3hrASixA2inuWFMz6qHsNi6MT3Uiqw621sJjRTShtpMjbDYhPPZBwWUKdIYKIjSq9em6+uzWayf38aQ==} - engines: {node: '>=14'} - peerDependencies: - webpack: ^5.75.0 - - webpack-merge@5.9.0: - resolution: {integrity: sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==} - engines: {node: '>=10.0.0'} - - webpack-sources@2.3.1: - resolution: {integrity: sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==} - engines: {node: '>=10.13.0'} - - webpack-sources@3.3.3: - resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} - engines: {node: '>=10.13.0'} - - webpack@5.98.0: - resolution: {integrity: sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - - which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} - engines: {node: '>= 0.4'} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - wildcard@2.0.1: - resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} - - winston-transport@4.9.0: - resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==} - engines: {node: '>= 12.0.0'} - - winston@3.19.0: - resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==} - engines: {node: '>= 12.0.0'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - -snapshots: - - '@ant-design/colors@7.2.1': - dependencies: - '@ant-design/fast-color': 2.0.6 - - '@ant-design/cssinjs-utils@1.1.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@ant-design/cssinjs': 1.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@babel/runtime': 7.28.6 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - '@ant-design/cssinjs@1.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.28.6 - '@emotion/hash': 0.8.0 - '@emotion/unitless': 0.7.5 - classnames: 2.5.1 - csstype: 3.2.3 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - stylis: 4.3.6 - - '@ant-design/fast-color@2.0.6': - dependencies: - '@babel/runtime': 7.28.6 - - '@ant-design/icons-svg@4.4.2': {} - - '@ant-design/icons@5.6.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@ant-design/colors': 7.2.1 - '@ant-design/icons-svg': 4.4.2 - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - '@ant-design/react-slick@1.1.2(react@18.2.0)': - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - json2mq: 0.2.0 - react: 18.2.0 - resize-observer-polyfill: 1.5.1 - throttle-debounce: 5.0.2 - - '@babel/runtime@7.28.6': {} - - '@clusterio/lib@2.0.0-alpha.22b': - dependencies: - '@sinclair/typebox': 0.30.4 - ajv: 8.17.1 - chalk: 4.1.2 - fast-deep-equal: 3.1.3 - fs-extra: 11.3.3 - jszip: 3.10.1 - klaw: 4.1.0 - set-blocking: 2.0.0 - triple-beam: 1.4.1 - winston: 3.19.0 - winston-transport: 4.9.0 - ws: 8.19.0 - yargs: 17.7.2 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@clusterio/web_ui@2.0.0-alpha.22b': - dependencies: - '@ant-design/icons': 5.6.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@clusterio/lib': 2.0.0-alpha.22b - '@sinclair/typebox': 0.30.4 - '@swc/core': 1.15.8 - antd: 5.24.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - assert: 2.1.0 - browserify-zlib: 0.2.0 - buffer: 6.0.3 - clean-webpack-plugin: 4.0.0(webpack@5.98.0) - css-loader: 6.11.0(webpack@5.98.0) - events: 3.3.0 - file-loader: 6.2.0(webpack@5.98.0) - null-loader: 4.0.1(webpack@5.98.0) - os-browserify: 0.3.0 - path-browserify: 1.0.1 - process: 0.11.10 - rc-field-form: 1.44.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-router-dom: 6.30.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - stream-browserify: 3.0.0 - style-loader: 3.3.4(webpack@5.98.0) - swc-loader: 0.2.7(@swc/core@1.15.8)(webpack@5.98.0) - terser-webpack-plugin: 5.3.16(@swc/core@1.15.8)(webpack@5.98.0) - typescript: 5.9.3 - util: 0.12.5 - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack@5.98.0) - webpack-manifest-plugin: 5.0.1(webpack@5.98.0) - transitivePeerDependencies: - - '@rspack/core' - - '@swc/helpers' - - '@webpack-cli/generators' - - bufferutil - - date-fns - - esbuild - - luxon - - moment - - uglify-js - - utf-8-validate - - webpack-bundle-analyzer - - webpack-dev-server - - '@colors/colors@1.6.0': {} - - '@dabh/diagnostics@2.0.8': - dependencies: - '@so-ric/colorspace': 1.1.6 - enabled: 2.0.0 - kuler: 2.0.0 - - '@discoveryjs/json-ext@0.5.7': {} - - '@emotion/hash@0.8.0': {} - - '@emotion/unitless@0.7.5': {} - - '@isaacs/balanced-match@4.0.1': {} - - '@isaacs/brace-expansion@5.0.0': - dependencies: - '@isaacs/balanced-match': 4.0.1 - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/source-map@0.3.11': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@rc-component/async-validator@5.1.0': - dependencies: - '@babel/runtime': 7.28.6 - - '@rc-component/color-picker@2.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@ant-design/fast-color': 2.0.6 - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - '@rc-component/context@1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.28.6 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - '@rc-component/mini-decimal@1.1.0': - dependencies: - '@babel/runtime': 7.28.6 - - '@rc-component/mutate-observer@1.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - '@rc-component/portal@1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - '@rc-component/qrcode@1.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - '@rc-component/tour@1.15.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/portal': 1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@rc-component/trigger': 2.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - '@rc-component/trigger@2.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/portal': 1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-resize-observer: 1.4.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - '@remix-run/router@1.23.2': {} - - '@sinclair/typebox@0.30.4': {} - - '@so-ric/colorspace@1.1.6': - dependencies: - color: 5.0.3 - text-hex: 1.0.0 - - '@swc/core-darwin-arm64@1.15.8': - optional: true - - '@swc/core-darwin-x64@1.15.8': - optional: true - - '@swc/core-linux-arm-gnueabihf@1.15.8': - optional: true - - '@swc/core-linux-arm64-gnu@1.15.8': - optional: true - - '@swc/core-linux-arm64-musl@1.15.8': - optional: true - - '@swc/core-linux-x64-gnu@1.15.8': - optional: true - - '@swc/core-linux-x64-musl@1.15.8': - optional: true - - '@swc/core-win32-arm64-msvc@1.15.8': - optional: true - - '@swc/core-win32-ia32-msvc@1.15.8': - optional: true - - '@swc/core-win32-x64-msvc@1.15.8': - optional: true - - '@swc/core@1.15.8': - dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.25 - optionalDependencies: - '@swc/core-darwin-arm64': 1.15.8 - '@swc/core-darwin-x64': 1.15.8 - '@swc/core-linux-arm-gnueabihf': 1.15.8 - '@swc/core-linux-arm64-gnu': 1.15.8 - '@swc/core-linux-arm64-musl': 1.15.8 - '@swc/core-linux-x64-gnu': 1.15.8 - '@swc/core-linux-x64-musl': 1.15.8 - '@swc/core-win32-arm64-msvc': 1.15.8 - '@swc/core-win32-ia32-msvc': 1.15.8 - '@swc/core-win32-x64-msvc': 1.15.8 - - '@swc/counter@0.1.3': {} - - '@swc/types@0.1.25': - dependencies: - '@swc/counter': 0.1.3 - - '@types/eslint-scope@3.7.7': - dependencies: - '@types/eslint': 9.6.1 - '@types/estree': 1.0.8 - - '@types/eslint@9.6.1': - dependencies: - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - - '@types/estree@1.0.8': {} - - '@types/fs-extra@11.0.4': - dependencies: - '@types/jsonfile': 6.1.4 - '@types/node': 20.19.29 - - '@types/glob@7.2.0': - dependencies: - '@types/minimatch': 6.0.0 - '@types/node': 20.19.29 - - '@types/json-schema@7.0.15': {} - - '@types/jsonfile@6.1.4': - dependencies: - '@types/node': 20.19.29 - - '@types/minimatch@6.0.0': - dependencies: - minimatch: 10.1.1 - - '@types/node@20.19.29': - dependencies: - undici-types: 6.21.0 - - '@types/prop-types@15.7.15': {} - - '@types/react@18.2.0': - dependencies: - '@types/prop-types': 15.7.15 - '@types/scheduler': 0.26.0 - csstype: 3.2.3 - - '@types/scheduler@0.26.0': {} - - '@types/triple-beam@1.3.5': {} - - '@webassemblyjs/ast@1.14.1': - dependencies: - '@webassemblyjs/helper-numbers': 1.13.2 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - - '@webassemblyjs/floating-point-hex-parser@1.13.2': {} - - '@webassemblyjs/helper-api-error@1.13.2': {} - - '@webassemblyjs/helper-buffer@1.14.1': {} - - '@webassemblyjs/helper-numbers@1.13.2': - dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.13.2 - '@webassemblyjs/helper-api-error': 1.13.2 - '@xtuc/long': 4.2.2 - - '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} - - '@webassemblyjs/helper-wasm-section@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/wasm-gen': 1.14.1 - - '@webassemblyjs/ieee754@1.13.2': - dependencies: - '@xtuc/ieee754': 1.2.0 - - '@webassemblyjs/leb128@1.13.2': - dependencies: - '@xtuc/long': 4.2.2 - - '@webassemblyjs/utf8@1.13.2': {} - - '@webassemblyjs/wasm-edit@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/helper-wasm-section': 1.14.1 - '@webassemblyjs/wasm-gen': 1.14.1 - '@webassemblyjs/wasm-opt': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - '@webassemblyjs/wast-printer': 1.14.1 - - '@webassemblyjs/wasm-gen@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/ieee754': 1.13.2 - '@webassemblyjs/leb128': 1.13.2 - '@webassemblyjs/utf8': 1.13.2 - - '@webassemblyjs/wasm-opt@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/wasm-gen': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - - '@webassemblyjs/wasm-parser@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-api-error': 1.13.2 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/ieee754': 1.13.2 - '@webassemblyjs/leb128': 1.13.2 - '@webassemblyjs/utf8': 1.13.2 - - '@webassemblyjs/wast-printer@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@xtuc/long': 4.2.2 - - '@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.98.0)': - dependencies: - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack@5.98.0) - - '@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.98.0)': - dependencies: - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack@5.98.0) - - '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack@5.98.0)': - dependencies: - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack@5.98.0) - - '@xtuc/ieee754@1.2.0': {} - - '@xtuc/long@4.2.2': {} - - acorn@8.15.0: {} - - ajv-formats@2.1.1: - dependencies: - ajv: 8.17.1 - - ajv-keywords@3.5.2(ajv@6.12.6): - dependencies: - ajv: 6.12.6 - - ajv-keywords@5.1.0(ajv@8.17.1): - dependencies: - ajv: 8.17.1 - fast-deep-equal: 3.1.3 - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-regex@5.0.1: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - antd@5.24.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@ant-design/colors': 7.2.1 - '@ant-design/cssinjs': 1.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@ant-design/cssinjs-utils': 1.1.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@ant-design/fast-color': 2.0.6 - '@ant-design/icons': 5.6.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@ant-design/react-slick': 1.1.2(react@18.2.0) - '@babel/runtime': 7.28.6 - '@rc-component/color-picker': 2.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@rc-component/mutate-observer': 1.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@rc-component/qrcode': 1.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@rc-component/tour': 1.15.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@rc-component/trigger': 2.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - copy-to-clipboard: 3.3.3 - dayjs: 1.11.19 - rc-cascader: 3.33.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-checkbox: 3.5.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-collapse: 3.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-dialog: 9.6.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-drawer: 7.2.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-dropdown: 4.2.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-field-form: 2.7.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-image: 7.11.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-input: 1.7.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-input-number: 9.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-mentions: 2.19.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-menu: 9.16.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-notification: 5.6.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-pagination: 5.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-picker: 4.11.3(dayjs@1.11.19)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-progress: 4.0.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-rate: 2.13.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-resize-observer: 1.4.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-segmented: 2.7.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-select: 14.16.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-slider: 11.1.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-steps: 6.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-switch: 4.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-table: 7.50.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-tabs: 15.5.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-textarea: 1.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-tooltip: 6.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-tree: 5.13.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-tree-select: 5.27.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-upload: 4.8.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - scroll-into-view-if-needed: 3.1.0 - throttle-debounce: 5.0.2 - transitivePeerDependencies: - - date-fns - - luxon - - moment - - array-union@1.0.2: - dependencies: - array-uniq: 1.0.3 - - array-uniq@1.0.3: {} - - assert@2.1.0: - dependencies: - call-bind: 1.0.8 - is-nan: 1.3.2 - object-is: 1.1.6 - object.assign: 4.1.7 - util: 0.12.5 - - async-validator@4.2.5: {} - - async@3.2.6: {} - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - - balanced-match@1.0.2: {} - - base64-js@1.5.1: {} - - baseline-browser-mapping@2.9.14: {} - - big.js@5.2.2: {} - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - browserify-zlib@0.2.0: - dependencies: - pako: 1.0.11 - - browserslist@4.28.1: - dependencies: - baseline-browser-mapping: 2.9.14 - caniuse-lite: 1.0.30001764 - electron-to-chromium: 1.5.267 - node-releases: 2.0.27 - update-browserslist-db: 1.2.3(browserslist@4.28.1) - - buffer-from@1.1.2: {} - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - caniuse-lite@1.0.30001764: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chrome-trace-event@1.0.4: {} - - classnames@2.5.1: {} - - clean-webpack-plugin@4.0.0(webpack@5.98.0): - dependencies: - del: 4.1.1 - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clone-deep@4.0.1: - dependencies: - is-plain-object: 2.0.4 - kind-of: 6.0.3 - shallow-clone: 3.0.1 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-convert@3.1.3: - dependencies: - color-name: 2.1.0 - - color-name@1.1.4: {} - - color-name@2.1.0: {} - - color-string@2.1.4: - dependencies: - color-name: 2.1.0 - - color@5.0.3: - dependencies: - color-convert: 3.1.3 - color-string: 2.1.4 - - colorette@2.0.20: {} - - commander@10.0.1: {} - - commander@2.20.3: {} - - compute-scroll-into-view@3.1.1: {} - - concat-map@0.0.1: {} - - copy-to-clipboard@3.3.3: - dependencies: - toggle-selection: 1.0.6 - - core-util-is@1.0.3: {} - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - css-loader@6.11.0(webpack@5.98.0): - dependencies: - icss-utils: 5.1.0(postcss@8.5.6) - postcss: 8.5.6 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.6) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.6) - postcss-modules-scope: 3.2.1(postcss@8.5.6) - postcss-modules-values: 4.0.0(postcss@8.5.6) - postcss-value-parser: 4.2.0 - semver: 7.7.3 - optionalDependencies: - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - - cssesc@3.0.0: {} - - csstype@3.2.3: {} - - dayjs@1.11.19: {} - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - - del@4.1.1: - dependencies: - '@types/glob': 7.2.0 - globby: 6.1.0 - is-path-cwd: 2.2.0 - is-path-in-cwd: 2.1.0 - p-map: 2.1.0 - pify: 4.0.1 - rimraf: 2.7.1 - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - electron-to-chromium@1.5.267: {} - - emoji-regex@8.0.0: {} - - emojis-list@3.0.0: {} - - enabled@2.0.0: {} - - enhanced-resolve@5.18.4: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.0 - - envinfo@7.21.0: {} - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-module-lexer@1.7.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - escalade@3.2.0: {} - - eslint-scope@5.1.1: - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@4.3.0: {} - - estraverse@5.3.0: {} - - events@3.3.0: {} - - fast-deep-equal@3.1.3: {} - - fast-json-stable-stringify@2.1.0: {} - - fast-uri@3.1.0: {} - - fastest-levenshtein@1.0.16: {} - - fecha@4.2.3: {} - - file-loader@6.2.0(webpack@5.98.0): - dependencies: - loader-utils: 2.0.4 - schema-utils: 3.3.0 - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - fn.name@1.1.0: {} - - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - - fs-extra@11.3.3: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 - - fs.realpath@1.0.0: {} - - function-bind@1.1.2: {} - - generator-function@2.0.1: {} - - get-caller-file@2.0.5: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - glob-to-regexp@0.4.1: {} - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - globby@6.1.0: - dependencies: - array-union: 1.0.2 - glob: 7.2.3 - object-assign: 4.1.1 - pify: 2.3.0 - pinkie-promise: 2.0.1 - - gopd@1.2.0: {} - - graceful-fs@4.2.11: {} - - has-flag@4.0.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - icss-utils@5.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - ieee754@1.2.1: {} - - immediate@3.0.6: {} - - import-local@3.2.0: - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - interpret@3.1.1: {} - - is-arguments@1.2.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-fullwidth-code-point@3.0.0: {} - - is-generator-function@1.1.2: - dependencies: - call-bound: 1.0.4 - generator-function: 2.0.1 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-nan@1.3.2: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - - is-path-cwd@2.2.0: {} - - is-path-in-cwd@2.1.0: - dependencies: - is-path-inside: 2.1.0 - - is-path-inside@2.1.0: - dependencies: - path-is-inside: 1.0.2 - - is-plain-object@2.0.4: - dependencies: - isobject: 3.0.1 - - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - is-stream@2.0.1: {} - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.19 - - isarray@1.0.0: {} - - isexe@2.0.0: {} - - isobject@3.0.1: {} - - jest-worker@27.5.1: - dependencies: - '@types/node': 20.19.29 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - js-tokens@4.0.0: {} - - json-parse-even-better-errors@2.3.1: {} - - json-schema-traverse@0.4.1: {} - - json-schema-traverse@1.0.0: {} - - json2mq@0.2.0: - dependencies: - string-convert: 0.2.1 - - json5@2.2.3: {} - - jsonfile@6.2.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - - jszip@3.10.1: - dependencies: - lie: 3.3.0 - pako: 1.0.11 - readable-stream: 2.3.8 - setimmediate: 1.0.5 - - kind-of@6.0.3: {} - - klaw@4.1.0: {} - - kuler@2.0.0: {} - - lie@3.3.0: - dependencies: - immediate: 3.0.6 - - loader-runner@4.3.1: {} - - loader-utils@2.0.4: - dependencies: - big.js: 5.2.2 - emojis-list: 3.0.0 - json5: 2.2.3 - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - logform@2.7.0: - dependencies: - '@colors/colors': 1.6.0 - '@types/triple-beam': 1.3.5 - fecha: 4.2.3 - ms: 2.1.3 - safe-stable-stringify: 2.5.0 - triple-beam: 1.4.1 - - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - - math-intrinsics@1.1.0: {} - - merge-stream@2.0.0: {} - - mime-db@1.52.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - minimatch@10.1.1: - dependencies: - '@isaacs/brace-expansion': 5.0.0 - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - - ms@2.1.3: {} - - nanoid@3.3.11: {} - - neo-async@2.6.2: {} - - node-releases@2.0.27: {} - - null-loader@4.0.1(webpack@5.98.0): - dependencies: - loader-utils: 2.0.4 - schema-utils: 3.3.0 - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - - object-assign@4.1.1: {} - - object-is@1.1.6: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - one-time@1.0.0: - dependencies: - fn.name: 1.1.0 - - os-browserify@0.3.0: {} - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - - p-map@2.1.0: {} - - p-try@2.2.0: {} - - pako@1.0.11: {} - - path-browserify@1.0.1: {} - - path-exists@4.0.0: {} - - path-is-absolute@1.0.1: {} - - path-is-inside@1.0.2: {} - - path-key@3.1.1: {} - - path-parse@1.0.7: {} - - picocolors@1.1.1: {} - - pify@2.3.0: {} - - pify@4.0.1: {} - - pinkie-promise@2.0.1: - dependencies: - pinkie: 2.0.4 - - pinkie@2.0.4: {} - - pkg-dir@4.2.0: - dependencies: - find-up: 4.1.0 - - possible-typed-array-names@1.1.0: {} - - postcss-modules-extract-imports@3.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - postcss-modules-local-by-default@4.2.0(postcss@8.5.6): - dependencies: - icss-utils: 5.1.0(postcss@8.5.6) - postcss: 8.5.6 - postcss-selector-parser: 7.1.1 - postcss-value-parser: 4.2.0 - - postcss-modules-scope@3.2.1(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-selector-parser: 7.1.1 - - postcss-modules-values@4.0.0(postcss@8.5.6): - dependencies: - icss-utils: 5.1.0(postcss@8.5.6) - postcss: 8.5.6 - - postcss-selector-parser@7.1.1: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - postcss-value-parser@4.2.0: {} - - postcss@8.5.6: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - process-nextick-args@2.0.1: {} - - process@0.11.10: {} - - punycode@2.3.1: {} - - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - - rc-cascader@3.33.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-select: 14.16.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-tree: 5.13.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-checkbox@3.5.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-collapse@3.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-dialog@9.6.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/portal': 1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-drawer@7.2.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/portal': 1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-dropdown@4.2.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/trigger': 2.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-field-form@1.44.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - async-validator: 4.2.5 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-field-form@2.7.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/async-validator': 5.1.0 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-image@7.11.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/portal': 1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-dialog: 9.6.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-input-number@9.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/mini-decimal': 1.1.0 - classnames: 2.5.1 - rc-input: 1.7.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-input@1.7.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-mentions@2.19.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/trigger': 2.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-input: 1.7.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-menu: 9.16.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-textarea: 1.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-menu@9.16.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/trigger': 2.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-overflow: 1.5.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-motion@2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-notification@5.6.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-overflow@1.5.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-pagination@5.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-picker@4.11.3(dayjs@1.11.19)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/trigger': 2.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-overflow: 1.5.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-resize-observer: 1.4.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - optionalDependencies: - dayjs: 1.11.19 - - rc-progress@4.0.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-rate@2.13.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-resize-observer@1.4.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - resize-observer-polyfill: 1.5.1 - - rc-segmented@2.7.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-select@14.16.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/trigger': 2.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-overflow: 1.5.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-virtual-list: 3.19.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-slider@11.1.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-steps@6.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-switch@4.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-table@7.50.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/context': 1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-virtual-list: 3.19.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-tabs@15.5.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-dropdown: 4.2.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-menu: 9.16.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-resize-observer: 1.4.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-textarea@1.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-input: 1.7.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-resize-observer: 1.4.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-tooltip@6.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - '@rc-component/trigger': 2.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-tree-select@5.27.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-select: 14.16.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-tree: 5.13.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-tree@5.13.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-motion: 2.9.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-virtual-list: 3.19.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-upload@4.8.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - rc-util@5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-is: 18.3.1 - - rc-virtual-list@3.19.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@babel/runtime': 7.28.6 - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - rc-util: 5.44.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - react-dom@18.2.0(react@18.2.0): - dependencies: - loose-envify: 1.4.0 - react: 18.2.0 - scheduler: 0.23.2 - - react-is@18.3.1: {} - - react-router-dom@6.30.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@remix-run/router': 1.23.2 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-router: 6.30.3(react@18.2.0) - - react-router@6.30.3(react@18.2.0): - dependencies: - '@remix-run/router': 1.23.2 - react: 18.2.0 - - react@18.2.0: - dependencies: - loose-envify: 1.4.0 - - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - rechoir@0.8.0: - dependencies: - resolve: 1.22.11 - - require-directory@2.1.1: {} - - require-from-string@2.0.2: {} - - resize-observer-polyfill@1.5.1: {} - - resolve-cwd@3.0.0: - dependencies: - resolve-from: 5.0.0 - - resolve-from@5.0.0: {} - - resolve@1.22.11: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - rimraf@2.7.1: - dependencies: - glob: 7.2.3 - - safe-buffer@5.1.2: {} - - safe-buffer@5.2.1: {} - - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - - safe-stable-stringify@2.5.0: {} - - scheduler@0.23.2: - dependencies: - loose-envify: 1.4.0 - - schema-utils@3.3.0: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - ajv-keywords: 3.5.2(ajv@6.12.6) - - schema-utils@4.3.3: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 8.17.1 - ajv-formats: 2.1.1 - ajv-keywords: 5.1.0(ajv@8.17.1) - - scroll-into-view-if-needed@3.1.0: - dependencies: - compute-scroll-into-view: 3.1.1 - - semver@7.7.3: {} - - serialize-javascript@6.0.2: - dependencies: - randombytes: 2.1.0 - - set-blocking@2.0.0: {} - - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - setimmediate@1.0.5: {} - - shallow-clone@3.0.1: - dependencies: - kind-of: 6.0.3 - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - source-list-map@2.0.1: {} - - source-map-js@1.2.1: {} - - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map@0.6.1: {} - - stack-trace@0.0.10: {} - - stream-browserify@3.0.0: - dependencies: - inherits: 2.0.4 - readable-stream: 3.6.2 - - string-convert@0.2.1: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - style-loader@3.3.4(webpack@5.98.0): - dependencies: - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - - stylis@4.3.6: {} - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - swc-loader@0.2.7(@swc/core@1.15.8)(webpack@5.98.0): - dependencies: - '@swc/core': 1.15.8 - '@swc/counter': 0.1.3 - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - - tapable@2.3.0: {} - - terser-webpack-plugin@5.3.16(@swc/core@1.15.8)(webpack@5.98.0): - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - jest-worker: 27.5.1 - schema-utils: 4.3.3 - serialize-javascript: 6.0.2 - terser: 5.44.1 - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - optionalDependencies: - '@swc/core': 1.15.8 - - terser@5.44.1: - dependencies: - '@jridgewell/source-map': 0.3.11 - acorn: 8.15.0 - commander: 2.20.3 - source-map-support: 0.5.21 - - text-hex@1.0.0: {} - - throttle-debounce@5.0.2: {} - - toggle-selection@1.0.6: {} - - triple-beam@1.4.1: {} - - typescript@5.9.3: {} - - undici-types@6.21.0: {} - - universalify@2.0.1: {} - - update-browserslist-db@1.2.3(browserslist@4.28.1): - dependencies: - browserslist: 4.28.1 - escalade: 3.2.0 - picocolors: 1.1.1 - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - util-deprecate@1.0.2: {} - - util@0.12.5: - dependencies: - inherits: 2.0.4 - is-arguments: 1.2.0 - is-generator-function: 1.1.2 - is-typed-array: 1.1.15 - which-typed-array: 1.1.19 - - watchpack@2.5.0: - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - - webpack-cli@5.1.4(webpack@5.98.0): - dependencies: - '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.98.0) - '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.98.0) - '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack@5.98.0) - colorette: 2.0.20 - commander: 10.0.1 - cross-spawn: 7.0.6 - envinfo: 7.21.0 - fastest-levenshtein: 1.0.16 - import-local: 3.2.0 - interpret: 3.1.1 - rechoir: 0.8.0 - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-merge: 5.9.0 - - webpack-manifest-plugin@5.0.1(webpack@5.98.0): - dependencies: - tapable: 2.3.0 - webpack: 5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4) - webpack-sources: 2.3.1 - - webpack-merge@5.9.0: - dependencies: - clone-deep: 4.0.1 - wildcard: 2.0.1 - - webpack-sources@2.3.1: - dependencies: - source-list-map: 2.0.1 - source-map: 0.6.1 - - webpack-sources@3.3.3: {} - - webpack@5.98.0(@swc/core@1.15.8)(webpack-cli@5.1.4): - dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.8 - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/wasm-edit': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.15.0 - browserslist: 4.28.1 - chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.4 - es-module-lexer: 1.7.0 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.1 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 4.3.3 - tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(@swc/core@1.15.8)(webpack@5.98.0) - watchpack: 2.5.0 - webpack-sources: 3.3.3 - optionalDependencies: - webpack-cli: 5.1.4(webpack@5.98.0) - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - - which-typed-array@1.1.19: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - wildcard@2.0.1: {} - - winston-transport@4.9.0: - dependencies: - logform: 2.7.0 - readable-stream: 3.6.2 - triple-beam: 1.4.1 - - winston@3.19.0: - dependencies: - '@colors/colors': 1.6.0 - '@dabh/diagnostics': 2.0.8 - async: 3.2.6 - is-stream: 2.0.1 - logform: 2.7.0 - one-time: 1.0.0 - readable-stream: 3.6.2 - safe-stable-stringify: 2.5.0 - stack-trace: 0.0.10 - triple-beam: 1.4.1 - winston-transport: 4.9.0 - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrappy@1.0.2: {} - - ws@8.19.0: {} - - y18n@5.0.8: {} - - yargs-parser@21.1.1: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml deleted file mode 100644 index ca56af10fd..0000000000 --- a/pnpm-workspace.yaml +++ /dev/null @@ -1,16 +0,0 @@ -packages: - - '*' - -catalog: - '@clusterio/lib': 2.0.0-alpha.22b - '@clusterio/web_ui': 2.0.0-alpha.22b - '@sinclair/typebox': ^0.30.4 - '@types/node': ^20.19.29 - '@types/react': 18.2.0 - antd: 5.24.2 - react: 18.2.0 - react-dom: 18.2.0 - typescript: ^5.9.3 - webpack: 5.98.0 - webpack-cli: 5.1.4 - webpack-merge: 5.9.0