This commit is contained in:
@@ -259,7 +259,8 @@ func (x *Slide) GetImageUrl() string {
|
|||||||
|
|
||||||
// SlideDeck is the final structured conversion artifact.
|
// SlideDeck is the final structured conversion artifact.
|
||||||
type SlideDeck struct {
|
type SlideDeck struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
SourceFilename string `protobuf:"bytes,2,opt,name=source_filename,json=sourceFilename,proto3" json:"source_filename,omitempty"`
|
SourceFilename string `protobuf:"bytes,2,opt,name=source_filename,json=sourceFilename,proto3" json:"source_filename,omitempty"`
|
||||||
Slides []*Slide `protobuf:"bytes,3,rep,name=slides,proto3" json:"slides,omitempty"`
|
Slides []*Slide `protobuf:"bytes,3,rep,name=slides,proto3" json:"slides,omitempty"`
|
||||||
@@ -397,7 +398,8 @@ func (x *CreateConversionRequest) GetResolution() ConversionResolution {
|
|||||||
|
|
||||||
// CreateConversionResponse returns upload details for the session.
|
// CreateConversionResponse returns upload details for the session.
|
||||||
type CreateConversionResponse struct {
|
type CreateConversionResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
UploadBucket string `protobuf:"bytes,2,opt,name=upload_bucket,json=uploadBucket,proto3" json:"upload_bucket,omitempty"`
|
UploadBucket string `protobuf:"bytes,2,opt,name=upload_bucket,json=uploadBucket,proto3" json:"upload_bucket,omitempty"`
|
||||||
UploadObjectKey string `protobuf:"bytes,3,opt,name=upload_object_key,json=uploadObjectKey,proto3" json:"upload_object_key,omitempty"`
|
UploadObjectKey string `protobuf:"bytes,3,opt,name=upload_object_key,json=uploadObjectKey,proto3" json:"upload_object_key,omitempty"`
|
||||||
@@ -474,8 +476,9 @@ func (x *CreateConversionResponse) GetExpiresAt() *timestamppb.Timestamp {
|
|||||||
|
|
||||||
// StartConversionRequest requests conversion of an already uploaded PPTX.
|
// StartConversionRequest requests conversion of an already uploaded PPTX.
|
||||||
type StartConversionRequest struct {
|
type StartConversionRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -519,9 +522,10 @@ func (x *StartConversionRequest) GetConversionId() string {
|
|||||||
|
|
||||||
// StartConversionResponse returns the first known status after enqueue.
|
// StartConversionResponse returns the first known status after enqueue.
|
||||||
type StartConversionResponse struct {
|
type StartConversionResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
Status ConversionStatus `protobuf:"varint,2,opt,name=status,proto3,enum=officeconvertapi.v1.ConversionStatus" json:"status,omitempty"`
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
|
Status ConversionStatus `protobuf:"varint,2,opt,name=status,proto3,enum=officeconvertapi.v1.ConversionStatus" json:"status,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -572,8 +576,9 @@ func (x *StartConversionResponse) GetStatus() ConversionStatus {
|
|||||||
|
|
||||||
// GetConversionStatusRequest asks for a specific conversion status.
|
// GetConversionStatusRequest asks for a specific conversion status.
|
||||||
type GetConversionStatusRequest struct {
|
type GetConversionStatusRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -617,7 +622,8 @@ func (x *GetConversionStatusRequest) GetConversionId() string {
|
|||||||
|
|
||||||
// GetConversionStatusResponse returns current status and optional error info.
|
// GetConversionStatusResponse returns current status and optional error info.
|
||||||
type GetConversionStatusResponse struct {
|
type GetConversionStatusResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
Status ConversionStatus `protobuf:"varint,2,opt,name=status,proto3,enum=officeconvertapi.v1.ConversionStatus" json:"status,omitempty"`
|
Status ConversionStatus `protobuf:"varint,2,opt,name=status,proto3,enum=officeconvertapi.v1.ConversionStatus" json:"status,omitempty"`
|
||||||
ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"`
|
ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"`
|
||||||
@@ -710,8 +716,9 @@ func (x *GetConversionStatusResponse) GetMaxProgress() int32 {
|
|||||||
|
|
||||||
// GetSlideDeckRequest fetches a completed deck.
|
// GetSlideDeckRequest fetches a completed deck.
|
||||||
type GetSlideDeckRequest struct {
|
type GetSlideDeckRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -800,8 +807,9 @@ func (x *GetSlideDeckResponse) GetSlideDeck() *SlideDeck {
|
|||||||
|
|
||||||
// DeleteConversionRequest requests immediate cleanup.
|
// DeleteConversionRequest requests immediate cleanup.
|
||||||
type DeleteConversionRequest struct {
|
type DeleteConversionRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -845,9 +853,10 @@ func (x *DeleteConversionRequest) GetConversionId() string {
|
|||||||
|
|
||||||
// DeleteConversionResponse confirms cleanup details.
|
// DeleteConversionResponse confirms cleanup details.
|
||||||
type DeleteConversionResponse struct {
|
type DeleteConversionResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
Deleted bool `protobuf:"varint,2,opt,name=deleted,proto3" json:"deleted,omitempty"`
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
|
Deleted bool `protobuf:"varint,2,opt,name=deleted,proto3" json:"deleted,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ message Slide {
|
|||||||
|
|
||||||
// SlideDeck is the final structured conversion artifact.
|
// SlideDeck is the final structured conversion artifact.
|
||||||
message SlideDeck {
|
message SlideDeck {
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
string conversion_id = 1;
|
string conversion_id = 1;
|
||||||
string source_filename = 2;
|
string source_filename = 2;
|
||||||
repeated Slide slides = 3;
|
repeated Slide slides = 3;
|
||||||
@@ -78,6 +79,7 @@ message CreateConversionRequest {
|
|||||||
|
|
||||||
// CreateConversionResponse returns upload details for the session.
|
// CreateConversionResponse returns upload details for the session.
|
||||||
message CreateConversionResponse {
|
message CreateConversionResponse {
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
string conversion_id = 1;
|
string conversion_id = 1;
|
||||||
string upload_bucket = 2;
|
string upload_bucket = 2;
|
||||||
string upload_object_key = 3;
|
string upload_object_key = 3;
|
||||||
@@ -87,22 +89,26 @@ message CreateConversionResponse {
|
|||||||
|
|
||||||
// StartConversionRequest requests conversion of an already uploaded PPTX.
|
// StartConversionRequest requests conversion of an already uploaded PPTX.
|
||||||
message StartConversionRequest {
|
message StartConversionRequest {
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
string conversion_id = 1;
|
string conversion_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartConversionResponse returns the first known status after enqueue.
|
// StartConversionResponse returns the first known status after enqueue.
|
||||||
message StartConversionResponse {
|
message StartConversionResponse {
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
string conversion_id = 1;
|
string conversion_id = 1;
|
||||||
ConversionStatus status = 2;
|
ConversionStatus status = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConversionStatusRequest asks for a specific conversion status.
|
// GetConversionStatusRequest asks for a specific conversion status.
|
||||||
message GetConversionStatusRequest {
|
message GetConversionStatusRequest {
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
string conversion_id = 1;
|
string conversion_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConversionStatusResponse returns current status and optional error info.
|
// GetConversionStatusResponse returns current status and optional error info.
|
||||||
message GetConversionStatusResponse {
|
message GetConversionStatusResponse {
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
string conversion_id = 1;
|
string conversion_id = 1;
|
||||||
ConversionStatus status = 2;
|
ConversionStatus status = 2;
|
||||||
string error_message = 3;
|
string error_message = 3;
|
||||||
@@ -114,6 +120,7 @@ message GetConversionStatusResponse {
|
|||||||
|
|
||||||
// GetSlideDeckRequest fetches a completed deck.
|
// GetSlideDeckRequest fetches a completed deck.
|
||||||
message GetSlideDeckRequest {
|
message GetSlideDeckRequest {
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
string conversion_id = 1;
|
string conversion_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,11 +131,13 @@ message GetSlideDeckResponse {
|
|||||||
|
|
||||||
// DeleteConversionRequest requests immediate cleanup.
|
// DeleteConversionRequest requests immediate cleanup.
|
||||||
message DeleteConversionRequest {
|
message DeleteConversionRequest {
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
string conversion_id = 1;
|
string conversion_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteConversionResponse confirms cleanup details.
|
// DeleteConversionResponse confirms cleanup details.
|
||||||
message DeleteConversionResponse {
|
message DeleteConversionResponse {
|
||||||
|
// Session identifier: KSUID in standard base62 text form. Well-formed values are at most 27 characters (see https://github.com/segmentio/ksuid).
|
||||||
string conversion_id = 1;
|
string conversion_id = 1;
|
||||||
bool deleted = 2;
|
bool deleted = 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ dependencies = [
|
|||||||
"connectrpc>=0.6.0",
|
"connectrpc>=0.6.0",
|
||||||
"minio>=7.2.18",
|
"minio>=7.2.18",
|
||||||
"officeconvert",
|
"officeconvert",
|
||||||
|
"svix-ksuid>=0.7.0",
|
||||||
"uvicorn>=0.35.0",
|
"uvicorn>=0.35.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ from pathlib import Path
|
|||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import uuid
|
|
||||||
|
|
||||||
from connectrpc.code import Code
|
from connectrpc.code import Code
|
||||||
from connectrpc.errors import ConnectError
|
from connectrpc.errors import ConnectError
|
||||||
@@ -29,6 +28,7 @@ from officeconvert.conversion import (
|
|||||||
RESOLUTION_UHD,
|
RESOLUTION_UHD,
|
||||||
)
|
)
|
||||||
from officeconvertapi.v1 import conversion_connect, conversion_pb2
|
from officeconvertapi.v1 import conversion_connect, conversion_pb2
|
||||||
|
from ksuid import Ksuid
|
||||||
|
|
||||||
from minio.error import S3Error
|
from minio.error import S3Error
|
||||||
|
|
||||||
@@ -75,8 +75,9 @@ class ConversionServiceImpl(conversion_connect.ConversionService):
|
|||||||
if resolution not in _RESOLUTION_PRESET_BY_PROTO:
|
if resolution not in _RESOLUTION_PRESET_BY_PROTO:
|
||||||
raise ConnectError(Code.INVALID_ARGUMENT, "resolution is invalid")
|
raise ConnectError(Code.INVALID_ARGUMENT, "resolution is invalid")
|
||||||
|
|
||||||
conversion_id = str(uuid.uuid4())
|
ksuid = Ksuid()
|
||||||
bucket_name = f"oc-{conversion_id}"
|
conversion_id = str(ksuid)
|
||||||
|
bucket_name = f"oc-{bytes(ksuid).hex()}"
|
||||||
upload_key = "input/source.pptx"
|
upload_key = "input/source.pptx"
|
||||||
expires_at = utc_now() + timedelta(seconds=self._config.s3_session_ttl_seconds)
|
expires_at = utc_now() + timedelta(seconds=self._config.s3_session_ttl_seconds)
|
||||||
|
|
||||||
|
|||||||
Generated
+20
@@ -292,6 +292,7 @@ dependencies = [
|
|||||||
{ name = "connectrpc" },
|
{ name = "connectrpc" },
|
||||||
{ name = "minio" },
|
{ name = "minio" },
|
||||||
{ name = "officeconvert" },
|
{ name = "officeconvert" },
|
||||||
|
{ name = "svix-ksuid" },
|
||||||
{ name = "uvicorn" },
|
{ name = "uvicorn" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -300,6 +301,7 @@ requires-dist = [
|
|||||||
{ name = "connectrpc", specifier = ">=0.6.0" },
|
{ name = "connectrpc", specifier = ">=0.6.0" },
|
||||||
{ name = "minio", specifier = ">=7.2.18" },
|
{ name = "minio", specifier = ">=7.2.18" },
|
||||||
{ name = "officeconvert", editable = "packages/officeconvert" },
|
{ name = "officeconvert", editable = "packages/officeconvert" },
|
||||||
|
{ name = "svix-ksuid", specifier = ">=0.7.0" },
|
||||||
{ name = "uvicorn", specifier = ">=0.35.0" },
|
{ name = "uvicorn", specifier = ">=0.35.0" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -479,6 +481,12 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/2d/d8/d6710bbb38f6a715135f7c8a8e5c6227d69299a2b7e989c81315a08054e7/pyqwest-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:a7b8d8ae51ccf6375a9e82e5b38d2129ee3121acf4933a37e541f4fe04a5f758", size = 4577924, upload-time = "2026-03-06T02:32:31.013Z" },
|
{ url = "https://files.pythonhosted.org/packages/2d/d8/d6710bbb38f6a715135f7c8a8e5c6227d69299a2b7e989c81315a08054e7/pyqwest-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:a7b8d8ae51ccf6375a9e82e5b38d2129ee3121acf4933a37e541f4fe04a5f758", size = 4577924, upload-time = "2026-03-06T02:32:31.013Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-baseconv"
|
||||||
|
version = "1.2.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/33/d0/9297d7d8dd74767b4d5560d834b30b2fff17d39987c23ed8656f476e0d9b/python-baseconv-1.2.2.tar.gz", hash = "sha256:0539f8bd0464013b05ad62e0a1673f0ac9086c76b43ebf9f833053527cd9931b", size = 4929, upload-time = "2019-04-04T19:28:57.17Z" }
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-pptx"
|
name = "python-pptx"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@@ -494,6 +502,18 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/d9/4f/00be2196329ebbff56ce564aa94efb0fbc828d00de250b1980de1a34ab49/python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba", size = 472788, upload-time = "2024-08-07T17:33:28.192Z" },
|
{ url = "https://files.pythonhosted.org/packages/d9/4f/00be2196329ebbff56ce564aa94efb0fbc828d00de250b1980de1a34ab49/python_pptx-1.0.2-py3-none-any.whl", hash = "sha256:160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba", size = 472788, upload-time = "2024-08-07T17:33:28.192Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "svix-ksuid"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "python-baseconv" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ef/f0/b534cc208ee8ea010254208345324d2e3d3c5d13a210d2249f9f11840f2f/svix_ksuid-0.7.0.tar.gz", hash = "sha256:e6b824437b5bb76d75163b928cbf9ae6c871d3e713e5de75cde89bae6a19bfe8", size = 5810, upload-time = "2026-03-14T02:44:51.944Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/74/51/922e6ad4da6b3f4863ea059db2391259c9247b9fe4f0c08da50eff4e1312/svix_ksuid-0.7.0-py3-none-any.whl", hash = "sha256:6bceae49f85e026c77a13e6de81bf4536c15039ecb160415e954403618c2a06f", size = 5607, upload-time = "2026-03-14T02:44:50.822Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "4.15.0"
|
version = "4.15.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user