mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-24 20:39:59 -06:00
Compare commits
56 Commits
v4.5.0
...
21176-remo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cedbeb7b19 | ||
|
|
e81ccb9be6 | ||
|
|
bc83d04c8f | ||
|
|
339ad455e4 | ||
|
|
f24376cfab | ||
|
|
47d4ae29c1 | ||
|
|
8fce672682 | ||
|
|
f776b97415 | ||
|
|
3cc1f30287 | ||
|
|
6d166aa10d | ||
|
|
040a2ae9a9 | ||
|
|
39f11f28fb | ||
|
|
62b9025a9e | ||
|
|
21091f22e6 | ||
|
|
3efa23cf8f | ||
|
|
0f62137957 | ||
|
|
7858ccb712 | ||
|
|
6b7b38ee0a | ||
|
|
c8f17e06a2 | ||
|
|
edace6aff4 | ||
|
|
586bc132b6 | ||
|
|
52a2b934a0 | ||
|
|
3d1f18d6dd | ||
|
|
3e2a26984f | ||
|
|
f5f0c19860 | ||
|
|
8da9b11ab8 | ||
|
|
ca67fa9999 | ||
|
|
eff768192e | ||
|
|
1e297d55ee | ||
|
|
fdb987ef91 | ||
|
|
b5a23db43c | ||
|
|
366b69aff7 | ||
|
|
c3e8c5e69c | ||
|
|
b55f36469d | ||
|
|
1c46215cd5 | ||
|
|
7fded2fd87 | ||
|
|
0ddc5805c4 | ||
|
|
c1bbc026e2 | ||
|
|
8cbfe94fba | ||
|
|
434334d927 | ||
|
|
fff99fd3ff | ||
|
|
6bd083b7ed | ||
|
|
f38faf2e01 | ||
|
|
f4892caa51 | ||
|
|
e60807adc5 | ||
|
|
e14934e5a5 | ||
|
|
ae03723e43 | ||
|
|
c0f79df91f | ||
|
|
edbfd0bae6 | ||
|
|
c3e111c769 | ||
|
|
c11f4b3716 | ||
|
|
a54ad24b47 | ||
|
|
3624b88c3f | ||
|
|
f54ed8bb7f | ||
|
|
5d0609e729 | ||
|
|
865b88e724 |
@@ -15,7 +15,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: NetBox version
|
label: NetBox version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
placeholder: v4.4.10
|
placeholder: v4.5.1
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/02-bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/02-bug_report.yaml
vendored
@@ -27,7 +27,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: NetBox Version
|
label: NetBox Version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
placeholder: v4.4.10
|
placeholder: v4.5.1
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
|||||||
43
.github/ISSUE_TEMPLATE/03-performance.yaml
vendored
Normal file
43
.github/ISSUE_TEMPLATE/03-performance.yaml
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
name: 🏁 Performance
|
||||||
|
type: Performance
|
||||||
|
description: An opportunity to improve application performance
|
||||||
|
labels: ["netbox", "type: performance", "status: needs triage"]
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: NetBox Version
|
||||||
|
description: What version of NetBox are you currently running?
|
||||||
|
placeholder: v4.5.1
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: Python Version
|
||||||
|
description: What version of Python are you currently running?
|
||||||
|
options:
|
||||||
|
- "3.12"
|
||||||
|
- "3.13"
|
||||||
|
- "3.14"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Area(s) of Concern
|
||||||
|
description: Which application interface(s) are affected?
|
||||||
|
options:
|
||||||
|
- label: User Interface
|
||||||
|
- label: REST API
|
||||||
|
- label: GraphQL API
|
||||||
|
- label: Python ORM
|
||||||
|
- label: Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Details
|
||||||
|
description: >
|
||||||
|
Describe in detail the operations being performed and the indications of a performance issue.
|
||||||
|
Include any relevant testing parameters, benchmarks, and expected results.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
25
.github/ISSUE_TEMPLATE/06-deprecation.yaml
vendored
25
.github/ISSUE_TEMPLATE/06-deprecation.yaml
vendored
@@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
name: 🗑️ Deprecation
|
|
||||||
type: Deprecation
|
|
||||||
description: The removal of an existing feature or resource
|
|
||||||
labels: ["netbox", "type: deprecation"]
|
|
||||||
body:
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Proposed Changes
|
|
||||||
description: >
|
|
||||||
Describe in detail the proposed changes. What is being removed?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Justification
|
|
||||||
description: Please provide justification for the proposed change(s).
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Impact
|
|
||||||
description: List all areas of the application that will be affected by this change.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
31
.github/ISSUE_TEMPLATE/07-deprecation.yaml
vendored
Normal file
31
.github/ISSUE_TEMPLATE/07-deprecation.yaml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: ⚠️ Deprecation
|
||||||
|
type: Deprecation
|
||||||
|
description: Designation of a feature or behavior that will be removed in a future release
|
||||||
|
labels: ["netbox", "type: deprecation"]
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Deprecated Functionality
|
||||||
|
description: >
|
||||||
|
Describe the feature(s) and/or behavior that is being flagged for deprecation.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Scheduled removal
|
||||||
|
description: In what future release will the deprecated functionality be removed?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Justification
|
||||||
|
description: Please provide justification for the deprecation.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Impact
|
||||||
|
description: List all areas of the application that will be affected by this change.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
20
.github/ISSUE_TEMPLATE/08-feature_removal.yaml
vendored
Normal file
20
.github/ISSUE_TEMPLATE/08-feature_removal.yaml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: 🗑️ Feature Removal
|
||||||
|
type: Removal
|
||||||
|
description: The removal of a deprecated feature or resource
|
||||||
|
labels: ["netbox", "type: removal"]
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Deprecation Issue
|
||||||
|
description: Specify the issue in which this deprecation was announced.
|
||||||
|
placeholder: "#1234"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Summary of Changes
|
||||||
|
description: >
|
||||||
|
List all changes necessary to remove the deprecated feature or resource.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -30,13 +30,13 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3
|
uses: github/codeql-action/init@v4
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
build-mode: ${{ matrix.build-mode }}
|
build-mode: ${{ matrix.build-mode }}
|
||||||
config-file: .github/codeql/codeql-config.yml
|
config-file: .github/codeql/codeql-config.yml
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3
|
uses: github/codeql-action/analyze@v4
|
||||||
with:
|
with:
|
||||||
category: "/language:${{matrix.language}}"
|
category: "/language:${{matrix.language}}"
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: 3.11
|
python-version: 3.12
|
||||||
|
|
||||||
- name: Install system dependencies
|
- name: Install system dependencies
|
||||||
run: sudo apt install -y gettext
|
run: sudo apt install -y gettext
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -9,7 +9,8 @@ yarn-error.log*
|
|||||||
/netbox/netbox/configuration.py
|
/netbox/netbox/configuration.py
|
||||||
/netbox/netbox/ldap_config.py
|
/netbox/netbox/ldap_config.py
|
||||||
/netbox/local/*
|
/netbox/local/*
|
||||||
/netbox/media
|
/netbox/media/*
|
||||||
|
!/netbox/media/.gitkeep
|
||||||
/netbox/reports/*
|
/netbox/reports/*
|
||||||
!/netbox/reports/__init__.py
|
!/netbox/reports/__init__.py
|
||||||
/netbox/scripts/*
|
/netbox/scripts/*
|
||||||
|
|||||||
3132
contrib/openapi.json
3132
contrib/openapi.json
File diff suppressed because it is too large
Load Diff
@@ -10,9 +10,11 @@ Change records are exposed in the API via the read-only endpoint `/api/extras/ob
|
|||||||
|
|
||||||
## User Messages
|
## User Messages
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.4."
|
When creating, modifying, or deleting an object in NetBox, a user has the option of recording an arbitrary message (up to 200 characters) that will appear in the change record. This can be helpful to capture additional context, such as the reason for a change or a reference to an external ticket.
|
||||||
|
|
||||||
When creating, modifying, or deleting an object in NetBox, a user has the option of recording an arbitrary message that will appear in the change record. This can be helpful to capture additional context, such as the reason for the change.
|
When editing an object via the web UI, the "Changelog message" field appears at the bottom of the form. This field is optional. The changelog message field is available in object create forms, object edit forms, delete confirmation dialogs, and bulk operations.
|
||||||
|
|
||||||
|
For information on including changelog messages when making changes via the REST API, see [Changelog Messages](../integrations/rest-api.md#changelog-messages).
|
||||||
|
|
||||||
## Correlating Changes by Request
|
## Correlating Changes by Request
|
||||||
|
|
||||||
|
|||||||
@@ -610,9 +610,7 @@ http://netbox/api/dcim/sites/ \
|
|||||||
|
|
||||||
## Changelog Messages
|
## Changelog Messages
|
||||||
|
|
||||||
!!! info "This feature was introduced in NetBox v4.4."
|
Most objects in NetBox support [change logging](../features/change-logging.md), which generates a detailed record each time an object is created, modified, or deleted. Additionally, users can attach a message to the change record as well. This is accomplished via the REST API by including a `changelog_message` field in the object representation.
|
||||||
|
|
||||||
Most objects in NetBox support [change logging](../features/change-logging.md), which generates a detailed record each time an object is created, modified, or deleted. Beginning in NetBox v4.4, users can attach a message to the change record as well. This is accomplished via the REST API by including a `changelog_message` field in the object representation.
|
|
||||||
|
|
||||||
For example, the following API request will create a new site and record a message in the resulting changelog entry:
|
For example, the following API request will create a new site and record a message in the resulting changelog entry:
|
||||||
|
|
||||||
@@ -628,7 +626,7 @@ http://netbox/api/dcim/sites/ \
|
|||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
This approach works when creating, modifying, or deleting objects, either individually or in bulk.
|
This approach works when creating, modifying, or deleting objects, either individually or in bulk. For more information about change logging, see [Change Logging](../features/change-logging.md).
|
||||||
|
|
||||||
## Uploading Files
|
## Uploading Files
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
* [#20912](https://github.com/netbox-community/netbox/issues/20912) - Fix inconsistent clearing of `module` field on ModuleBay
|
* [#20912](https://github.com/netbox-community/netbox/issues/20912) - Fix inconsistent clearing of `module` field on ModuleBay
|
||||||
* [#20944](https://github.com/netbox-community/netbox/issues/20944) - Ensure cached scope is updated on child objects when a parent region/site/location is changed
|
* [#20944](https://github.com/netbox-community/netbox/issues/20944) - Ensure cached scope is updated on child objects when a parent region/site/location is changed
|
||||||
* [#20948](https://github.com/netbox-community/netbox/issues/20948) - Handle the deletion of related objects with `on_delete=RESTRICT` the same as `CASCADE`
|
* [#20948](https://github.com/netbox-community/netbox/issues/20948) - Handle the deletion of related objects with `on_delete=RESTRICT` the same as `CASCADE`
|
||||||
|
* [#20966](https://github.com/netbox-community/netbox/issues/20966) - Fix UI rendering issue when scrolling list of object types in permissions form
|
||||||
* [#20969](https://github.com/netbox-community/netbox/issues/20969) - Fix querying of front port templates by `rear_port_id`
|
* [#20969](https://github.com/netbox-community/netbox/issues/20969) - Fix querying of front port templates by `rear_port_id`
|
||||||
* [#21011](https://github.com/netbox-community/netbox/issues/21011) - Avoid writing to the database when loading active ConfigRevision
|
* [#21011](https://github.com/netbox-community/netbox/issues/21011) - Avoid writing to the database when loading active ConfigRevision
|
||||||
* [#21032](https://github.com/netbox-community/netbox/issues/21032) - Avoid SQL subquery in RestrictedQuerySet where unnecessary
|
* [#21032](https://github.com/netbox-community/netbox/issues/21032) - Avoid SQL subquery in RestrictedQuerySet where unnecessary
|
||||||
|
|||||||
@@ -1,4 +1,41 @@
|
|||||||
## v4.5.0 (FUTURE)
|
# NetBox v4.5
|
||||||
|
|
||||||
|
## v4.5.1 (2026-01-20)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* [#21018](https://github.com/netbox-community/netbox/issues/21018) - Enable filtering prefixes by location/site/site group/region directly via GraphQL API
|
||||||
|
* [#21142](https://github.com/netbox-community/netbox/issues/21142) - Enable filtering device components by site/location/rack directly via GraphQL API
|
||||||
|
* [#21144](https://github.com/netbox-community/netbox/issues/21144) - Enable specifying a prefix length for IP addresses when utilizing the `/api/ipam/prefixes/<id>/available-ips/` REST API endpoint
|
||||||
|
* [#21165](https://github.com/netbox-community/netbox/issues/21165) - VLAN selector should default to group (instead of site)
|
||||||
|
* [#21178](https://github.com/netbox-community/netbox/issues/21178) - Improve consistency of rack measurements in UI
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#19901](https://github.com/netbox-community/netbox/issues/19901) - Fix `RelatedObjectDoesNotExist` exception when importing modules into unnamed devices
|
||||||
|
* [#20239](https://github.com/netbox-community/netbox/issues/20239) - Prevent shared mutable state in PluginMenuItem & PluginMenuButton
|
||||||
|
* [#20933](https://github.com/netbox-community/netbox/issues/20933) - Fix writable `data_file` assignment for ConfigContext and ConfigContextProfile via the REST API
|
||||||
|
* [#21039](https://github.com/netbox-community/netbox/issues/21039) - Fix support for AVIF image uploads
|
||||||
|
* [#21050](https://github.com/netbox-community/netbox/issues/21050) - Clear device OOB IP assignments when reassigning IP addresses
|
||||||
|
* [#21051](https://github.com/netbox-community/netbox/issues/21051) - Remove irrelevant object types from permissions form
|
||||||
|
* [#21097](https://github.com/netbox-community/netbox/issues/21097) - Fix comparison lookups for ID filters in GraphQL API
|
||||||
|
* [#21102](https://github.com/netbox-community/netbox/issues/21102) - Fix GraphiQL explorer UI
|
||||||
|
* [#21117](https://github.com/netbox-community/netbox/issues/21117) - Avoid `ValueError` exception when `API_TOKEN_PEPPERS` is not defined
|
||||||
|
* [#21118](https://github.com/netbox-community/netbox/issues/21118) - Address performance issue when saving sites with many assigned objects
|
||||||
|
* [#21124](https://github.com/netbox-community/netbox/issues/21124) - Fix front/rear port mapping for module types
|
||||||
|
* [#21134](https://github.com/netbox-community/netbox/issues/21134) - Fix bulk renaming for module types
|
||||||
|
* [#21139](https://github.com/netbox-community/netbox/issues/21139) - Support `fields` parameter for job, object change, and object type REST API endpoints
|
||||||
|
* [#21140](https://github.com/netbox-community/netbox/issues/21140) - Restore translation for object attribute labels on several UI views
|
||||||
|
* [#21160](https://github.com/netbox-community/netbox/issues/21160) - Fix performance issue loading UI views caused by unintended `APISelect` choices resolution
|
||||||
|
* [#21166](https://github.com/netbox-community/netbox/issues/21166) - Fix support for 32-bit ASN filtering in GraphQL API
|
||||||
|
* [#21175](https://github.com/netbox-community/netbox/issues/21175) - Fix pending migrations warning when `DEFAULT_LANGUAGE` is set
|
||||||
|
* [#21181](https://github.com/netbox-community/netbox/issues/21181) - Handle `AuthenticationFailed` exception when using an invalid API token to fetch media files
|
||||||
|
* [#21213](https://github.com/netbox-community/netbox/issues/21213) - Tag weight field should be marked as required in UI forms
|
||||||
|
* [#21231](https://github.com/netbox-community/netbox/issues/21231) - Presence of object types table should be checked only during migration (performance improvement)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v4.5.0 (2026-01-06)
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|
||||||
|
|||||||
@@ -44,3 +44,4 @@ class DataFileSerializer(NetBoxModelSerializer):
|
|||||||
'id', 'url', 'display_url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
|
'id', 'url', 'display_url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'path')
|
brief_fields = ('id', 'url', 'display', 'path')
|
||||||
|
read_only_fields = ['path', 'last_updated', 'size', 'hash']
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ from rest_framework.decorators import action
|
|||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.routers import APIRootView
|
from rest_framework.routers import APIRootView
|
||||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
|
||||||
from rq.job import Job as RQ_Job
|
from rq.job import Job as RQ_Job
|
||||||
from rq.worker import Worker
|
from rq.worker import Worker
|
||||||
|
|
||||||
@@ -64,7 +63,7 @@ class DataFileViewSet(NetBoxReadOnlyModelViewSet):
|
|||||||
filterset_class = filtersets.DataFileFilterSet
|
filterset_class = filtersets.DataFileFilterSet
|
||||||
|
|
||||||
|
|
||||||
class JobViewSet(ReadOnlyModelViewSet):
|
class JobViewSet(NetBoxReadOnlyModelViewSet):
|
||||||
"""
|
"""
|
||||||
Retrieve a list of job results
|
Retrieve a list of job results
|
||||||
"""
|
"""
|
||||||
@@ -73,19 +72,20 @@ class JobViewSet(ReadOnlyModelViewSet):
|
|||||||
filterset_class = filtersets.JobFilterSet
|
filterset_class = filtersets.JobFilterSet
|
||||||
|
|
||||||
|
|
||||||
class ObjectChangeViewSet(ReadOnlyModelViewSet):
|
class ObjectChangeViewSet(NetBoxReadOnlyModelViewSet):
|
||||||
"""
|
"""
|
||||||
Retrieve a list of recent changes.
|
Retrieve a list of recent changes.
|
||||||
"""
|
"""
|
||||||
metadata_class = ContentTypeMetadata
|
metadata_class = ContentTypeMetadata
|
||||||
|
queryset = ObjectChange.objects.all()
|
||||||
serializer_class = serializers.ObjectChangeSerializer
|
serializer_class = serializers.ObjectChangeSerializer
|
||||||
filterset_class = filtersets.ObjectChangeFilterSet
|
filterset_class = filtersets.ObjectChangeFilterSet
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return ObjectChange.objects.valid_models()
|
return super().get_queryset().valid_models()
|
||||||
|
|
||||||
|
|
||||||
class ObjectTypeViewSet(ReadOnlyModelViewSet):
|
class ObjectTypeViewSet(NetBoxReadOnlyModelViewSet):
|
||||||
"""
|
"""
|
||||||
Read-only list of ObjectTypes.
|
Read-only list of ObjectTypes.
|
||||||
"""
|
"""
|
||||||
@@ -94,6 +94,16 @@ class ObjectTypeViewSet(ReadOnlyModelViewSet):
|
|||||||
serializer_class = serializers.ObjectTypeSerializer
|
serializer_class = serializers.ObjectTypeSerializer
|
||||||
filterset_class = filtersets.ObjectTypeFilterSet
|
filterset_class = filtersets.ObjectTypeFilterSet
|
||||||
|
|
||||||
|
def initial(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Override initial() to skip the restrict() call since ObjectType (a ContentType proxy)
|
||||||
|
doesn't use RestrictedQuerySet and is publicly accessible metadata.
|
||||||
|
"""
|
||||||
|
# Call GenericViewSet.initial() directly, skipping BaseViewSet.initial()
|
||||||
|
# which would try to call restrict() on the queryset
|
||||||
|
from rest_framework.viewsets import GenericViewSet
|
||||||
|
GenericViewSet.initial(self, request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class BaseRQViewSet(viewsets.ViewSet):
|
class BaseRQViewSet(viewsets.ViewSet):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from django.core.validators import RegexValidator
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from netbox.constants import CENSOR_TOKEN, CENSOR_TOKEN_CHANGED
|
from netbox.constants import CENSOR_TOKEN, CENSOR_TOKEN_CHANGED
|
||||||
from netbox.models import PrimaryModel
|
from netbox.models import PrimaryModel
|
||||||
@@ -128,7 +128,9 @@ class DataSource(JobsMixin, PrimaryModel):
|
|||||||
# Ensure URL scheme matches selected type
|
# Ensure URL scheme matches selected type
|
||||||
if self.backend_class.is_local and self.url_scheme not in ('file', ''):
|
if self.backend_class.is_local and self.url_scheme not in ('file', ''):
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'source_url': "URLs for local sources must start with file:// (or specify no scheme)"
|
'source_url': _("URLs for local sources must start with {scheme} (or specify no scheme)").format(
|
||||||
|
scheme='file://'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ class ObjectTypeQuerySet(models.QuerySet):
|
|||||||
|
|
||||||
class ObjectTypeManager(models.Manager):
|
class ObjectTypeManager(models.Manager):
|
||||||
|
|
||||||
|
# TODO: Remove this in NetBox v5.0
|
||||||
|
# Cache the result of introspection to avoid repeated queries.
|
||||||
|
_table_exists = False
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return ObjectTypeQuerySet(self.model, using=self._db)
|
return ObjectTypeQuerySet(self.model, using=self._db)
|
||||||
|
|
||||||
@@ -69,10 +73,12 @@ class ObjectTypeManager(models.Manager):
|
|||||||
# TODO: Remove this in NetBox v5.0
|
# TODO: Remove this in NetBox v5.0
|
||||||
# If the ObjectType table has not yet been provisioned (e.g. because we're in a pre-v4.4 migration),
|
# If the ObjectType table has not yet been provisioned (e.g. because we're in a pre-v4.4 migration),
|
||||||
# fall back to ContentType.
|
# fall back to ContentType.
|
||||||
if 'core_objecttype' not in connection.introspection.table_names():
|
if not ObjectTypeManager._table_exists:
|
||||||
ct = ContentType.objects.get_for_model(model, for_concrete_model=for_concrete_model)
|
if 'core_objecttype' not in connection.introspection.table_names():
|
||||||
ct.features = get_model_features(ct.model_class())
|
ct = ContentType.objects.get_for_model(model, for_concrete_model=for_concrete_model)
|
||||||
return ct
|
ct.features = get_model_features(ct.model_class())
|
||||||
|
return ct
|
||||||
|
ObjectTypeManager._table_exists = True
|
||||||
|
|
||||||
if not inspect.isclass(model):
|
if not inspect.isclass(model):
|
||||||
model = model.__class__
|
model = model.__class__
|
||||||
|
|||||||
@@ -140,9 +140,6 @@ class FrontPortFormMixin(forms.Form):
|
|||||||
widget=forms.SelectMultiple(attrs={'size': 8})
|
widget=forms.SelectMultiple(attrs={'size': 8})
|
||||||
)
|
)
|
||||||
|
|
||||||
port_mapping_model = PortMapping
|
|
||||||
parent_field = 'device'
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
|
|
||||||
@@ -203,3 +200,22 @@ class FrontPortFormMixin(forms.Form):
|
|||||||
using=connection,
|
using=connection,
|
||||||
update_fields=None
|
update_fields=None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _get_rear_port_choices(self, parent_filter, front_port):
|
||||||
|
"""
|
||||||
|
Return a list of choices representing each available rear port & position pair on the parent object (identified
|
||||||
|
by a Q filter), excluding those assigned to the specified instance.
|
||||||
|
"""
|
||||||
|
occupied_rear_port_positions = [
|
||||||
|
f'{mapping.rear_port_id}:{mapping.rear_port_position}'
|
||||||
|
for mapping in self.port_mapping_model.objects.filter(parent_filter).exclude(front_port=front_port.pk)
|
||||||
|
]
|
||||||
|
|
||||||
|
choices = []
|
||||||
|
for rear_port in self.rear_port_model.objects.filter(parent_filter):
|
||||||
|
for i in range(1, rear_port.positions + 1):
|
||||||
|
pair_id = f'{rear_port.pk}:{i}'
|
||||||
|
if pair_id not in occupied_rear_port_positions:
|
||||||
|
pair_label = f'{rear_port.name}:{i}'
|
||||||
|
choices.append((pair_id, pair_label))
|
||||||
|
return choices
|
||||||
|
|||||||
@@ -1124,9 +1124,8 @@ class FrontPortTemplateForm(FrontPortFormMixin, ModularComponentTemplateForm):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Override FrontPortFormMixin attrs
|
|
||||||
port_mapping_model = PortTemplateMapping
|
port_mapping_model = PortTemplateMapping
|
||||||
parent_field = 'device_type'
|
rear_port_model = RearPortTemplate
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPortTemplate
|
model = FrontPortTemplate
|
||||||
@@ -1137,13 +1136,14 @@ class FrontPortTemplateForm(FrontPortFormMixin, ModularComponentTemplateForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Populate rear port choices based on parent DeviceType or ModuleType
|
||||||
if device_type_id := self.data.get('device_type') or self.initial.get('device_type'):
|
if device_type_id := self.data.get('device_type') or self.initial.get('device_type'):
|
||||||
device_type = DeviceType.objects.get(pk=device_type_id)
|
parent_filter = Q(device_type=device_type_id)
|
||||||
|
elif module_type_id := self.data.get('module_type') or self.initial.get('module_type'):
|
||||||
|
parent_filter = Q(module_type=module_type_id)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
self.fields['rear_ports'].choices = self._get_rear_port_choices(parent_filter, self.instance)
|
||||||
# Populate rear port choices
|
|
||||||
self.fields['rear_ports'].choices = self._get_rear_port_choices(device_type, self.instance)
|
|
||||||
|
|
||||||
# Set initial rear port mappings
|
# Set initial rear port mappings
|
||||||
if self.instance.pk:
|
if self.instance.pk:
|
||||||
@@ -1152,27 +1152,6 @@ class FrontPortTemplateForm(FrontPortFormMixin, ModularComponentTemplateForm):
|
|||||||
for mapping in PortTemplateMapping.objects.filter(front_port_id=self.instance.pk)
|
for mapping in PortTemplateMapping.objects.filter(front_port_id=self.instance.pk)
|
||||||
]
|
]
|
||||||
|
|
||||||
def _get_rear_port_choices(self, device_type, front_port):
|
|
||||||
"""
|
|
||||||
Return a list of choices representing each available rear port & position pair on the device type, excluding
|
|
||||||
those assigned to the specified instance.
|
|
||||||
"""
|
|
||||||
occupied_rear_port_positions = [
|
|
||||||
f'{mapping.rear_port_id}:{mapping.rear_port_position}'
|
|
||||||
for mapping in device_type.port_mappings.exclude(front_port=front_port.pk)
|
|
||||||
]
|
|
||||||
|
|
||||||
choices = []
|
|
||||||
for rear_port in RearPortTemplate.objects.filter(device_type=device_type):
|
|
||||||
for i in range(1, rear_port.positions + 1):
|
|
||||||
pair_id = f'{rear_port.pk}:{i}'
|
|
||||||
if pair_id not in occupied_rear_port_positions:
|
|
||||||
pair_label = f'{rear_port.name}:{i}'
|
|
||||||
choices.append(
|
|
||||||
(pair_id, pair_label)
|
|
||||||
)
|
|
||||||
return choices
|
|
||||||
|
|
||||||
|
|
||||||
class RearPortTemplateForm(ModularComponentTemplateForm):
|
class RearPortTemplateForm(ModularComponentTemplateForm):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
@@ -1619,6 +1598,9 @@ class FrontPortForm(FrontPortFormMixin, ModularDeviceComponentForm):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
port_mapping_model = PortMapping
|
||||||
|
rear_port_model = RearPort
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPort
|
model = FrontPort
|
||||||
fields = [
|
fields = [
|
||||||
@@ -1629,13 +1611,12 @@ class FrontPortForm(FrontPortFormMixin, ModularDeviceComponentForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Populate rear port choices
|
||||||
if device_id := self.data.get('device') or self.initial.get('device'):
|
if device_id := self.data.get('device') or self.initial.get('device'):
|
||||||
device = Device.objects.get(pk=device_id)
|
parent_filter = Q(device=device_id)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
self.fields['rear_ports'].choices = self._get_rear_port_choices(parent_filter, self.instance)
|
||||||
# Populate rear port choices
|
|
||||||
self.fields['rear_ports'].choices = self._get_rear_port_choices(device, self.instance)
|
|
||||||
|
|
||||||
# Set initial rear port mappings
|
# Set initial rear port mappings
|
||||||
if self.instance.pk:
|
if self.instance.pk:
|
||||||
@@ -1644,27 +1625,6 @@ class FrontPortForm(FrontPortFormMixin, ModularDeviceComponentForm):
|
|||||||
for mapping in PortMapping.objects.filter(front_port_id=self.instance.pk)
|
for mapping in PortMapping.objects.filter(front_port_id=self.instance.pk)
|
||||||
]
|
]
|
||||||
|
|
||||||
def _get_rear_port_choices(self, device, front_port):
|
|
||||||
"""
|
|
||||||
Return a list of choices representing each available rear port & position pair on the device, excluding those
|
|
||||||
assigned to the specified instance.
|
|
||||||
"""
|
|
||||||
occupied_rear_port_positions = [
|
|
||||||
f'{mapping.rear_port_id}:{mapping.rear_port_position}'
|
|
||||||
for mapping in device.port_mappings.exclude(front_port=front_port.pk)
|
|
||||||
]
|
|
||||||
|
|
||||||
choices = []
|
|
||||||
for rear_port in RearPort.objects.filter(device=device):
|
|
||||||
for i in range(1, rear_port.positions + 1):
|
|
||||||
pair_id = f'{rear_port.pk}:{i}'
|
|
||||||
if pair_id not in occupied_rear_port_positions:
|
|
||||||
pair_label = f'{rear_port.name}:{i}'
|
|
||||||
choices.append(
|
|
||||||
(pair_id, pair_label)
|
|
||||||
)
|
|
||||||
return choices
|
|
||||||
|
|
||||||
|
|
||||||
class RearPortForm(ModularDeviceComponentForm):
|
class RearPortForm(ModularDeviceComponentForm):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ if TYPE_CHECKING:
|
|||||||
from netbox.graphql.filter_lookups import IntegerLookup
|
from netbox.graphql.filter_lookups import IntegerLookup
|
||||||
from extras.graphql.filters import ConfigTemplateFilter
|
from extras.graphql.filters import ConfigTemplateFilter
|
||||||
from ipam.graphql.filters import VLANFilter, VLANTranslationPolicyFilter
|
from ipam.graphql.filters import VLANFilter, VLANTranslationPolicyFilter
|
||||||
|
from dcim.graphql.filters import LocationFilter, RegionFilter, SiteFilter, SiteGroupFilter
|
||||||
from .filters import *
|
from .filters import *
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@@ -35,9 +36,32 @@ class ScopedFilterMixin:
|
|||||||
)
|
)
|
||||||
scope_id: ID | None = strawberry_django.filter_field()
|
scope_id: ID | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
# Cached relations
|
||||||
|
_location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
|
strawberry_django.filter_field(name='location')
|
||||||
|
)
|
||||||
|
_region: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
|
strawberry_django.filter_field(name='region')
|
||||||
|
)
|
||||||
|
_site_group: Annotated['SiteGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
|
strawberry_django.filter_field(name='site_group')
|
||||||
|
)
|
||||||
|
_site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
|
strawberry_django.filter_field(name='site')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ComponentModelFilterMixin:
|
class ComponentModelFilterMixin:
|
||||||
|
_site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
|
strawberry_django.filter_field(name='site')
|
||||||
|
)
|
||||||
|
_location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
|
strawberry_django.filter_field(name='location')
|
||||||
|
)
|
||||||
|
_rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
|
strawberry_django.filter_field(name='rack')
|
||||||
|
)
|
||||||
device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
||||||
device_id: ID | None = strawberry_django.filter_field()
|
device_id: ID | None = strawberry_django.filter_field()
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|||||||
@@ -259,11 +259,13 @@ class Module(TrackingModelMixin, PrimaryModel, ConfigContextModel):
|
|||||||
module_bays = []
|
module_bays = []
|
||||||
modules = []
|
modules = []
|
||||||
while module:
|
while module:
|
||||||
if module.pk in modules or module.module_bay.pk in module_bays:
|
module_module_bay = getattr(module, "module_bay", None)
|
||||||
|
if module.pk in modules or (module_module_bay and module_module_bay.pk in module_bays):
|
||||||
raise ValidationError(_("A module bay cannot belong to a module installed within it."))
|
raise ValidationError(_("A module bay cannot belong to a module installed within it."))
|
||||||
modules.append(module.pk)
|
modules.append(module.pk)
|
||||||
module_bays.append(module.module_bay.pk)
|
if module_module_bay:
|
||||||
module = module.module_bay.module if module.module_bay else None
|
module_bays.append(module_module_bay.pk)
|
||||||
|
module = module_module_bay.module if module_module_bay else None
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
is_new = self.pk is None
|
is_new = self.pk is None
|
||||||
|
|||||||
@@ -211,12 +211,16 @@ def sync_cached_scope_fields(instance, created, **kwargs):
|
|||||||
for model in (Prefix, Cluster, WirelessLAN):
|
for model in (Prefix, Cluster, WirelessLAN):
|
||||||
qs = model.objects.filter(**filters)
|
qs = model.objects.filter(**filters)
|
||||||
|
|
||||||
|
# Bulk update cached fields to avoid O(N) performance issues with large datasets.
|
||||||
|
# This does not trigger post_save signals, avoiding spurious change log entries.
|
||||||
|
objects_to_update = []
|
||||||
for obj in qs:
|
for obj in qs:
|
||||||
# Recompute cache using the same logic as save()
|
# Recompute cache using the same logic as save()
|
||||||
obj.cache_related_objects()
|
obj.cache_related_objects()
|
||||||
obj.save(update_fields=[
|
objects_to_update.append(obj)
|
||||||
'_location',
|
|
||||||
'_site',
|
if objects_to_update:
|
||||||
'_site_group',
|
model.objects.bulk_update(
|
||||||
'_region',
|
objects_to_update,
|
||||||
])
|
['_location', '_site', '_site_group', '_region']
|
||||||
|
)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class RackDimensionsPanel(panels.ObjectAttributesPanel):
|
|||||||
outer_width = attrs.NumericAttr('outer_width', unit_accessor='get_outer_unit_display')
|
outer_width = attrs.NumericAttr('outer_width', unit_accessor='get_outer_unit_display')
|
||||||
outer_height = attrs.NumericAttr('outer_height', unit_accessor='get_outer_unit_display')
|
outer_height = attrs.NumericAttr('outer_height', unit_accessor='get_outer_unit_display')
|
||||||
outer_depth = attrs.NumericAttr('outer_depth', unit_accessor='get_outer_unit_display')
|
outer_depth = attrs.NumericAttr('outer_depth', unit_accessor='get_outer_unit_display')
|
||||||
mounting_depth = attrs.TextAttr('mounting_depth', format_string='{}mm')
|
mounting_depth = attrs.TextAttr('mounting_depth', format_string=_('{} millimeters'))
|
||||||
|
|
||||||
|
|
||||||
class RackNumberingPanel(panels.ObjectAttributesPanel):
|
class RackNumberingPanel(panels.ObjectAttributesPanel):
|
||||||
|
|||||||
@@ -1845,6 +1845,7 @@ class ModuleTypeBulkEditView(generic.BulkEditView):
|
|||||||
class ModuleTypeBulkRenameView(generic.BulkRenameView):
|
class ModuleTypeBulkRenameView(generic.BulkRenameView):
|
||||||
queryset = ModuleType.objects.all()
|
queryset = ModuleType.objects.all()
|
||||||
filterset = filtersets.ModuleTypeFilterSet
|
filterset = filtersets.ModuleTypeFilterSet
|
||||||
|
field_name = 'model'
|
||||||
|
|
||||||
|
|
||||||
@register_model_view(ModuleType, 'bulk_delete', path='delete', detail=False)
|
@register_model_view(ModuleType, 'bulk_delete', path='delete', detail=False)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class ConfigContextProfileSerializer(PrimaryModelSerializer):
|
|||||||
)
|
)
|
||||||
data_file = DataFileSerializer(
|
data_file = DataFileSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
read_only=True
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -143,7 +143,7 @@ class ConfigContextSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedM
|
|||||||
)
|
)
|
||||||
data_file = DataFileSerializer(
|
data_file = DataFileSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
read_only=True
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -4,6 +4,17 @@ from extras.choices import LogLevelChoices
|
|||||||
# Custom fields
|
# Custom fields
|
||||||
CUSTOMFIELD_EMPTY_VALUES = (None, '', [])
|
CUSTOMFIELD_EMPTY_VALUES = (None, '', [])
|
||||||
|
|
||||||
|
# ImageAttachment
|
||||||
|
IMAGE_ATTACHMENT_IMAGE_FORMATS = {
|
||||||
|
'avif': 'image/avif',
|
||||||
|
'bmp': 'image/bmp',
|
||||||
|
'gif': 'image/gif',
|
||||||
|
'jpeg': 'image/jpeg',
|
||||||
|
'jpg': 'image/jpeg',
|
||||||
|
'png': 'image/png',
|
||||||
|
'webp': 'image/webp',
|
||||||
|
}
|
||||||
|
|
||||||
# Template Export
|
# Template Export
|
||||||
DEFAULT_MIME_TYPE = 'text/plain; charset=utf-8'
|
DEFAULT_MIME_TYPE = 'text/plain; charset=utf-8'
|
||||||
|
|
||||||
|
|||||||
@@ -271,10 +271,6 @@ class EventRuleImportForm(OwnerCSVMixin, NetBoxModelImportForm):
|
|||||||
|
|
||||||
class TagImportForm(OwnerCSVMixin, CSVModelForm):
|
class TagImportForm(OwnerCSVMixin, CSVModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
weight = forms.IntegerField(
|
|
||||||
label=_('Weight'),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
object_types = CSVMultipleContentTypeField(
|
object_types = CSVMultipleContentTypeField(
|
||||||
label=_('Object types'),
|
label=_('Object types'),
|
||||||
queryset=ObjectType.objects.with_feature('tags'),
|
queryset=ObjectType.objects.with_feature('tags'),
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from core.forms.mixins import SyncedDataMixin
|
from core.forms.mixins import SyncedDataMixin
|
||||||
from core.models import ObjectType
|
from core.models import ObjectType
|
||||||
from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
|
from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
|
||||||
|
from extras.constants import IMAGE_ATTACHMENT_IMAGE_FORMATS
|
||||||
from extras.choices import *
|
from extras.choices import *
|
||||||
from extras.models import *
|
from extras.models import *
|
||||||
from netbox.events import get_event_type_choices
|
from netbox.events import get_event_type_choices
|
||||||
@@ -570,10 +571,6 @@ class TagForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm):
|
|||||||
queryset=ObjectType.objects.with_feature('tags'),
|
queryset=ObjectType.objects.with_feature('tags'),
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
weight = forms.IntegerField(
|
|
||||||
label=_('Weight'),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
FieldSet('name', 'slug', 'color', 'weight', 'description', 'object_types', name=_('Tag')),
|
FieldSet('name', 'slug', 'color', 'weight', 'description', 'object_types', name=_('Tag')),
|
||||||
@@ -784,8 +781,11 @@ class ImageAttachmentForm(forms.ModelForm):
|
|||||||
fields = [
|
fields = [
|
||||||
'image', 'name', 'description',
|
'image', 'name', 'description',
|
||||||
]
|
]
|
||||||
help_texts = {
|
# Explicitly set 'image/avif' to support AVIF selection in Firefox
|
||||||
'name': _("If no name is specified, the file name will be used.")
|
widgets = {
|
||||||
|
'image': forms.ClearableFileInput(
|
||||||
|
attrs={'accept': ','.join(sorted(set(IMAGE_ATTACHMENT_IMAGE_FORMATS.values())))}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
@@ -7,7 +8,7 @@ from rest_framework import status
|
|||||||
|
|
||||||
from core.choices import ManagedFileRootPathChoices
|
from core.choices import ManagedFileRootPathChoices
|
||||||
from core.events import *
|
from core.events import *
|
||||||
from core.models import ObjectType
|
from core.models import DataFile, DataSource, ObjectType
|
||||||
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site
|
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site
|
||||||
from extras.choices import *
|
from extras.choices import *
|
||||||
from extras.models import *
|
from extras.models import *
|
||||||
@@ -731,6 +732,51 @@ class ConfigContextProfileTest(APIViewTestCases.APIViewTestCase):
|
|||||||
)
|
)
|
||||||
ConfigContextProfile.objects.bulk_create(profiles)
|
ConfigContextProfile.objects.bulk_create(profiles)
|
||||||
|
|
||||||
|
def test_update_data_source_and_data_file(self):
|
||||||
|
"""
|
||||||
|
Regression test: Ensure data_source and data_file can be assigned via the API.
|
||||||
|
|
||||||
|
This specifically covers PATCHing a ConfigContext with integer IDs for both fields.
|
||||||
|
"""
|
||||||
|
self.add_permissions(
|
||||||
|
'core.view_datafile',
|
||||||
|
'core.view_datasource',
|
||||||
|
'extras.view_configcontextprofile',
|
||||||
|
'extras.change_configcontextprofile',
|
||||||
|
)
|
||||||
|
config_context_profile = ConfigContextProfile.objects.first()
|
||||||
|
|
||||||
|
# Create a data source and file
|
||||||
|
datasource = DataSource.objects.create(
|
||||||
|
name='Data Source 1',
|
||||||
|
type='local',
|
||||||
|
source_url='file:///tmp/netbox-datasource/',
|
||||||
|
)
|
||||||
|
# Generate a valid dummy YAML file
|
||||||
|
file_data = b'profile: configcontext\n'
|
||||||
|
datafile = DataFile.objects.create(
|
||||||
|
source=datasource,
|
||||||
|
path='dir1/file1.yml',
|
||||||
|
last_updated=now(),
|
||||||
|
size=len(file_data),
|
||||||
|
hash=hashlib.sha256(file_data).hexdigest(),
|
||||||
|
data=file_data,
|
||||||
|
)
|
||||||
|
|
||||||
|
url = self._get_detail_url(config_context_profile)
|
||||||
|
payload = {
|
||||||
|
'data_source': datasource.pk,
|
||||||
|
'data_file': datafile.pk,
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, payload, format='json', **self.header)
|
||||||
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
config_context_profile.refresh_from_db()
|
||||||
|
self.assertEqual(config_context_profile.data_source_id, datasource.pk)
|
||||||
|
self.assertEqual(config_context_profile.data_file_id, datafile.pk)
|
||||||
|
self.assertEqual(response.data['data_source']['id'], datasource.pk)
|
||||||
|
self.assertEqual(response.data['data_file']['id'], datafile.pk)
|
||||||
|
|
||||||
|
|
||||||
class ConfigContextTest(APIViewTestCases.APIViewTestCase):
|
class ConfigContextTest(APIViewTestCases.APIViewTestCase):
|
||||||
model = ConfigContext
|
model = ConfigContext
|
||||||
@@ -812,6 +858,51 @@ class ConfigContextTest(APIViewTestCases.APIViewTestCase):
|
|||||||
rendered_context = device.get_config_context()
|
rendered_context = device.get_config_context()
|
||||||
self.assertEqual(rendered_context['bar'], 456)
|
self.assertEqual(rendered_context['bar'], 456)
|
||||||
|
|
||||||
|
def test_update_data_source_and_data_file(self):
|
||||||
|
"""
|
||||||
|
Regression test: Ensure data_source and data_file can be assigned via the API.
|
||||||
|
|
||||||
|
This specifically covers PATCHing a ConfigContext with integer IDs for both fields.
|
||||||
|
"""
|
||||||
|
self.add_permissions(
|
||||||
|
'core.view_datafile',
|
||||||
|
'core.view_datasource',
|
||||||
|
'extras.view_configcontext',
|
||||||
|
'extras.change_configcontext',
|
||||||
|
)
|
||||||
|
config_context = ConfigContext.objects.first()
|
||||||
|
|
||||||
|
# Create a data source and file
|
||||||
|
datasource = DataSource.objects.create(
|
||||||
|
name='Data Source 1',
|
||||||
|
type='local',
|
||||||
|
source_url='file:///tmp/netbox-datasource/',
|
||||||
|
)
|
||||||
|
# Generate a valid dummy YAML file
|
||||||
|
file_data = b'context: config\n'
|
||||||
|
datafile = DataFile.objects.create(
|
||||||
|
source=datasource,
|
||||||
|
path='dir1/file1.yml',
|
||||||
|
last_updated=now(),
|
||||||
|
size=len(file_data),
|
||||||
|
hash=hashlib.sha256(file_data).hexdigest(),
|
||||||
|
data=file_data,
|
||||||
|
)
|
||||||
|
|
||||||
|
url = self._get_detail_url(config_context)
|
||||||
|
payload = {
|
||||||
|
'data_source': datasource.pk,
|
||||||
|
'data_file': datafile.pk,
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, payload, format='json', **self.header)
|
||||||
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
config_context.refresh_from_db()
|
||||||
|
self.assertEqual(config_context.data_source_id, datasource.pk)
|
||||||
|
self.assertEqual(config_context.data_file_id, datafile.pk)
|
||||||
|
self.assertEqual(response.data['data_source']['id'], datasource.pk)
|
||||||
|
self.assertEqual(response.data['data_file']['id'], datafile.pk)
|
||||||
|
|
||||||
|
|
||||||
class ConfigTemplateTest(APIViewTestCases.APIViewTestCase):
|
class ConfigTemplateTest(APIViewTestCases.APIViewTestCase):
|
||||||
model = ConfigTemplate
|
model = ConfigTemplate
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
|
|||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
from django.test import tag, TestCase
|
from django.test import tag, TestCase
|
||||||
|
|
||||||
from core.models import DataSource, ObjectType
|
from core.models import AutoSyncRecord, DataSource, ObjectType
|
||||||
from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Platform, Region, Site, SiteGroup
|
from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Platform, Region, Site, SiteGroup
|
||||||
from extras.models import ConfigContext, ConfigContextProfile, ConfigTemplate, ImageAttachment, Tag, TaggedItem
|
from extras.models import ConfigContext, ConfigContextProfile, ConfigTemplate, ImageAttachment, Tag, TaggedItem
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
@@ -754,3 +754,53 @@ class ConfigTemplateTest(TestCase):
|
|||||||
@tag('regression')
|
@tag('regression')
|
||||||
def test_config_template_with_data_source_nested_templates(self):
|
def test_config_template_with_data_source_nested_templates(self):
|
||||||
self.assertEqual(self.BASE_TEMPLATE, self.main_config_template.render({}))
|
self.assertEqual(self.BASE_TEMPLATE, self.main_config_template.render({}))
|
||||||
|
|
||||||
|
@tag('regression')
|
||||||
|
def test_autosyncrecord_cleanup_on_detach(self):
|
||||||
|
"""Test that AutoSyncRecord is deleted when detaching from DataSource."""
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
templates_dir = Path(temp_dir) / "templates"
|
||||||
|
templates_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
self._create_template_file(templates_dir, 'test.j2', 'Test content')
|
||||||
|
|
||||||
|
data_source = DataSource(
|
||||||
|
name="Test DataSource for Detach",
|
||||||
|
type="local",
|
||||||
|
source_url=str(templates_dir),
|
||||||
|
)
|
||||||
|
data_source.save()
|
||||||
|
data_source.sync()
|
||||||
|
|
||||||
|
data_file = data_source.datafiles.filter(path__endswith='test.j2').first()
|
||||||
|
|
||||||
|
# Create a ConfigTemplate with data_file and auto_sync_enabled
|
||||||
|
config_template = ConfigTemplate(
|
||||||
|
name="TestTemplateForDetach",
|
||||||
|
data_file=data_file,
|
||||||
|
auto_sync_enabled=True
|
||||||
|
)
|
||||||
|
config_template.clean()
|
||||||
|
config_template.save()
|
||||||
|
|
||||||
|
# Verify AutoSyncRecord was created
|
||||||
|
object_type = ObjectType.objects.get_for_model(ConfigTemplate)
|
||||||
|
autosync_records = AutoSyncRecord.objects.filter(
|
||||||
|
object_type=object_type,
|
||||||
|
object_id=config_template.pk
|
||||||
|
)
|
||||||
|
self.assertEqual(autosync_records.count(), 1, "AutoSyncRecord should be created")
|
||||||
|
|
||||||
|
# Detach from DataSource
|
||||||
|
config_template.data_file = None
|
||||||
|
config_template.data_source = None
|
||||||
|
config_template.auto_sync_enabled = False
|
||||||
|
config_template.clean()
|
||||||
|
config_template.save()
|
||||||
|
|
||||||
|
# Verify AutoSyncRecord was deleted
|
||||||
|
autosync_records = AutoSyncRecord.objects.filter(
|
||||||
|
object_type=object_type,
|
||||||
|
object_id=config_template.pk
|
||||||
|
)
|
||||||
|
self.assertEqual(autosync_records.count(), 0, "AutoSyncRecord should be deleted after detaching")
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from taggit.managers import _TaggableManager
|
|||||||
|
|
||||||
from netbox.context import current_request
|
from netbox.context import current_request
|
||||||
|
|
||||||
|
from .constants import IMAGE_ATTACHMENT_IMAGE_FORMATS
|
||||||
from .validators import CustomValidator
|
from .validators import CustomValidator
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@@ -78,7 +79,7 @@ def image_upload(instance, filename):
|
|||||||
"""
|
"""
|
||||||
upload_dir = 'image-attachments'
|
upload_dir = 'image-attachments'
|
||||||
default_filename = 'unnamed'
|
default_filename = 'unnamed'
|
||||||
allowed_img_extensions = ('bmp', 'gif', 'jpeg', 'jpg', 'png', 'webp')
|
allowed_img_extensions = IMAGE_ATTACHMENT_IMAGE_FORMATS.keys()
|
||||||
|
|
||||||
# Normalize Windows paths and create a Path object.
|
# Normalize Windows paths and create a Path object.
|
||||||
normalized_filename = str(filename).replace('\\', '/')
|
normalized_filename = str(filename).replace('\\', '/')
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from ..field_serializers import IPAddressField, IPNetworkField
|
|||||||
__all__ = (
|
__all__ = (
|
||||||
'AggregateSerializer',
|
'AggregateSerializer',
|
||||||
'AvailableIPSerializer',
|
'AvailableIPSerializer',
|
||||||
|
'AvailableIPRequestSerializer',
|
||||||
'AvailablePrefixSerializer',
|
'AvailablePrefixSerializer',
|
||||||
'IPAddressSerializer',
|
'IPAddressSerializer',
|
||||||
'IPRangeSerializer',
|
'IPRangeSerializer',
|
||||||
@@ -147,6 +148,43 @@ class IPRangeSerializer(PrimaryModelSerializer):
|
|||||||
# IP addresses
|
# IP addresses
|
||||||
#
|
#
|
||||||
|
|
||||||
|
class AvailableIPRequestSerializer(serializers.Serializer):
|
||||||
|
"""
|
||||||
|
Request payload for creating IP addresses from the available-ips endpoint.
|
||||||
|
"""
|
||||||
|
prefix_length = serializers.IntegerField(required=False)
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
data = super().to_internal_value(data)
|
||||||
|
|
||||||
|
prefix_length = data.get('prefix_length')
|
||||||
|
if prefix_length is None:
|
||||||
|
# No override requested; the parent prefix/range mask length will be used.
|
||||||
|
return data
|
||||||
|
|
||||||
|
parent = self.context.get('parent')
|
||||||
|
if parent is None:
|
||||||
|
return data
|
||||||
|
|
||||||
|
# Validate the requested prefix length
|
||||||
|
if prefix_length < parent.mask_length:
|
||||||
|
raise serializers.ValidationError({
|
||||||
|
'prefix_length': 'Prefix length must be greater than or equal to the parent mask length ({})'.format(
|
||||||
|
parent.mask_length
|
||||||
|
)
|
||||||
|
})
|
||||||
|
elif parent.family == 4 and prefix_length > 32:
|
||||||
|
raise serializers.ValidationError({
|
||||||
|
'prefix_length': 'Invalid prefix length ({}) for IPv6'.format(prefix_length)
|
||||||
|
})
|
||||||
|
elif parent.family == 6 and prefix_length > 128:
|
||||||
|
raise serializers.ValidationError({
|
||||||
|
'prefix_length': 'Invalid prefix length ({}) for IPv4'.format(prefix_length)
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class IPAddressSerializer(PrimaryModelSerializer):
|
class IPAddressSerializer(PrimaryModelSerializer):
|
||||||
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
|
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
|
||||||
address = IPAddressField()
|
address = IPAddressField()
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ class AvailablePrefixesView(AvailableObjectsView):
|
|||||||
class AvailableIPAddressesView(AvailableObjectsView):
|
class AvailableIPAddressesView(AvailableObjectsView):
|
||||||
queryset = IPAddress.objects.all()
|
queryset = IPAddress.objects.all()
|
||||||
read_serializer_class = serializers.AvailableIPSerializer
|
read_serializer_class = serializers.AvailableIPSerializer
|
||||||
write_serializer_class = serializers.AvailableIPSerializer
|
write_serializer_class = serializers.AvailableIPRequestSerializer
|
||||||
advisory_lock_key = 'available-ips'
|
advisory_lock_key = 'available-ips'
|
||||||
|
|
||||||
def get_available_objects(self, parent, limit=None):
|
def get_available_objects(self, parent, limit=None):
|
||||||
@@ -421,8 +421,9 @@ class AvailableIPAddressesView(AvailableObjectsView):
|
|||||||
def prep_object_data(self, requested_objects, available_objects, parent):
|
def prep_object_data(self, requested_objects, available_objects, parent):
|
||||||
available_ips = iter(available_objects)
|
available_ips = iter(available_objects)
|
||||||
for i, request_data in enumerate(requested_objects):
|
for i, request_data in enumerate(requested_objects):
|
||||||
|
prefix_length = request_data.pop('prefix_length', None) or parent.mask_length
|
||||||
request_data.update({
|
request_data.update({
|
||||||
'address': f'{next(available_ips)}/{parent.mask_length}',
|
'address': f'{next(available_ips)}/{prefix_length}',
|
||||||
'vrf': parent.vrf.pk if parent.vrf else None,
|
'vrf': parent.vrf.pk if parent.vrf else None,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -435,7 +436,7 @@ class AvailableIPAddressesView(AvailableObjectsView):
|
|||||||
@extend_schema(
|
@extend_schema(
|
||||||
methods=["post"],
|
methods=["post"],
|
||||||
responses={201: serializers.IPAddressSerializer(many=True)},
|
responses={201: serializers.IPAddressSerializer(many=True)},
|
||||||
request=serializers.IPAddressSerializer(many=True),
|
request=serializers.AvailableIPRequestSerializer(many=True),
|
||||||
)
|
)
|
||||||
def post(self, request, pk):
|
def post(self, request, pk):
|
||||||
return super().post(request, pk)
|
return super().post(request, pk)
|
||||||
|
|||||||
@@ -538,7 +538,7 @@ class VLANFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
|
|||||||
FieldSet('qinq_role', 'qinq_svlan_id', name=_('Q-in-Q/802.1ad')),
|
FieldSet('qinq_role', 'qinq_svlan_id', name=_('Q-in-Q/802.1ad')),
|
||||||
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
|
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
|
||||||
)
|
)
|
||||||
selector_fields = ('filter_id', 'q', 'site_id')
|
selector_fields = ('filter_id', 'q', 'group_id')
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
|||||||
@@ -372,8 +372,8 @@ class IPAddressForm(TenancyForm, PrimaryModelForm):
|
|||||||
'virtual_machine_id': instance.assigned_object.virtual_machine.pk,
|
'virtual_machine_id': instance.assigned_object.virtual_machine.pk,
|
||||||
})
|
})
|
||||||
|
|
||||||
# Disable object assignment fields if the IP address is designated as primary
|
# Disable object assignment fields if the IP address is designated as primary or OOB
|
||||||
if self.initial.get('primary_for_parent'):
|
if self.initial.get('primary_for_parent') or self.initial.get('oob_for_parent'):
|
||||||
self.fields['interface'].disabled = True
|
self.fields['interface'].disabled = True
|
||||||
self.fields['vminterface'].disabled = True
|
self.fields['vminterface'].disabled = True
|
||||||
self.fields['fhrpgroup'].disabled = True
|
self.fields['fhrpgroup'].disabled = True
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin
|
|||||||
from virtualization.models import VMInterface
|
from virtualization.models import VMInterface
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from netbox.graphql.filter_lookups import IntegerLookup, IntegerRangeArrayLookup
|
from netbox.graphql.filter_lookups import BigIntegerLookup, IntegerLookup, IntegerRangeArrayLookup
|
||||||
from circuits.graphql.filters import ProviderFilter
|
from circuits.graphql.filters import ProviderFilter
|
||||||
from core.graphql.filters import ContentTypeFilter
|
from core.graphql.filters import ContentTypeFilter
|
||||||
from dcim.graphql.filters import SiteFilter
|
from dcim.graphql.filters import SiteFilter
|
||||||
@@ -53,7 +53,7 @@ __all__ = (
|
|||||||
class ASNFilter(TenancyFilterMixin, PrimaryModelFilter):
|
class ASNFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
|
rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
|
||||||
rir_id: ID | None = strawberry_django.filter_field()
|
rir_id: ID | None = strawberry_django.filter_field()
|
||||||
asn: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
asn: Annotated['BigIntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
sites: (
|
sites: (
|
||||||
@@ -70,10 +70,10 @@ class ASNRangeFilter(TenancyFilterMixin, OrganizationalModelFilter):
|
|||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
|
rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
|
||||||
rir_id: ID | None = strawberry_django.filter_field()
|
rir_id: ID | None = strawberry_django.filter_field()
|
||||||
start: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
start: Annotated['BigIntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
end: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
end: Annotated['BigIntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -940,6 +940,13 @@ class IPAddress(ContactsMixin, PrimaryModel):
|
|||||||
_("Cannot reassign IP address while it is designated as the primary IP for the parent object")
|
_("Cannot reassign IP address while it is designated as the primary IP for the parent object")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# can't use is_oob_ip as self.assigned_object might be changed
|
||||||
|
if hasattr(original_parent, 'oob_ip') and original_parent.oob_ip_id == self.pk:
|
||||||
|
if parent != original_parent:
|
||||||
|
raise ValidationError(
|
||||||
|
_("Cannot reassign IP address while it is designated as the OOB IP for the parent object")
|
||||||
|
)
|
||||||
|
|
||||||
# Validate IP status selection
|
# Validate IP status selection
|
||||||
if self.status == IPAddressStatusChoices.STATUS_SLAAC and self.family != 6:
|
if self.status == IPAddressStatusChoices.STATUS_SLAAC and self.family != 6:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
|
|||||||
@@ -370,6 +370,11 @@ class AnnotatedIPAddressTable(IPAddressTable):
|
|||||||
verbose_name=_('IP Address')
|
verbose_name=_('IP Address')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def render_pk(self, value, record, bound_column):
|
||||||
|
if type(record) is not self._meta.model:
|
||||||
|
return ''
|
||||||
|
return bound_column.column.render(value, bound_column, record)
|
||||||
|
|
||||||
class Meta(IPAddressTable.Meta):
|
class Meta(IPAddressTable.Meta):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -595,6 +595,31 @@ class PrefixTest(APIViewTestCases.APIViewTestCase):
|
|||||||
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
||||||
self.assertEqual(len(response.data), 8)
|
self.assertEqual(len(response.data), 8)
|
||||||
|
|
||||||
|
def test_create_available_ip_with_mask(self):
|
||||||
|
"""
|
||||||
|
Test the creation of an available IP address with a specific prefix length.
|
||||||
|
"""
|
||||||
|
prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24'))
|
||||||
|
url = reverse('ipam-api:prefix-available-ips', kwargs={'pk': prefix.pk})
|
||||||
|
self.add_permissions('ipam.view_prefix', 'ipam.add_ipaddress')
|
||||||
|
|
||||||
|
# Create an available IP with a specific prefix length
|
||||||
|
data = {
|
||||||
|
'prefix_length': 32,
|
||||||
|
'description': 'Test IP 1',
|
||||||
|
}
|
||||||
|
response = self.client.post(url, data, format='json', **self.header)
|
||||||
|
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
||||||
|
self.assertEqual(response.data['address'], '192.0.2.1/32')
|
||||||
|
self.assertEqual(response.data['description'], data['description'])
|
||||||
|
|
||||||
|
# Attempt to create an available IP with a prefix length less than its parent prefix
|
||||||
|
data = {
|
||||||
|
'prefix_length': 23, # Prefix is a /24
|
||||||
|
}
|
||||||
|
response = self.client.post(url, data, format='json', **self.header)
|
||||||
|
self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@tag('regression')
|
@tag('regression')
|
||||||
def test_graphql_tenant_prefixes_contains_nested_skips_invalid(self):
|
def test_graphql_tenant_prefixes_contains_nested_skips_invalid(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
41
netbox/ipam/tests/test_tables.py
Normal file
41
netbox/ipam/tests/test_tables.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
from django.test import RequestFactory, TestCase
|
||||||
|
from netaddr import IPNetwork
|
||||||
|
|
||||||
|
from ipam.models import IPAddress, IPRange, Prefix
|
||||||
|
from ipam.tables import AnnotatedIPAddressTable
|
||||||
|
from ipam.utils import annotate_ip_space
|
||||||
|
|
||||||
|
|
||||||
|
class AnnotatedIPAddressTableTest(TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.prefix = Prefix.objects.create(
|
||||||
|
prefix=IPNetwork('10.1.1.0/24'),
|
||||||
|
status='active'
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.ip_address = IPAddress.objects.create(
|
||||||
|
address='10.1.1.1/24',
|
||||||
|
status='active'
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.ip_range = IPRange.objects.create(
|
||||||
|
start_address=IPNetwork('10.1.1.2/24'),
|
||||||
|
end_address=IPNetwork('10.1.1.10/24'),
|
||||||
|
status='active'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_ipaddress_has_checkbox_iprange_does_not(self):
|
||||||
|
data = annotate_ip_space(self.prefix)
|
||||||
|
table = AnnotatedIPAddressTable(data, orderable=False)
|
||||||
|
table.columns.show('pk')
|
||||||
|
|
||||||
|
request = RequestFactory().get('/')
|
||||||
|
html = table.as_html(request)
|
||||||
|
|
||||||
|
ipaddress_checkbox_count = html.count(f'name="pk" value="{self.ip_address.pk}"')
|
||||||
|
self.assertEqual(ipaddress_checkbox_count, 1)
|
||||||
|
|
||||||
|
iprange_checkbox_count = html.count(f'name="pk" value="{self.ip_range.pk}"')
|
||||||
|
self.assertEqual(iprange_checkbox_count, 0)
|
||||||
@@ -49,6 +49,9 @@ def add_requested_prefixes(parent, prefix_list, show_available=True, show_assign
|
|||||||
if prefix_list and show_available:
|
if prefix_list and show_available:
|
||||||
|
|
||||||
# Find all unallocated space, add fake Prefix objects to child_prefixes.
|
# Find all unallocated space, add fake Prefix objects to child_prefixes.
|
||||||
|
# IMPORTANT: These are unsaved Prefix instances (pk=None). If this is ever changed to use
|
||||||
|
# saved Prefix instances with real pks, bulk delete will fail for mixed-type selections
|
||||||
|
# due to single-model form validation. See: https://github.com/netbox-community/netbox/issues/21176
|
||||||
available_prefixes = netaddr.IPSet(parent) ^ netaddr.IPSet([p.prefix for p in prefix_list])
|
available_prefixes = netaddr.IPSet(parent) ^ netaddr.IPSet([p.prefix for p in prefix_list])
|
||||||
available_prefixes = [Prefix(prefix=p, status=None) for p in available_prefixes.iter_cidrs()]
|
available_prefixes = [Prefix(prefix=p, status=None) for p in available_prefixes.iter_cidrs()]
|
||||||
child_prefixes = child_prefixes + available_prefixes
|
child_prefixes = child_prefixes + available_prefixes
|
||||||
|
|||||||
0
netbox/media/.gitkeep
Normal file
0
netbox/media/.gitkeep
Normal file
@@ -19,8 +19,11 @@ from strawberry_django import (
|
|||||||
process_filters,
|
process_filters,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from netbox.graphql.scalars import BigInt
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ArrayLookup',
|
'ArrayLookup',
|
||||||
|
'BigIntegerLookup',
|
||||||
'FloatArrayLookup',
|
'FloatArrayLookup',
|
||||||
'FloatLookup',
|
'FloatLookup',
|
||||||
'IntegerArrayLookup',
|
'IntegerArrayLookup',
|
||||||
@@ -78,6 +81,29 @@ class IntegerLookup:
|
|||||||
return process_filters(filters=filters, queryset=queryset, info=info, prefix=prefix)
|
return process_filters(filters=filters, queryset=queryset, info=info, prefix=prefix)
|
||||||
|
|
||||||
|
|
||||||
|
@strawberry.input(one_of=True, description='Lookup for BigInteger fields. Only one of the lookup fields can be set.')
|
||||||
|
class BigIntegerLookup:
|
||||||
|
filter_lookup: FilterLookup[BigInt] | None = strawberry_django.filter_field()
|
||||||
|
range_lookup: RangeLookup[BigInt] | None = strawberry_django.filter_field()
|
||||||
|
comparison_lookup: ComparisonFilterLookup[BigInt] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
def get_filter(self):
|
||||||
|
for field in self.__strawberry_definition__.fields:
|
||||||
|
value = getattr(self, field.name, None)
|
||||||
|
if value is not strawberry.UNSET:
|
||||||
|
return value
|
||||||
|
return None
|
||||||
|
|
||||||
|
@strawberry_django.filter_field
|
||||||
|
def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> Tuple[QuerySet, Q]:
|
||||||
|
filters = self.get_filter()
|
||||||
|
|
||||||
|
if not filters:
|
||||||
|
return queryset, Q()
|
||||||
|
|
||||||
|
return process_filters(filters=filters, queryset=queryset, info=info, prefix=prefix)
|
||||||
|
|
||||||
|
|
||||||
@strawberry.input(one_of=True, description='Lookup for Float fields. Only one of the lookup fields can be set.')
|
@strawberry.input(one_of=True, description='Lookup for Float fields. Only one of the lookup fields can be set.')
|
||||||
class FloatLookup:
|
class FloatLookup:
|
||||||
filter_lookup: FilterLookup[float] | None = strawberry_django.filter_field()
|
filter_lookup: FilterLookup[float] | None = strawberry_django.filter_field()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry import ID
|
from strawberry import ID
|
||||||
from strawberry_django import FilterLookup
|
from strawberry_django import ComparisonFilterLookup, FilterLookup
|
||||||
|
|
||||||
from core.graphql.filter_mixins import ChangeLoggingMixin
|
from core.graphql.filter_mixins import ChangeLoggingMixin
|
||||||
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, JournalEntriesFilterMixin, TagsFilterMixin
|
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, JournalEntriesFilterMixin, TagsFilterMixin
|
||||||
@@ -23,7 +23,7 @@ __all__ = (
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BaseModelFilter:
|
class BaseModelFilter:
|
||||||
id: FilterLookup[ID] | None = strawberry_django.filter_field()
|
id: ComparisonFilterLookup[ID] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
class ChangeLoggedModelFilter(ChangeLoggingMixin, BaseModelFilter):
|
class ChangeLoggedModelFilter(ChangeLoggingMixin, BaseModelFilter):
|
||||||
|
|||||||
@@ -569,7 +569,6 @@ class SyncedDataMixin(models.Model):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
AutoSyncRecord.objects.filter(
|
AutoSyncRecord.objects.filter(
|
||||||
datafile=self.data_file,
|
|
||||||
object_type=object_type,
|
object_type=object_type,
|
||||||
object_id=self.pk
|
object_id=self.pk
|
||||||
).delete()
|
).delete()
|
||||||
@@ -582,7 +581,6 @@ class SyncedDataMixin(models.Model):
|
|||||||
# Delete AutoSyncRecord
|
# Delete AutoSyncRecord
|
||||||
object_type = ObjectType.objects.get_for_model(self)
|
object_type = ObjectType.objects.get_for_model(self)
|
||||||
AutoSyncRecord.objects.filter(
|
AutoSyncRecord.objects.filter(
|
||||||
datafile=self.data_file,
|
|
||||||
object_type=object_type,
|
object_type=object_type,
|
||||||
object_id=self.pk
|
object_id=self.pk
|
||||||
).delete()
|
).delete()
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ VPN_MENU = Menu(
|
|||||||
label=_('L2VPNs'),
|
label=_('L2VPNs'),
|
||||||
items=(
|
items=(
|
||||||
get_model_item('vpn', 'l2vpn', _('L2VPNs')),
|
get_model_item('vpn', 'l2vpn', _('L2VPNs')),
|
||||||
get_model_item('vpn', 'l2vpntermination', _('Terminations')),
|
get_model_item('vpn', 'l2vpntermination', _('L2VPN Terminations')),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
MenuGroup(
|
MenuGroup(
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ class PluginMenuItem:
|
|||||||
Alternatively, a pre-generated url can be set on the object which will be rendered literally.
|
Alternatively, a pre-generated url can be set on the object which will be rendered literally.
|
||||||
Buttons are each specified as a list of PluginMenuButton instances.
|
Buttons are each specified as a list of PluginMenuButton instances.
|
||||||
"""
|
"""
|
||||||
permissions = []
|
|
||||||
buttons = []
|
|
||||||
_url = None
|
_url = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -54,10 +52,14 @@ class PluginMenuItem:
|
|||||||
if type(permissions) not in (list, tuple):
|
if type(permissions) not in (list, tuple):
|
||||||
raise TypeError(_("Permissions must be passed as a tuple or list."))
|
raise TypeError(_("Permissions must be passed as a tuple or list."))
|
||||||
self.permissions = permissions
|
self.permissions = permissions
|
||||||
|
else:
|
||||||
|
self.permissions = []
|
||||||
if buttons is not None:
|
if buttons is not None:
|
||||||
if type(buttons) not in (list, tuple):
|
if type(buttons) not in (list, tuple):
|
||||||
raise TypeError(_("Buttons must be passed as a tuple or list."))
|
raise TypeError(_("Buttons must be passed as a tuple or list."))
|
||||||
self.buttons = buttons
|
self.buttons = buttons
|
||||||
|
else:
|
||||||
|
self.buttons = []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
@@ -74,7 +76,6 @@ class PluginMenuButton:
|
|||||||
ButtonColorChoices.
|
ButtonColorChoices.
|
||||||
"""
|
"""
|
||||||
color = ButtonColorChoices.DEFAULT
|
color = ButtonColorChoices.DEFAULT
|
||||||
permissions = []
|
|
||||||
_url = None
|
_url = None
|
||||||
|
|
||||||
def __init__(self, link, title, icon_class, color=None, permissions=None):
|
def __init__(self, link, title, icon_class, color=None, permissions=None):
|
||||||
@@ -87,6 +88,8 @@ class PluginMenuButton:
|
|||||||
if type(permissions) not in (list, tuple):
|
if type(permissions) not in (list, tuple):
|
||||||
raise TypeError(_("Permissions must be passed as a tuple or list."))
|
raise TypeError(_("Permissions must be passed as a tuple or list."))
|
||||||
self.permissions = permissions
|
self.permissions = permissions
|
||||||
|
else:
|
||||||
|
self.permissions = []
|
||||||
if color is not None:
|
if color is not None:
|
||||||
if color not in ButtonColorChoices.values():
|
if color not in ButtonColorChoices.values():
|
||||||
raise ValueError(_("Button color must be a choice within ButtonColorChoices."))
|
raise ValueError(_("Button color must be a choice within ButtonColorChoices."))
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from netbox.tests.dummy_plugin import config as dummy_config
|
|||||||
from netbox.tests.dummy_plugin.data_backends import DummyBackend
|
from netbox.tests.dummy_plugin.data_backends import DummyBackend
|
||||||
from netbox.tests.dummy_plugin.jobs import DummySystemJob
|
from netbox.tests.dummy_plugin.jobs import DummySystemJob
|
||||||
from netbox.tests.dummy_plugin.webhook_callbacks import set_context
|
from netbox.tests.dummy_plugin.webhook_callbacks import set_context
|
||||||
from netbox.plugins.navigation import PluginMenu
|
from netbox.plugins.navigation import PluginMenu, PluginMenuItem, PluginMenuButton
|
||||||
from netbox.plugins.utils import get_plugin_config
|
from netbox.plugins.utils import get_plugin_config
|
||||||
from netbox.graphql.schema import Query
|
from netbox.graphql.schema import Query
|
||||||
from netbox.registry import registry
|
from netbox.registry import registry
|
||||||
@@ -227,3 +227,46 @@ class PluginTest(TestCase):
|
|||||||
Test the registration of webhook callbacks.
|
Test the registration of webhook callbacks.
|
||||||
"""
|
"""
|
||||||
self.assertIn(set_context, registry['webhook_callbacks'])
|
self.assertIn(set_context, registry['webhook_callbacks'])
|
||||||
|
|
||||||
|
|
||||||
|
class PluginNavigationTest(TestCase):
|
||||||
|
|
||||||
|
def test_plugin_menu_item_independent_permissions(self):
|
||||||
|
item1 = PluginMenuItem(link='test1', link_text='Test 1')
|
||||||
|
item1.permissions.append('leaked_permission')
|
||||||
|
|
||||||
|
item2 = PluginMenuItem(link='test2', link_text='Test 2')
|
||||||
|
|
||||||
|
self.assertIsNot(item1.permissions, item2.permissions)
|
||||||
|
self.assertEqual(item1.permissions, ['leaked_permission'])
|
||||||
|
self.assertEqual(item2.permissions, [])
|
||||||
|
|
||||||
|
def test_plugin_menu_item_independent_buttons(self):
|
||||||
|
item1 = PluginMenuItem(link='test1', link_text='Test 1')
|
||||||
|
button = PluginMenuButton(link='button1', title='Button 1', icon_class='mdi-test')
|
||||||
|
item1.buttons.append(button)
|
||||||
|
|
||||||
|
item2 = PluginMenuItem(link='test2', link_text='Test 2')
|
||||||
|
|
||||||
|
self.assertIsNot(item1.buttons, item2.buttons)
|
||||||
|
self.assertEqual(len(item1.buttons), 1)
|
||||||
|
self.assertEqual(item1.buttons[0], button)
|
||||||
|
self.assertEqual(item2.buttons, [])
|
||||||
|
|
||||||
|
def test_plugin_menu_button_independent_permissions(self):
|
||||||
|
button1 = PluginMenuButton(link='button1', title='Button 1', icon_class='mdi-test')
|
||||||
|
button1.permissions.append('leaked_permission')
|
||||||
|
|
||||||
|
button2 = PluginMenuButton(link='button2', title='Button 2', icon_class='mdi-test')
|
||||||
|
|
||||||
|
self.assertIsNot(button1.permissions, button2.permissions)
|
||||||
|
self.assertEqual(button1.permissions, ['leaked_permission'])
|
||||||
|
self.assertEqual(button2.permissions, [])
|
||||||
|
|
||||||
|
def test_explicit_permissions_remain_independent(self):
|
||||||
|
item1 = PluginMenuItem(link='test1', link_text='Test 1', permissions=['explicit_permission'])
|
||||||
|
item2 = PluginMenuItem(link='test2', link_text='Test 2', permissions=['different_permission'])
|
||||||
|
|
||||||
|
self.assertIsNot(item1.permissions, item2.permissions)
|
||||||
|
self.assertEqual(item1.permissions, ['explicit_permission'])
|
||||||
|
self.assertEqual(item2.permissions, ['different_permission'])
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ class ObjectAttributesPanel(ObjectPanel, metaclass=ObjectAttributesPanelMeta):
|
|||||||
"""
|
"""
|
||||||
label = name[:1].upper() + name[1:]
|
label = name[:1].upper() + name[1:]
|
||||||
label = label.replace('_', ' ')
|
label = label.replace('_', ' ')
|
||||||
return label
|
return _(label)
|
||||||
|
|
||||||
def get_context(self, context):
|
def get_context(self, context):
|
||||||
# Determine which attributes to display in the panel based on only/exclude args
|
# Determine which attributes to display in the panel based on only/exclude args
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
.docExplorerWrap{height:unset!important;min-width:unset!important;width:unset!important}.docExplorerWrap svg{display:unset}.doc-explorer-title{font-size:var(--font-size-h2);font-weight:var(--font-weight-medium)}.doc-explorer-rhs{display:none}.graphiql-explorer-root{font-family:var(--font-family-mono)!important;font-size:var(--font-size-body)!important;padding:0!important}.graphiql-explorer-root>div>div{border-color:hsla(var(--color-neutral),var(--alpha-background-heavy))!important;padding-top:var(--px-16)}.graphiql-explorer-root input{background:unset}.graphiql-explorer-root select{background:hsl(var(--color-base))!important;border:1px solid hsla(var(--color-neutral),var(--alpha-secondary));border-radius:var(--border-radius-4);color:hsl(var(--color-neutral))!important;margin:0 var(--px-8);padding:var(--px-4) var(--px-6)}.graphiql-operation-title-bar .toolbar-button{line-height:0;margin-left:var(--px-8);color:hsla(var(--color-neutral),var(--alpha-secondary, .6));font-size:var(--font-size-h3);vertical-align:middle}.graphiql-explorer-graphql-arguments input{line-height:0}.graphiql-explorer-actions{border-color:hsla(var(--color-neutral),var(--alpha-background-heavy))!important}
|
.docExplorerWrap{height:unset!important;min-width:unset!important;width:unset!important}.docExplorerWrap svg{display:unset}.doc-explorer-title{font-size:var(--font-size-h2);font-weight:var(--font-weight-medium)}.doc-explorer-rhs{display:none}.graphiql-explorer-root{font-family:var(--font-family-mono)!important;font-size:var(--font-size-body)!important;padding:0!important}.graphiql-explorer-root>div>div{padding-top:var(--px-16);border-color:hsla(var(--color-neutral),var(--alpha-background-heavy))!important}.graphiql-explorer-root>div{overflow:auto!important}.graphiql-explorer-root input{background:unset}.graphiql-explorer-root select{border:1px solid hsla(var(--color-neutral),var(--alpha-secondary));border-radius:var(--border-radius-4);margin:0 var(--px-8);padding:var(--px-4)var(--px-6);background:hsl(var(--color-base))!important;color:hsl(var(--color-neutral))!important}.toolbar-button{all:unset;cursor:pointer;margin-left:var(--px-6);color:hsl(var(--color-primary));line-height:0!important;font-size:var(--font-size-h3)!important}.graphiql-explorer-slug .toolbar-button,.graphiql-explorer-graphql-arguments .toolbar-button{font-size:inherit!important}.graphiql-explorer-graphql-arguments input{min-width:2rem;line-height:0}.graphiql-explorer-actions{border-color:hsla(var(--color-neutral),var(--alpha-background-heavy))!important}
|
||||||
|
|||||||
2
netbox/project-static/dist/netbox.css
vendored
2
netbox/project-static/dist/netbox.css
vendored
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
svg{--nbx-rack-bg: var(--tblr-bg-surface-secondary);--nbx-rack-border: #000;--nbx-rack-slot-bg: #e9ecef;--nbx-rack-slot-border: #adb5bd;--nbx-rack-slot-hover-bg: #ced4da;--nbx-rack-link-color: #0d6efd;--nbx-rack-unit-color: #6c757d}svg[data-bs-theme=dark]{--nbx-rack-bg: rgb(27, 41, 58);--nbx-rack-border: #6c757d;--nbx-rack-slot-bg: #343a40;--nbx-rack-slot-border: #495057;--nbx-rack-slot-hover-bg: #212529;--nbx-rack-link-color: #9ec5fe;--nbx-rack-unit-color: #adb5bd}rect{box-sizing:border-box}text{text-anchor:middle;dominant-baseline:middle}svg{background-color:var(--nbx-rack-bg);font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Noto Sans,Liberation Sans,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-size:.875rem}svg .unit{margin:0;padding:5px 0;fill:var(--nbx-rack-unit-color)}svg .hidden{visibility:hidden}svg rect.shaded,svg image.shaded{opacity:25%}svg text.shaded{opacity:50%}svg .rack{fill:none;stroke-width:2px;stroke:var(--nbx-rack-border)}svg .slot{fill:var(--nbx-rack-slot-bg);stroke:var(--nbx-rack-slot-border)}svg .slot:hover{fill:var(--nbx-rack-slot-hover-bg)}svg .slot+.add-device{fill:var(--nbx-rack-link-color);opacity:0;pointer-events:none}svg .slot:hover+.add-device{opacity:1}svg .slot.occupied[class],svg .slot.occupied:hover[class]{fill:url(#occupied)}svg .slot.blocked[class],svg .slot.blocked:hover[class]{fill:url(#blocked)}svg .slot.blocked:hover+.add-device{opacity:0}svg .reservation[class]{fill:url(#reserved)}
|
svg{--nbx-rack-bg: var(--tblr-bg-surface-secondary);--nbx-rack-border: #000;--nbx-rack-slot-bg: #e9ecef;--nbx-rack-slot-border: #adb5bd;--nbx-rack-slot-hover-bg: #ced4da;--nbx-rack-link-color: #0d6efd;--nbx-rack-unit-color: #6c757d}svg[data-bs-theme=dark]{--nbx-rack-bg: rgb(27, 41, 58);--nbx-rack-border: #6c757d;--nbx-rack-slot-bg: #343a40;--nbx-rack-slot-border: #495057;--nbx-rack-slot-hover-bg: #212529;--nbx-rack-link-color: rgb(158.2, 197, 254.2);--nbx-rack-unit-color: #adb5bd}rect{box-sizing:border-box}text{text-anchor:middle;dominant-baseline:middle}svg{background-color:var(--nbx-rack-bg);font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Noto Sans,Liberation Sans,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-size:.875rem}svg .unit{margin:0;padding:5px 0;fill:var(--nbx-rack-unit-color)}svg .hidden{visibility:hidden}svg rect.shaded,svg image.shaded{opacity:25%}svg text.shaded{opacity:50%}svg .rack{fill:none;stroke-width:2px;stroke:var(--nbx-rack-border)}svg .slot{fill:var(--nbx-rack-slot-bg);stroke:var(--nbx-rack-slot-border)}svg .slot:hover{fill:var(--nbx-rack-slot-hover-bg)}svg .slot+.add-device{fill:var(--nbx-rack-link-color);opacity:0;pointer-events:none}svg .slot:hover+.add-device{opacity:1}svg .slot.occupied[class],svg .slot.occupied:hover[class]{fill:url(#occupied)}svg .slot.blocked[class],svg .slot.blocked:hover[class]{fill:url(#blocked)}svg .slot.blocked:hover+.add-device{opacity:0}svg .reservation[class]{fill:url(#reserved)}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@graphiql/plugin-explorer": "3.2.6",
|
"@graphiql/plugin-explorer": "4.0.6",
|
||||||
"graphiql": "4.1.2",
|
"graphiql": "4.1.2",
|
||||||
"graphql": "16.12.0",
|
"graphql": "16.12.0",
|
||||||
"js-cookie": "3.0.5",
|
"js-cookie": "3.0.5",
|
||||||
|
|||||||
@@ -37,23 +37,23 @@
|
|||||||
"typeface-roboto-mono": "1.1.13"
|
"typeface-roboto-mono": "1.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^2.0.0",
|
"@eslint/compat": "^2.0.1",
|
||||||
"@eslint/eslintrc": "^3.3.3",
|
"@eslint/eslintrc": "^3.3.3",
|
||||||
"@eslint/js": "^9.39.2",
|
"@eslint/js": "^9.39.2",
|
||||||
"@types/bootstrap": "5.2.10",
|
"@types/bootstrap": "5.2.10",
|
||||||
"@types/cookie": "^1.0.0",
|
"@types/cookie": "^1.0.0",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
"@typescript-eslint/eslint-plugin": "^8.53.1",
|
||||||
"@typescript-eslint/parser": "^8.48.1",
|
"@typescript-eslint/parser": "^8.53.1",
|
||||||
"esbuild": "^0.27.1",
|
"esbuild": "^0.27.2",
|
||||||
"esbuild-sass-plugin": "^3.3.1",
|
"esbuild-sass-plugin": "^3.6.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-import-resolver-typescript": "^4.4.4",
|
"eslint-import-resolver-typescript": "^4.4.4",
|
||||||
"eslint-plugin-import": "^2.32.0",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
"eslint-plugin-prettier": "^5.5.1",
|
"eslint-plugin-prettier": "^5.5.5",
|
||||||
"globals": "^16.5.0",
|
"globals": "^17.0.0",
|
||||||
"prettier": "^3.7.4",
|
"prettier": "^3.8.0",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
|||||||
@@ -24,142 +24,135 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.4.0"
|
tslib "^2.4.0"
|
||||||
|
|
||||||
"@esbuild/aix-ppc64@0.27.1":
|
"@esbuild/aix-ppc64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz#116edcd62c639ed8ab551e57b38251bb28384de4"
|
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz#521cbd968dcf362094034947f76fa1b18d2d403c"
|
||||||
integrity sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==
|
integrity sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==
|
||||||
|
|
||||||
"@esbuild/android-arm64@0.27.1":
|
"@esbuild/android-arm64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz#31c00d864c80f6de1900a11de8a506dbfbb27349"
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz#61ea550962d8aa12a9b33194394e007657a6df57"
|
||||||
integrity sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==
|
integrity sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==
|
||||||
|
|
||||||
"@esbuild/android-arm@0.27.1":
|
"@esbuild/android-arm@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.1.tgz#d2b73ab0ba894923a1d1378fd4b15cc20985f436"
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.2.tgz#554887821e009dd6d853f972fde6c5143f1de142"
|
||||||
integrity sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==
|
integrity sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==
|
||||||
|
|
||||||
"@esbuild/android-x64@0.27.1":
|
"@esbuild/android-x64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.1.tgz#d9f74d8278191317250cfe0c15a13f410540b122"
|
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.2.tgz#a7ce9d0721825fc578f9292a76d9e53334480ba2"
|
||||||
integrity sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==
|
integrity sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==
|
||||||
|
|
||||||
"@esbuild/darwin-arm64@0.27.1":
|
"@esbuild/darwin-arm64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz#baf6914b8c57ed9d41f9de54023aa3ff9b084680"
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz#2cb7659bd5d109803c593cfc414450d5430c8256"
|
||||||
integrity sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==
|
integrity sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==
|
||||||
|
|
||||||
"@esbuild/darwin-x64@0.27.1":
|
"@esbuild/darwin-x64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz#64e37400795f780a76c858a118ff19681a64b4e0"
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz#e741fa6b1abb0cd0364126ba34ca17fd5e7bf509"
|
||||||
integrity sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==
|
integrity sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==
|
||||||
|
|
||||||
"@esbuild/freebsd-arm64@0.27.1":
|
"@esbuild/freebsd-arm64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz#6572f2f235933eee906e070dfaae54488ee60acd"
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz#2b64e7116865ca172d4ce034114c21f3c93e397c"
|
||||||
integrity sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==
|
integrity sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==
|
||||||
|
|
||||||
"@esbuild/freebsd-x64@0.27.1":
|
"@esbuild/freebsd-x64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz#83105dba9cf6ac4f44336799446d7f75c8c3a1e1"
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz#e5252551e66f499e4934efb611812f3820e990bb"
|
||||||
integrity sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==
|
integrity sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==
|
||||||
|
|
||||||
"@esbuild/linux-arm64@0.27.1":
|
"@esbuild/linux-arm64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz#035ff647d4498bdf16eb2d82801f73b366477dfa"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz#dc4acf235531cd6984f5d6c3b13dbfb7ddb303cb"
|
||||||
integrity sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==
|
integrity sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==
|
||||||
|
|
||||||
"@esbuild/linux-arm@0.27.1":
|
"@esbuild/linux-arm@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz#3516c74d2afbe305582dbb546d60f7978a8ece7f"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz#56a900e39240d7d5d1d273bc053daa295c92e322"
|
||||||
integrity sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==
|
integrity sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==
|
||||||
|
|
||||||
"@esbuild/linux-ia32@0.27.1":
|
"@esbuild/linux-ia32@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz#788db5db8ecd3d75dd41c42de0fe8f1fd967a4a7"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz#d4a36d473360f6870efcd19d52bbfff59a2ed1cc"
|
||||||
integrity sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==
|
integrity sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==
|
||||||
|
|
||||||
"@esbuild/linux-loong64@0.27.1":
|
"@esbuild/linux-loong64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz#8211f08b146916a6302ec2b8f87ec0cc4b62c49e"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz#fcf0ab8c3eaaf45891d0195d4961cb18b579716a"
|
||||||
integrity sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==
|
integrity sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==
|
||||||
|
|
||||||
"@esbuild/linux-mips64el@0.27.1":
|
"@esbuild/linux-mips64el@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz#cc58586ea83b3f171e727a624e7883a1c3eb4c04"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz#598b67d34048bb7ee1901cb12e2a0a434c381c10"
|
||||||
integrity sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==
|
integrity sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==
|
||||||
|
|
||||||
"@esbuild/linux-ppc64@0.27.1":
|
"@esbuild/linux-ppc64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz#632477bbd98175cf8e53a7c9952d17fb2d6d4115"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz#3846c5df6b2016dab9bc95dde26c40f11e43b4c0"
|
||||||
integrity sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==
|
integrity sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==
|
||||||
|
|
||||||
"@esbuild/linux-riscv64@0.27.1":
|
"@esbuild/linux-riscv64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz#35435a82435a8a750edf433b83ac0d10239ac3fe"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz#173d4475b37c8d2c3e1707e068c174bb3f53d07d"
|
||||||
integrity sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==
|
integrity sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==
|
||||||
|
|
||||||
"@esbuild/linux-s390x@0.27.1":
|
"@esbuild/linux-s390x@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz#172edd7086438edacd86c0e2ea25ac9dbb62aac5"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz#f7a4790105edcab8a5a31df26fbfac1aa3dacfab"
|
||||||
integrity sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==
|
integrity sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==
|
||||||
|
|
||||||
"@esbuild/linux-x64@0.27.1":
|
"@esbuild/linux-x64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz#09c771de9e2d8169d5969adf298ae21581f08c7f"
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz#2ecc1284b1904aeb41e54c9ddc7fcd349b18f650"
|
||||||
integrity sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==
|
integrity sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==
|
||||||
|
|
||||||
"@esbuild/netbsd-arm64@0.27.1":
|
"@esbuild/netbsd-arm64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz#475ac0ce7edf109a358b1669f67759de4bcbb7c4"
|
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz#e2863c2cd1501845995cb11adf26f7fe4be527b0"
|
||||||
integrity sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==
|
integrity sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==
|
||||||
|
|
||||||
"@esbuild/netbsd-x64@0.27.1":
|
"@esbuild/netbsd-x64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz#3c31603d592477dc43b63df1ae100000f7fb59d7"
|
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz#93f7609e2885d1c0b5a1417885fba8d1fcc41272"
|
||||||
integrity sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==
|
integrity sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==
|
||||||
|
|
||||||
"@esbuild/openbsd-arm64@0.27.1":
|
"@esbuild/openbsd-arm64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz#482067c847665b10d66431e936d4bc5fa8025abf"
|
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz#a1985604a203cdc325fd47542e106fafd698f02e"
|
||||||
integrity sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==
|
integrity sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==
|
||||||
|
|
||||||
"@esbuild/openbsd-x64@0.27.1":
|
"@esbuild/openbsd-x64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz#687a188c2b184e5b671c5f74a6cd6247c0718c52"
|
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz#8209e46c42f1ffbe6e4ef77a32e1f47d404ad42a"
|
||||||
integrity sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==
|
integrity sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==
|
||||||
|
|
||||||
"@esbuild/openharmony-arm64@0.27.1":
|
"@esbuild/openharmony-arm64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz#9929ee7fa8c1db2f33ef4d86198018dac9c1744f"
|
resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz#8fade4441893d9cc44cbd7dcf3776f508ab6fb2f"
|
||||||
integrity sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==
|
integrity sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==
|
||||||
|
|
||||||
"@esbuild/sunos-x64@0.27.1":
|
"@esbuild/sunos-x64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz#94071a146f313e7394c6424af07b2b564f1f994d"
|
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz#980d4b9703a16f0f07016632424fc6d9a789dfc2"
|
||||||
integrity sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==
|
integrity sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==
|
||||||
|
|
||||||
"@esbuild/win32-arm64@0.27.1":
|
"@esbuild/win32-arm64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz#869fde72a3576fdf48824085d05493fceebe395d"
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz#1c09a3633c949ead3d808ba37276883e71f6111a"
|
||||||
integrity sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==
|
integrity sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==
|
||||||
|
|
||||||
"@esbuild/win32-ia32@0.27.1":
|
"@esbuild/win32-ia32@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz#31d7585893ed7b54483d0b8d87a4bfeba0ecfff5"
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz#1b1e3a63ad4bef82200fef4e369e0fff7009eee5"
|
||||||
integrity sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==
|
integrity sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==
|
||||||
|
|
||||||
"@esbuild/win32-x64@0.27.1":
|
"@esbuild/win32-x64@0.27.2":
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz#5efe5a112938b1180e98c76685ff9185cfa4f16e"
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz#9e585ab6086bef994c6e8a5b3a0481219ada862b"
|
||||||
integrity sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==
|
integrity sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==
|
||||||
|
|
||||||
"@eslint-community/eslint-utils@^4.7.0":
|
|
||||||
version "4.7.0"
|
|
||||||
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz"
|
|
||||||
integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==
|
|
||||||
dependencies:
|
|
||||||
eslint-visitor-keys "^3.4.3"
|
|
||||||
|
|
||||||
"@eslint-community/eslint-utils@^4.8.0":
|
"@eslint-community/eslint-utils@^4.8.0":
|
||||||
version "4.9.0"
|
version "4.9.0"
|
||||||
@@ -168,22 +161,24 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
eslint-visitor-keys "^3.4.3"
|
eslint-visitor-keys "^3.4.3"
|
||||||
|
|
||||||
"@eslint-community/regexpp@^4.10.0":
|
"@eslint-community/eslint-utils@^4.9.1":
|
||||||
version "4.12.1"
|
version "4.9.1"
|
||||||
resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz"
|
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595"
|
||||||
integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
|
integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==
|
||||||
|
dependencies:
|
||||||
|
eslint-visitor-keys "^3.4.3"
|
||||||
|
|
||||||
"@eslint-community/regexpp@^4.12.1":
|
"@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.12.2":
|
||||||
version "4.12.2"
|
version "4.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b"
|
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b"
|
||||||
integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==
|
integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==
|
||||||
|
|
||||||
"@eslint/compat@^2.0.0":
|
"@eslint/compat@^2.0.1":
|
||||||
version "2.0.0"
|
version "2.0.1"
|
||||||
resolved "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.0.tgz"
|
resolved "https://registry.yarnpkg.com/@eslint/compat/-/compat-2.0.1.tgz#5894516f8ce9ba884f4d4ba5ecb6b6459b231144"
|
||||||
integrity sha512-T9AfE1G1uv4wwq94ozgTGio5EUQBqAVe1X9qsQtSNVEYW6j3hvtZVm8Smr4qL1qDPFg+lOB2cL5RxTRMzq4CTA==
|
integrity sha512-yl/JsgplclzuvGFNqwNYV4XNPhP3l62ZOP9w/47atNAdmDtIFCx6X7CSk/SlWUuBGkT4Et/5+UD+WyvX2iiIWA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint/core" "^1.0.0"
|
"@eslint/core" "^1.0.1"
|
||||||
|
|
||||||
"@eslint/config-array@^0.21.1":
|
"@eslint/config-array@^0.21.1":
|
||||||
version "0.21.1"
|
version "0.21.1"
|
||||||
@@ -208,10 +203,10 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/json-schema" "^7.0.15"
|
"@types/json-schema" "^7.0.15"
|
||||||
|
|
||||||
"@eslint/core@^1.0.0":
|
"@eslint/core@^1.0.1":
|
||||||
version "1.0.0"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/@eslint/core/-/core-1.0.0.tgz"
|
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-1.0.1.tgz#701ff760cbd279f9490bef0ce54095f4088d4def"
|
||||||
integrity sha512-PRfWP+8FOldvbApr6xL7mNCw4cJcSTq4GA7tYbgq15mRb0kWKO/wEB2jr+uwjFH3sZvEZneZyCUGTxsv4Sahyw==
|
integrity sha512-r18fEAj9uCk+VjzGt2thsbOmychS+4kxI14spVNibUO2vqKX7obOG+ymZljAwuPZl+S3clPGwCwTDtrdqTiY6Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/json-schema" "^7.0.15"
|
"@types/json-schema" "^7.0.15"
|
||||||
|
|
||||||
@@ -294,10 +289,10 @@
|
|||||||
react-compiler-runtime "19.1.0-rc.1"
|
react-compiler-runtime "19.1.0-rc.1"
|
||||||
zustand "^5"
|
zustand "^5"
|
||||||
|
|
||||||
"@graphiql/plugin-explorer@3.2.6":
|
"@graphiql/plugin-explorer@4.0.6":
|
||||||
version "3.2.6"
|
version "4.0.6"
|
||||||
resolved "https://registry.npmjs.org/@graphiql/plugin-explorer/-/plugin-explorer-3.2.6.tgz"
|
resolved "https://registry.yarnpkg.com/@graphiql/plugin-explorer/-/plugin-explorer-4.0.6.tgz#bec1207dc27334914590ab31f46c2e944bbf4ebf"
|
||||||
integrity sha512-MXzG/zVNzZfes4Em253bHyAbD/lwwAZkPKvxCAQkjz0i3dtcv4uF3D8iqJ7214iu3SCphbORYZZUC93fik1yew==
|
integrity sha512-TppIi92YPER3v70nlF01KTQrq9AiYqkZicSd1hpU7aqGmbqw/pLwBNLUEcfENBoJtw574Qxjswb01+GaYK0Tzw==
|
||||||
dependencies:
|
dependencies:
|
||||||
graphiql-explorer "^0.9.0"
|
graphiql-explorer "^0.9.0"
|
||||||
|
|
||||||
@@ -940,101 +935,100 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/estree" "*"
|
"@types/estree" "*"
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@^8.48.1":
|
"@typescript-eslint/eslint-plugin@^8.53.1":
|
||||||
version "8.48.1"
|
version "8.53.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.1.tgz#c772d1dbdd97cfddf85f5a161a97783233643631"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz#f6640f6f8749b71d9ab457263939e8932a3c6b46"
|
||||||
integrity sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==
|
integrity sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/regexpp" "^4.10.0"
|
"@eslint-community/regexpp" "^4.12.2"
|
||||||
"@typescript-eslint/scope-manager" "8.48.1"
|
"@typescript-eslint/scope-manager" "8.53.1"
|
||||||
"@typescript-eslint/type-utils" "8.48.1"
|
"@typescript-eslint/type-utils" "8.53.1"
|
||||||
"@typescript-eslint/utils" "8.48.1"
|
"@typescript-eslint/utils" "8.53.1"
|
||||||
"@typescript-eslint/visitor-keys" "8.48.1"
|
"@typescript-eslint/visitor-keys" "8.53.1"
|
||||||
graphemer "^1.4.0"
|
ignore "^7.0.5"
|
||||||
ignore "^7.0.0"
|
|
||||||
natural-compare "^1.4.0"
|
natural-compare "^1.4.0"
|
||||||
ts-api-utils "^2.1.0"
|
ts-api-utils "^2.4.0"
|
||||||
|
|
||||||
"@typescript-eslint/parser@^8.48.1":
|
"@typescript-eslint/parser@^8.53.1":
|
||||||
version "8.48.1"
|
version "8.53.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.48.1.tgz#4e3c66d9ec20683ec142417fafeadab61c479c3f"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.53.1.tgz#58d4a70cc2daee2becf7d4521d65ea1782d6ec68"
|
||||||
integrity sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==
|
integrity sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager" "8.48.1"
|
"@typescript-eslint/scope-manager" "8.53.1"
|
||||||
"@typescript-eslint/types" "8.48.1"
|
"@typescript-eslint/types" "8.53.1"
|
||||||
"@typescript-eslint/typescript-estree" "8.48.1"
|
"@typescript-eslint/typescript-estree" "8.53.1"
|
||||||
"@typescript-eslint/visitor-keys" "8.48.1"
|
"@typescript-eslint/visitor-keys" "8.53.1"
|
||||||
debug "^4.3.4"
|
debug "^4.4.3"
|
||||||
|
|
||||||
"@typescript-eslint/project-service@8.48.1":
|
"@typescript-eslint/project-service@8.53.1":
|
||||||
version "8.48.1"
|
version "8.53.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.48.1.tgz#cfe1741613b9112d85ae766de9e09b27a7d3f2f1"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.53.1.tgz#4e47856a0b14a1ceb28b0294b4badef3be1e9734"
|
||||||
integrity sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==
|
integrity sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/tsconfig-utils" "^8.48.1"
|
"@typescript-eslint/tsconfig-utils" "^8.53.1"
|
||||||
"@typescript-eslint/types" "^8.48.1"
|
"@typescript-eslint/types" "^8.53.1"
|
||||||
debug "^4.3.4"
|
debug "^4.4.3"
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@8.48.1":
|
"@typescript-eslint/scope-manager@8.53.1":
|
||||||
version "8.48.1"
|
version "8.53.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.48.1.tgz#8bc70643e7cca57864b1ff95dd350fc27756bec0"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz#6c4b8c82cd45ae3b365afc2373636e166743a8fa"
|
||||||
integrity sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==
|
integrity sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "8.48.1"
|
"@typescript-eslint/types" "8.53.1"
|
||||||
"@typescript-eslint/visitor-keys" "8.48.1"
|
"@typescript-eslint/visitor-keys" "8.53.1"
|
||||||
|
|
||||||
"@typescript-eslint/tsconfig-utils@8.48.1", "@typescript-eslint/tsconfig-utils@^8.48.1":
|
"@typescript-eslint/tsconfig-utils@8.53.1", "@typescript-eslint/tsconfig-utils@^8.53.1":
|
||||||
version "8.48.1"
|
version "8.53.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.1.tgz#68139ce2d258f984e2b33a95389158f1212af646"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz#efe80b8d019cd49e5a1cf46c2eb0cd2733076424"
|
||||||
integrity sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==
|
integrity sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==
|
||||||
|
|
||||||
"@typescript-eslint/type-utils@8.48.1":
|
"@typescript-eslint/type-utils@8.53.1":
|
||||||
version "8.48.1"
|
version "8.53.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.48.1.tgz#955bd3ddd648450f0a627925ff12ade63fb7516d"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.53.1.tgz#95de2651a96d580bf5c6c6089ddd694284d558ad"
|
||||||
integrity sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==
|
integrity sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "8.48.1"
|
"@typescript-eslint/types" "8.53.1"
|
||||||
"@typescript-eslint/typescript-estree" "8.48.1"
|
"@typescript-eslint/typescript-estree" "8.53.1"
|
||||||
"@typescript-eslint/utils" "8.48.1"
|
"@typescript-eslint/utils" "8.53.1"
|
||||||
debug "^4.3.4"
|
debug "^4.4.3"
|
||||||
ts-api-utils "^2.1.0"
|
ts-api-utils "^2.4.0"
|
||||||
|
|
||||||
"@typescript-eslint/types@8.48.1", "@typescript-eslint/types@^8.48.1":
|
"@typescript-eslint/types@8.53.1", "@typescript-eslint/types@^8.53.1":
|
||||||
version "8.48.1"
|
version "8.53.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.48.1.tgz#a9ff808f5f798f28767d5c0b015a88fa7ce46bd7"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.53.1.tgz#101f203f0807a63216cceceedb815fabe21d5793"
|
||||||
integrity sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==
|
integrity sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@8.48.1":
|
"@typescript-eslint/typescript-estree@8.53.1":
|
||||||
version "8.48.1"
|
version "8.53.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.1.tgz#0d0e31fc47c5796c6463ab50cde19e1718d465b1"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz#b6dce2303c9e27e95b8dcd8c325868fff53e488f"
|
||||||
integrity sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==
|
integrity sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/project-service" "8.48.1"
|
"@typescript-eslint/project-service" "8.53.1"
|
||||||
"@typescript-eslint/tsconfig-utils" "8.48.1"
|
"@typescript-eslint/tsconfig-utils" "8.53.1"
|
||||||
"@typescript-eslint/types" "8.48.1"
|
"@typescript-eslint/types" "8.53.1"
|
||||||
"@typescript-eslint/visitor-keys" "8.48.1"
|
"@typescript-eslint/visitor-keys" "8.53.1"
|
||||||
debug "^4.3.4"
|
debug "^4.4.3"
|
||||||
minimatch "^9.0.4"
|
minimatch "^9.0.5"
|
||||||
semver "^7.6.0"
|
semver "^7.7.3"
|
||||||
tinyglobby "^0.2.15"
|
tinyglobby "^0.2.15"
|
||||||
ts-api-utils "^2.1.0"
|
ts-api-utils "^2.4.0"
|
||||||
|
|
||||||
"@typescript-eslint/utils@8.48.1":
|
"@typescript-eslint/utils@8.53.1":
|
||||||
version "8.48.1"
|
version "8.53.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.48.1.tgz#6cf7b99e0943b33a983ef687b9a86b65578b5c32"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.53.1.tgz#81fe6c343de288701b774f4d078382f567e6edaa"
|
||||||
integrity sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==
|
integrity sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils" "^4.7.0"
|
"@eslint-community/eslint-utils" "^4.9.1"
|
||||||
"@typescript-eslint/scope-manager" "8.48.1"
|
"@typescript-eslint/scope-manager" "8.53.1"
|
||||||
"@typescript-eslint/types" "8.48.1"
|
"@typescript-eslint/types" "8.53.1"
|
||||||
"@typescript-eslint/typescript-estree" "8.48.1"
|
"@typescript-eslint/typescript-estree" "8.53.1"
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@8.48.1":
|
"@typescript-eslint/visitor-keys@8.53.1":
|
||||||
version "8.48.1"
|
version "8.53.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.1.tgz#247d4fe6dcc044f45b7f1c15110bf95e5d73b334"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz#405f04959be22b9be364939af8ac19c3649b6eb7"
|
||||||
integrity sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==
|
integrity sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "8.48.1"
|
"@typescript-eslint/types" "8.53.1"
|
||||||
eslint-visitor-keys "^4.2.1"
|
eslint-visitor-keys "^4.2.1"
|
||||||
|
|
||||||
"@unrs/resolver-binding-android-arm-eabi@1.11.1":
|
"@unrs/resolver-binding-android-arm-eabi@1.11.1":
|
||||||
@@ -1161,14 +1155,6 @@ ansi-styles@^4.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
color-convert "^2.0.1"
|
color-convert "^2.0.1"
|
||||||
|
|
||||||
anymatch@~3.1.2:
|
|
||||||
version "3.1.3"
|
|
||||||
resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz"
|
|
||||||
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
|
|
||||||
dependencies:
|
|
||||||
normalize-path "^3.0.0"
|
|
||||||
picomatch "^2.0.4"
|
|
||||||
|
|
||||||
argparse@^2.0.1:
|
argparse@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
|
resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
|
||||||
@@ -1288,11 +1274,6 @@ balanced-match@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
|
||||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
binary-extensions@^2.0.0:
|
|
||||||
version "2.3.0"
|
|
||||||
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz"
|
|
||||||
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
|
|
||||||
|
|
||||||
bootstrap@5.3.7:
|
bootstrap@5.3.7:
|
||||||
version "5.3.7"
|
version "5.3.7"
|
||||||
resolved "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz"
|
resolved "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz"
|
||||||
@@ -1318,7 +1299,7 @@ brace-expansion@^2.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
balanced-match "^1.0.0"
|
balanced-match "^1.0.0"
|
||||||
|
|
||||||
braces@^3.0.3, braces@~3.0.2:
|
braces@^3.0.3:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz"
|
resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz"
|
||||||
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
|
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
|
||||||
@@ -1375,21 +1356,6 @@ chalk@^4.0.0:
|
|||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
"chokidar@>=3.0.0 <4.0.0":
|
|
||||||
version "3.6.0"
|
|
||||||
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
|
|
||||||
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
|
|
||||||
dependencies:
|
|
||||||
anymatch "~3.1.2"
|
|
||||||
braces "~3.0.2"
|
|
||||||
glob-parent "~5.1.2"
|
|
||||||
is-binary-path "~2.1.0"
|
|
||||||
is-glob "~4.0.1"
|
|
||||||
normalize-path "~3.0.0"
|
|
||||||
readdirp "~3.6.0"
|
|
||||||
optionalDependencies:
|
|
||||||
fsevents "~2.3.2"
|
|
||||||
|
|
||||||
chokidar@^4.0.0:
|
chokidar@^4.0.0:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz"
|
resolved "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz"
|
||||||
@@ -1533,20 +1499,13 @@ debug@^3.2.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
debug@^4.3.1, debug@^4.3.2, debug@^4.4.1:
|
debug@^4.3.1, debug@^4.3.2, debug@^4.4.1, debug@^4.4.3:
|
||||||
version "4.4.3"
|
version "4.4.3"
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz"
|
resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz"
|
||||||
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
|
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.3"
|
ms "^2.1.3"
|
||||||
|
|
||||||
debug@^4.3.4:
|
|
||||||
version "4.4.1"
|
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz"
|
|
||||||
integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==
|
|
||||||
dependencies:
|
|
||||||
ms "^2.1.3"
|
|
||||||
|
|
||||||
decode-uri-component@^0.4.1:
|
decode-uri-component@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz"
|
resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz"
|
||||||
@@ -1805,46 +1764,45 @@ es-to-primitive@^1.3.0:
|
|||||||
is-date-object "^1.0.5"
|
is-date-object "^1.0.5"
|
||||||
is-symbol "^1.0.4"
|
is-symbol "^1.0.4"
|
||||||
|
|
||||||
esbuild-sass-plugin@^3.3.1:
|
esbuild-sass-plugin@^3.6.0:
|
||||||
version "3.3.1"
|
version "3.6.0"
|
||||||
resolved "https://registry.npmjs.org/esbuild-sass-plugin/-/esbuild-sass-plugin-3.3.1.tgz"
|
resolved "https://registry.yarnpkg.com/esbuild-sass-plugin/-/esbuild-sass-plugin-3.6.0.tgz#6e93d0aec87b6ab7bde2e459c5f1ab472088bd41"
|
||||||
integrity sha512-SnO1ls+d52n6j8gRRpjexXI8MsHEaumS0IdDHaYM29Y6gakzZYMls6i9ql9+AWMSQk/eryndmUpXEgT34QrX1A==
|
integrity sha512-lzPJQSEXcnj5amBPPib5lBjsDNPzvdMnX+1Rf7eha9BIpLSM5Ad2pi+Rqg5CAlWMduCgLntS2hLAqG7v1fxWGw==
|
||||||
dependencies:
|
dependencies:
|
||||||
resolve "^1.22.8"
|
resolve "^1.22.11"
|
||||||
safe-identifier "^0.4.2"
|
sass "^1.97.2"
|
||||||
sass "^1.71.1"
|
|
||||||
|
|
||||||
esbuild@^0.27.1:
|
esbuild@^0.27.2:
|
||||||
version "0.27.1"
|
version "0.27.2"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.1.tgz#56bf43e6a4b4d2004642ec7c091b78de02b0831a"
|
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.2.tgz#d83ed2154d5813a5367376bb2292a9296fc83717"
|
||||||
integrity sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==
|
integrity sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@esbuild/aix-ppc64" "0.27.1"
|
"@esbuild/aix-ppc64" "0.27.2"
|
||||||
"@esbuild/android-arm" "0.27.1"
|
"@esbuild/android-arm" "0.27.2"
|
||||||
"@esbuild/android-arm64" "0.27.1"
|
"@esbuild/android-arm64" "0.27.2"
|
||||||
"@esbuild/android-x64" "0.27.1"
|
"@esbuild/android-x64" "0.27.2"
|
||||||
"@esbuild/darwin-arm64" "0.27.1"
|
"@esbuild/darwin-arm64" "0.27.2"
|
||||||
"@esbuild/darwin-x64" "0.27.1"
|
"@esbuild/darwin-x64" "0.27.2"
|
||||||
"@esbuild/freebsd-arm64" "0.27.1"
|
"@esbuild/freebsd-arm64" "0.27.2"
|
||||||
"@esbuild/freebsd-x64" "0.27.1"
|
"@esbuild/freebsd-x64" "0.27.2"
|
||||||
"@esbuild/linux-arm" "0.27.1"
|
"@esbuild/linux-arm" "0.27.2"
|
||||||
"@esbuild/linux-arm64" "0.27.1"
|
"@esbuild/linux-arm64" "0.27.2"
|
||||||
"@esbuild/linux-ia32" "0.27.1"
|
"@esbuild/linux-ia32" "0.27.2"
|
||||||
"@esbuild/linux-loong64" "0.27.1"
|
"@esbuild/linux-loong64" "0.27.2"
|
||||||
"@esbuild/linux-mips64el" "0.27.1"
|
"@esbuild/linux-mips64el" "0.27.2"
|
||||||
"@esbuild/linux-ppc64" "0.27.1"
|
"@esbuild/linux-ppc64" "0.27.2"
|
||||||
"@esbuild/linux-riscv64" "0.27.1"
|
"@esbuild/linux-riscv64" "0.27.2"
|
||||||
"@esbuild/linux-s390x" "0.27.1"
|
"@esbuild/linux-s390x" "0.27.2"
|
||||||
"@esbuild/linux-x64" "0.27.1"
|
"@esbuild/linux-x64" "0.27.2"
|
||||||
"@esbuild/netbsd-arm64" "0.27.1"
|
"@esbuild/netbsd-arm64" "0.27.2"
|
||||||
"@esbuild/netbsd-x64" "0.27.1"
|
"@esbuild/netbsd-x64" "0.27.2"
|
||||||
"@esbuild/openbsd-arm64" "0.27.1"
|
"@esbuild/openbsd-arm64" "0.27.2"
|
||||||
"@esbuild/openbsd-x64" "0.27.1"
|
"@esbuild/openbsd-x64" "0.27.2"
|
||||||
"@esbuild/openharmony-arm64" "0.27.1"
|
"@esbuild/openharmony-arm64" "0.27.2"
|
||||||
"@esbuild/sunos-x64" "0.27.1"
|
"@esbuild/sunos-x64" "0.27.2"
|
||||||
"@esbuild/win32-arm64" "0.27.1"
|
"@esbuild/win32-arm64" "0.27.2"
|
||||||
"@esbuild/win32-ia32" "0.27.1"
|
"@esbuild/win32-ia32" "0.27.2"
|
||||||
"@esbuild/win32-x64" "0.27.1"
|
"@esbuild/win32-x64" "0.27.2"
|
||||||
|
|
||||||
escape-string-regexp@^4.0.0:
|
escape-string-regexp@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
@@ -1918,13 +1876,13 @@ eslint-plugin-import@^2.32.0:
|
|||||||
string.prototype.trimend "^1.0.9"
|
string.prototype.trimend "^1.0.9"
|
||||||
tsconfig-paths "^3.15.0"
|
tsconfig-paths "^3.15.0"
|
||||||
|
|
||||||
eslint-plugin-prettier@^5.5.1:
|
eslint-plugin-prettier@^5.5.5:
|
||||||
version "5.5.4"
|
version "5.5.5"
|
||||||
resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz#9eae11593faa108859c26f9a9c367d619a0769c0"
|
||||||
integrity sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==
|
integrity sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==
|
||||||
dependencies:
|
dependencies:
|
||||||
prettier-linter-helpers "^1.0.0"
|
prettier-linter-helpers "^1.0.1"
|
||||||
synckit "^0.11.7"
|
synckit "^0.11.12"
|
||||||
|
|
||||||
eslint-scope@^8.4.0:
|
eslint-scope@^8.4.0:
|
||||||
version "8.4.0"
|
version "8.4.0"
|
||||||
@@ -2110,11 +2068,6 @@ framer-motion@^12:
|
|||||||
motion-utils "^12.23.6"
|
motion-utils "^12.23.6"
|
||||||
tslib "^2.4.0"
|
tslib "^2.4.0"
|
||||||
|
|
||||||
fsevents@~2.3.2:
|
|
||||||
version "2.3.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
|
||||||
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
|
||||||
|
|
||||||
function-bind@^1.1.2:
|
function-bind@^1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
|
||||||
@@ -2226,22 +2179,15 @@ glob-parent@^6.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.3"
|
is-glob "^4.0.3"
|
||||||
|
|
||||||
glob-parent@~5.1.2:
|
|
||||||
version "5.1.2"
|
|
||||||
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
|
|
||||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
|
||||||
dependencies:
|
|
||||||
is-glob "^4.0.1"
|
|
||||||
|
|
||||||
globals@^14.0.0:
|
globals@^14.0.0:
|
||||||
version "14.0.0"
|
version "14.0.0"
|
||||||
resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz"
|
resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz"
|
||||||
integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
|
integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
|
||||||
|
|
||||||
globals@^16.5.0:
|
globals@^17.0.0:
|
||||||
version "16.5.0"
|
version "17.0.0"
|
||||||
resolved "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz"
|
resolved "https://registry.yarnpkg.com/globals/-/globals-17.0.0.tgz#a4196d9cfeb4d627ba165b4647b1f5853bf90a30"
|
||||||
integrity sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==
|
integrity sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==
|
||||||
|
|
||||||
globalthis@^1.0.3, globalthis@^1.0.4:
|
globalthis@^1.0.3, globalthis@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
@@ -2270,11 +2216,6 @@ gopd@^1.2.0:
|
|||||||
resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz"
|
||||||
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
|
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
|
||||||
|
|
||||||
graphemer@^1.4.0:
|
|
||||||
version "1.4.0"
|
|
||||||
resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz"
|
|
||||||
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
|
|
||||||
|
|
||||||
graphiql-explorer@^0.9.0:
|
graphiql-explorer@^0.9.0:
|
||||||
version "0.9.0"
|
version "0.9.0"
|
||||||
resolved "https://registry.npmjs.org/graphiql-explorer/-/graphiql-explorer-0.9.0.tgz"
|
resolved "https://registry.npmjs.org/graphiql-explorer/-/graphiql-explorer-0.9.0.tgz"
|
||||||
@@ -2372,16 +2313,11 @@ ignore@^5.2.0:
|
|||||||
resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz"
|
resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz"
|
||||||
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
|
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
|
||||||
|
|
||||||
ignore@^7.0.0:
|
ignore@^7.0.5:
|
||||||
version "7.0.5"
|
version "7.0.5"
|
||||||
resolved "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9"
|
||||||
integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==
|
integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==
|
||||||
|
|
||||||
immutable@^4.0.0:
|
|
||||||
version "4.3.7"
|
|
||||||
resolved "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz"
|
|
||||||
integrity sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==
|
|
||||||
|
|
||||||
immutable@^5.0.2:
|
immutable@^5.0.2:
|
||||||
version "5.0.3"
|
version "5.0.3"
|
||||||
resolved "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz"
|
resolved "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz"
|
||||||
@@ -2460,13 +2396,6 @@ is-bigint@^1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-bigints "^1.0.2"
|
has-bigints "^1.0.2"
|
||||||
|
|
||||||
is-binary-path@~2.1.0:
|
|
||||||
version "2.1.0"
|
|
||||||
resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz"
|
|
||||||
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
|
|
||||||
dependencies:
|
|
||||||
binary-extensions "^2.0.0"
|
|
||||||
|
|
||||||
is-boolean-object@^1.1.0:
|
is-boolean-object@^1.1.0:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz"
|
||||||
@@ -2562,7 +2491,7 @@ is-generator-function@^1.0.10:
|
|||||||
has-tostringtag "^1.0.2"
|
has-tostringtag "^1.0.2"
|
||||||
safe-regex-test "^1.1.0"
|
safe-regex-test "^1.1.0"
|
||||||
|
|
||||||
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
|
is-glob@^4.0.0, is-glob@^4.0.3:
|
||||||
version "4.0.3"
|
version "4.0.3"
|
||||||
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
|
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
|
||||||
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
|
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
|
||||||
@@ -2857,7 +2786,7 @@ minimatch@^3.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimatch@^9.0.4:
|
minimatch@^9.0.5:
|
||||||
version "9.0.5"
|
version "9.0.5"
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz"
|
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz"
|
||||||
integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
|
integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
|
||||||
@@ -2901,11 +2830,6 @@ node-addon-api@^7.0.0:
|
|||||||
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz"
|
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz"
|
||||||
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
||||||
|
|
||||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
|
|
||||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
|
||||||
|
|
||||||
nullthrows@^1.0.0:
|
nullthrows@^1.0.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz"
|
||||||
@@ -3034,7 +2958,7 @@ path-parse@^1.0.7:
|
|||||||
resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
|
resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
|
||||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||||
|
|
||||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
|
picomatch@^2.3.1:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
|
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
|
||||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||||
@@ -3054,17 +2978,17 @@ prelude-ls@^1.2.1:
|
|||||||
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
|
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
|
||||||
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
||||||
|
|
||||||
prettier-linter-helpers@^1.0.0:
|
prettier-linter-helpers@^1.0.1:
|
||||||
version "1.0.0"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz"
|
resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz#6a31f88a4bad6c7adda253de12ba4edaea80ebcd"
|
||||||
integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
|
integrity sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-diff "^1.1.2"
|
fast-diff "^1.1.2"
|
||||||
|
|
||||||
prettier@^3.7.4:
|
prettier@^3.8.0:
|
||||||
version "3.7.4"
|
version "3.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.0.tgz#f72cf71505133f40cfa2ef77a2668cdc558fcd69"
|
||||||
integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==
|
integrity sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==
|
||||||
|
|
||||||
punycode.js@^2.3.1:
|
punycode.js@^2.3.1:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
@@ -3137,13 +3061,6 @@ readdirp@^4.0.1:
|
|||||||
resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz"
|
resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz"
|
||||||
integrity sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==
|
integrity sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==
|
||||||
|
|
||||||
readdirp@~3.6.0:
|
|
||||||
version "3.6.0"
|
|
||||||
resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz"
|
|
||||||
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
|
|
||||||
dependencies:
|
|
||||||
picomatch "^2.2.1"
|
|
||||||
|
|
||||||
reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9:
|
reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9:
|
||||||
version "1.0.10"
|
version "1.0.10"
|
||||||
resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz"
|
resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz"
|
||||||
@@ -3190,7 +3107,16 @@ resolve-pkg-maps@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz"
|
||||||
integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==
|
integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==
|
||||||
|
|
||||||
resolve@^1.22.4, resolve@^1.22.8:
|
resolve@^1.22.11:
|
||||||
|
version "1.22.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262"
|
||||||
|
integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==
|
||||||
|
dependencies:
|
||||||
|
is-core-module "^2.16.1"
|
||||||
|
path-parse "^1.0.7"
|
||||||
|
supports-preserve-symlinks-flag "^1.0.0"
|
||||||
|
|
||||||
|
resolve@^1.22.4:
|
||||||
version "1.22.8"
|
version "1.22.8"
|
||||||
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz"
|
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz"
|
||||||
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
|
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
|
||||||
@@ -3220,11 +3146,6 @@ safe-array-concat@^1.1.3:
|
|||||||
has-symbols "^1.1.0"
|
has-symbols "^1.1.0"
|
||||||
isarray "^2.0.5"
|
isarray "^2.0.5"
|
||||||
|
|
||||||
safe-identifier@^0.4.2:
|
|
||||||
version "0.4.2"
|
|
||||||
resolved "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz"
|
|
||||||
integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==
|
|
||||||
|
|
||||||
safe-push-apply@^1.0.0:
|
safe-push-apply@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz"
|
||||||
@@ -3251,7 +3172,7 @@ safe-regex-test@^1.1.0:
|
|||||||
es-errors "^1.3.0"
|
es-errors "^1.3.0"
|
||||||
is-regex "^1.2.1"
|
is-regex "^1.2.1"
|
||||||
|
|
||||||
sass@1.97.2:
|
sass@1.97.2, sass@^1.97.2:
|
||||||
version "1.97.2"
|
version "1.97.2"
|
||||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.97.2.tgz#e515a319092fd2c3b015228e3094b40198bff0da"
|
resolved "https://registry.yarnpkg.com/sass/-/sass-1.97.2.tgz#e515a319092fd2c3b015228e3094b40198bff0da"
|
||||||
integrity sha512-y5LWb0IlbO4e97Zr7c3mlpabcbBtS+ieiZ9iwDooShpFKWXf62zz5pEPdwrLYm+Bxn1fnbwFGzHuCLSA9tBmrw==
|
integrity sha512-y5LWb0IlbO4e97Zr7c3mlpabcbBtS+ieiZ9iwDooShpFKWXf62zz5pEPdwrLYm+Bxn1fnbwFGzHuCLSA9tBmrw==
|
||||||
@@ -3262,15 +3183,6 @@ sass@1.97.2:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@parcel/watcher" "^2.4.1"
|
"@parcel/watcher" "^2.4.1"
|
||||||
|
|
||||||
sass@^1.71.1:
|
|
||||||
version "1.77.8"
|
|
||||||
resolved "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz"
|
|
||||||
integrity sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==
|
|
||||||
dependencies:
|
|
||||||
chokidar ">=3.0.0 <4.0.0"
|
|
||||||
immutable "^4.0.0"
|
|
||||||
source-map-js ">=0.6.2 <2.0.0"
|
|
||||||
|
|
||||||
scheduler@^0.23.2:
|
scheduler@^0.23.2:
|
||||||
version "0.23.2"
|
version "0.23.2"
|
||||||
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz"
|
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz"
|
||||||
@@ -3288,12 +3200,7 @@ semver@^6.3.1:
|
|||||||
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
|
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
|
||||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||||
|
|
||||||
semver@^7.6.0:
|
semver@^7.7.1, semver@^7.7.3:
|
||||||
version "7.7.2"
|
|
||||||
resolved "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz"
|
|
||||||
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
|
|
||||||
|
|
||||||
semver@^7.7.1:
|
|
||||||
version "7.7.3"
|
version "7.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
|
||||||
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
|
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
|
||||||
@@ -3495,10 +3402,10 @@ supports-preserve-symlinks-flag@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
|
||||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||||
|
|
||||||
synckit@^0.11.7:
|
synckit@^0.11.12:
|
||||||
version "0.11.11"
|
version "0.11.12"
|
||||||
resolved "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz"
|
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.12.tgz#abe74124264fbc00a48011b0d98bdc1cffb64a7b"
|
||||||
integrity sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==
|
integrity sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@pkgr/core" "^0.2.9"
|
"@pkgr/core" "^0.2.9"
|
||||||
|
|
||||||
@@ -3540,10 +3447,10 @@ tom-select@2.4.3:
|
|||||||
"@orchidjs/sifter" "^1.1.0"
|
"@orchidjs/sifter" "^1.1.0"
|
||||||
"@orchidjs/unicode-variants" "^1.1.2"
|
"@orchidjs/unicode-variants" "^1.1.2"
|
||||||
|
|
||||||
ts-api-utils@^2.1.0:
|
ts-api-utils@^2.4.0:
|
||||||
version "2.1.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz"
|
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.4.0.tgz#2690579f96d2790253bdcf1ca35d569ad78f9ad8"
|
||||||
integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==
|
integrity sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==
|
||||||
|
|
||||||
tsconfig-paths@^3.15.0:
|
tsconfig-paths@^3.15.0:
|
||||||
version "3.15.0"
|
version "3.15.0"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version: "4.5.0"
|
version: "4.5.1"
|
||||||
edition: "Community"
|
edition: "Community"
|
||||||
published: "2026-01-06"
|
published: "2026-01-20"
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
{% block extra_controls %}
|
{% block extra_controls %}
|
||||||
{% include 'ipam/inc/toggle_available.html' %}
|
{% include 'ipam/inc/toggle_available.html' %}
|
||||||
|
{% include 'ipam/inc/max_depth.html' %}
|
||||||
|
{% include 'ipam/inc/max_length.html' %}
|
||||||
{% if perms.ipam.add_prefix and first_available_prefix %}
|
{% if perms.ipam.add_prefix and first_available_prefix %}
|
||||||
<a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}" class="btn btn-primary">
|
<a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}" class="btn btn-primary">
|
||||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Prefix" %}
|
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Prefix" %}
|
||||||
|
|||||||
20
netbox/templates/ipam/inc/max_depth.html
Normal file
20
netbox/templates/ipam/inc/max_depth.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load helpers %}
|
||||||
|
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="max_depth" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||||
|
{% trans "Max Depth" %}{% if "depth__lte" in request.GET %}: {{ request.GET.depth__lte }}{% endif %}
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="max_depth">
|
||||||
|
{% if request.GET.depth__lte %}
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="{{ request.path }}{% querystring request depth__lte=None page=1 %}">{% trans "Clear" %}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% for i in 16|as_range %}
|
||||||
|
<li><a class="dropdown-item" href="{{ request.path }}{% querystring request depth__lte=i page=1 %}">
|
||||||
|
{{ i }} {% if request.GET.depth__lte == i %}<i class="mdi mdi-check-bold"></i>{% endif %}
|
||||||
|
</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
20
netbox/templates/ipam/inc/max_length.html
Normal file
20
netbox/templates/ipam/inc/max_length.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load helpers %}
|
||||||
|
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="max_length" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||||
|
{% trans "Max Length" %}{% if "mask_length__lte" in request.GET %}: {{ request.GET.mask_length__lte }}{% endif %}
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="max_length">
|
||||||
|
{% if request.GET.mask_length__lte %}
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="{{ request.path }}{% querystring request mask_length__lte=None page=1 %}">{% trans "Clear" %}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% for i in "4,8,12,16,20,24,28,32,40,48,56,64"|split %}
|
||||||
|
<li><a class="dropdown-item" href="{{ request.path }}{% querystring request mask_length__lte=i page=1 %}">
|
||||||
|
{{ i }} {% if request.GET.mask_length__lte == i %}<i class="mdi mdi-check-bold"></i>{% endif %}
|
||||||
|
</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
{% block extra_controls %}
|
{% block extra_controls %}
|
||||||
{% include 'ipam/inc/toggle_available.html' %}
|
{% include 'ipam/inc/toggle_available.html' %}
|
||||||
|
{% include 'ipam/inc/max_depth.html' %}
|
||||||
|
{% include 'ipam/inc/max_length.html' %}
|
||||||
{% if perms.ipam.add_prefix and first_available_prefix %}
|
{% if perms.ipam.add_prefix and first_available_prefix %}
|
||||||
<a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}&vrf={{ object.vrf.pk }}&site={{ object.site.pk }}&tenant_group={{ object.tenant.group.pk }}&tenant={{ object.tenant.pk }}" class="btn btn-primary">
|
<a href="{% url 'ipam:prefix_add' %}?prefix={{ first_available_prefix }}&vrf={{ object.vrf.pk }}&site={{ object.site.pk }}&tenant_group={{ object.tenant.group.pk }}&tenant={{ object.tenant.pk }}" class="btn btn-primary">
|
||||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Prefix" %}
|
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Prefix" %}
|
||||||
|
|||||||
@@ -6,38 +6,6 @@
|
|||||||
<button class="btn btn-outline-secondary toggle-depth" type="button">
|
<button class="btn btn-outline-secondary toggle-depth" type="button">
|
||||||
{% trans "Hide Depth Indicators" %}
|
{% trans "Hide Depth Indicators" %}
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown">
|
{% include 'ipam/inc/max_depth.html' %}
|
||||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="max_depth" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
{% include 'ipam/inc/max_length.html' %}
|
||||||
{% trans "Max Depth" %}{% if "depth__lte" in request.GET %}: {{ request.GET.depth__lte }}{% endif %}
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="max_depth">
|
|
||||||
{% if request.GET.depth__lte %}
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item" href="{% url 'ipam:prefix_list' %}{% querystring request depth__lte=None page=1 %}">{% trans "Clear" %}</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% for i in 16|as_range %}
|
|
||||||
<li><a class="dropdown-item" href="{% url 'ipam:prefix_list' %}{% querystring request depth__lte=i page=1 %}">
|
|
||||||
{{ i }} {% if request.GET.depth__lte == i %}<i class="mdi mdi-check-bold"></i>{% endif %}
|
|
||||||
</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="max_length" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
|
||||||
{% trans "Max Length" %}{% if "mask_length__lte" in request.GET %}: {{ request.GET.mask_length__lte }}{% endif %}
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="max_length">
|
|
||||||
{% if request.GET.mask_length__lte %}
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item" href="{% url 'ipam:prefix_list' %}{% querystring request mask_length__lte=None page=1 %}">{% trans "Clear" %}</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% for i in "4,8,12,16,20,24,28,32,40,48,56,64"|split %}
|
|
||||||
<li><a class="dropdown-item" href="{% url 'ipam:prefix_list' %}{% querystring request mask_length__lte=i page=1 %}">
|
|
||||||
{{ i }} {% if request.GET.mask_length__lte == i %}<i class="mdi mdi-check-bold"></i>{% endif %}
|
|
||||||
</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -3,9 +3,10 @@ import string
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
|
|
||||||
OBJECTPERMISSION_OBJECT_TYPES = Q(
|
OBJECTPERMISSION_OBJECT_TYPES = (
|
||||||
~Q(app_label__in=['account', 'admin', 'auth', 'contenttypes', 'sessions', 'taggit', 'users']) |
|
(Q(public=True) & ~Q(app_label='core', model='objecttype'))
|
||||||
Q(app_label='users', model__in=['objectpermission', 'token', 'group', 'user', 'owner'])
|
| Q(app_label='core', model__in=['managedfile'])
|
||||||
|
| Q(app_label='extras', model__in=['scriptmodule', 'taggeditem'])
|
||||||
)
|
)
|
||||||
|
|
||||||
CONSTRAINT_TOKEN_USER = '$user'
|
CONSTRAINT_TOKEN_USER = '$user'
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ class UserTokenForm(forms.ModelForm):
|
|||||||
token = forms.CharField(
|
token = forms.CharField(
|
||||||
label=_('Token'),
|
label=_('Token'),
|
||||||
help_text=_(
|
help_text=_(
|
||||||
'Tokens must be at least 40 characters in length. <strong>Be sure to record your key</strong> prior to '
|
'Tokens must be at least 40 characters in length. <strong>Be sure to record your token</strong> prior to '
|
||||||
'submitting this form, as it will no longer be accessible once the token has been created.'
|
'submitting this form, as it will no longer be accessible once the token has been created.'
|
||||||
),
|
),
|
||||||
widget=forms.TextInput(
|
widget=forms.TextInput(
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class Token(models.Model):
|
|||||||
write_enabled = models.BooleanField(
|
write_enabled = models.BooleanField(
|
||||||
verbose_name=_('write enabled'),
|
verbose_name=_('write enabled'),
|
||||||
default=True,
|
default=True,
|
||||||
help_text=_('Permit create/update/delete operations using this key')
|
help_text=_('Permit create/update/delete operations using this token')
|
||||||
)
|
)
|
||||||
# For legacy v1 tokens, this field stores the plaintext 40-char token value. Not used for v2.
|
# For legacy v1 tokens, this field stores the plaintext 40-char token value. Not used for v2.
|
||||||
plaintext = models.CharField(
|
plaintext = models.CharField(
|
||||||
@@ -213,6 +213,9 @@ class Token(models.Model):
|
|||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
|
|
||||||
|
if self.version == TokenVersionChoices.V2 and not settings.API_TOKEN_PEPPERS:
|
||||||
|
raise ValidationError(_("Unable to save v2 tokens: API_TOKEN_PEPPERS is not defined."))
|
||||||
|
|
||||||
if self._state.adding:
|
if self._state.adding:
|
||||||
if self.pepper_id is not None and self.pepper_id not in settings.API_TOKEN_PEPPERS:
|
if self.pepper_id is not None and self.pepper_id not in settings.API_TOKEN_PEPPERS:
|
||||||
raise ValidationError(_(
|
raise ValidationError(_(
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user