an attempt at a lightweight photo/album viewer

some backend proprocessing, update to drizzle beta, a basic segmented album

+2178 -132
+1 -1
preprocessing/.python-version
··· 1 - 3.14 1 + 3.9
+6
preprocessing/.vscode/settings.json
··· 1 + { 2 + "python.analysis.extraPaths": [ 3 + "./albums-api-client", 4 + "./photos-api-client" 5 + ] 6 + }
+2 -2
preprocessing/pyproject.toml
··· 3 3 version = "0.1.0" 4 4 description = "Add your description here" 5 5 readme = "README.md" 6 - requires-python = ">=3.14" 6 + requires-python = ">=3.9" 7 7 dependencies = [ 8 - "openapi-python-client>=0.28.0", 8 + "openapi-python-client>=0.26.2", 9 9 ]
+49
preprocessing/saveeachphototobackend.py
··· 1 + import argparse 2 + import json 3 + import os 4 + from datetime import datetime 5 + 6 + from photos_api_client import Client 7 + from photos_api_client.api.default import post_photo, post_album, post_batch_photos_in_album 8 + from photos_api_client.models import PostAlbumBody, PostAlbumBodyAlbum 9 + from photos_api_client.models import PostBatchPhotosInAlbumBody, PostBatchPhotosInAlbumBodyAlbum, PostBatchPhotosInAlbumBodyContentsItem 10 + from photos_api_client.models.post_photo_body import PostPhotoBody 11 + 12 + parser = argparse.ArgumentParser(description="Generate api calls for photo directories.") 13 + parser.add_argument("target_directory", help="The root directory to process.") 14 + args = parser.parse_args() 15 + 16 + store_json_path = os.path.join(args.target_directory, 'store.geo.json') 17 + 18 + with open(store_json_path, 'r') as f: 19 + store_data = json.load(f) 20 + 21 + print(store_data[0].keys()) 22 + 23 + def fill_photo_record(image): 24 + imgToPost = PostPhotoBody( 25 + file_name=image['file'], 26 + date_created=datetime.fromisoformat(image['timestamp']), 27 + date_modified=datetime.fromisoformat(image['timestamp']), 28 + width=image['metadata']['width'], 29 + height=image['metadata']['width'], 30 + orientation=image['metadata']['orientation'], 31 + region=image['metadata']['region'] if 'region' in image['metadata'] else None 32 + ) 33 + 34 + # imgToPost.file_name = image.file 35 + # imgToPost.date_created = image.timestamp 36 + # imgToPost.date_modified = image.timestamp 37 + # imgToPost.width = image.metadata.width 38 + # imgToPost.height = image.metadata.width 39 + # imgToPost.orientation = image.metadata.orientation 40 + # imgToPost.region = image.region 41 + return imgToPost 42 + 43 + client = Client(base_url="http://adams-laptop:8000") 44 + photoRecords = [fill_photo_record(image) 45 + for seg in store_data[0]['segments'] for image in seg['images']] 46 + for photo in photoRecords: 47 + print(photo.file_name) 48 + resp = post_photo.sync(client=client, body=photo) 49 + print(resp)
+55
preprocessing/savetobackend.py
··· 1 + import argparse 2 + import json 3 + import os 4 + from datetime import datetime 5 + 6 + from photos_api_client import Client 7 + from photos_api_client.api.default import post_album, post_batch_photos_in_album 8 + from photos_api_client.models import PostAlbumBody, PostAlbumBodyAlbum 9 + from photos_api_client.models import PostBatchPhotosInAlbumBody, PostBatchPhotosInAlbumBodyAlbum, PostBatchPhotosInAlbumBodyContentsItem 10 + 11 + parser = argparse.ArgumentParser(description="Generate api calls for photo directories.") 12 + parser.add_argument("target_directory", help="The root directory to process.") 13 + args = parser.parse_args() 14 + 15 + store_json_path = os.path.join(args.target_directory, 'store.geo.json') 16 + 17 + with open(store_json_path, 'r') as f: 18 + store_data = json.load(f) 19 + 20 + print(store_data[0].keys()) 21 + 22 + def fill_photo_record(image): 23 + imgToPost = PostBatchPhotosInAlbumBodyContentsItem( 24 + file_name=image['file'], 25 + date_created=datetime.fromisoformat(image['timestamp']), 26 + date_modified=datetime.fromisoformat(image['timestamp']), 27 + width=image['metadata']['width'], 28 + height=image['metadata']['width'], 29 + orientation=image['metadata']['orientation'], 30 + region=image['metadata']['region'] if 'region' in image['metadata'] else None 31 + ) 32 + 33 + # imgToPost.file_name = image.file 34 + # imgToPost.date_created = image.timestamp 35 + # imgToPost.date_modified = image.timestamp 36 + # imgToPost.width = image.metadata.width 37 + # imgToPost.height = image.metadata.width 38 + # imgToPost.orientation = image.metadata.orientation 39 + # imgToPost.region = image.region 40 + return imgToPost 41 + 42 + client = Client(base_url="http://adams-laptop:8000") 43 + photoRecords = [fill_photo_record(image) 44 + for seg in store_data[0]['segments'] for image in seg['images']] 45 + 46 + 47 + resp = post_batch_photos_in_album.sync(client=client, body=PostBatchPhotosInAlbumBody( 48 + album=PostBatchPhotosInAlbumBodyAlbum( 49 + title=args.target_directory, 50 + year=2025 51 + ), 52 + contents=photoRecords 53 + )) 54 + 55 + print(resp)
+31
preprocessing/savetobackendblunt.py
··· 1 + import argparse 2 + import json 3 + import os 4 + from datetime import datetime 5 + 6 + from photos_api_client import Client 7 + from photos_api_client.api.default import post_segmented_album_with_new_photos_w_blunt_geo 8 + from photos_api_client.models import PostSegmentedAlbumWithNewPhotosWBluntGeoBody 9 + 10 + parser = argparse.ArgumentParser(description="Generate api calls for photo directories.") 11 + parser.add_argument("target_directory", help="The root directory to process.") 12 + args = parser.parse_args() 13 + 14 + store_json_path = os.path.join(args.target_directory, 'store.geo.json') 15 + 16 + with open(store_json_path, 'r') as f: 17 + store_data = json.load(f) 18 + 19 + print(store_data[0].keys()) 20 + 21 + client = Client(base_url="http://adams-laptop:8000") 22 + album=dict() 23 + album['album'] = dict() 24 + album['album']['title'] = 'a test' 25 + album['album']['year'] = 2025 26 + album['sections'] = store_data 27 + data = PostSegmentedAlbumWithNewPhotosWBluntGeoBody.from_dict(album) 28 + 29 + resp = post_segmented_album_with_new_photos_w_blunt_geo.sync(client=client, body=data) 30 + 31 + print(resp)
+102
preprocessing/srv/geoclust.py
··· 1 + import os 2 + import sys 3 + import json 4 + from PIL import Image 5 + from PIL.ExifTags import TAGS 6 + from datetime import datetime 7 + from geopy.distance import geodesic 8 + 9 + def decimal_coords(coords, ref): 10 + """Convert GPS coordinates from DMS to decimal degrees.""" 11 + decimal_degrees = float(coords[0]) + float(coords[1])/60 + float(coords[2])/3600 12 + if ref in ('S', 'W'): 13 + decimal_degrees = -decimal_degrees 14 + return decimal_degrees 15 + 16 + def main(directory, threshold): 17 + # Find GPSInfo tag in EXIF 18 + GPSINFO_TAG = next(tag for tag, name in TAGS.items() if name == "GPSInfo") 19 + 20 + # Collect image data 21 + files = os.listdir(directory) 22 + images = [] 23 + prog=1 24 + for filename in files: 25 + if prog % 200 == 0 or len(files) == prog: 26 + print(f"Progress: {prog} / {len(files)}") 27 + prog+=1 28 + filepath = os.path.join(directory, filename) 29 + if filename.lower().endswith(('.jpg', '.jpeg')): 30 + try: 31 + with Image.open(filepath) as img: 32 + exif = img.getexif() 33 + if exif: 34 + gps_info = exif.get(GPSINFO_TAG, None) 35 + if gps_info: 36 + # Extract latitude and longitude 37 + lat_coords = gps_info[2] 38 + lat_ref = gps_info[1] 39 + lon_coords = gps_info[4] 40 + lon_ref = gps_info[3] 41 + lat = decimal_coords(lat_coords, lat_ref) 42 + lon = decimal_coords(lon_coords, lon_ref) 43 + modtime = datetime.fromtimestamp(os.path.getmtime(filepath)) 44 + images.append({ 45 + 'modtime': modtime.timestamp(), 46 + 'lat': lat, 47 + 'lon': lon, 48 + 'filename': filename 49 + }) 50 + # (modtime, lat, lon, filename)) 51 + except Exception as e: 52 + print(f"Error processing {filename}: {e}") 53 + 54 + # Sort images by modification time 55 + images.sort(key=lambda x: x['modtime']) 56 + 57 + # Group into clusters based on distance threshold 58 + clusters = [] 59 + current_cluster = [] 60 + for i in range(len(images)): 61 + if i == 0: 62 + current_cluster.append(images[i]) 63 + else: 64 + prev = images[i - 1] 65 + curr = images[i] 66 + distance = geodesic((prev['lat'], prev['lon']), (curr['lat'], curr['lon'])).kilometers 67 + if distance < threshold: 68 + current_cluster.append(curr) 69 + else: 70 + clusters.append(current_cluster) 71 + current_cluster = [curr] 72 + if current_cluster: 73 + clusters.append(current_cluster) 74 + 75 + with open(f"{directory}.clusters.json", 'w') as f: 76 + json.dump([clusters], f, indent=2) 77 + 78 + # Output results 79 + print(f"\n{'='*60}") 80 + print(f"{'GPS Clustering Report':^60}") 81 + print(f"{'='*60}") 82 + 83 + for idx, cluster in enumerate(clusters): 84 + print(f"\nCluster {idx} (Representative: {cluster[0]['filename']})") 85 + for img in cluster: 86 + print(f" File: {img['filename']}") 87 + print(f" Lat: {img['lat']:.6f}, Lon: {img['lon']:.6f}") 88 + 89 + print(f"\n{'='*60}") 90 + print(f"Total Clusters: {len(clusters)}") 91 + print(f"{'='*60}") 92 + 93 + if __name__ == "__main__": 94 + DEFAULT_THRESHOLD=30.0 95 + if len(sys.argv) < 2: 96 + print("Usage: python script.py <directory> [threshold]") 97 + print(f"Default threshold is {DEFAULT_THRESHOLD} km") 98 + sys.exit(1) 99 + 100 + directory = sys.argv[1] 101 + threshold = float(sys.argv[2]) if len(sys.argv) > 2 else DEFAULT_THRESHOLD 102 + main(directory, threshold)
+133
preprocessing/srv/geokdclust.py
··· 1 + import sys 2 + import json 3 + from kdtree import GeoIndexer 4 + 5 + def main(): 6 + if len(sys.argv) < 2: 7 + print("Usage: python script.py <directory> [threshold]") 8 + print(f"Default threshold is {DEFAULT_THRESHOLD} km") 9 + sys.exit(1) 10 + 11 + directory = sys.argv[1] 12 + 13 + # Define the column names in order based on the schema 14 + column_names = [ 15 + 'geonameid', 16 + 'name', 17 + 'asciiname', 18 + 'alternatenames', 19 + 'latitude', 20 + 'longitude', 21 + 'feature_class', 22 + 'feature_code', 23 + 'country_code', 24 + 'cc2', 25 + 'admin1_code', 26 + 'admin2_code', 27 + 'admin3_code', 28 + 'admin4_code', 29 + 'population', 30 + 'elevation', 31 + 'dem', 32 + 'timezone', 33 + 'modification_date' 34 + ] 35 + 36 + # Indices of the required columns (1,9,11,12,3,5,6) in the data file 37 + required_indices = [0, 8, 10, 11, 2, 4, 5] 38 + 39 + # Extract the corresponding column names 40 + required_columns = [column_names[i] for i in required_indices] 41 + 42 + # List to store all rows as dictionaries 43 + data = [] 44 + 45 + # Read the file and process each line 46 + num_cities = '500' 47 + with open(f"cities{num_cities}.txt", 'r') as file: 48 + for line in file: 49 + line = line.strip() 50 + if not line: 51 + continue # Skip empty lines 52 + 53 + fields = line.split('\t') 54 + if len(fields) < max(required_indices) + 1: 55 + continue # Skip lines with insufficient data 56 + 57 + row = {} 58 + for idx, col in zip(required_indices, required_columns): 59 + row[col] = fields[idx] 60 + 61 + data.append(row) 62 + 63 + city_nodes = [( 64 + float(r['latitude']), 65 + float(r['longitude']), 66 + f"{r['country_code']}:{r['admin1_code']}:{r['admin2_code']}" 67 + ) for r in data] 68 + 69 + a_col_names = [ 70 + 'code', 71 + 'name', 72 + 'asciiname', 73 + 'geonameId' 74 + ] 75 + regions=dict() 76 + 77 + with open('admin2Codes.txt', 'r') as file: 78 + for line in file: 79 + line = line.strip() 80 + if not line: 81 + continue # Skip empty lines 82 + 83 + fields = line.split('\t') 84 + regions[fields[0].replace('.',':')] = fields[1] 85 + 86 + # 1. Build the index 87 + print("Building index...") 88 + indexer = GeoIndexer(city_nodes) 89 + print("Index built successfully.") 90 + 91 + with open(f"{directory}.clusters.json") as file: 92 + clusters = json.load(file)[0] 93 + 94 + rbc = dict() 95 + unk = [] 96 + 97 + towns = set() 98 + 99 + 100 + for c in clusters: 101 + for n in c: 102 + # 3. Find the nearest node 103 + nearest_node = indexer.find_nearest( 104 + float(n['lat']), 105 + float(n['lon']) 106 + ) 107 + 108 + print(f"\nQuery Point: ({n['lat']}, {n['lon']})") 109 + print(f"Nearest Node Found: {nearest_node}") 110 + 111 + if nearest_node[2] in regions: 112 + print(f"Derived Admin Region: {regions[nearest_node[2]]}") 113 + if nearest_node[2] in rbc: 114 + rbc[nearest_node[2]].append(n) 115 + else: 116 + rbc[nearest_node[2]] = [n] 117 + 118 + towns.add(nearest_node) 119 + 120 + else: 121 + print(f"Unknown Admin Region: {nearest_node[2]}") 122 + unk.append(n) 123 + 124 + with open(f"{directory}.region.clusters.{num_cities}.json", 'w') as file: 125 + l = list(rbc.values()) 126 + l.append(unk) 127 + json.dump([l], file) 128 + 129 + with open(f"{directory}.close_cities.{num_cities}.json", 'w') as file: 130 + json.dump(list(towns), file) 131 + 132 + if __name__ == '__main__': 133 + main()
+24
preprocessing/srv/isocountry.py
··· 1 + import json 2 + 3 + def parse_country_data(file_path): 4 + countries = dict() 5 + 6 + with open(file_path, 'r', encoding='utf-8') as file: 7 + for line in file: 8 + line = line.strip() 9 + if not line or line.startswith('#'): 10 + continue # Skip comments and empty lines 11 + 12 + fields = line.split() 13 + if len(fields) >= 5: 14 + iso_code = fields[0] 15 + country_name = fields[4] 16 + countries[iso_code] = country_name 17 + 18 + return countries 19 + 20 + if __name__ == "__main__": 21 + file_path = 'countryInfo.txt' # Replace with your actual file path 22 + result = parse_country_data(file_path) 23 + with open('countries.json', 'w') as f: 24 + json.dump(result, f, indent=2, ensure_ascii=False)
+136
preprocessing/srv/kdtree.py
··· 1 + import math 2 + 3 + class _Node: 4 + """A private helper class for a node in the Kd-tree.""" 5 + def __init__(self, point, axis, left=None, right=None): 6 + self.point = point 7 + self.axis = axis 8 + self.left = left 9 + self.right = right 10 + 11 + class GeoIndexer: 12 + """ 13 + Indexes a collection of latitude/longitude nodes using a Kd-tree 14 + for efficient approximate nearest neighbor searches. 15 + """ 16 + 17 + def __init__(self, nodes: list[tuple[float, float, str]]): 18 + """ 19 + Initializes the index and builds the Kd-tree from the nodes. 20 + 21 + Args: 22 + nodes: A list of unique (latitude, longitude) tuples. 23 + """ 24 + if not nodes: 25 + raise ValueError("Node list cannot be empty.") 26 + 27 + # k=2 for (latitude, longitude) 28 + self._k = 2 29 + self._root = self._build_kdtree(nodes) 30 + 31 + def _build_kdtree(self, points: list[tuple[float, float, str]], depth: int = 0): 32 + """Recursively builds the Kd-tree.""" 33 + if not points: 34 + return None 35 + 36 + # Determine the axis to split on 37 + axis = depth % self._k 38 + 39 + # Sort points by the current axis and find the median 40 + points.sort(key=lambda point: point[axis]) 41 + median_idx = len(points) // 2 42 + median_point = points[median_idx] 43 + 44 + # Create a new node and recursively build subtrees 45 + return _Node( 46 + point=median_point, 47 + axis=axis, 48 + left=self._build_kdtree(points[:median_idx], depth + 1), 49 + right=self._build_kdtree(points[median_idx + 1:], depth + 1) 50 + ) 51 + 52 + def find_nearest(self, lat: float, lon: float) -> tuple[float, float, str]: 53 + """ 54 + Finds the node in the index that is closest to the given 55 + latitude and longitude. 56 + 57 + Args: 58 + lat: The latitude of the query point. 59 + lon: The longitude of the query point. 60 + 61 + Returns: 62 + The (latitude, longitude) tuple of the nearest node. 63 + """ 64 + query_point = (lat, lon) 65 + if self._root is None: 66 + raise Exception("The tree is empty.") 67 + 68 + # The state is a list [best_point, best_sq_dist] so it can be modified 69 + # by the nested helper function (pass-by-reference behavior). 70 + state = [None, float('inf')] 71 + 72 + self._find_nearest_recursive(self._root, query_point, state) 73 + 74 + return state[0] 75 + 76 + def _find_nearest_recursive(self, node: _Node, query_point: tuple[float, float, str], state: list): 77 + """Recursively searches the tree for the nearest neighbor.""" 78 + if node is None: 79 + return 80 + 81 + # Calculate squared Euclidean distance between current node and query point 82 + dist_sq = (node.point[0] - query_point[0])**2 + (node.point[1] - query_point[1])**2 83 + 84 + # If this node is closer, update our best guess 85 + if dist_sq < state[1]: 86 + state[0] = node.point 87 + state[1] = dist_sq 88 + 89 + axis = node.axis 90 + # Determine which subtree to search first 91 + if query_point[axis] < node.point[axis]: 92 + primary_child = node.left 93 + secondary_child = node.right 94 + else: 95 + primary_child = node.right 96 + secondary_child = node.left 97 + 98 + # Recurse into the primary subtree 99 + self._find_nearest_recursive(primary_child, query_point, state) 100 + 101 + # Check if we need to search the secondary subtree (pruning step) 102 + # We only need to search the other side if the distance from the query 103 + # point to the splitting plane is less than our current best distance. 104 + dist_to_plane_sq = (node.point[axis] - query_point[axis]) ** 2 105 + 106 + if dist_to_plane_sq < state[1]: 107 + self._find_nearest_recursive(secondary_child, query_point, state) 108 + 109 + 110 + # --- Example Usage --- 111 + 112 + if __name__ == '__main__': 113 + # A list of nodes (e.g., locations in a city) 114 + # Format: (latitude, longitude) 115 + city_nodes = [ 116 + (34.0522, -118.2437, "LA"), # Los Angeles 117 + (34.0722, -118.2337, "LA"), # Near Dodger Stadium 118 + (34.0430, -118.2673, "LA"), # Near Crypto.com Arena 119 + (34.1522, -118.2553, "LA"), # Griffith Observatory 120 + (34.1016, -118.3409, "LA"), # Hollywood Walk of Fame 121 + (34.0620, -118.3617, "LA"), # LACMA 122 + ] 123 + 124 + # 1. Build the index 125 + print("Building index...") 126 + indexer = GeoIndexer(city_nodes) 127 + print("Index built successfully.") 128 + 129 + # 2. Define a query point 130 + query_lat, query_lon = 34.0800, -118.3000 # A point in Koreatown 131 + 132 + # 3. Find the nearest node 133 + nearest_node = indexer.find_nearest(query_lat, query_lon) 134 + 135 + print(f"\nQuery Point: ({query_lat}, {query_lon})") 136 + print(f"Nearest Node Found: {nearest_node}")
+274
preprocessing/srv/phmeta.py
··· 1 + import os 2 + import sys 3 + import json 4 + import argparse 5 + from datetime import datetime, timezone, timedelta 6 + from collections import defaultdict 7 + from PIL import Image 8 + from PIL.ExifTags import TAGS 9 + 10 + from kdtree import GeoIndexer 11 + 12 + from photos_api_client import Client 13 + from photos_api_client.api.default import post_album, post_batch_photos_in_album 14 + from photos_api_client.models import PostAlbumBody, PostAlbumBodyAlbum 15 + from photos_api_client.models import PostBatchPhotosInAlbumBody, PostBatchPhotosInAlbumBodyAlbum, PostBatchPhotosInAlbumBodyContentsItem 16 + 17 + 18 + # Define GMT+10 timezone 19 + GMT10 = timezone(timedelta(hours=10)) 20 + 21 + def decimal_coords(coords, ref): 22 + """Convert GPS coordinates from DMS to decimal degrees.""" 23 + decimal_degrees = float(coords[0]) + float(coords[1])/60 + float(coords[2])/3600 24 + if ref in ('S', 'W'): 25 + decimal_degrees = -decimal_degrees 26 + return decimal_degrees 27 + 28 + def get_image_info(filepath, date_source): 29 + """Extracts metadata and date from an image file.""" 30 + try: 31 + img = Image.open(filepath) 32 + width, height = img.size 33 + orientation = 1 34 + timestamp = None 35 + lat = None 36 + lon = None 37 + 38 + # Get date from EXIF 39 + if date_source == 'exif': 40 + if hasattr(img, '_getexif'): 41 + exif_data = img._getexif() 42 + if exif_data: 43 + for tag, value in exif_data.items(): 44 + tag_name = TAGS.get(tag, tag) 45 + if tag_name == 'Orientation': 46 + orientation = value 47 + # DateTimeOriginal is preferred as it's when the photo was taken 48 + if tag_name == 'DateTimeOriginal' and value: 49 + # Format is 'YYYY:MM:DD HH:MM:SS' 50 + timestamp = datetime.strptime(value, '%Y:%m:%d %H:%M:%S') 51 + # Fallback to DateTime 52 + elif tag_name == 'DateTime' and not timestamp and value: 53 + timestamp = datetime.strptime(value, '%Y:%m:%d %H:%M:%S') 54 + if tag_name =='GPSInfo': 55 + print("Yloo") 56 + # Extract latitude and longitude 57 + gps_info = value 58 + lat_coords = gps_info[2] 59 + lat_ref = gps_info[1] 60 + lon_coords = gps_info[4] 61 + lon_ref = gps_info[3] 62 + lat = decimal_coords(lat_coords, lat_ref) 63 + lon = decimal_coords(lon_coords, lon_ref) 64 + 65 + # If no EXIF date or if source is modtime, use file modification time 66 + if not timestamp: 67 + mod_time = os.path.getmtime(filepath) 68 + timestamp = datetime.fromtimestamp(mod_time) 69 + 70 + metadata = { 71 + "width": width, 72 + "height": height, 73 + "orientation": orientation, 74 + "lat": lat, 75 + "lon": lon, 76 + } 77 + return metadata, timestamp 78 + 79 + except Exception as e: 80 + print(f"Error processing {filepath}: {e}") 81 + return None, None 82 + 83 + def create_store_json(directory, date_source, backend_api): 84 + """Creates a store.json file in a directory based on its image contents, segmented by day.""" 85 + 86 + # Define the column names in order based on the schema 87 + column_names = [ 88 + 'geonameid', 89 + 'name', 90 + 'asciiname', 91 + 'alternatenames', 92 + 'latitude', 93 + 'longitude', 94 + 'feature_class', 95 + 'feature_code', 96 + 'country_code', 97 + 'cc2', 98 + 'admin1_code', 99 + 'admin2_code', 100 + 'admin3_code', 101 + 'admin4_code', 102 + 'population', 103 + 'elevation', 104 + 'dem', 105 + 'timezone', 106 + 'modification_date' 107 + ] 108 + 109 + # Indices of the required columns (1,9,11,12,3,5,6) in the data file 110 + required_indices = [0, 8, 10, 11, 2, 4, 5] 111 + 112 + # Extract the corresponding column names 113 + required_columns = [column_names[i] for i in required_indices] 114 + 115 + # List to store all rows as dictionaries 116 + data = [] 117 + 118 + # Read the file and process each line 119 + num_cities = '500' 120 + with open(f"/mnt/t7/adam/geocoding/cities{num_cities}.txt", 'r') as file: 121 + for line in file: 122 + line = line.strip() 123 + if not line: 124 + continue # Skip empty lines 125 + 126 + fields = line.split('\t') 127 + if len(fields) < max(required_indices) + 1: 128 + continue # Skip lines with insufficient data 129 + 130 + row = {} 131 + for idx, col in zip(required_indices, required_columns): 132 + row[col] = fields[idx] 133 + 134 + data.append(row) 135 + 136 + city_nodes = [( 137 + float(r['latitude']), 138 + float(r['longitude']), 139 + f"{r['country_code']}:{r['admin1_code']}:{r['admin2_code']}" 140 + ) for r in data] 141 + 142 + a_col_names = [ 143 + 'code', 144 + 'name', 145 + 'asciiname', 146 + 'geonameId' 147 + ] 148 + regions=dict() 149 + 150 + with open('/mnt/t7/adam/geocoding/admin2Codes.txt', 'r') as file: 151 + for line in file: 152 + line = line.strip() 153 + if not line: 154 + continue # Skip empty lines 155 + 156 + fields = line.split('\t') 157 + regions[fields[0].replace('.',':')] = fields[1] 158 + 159 + # 1. Build the index 160 + print("Building index...") 161 + indexer = GeoIndexer(city_nodes) 162 + print("Index built successfully.") 163 + 164 + images_by_day = defaultdict(list) 165 + total_images = 0 166 + 167 + for filename in sorted(os.listdir(directory)): 168 + if filename.lower().endswith(('.jpg', '.jpeg')): 169 + print(filename) 170 + filepath = os.path.join(directory, filename) 171 + metadata, timestamp = get_image_info(filepath, date_source) 172 + nearest_node = None 173 + if metadata and timestamp: 174 + # Convert timestamp to GMT+10 and get the date part 175 + local_dt = timestamp.astimezone(GMT10) 176 + day = local_dt.date() 177 + if metadata['lat'] is not None and metadata['lon'] is not None: 178 + nearest_node = indexer.find_nearest( 179 + float(metadata['lat']), 180 + float(metadata['lon']) 181 + ) 182 + 183 + images_by_day[day].append({ 184 + "file": filename, 185 + "timestamp": timestamp.isoformat(), 186 + # reconstruct with minimal info 187 + "metadata": { 188 + "width": metadata['width'], 189 + "height": metadata['height'], 190 + "orientation": metadata['orientation'], 191 + "region": nearest_node[2], 192 + } if nearest_node is not None else { 193 + "width": metadata['width'], 194 + "height": metadata['height'], 195 + "orientation": metadata['orientation'] 196 + } 197 + }) 198 + total_images += 1 199 + 200 + if not total_images: 201 + return 202 + 203 + dir_name = os.path.basename(directory) 204 + segments = [] 205 + # Sort days to have segments in chronological order 206 + for day in sorted(images_by_day.keys()): 207 + segment_images = images_by_day[day] 208 + segments.append({ 209 + "segmentId": f"{dir_name}_{day.strftime('%Y-%m-%d')}", 210 + "header": day.strftime('%a, %B %d, %Y'), 211 + "images": segment_images 212 + }) 213 + 214 + store_data = [ 215 + { 216 + "sectionId": dir_name, 217 + "totalImages": total_images, 218 + "segments": segments 219 + } 220 + ] 221 + 222 + if backend_api: 223 + client = Client(base_url="http://adams-laptop:8000") 224 + photoRecords = [fill_photo_record(image) 225 + for seg in segments for image in seg] 226 + post_batch_photos_in_album.sync(client, body=PostBatchPhotosInAlbumBody.from_dict({ 227 + "album": PostBatchPhotosInAlbumBodyAlbum.from_dict({ 228 + "title": dir_name, 229 + "year": 2025 230 + }), 231 + "contents": photoRecords 232 + })) 233 + else: 234 + store_json_path = os.path.join(directory, 'store.geo.json') 235 + with open(store_json_path, 'w') as f: 236 + json.dump(store_data, f, indent=2) 237 + print(f"Created {store_json_path}") 238 + 239 + def fill_photo_record(image): 240 + imgToPost = PostBatchPhotosInAlbumBodyContentsItem() 241 + imgToPost.file_name = image.file 242 + imgToPost.date_created = image.timestamp 243 + imgToPost.date_modified = image.timestamp 244 + imgToPost.width = image.metadata.width 245 + imgToPost.height = image.metadata.width 246 + imgToPost.orientation = image.metadata.orientation 247 + imgToPost.region = image.region 248 + return imgToPost 249 + 250 + def main(): 251 + """Main function to traverse directories and generate store.json files.""" 252 + parser = argparse.ArgumentParser(description="Generate store.json for photo directories.") 253 + parser.add_argument("target_directory", help="The root directory to process.") 254 + parser.add_argument("--date-source", choices=['exif', 'modtime'], default='exif', 255 + help="Source for image date (exif or modtime). Defaults to exif.") 256 + parser.add_argument("--single-dir", action='store_true', 257 + help="whether to operate on only a single directory") 258 + parser.add_argument("--backend-api", default=None, 259 + help="use the api client rather than saving json to filesystem") 260 + args = parser.parse_args() 261 + 262 + root_dir = args.target_directory 263 + if not os.path.isdir(root_dir): 264 + print(f"Error: {root_dir} is not a valid directory.") 265 + sys.exit(1) 266 + 267 + if args.single_dir: 268 + create_store_json(root_dir, args.date_source, args.backend_api) 269 + else: 270 + for dirpath, _, _ in os.walk(root_dir): 271 + create_store_json(dirpath, args.date_source, args.backend_api) 272 + 273 + if __name__ == "__main__": 274 + main()
+365 -20
preprocessing/uv.lock
··· 1 1 version = 1 2 2 revision = 3 3 - requires-python = ">=3.14" 3 + requires-python = ">=3.9" 4 + resolution-markers = [ 5 + "python_full_version >= '3.10'", 6 + "python_full_version < '3.10'", 7 + ] 4 8 5 9 [[package]] 6 10 name = "annotated-types" ··· 16 20 version = "4.12.0" 17 21 source = { registry = "https://pypi.org/simple" } 18 22 dependencies = [ 23 + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, 19 24 { name = "idna" }, 25 + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 20 26 ] 21 27 sdist = { url = "https://files.pythonhosted.org/packages/16/ce/8a777047513153587e5434fd752e89334ac33e379aa3497db860eeb60377/anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0", size = 228266, upload-time = "2025-11-28T23:37:38.911Z" } 22 28 wheels = [ ··· 43 49 44 50 [[package]] 45 51 name = "click" 52 + version = "8.1.8" 53 + source = { registry = "https://pypi.org/simple" } 54 + resolution-markers = [ 55 + "python_full_version < '3.10'", 56 + ] 57 + dependencies = [ 58 + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, 59 + ] 60 + sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } 61 + wheels = [ 62 + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, 63 + ] 64 + 65 + [[package]] 66 + name = "click" 46 67 version = "8.3.1" 47 68 source = { registry = "https://pypi.org/simple" } 69 + resolution-markers = [ 70 + "python_full_version >= '3.10'", 71 + ] 48 72 dependencies = [ 49 - { name = "colorama", marker = "sys_platform == 'win32'" }, 73 + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, 50 74 ] 51 75 sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } 52 76 wheels = [ ··· 60 84 sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } 61 85 wheels = [ 62 86 { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, 87 + ] 88 + 89 + [[package]] 90 + name = "exceptiongroup" 91 + version = "1.3.1" 92 + source = { registry = "https://pypi.org/simple" } 93 + dependencies = [ 94 + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 95 + ] 96 + sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } 97 + wheels = [ 98 + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, 63 99 ] 64 100 65 101 [[package]] ··· 122 158 123 159 [[package]] 124 160 name = "markdown-it-py" 161 + version = "3.0.0" 162 + source = { registry = "https://pypi.org/simple" } 163 + resolution-markers = [ 164 + "python_full_version < '3.10'", 165 + ] 166 + dependencies = [ 167 + { name = "mdurl", marker = "python_full_version < '3.10'" }, 168 + ] 169 + sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } 170 + wheels = [ 171 + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, 172 + ] 173 + 174 + [[package]] 175 + name = "markdown-it-py" 125 176 version = "4.0.0" 126 177 source = { registry = "https://pypi.org/simple" } 178 + resolution-markers = [ 179 + "python_full_version >= '3.10'", 180 + ] 127 181 dependencies = [ 128 - { name = "mdurl" }, 182 + { name = "mdurl", marker = "python_full_version >= '3.10'" }, 129 183 ] 130 184 sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } 131 185 wheels = [ ··· 138 192 source = { registry = "https://pypi.org/simple" } 139 193 sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } 140 194 wheels = [ 195 + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, 196 + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, 197 + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, 198 + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, 199 + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, 200 + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, 201 + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, 202 + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, 203 + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, 204 + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, 205 + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, 206 + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, 207 + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, 208 + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, 209 + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, 210 + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, 211 + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, 212 + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, 213 + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, 214 + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, 215 + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, 216 + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, 217 + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, 218 + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, 219 + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, 220 + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, 221 + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, 222 + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, 223 + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, 224 + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, 225 + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, 226 + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, 227 + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, 228 + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, 229 + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, 230 + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, 231 + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, 232 + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, 233 + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, 234 + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, 235 + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, 236 + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, 237 + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, 238 + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, 239 + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, 240 + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, 241 + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, 242 + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, 243 + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, 244 + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, 245 + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, 246 + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, 247 + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, 248 + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, 249 + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, 141 250 { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, 142 251 { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, 143 252 { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, ··· 160 269 { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, 161 270 { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, 162 271 { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, 272 + { url = "https://files.pythonhosted.org/packages/56/23/0d8c13a44bde9154821586520840643467aee574d8ce79a17da539ee7fed/markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26", size = 11623, upload-time = "2025-09-27T18:37:29.296Z" }, 273 + { url = "https://files.pythonhosted.org/packages/fd/23/07a2cb9a8045d5f3f0890a8c3bc0859d7a47bfd9a560b563899bec7b72ed/markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc", size = 12049, upload-time = "2025-09-27T18:37:30.234Z" }, 274 + { url = "https://files.pythonhosted.org/packages/bc/e4/6be85eb81503f8e11b61c0b6369b6e077dcf0a74adbd9ebf6b349937b4e9/markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c", size = 21923, upload-time = "2025-09-27T18:37:31.177Z" }, 275 + { url = "https://files.pythonhosted.org/packages/6f/bc/4dc914ead3fe6ddaef035341fee0fc956949bbd27335b611829292b89ee2/markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42", size = 20543, upload-time = "2025-09-27T18:37:32.168Z" }, 276 + { url = "https://files.pythonhosted.org/packages/89/6e/5fe81fbcfba4aef4093d5f856e5c774ec2057946052d18d168219b7bd9f9/markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b", size = 20585, upload-time = "2025-09-27T18:37:33.166Z" }, 277 + { url = "https://files.pythonhosted.org/packages/f6/f6/e0e5a3d3ae9c4020f696cd055f940ef86b64fe88de26f3a0308b9d3d048c/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758", size = 21387, upload-time = "2025-09-27T18:37:34.185Z" }, 278 + { url = "https://files.pythonhosted.org/packages/c8/25/651753ef4dea08ea790f4fbb65146a9a44a014986996ca40102e237aa49a/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2", size = 20133, upload-time = "2025-09-27T18:37:35.138Z" }, 279 + { url = "https://files.pythonhosted.org/packages/dc/0a/c3cf2b4fef5f0426e8a6d7fce3cb966a17817c568ce59d76b92a233fdbec/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d", size = 20588, upload-time = "2025-09-27T18:37:36.096Z" }, 280 + { url = "https://files.pythonhosted.org/packages/cd/1b/a7782984844bd519ad4ffdbebbba2671ec5d0ebbeac34736c15fb86399e8/markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7", size = 14566, upload-time = "2025-09-27T18:37:37.09Z" }, 281 + { url = "https://files.pythonhosted.org/packages/18/1f/8d9c20e1c9440e215a44be5ab64359e207fcb4f675543f1cf9a2a7f648d0/markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e", size = 15053, upload-time = "2025-09-27T18:37:38.054Z" }, 282 + { url = "https://files.pythonhosted.org/packages/4e/d3/fe08482b5cd995033556d45041a4f4e76e7f0521112a9c9991d40d39825f/markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8", size = 13928, upload-time = "2025-09-27T18:37:39.037Z" }, 163 283 ] 164 284 165 285 [[package]] ··· 173 293 174 294 [[package]] 175 295 name = "openapi-python-client" 296 + version = "0.26.2" 297 + source = { registry = "https://pypi.org/simple" } 298 + resolution-markers = [ 299 + "python_full_version < '3.10'", 300 + ] 301 + dependencies = [ 302 + { name = "attrs", marker = "python_full_version < '3.10'" }, 303 + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, 304 + { name = "httpx", marker = "python_full_version < '3.10'" }, 305 + { name = "jinja2", marker = "python_full_version < '3.10'" }, 306 + { name = "pydantic", marker = "python_full_version < '3.10'" }, 307 + { name = "python-dateutil", marker = "python_full_version < '3.10'" }, 308 + { name = "ruamel-yaml", marker = "python_full_version < '3.10'" }, 309 + { name = "ruff", version = "0.13.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, 310 + { name = "shellingham", marker = "python_full_version < '3.10'" }, 311 + { name = "typer", version = "0.19.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, 312 + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, 313 + ] 314 + sdist = { url = "https://files.pythonhosted.org/packages/a1/dd/eda1ecf8bec21dd92f4729a743adb1e496b2bb67e2b60ce6652d74efc477/openapi_python_client-0.26.2.tar.gz", hash = "sha256:99ed575573b49c322456052a344bcdb352edc520c3b7a7fed62347ab6a03c60b", size = 126186, upload-time = "2025-10-06T14:20:42.225Z" } 315 + wheels = [ 316 + { url = "https://files.pythonhosted.org/packages/a5/97/5cbc4147d67bf4dbe428f4c41cfac808e2b61c2c1a71ce2ec52a4f604569/openapi_python_client-0.26.2-py3-none-any.whl", hash = "sha256:5b615bf359872a77c220797cce0214d02ab7552d9ef5bc2c11b24d8dda609ad4", size = 183641, upload-time = "2025-10-06T14:20:40.457Z" }, 317 + ] 318 + 319 + [[package]] 320 + name = "openapi-python-client" 176 321 version = "0.28.0" 177 322 source = { registry = "https://pypi.org/simple" } 323 + resolution-markers = [ 324 + "python_full_version >= '3.10'", 325 + ] 178 326 dependencies = [ 179 - { name = "attrs" }, 180 - { name = "colorama", marker = "sys_platform == 'win32'" }, 181 - { name = "httpx" }, 182 - { name = "jinja2" }, 183 - { name = "pydantic" }, 184 - { name = "python-dateutil" }, 185 - { name = "ruamel-yaml" }, 186 - { name = "ruff" }, 187 - { name = "shellingham" }, 188 - { name = "typer" }, 327 + { name = "attrs", marker = "python_full_version >= '3.10'" }, 328 + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, 329 + { name = "httpx", marker = "python_full_version >= '3.10'" }, 330 + { name = "jinja2", marker = "python_full_version >= '3.10'" }, 331 + { name = "pydantic", marker = "python_full_version >= '3.10'" }, 332 + { name = "python-dateutil", marker = "python_full_version >= '3.10'" }, 333 + { name = "ruamel-yaml", marker = "python_full_version >= '3.10'" }, 334 + { name = "ruff", version = "0.14.10", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 335 + { name = "shellingham", marker = "python_full_version >= '3.10'" }, 336 + { name = "typer", version = "0.20.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 189 337 ] 190 338 sdist = { url = "https://files.pythonhosted.org/packages/f8/5f/78cf6f3aa1015b27ae725593eb04f98caa934d4ab3681efbe596437f3edd/openapi_python_client-0.28.0.tar.gz", hash = "sha256:e0162b17bfee9b990d4dc38b256af129e0035e551a33dac8e4d5d4abfdb7c3d2", size = 125884, upload-time = "2025-12-03T20:54:20.57Z" } 191 339 wheels = [ ··· 197 345 version = "0.1.0" 198 346 source = { virtual = "." } 199 347 dependencies = [ 200 - { name = "openapi-python-client" }, 348 + { name = "openapi-python-client", version = "0.26.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, 349 + { name = "openapi-python-client", version = "0.28.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 201 350 ] 202 351 203 352 [package.metadata] 204 - requires-dist = [{ name = "openapi-python-client", specifier = ">=0.28.0" }] 353 + requires-dist = [{ name = "openapi-python-client", specifier = ">=0.26.2" }] 205 354 206 355 [[package]] 207 356 name = "pydantic" ··· 227 376 ] 228 377 sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } 229 378 wheels = [ 379 + { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, 380 + { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, 381 + { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, 382 + { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, 383 + { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, 384 + { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, 385 + { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, 386 + { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, 387 + { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, 388 + { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, 389 + { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, 390 + { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, 391 + { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, 392 + { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, 393 + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, 394 + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, 395 + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, 396 + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, 397 + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, 398 + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, 399 + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, 400 + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, 401 + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, 402 + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, 403 + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, 404 + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, 405 + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, 406 + { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, 407 + { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, 408 + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, 409 + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, 410 + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, 411 + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, 412 + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, 413 + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, 414 + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, 415 + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, 416 + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, 417 + { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, 418 + { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, 419 + { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, 420 + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, 421 + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, 422 + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, 423 + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, 424 + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, 425 + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, 426 + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, 427 + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, 428 + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, 429 + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, 430 + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, 431 + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, 432 + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, 433 + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, 230 434 { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, 231 435 { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, 232 436 { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, ··· 255 459 { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, 256 460 { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, 257 461 { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, 462 + { url = "https://files.pythonhosted.org/packages/54/db/160dffb57ed9a3705c4cbcbff0ac03bdae45f1ca7d58ab74645550df3fbd/pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf", size = 2107999, upload-time = "2025-11-04T13:42:03.885Z" }, 463 + { url = "https://files.pythonhosted.org/packages/a3/7d/88e7de946f60d9263cc84819f32513520b85c0f8322f9b8f6e4afc938383/pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5", size = 1929745, upload-time = "2025-11-04T13:42:06.075Z" }, 464 + { url = "https://files.pythonhosted.org/packages/d5/c2/aef51e5b283780e85e99ff19db0f05842d2d4a8a8cd15e63b0280029b08f/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d", size = 1920220, upload-time = "2025-11-04T13:42:08.457Z" }, 465 + { url = "https://files.pythonhosted.org/packages/c7/97/492ab10f9ac8695cd76b2fdb24e9e61f394051df71594e9bcc891c9f586e/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60", size = 2067296, upload-time = "2025-11-04T13:42:10.817Z" }, 466 + { url = "https://files.pythonhosted.org/packages/ec/23/984149650e5269c59a2a4c41d234a9570adc68ab29981825cfaf4cfad8f4/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82", size = 2231548, upload-time = "2025-11-04T13:42:13.843Z" }, 467 + { url = "https://files.pythonhosted.org/packages/71/0c/85bcbb885b9732c28bec67a222dbed5ed2d77baee1f8bba2002e8cd00c5c/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5", size = 2362571, upload-time = "2025-11-04T13:42:16.208Z" }, 468 + { url = "https://files.pythonhosted.org/packages/c0/4a/412d2048be12c334003e9b823a3fa3d038e46cc2d64dd8aab50b31b65499/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3", size = 2068175, upload-time = "2025-11-04T13:42:18.911Z" }, 469 + { url = "https://files.pythonhosted.org/packages/73/f4/c58b6a776b502d0a5540ad02e232514285513572060f0d78f7832ca3c98b/pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425", size = 2177203, upload-time = "2025-11-04T13:42:22.578Z" }, 470 + { url = "https://files.pythonhosted.org/packages/ed/ae/f06ea4c7e7a9eead3d165e7623cd2ea0cb788e277e4f935af63fc98fa4e6/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504", size = 2148191, upload-time = "2025-11-04T13:42:24.89Z" }, 471 + { url = "https://files.pythonhosted.org/packages/c1/57/25a11dcdc656bf5f8b05902c3c2934ac3ea296257cc4a3f79a6319e61856/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5", size = 2343907, upload-time = "2025-11-04T13:42:27.683Z" }, 472 + { url = "https://files.pythonhosted.org/packages/96/82/e33d5f4933d7a03327c0c43c65d575e5919d4974ffc026bc917a5f7b9f61/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3", size = 2322174, upload-time = "2025-11-04T13:42:30.776Z" }, 473 + { url = "https://files.pythonhosted.org/packages/81/45/4091be67ce9f469e81656f880f3506f6a5624121ec5eb3eab37d7581897d/pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460", size = 1990353, upload-time = "2025-11-04T13:42:33.111Z" }, 474 + { url = "https://files.pythonhosted.org/packages/44/8a/a98aede18db6e9cd5d66bcacd8a409fcf8134204cdede2e7de35c5a2c5ef/pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b", size = 2015698, upload-time = "2025-11-04T13:42:35.484Z" }, 475 + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, 476 + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, 477 + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, 478 + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, 479 + { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, 480 + { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, 481 + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, 482 + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, 483 + { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, 484 + { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, 485 + { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, 486 + { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, 487 + { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, 488 + { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, 489 + { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, 490 + { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, 491 + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, 492 + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, 493 + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, 494 + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, 495 + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, 496 + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, 497 + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, 498 + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, 258 499 ] 259 500 260 501 [[package]] ··· 283 524 version = "14.2.0" 284 525 source = { registry = "https://pypi.org/simple" } 285 526 dependencies = [ 286 - { name = "markdown-it-py" }, 527 + { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, 528 + { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 287 529 { name = "pygments" }, 288 530 ] 289 531 sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } ··· 309 551 source = { registry = "https://pypi.org/simple" } 310 552 sdist = { url = "https://files.pythonhosted.org/packages/ea/97/60fda20e2fb54b83a61ae14648b0817c8f5d84a3821e40bfbdae1437026a/ruamel_yaml_clib-0.2.15.tar.gz", hash = "sha256:46e4cc8c43ef6a94885f72512094e482114a8a706d3c555a34ed4b0d20200600", size = 225794, upload-time = "2025-11-16T16:12:59.761Z" } 311 553 wheels = [ 554 + { url = "https://files.pythonhosted.org/packages/f7/5a/4ab767cd42dcd65b83c323e1620d7c01ee60a52f4032fb7b61501f45f5c2/ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88eea8baf72f0ccf232c22124d122a7f26e8a24110a0273d9bcddcb0f7e1fa03", size = 147454, upload-time = "2025-11-16T16:13:02.54Z" }, 555 + { url = "https://files.pythonhosted.org/packages/40/44/184173ac1e74fd35d308108bcbf83904d6ef8439c70763189225a166b238/ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b6f7d74d094d1f3a4e157278da97752f16ee230080ae331fcc219056ca54f77", size = 132467, upload-time = "2025-11-16T16:13:03.539Z" }, 556 + { url = "https://files.pythonhosted.org/packages/49/1b/2d2077a25fe682ae335007ca831aff42e3cbc93c14066675cf87a6c7fc3e/ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4be366220090d7c3424ac2b71c90d1044ea34fca8c0b88f250064fd06087e614", size = 693454, upload-time = "2025-11-16T20:22:41.083Z" }, 557 + { url = "https://files.pythonhosted.org/packages/90/16/e708059c4c429ad2e33be65507fc1730641e5f239fb2964efc1ba6edea94/ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f66f600833af58bea694d5892453f2270695b92200280ee8c625ec5a477eed3", size = 700345, upload-time = "2025-11-16T16:13:04.771Z" }, 558 + { url = "https://files.pythonhosted.org/packages/d9/79/0e8ef51df1f0950300541222e3332f20707a9c210b98f981422937d1278c/ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da3d6adadcf55a93c214d23941aef4abfd45652110aed6580e814152f385b862", size = 731306, upload-time = "2025-11-16T16:13:06.312Z" }, 559 + { url = "https://files.pythonhosted.org/packages/a6/f4/2cdb54b142987ddfbd01fc45ac6bd882695fbcedb9d8bbf796adc3fc3746/ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e9fde97ecb7bb9c41261c2ce0da10323e9227555c674989f8d9eb7572fc2098d", size = 692415, upload-time = "2025-11-16T16:13:07.465Z" }, 560 + { url = "https://files.pythonhosted.org/packages/a0/07/40b5fc701cce8240a3e2d26488985d3bbdc446e9fe397c135528d412fea6/ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:05c70f7f86be6f7bee53794d80050a28ae7e13e4a0087c1839dcdefd68eb36b6", size = 705007, upload-time = "2025-11-16T20:22:42.856Z" }, 561 + { url = "https://files.pythonhosted.org/packages/82/19/309258a1df6192fb4a77ffa8eae3e8150e8d0ffa56c1b6fa92e450ba2740/ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f1d38cbe622039d111b69e9ca945e7e3efebb30ba998867908773183357f3ed", size = 723974, upload-time = "2025-11-16T16:13:08.72Z" }, 562 + { url = "https://files.pythonhosted.org/packages/67/3a/d6ee8263b521bfceb5cd2faeb904a15936480f2bb01c7ff74a14ec058ca4/ruamel_yaml_clib-0.2.15-cp310-cp310-win32.whl", hash = "sha256:fe239bdfdae2302e93bd6e8264bd9b71290218fff7084a9db250b55caaccf43f", size = 102836, upload-time = "2025-11-16T16:13:10.27Z" }, 563 + { url = "https://files.pythonhosted.org/packages/ed/03/92aeb5c69018387abc49a8bb4f83b54a0471d9ef48e403b24bac68f01381/ruamel_yaml_clib-0.2.15-cp310-cp310-win_amd64.whl", hash = "sha256:468858e5cbde0198337e6a2a78eda8c3fb148bdf4c6498eaf4bc9ba3f8e780bd", size = 121917, upload-time = "2025-11-16T16:13:12.145Z" }, 564 + { url = "https://files.pythonhosted.org/packages/2c/80/8ce7b9af532aa94dd83360f01ce4716264db73de6bc8efd22c32341f6658/ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c583229f336682b7212a43d2fa32c30e643d3076178fb9f7a6a14dde85a2d8bd", size = 147998, upload-time = "2025-11-16T16:13:13.241Z" }, 565 + { url = "https://files.pythonhosted.org/packages/53/09/de9d3f6b6701ced5f276d082ad0f980edf08ca67114523d1b9264cd5e2e0/ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56ea19c157ed8c74b6be51b5fa1c3aff6e289a041575f0556f66e5fb848bb137", size = 132743, upload-time = "2025-11-16T16:13:14.265Z" }, 566 + { url = "https://files.pythonhosted.org/packages/0e/f7/73a9b517571e214fe5c246698ff3ed232f1ef863c8ae1667486625ec688a/ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5fea0932358e18293407feb921d4f4457db837b67ec1837f87074667449f9401", size = 731459, upload-time = "2025-11-16T20:22:44.338Z" }, 567 + { url = "https://files.pythonhosted.org/packages/9b/a2/0dc0013169800f1c331a6f55b1282c1f4492a6d32660a0cf7b89e6684919/ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71831bd61fbdb7aa0399d5c4da06bea37107ab5c79ff884cc07f2450910262", size = 749289, upload-time = "2025-11-16T16:13:15.633Z" }, 568 + { url = "https://files.pythonhosted.org/packages/aa/ed/3fb20a1a96b8dc645d88c4072df481fe06e0289e4d528ebbdcc044ebc8b3/ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:617d35dc765715fa86f8c3ccdae1e4229055832c452d4ec20856136acc75053f", size = 777630, upload-time = "2025-11-16T16:13:16.898Z" }, 569 + { url = "https://files.pythonhosted.org/packages/60/50/6842f4628bc98b7aa4733ab2378346e1441e150935ad3b9f3c3c429d9408/ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b45498cc81a4724a2d42273d6cfc243c0547ad7c6b87b4f774cb7bcc131c98d", size = 744368, upload-time = "2025-11-16T16:13:18.117Z" }, 570 + { url = "https://files.pythonhosted.org/packages/d3/b0/128ae8e19a7d794c2e36130a72b3bb650ce1dd13fb7def6cf10656437dcf/ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:def5663361f6771b18646620fca12968aae730132e104688766cf8a3b1d65922", size = 745233, upload-time = "2025-11-16T20:22:45.833Z" }, 571 + { url = "https://files.pythonhosted.org/packages/75/05/91130633602d6ba7ce3e07f8fc865b40d2a09efd4751c740df89eed5caf9/ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:014181cdec565c8745b7cbc4de3bf2cc8ced05183d986e6d1200168e5bb59490", size = 770963, upload-time = "2025-11-16T16:13:19.344Z" }, 572 + { url = "https://files.pythonhosted.org/packages/fd/4b/fd4542e7f33d7d1bc64cc9ac9ba574ce8cf145569d21f5f20133336cdc8c/ruamel_yaml_clib-0.2.15-cp311-cp311-win32.whl", hash = "sha256:d290eda8f6ada19e1771b54e5706b8f9807e6bb08e873900d5ba114ced13e02c", size = 102640, upload-time = "2025-11-16T16:13:20.498Z" }, 573 + { url = "https://files.pythonhosted.org/packages/bb/eb/00ff6032c19c7537371e3119287999570867a0eafb0154fccc80e74bf57a/ruamel_yaml_clib-0.2.15-cp311-cp311-win_amd64.whl", hash = "sha256:bdc06ad71173b915167702f55d0f3f027fc61abd975bd308a0968c02db4a4c3e", size = 121996, upload-time = "2025-11-16T16:13:21.855Z" }, 574 + { url = "https://files.pythonhosted.org/packages/72/4b/5fde11a0722d676e469d3d6f78c6a17591b9c7e0072ca359801c4bd17eee/ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb15a2e2a90c8475df45c0949793af1ff413acfb0a716b8b94e488ea95ce7cff", size = 149088, upload-time = "2025-11-16T16:13:22.836Z" }, 575 + { url = "https://files.pythonhosted.org/packages/85/82/4d08ac65ecf0ef3b046421985e66301a242804eb9a62c93ca3437dc94ee0/ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64da03cbe93c1e91af133f5bec37fd24d0d4ba2418eaf970d7166b0a26a148a2", size = 134553, upload-time = "2025-11-16T16:13:24.151Z" }, 576 + { url = "https://files.pythonhosted.org/packages/b9/cb/22366d68b280e281a932403b76da7a988108287adff2bfa5ce881200107a/ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f6d3655e95a80325b84c4e14c080b2470fe4f33b6846f288379ce36154993fb1", size = 737468, upload-time = "2025-11-16T20:22:47.335Z" }, 577 + { url = "https://files.pythonhosted.org/packages/71/73/81230babf8c9e33770d43ed9056f603f6f5f9665aea4177a2c30ae48e3f3/ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71845d377c7a47afc6592aacfea738cc8a7e876d586dfba814501d8c53c1ba60", size = 753349, upload-time = "2025-11-16T16:13:26.269Z" }, 578 + { url = "https://files.pythonhosted.org/packages/61/62/150c841f24cda9e30f588ef396ed83f64cfdc13b92d2f925bb96df337ba9/ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e5499db1ccbc7f4b41f0565e4f799d863ea720e01d3e99fa0b7b5fcd7802c9", size = 788211, upload-time = "2025-11-16T16:13:27.441Z" }, 579 + { url = "https://files.pythonhosted.org/packages/30/93/e79bd9cbecc3267499d9ead919bd61f7ddf55d793fb5ef2b1d7d92444f35/ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4b293a37dc97e2b1e8a1aec62792d1e52027087c8eea4fc7b5abd2bdafdd6642", size = 743203, upload-time = "2025-11-16T16:13:28.671Z" }, 580 + { url = "https://files.pythonhosted.org/packages/8d/06/1eb640065c3a27ce92d76157f8efddb184bd484ed2639b712396a20d6dce/ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:512571ad41bba04eac7268fe33f7f4742210ca26a81fe0c75357fa682636c690", size = 747292, upload-time = "2025-11-16T20:22:48.584Z" }, 581 + { url = "https://files.pythonhosted.org/packages/a5/21/ee353e882350beab65fcc47a91b6bdc512cace4358ee327af2962892ff16/ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5e9f630c73a490b758bf14d859a39f375e6999aea5ddd2e2e9da89b9953486a", size = 771624, upload-time = "2025-11-16T16:13:29.853Z" }, 582 + { url = "https://files.pythonhosted.org/packages/57/34/cc1b94057aa867c963ecf9ea92ac59198ec2ee3a8d22a126af0b4d4be712/ruamel_yaml_clib-0.2.15-cp312-cp312-win32.whl", hash = "sha256:f4421ab780c37210a07d138e56dd4b51f8642187cdfb433eb687fe8c11de0144", size = 100342, upload-time = "2025-11-16T16:13:31.067Z" }, 583 + { url = "https://files.pythonhosted.org/packages/b3/e5/8925a4208f131b218f9a7e459c0d6fcac8324ae35da269cb437894576366/ruamel_yaml_clib-0.2.15-cp312-cp312-win_amd64.whl", hash = "sha256:2b216904750889133d9222b7b873c199d48ecbb12912aca78970f84a5aa1a4bc", size = 119013, upload-time = "2025-11-16T16:13:32.164Z" }, 584 + { url = "https://files.pythonhosted.org/packages/17/5e/2f970ce4c573dc30c2f95825f2691c96d55560268ddc67603dc6ea2dd08e/ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dcec721fddbb62e60c2801ba08c87010bd6b700054a09998c4d09c08147b8fb", size = 147450, upload-time = "2025-11-16T16:13:33.542Z" }, 585 + { url = "https://files.pythonhosted.org/packages/d6/03/a1baa5b94f71383913f21b96172fb3a2eb5576a4637729adbf7cd9f797f8/ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:65f48245279f9bb301d1276f9679b82e4c080a1ae25e679f682ac62446fac471", size = 133139, upload-time = "2025-11-16T16:13:34.587Z" }, 586 + { url = "https://files.pythonhosted.org/packages/dc/19/40d676802390f85784235a05788fd28940923382e3f8b943d25febbb98b7/ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46895c17ead5e22bea5e576f1db7e41cb273e8d062c04a6a49013d9f60996c25", size = 731474, upload-time = "2025-11-16T20:22:49.934Z" }, 587 + { url = "https://files.pythonhosted.org/packages/ce/bb/6ef5abfa43b48dd55c30d53e997f8f978722f02add61efba31380d73e42e/ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3eb199178b08956e5be6288ee0b05b2fb0b5c1f309725ad25d9c6ea7e27f962a", size = 748047, upload-time = "2025-11-16T16:13:35.633Z" }, 588 + { url = "https://files.pythonhosted.org/packages/ff/5d/e4f84c9c448613e12bd62e90b23aa127ea4c46b697f3d760acc32cb94f25/ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d1032919280ebc04a80e4fb1e93f7a738129857eaec9448310e638c8bccefcf", size = 782129, upload-time = "2025-11-16T16:13:36.781Z" }, 589 + { url = "https://files.pythonhosted.org/packages/de/4b/e98086e88f76c00c88a6bcf15eae27a1454f661a9eb72b111e6bbb69024d/ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab0df0648d86a7ecbd9c632e8f8d6b21bb21b5fc9d9e095c796cacf32a728d2d", size = 736848, upload-time = "2025-11-16T16:13:37.952Z" }, 590 + { url = "https://files.pythonhosted.org/packages/0c/5c/5964fcd1fd9acc53b7a3a5d9a05ea4f95ead9495d980003a557deb9769c7/ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:331fb180858dd8534f0e61aa243b944f25e73a4dae9962bd44c46d1761126bbf", size = 741630, upload-time = "2025-11-16T20:22:51.718Z" }, 591 + { url = "https://files.pythonhosted.org/packages/07/1e/99660f5a30fceb58494598e7d15df883a07292346ef5696f0c0ae5dee8c6/ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd4c928ddf6bce586285daa6d90680b9c291cfd045fc40aad34e445d57b1bf51", size = 766619, upload-time = "2025-11-16T16:13:39.178Z" }, 592 + { url = "https://files.pythonhosted.org/packages/36/2f/fa0344a9327b58b54970e56a27b32416ffbcfe4dcc0700605516708579b2/ruamel_yaml_clib-0.2.15-cp313-cp313-win32.whl", hash = "sha256:bf0846d629e160223805db9fe8cc7aec16aaa11a07310c50c8c7164efa440aec", size = 100171, upload-time = "2025-11-16T16:13:40.456Z" }, 593 + { url = "https://files.pythonhosted.org/packages/06/c4/c124fbcef0684fcf3c9b72374c2a8c35c94464d8694c50f37eef27f5a145/ruamel_yaml_clib-0.2.15-cp313-cp313-win_amd64.whl", hash = "sha256:45702dfbea1420ba3450bb3dd9a80b33f0badd57539c6aac09f42584303e0db6", size = 118845, upload-time = "2025-11-16T16:13:41.481Z" }, 312 594 { url = "https://files.pythonhosted.org/packages/3e/bd/ab8459c8bb759c14a146990bf07f632c1cbec0910d4853feeee4be2ab8bb/ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:753faf20b3a5906faf1fc50e4ddb8c074cb9b251e00b14c18b28492f933ac8ef", size = 147248, upload-time = "2025-11-16T16:13:42.872Z" }, 313 595 { url = "https://files.pythonhosted.org/packages/69/f2/c4cec0a30f1955510fde498aac451d2e52b24afdbcb00204d3a951b772c3/ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:480894aee0b29752560a9de46c0e5f84a82602f2bc5c6cde8db9a345319acfdf", size = 133764, upload-time = "2025-11-16T16:13:43.932Z" }, 314 596 { url = "https://files.pythonhosted.org/packages/82/c7/2480d062281385a2ea4f7cc9476712446e0c548cd74090bff92b4b49e898/ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d3b58ab2454b4747442ac76fab66739c72b1e2bb9bd173d7694b9f9dbc9c000", size = 730537, upload-time = "2025-11-16T20:22:52.918Z" }, ··· 319 601 { url = "https://files.pythonhosted.org/packages/b9/17/4e01a602693b572149f92c983c1f25bd608df02c3f5cf50fd1f94e124a59/ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:542d77b72786a35563f97069b9379ce762944e67055bea293480f7734b2c7e5e", size = 765882, upload-time = "2025-11-16T16:13:49.526Z" }, 320 602 { url = "https://files.pythonhosted.org/packages/9f/17/7999399081d39ebb79e807314de6b611e1d1374458924eb2a489c01fc5ad/ruamel_yaml_clib-0.2.15-cp314-cp314-win32.whl", hash = "sha256:424ead8cef3939d690c4b5c85ef5b52155a231ff8b252961b6516ed7cf05f6aa", size = 102567, upload-time = "2025-11-16T16:13:50.78Z" }, 321 603 { url = "https://files.pythonhosted.org/packages/d2/67/be582a7370fdc9e6846c5be4888a530dcadd055eef5b932e0e85c33c7d73/ruamel_yaml_clib-0.2.15-cp314-cp314-win_amd64.whl", hash = "sha256:ac9b8d5fa4bb7fd2917ab5027f60d4234345fd366fe39aa711d5dca090aa1467", size = 122847, upload-time = "2025-11-16T16:13:51.807Z" }, 604 + { url = "https://files.pythonhosted.org/packages/99/85/6eefb87f379dd7de0f84b5b0613fbeb62b77c3b3fea0bcc39d1645af82fb/ruamel_yaml_clib-0.2.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:923816815974425fbb1f1bf57e85eca6e14d8adc313c66db21c094927ad01815", size = 148240, upload-time = "2025-11-16T16:13:53.911Z" }, 605 + { url = "https://files.pythonhosted.org/packages/52/9b/daff728d384563063cfcc08b67178ae0974e4bf8a239a11c826098c325e7/ruamel_yaml_clib-0.2.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dcc7f3162d3711fd5d52e2267e44636e3e566d1e5675a5f0b30e98f2c4af7974", size = 132980, upload-time = "2025-11-16T16:13:55.059Z" }, 606 + { url = "https://files.pythonhosted.org/packages/e7/a5/e9e14332cb5144df205cb8a705edf6af9360a751a3e5801e1fa0bb8fa30d/ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5d3c9210219cbc0f22706f19b154c9a798ff65a6beeafbf77fc9c057ec806f7d", size = 695861, upload-time = "2025-11-16T20:22:55.659Z" }, 607 + { url = "https://files.pythonhosted.org/packages/45/a1/ac45c5c0a406edbdfedb4148f06125bf994a378082bf85e13ace9b2f2a0f/ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bb7b728fd9f405aa00b4a0b17ba3f3b810d0ccc5f77f7373162e9b5f0ff75d5", size = 703585, upload-time = "2025-11-16T16:13:56.515Z" }, 608 + { url = "https://files.pythonhosted.org/packages/ca/20/3e7b0d26261c2ac0c272f42f21408bf2d01aaa08cddd378a51056b3f5fbc/ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3cb75a3c14f1d6c3c2a94631e362802f70e83e20d1f2b2ef3026c05b415c4900", size = 734102, upload-time = "2025-11-16T16:13:57.812Z" }, 609 + { url = "https://files.pythonhosted.org/packages/00/3d/231c8902ef8e35c5dd31bb484d3b807aee952b53ee434c0ebb3609ced170/ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:badd1d7283f3e5894779a6ea8944cc765138b96804496c91812b2829f70e18a7", size = 695072, upload-time = "2025-11-16T16:13:59.295Z" }, 610 + { url = "https://files.pythonhosted.org/packages/dc/fb/f97f98e6d82e07f1b499fb251505d2fda30da318cc7f8708c22397bbb265/ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0ba6604bbc3dfcef844631932d06a1a4dcac3fee904efccf582261948431628a", size = 707541, upload-time = "2025-11-16T20:22:56.84Z" }, 611 + { url = "https://files.pythonhosted.org/packages/99/3e/dcb5158d09dc028ada88555a22eeaace8717f4e7bd077d8bfa22ce07e02b/ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8220fd4c6f98485e97aea65e1df76d4fed1678ede1fe1d0eed2957230d287c4", size = 726459, upload-time = "2025-11-16T16:14:00.443Z" }, 612 + { url = "https://files.pythonhosted.org/packages/28/e8/4752698aada0a6fef4cef3346d61b5de11304950886cc03354d9af554082/ruamel_yaml_clib-0.2.15-cp39-cp39-win32.whl", hash = "sha256:04d21dc9c57d9608225da28285900762befbb0165ae48482c15d8d4989d4af14", size = 103348, upload-time = "2025-11-16T16:14:01.964Z" }, 613 + { url = "https://files.pythonhosted.org/packages/f8/3b/5db40ece46cff9d9a265fb636722f1f5c1a027c1da09921a6c5693d920be/ruamel_yaml_clib-0.2.15-cp39-cp39-win_amd64.whl", hash = "sha256:27dc656e84396e6d687f97c6e65fb284d100483628f02d95464fd731743a4afe", size = 122500, upload-time = "2025-11-16T16:14:03.018Z" }, 614 + ] 615 + 616 + [[package]] 617 + name = "ruff" 618 + version = "0.13.3" 619 + source = { registry = "https://pypi.org/simple" } 620 + resolution-markers = [ 621 + "python_full_version < '3.10'", 622 + ] 623 + sdist = { url = "https://files.pythonhosted.org/packages/c7/8e/f9f9ca747fea8e3ac954e3690d4698c9737c23b51731d02df999c150b1c9/ruff-0.13.3.tar.gz", hash = "sha256:5b0ba0db740eefdfbcce4299f49e9eaefc643d4d007749d77d047c2bab19908e", size = 5438533, upload-time = "2025-10-02T19:29:31.582Z" } 624 + wheels = [ 625 + { url = "https://files.pythonhosted.org/packages/d2/33/8f7163553481466a92656d35dea9331095122bb84cf98210bef597dd2ecd/ruff-0.13.3-py3-none-linux_armv6l.whl", hash = "sha256:311860a4c5e19189c89d035638f500c1e191d283d0cc2f1600c8c80d6dcd430c", size = 12484040, upload-time = "2025-10-02T19:28:49.199Z" }, 626 + { url = "https://files.pythonhosted.org/packages/b0/b5/4a21a4922e5dd6845e91896b0d9ef493574cbe061ef7d00a73c61db531af/ruff-0.13.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2bdad6512fb666b40fcadb65e33add2b040fc18a24997d2e47fee7d66f7fcae2", size = 13122975, upload-time = "2025-10-02T19:28:52.446Z" }, 627 + { url = "https://files.pythonhosted.org/packages/40/90/15649af836d88c9f154e5be87e64ae7d2b1baa5a3ef317cb0c8fafcd882d/ruff-0.13.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fc6fa4637284708d6ed4e5e970d52fc3b76a557d7b4e85a53013d9d201d93286", size = 12346621, upload-time = "2025-10-02T19:28:54.712Z" }, 628 + { url = "https://files.pythonhosted.org/packages/a5/42/bcbccb8141305f9a6d3f72549dd82d1134299177cc7eaf832599700f95a7/ruff-0.13.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c9e6469864f94a98f412f20ea143d547e4c652f45e44f369d7b74ee78185838", size = 12574408, upload-time = "2025-10-02T19:28:56.679Z" }, 629 + { url = "https://files.pythonhosted.org/packages/ce/19/0f3681c941cdcfa2d110ce4515624c07a964dc315d3100d889fcad3bfc9e/ruff-0.13.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5bf62b705f319476c78891e0e97e965b21db468b3c999086de8ffb0d40fd2822", size = 12285330, upload-time = "2025-10-02T19:28:58.79Z" }, 630 + { url = "https://files.pythonhosted.org/packages/10/f8/387976bf00d126b907bbd7725219257feea58650e6b055b29b224d8cb731/ruff-0.13.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78cc1abed87ce40cb07ee0667ce99dbc766c9f519eabfd948ed87295d8737c60", size = 13980815, upload-time = "2025-10-02T19:29:01.577Z" }, 631 + { url = "https://files.pythonhosted.org/packages/0c/a6/7c8ec09d62d5a406e2b17d159e4817b63c945a8b9188a771193b7e1cc0b5/ruff-0.13.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4fb75e7c402d504f7a9a259e0442b96403fa4a7310ffe3588d11d7e170d2b1e3", size = 14987733, upload-time = "2025-10-02T19:29:04.036Z" }, 632 + { url = "https://files.pythonhosted.org/packages/97/e5/f403a60a12258e0fd0c2195341cfa170726f254c788673495d86ab5a9a9d/ruff-0.13.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17b951f9d9afb39330b2bdd2dd144ce1c1335881c277837ac1b50bfd99985ed3", size = 14439848, upload-time = "2025-10-02T19:29:06.684Z" }, 633 + { url = "https://files.pythonhosted.org/packages/39/49/3de381343e89364c2334c9f3268b0349dc734fc18b2d99a302d0935c8345/ruff-0.13.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6052f8088728898e0a449f0dde8fafc7ed47e4d878168b211977e3e7e854f662", size = 13421890, upload-time = "2025-10-02T19:29:08.767Z" }, 634 + { url = "https://files.pythonhosted.org/packages/ab/b5/c0feca27d45ae74185a6bacc399f5d8920ab82df2d732a17213fb86a2c4c/ruff-0.13.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc742c50f4ba72ce2a3be362bd359aef7d0d302bf7637a6f942eaa763bd292af", size = 13444870, upload-time = "2025-10-02T19:29:11.234Z" }, 635 + { url = "https://files.pythonhosted.org/packages/50/a1/b655298a1f3fda4fdc7340c3f671a4b260b009068fbeb3e4e151e9e3e1bf/ruff-0.13.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:8e5640349493b378431637019366bbd73c927e515c9c1babfea3e932f5e68e1d", size = 13691599, upload-time = "2025-10-02T19:29:13.353Z" }, 636 + { url = "https://files.pythonhosted.org/packages/32/b0/a8705065b2dafae007bcae21354e6e2e832e03eb077bb6c8e523c2becb92/ruff-0.13.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6b139f638a80eae7073c691a5dd8d581e0ba319540be97c343d60fb12949c8d0", size = 12421893, upload-time = "2025-10-02T19:29:15.668Z" }, 637 + { url = "https://files.pythonhosted.org/packages/0d/1e/cbe7082588d025cddbb2f23e6dfef08b1a2ef6d6f8328584ad3015b5cebd/ruff-0.13.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6b547def0a40054825de7cfa341039ebdfa51f3d4bfa6a0772940ed351d2746c", size = 12267220, upload-time = "2025-10-02T19:29:17.583Z" }, 638 + { url = "https://files.pythonhosted.org/packages/a5/99/4086f9c43f85e0755996d09bdcb334b6fee9b1eabdf34e7d8b877fadf964/ruff-0.13.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9cc48a3564423915c93573f1981d57d101e617839bef38504f85f3677b3a0a3e", size = 13177818, upload-time = "2025-10-02T19:29:19.943Z" }, 639 + { url = "https://files.pythonhosted.org/packages/9b/de/7b5db7e39947d9dc1c5f9f17b838ad6e680527d45288eeb568e860467010/ruff-0.13.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1a993b17ec03719c502881cb2d5f91771e8742f2ca6de740034433a97c561989", size = 13618715, upload-time = "2025-10-02T19:29:22.527Z" }, 640 + { url = "https://files.pythonhosted.org/packages/28/d3/bb25ee567ce2f61ac52430cf99f446b0e6d49bdfa4188699ad005fdd16aa/ruff-0.13.3-py3-none-win32.whl", hash = "sha256:f14e0d1fe6460f07814d03c6e32e815bff411505178a1f539a38f6097d3e8ee3", size = 12334488, upload-time = "2025-10-02T19:29:24.782Z" }, 641 + { url = "https://files.pythonhosted.org/packages/cf/49/12f5955818a1139eed288753479ba9d996f6ea0b101784bb1fe6977ec128/ruff-0.13.3-py3-none-win_amd64.whl", hash = "sha256:621e2e5812b691d4f244638d693e640f188bacbb9bc793ddd46837cea0503dd2", size = 13455262, upload-time = "2025-10-02T19:29:26.882Z" }, 642 + { url = "https://files.pythonhosted.org/packages/fe/72/7b83242b26627a00e3af70d0394d68f8f02750d642567af12983031777fc/ruff-0.13.3-py3-none-win_arm64.whl", hash = "sha256:9e9e9d699841eaf4c2c798fa783df2fabc680b72059a02ca0ed81c460bc58330", size = 12538484, upload-time = "2025-10-02T19:29:28.951Z" }, 322 643 ] 323 644 324 645 [[package]] 325 646 name = "ruff" 326 647 version = "0.14.10" 327 648 source = { registry = "https://pypi.org/simple" } 649 + resolution-markers = [ 650 + "python_full_version >= '3.10'", 651 + ] 328 652 sdist = { url = "https://files.pythonhosted.org/packages/57/08/52232a877978dd8f9cf2aeddce3e611b40a63287dfca29b6b8da791f5e8d/ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4", size = 5859763, upload-time = "2025-12-18T19:28:57.98Z" } 329 653 wheels = [ 330 654 { url = "https://files.pythonhosted.org/packages/60/01/933704d69f3f05ee16ef11406b78881733c186fe14b6a46b05cfcaf6d3b2/ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49", size = 13527080, upload-time = "2025-12-18T19:29:25.642Z" }, ··· 367 691 368 692 [[package]] 369 693 name = "typer" 694 + version = "0.19.2" 695 + source = { registry = "https://pypi.org/simple" } 696 + resolution-markers = [ 697 + "python_full_version < '3.10'", 698 + ] 699 + dependencies = [ 700 + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, 701 + { name = "rich", marker = "python_full_version < '3.10'" }, 702 + { name = "shellingham", marker = "python_full_version < '3.10'" }, 703 + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, 704 + ] 705 + sdist = { url = "https://files.pythonhosted.org/packages/21/ca/950278884e2ca20547ff3eb109478c6baf6b8cf219318e6bc4f666fad8e8/typer-0.19.2.tar.gz", hash = "sha256:9ad824308ded0ad06cc716434705f691d4ee0bfd0fb081839d2e426860e7fdca", size = 104755, upload-time = "2025-09-23T09:47:48.256Z" } 706 + wheels = [ 707 + { url = "https://files.pythonhosted.org/packages/00/22/35617eee79080a5d071d0f14ad698d325ee6b3bf824fc0467c03b30e7fa8/typer-0.19.2-py3-none-any.whl", hash = "sha256:755e7e19670ffad8283db353267cb81ef252f595aa6834a0d1ca9312d9326cb9", size = 46748, upload-time = "2025-09-23T09:47:46.777Z" }, 708 + ] 709 + 710 + [[package]] 711 + name = "typer" 370 712 version = "0.20.1" 371 713 source = { registry = "https://pypi.org/simple" } 714 + resolution-markers = [ 715 + "python_full_version >= '3.10'", 716 + ] 372 717 dependencies = [ 373 - { name = "click" }, 374 - { name = "rich" }, 375 - { name = "shellingham" }, 376 - { name = "typing-extensions" }, 718 + { name = "click", version = "8.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, 719 + { name = "rich", marker = "python_full_version >= '3.10'" }, 720 + { name = "shellingham", marker = "python_full_version >= '3.10'" }, 721 + { name = "typing-extensions", marker = "python_full_version >= '3.10'" }, 377 722 ] 378 723 sdist = { url = "https://files.pythonhosted.org/packages/6d/c1/933d30fd7a123ed981e2a1eedafceab63cb379db0402e438a13bc51bbb15/typer-0.20.1.tar.gz", hash = "sha256:68585eb1b01203689c4199bc440d6be616f0851e9f0eb41e4a778845c5a0fd5b", size = 105968, upload-time = "2025-12-19T16:48:56.302Z" } 379 724 wheels = [
+1
server/.vscode/settings.json
··· 1 1 { 2 + "terminal.integrated.scrollback": 100000000, 2 3 "deno.enablePaths": [ 3 4 "./" 4 5 ],
+5 -3
server/deno.json
··· 8 8 "@scalar/hono-api-reference": "npm:@scalar/hono-api-reference@^0.9.28", 9 9 "@std/dotenv": "jsr:@std/dotenv@^0.225.5", 10 10 "@types/deno": "npm:@types/deno@^2.5.0", 11 - "drizzle-orm": "npm:drizzle-orm@^0.45.1", 11 + "drizzle-orm": "npm:drizzle-orm@^1.0.0-beta.6-c513c71", 12 12 "drizzle-zod": "npm:drizzle-zod@^0.8.3", 13 13 "eslint": "npm:eslint@^9.39.2", 14 14 "hono": "npm:hono@^4.11.0", ··· 23 23 }, 24 24 "tasks": { 25 25 "generate": "deno run runtimes/generate.ts", 26 - "dev": "deno run $(deno task allow-native-sql) --allow-read=.env --allow-net=0.0.0.0:8000 --allow-sys=hostname --allow-env=LIBSQL_JS_DEV,CI,TERM,NODE_V8_COVERAGE runtimes/main.ts", 26 + "dev": "deno run $(deno task allow-native-sql) --allow-read=.env --allow-net=0.0.0.0:8000 --allow-sys=hostname --allow-env=LIBSQL_JS_DEV,CI,TERM,NODE_V8_COVERAGE runtimes/dev.ts", 27 27 "serve": "deno run $(deno task allow-native-sql) --allow-read=.env --allow-net=0.0.0.0:8000 --allow-env=LIBSQL_JS_DEV runtimes/main.ts", 28 28 "lint": "eslint .", 29 29 "db-gen": "cd jail/drizzle-kit; make gen", ··· 38 38 "pino-pretty", 39 39 "stoker", 40 40 "@hono/zod-openapi", 41 - "drizzle-zod" 41 + "drizzle-zod", 42 + "drizzle-orm", 43 + "stoker/http-status-codes" 42 44 ], 43 45 "jsx": "precompile", 44 46 "jsxImportSource": "hono/jsx"
+443 -6
server/deno.lock
··· 8 8 "npm:@libsql/client@~0.15.15": "0.15.15", 9 9 "npm:@scalar/hono-api-reference@~0.9.28": "0.9.28_hono@4.11.0", 10 10 "npm:@types/deno@^2.5.0": "2.5.0", 11 - "npm:drizzle-orm@~0.45.1": "0.45.1_@libsql+client@0.15.15_postgres@3.4.7", 12 - "npm:drizzle-zod@~0.8.3": "0.8.3_drizzle-orm@0.45.1__@libsql+client@0.15.15__postgres@3.4.7_zod@4.1.13_@libsql+client@0.15.15_postgres@3.4.7", 11 + "npm:drizzle-orm@^1.0.0-beta.6-c513c71": "1.0.0-beta.6-c513c71_@libsql+client@0.15.15_@types+mssql@9.1.8_mssql@11.0.1_postgres@3.4.7", 12 + "npm:drizzle-zod@~0.8.3": "0.8.3_drizzle-orm@1.0.0-beta.6-c513c71__@libsql+client@0.15.15__@types+mssql@9.1.8__mssql@11.0.1__postgres@3.4.7_zod@4.1.13_@libsql+client@0.15.15_postgres@3.4.7", 13 13 "npm:eslint@^9.39.2": "9.39.2", 14 14 "npm:hono-pino@~0.10.3": "0.10.3_hono@4.11.0_pino@10.1.0", 15 15 "npm:hono@^4.11.0": "4.11.0", ··· 84 84 "zod" 85 85 ] 86 86 }, 87 + "@azure-rest/core-client@2.5.1": { 88 + "integrity": "sha512-EHaOXW0RYDKS5CFffnixdyRPak5ytiCtU7uXDcP/uiY+A6jFRwNGzzJBiznkCzvi5EYpY+YWinieqHb0oY916A==", 89 + "dependencies": [ 90 + "@azure/abort-controller", 91 + "@azure/core-auth", 92 + "@azure/core-rest-pipeline", 93 + "@azure/core-tracing", 94 + "@typespec/ts-http-runtime", 95 + "tslib" 96 + ] 97 + }, 98 + "@azure/abort-controller@2.1.2": { 99 + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", 100 + "dependencies": [ 101 + "tslib" 102 + ] 103 + }, 104 + "@azure/core-auth@1.10.1": { 105 + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", 106 + "dependencies": [ 107 + "@azure/abort-controller", 108 + "@azure/core-util", 109 + "tslib" 110 + ] 111 + }, 112 + "@azure/core-client@1.10.1": { 113 + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", 114 + "dependencies": [ 115 + "@azure/abort-controller", 116 + "@azure/core-auth", 117 + "@azure/core-rest-pipeline", 118 + "@azure/core-tracing", 119 + "@azure/core-util", 120 + "@azure/logger", 121 + "tslib" 122 + ] 123 + }, 124 + "@azure/core-http-compat@2.3.1": { 125 + "integrity": "sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g==", 126 + "dependencies": [ 127 + "@azure/abort-controller", 128 + "@azure/core-client", 129 + "@azure/core-rest-pipeline" 130 + ] 131 + }, 132 + "@azure/core-lro@2.7.2": { 133 + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", 134 + "dependencies": [ 135 + "@azure/abort-controller", 136 + "@azure/core-util", 137 + "@azure/logger", 138 + "tslib" 139 + ] 140 + }, 141 + "@azure/core-paging@1.6.2": { 142 + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", 143 + "dependencies": [ 144 + "tslib" 145 + ] 146 + }, 147 + "@azure/core-rest-pipeline@1.22.2": { 148 + "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==", 149 + "dependencies": [ 150 + "@azure/abort-controller", 151 + "@azure/core-auth", 152 + "@azure/core-tracing", 153 + "@azure/core-util", 154 + "@azure/logger", 155 + "@typespec/ts-http-runtime", 156 + "tslib" 157 + ] 158 + }, 159 + "@azure/core-tracing@1.3.1": { 160 + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", 161 + "dependencies": [ 162 + "tslib" 163 + ] 164 + }, 165 + "@azure/core-util@1.13.1": { 166 + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", 167 + "dependencies": [ 168 + "@azure/abort-controller", 169 + "@typespec/ts-http-runtime", 170 + "tslib" 171 + ] 172 + }, 173 + "@azure/identity@4.13.0": { 174 + "integrity": "sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==", 175 + "dependencies": [ 176 + "@azure/abort-controller", 177 + "@azure/core-auth", 178 + "@azure/core-client", 179 + "@azure/core-rest-pipeline", 180 + "@azure/core-tracing", 181 + "@azure/core-util", 182 + "@azure/logger", 183 + "@azure/msal-browser", 184 + "@azure/msal-node", 185 + "open", 186 + "tslib" 187 + ] 188 + }, 189 + "@azure/keyvault-common@2.0.0": { 190 + "integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==", 191 + "dependencies": [ 192 + "@azure/abort-controller", 193 + "@azure/core-auth", 194 + "@azure/core-client", 195 + "@azure/core-rest-pipeline", 196 + "@azure/core-tracing", 197 + "@azure/core-util", 198 + "@azure/logger", 199 + "tslib" 200 + ] 201 + }, 202 + "@azure/keyvault-keys@4.10.0": { 203 + "integrity": "sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag==", 204 + "dependencies": [ 205 + "@azure-rest/core-client", 206 + "@azure/abort-controller", 207 + "@azure/core-auth", 208 + "@azure/core-http-compat", 209 + "@azure/core-lro", 210 + "@azure/core-paging", 211 + "@azure/core-rest-pipeline", 212 + "@azure/core-tracing", 213 + "@azure/core-util", 214 + "@azure/keyvault-common", 215 + "@azure/logger", 216 + "tslib" 217 + ] 218 + }, 219 + "@azure/logger@1.3.0": { 220 + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", 221 + "dependencies": [ 222 + "@typespec/ts-http-runtime", 223 + "tslib" 224 + ] 225 + }, 226 + "@azure/msal-browser@4.27.0": { 227 + "integrity": "sha512-bZ8Pta6YAbdd0o0PEaL1/geBsPrLEnyY/RDWqvF1PP9RUH8EMLvUMGoZFYS6jSlUan6KZ9IMTLCnwpWWpQRK/w==", 228 + "dependencies": [ 229 + "@azure/msal-common" 230 + ] 231 + }, 232 + "@azure/msal-common@15.13.3": { 233 + "integrity": "sha512-shSDU7Ioecya+Aob5xliW9IGq1Ui8y4EVSdWGyI1Gbm4Vg61WpP95LuzcY214/wEjSn6w4PZYD4/iVldErHayQ==" 234 + }, 235 + "@azure/msal-node@3.8.4": { 236 + "integrity": "sha512-lvuAwsDpPDE/jSuVQOBMpLbXuVuLsPNRwWCyK3/6bPlBk0fGWegqoZ0qjZclMWyQ2JNvIY3vHY7hoFmFmFQcOw==", 237 + "dependencies": [ 238 + "@azure/msal-common", 239 + "jsonwebtoken", 240 + "uuid" 241 + ] 242 + }, 87 243 "@babel/helper-string-parser@7.27.1": { 88 244 "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" 89 245 }, ··· 267 423 "@jridgewell/sourcemap-codec@1.5.5": { 268 424 "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" 269 425 }, 426 + "@js-joda/core@5.6.5": { 427 + "integrity": "sha512-3zwefSMwHpu8iVUW8YYz227sIv6UFqO31p1Bf1ZH/Vom7CmNyUsXjDBlnNzcuhmOL1XfxZ3nvND42kR23XlbcQ==" 428 + }, 270 429 "@libsql/client@0.15.15": { 271 430 "integrity": "sha512-twC0hQxPNHPKfeOv3sNT6u2pturQjLcI+CnpTM0SjRpocEGgfiZ7DWKXLNnsothjyJmDqEsBQJ5ztq9Wlu470w==", 272 431 "dependencies": [ ··· 392 551 "picomatch" 393 552 ] 394 553 }, 554 + "@tediousjs/connection-string@0.5.0": { 555 + "integrity": "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==" 556 + }, 395 557 "@types/debug@4.1.12": { 396 558 "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", 397 559 "dependencies": [ ··· 416 578 "@types/ms@2.1.0": { 417 579 "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" 418 580 }, 581 + "@types/mssql@9.1.8": { 582 + "integrity": "sha512-mt9h5jWj+DYE5jxnKaWSV/GqDf9FV52XYVk6T3XZF69noEe+JJV6MKirii48l81+cjmAkSq+qeKX+k61fHkYrQ==", 583 + "dependencies": [ 584 + "@types/node", 585 + "tarn", 586 + "tedious" 587 + ] 588 + }, 419 589 "@types/node@25.0.3": { 420 590 "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", 421 591 "dependencies": [ 422 592 "undici-types" 593 + ] 594 + }, 595 + "@types/readable-stream@4.0.23": { 596 + "integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==", 597 + "dependencies": [ 598 + "@types/node" 423 599 ] 424 600 }, 425 601 "@types/unist@3.0.3": { ··· 529 705 "eslint-visitor-keys@4.2.1" 530 706 ] 531 707 }, 708 + "@typespec/ts-http-runtime@0.3.2": { 709 + "integrity": "sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==", 710 + "dependencies": [ 711 + "http-proxy-agent", 712 + "https-proxy-agent", 713 + "tslib" 714 + ] 715 + }, 532 716 "@vitest/eslint-plugin@1.5.2_eslint@9.39.2_typescript@5.9.3": { 533 717 "integrity": "sha512-2t1F2iecXB/b1Ox4U137lhD3chihEE3dRVtu3qMD35tc6UqUjg1VGRJoS1AkFKwpT8zv8OQInzPQO06hrRkeqw==", 534 718 "dependencies": [ ··· 582 766 "@vue/shared@3.5.25": { 583 767 "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==" 584 768 }, 769 + "abort-controller@3.0.0": { 770 + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 771 + "dependencies": [ 772 + "event-target-shim" 773 + ] 774 + }, 585 775 "acorn-jsx@5.3.2_acorn@8.15.0": { 586 776 "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 587 777 "dependencies": [ ··· 591 781 "acorn@8.15.0": { 592 782 "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", 593 783 "bin": true 784 + }, 785 + "agent-base@7.1.4": { 786 + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==" 594 787 }, 595 788 "ajv@6.12.6": { 596 789 "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", ··· 622 815 "balanced-match@1.0.2": { 623 816 "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 624 817 }, 818 + "base64-js@1.5.1": { 819 + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 820 + }, 625 821 "baseline-browser-mapping@2.9.7": { 626 822 "integrity": "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==", 627 823 "bin": true 824 + }, 825 + "bl@6.1.6": { 826 + "integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==", 827 + "dependencies": [ 828 + "@types/readable-stream", 829 + "buffer", 830 + "inherits", 831 + "readable-stream" 832 + ] 628 833 }, 629 834 "boolbase@1.0.0": { 630 835 "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" ··· 653 858 ], 654 859 "bin": true 655 860 }, 861 + "buffer-equal-constant-time@1.0.1": { 862 + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" 863 + }, 864 + "buffer@6.0.3": { 865 + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 866 + "dependencies": [ 867 + "base64-js", 868 + "ieee754" 869 + ] 870 + }, 656 871 "builtin-modules@5.0.0": { 657 872 "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==" 873 + }, 874 + "bundle-name@4.1.0": { 875 + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", 876 + "dependencies": [ 877 + "run-applescript" 878 + ] 658 879 }, 659 880 "cac@6.7.14": { 660 881 "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==" ··· 702 923 "colorette@2.0.20": { 703 924 "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" 704 925 }, 926 + "commander@11.1.0": { 927 + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==" 928 + }, 705 929 "comment-parser@1.4.1": { 706 930 "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==" 707 931 }, ··· 753 977 "deep-is@0.1.4": { 754 978 "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" 755 979 }, 980 + "default-browser-id@5.0.1": { 981 + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==" 982 + }, 983 + "default-browser@5.4.0": { 984 + "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", 985 + "dependencies": [ 986 + "bundle-name", 987 + "default-browser-id" 988 + ] 989 + }, 990 + "define-lazy-prop@3.0.0": { 991 + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==" 992 + }, 756 993 "defu@6.1.4": { 757 994 "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==" 758 995 }, ··· 771 1008 "diff-sequences@27.5.1": { 772 1009 "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" 773 1010 }, 774 - "drizzle-orm@0.45.1_@libsql+client@0.15.15_postgres@3.4.7": { 775 - "integrity": "sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==", 1011 + "drizzle-orm@1.0.0-beta.6-c513c71_@libsql+client@0.15.15_@types+mssql@9.1.8_mssql@11.0.1_postgres@3.4.7": { 1012 + "integrity": "sha512-DBVdm8wIwalaNPFangbtrDNHV4H5JMi12ri1dVnQ9xWuFJITr+vDPDvOIE7Y70lD8jd+iTUpftNBv80JkYgeDA==", 776 1013 "dependencies": [ 777 1014 "@libsql/client", 1015 + "@types/mssql", 1016 + "mssql", 778 1017 "postgres" 779 1018 ], 780 1019 "optionalPeers": [ ··· 782 1021 "postgres" 783 1022 ] 784 1023 }, 785 - "drizzle-zod@0.8.3_drizzle-orm@0.45.1__@libsql+client@0.15.15__postgres@3.4.7_zod@4.1.13_@libsql+client@0.15.15_postgres@3.4.7": { 1024 + "drizzle-zod@0.8.3_drizzle-orm@1.0.0-beta.6-c513c71__@libsql+client@0.15.15__@types+mssql@9.1.8__mssql@11.0.1__postgres@3.4.7_zod@4.1.13_@libsql+client@0.15.15_postgres@3.4.7": { 786 1025 "integrity": "sha512-66yVOuvGhKJnTdiqj1/Xaaz9/qzOdRJADpDa68enqS6g3t0kpNkwNYjUuaeXgZfO/UWuIM9HIhSlJ6C5ZraMww==", 787 1026 "dependencies": [ 788 1027 "drizzle-orm", 789 1028 "zod" 1029 + ] 1030 + }, 1031 + "ecdsa-sig-formatter@1.0.11": { 1032 + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 1033 + "dependencies": [ 1034 + "safe-buffer" 790 1035 ] 791 1036 }, 792 1037 "electron-to-chromium@1.5.267": { ··· 1158 1403 "esutils@2.0.3": { 1159 1404 "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" 1160 1405 }, 1406 + "event-target-shim@5.0.1": { 1407 + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" 1408 + }, 1409 + "events@3.3.0": { 1410 + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" 1411 + }, 1161 1412 "exsolve@1.0.8": { 1162 1413 "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==" 1163 1414 }, ··· 1286 1537 "html-entities@2.6.0": { 1287 1538 "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==" 1288 1539 }, 1540 + "http-proxy-agent@7.0.2": { 1541 + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", 1542 + "dependencies": [ 1543 + "agent-base", 1544 + "debug" 1545 + ] 1546 + }, 1547 + "https-proxy-agent@7.0.6": { 1548 + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", 1549 + "dependencies": [ 1550 + "agent-base", 1551 + "debug" 1552 + ] 1553 + }, 1554 + "iconv-lite@0.6.3": { 1555 + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 1556 + "dependencies": [ 1557 + "safer-buffer" 1558 + ] 1559 + }, 1560 + "ieee754@1.2.1": { 1561 + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 1562 + }, 1289 1563 "ignore@5.3.2": { 1290 1564 "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==" 1291 1565 }, ··· 1305 1579 "indent-string@5.0.0": { 1306 1580 "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==" 1307 1581 }, 1582 + "inherits@2.0.4": { 1583 + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1584 + }, 1308 1585 "is-builtin-module@5.0.0": { 1309 1586 "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", 1310 1587 "dependencies": [ 1311 1588 "builtin-modules" 1312 1589 ] 1313 1590 }, 1591 + "is-docker@3.0.0": { 1592 + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", 1593 + "bin": true 1594 + }, 1314 1595 "is-extglob@2.1.1": { 1315 1596 "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" 1316 1597 }, ··· 1320 1601 "is-extglob" 1321 1602 ] 1322 1603 }, 1604 + "is-inside-container@1.0.0": { 1605 + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", 1606 + "dependencies": [ 1607 + "is-docker" 1608 + ], 1609 + "bin": true 1610 + }, 1611 + "is-wsl@3.1.0": { 1612 + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", 1613 + "dependencies": [ 1614 + "is-inside-container" 1615 + ] 1616 + }, 1323 1617 "isexe@2.0.0": { 1324 1618 "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" 1325 1619 }, ··· 1328 1622 }, 1329 1623 "js-base64@3.7.8": { 1330 1624 "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==" 1625 + }, 1626 + "js-md4@0.3.2": { 1627 + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==" 1331 1628 }, 1332 1629 "js-yaml@4.1.1": { 1333 1630 "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", ··· 1365 1662 "eslint-visitor-keys@3.4.3", 1366 1663 "espree@9.6.1_acorn@8.15.0", 1367 1664 "semver" 1665 + ] 1666 + }, 1667 + "jsonwebtoken@9.0.3": { 1668 + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", 1669 + "dependencies": [ 1670 + "jws", 1671 + "lodash.includes", 1672 + "lodash.isboolean", 1673 + "lodash.isinteger", 1674 + "lodash.isnumber", 1675 + "lodash.isplainobject", 1676 + "lodash.isstring", 1677 + "lodash.once", 1678 + "ms", 1679 + "semver" 1680 + ] 1681 + }, 1682 + "jwa@2.0.1": { 1683 + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", 1684 + "dependencies": [ 1685 + "buffer-equal-constant-time", 1686 + "ecdsa-sig-formatter", 1687 + "safe-buffer" 1688 + ] 1689 + }, 1690 + "jws@4.0.1": { 1691 + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", 1692 + "dependencies": [ 1693 + "jwa", 1694 + "safe-buffer" 1368 1695 ] 1369 1696 }, 1370 1697 "keyv@4.5.4": { ··· 1414 1741 "p-locate" 1415 1742 ] 1416 1743 }, 1744 + "lodash.includes@4.3.0": { 1745 + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" 1746 + }, 1747 + "lodash.isboolean@3.0.3": { 1748 + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" 1749 + }, 1750 + "lodash.isinteger@4.0.4": { 1751 + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" 1752 + }, 1753 + "lodash.isnumber@3.0.3": { 1754 + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" 1755 + }, 1756 + "lodash.isplainobject@4.0.6": { 1757 + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" 1758 + }, 1759 + "lodash.isstring@4.0.1": { 1760 + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" 1761 + }, 1417 1762 "lodash.merge@4.6.2": { 1418 1763 "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" 1764 + }, 1765 + "lodash.once@4.1.1": { 1766 + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" 1419 1767 }, 1420 1768 "lodash@4.17.21": { 1421 1769 "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" ··· 1832 2180 "ms@2.1.3": { 1833 2181 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1834 2182 }, 2183 + "mssql@11.0.1": { 2184 + "integrity": "sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w==", 2185 + "dependencies": [ 2186 + "@tediousjs/connection-string", 2187 + "commander", 2188 + "debug", 2189 + "rfdc", 2190 + "tarn", 2191 + "tedious" 2192 + ], 2193 + "bin": true 2194 + }, 1835 2195 "nanoid@3.3.11": { 1836 2196 "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 1837 2197 "bin": true ··· 1839 2199 "nanoid@5.1.5": { 1840 2200 "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", 1841 2201 "bin": true 2202 + }, 2203 + "native-duplexpair@1.0.0": { 2204 + "integrity": "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==" 1842 2205 }, 1843 2206 "natural-compare@1.4.0": { 1844 2207 "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" ··· 1877 2240 "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1878 2241 "dependencies": [ 1879 2242 "wrappy" 2243 + ] 2244 + }, 2245 + "open@10.2.0": { 2246 + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", 2247 + "dependencies": [ 2248 + "default-browser", 2249 + "define-lazy-prop", 2250 + "is-inside-container", 2251 + "wsl-utils" 1880 2252 ] 1881 2253 }, 1882 2254 "openapi3-ts@4.5.0": { ··· 2044 2416 "process-warning@5.0.0": { 2045 2417 "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==" 2046 2418 }, 2419 + "process@0.11.10": { 2420 + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" 2421 + }, 2047 2422 "promise-limit@2.7.0": { 2048 2423 "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==" 2049 2424 }, ··· 2063 2438 "quick-format-unescaped@4.0.4": { 2064 2439 "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" 2065 2440 }, 2441 + "readable-stream@4.7.0": { 2442 + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", 2443 + "dependencies": [ 2444 + "abort-controller", 2445 + "buffer", 2446 + "events", 2447 + "process", 2448 + "string_decoder" 2449 + ] 2450 + }, 2066 2451 "real-require@0.2.0": { 2067 2452 "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" 2068 2453 }, ··· 2099 2484 "resolve-pkg-maps@1.0.0": { 2100 2485 "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==" 2101 2486 }, 2487 + "rfdc@1.4.1": { 2488 + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" 2489 + }, 2490 + "run-applescript@7.1.0": { 2491 + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==" 2492 + }, 2493 + "safe-buffer@5.2.1": { 2494 + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 2495 + }, 2102 2496 "safe-stable-stringify@2.5.0": { 2103 2497 "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==" 2498 + }, 2499 + "safer-buffer@2.1.2": { 2500 + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 2104 2501 }, 2105 2502 "scslre@0.3.0": { 2106 2503 "integrity": "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==", ··· 2154 2551 "split2@4.2.0": { 2155 2552 "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" 2156 2553 }, 2554 + "sprintf-js@1.1.3": { 2555 + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" 2556 + }, 2157 2557 "stoker@2.0.1_@hono+zod-openapi@1.1.5__hono@4.11.0__zod@4.1.13_hono@4.11.0_zod@4.1.13": { 2158 2558 "integrity": "sha512-liSQNnJmn8fWSEan7sVaFe6iSHuN3X02fDGLS6snwW+FUuKi5HmKUHm3P+Kzr5xiDPqRpmSTtmGEBbSL9H2zkQ==", 2159 2559 "dependencies": [ ··· 2162 2562 ], 2163 2563 "optionalPeers": [ 2164 2564 "@hono/zod-openapi" 2565 + ] 2566 + }, 2567 + "string_decoder@1.3.0": { 2568 + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 2569 + "dependencies": [ 2570 + "safe-buffer" 2165 2571 ] 2166 2572 }, 2167 2573 "strip-indent@4.1.1": { ··· 2191 2597 "tapable@2.3.0": { 2192 2598 "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==" 2193 2599 }, 2600 + "tarn@3.0.2": { 2601 + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==" 2602 + }, 2603 + "tedious@18.6.2": { 2604 + "integrity": "sha512-g7jC56o3MzLkE3lHkaFe2ZdOVFBahq5bsB60/M4NYUbocw/MCrS89IOEQUFr+ba6pb8ZHczZ/VqCyYeYq0xBAg==", 2605 + "dependencies": [ 2606 + "@azure/core-auth", 2607 + "@azure/identity", 2608 + "@azure/keyvault-keys", 2609 + "@js-joda/core", 2610 + "@types/node", 2611 + "bl", 2612 + "iconv-lite", 2613 + "js-md4", 2614 + "native-duplexpair", 2615 + "sprintf-js" 2616 + ] 2617 + }, 2194 2618 "thread-stream@3.1.0": { 2195 2619 "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", 2196 2620 "dependencies": [ ··· 2233 2657 "typescript" 2234 2658 ] 2235 2659 }, 2660 + "tslib@2.8.1": { 2661 + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" 2662 + }, 2236 2663 "type-check@0.4.0": { 2237 2664 "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 2238 2665 "dependencies": [ ··· 2300 2727 "util-deprecate@1.0.2": { 2301 2728 "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 2302 2729 }, 2730 + "uuid@8.3.2": { 2731 + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 2732 + "bin": true 2733 + }, 2303 2734 "vue-eslint-parser@10.2.0_eslint@9.39.2": { 2304 2735 "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==", 2305 2736 "dependencies": [ ··· 2331 2762 "ws@8.18.3": { 2332 2763 "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==" 2333 2764 }, 2765 + "wsl-utils@0.1.0": { 2766 + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", 2767 + "dependencies": [ 2768 + "is-wsl" 2769 + ] 2770 + }, 2334 2771 "xml-name-validator@4.0.0": { 2335 2772 "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==" 2336 2773 }, ··· 2363 2800 "npm:@libsql/client@~0.15.15", 2364 2801 "npm:@scalar/hono-api-reference@~0.9.28", 2365 2802 "npm:@types/deno@^2.5.0", 2366 - "npm:drizzle-orm@~0.45.1", 2803 + "npm:drizzle-orm@^1.0.0-beta.6-c513c71", 2367 2804 "npm:drizzle-zod@~0.8.3", 2368 2805 "npm:eslint@^9.39.2", 2369 2806 "npm:hono-pino@~0.10.3",
+7 -1
server/eslint.config.mjs
··· 1 1 import antfu from '@antfu/eslint-config' 2 2 3 - export default antfu() 3 + export default antfu({ 4 + rules: { 5 + 'ts/no-use-before-define': 'off', 6 + }, 7 + ignores: [ 8 + ], 9 + })
+1 -1
server/jail/drizzle-kit/Containerfile
··· 7 7 WORKDIR /home/jail 8 8 9 9 RUN npm init -y 10 - RUN npm i drizzle-kit drizzle-orm @libsql/client 10 + RUN npm i drizzle-kit@beta drizzle-orm@^1.0.0-beta.5 @libsql/client 11 11 12 12 COPY drizzle.config.ts.jail drizzle.config.ts 13 13 COPY build/schema src/db/schema
+2 -2
server/runtimes/dev.ts
··· 1 - import openapiConfig from '@/openapi.config.ts' 2 - import { PhotoAPI } from '@/src/app.ts' 3 1 import type { AppBindings } from '@/types.ts' 4 2 import { OpenAPIHono } from '@hono/zod-openapi' 5 3 import { Scalar } from '@scalar/hono-api-reference' ··· 7 5 import pino from 'pino' 8 6 import pretty from 'pino-pretty' 9 7 import defaultHook from 'stoker/openapi/default-hook' 8 + import openapiConfig from '@/openapi.config.ts' 9 + import { PhotoAPI } from '@/src/app.ts' 10 10 11 11 function devTools(app: OpenAPIHono<AppBindings>) { 12 12 app.use(pinoLogger({
+1 -1
server/runtimes/generate.ts
··· 1 - import { PhotoAPI } from '@/src/app.ts' 2 1 import type { AppBindings } from '@/types.ts' 3 2 import { OpenAPIHono } from '@hono/zod-openapi' 4 3 import defaultHook from 'stoker/openapi/default-hook' 4 + import { PhotoAPI } from '@/src/app.ts' 5 5 6 6 new PhotoAPI(new OpenAPIHono<AppBindings>({ 7 7 strict: false,
+1 -1
server/runtimes/main.ts
··· 1 - import { PhotoAPI } from '@/src/app.ts' 2 1 import type { AppBindings } from '@/types.ts' 3 2 import { OpenAPIHono } from '@hono/zod-openapi' 4 3 import defaultHook from 'stoker/openapi/default-hook' 4 + import { PhotoAPI } from '@/src/app.ts' 5 5 6 6 new PhotoAPI(new OpenAPIHono<AppBindings>({ 7 7 strict: false,
+122
server/src/albums-segmented.ts
··· 1 + import { createInsertSchema, sole } from "@/src/common.ts"; 2 + import db from "@/src/db/index.ts"; 3 + import { album as albumTb, segmentToPhoto } from "@/src/db/schema/album.ts"; 4 + import { albumSection, albumSegment } from "@/src/db/schema/index.ts"; 5 + import { createRoute, z } from "@hono/zod-openapi"; 6 + import { createSelectSchema } from "drizzle-zod"; 7 + import * as HttpStatusCodes from "stoker/http-status-codes"; 8 + import jsonContent from "stoker/openapi/helpers/json-content"; 9 + import jsonContentRequired from "stoker/openapi/helpers/json-content-required"; 10 + import createErrorSchema from "stoker/openapi/schemas/create-error-schema"; 11 + import type { Passthrough } from "./app.ts"; 12 + import { albumToPhoto, photo } from "./db/schema/index.ts"; 13 + 14 + export const routes: Array<Passthrough> = [ 15 + (app) => 16 + app.openapi( 17 + createRoute({ 18 + path: "/segmentedAlbumWithNewPhotosWBluntGeo", 19 + method: "post", 20 + request: { 21 + body: jsonContentRequired( 22 + segmentedAlbumWithNewPhotosWBluntGeo, 23 + "album to create", 24 + ), 25 + }, 26 + responses: { 27 + [HttpStatusCodes.OK]: jsonContent( 28 + createSelectSchema(albumTb), 29 + "created album", 30 + ), 31 + [HttpStatusCodes.UNPROCESSABLE_ENTITY]: jsonContent( 32 + createErrorSchema(segmentedAlbumWithNewPhotosWBluntGeo), 33 + "validation errors", 34 + ), 35 + }, 36 + }), 37 + async (c) => { 38 + const album = c.req.valid("json"); 39 + const [createdAlbum] = await db.insert(albumTb).values(album.album) 40 + .returning(); 41 + 42 + for (const section of album.sections) { 43 + const [createdSection] = await db.insert(albumSection).values([{ 44 + albumId: createdAlbum.id, 45 + }]).returning(); 46 + for (const segment of section.segments) { 47 + const createdSegment = sole( 48 + await db.insert(albumSegment).values([{ 49 + sectionId: createdSection.id, 50 + }]).returning(), 51 + ); 52 + 53 + const imgs = segment.images.map((i) => { 54 + return { 55 + fileName: i.file, 56 + dateCreated: i.timestamp.getTime(), 57 + dateModified: i.timestamp.getTime(), 58 + width: i.metadata.width, 59 + height: i.metadata.height, 60 + orientation: i.metadata.orientation, 61 + region: i.metadata.region, 62 + }; 63 + }); 64 + 65 + const createdPhotos = await db.insert(photo).values(imgs).returning(); 66 + 67 + await db.insert(segmentToPhoto).values(createdPhotos.map((p) => { 68 + return { 69 + segmentId: createdSegment.id, 70 + photoId: p.id, 71 + }; 72 + })); 73 + await db.insert(albumToPhoto).values(createdPhotos.map((p) => { 74 + return { 75 + albumId: createdAlbum.id, 76 + photoId: p.id, 77 + }; 78 + })); 79 + } 80 + } 81 + return c.json(createdAlbum, HttpStatusCodes.OK); 82 + }, 83 + ), 84 + ]; 85 + 86 + const segmentedAlbumWithNewPhotosWBluntGeo = z.object({ 87 + album: createInsertSchema(albumTb).omit({ 88 + id: true, 89 + cover: true, 90 + }), 91 + sections: z.array(z.object({ 92 + segments: z.array(z.object({ 93 + images: z.array(z.object({ 94 + file: z.string(), 95 + timestamp: z.coerce.date(), 96 + metadata: z.object({ 97 + width: z.number(), 98 + height: z.number(), 99 + orientation: z.number(), 100 + region: z.optional(z.string()), 101 + }), 102 + })), 103 + })), 104 + })), 105 + }); 106 + 107 + const segmentedAlbumWithNewPhotos = z.object({ 108 + album: createInsertSchema(albumTb).omit({ 109 + id: true, 110 + cover: true, 111 + }), 112 + sections: z.array(z.object({ 113 + segments: z.array(z.object({ 114 + images: z.array( 115 + createInsertSchema(photo).omit({ 116 + id: true, 117 + takenWith: true, 118 + }), 119 + ), 120 + })), 121 + })), 122 + });
+179
server/src/albums.ts
··· 1 + import { createInsertSchema, mapInBatches } from "@/src/common.ts"; 2 + import db from "@/src/db/index.ts"; 3 + import { album as albumTb } from "@/src/db/schema/album.ts"; 4 + import { createRoute, z } from "@hono/zod-openapi"; 5 + import { createSelectSchema } from "drizzle-zod"; 6 + import * as HttpStatusCodes from "stoker/http-status-codes"; 7 + import jsonContent from "stoker/openapi/helpers/json-content"; 8 + import jsonContentRequired from "stoker/openapi/helpers/json-content-required"; 9 + import createErrorSchema from "stoker/openapi/schemas/create-error-schema"; 10 + import IdParamsSchema from "stoker/openapi/schemas/id-params"; 11 + import type { App, Passthrough } from "./app.ts"; 12 + import { albumToPhoto, photo } from "./db/schema/index.ts"; 13 + 14 + export const routes: Array<Passthrough> = [ 15 + (app) => 16 + app.openapi( 17 + createRoute({ 18 + path: "/albums", 19 + method: "get", 20 + responses: { 21 + [HttpStatusCodes.OK]: jsonContent(listOf, "all albums"), 22 + }, 23 + }), 24 + async (c) => c.json(await db.query.album.findMany()), 25 + ), 26 + (app) => 27 + app.openapi( 28 + createRoute({ 29 + path: "/album/{id}", 30 + method: "get", 31 + request: { 32 + params: IdParamsSchema, 33 + }, 34 + responses: { 35 + [HttpStatusCodes.OK]: jsonContent( 36 + createSelectSchema(albumTb), 37 + "an album", 38 + ), 39 + [HttpStatusCodes.UNPROCESSABLE_ENTITY]: jsonContent( 40 + createErrorSchema(createSelectSchema(albumTb)), 41 + "validation errors", 42 + ), 43 + }, 44 + }), 45 + async (c) => { 46 + const params = c.req.valid("param"); 47 + return c.json( 48 + await db.query.album.findFirst({ 49 + where: { 50 + id: params.id, 51 + }, 52 + with: { 53 + sections: { 54 + columns: { 55 + id: true, 56 + }, 57 + with: { 58 + segments: true, 59 + }, 60 + }, 61 + }, 62 + }), 63 + HttpStatusCodes.OK, 64 + ); 65 + }, 66 + ), 67 + createAlbum, 68 + createBatchPhotosInAlbum, 69 + ]; 70 + 71 + const listOf = z.array(createSelectSchema(albumTb, {})); 72 + 73 + function createAlbum(app: App) { 74 + return app.openapi( 75 + createRoute({ 76 + path: "/album", 77 + method: "post", 78 + request: { 79 + body: jsonContentRequired( 80 + albumWithExistingPhotos, 81 + "album to create", 82 + ), 83 + }, 84 + responses: { 85 + [HttpStatusCodes.OK]: jsonContent( 86 + createSelectSchema(albumTb), 87 + "created album", 88 + ), 89 + [HttpStatusCodes.UNPROCESSABLE_ENTITY]: jsonContent( 90 + createErrorSchema(albumWithExistingPhotos), 91 + "validation errors", 92 + ), 93 + }, 94 + }), 95 + async (c) => { 96 + const album = c.req.valid("json"); 97 + const [created] = await db.insert(albumTb).values(album.album) 98 + .returning(); 99 + const v = album.contents.map((photoId) => { 100 + return { 101 + albumId: created.id, 102 + photoId, 103 + }; 104 + }); 105 + await db.insert(albumToPhoto).values(v); 106 + return c.json(created, HttpStatusCodes.OK); 107 + }, 108 + ); 109 + } 110 + 111 + const albumWithExistingPhotos = z.object({ 112 + album: createInsertSchema(albumTb).omit({ 113 + id: true, 114 + cover: true, 115 + }), 116 + contents: z.array( 117 + createInsertSchema(albumToPhoto).shape.photoId, 118 + ), 119 + }); 120 + 121 + function createBatchPhotosInAlbum(app: App) { 122 + return app.openapi( 123 + createRoute({ 124 + path: "/batchPhotosInAlbum", 125 + method: "post", 126 + request: { 127 + body: jsonContentRequired( 128 + albumWithNewPhotos, 129 + "album to create", 130 + ), 131 + }, 132 + responses: { 133 + [HttpStatusCodes.OK]: jsonContent( 134 + createSelectSchema(albumTb), 135 + "created album", 136 + ), 137 + [HttpStatusCodes.UNPROCESSABLE_ENTITY]: jsonContent( 138 + createErrorSchema(albumWithNewPhotos), 139 + "validation errors", 140 + ), 141 + }, 142 + }), 143 + async (c) => { 144 + const album = c.req.valid("json"); 145 + const [createdAlbum] = await db.insert(albumTb).values(album.album) 146 + .returning(); 147 + 148 + const createdPhotos = await mapInBatches( 149 + album.contents, 150 + // Math.floor(65564 / 10), 151 + 1000, 152 + async (values) => { 153 + return await db.insert(photo).values(values).returning(); 154 + }, 155 + ); 156 + const v = createdPhotos.map((photoRecord) => { 157 + return { 158 + albumId: createdAlbum.id, 159 + photoId: photoRecord.id, 160 + }; 161 + }); 162 + await db.insert(albumToPhoto).values(v); 163 + return c.json(createdAlbum, HttpStatusCodes.OK); 164 + }, 165 + ); 166 + } 167 + 168 + const albumWithNewPhotos = z.object({ 169 + album: createInsertSchema(albumTb).omit({ 170 + id: true, 171 + cover: true, 172 + }), 173 + contents: z.array( 174 + createInsertSchema(photo).omit({ 175 + id: true, 176 + takenWith: true, 177 + }), 178 + ), 179 + });
+36 -13
server/src/app.ts
··· 1 - import type { OpenAPIHono } from '@hono/zod-openapi' 2 1 import type { AppBindings } from '@/types.ts' 2 + import type { OpenAPIHono } from '@hono/zod-openapi' 3 3 4 + import env from '@/env.ts' 5 + import openApiConf from '@/openapi.config.ts' 6 + import { ErrorHandler } from "hono" 7 + import { INTERNAL_SERVER_ERROR } from "stoker/http-status-codes" 4 8 import notFound from 'stoker/middlewares/not-found' 5 - import onError from 'stoker/middlewares/on-error' 6 - import openApiConf from '@/openapi.config.ts' 7 - import { allAlbums, createAlbum } from './routes.ts' 9 + import { routes as albumSegementedRoutes } from './albums-segmented.ts' 10 + import { routes as albumRoutes } from './albums.ts' 11 + import { routes as photoRoutes } from './photos.ts' 8 12 9 13 export type App = Pick<OpenAPIHono<AppBindings>, 'openapi'> 10 14 15 + export type Passthrough = (a: App) => App 16 + 11 17 interface Routing { 12 - (r: (a: App) => App): PhotoAPI 18 + (r: Passthrough): PhotoAPI 13 19 } 14 20 15 21 export class PhotoAPI { ··· 21 27 22 28 configureRoutes(): PhotoAPI { 23 29 return this 24 - .addRoute(allAlbums) 25 - .addRoute(createAlbum) 26 - } 27 - 28 - private addRoute: Routing = (r) => { 29 - this.app = r(this.app) as OpenAPIHono<AppBindings> 30 - return this 30 + .addRoutes(albumRoutes) 31 + .addRoutes(photoRoutes) 32 + .addRoutes(albumSegementedRoutes) 31 33 } 32 34 33 35 serve() { 34 36 Deno.serve(this.app 35 37 .notFound(notFound) 36 - .onError(onError) 38 + .onError(this.onError) 37 39 .fetch) 38 40 } 39 41 ··· 41 43 const doc = this.app.getOpenAPI31Document(openApiConf) 42 44 Deno.writeTextFileSync(path, JSON.stringify(doc, null, 2)) 43 45 } 46 + 47 + private addRoute: Routing = (r) => { 48 + this.app = r(this.app) as OpenAPIHono<AppBindings> 49 + return this 50 + } 51 + 52 + addRoutes(routies: Array<Passthrough>): PhotoAPI { 53 + routies.forEach(this.addRoute) 54 + return this 55 + } 56 + private onError: ErrorHandler<AppBindings> = (err, c) => { 57 + c.var.logger.error(err.message) 58 + c.var.logger.error(err.stack) 59 + return c.json( 60 + { 61 + message: err.message, 62 + stack: env.NODE_ENV === "production" ? void 0 : err.stack 63 + }, 64 + INTERNAL_SERVER_ERROR 65 + ); 66 + }; 44 67 }
+53
server/src/common.ts
··· 1 + import { createSchemaFactory } from 'drizzle-zod'; 2 + 3 + export const { createInsertSchema } = createSchemaFactory({ 4 + coerce: { 5 + date: true, 6 + }, 7 + }) 8 + 9 + export async function batch<T>( 10 + items: T[], 11 + batchSize: number, 12 + consumer: (batch: T[]) => Promise<void> 13 + ): Promise<void> { 14 + for (let i = 0; i < items.length; i += batchSize) { 15 + const batch = items.slice(i, i + batchSize); 16 + // Wait for the current batch to finish before starting the next 17 + await consumer(batch); 18 + } 19 + } 20 + 21 + /** 22 + * Processes an array in batches and accumulates the results. 23 + * * @template T - The type of input items 24 + * @template R - The type of individual processed results 25 + * @param items - The source array 26 + * @param batchSize - Size of each batch 27 + * @param processor - Function that processes a batch and returns an array of results 28 + * @returns A single flattened array of all results 29 + */ 30 + export async function mapInBatches<T, R>( 31 + items: T[], 32 + batchSize: number, 33 + processor: (batch: T[]) => Promise<R[]> 34 + ): Promise<R[]> { 35 + const allResults: R[] = []; 36 + 37 + for (let i = 0; i < items.length; i += batchSize) { 38 + const batch = items.slice(i, i + batchSize); 39 + 40 + // Execute the processor for the current chunk 41 + const batchResult = await processor(batch); 42 + 43 + // Accumulate the results into the main list 44 + allResults.push(...batchResult); 45 + } 46 + 47 + return allResults; 48 + } 49 + 50 + export function sole<I>(list: I[]): I { 51 + const [val] = list 52 + return val 53 + }
+3 -3
server/src/db/index.ts
··· 1 - import { drizzle } from 'drizzle-orm/libsql' 2 1 import env from '@/env.ts' 3 - import * as schema from './schema/index.ts' 2 + import { drizzle } from 'drizzle-orm/libsql' 3 + import { relations } from './schema/index.ts' 4 4 5 - const db = drizzle(env.DATABASE_URL, { schema }) 5 + const db = drizzle(env.DATABASE_URL, { relations }) 6 6 7 7 export default db
+38 -3
server/src/db/schema/album.ts
··· 1 - import { int, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core' 2 - import { photo } from './photo.ts' 1 + import { int, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'; 2 + import { photo } from './photo.ts'; 3 3 4 4 export const album = sqliteTable('album', { 5 - id: int().primaryKey({ autoIncrement: true }), 5 + id: int('id', { mode: 'number' }).primaryKey({ autoIncrement: true }), 6 6 title: text().notNull(), 7 7 year: int().notNull(), 8 8 cover: text(), 9 9 }) 10 10 11 + export const albumSection = sqliteTable('albumSection', { 12 + albumId: int().notNull().references(() => album.id), 13 + id: int('id', { mode: 'number' }).primaryKey({ autoIncrement: true }), 14 + }) 15 + 16 + export const albumSegment = sqliteTable('albumSegment', { 17 + sectionId: int().notNull().references(() => albumSection.id), 18 + id: int('id', { mode: 'number' }).primaryKey({ autoIncrement: true }), 19 + day: text(), 20 + month: text(), 21 + year: text() 22 + }) 23 + 11 24 export const albumToPhoto = sqliteTable('albumToPhoto', { 12 25 albumId: int().notNull().references(() => album.id), 13 26 photoId: int().notNull().references(() => photo.id), 14 27 }, table => [ 15 28 primaryKey({ columns: [table.albumId, table.photoId] }), 16 29 ]) 30 + 31 + export const segmentToPhoto = sqliteTable('segmentToPhoto', { 32 + segmentId: int().notNull().references(() => albumSegment.id), 33 + photoId: int().notNull().references(() => photo.id), 34 + }, table => [ 35 + primaryKey({ columns: [table.segmentId, table.photoId] }), 36 + ]) 37 + 38 + /* 39 + export const albumsMeta = sqliteView("album_sections_view").as((qb) => qb 40 + .select({ 41 + id: album.id, 42 + title: album.title, 43 + cover: album.cover, 44 + // photosCount: count(albumToPhoto.photoId) 45 + photosCount: sql`count(*)`.mapWith(Number) 46 + }) 47 + .from(album) 48 + .leftJoin(albumToPhoto, eq(albumToPhoto.albumId, album.id)) 49 + .groupBy(album.id) 50 + ); 51 + */
+31 -2
server/src/db/schema/index.ts
··· 1 - export * from './album.ts' 2 - export * from './photo.ts' 1 + import { defineRelations } from "drizzle-orm"; 2 + import { album, albumSection, albumSegment } from "./album.ts"; 3 + import { photo } from './photo.ts'; 4 + 5 + export const relations = defineRelations({ 6 + album, 7 + albumSection, 8 + albumSegment, 9 + photo 10 + }, (r) => ({ 11 + albumSegment: { 12 + parent: r.one.albumSection({ 13 + from: r.albumSegment.sectionId, 14 + to: r.albumSection.id 15 + }) 16 + }, 17 + albumSection: { 18 + parent: r.one.album({ 19 + from: r.albumSection.albumId, 20 + to: r.album.id, 21 + }), 22 + segments: r.many.albumSegment() 23 + }, 24 + album: { 25 + sections: r.many.albumSection() 26 + } 27 + })) 28 + 29 + export * from './album.ts'; 30 + export * from './photo.ts'; 31 +
+16 -5
server/src/db/schema/photo.ts
··· 1 - import { int, real, sqliteTable, text } from 'drizzle-orm/sqlite-core' 1 + import { defineRelations } from "drizzle-orm"; 2 + import { int, integer, real, sqliteTable, sqliteView, text } from 'drizzle-orm/sqlite-core'; 2 3 3 4 export const photo = sqliteTable('photo', { 4 - id: int().notNull().primaryKey({ autoIncrement: true }), 5 + id: integer({ mode: 'number' }).notNull().primaryKey({ autoIncrement: true }), 5 6 fileName: text().notNull(), 6 - dateCreated: int({ mode: 'timestamp_ms' }).notNull(), 7 - dateModified: int({ mode: 'timestamp_ms' }).notNull(), 7 + dateCreated: int().notNull(), 8 + dateModified: int().notNull(), 8 9 lat: real(), 9 10 lon: real(), 10 11 width: int().notNull(), 11 12 height: int().notNull(), 12 - takenWith: text().notNull(), 13 + orientation: int().notNull(), 14 + region: text(), 15 + takenWith: text(), 13 16 }) 17 + 18 + const timeline = sqliteView("timeline_view").as((qb) => qb 19 + .select() 20 + .from(photo) 21 + ); 22 + 23 + 24 + export const relations = defineRelations({ photo })
+61
server/src/photos.ts
··· 1 + import db from '@/src/db/index.ts' 2 + import { createRoute, z } from '@hono/zod-openapi' 3 + import { createSelectSchema } from 'drizzle-zod' 4 + import * as HttpStatusCodes from 'stoker/http-status-codes' 5 + import jsonContent from 'stoker/openapi/helpers/json-content' 6 + import jsonContentRequired from 'stoker/openapi/helpers/json-content-required' 7 + import createErrorSchema from 'stoker/openapi/schemas/create-error-schema' 8 + import type { Passthrough } from './app.ts' 9 + import { createInsertSchema, sole } from './common.ts' 10 + import { photo } from './db/schema/photo.ts' 11 + 12 + export const routes: Array<Passthrough> = [ 13 + app => 14 + app.openapi( 15 + createRoute({ 16 + path: '/photos', 17 + method: 'get', 18 + responses: { 19 + [HttpStatusCodes.OK]: jsonContent(listOf, 'all photos'), 20 + }, 21 + }), 22 + async c => c.json(await db.query.photo.findMany()), 23 + ), 24 + app => 25 + app.openapi( 26 + createRoute({ 27 + path: '/photo', 28 + method: 'post', 29 + request: { 30 + body: jsonContentRequired( 31 + createInsertSchema(photo).omit({ 32 + id: true, 33 + }), 34 + 'photo to create', 35 + ), 36 + }, 37 + responses: { 38 + [HttpStatusCodes.OK]: jsonContent( 39 + createSelectSchema(photo), 40 + 'created photo', 41 + ), 42 + [HttpStatusCodes.UNPROCESSABLE_ENTITY]: jsonContent( 43 + createErrorSchema( 44 + createInsertSchema(photo).omit({ 45 + id: true, 46 + }), 47 + ), 48 + 'validation errors', 49 + ), 50 + }, 51 + }), 52 + async (c) => 53 + c.json( 54 + sole(await db.insert(photo).values(c.req.valid('json')).returning()), 55 + HttpStatusCodes.OK, 56 + ), 57 + ), 58 + ] 59 + 60 + const listOf = z.array(createSelectSchema(photo, { 61 + }))
-68
server/src/routes.ts
··· 1 - import type { App } from './app.ts' 2 - import { createRoute, z } from '@hono/zod-openapi' 3 - import { createInsertSchema, createSelectSchema } from 'drizzle-zod' 4 - import * as HttpStatusCodes from 'stoker/http-status-codes' 5 - import jsonContent from 'stoker/openapi/helpers/json-content' 6 - import jsonContentRequired from 'stoker/openapi/helpers/json-content-required' 7 - import db from '@/src/db/index.ts' 8 - import { album as albumTb } from '@/src/db/schema/album.ts' 9 - import { albumToPhoto } from './db/schema/index.ts' 10 - 11 - export function allAlbums(app: App): App { 12 - return app.openapi( 13 - createRoute({ 14 - path: '/albums', 15 - method: 'get', 16 - responses: { 17 - [HttpStatusCodes.OK]: jsonContent( 18 - z.array(createSelectSchema(albumTb)), 19 - 'all albums', 20 - ), 21 - }, 22 - }), 23 - async c => c.json(await db.query.album.findMany()), 24 - ) 25 - } 26 - 27 - export function createAlbum(app: App) { 28 - return app.openapi( 29 - createRoute({ 30 - path: '/album', 31 - method: 'post', 32 - request: { 33 - body: jsonContentRequired( 34 - z.object({ 35 - album: createInsertSchema(albumTb).omit({ 36 - id: true, 37 - cover: true, 38 - }), 39 - contents: z.array( 40 - createInsertSchema(albumToPhoto).pick({ 41 - photoId: true, 42 - }), 43 - ), 44 - }), 45 - 'album to create', 46 - ), 47 - }, 48 - responses: { 49 - [HttpStatusCodes.OK]: jsonContent( 50 - createSelectSchema(albumTb), 51 - 'created album', 52 - ), 53 - }, 54 - }), 55 - async (c) => { 56 - const album = c.req.valid('json') 57 - const [created] = await db.insert(albumTb).values(album.album).returning() 58 - const v = album.contents.map((aToP) => { 59 - return { 60 - albumId: created.id, 61 - photoId: aToP.photoId, 62 - } 63 - }) 64 - await db.insert(albumToPhoto).values(v) 65 - return c.json(created) 66 - }, 67 - ) 68 - }