Compare commits

..

2 Commits

Author SHA1 Message Date
briquet bae6affcba 🔖(minor) bump release to 1.36.2 2026-10-21 21:59:23 +02:00
briquet 708f0d332e 🔒️(frontend) upgrade Node to 22.23.4 and 24.22.1
Pin Node images to the 2026-10-20 security releases
2026-10-21 21:58:23 +02:00
25 changed files with 82 additions and 401 deletions
+1 -1
View File
@@ -215,7 +215,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version: "24"
node-version: "24.22.1"
- name: Install dependencies
run: cd src/frontend/ && npm ci --ignore-scripts
+3 -3
View File
@@ -8,14 +8,14 @@ and this project adheres to
## [Unreleased]
## [1.36.2] - 2026-10-21
### Fixed
- 🐛(backend) return RFC 6749 errors from the application token endpoint
- 🔒️(frontend) upgrade Node to 22.23.4 and 24.22.1 security releases
## [1.36.1] - 2026-10-21
### Added
### Changed
- 🔖(helm) release chart 0.0.28
+1 -1
View File
@@ -39,7 +39,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --no-dev
# ---- mails ----
FROM node:22-alpine AS mail-builder
FROM node:22.23.4-alpine AS mail-builder
RUN apk upgrade --no-cache ca-certificates
+1 -1
View File
@@ -175,7 +175,7 @@ services:
working_dir: /app
node:
image: node:22-alpine
image: node:22.23.4-alpine
user: "${DOCKER_USER:-1000}"
environment:
HOME: /tmp
+1 -1
View File
@@ -1,5 +1,5 @@
# ---- Front-end image ----
FROM node:22-alpine AS frontend-deps
FROM node:22.23.4-alpine AS frontend-deps
RUN apk upgrade --no-cache ca-certificates
+19 -87
View File
@@ -94,38 +94,8 @@ paths:
token_type: "Bearer"
expires_in: 3600
scope: "rooms:list rooms:retrieve rooms:create"
'400':
description: |
Invalid request. When several parameters are invalid, `error` reports
the most relevant one (client credentials first, then grant type) and
`error_description` lists every invalid parameter.
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthError'
examples:
missingClientCredentials:
summary: Missing client credentials
value:
error: "invalid_client"
error_description: "client_id : This field is required.; client_secret : This field is required."
unsupportedGrantType:
summary: Unsupported grant type
value:
error: "unsupported_grant_type"
error_description: "grant_type : 'authorization_code' is not a valid choice."
missingParameter:
summary: Missing required parameter
value:
error: "invalid_request"
error_description: "grant_type : This field is required."
invalidScope:
summary: Scope is not a valid email address
value:
error: "invalid_scope"
error_description: "Scope should be a valid email address."
'401':
description: Client authentication failed
description: Authentication failed
content:
application/json:
schema:
@@ -134,13 +104,22 @@ paths:
invalidCredentials:
summary: Invalid credentials
value:
error: "invalid_client"
error_description: "Invalid credentials"
error: "Invalid credentials"
inactiveApplication:
summary: Application is inactive
value:
error: "invalid_client"
error_description: "Application is inactive"
error: "Application is inactive"
'400':
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthError'
examples:
userNotFound:
summary: User not found
value:
error: "User not found."
'403':
description: Access denied - cannot delegate user
content:
@@ -151,36 +130,7 @@ paths:
delegationDenied:
summary: Domain not authorized
value:
error: "invalid_scope"
error_description: "This application is not authorized for this email domain."
'404':
description: |
User not found. Only returned when the application is not allowed
to create users.
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthError'
examples:
userNotFound:
summary: User not found
value:
error: "invalid_scope"
error_description: "User not found."
'409':
description: |
The user could not be created or retrieved because of a concurrent
request. Retrying the request should succeed.
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthError'
examples:
provisionalUserConflict:
summary: Concurrent user creation
value:
error: "server_error"
error_description: "Failed to create or retrieve provisional user."
error: "This application is not authorized for this email domain."
'415':
description: |
Unsupported media type. The request body must be sent as
@@ -188,13 +138,12 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/OAuthError'
$ref: '#/components/schemas/Error'
examples:
unsupportedMediaType:
summary: Unsupported request content type
value:
error: "invalid_request"
error_description: "Unsupported media type 'text/plain' in request."
detail: 'Unsupported media type "text/plain" in request.'
/rooms:
get:
@@ -603,28 +552,11 @@ components:
OAuthError:
type: object
description: OAuth 2.0 error response (RFC 6749, section 5.2)
required:
- error
- error_description
description: OAuth2-compliant error response
properties:
error:
type: string
description: |
Error code. `server_error` is not defined by RFC 6749 for the token
endpoint and is only returned on an unexpected server-side failure.
enum:
- invalid_request
- invalid_client
- unsupported_grant_type
- invalid_scope
- server_error
example: "invalid_client"
error_description:
type: string
description: |
Human-readable description of the error. Contains printable ASCII
characters only, without double quotes or backslashes.
description: Human-readable error description
example: "Invalid credentials"
Error:
+2 -19
View File
@@ -393,28 +393,11 @@ components:
OAuthError:
type: object
description: OAuth 2.0 error response (RFC 6749, section 5.2)
required:
- error
- error_description
description: OAuth2-compliant error response
properties:
error:
type: string
description: |
Error code. `server_error` is not defined by RFC 6749 for the token
endpoint and is only returned on an unexpected server-side failure.
enum:
- invalid_request
- invalid_client
- unsupported_grant_type
- invalid_scope
- server_error
example: "invalid_client"
error_description:
type: string
description: |
Human-readable description of the error. Contains printable ASCII
characters only, without double quotes or backslashes.
description: Human-readable error description
example: "Invalid credentials"
Error:
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "agents"
version = "1.36.1"
version = "1.36.2"
requires-python = ">=3.12"
dependencies = [
"livekit-agents==1.7.0",
+1 -1
View File
@@ -9,7 +9,7 @@ resolution-markers = [
[[package]]
name = "agents"
version = "1.36.1"
version = "1.36.2"
source = { virtual = "." }
dependencies = [
{ name = "boto3" },
+2 -6
View File
@@ -52,14 +52,10 @@ _logger = logging.getLogger(__name__)
def error_message(response) -> Any:
"""Return the message of an error response, as DRF or the view wrote it.
An OAuth 2.0 error (RFC 6749 §5.2) carries its message in
``error_description``, its ``error`` being only a code.
"""
"""Return the message of an error response, as DRF or the view wrote it."""
data = getattr(response, "data", None)
if isinstance(data, Mapping):
return data.get("detail") or data.get("error_description") or data.get("error")
return data.get("detail") or data.get("error")
return None
@@ -1,98 +0,0 @@
"""OAuth 2.0 error responses for the external API token endpoint (RFC 6749 §5.2)."""
import unicodedata
from django.http import Http404
from rest_framework import exceptions as drf_exceptions
from rest_framework import status as drf_status
from core.api import exception_handler as default_exception_handler
OAUTH2_ERROR_INVALID_REQUEST = "invalid_request"
OAUTH2_ERROR_INVALID_CLIENT = "invalid_client"
OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type"
OAUTH2_ERROR_INVALID_SCOPE = "invalid_scope"
OAUTH2_ERROR_SERVER_ERROR = "server_error"
OAUTH2_CLIENT_CREDENTIALS_FIELDS = {"client_id", "client_secret"}
class OAuth2Error(drf_exceptions.APIException):
"""Token endpoint error carrying an RFC 6749 error code as its DRF code."""
def __init__(self, error, description, status_code=drf_status.HTTP_400_BAD_REQUEST):
super().__init__(detail=description, code=error)
self.status_code = status_code
def _get_validation_error_code(codes):
"""Map serializer error codes to the most relevant OAuth 2.0 error code."""
if OAUTH2_CLIENT_CREDENTIALS_FIELDS & codes.keys():
return OAUTH2_ERROR_INVALID_CLIENT
if "invalid_choice" in codes.get("grant_type", []):
return OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE
return OAUTH2_ERROR_INVALID_REQUEST
def _get_error_code(exc):
"""Return the OAuth 2.0 error code matching an exception."""
if isinstance(exc, OAuth2Error):
return exc.get_codes()
if isinstance(exc, drf_exceptions.ValidationError) and isinstance(exc.detail, dict):
return _get_validation_error_code(exc.get_codes())
if isinstance(
exc, (drf_exceptions.AuthenticationFailed, drf_exceptions.NotAuthenticated)
):
return OAUTH2_ERROR_INVALID_CLIENT
return OAUTH2_ERROR_INVALID_REQUEST
def _format_error_description(data):
"""Flatten DRF error data into "field : message; field : message"."""
if isinstance(data, dict):
if data.keys() == {"detail"}:
return _format_error_description(data["detail"])
return "; ".join(
f"{field} : {_format_error_description(messages)}"
for field, messages in data.items()
)
if isinstance(data, list):
return " ".join(_format_error_description(message) for message in data)
return str(data)
def _sanitize_error_description(description):
"""Restrict a description to the characters allowed by RFC 6749 §5.2.
Only printable ASCII is allowed, except for the double quote and the backslash.
Translated messages are transliterated so they stay readable ("é" becomes "e").
"""
ascii_description = (
unicodedata.normalize("NFKD", description)
.encode("ascii", "ignore")
.decode("ascii")
.replace('"', "'")
.replace("\\", "/")
)
return "".join(char for char in ascii_description if " " <= char <= "~")
def oauth2_exception_handler(exc, context):
"""Render errors as RFC 6749 §5.2 responses: {"error", "error_description"}.
Status codes and headers set by the default handler are kept.
"""
response = default_exception_handler(exc, context)
# A disabled feature flag raises Http404: keep it looking like a missing route.
if response is None or isinstance(exc, Http404):
return response
response.data = {
"error": _get_error_code(exc),
"error_description": _sanitize_error_description(
_format_error_description(response.data)
),
}
return response
+19 -25
View File
@@ -35,7 +35,7 @@ from ..services.provisional_user_service import (
ProvisionalUserIntegrityError,
ProvisionalUserService,
)
from . import authentication, exceptions, permissions, serializers
from . import authentication, permissions, serializers
class ApplicationViewSet(audit.AuditViewMixin, viewsets.ViewSet):
@@ -44,10 +44,6 @@ class ApplicationViewSet(audit.AuditViewMixin, viewsets.ViewSet):
audit_actor = None
audit_claimed_client_id = None
def get_exception_handler(self):
"""Render errors as OAuth 2.0 error responses (RFC 6749 section 5.2)."""
return exceptions.oauth2_exception_handler
@decorators.action(
detail=False,
methods=["post"],
@@ -96,33 +92,31 @@ class ApplicationViewSet(audit.AuditViewMixin, viewsets.ViewSet):
try:
validate_email(email)
except ValidationError as e:
raise exceptions.OAuth2Error(
exceptions.OAUTH2_ERROR_INVALID_SCOPE,
"Scope should be a valid email address.",
) from e
except ValidationError:
return drf_response.Response(
{
"error": "Scope should be a valid email address.",
},
status=drf_status.HTTP_400_BAD_REQUEST,
)
if not application.can_delegate_email(email):
raise exceptions.OAuth2Error(
exceptions.OAUTH2_ERROR_INVALID_SCOPE,
"This application is not authorized for this email domain.",
status_code=drf_status.HTTP_403_FORBIDDEN,
return drf_response.Response(
{
"error": "This application is not authorized for this email domain.",
},
status=drf_status.HTTP_403_FORBIDDEN,
)
try:
user, created = ProvisionalUserService().get_or_create(email, client_id)
except ProvisionalUserCreationDisabledError as not_found_error:
raise exceptions.OAuth2Error(
exceptions.OAUTH2_ERROR_INVALID_SCOPE,
"User not found.",
status_code=drf_status.HTTP_404_NOT_FOUND,
) from not_found_error
except ProvisionalUserIntegrityError as integrity_error:
raise exceptions.OAuth2Error(
exceptions.OAUTH2_ERROR_SERVER_ERROR,
"Failed to create or retrieve provisional user.",
status_code=drf_status.HTTP_409_CONFLICT,
) from integrity_error
raise drf_exceptions.NotFound("User not found.") from not_found_error
except ProvisionalUserIntegrityError:
return drf_response.Response(
{"error": "Failed to create or retrieve provisional user."},
status=drf_status.HTTP_409_CONFLICT,
)
scope = " ".join(application.scopes or [])
+1 -18
View File
@@ -33,7 +33,6 @@ class ThingViewSet(audit.AuditViewMixin, viewsets.ViewSet):
permission_classes = []
audit_actions = {"list": "thing.list", "create": "thing.create"}
error = None
error_body = {"error": "Already exists."}
def list(self, request):
if self.error is not None:
@@ -42,7 +41,7 @@ class ThingViewSet(audit.AuditViewMixin, viewsets.ViewSet):
return Response([])
def create(self, request):
return Response(self.error_body, status=409)
return Response({"error": "Already exists."}, status=409)
def destroy(self, request, pk=None):
return Response(status=204)
@@ -199,22 +198,6 @@ def test_error_message_of_a_view_response(audit_events):
assert event["error"] == {"message": "Already exists."}
def test_error_message_of_an_oauth2_error_response(audit_events):
"""An OAuth 2.0 error reports its description rather than its code."""
view = ThingViewSet.as_view(
{"post": "create"},
error_body={"error": "server_error", "error_description": "Already exists."},
)
response = view(APIRequestFactory().post("/things/"))
assert response.status_code == 409
[event] = find_events(audit_events, "thing.create")
assert event["error"] == {"message": "Already exists."}
def test_actions_missing_from_the_map_are_not_audited(audit_events):
"""Only the actions listed in ``audit_actions`` emit events."""
view = ThingViewSet.as_view({"delete": "destroy"})
+15 -124
View File
@@ -27,11 +27,6 @@ from core.services import provisional_user_service
pytestmark = pytest.mark.django_db
INVALID_CREDENTIALS_ERROR = {
"error": "invalid_client",
"error_description": "Invalid credentials",
}
def test_api_applications_generate_token_application_disabled(settings):
"""When APPLICATION_ENABLED is False, the endpoint should return 404."""
@@ -58,7 +53,6 @@ def test_api_applications_generate_token_application_disabled(settings):
)
assert response.status_code == 404
assert response.json() == {"detail": "Not found."}
def test_api_applications_generate_token_success(settings):
@@ -146,7 +140,7 @@ def test_api_applications_generate_token_invalid_credentials_query_count(
)
assert response.status_code == 401
assert response.json() == INVALID_CREDENTIALS_ERROR
assert "Invalid credentials" in str(response.data)
def test_api_applications_generate_token_form_urlencoded(settings):
@@ -207,7 +201,7 @@ def test_api_applications_generate_token_form_urlencoded_invalid_credentials():
)
assert response.status_code == 401
assert response.json() == INVALID_CREDENTIALS_ERROR
assert "Invalid credentials" in str(response.data)
def test_api_applications_generate_token_form_urlencoded_missing_fields():
@@ -221,14 +215,8 @@ def test_api_applications_generate_token_form_urlencoded_missing_fields():
)
assert response.status_code == 400
assert response.json() == {
"error": "invalid_client",
"error_description": (
"client_id : This field is required.; "
"client_secret : This field is required.; "
"scope : This field is required."
),
}
for field in ("client_id", "client_secret", "scope"):
assert field in response.data
def test_api_applications_generate_token_form_urlencoded_invalid_grant_type():
@@ -252,12 +240,7 @@ def test_api_applications_generate_token_form_urlencoded_invalid_grant_type():
)
assert response.status_code == 400
assert response.json() == {
"error": "unsupported_grant_type",
"error_description": (
"grant_type : 'authorization_code' is not a valid choice."
),
}
assert "grant_type" in response.data
def test_api_applications_generate_token_form_urlencoded_special_characters():
@@ -300,75 +283,6 @@ def test_api_applications_generate_token_unsupported_media_type():
)
assert response.status_code == 415
assert response.json() == {
"error": "invalid_request",
"error_description": "Unsupported media type 'text/plain' in request.",
}
def test_api_applications_generate_token_missing_fields():
"""Missing fields should return an RFC 6749 error listing every invalid
field, reported as "invalid_client" when client credentials are missing."""
client = APIClient()
response = client.post(
"/external-api/v1.0/application/token/",
{},
format="json",
)
assert response.status_code == 400
assert response.json() == {
"error": "invalid_client",
"error_description": (
"client_id : This field is required.; "
"client_secret : This field is required.; "
"grant_type : This field is required.; "
"scope : This field is required."
),
}
def test_api_applications_generate_token_missing_grant_type():
"""A request with client credentials but no grant_type should return
an "invalid_request" error."""
user = UserFactory(email="user@example.com")
plain_secret = "test-secret-123"
application = ApplicationFactory(client_secret=plain_secret, is_active=True)
client = APIClient()
response = client.post(
"/external-api/v1.0/application/token/",
{
"client_id": application.client_id,
"client_secret": plain_secret,
"scope": user.email,
},
format="json",
)
assert response.status_code == 400
assert response.json() == {
"error": "invalid_request",
"error_description": "grant_type : This field is required.",
}
def test_api_applications_generate_token_error_description_charset():
"""Translated error descriptions should only contain the characters
allowed by RFC 6749 section 5.2: printable ASCII, except double quotes
and backslashes."""
user = UserFactory(email="user@example.com")
application = ApplicationFactory(is_active=True)
response = _post_token(
application.client_id, "", user.email, HTTP_ACCEPT_LANGUAGE="fr-fr"
)
assert response.status_code == 400
assert response.json() == {
"error": "invalid_client",
"error_description": "client_secret : Ce champ ne peut etre vide.",
}
def test_api_applications_generate_token_invalid_client_id():
@@ -388,7 +302,7 @@ def test_api_applications_generate_token_invalid_client_id():
)
assert response.status_code == 401
assert response.json() == INVALID_CREDENTIALS_ERROR
assert "Invalid credentials" in str(response.data)
def test_api_applications_generate_token_invalid_client_secret():
@@ -409,7 +323,7 @@ def test_api_applications_generate_token_invalid_client_secret():
)
assert response.status_code == 401
assert response.json() == INVALID_CREDENTIALS_ERROR
assert "Invalid credentials" in str(response.data)
def test_token_unknown_client_id_with_valid_secret():
@@ -430,7 +344,7 @@ def test_token_unknown_client_id_with_valid_secret():
)
assert response.status_code == 401
assert response.json() == INVALID_CREDENTIALS_ERROR
assert "Invalid credentials" in str(response.data)
def test_token_rejects_secret_owned_by_another_application():
@@ -452,7 +366,7 @@ def test_token_rejects_secret_owned_by_another_application():
)
assert response.status_code == 401
assert response.json() == INVALID_CREDENTIALS_ERROR
assert "Invalid credentials" in str(response.data)
def test_api_applications_generate_token_inactive_application():
@@ -474,10 +388,7 @@ def test_api_applications_generate_token_inactive_application():
)
assert response.status_code == 401
assert response.json() == {
"error": "invalid_client",
"error_description": "Application is inactive",
}
assert "Application is inactive" in str(response.data)
def test_api_applications_generate_token_inactive_application_wrong_secret():
@@ -498,7 +409,7 @@ def test_api_applications_generate_token_inactive_application_wrong_secret():
)
assert response.status_code == 401
assert response.json() == INVALID_CREDENTIALS_ERROR
assert "Invalid credentials" in str(response.data)
assert "inactive" not in str(response.data).lower()
@@ -520,10 +431,7 @@ def test_api_applications_generate_token_invalid_email_format():
)
assert response.status_code == 400
assert response.json() == {
"error": "invalid_scope",
"error_description": "Scope should be a valid email address.",
}
assert "scope should be a valid email address." in str(response.data).lower()
def test_api_applications_generate_token_domain_not_authorized():
@@ -546,12 +454,7 @@ def test_api_applications_generate_token_domain_not_authorized():
)
assert response.status_code == 403
assert response.json() == {
"error": "invalid_scope",
"error_description": (
"This application is not authorized for this email domain."
),
}
assert "not authorized for this email domain" in str(response.data)
def test_api_applications_generate_token_domain_authorized():
@@ -599,10 +502,7 @@ def test_api_applications_generate_token_user_not_found():
)
assert response.status_code == 404
assert response.json() == {
"error": "invalid_scope",
"error_description": "User not found.",
}
assert "User not found" in str(response.data)
@freeze_time("2023-01-15 12:00:00")
@@ -815,7 +715,7 @@ def test_api_applications_token_new_user_race_condition(mock_get_by_email, setti
def test_api_applications_token_new_user_race_condition_unrecoverable(
mock_get_or_create, settings
):
"""Should return 409 when ProvisionalUserIntegrityError is raised."""
"""Should return 500 when ProvisionalUserIntegrityError is raised."""
settings.APPLICATION_ALLOW_USER_CREATION = True
settings.OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION = True
@@ -839,10 +739,6 @@ def test_api_applications_token_new_user_race_condition_unrecoverable(
)
assert response.status_code == 409
assert response.json() == {
"error": "server_error",
"error_description": "Failed to create or retrieve provisional user.",
}
assert mock_get_or_create.call_count == 1
@@ -1173,9 +1069,6 @@ def test_api_applications_generate_token_domain_denied_is_audited(audit_events):
assert event["entity"]["target"]["id"] == str(application.pk)
assert event["lasuite"]["details"] == {"requested_domain": "random.com"}
assert event["http"]["response"] == {"status_code": 403}
assert event["error"] == {
"message": "This application is not authorized for this email domain."
}
def test_api_applications_generate_token_invalid_email_is_audited(audit_events):
@@ -1191,7 +1084,6 @@ def test_api_applications_generate_token_invalid_email_is_audited(audit_events):
assert event["event"]["reason"] == "validation_error"
assert event["lasuite"]["actor"] == {"type": "application"}
assert event["http"]["response"] == {"status_code": 400}
assert event["error"] == {"message": "Scope should be a valid email address."}
assert "details" not in event["lasuite"]
@@ -1210,7 +1102,6 @@ def test_api_applications_generate_token_unknown_user_is_audited(audit_events):
assert event["event"]["reason"] == "not_found"
assert event["lasuite"]["details"] == {"requested_domain": "example.com"}
assert event["http"]["response"] == {"status_code": 404}
assert event["error"] == {"message": "User not found."}
def test_api_applications_generate_token_provisioning_is_audited(
+1 -1
View File
@@ -7,7 +7,7 @@ build-backend = "uv_build"
[project]
name = "meet"
version = "1.36.1"
version = "1.36.2"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
+1 -1
View File
@@ -1339,7 +1339,7 @@ wheels = [
[[package]]
name = "meet"
version = "1.36.1"
version = "1.36.2"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:22-alpine AS frontend-deps
FROM node:22.23.4-alpine AS frontend-deps
RUN apk upgrade --no-cache ca-certificates
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "meet",
"version": "1.36.1",
"version": "1.36.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "meet",
"version": "1.36.1",
"version": "1.36.2",
"dependencies": {
"@fontsource-variable/atkinson-hyperlegible-next": "5.3.0",
"@fontsource-variable/lexend": "5.3.0",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "meet",
"private": true,
"version": "1.36.1",
"version": "1.36.2",
"type": "module",
"scripts": {
"dev": "panda codegen && vite",
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "mail_mjml",
"version": "1.36.1",
"version": "1.36.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mail_mjml",
"version": "1.36.1",
"version": "1.36.2",
"license": "MIT",
"dependencies": {
"@html-to/text-cli": "0.6.1",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "mail_mjml",
"version": "1.36.1",
"version": "1.36.2",
"description": "An util to generate html and text django's templates from mjml templates",
"type": "module",
"dependencies": {
@@ -14,7 +14,7 @@
"build": "npm run build-mjml-to-html && npm run build-html-to-plain-text"
},
"volta": {
"node": "24.13.1"
"node": "24.22.1"
},
"repository": "https://github.com/suitenumerique/meet",
"author": "DINUM",
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "sdk",
"version": "1.36.1",
"version": "1.36.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sdk",
"version": "1.36.1",
"version": "1.36.2",
"license": "ISC",
"workspaces": [
"./library",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "sdk",
"version": "1.36.1",
"version": "1.36.2",
"author": "",
"license": "ISC",
"description": "",
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "summary"
version = "1.36.1"
version = "1.36.2"
requires-python = ">=3.13"
dependencies = [
"certifi>=2026.10.19",
+1 -1
View File
@@ -1484,7 +1484,7 @@ wheels = [
[[package]]
name = "summary"
version = "1.36.1"
version = "1.36.2"
source = { editable = "." }
dependencies = [
{ name = "boto3" },