django-cfg 1.4.95__py3-none-any.whl → 1.4.97__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of django-cfg might be problematic. Click here for more details.

django_cfg/__init__.py CHANGED
@@ -32,7 +32,7 @@ Example:
32
32
  default_app_config = "django_cfg.apps.DjangoCfgConfig"
33
33
 
34
34
  # Version information
35
- __version__ = "1.4.95"
35
+ __version__ = "1.4.97"
36
36
  __license__ = "MIT"
37
37
 
38
38
  # Import registry for organized lazy loading
@@ -4,8 +4,10 @@ JWT tokens are automatically injected into HTML responses for authenticated user
4
4
  This is specific to Next.js frontend apps only.
5
5
 
6
6
  Features:
7
- - Automatic extraction of ZIP archives with smart update detection
8
- - Auto-reextraction when ZIP is modified within last 5 minutes
7
+ - Automatic extraction of ZIP archives with metadata comparison (size + mtime)
8
+ - Auto-reextraction when ZIP content changes (size or timestamp)
9
+ - Marker file (.zip_meta) tracks ZIP metadata for reliable comparison
10
+ - Cache busting (no-store headers for HTML)
9
11
  - SPA routing with fallback strategies
10
12
  - JWT token injection for authenticated users
11
13
  """
@@ -28,24 +30,31 @@ logger = logging.getLogger(__name__)
28
30
 
29
31
  class ZipExtractionMixin:
30
32
  """
31
- Mixin for automatic ZIP extraction with age-based refresh.
33
+ Mixin for automatic ZIP extraction with metadata-based refresh.
32
34
 
33
35
  Provides intelligent ZIP archive handling:
34
36
  - Auto-extraction when directory doesn't exist
35
- - Auto-reextraction when extracted directory is older than 10 minutes
36
- - Simple age-based logic (no ZIP timestamp comparison needed)
37
+ - Auto-reextraction when ZIP metadata changes (size or mtime)
38
+ - Marker file (.zip_meta) tracks ZIP state for reliable comparison
39
+ - Works correctly in Docker where timestamps can be misleading
37
40
 
38
41
  Usage:
39
42
  class MyView(ZipExtractionMixin, View):
40
43
  app_name = 'myapp' # Will look for myapp.zip
41
44
  """
42
45
 
43
- # Maximum age of extracted directory before re-extraction (in seconds)
44
- MAX_DIRECTORY_AGE = 600 # 10 minutes = 600 seconds
45
-
46
46
  def extract_zip_if_needed(self, base_dir: Path, zip_path: Path, app_name: str) -> bool:
47
47
  """
48
- Extract ZIP archive if needed based on directory age.
48
+ Extract ZIP archive if needed based on ZIP metadata (size + mtime) comparison.
49
+
50
+ Logic:
51
+ 1. If directory doesn't exist → extract
52
+ 2. If marker file doesn't exist → extract
53
+ 3. If ZIP metadata changed (size or mtime) → remove and re-extract
54
+ 4. If metadata matches → use existing
55
+
56
+ Uses marker file (.zip_meta) to track ZIP metadata. More reliable than
57
+ just mtime comparison, especially in Docker where timestamps can be misleading.
49
58
 
50
59
  Args:
51
60
  base_dir: Target directory for extraction
@@ -57,47 +66,63 @@ class ZipExtractionMixin:
57
66
  """
58
67
  should_extract = False
59
68
 
69
+ # Check if ZIP exists first
70
+ if not zip_path.exists():
71
+ logger.error(f"[{app_name}] ZIP not found: {zip_path}")
72
+ return False
73
+
74
+ # Get ZIP metadata (size + mtime for reliable comparison)
75
+ zip_stat = zip_path.stat()
76
+ current_meta = f"{zip_stat.st_size}:{zip_stat.st_mtime}"
77
+
78
+ # Marker file stores ZIP metadata
79
+ marker_file = base_dir / '.zip_meta'
80
+
60
81
  # Priority 1: If directory doesn't exist at all - always extract
61
82
  if not base_dir.exists():
62
83
  should_extract = True
63
84
  logger.info(f"[{app_name}] Directory doesn't exist, will extract")
64
85
 
65
- # Priority 2: Directory exists - check if it's too old
86
+ # Priority 2: Marker file doesn't exist - extract (first run or corrupted)
87
+ elif not marker_file.exists():
88
+ should_extract = True
89
+ logger.info(f"[{app_name}] No marker file found, will extract")
90
+
91
+ # Priority 3: Compare stored metadata with current ZIP metadata
66
92
  else:
67
- # Get directory modification time
68
- dir_mtime = datetime.fromtimestamp(base_dir.stat().st_mtime)
69
- directory_age = (datetime.now() - dir_mtime).total_seconds()
70
-
71
- # If directory is older than MAX_DIRECTORY_AGE - re-extract
72
- if directory_age > self.MAX_DIRECTORY_AGE:
73
- logger.info(f"[{app_name}] Directory is old ({directory_age:.0f}s > {self.MAX_DIRECTORY_AGE}s), re-extracting")
74
- try:
75
- shutil.rmtree(base_dir)
76
- should_extract = True
77
- except Exception as e:
78
- logger.error(f"[{app_name}] Failed to remove old directory: {e}")
79
- return False
80
- else:
81
- logger.debug(f"[{app_name}] Directory is fresh ({directory_age:.0f}s), using existing")
93
+ try:
94
+ stored_meta = marker_file.read_text().strip()
95
+ if stored_meta != current_meta:
96
+ logger.info(f"[{app_name}] ZIP metadata changed (stored: {stored_meta}, current: {current_meta}), re-extracting")
97
+ try:
98
+ shutil.rmtree(base_dir)
99
+ should_extract = True
100
+ except Exception as e:
101
+ logger.error(f"[{app_name}] Failed to remove old directory: {e}")
102
+ return False
103
+ else:
104
+ logger.info(f"[{app_name}] ZIP unchanged (meta: {current_meta}), using existing directory")
105
+ except Exception as e:
106
+ logger.warning(f"[{app_name}] Failed to read marker file: {e}, will re-extract")
107
+ should_extract = True
82
108
 
83
109
  # Extract ZIP if needed
84
110
  if should_extract:
85
- if zip_path.exists():
86
- logger.info(f"[{app_name}] Extracting {zip_path.name} to {base_dir}...")
87
- try:
88
- base_dir.parent.mkdir(parents=True, exist_ok=True)
89
- with zipfile.ZipFile(zip_path, 'r') as zip_ref:
90
- zip_ref.extractall(base_dir)
91
- logger.info(f"[{app_name}] Successfully extracted {zip_path.name}")
92
- return True
93
- except Exception as e:
94
- logger.error(f"[{app_name}] Failed to extract: {e}")
95
- return False
96
- else:
97
- logger.error(f"[{app_name}] ZIP not found: {zip_path}")
111
+ logger.info(f"[{app_name}] Extracting {zip_path.name} to {base_dir}...")
112
+ try:
113
+ base_dir.parent.mkdir(parents=True, exist_ok=True)
114
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
115
+ zip_ref.extractall(base_dir)
116
+
117
+ # Write marker file with current metadata
118
+ marker_file.write_text(current_meta)
119
+ logger.info(f"[{app_name}] Successfully extracted {zip_path.name} and saved marker (meta: {current_meta})")
120
+ return True
121
+ except Exception as e:
122
+ logger.error(f"[{app_name}] Failed to extract: {e}")
98
123
  return False
99
124
 
100
- # Directory exists and is fresh
125
+ # Directory exists and is up-to-date
101
126
  return True
102
127
 
103
128
 
@@ -108,7 +133,7 @@ class NextJSStaticView(ZipExtractionMixin, View):
108
133
 
109
134
  Features:
110
135
  - Serves Next.js static export files like a static file server
111
- - Smart ZIP extraction: auto-refreshes when directory older than 10 minutes
136
+ - Smart ZIP extraction: compares ZIP metadata (size + mtime) with marker file
112
137
  - Automatically injects JWT tokens for authenticated users
113
138
  - Tokens injected into HTML responses only
114
139
  - Handles Next.js client-side routing (.html fallback)
@@ -117,9 +142,10 @@ class NextJSStaticView(ZipExtractionMixin, View):
117
142
 
118
143
  ZIP Extraction Logic:
119
144
  - If directory doesn't exist: extract from ZIP
120
- - If directory older than 10 minutes: remove and re-extract
121
- - If directory fresh (< 10 min): use existing files
122
- - This enables auto-refresh during development while caching in production
145
+ - If marker file missing: extract from ZIP
146
+ - If ZIP metadata changed: remove and re-extract
147
+ - If metadata matches: use existing files
148
+ - Marker file (.zip_meta) ensures reliable comparison in Docker
123
149
 
124
150
  Path resolution examples:
125
151
  - /cfg/admin/ → /cfg/admin/index.html
@@ -4,10 +4,21 @@ Views for Next.js admin integration.
4
4
  Serves Next.js static files with SPA routing support and JWT injection.
5
5
 
6
6
  Features:
7
- - Automatic extraction of ZIP archives with age-based refresh
8
- - Auto-reextraction when directory is older than 10 minutes
7
+ - Priority-based ZIP resolution (solution project package fallback)
8
+ - Automatic extraction with metadata comparison (ZIP size + mtime vs marker file)
9
+ - Cache busting (no-store headers for HTML)
9
10
  - SPA routing with fallback strategies
10
11
  - JWT token injection for authenticated users
12
+
13
+ ZIP Resolution Priority:
14
+ 1. Solution project: {BASE_DIR}/static/nextjs_admin.zip → {BASE_DIR}/static/nextjs_admin/
15
+ 2. Package fallback: django_cfg/static/frontend/nextjs_admin.zip → django_cfg/static/frontend/nextjs_admin/
16
+
17
+ Extraction Logic:
18
+ - Marker file (.zip_meta) tracks ZIP metadata (size:mtime)
19
+ - Re-extracts when metadata changes (size or timestamp)
20
+ - Reliable in Docker where timestamps can be misleading
21
+ - Ensures fresh builds are deployed automatically
11
22
  """
12
23
 
13
24
  import logging
@@ -31,20 +42,27 @@ class NextJsAdminView(ZipExtractionMixin, LoginRequiredMixin, View):
31
42
 
32
43
  Features:
33
44
  - Serves Next.js static build files
34
- - Smart ZIP extraction: auto-refreshes when directory older than 10 minutes
45
+ - Priority-based ZIP resolution (solution first, package fallback)
46
+ - Smart ZIP extraction: metadata comparison (size + mtime) with marker file
47
+ - Cache busting: no-store headers for HTML files
35
48
  - Automatic JWT token injection for authenticated users
36
49
  - SPA routing support (path/to/route → path/to/route/index.html)
37
- - Development mode support (proxies to dev server conceptually)
50
+
51
+ ZIP Resolution Priority:
52
+ 1. Solution: {BASE_DIR}/static/nextjs_admin.zip → {BASE_DIR}/static/nextjs_admin/
53
+ 2. Package: django_cfg/static/frontend/nextjs_admin.zip → django_cfg/static/frontend/nextjs_admin/
38
54
 
39
55
  ZIP Extraction Logic:
40
56
  - If directory doesn't exist: extract from ZIP
41
- - If directory older than 10 minutes: remove and re-extract
42
- - If directory fresh (< 10 min): use existing files
57
+ - If marker file missing: extract from ZIP
58
+ - If ZIP metadata changed: remove and re-extract
59
+ - If metadata matches: use existing files
60
+ - Marker file (.zip_meta) ensures reliable comparison in Docker
43
61
 
44
62
  URL Examples:
45
- /cfg/admin/ → admin/index.html
46
- /cfg/admin/crypto → admin/crypto/index.html
47
- /cfg/admin/_next/static/... → _next/static/...
63
+ /cfg/nextjs-admin/admin/ → admin/index.html
64
+ /cfg/nextjs-admin/admin/crypto → admin/crypto/index.html
65
+ /cfg/nextjs-admin/admin/_next/static/... → _next/static/...
48
66
  """
49
67
 
50
68
  def get(self, request, path=''):
@@ -58,17 +76,27 @@ class NextJsAdminView(ZipExtractionMixin, LoginRequiredMixin, View):
58
76
 
59
77
  nextjs_config = config.nextjs_admin
60
78
 
61
- # Use extracted directory from static/frontend/nextjs_admin/
62
- base_dir = Path(django_cfg.__file__).parent / 'static' / 'frontend' / 'nextjs_admin'
63
- zip_path = Path(django_cfg.__file__).parent / 'static' / 'frontend' / 'nextjs_admin.zip'
64
-
65
- # Fallback: Try solution project static directory
66
- if not zip_path.exists():
67
- from django.conf import settings
68
- solution_zip = Path(settings.BASE_DIR) / 'static' / 'nextjs_admin.zip'
69
- if solution_zip.exists():
70
- zip_path = solution_zip
71
- logger.info(f"[nextjs_admin] Using ZIP from solution project: {solution_zip}")
79
+ # Priority 1: Try solution project static directory first
80
+ from django.conf import settings
81
+ solution_zip = Path(settings.BASE_DIR) / 'static' / 'nextjs_admin.zip'
82
+ solution_base_dir = Path(settings.BASE_DIR) / 'static' / 'nextjs_admin'
83
+
84
+ # Priority 2: Fallback to django_cfg package
85
+ default_zip = Path(django_cfg.__file__).parent / 'static' / 'frontend' / 'nextjs_admin.zip'
86
+ default_base_dir = Path(django_cfg.__file__).parent / 'static' / 'frontend' / 'nextjs_admin'
87
+
88
+ # Choose which ZIP to use
89
+ if solution_zip.exists():
90
+ zip_path = solution_zip
91
+ base_dir = solution_base_dir
92
+ logger.info(f"[nextjs_admin] Using ZIP from solution project: {solution_zip}")
93
+ elif default_zip.exists():
94
+ zip_path = default_zip
95
+ base_dir = default_base_dir
96
+ logger.info(f"[nextjs_admin] Using ZIP from django_cfg package: {default_zip}")
97
+ else:
98
+ logger.error(f"[nextjs_admin] No ZIP found in solution ({solution_zip}) or package ({default_zip})")
99
+ return render(request, 'frontend/404.html', status=404)
72
100
 
73
101
  # Extract ZIP if needed using mixin
74
102
  if not self.extract_zip_if_needed(base_dir, zip_path, 'nextjs_admin'):
django_cfg/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "django-cfg"
7
- version = "1.4.95"
7
+ version = "1.4.97"
8
8
  description = "Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features."
9
9
  readme = "README.md"
10
10
  keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "nextjs-admin", "react-admin", "websocket", "centrifugo", "real-time", "typescript-generation", "ai-agents", "enterprise-django", "django-settings", "type-safe-config", "modern-django",]
@@ -6,7 +6,6 @@ Provides template tags for accessing django-cfg configuration constants.
6
6
 
7
7
  import os
8
8
  import socket
9
- import time
10
9
  from django import template
11
10
  from django.conf import settings
12
11
  from django.utils.safestring import mark_safe
@@ -184,26 +183,21 @@ def nextjs_admin_url(path=''):
184
183
  # Normalize path - remove leading/trailing slashes
185
184
  path = path.strip('/')
186
185
 
187
- # Add cache busting parameter (timestamp in milliseconds)
188
- cache_buster = f'_={int(time.time() * 1000)}'
189
-
190
186
  if not settings.DEBUG:
191
- # Production mode: always use static files with cache buster
192
- base_url = f'/cfg/admin/admin/{path}' if path else '/cfg/admin/admin/'
193
- return f'{base_url}?{cache_buster}'
187
+ # Production mode: always use static files
188
+ return f'/cfg/admin/{path}' if path else '/cfg/admin/'
194
189
 
195
190
  # Check if port 3001 is available for Tab 1 (built-in admin)
196
191
  port_3001_available = _is_port_available('localhost', 3001)
197
192
 
198
193
  if port_3001_available:
199
- # Dev server is running on 3001 - use it
200
- base_url = 'http://localhost:3001/admin'
201
- url = f'{base_url}/{path}' if path else base_url
202
- return f'{url}?{cache_buster}'
194
+ # Dev server is running on 3001 - use it (builtin admin has no /admin prefix)
195
+ base_url = 'http://localhost:3001'
196
+ return f'{base_url}/{path}' if path else base_url
203
197
  else:
204
- # No dev server or dev server stopped - use static files with cache buster
205
- base_url = f'/cfg/admin/admin/{path}' if path else '/cfg/admin/admin/'
206
- return f'{base_url}?{cache_buster}'
198
+ # No dev server or dev server stopped - use static files
199
+ # Static files are served from /cfg/admin/ but ZIP contains files in root (no /admin prefix)
200
+ return f'/cfg/admin/{path}' if path else '/cfg/admin/'
207
201
 
208
202
 
209
203
  @register.simple_tag
@@ -279,19 +273,15 @@ def nextjs_external_admin_url(route=''):
279
273
 
280
274
  route = route.strip('/')
281
275
 
282
- # Add cache busting parameter (timestamp in milliseconds)
283
- cache_buster = f'_={int(time.time() * 1000)}'
284
-
285
276
  # Auto-detect development mode: DEBUG=True + port 3000 available
286
277
  if settings.DEBUG and _is_port_available('localhost', 3000):
287
278
  # Development mode: solution project on port 3000
279
+ # Routes start with /admin in Next.js (e.g., /admin, /admin/crypto)
288
280
  base_url = 'http://localhost:3000/admin'
289
- url = f'{base_url}/{route}' if route else base_url
290
- return f'{url}?{cache_buster}'
281
+ return f'{base_url}/{route}' if route else base_url
291
282
  else:
292
283
  # Production mode: use relative URL - Django serves from extracted ZIP with /admin prefix
293
- base_url = f"/cfg/nextjs-admin/admin/{route}" if route else "/cfg/nextjs-admin/admin/"
294
- return f'{base_url}?{cache_buster}'
284
+ return f"/cfg/nextjs-admin/admin/{route}" if route else "/cfg/nextjs-admin/admin/"
295
285
  except Exception:
296
286
  return ''
297
287
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.4.95
3
+ Version: 1.4.97
4
4
  Summary: Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features.
5
5
  Project-URL: Homepage, https://djangocfg.com
6
6
  Project-URL: Documentation, https://djangocfg.com
@@ -1,5 +1,5 @@
1
1
  django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- django_cfg/__init__.py,sha256=FnIEFVmVUPO1EuonJ9G3Z2YKgVdqMoyS1vyH_zUM2NU,1620
2
+ django_cfg/__init__.py,sha256=_L0rVeRDg_vkLAwSqGT8ka-Mc6eZ0ddniv5wfm9RJvM,1620
3
3
  django_cfg/apps.py,sha256=72m3uuvyqGiLx6gOfE-BD3P61jddCCERuBOYpxTX518,1605
4
4
  django_cfg/config.py,sha256=y4Z3rnYsHBE0TehpwAIPaxr---mkvyKrZGGsNwYso74,1398
5
5
  django_cfg/apps/__init__.py,sha256=JtDmEYt1OcleWM2ZaeX0LKDnRQzPOavfaXBWG4ECB5Q,26
@@ -228,7 +228,7 @@ django_cfg/apps/frontend/apps.py,sha256=9eEK0Tq2nOsol7xSK5aobdwTDJTJrWx6Iy1I1DQb
228
228
  django_cfg/apps/frontend/setup.py,sha256=TxKQwQw4xTF6VSyhrQBzbUsdsVQR9JHdjc36LZKeQh4,2444
229
229
  django_cfg/apps/frontend/test_routing.py,sha256=fshJOR9ln7m3gXY9EI1_ix_6E5xua6DR264b16RIF-w,4832
230
230
  django_cfg/apps/frontend/urls.py,sha256=Vz22_2i2w1J0KQYDCxHnTF5rUf32kUUSBDJZrP07XgY,284
231
- django_cfg/apps/frontend/views.py,sha256=c2IjtgKKYD-jZO0YtZEUumYtWZ3RKhEfwQaCW2pDn9g,13861
231
+ django_cfg/apps/frontend/views.py,sha256=JPTJRPCGY3zLLiiz1F9CsZiLkXbQ5RfnViJZzBUpV7o,15023
232
232
  django_cfg/apps/frontend/templates/frontend/404.html,sha256=LCFig_dcgDDmYKhgOLu8R2KDs_aQS6Es6rAxLTAEXWs,2175
233
233
  django_cfg/apps/knowbase/README.md,sha256=HXt_J6WCN-LsMhA7p9mdvih07_vp_r_hkPdmqHhNEeo,3965
234
234
  django_cfg/apps/knowbase/__init__.py,sha256=cfGnxDQwjajPhUoleKkgvdabJcB0LdXEglnsBojKkPo,1045
@@ -1021,7 +1021,7 @@ django_cfg/modules/django_unfold/models/navigation.py,sha256=PPEeqA2HBaA1-VjADiX
1021
1021
  django_cfg/modules/nextjs_admin/__init__.py,sha256=lfrZYyNRExH3Z5De8G4hQBIZoFlW5Ejze3couNrztbY,312
1022
1022
  django_cfg/modules/nextjs_admin/apps.py,sha256=HxVUMmWTKdYpwJ00iIfWVFsBzsawsOVhEPZqjk_izjI,347
1023
1023
  django_cfg/modules/nextjs_admin/urls.py,sha256=7n0yStm0WNchw14Rtu_mgsIA3WKQsYP9WZt3-YOUWjU,603
1024
- django_cfg/modules/nextjs_admin/views.py,sha256=9mbpt_WxEksx2YJq5FdjNRa0MtOh6Teq89PW6Dlnykc,9927
1024
+ django_cfg/modules/nextjs_admin/views.py,sha256=zvRMZZRRcwIA3ie0AEnllOUUwLklbt3nq-P5Eq9BzeY,11385
1025
1025
  django_cfg/modules/nextjs_admin/models/__init__.py,sha256=WGw9KXcYd1O9AoA_bpMoz2gLZUlRzjGmUBjjbObcUi0,100
1026
1026
  django_cfg/modules/nextjs_admin/models/config.py,sha256=0ADqLuiywSCQfx_z9dkwjFCca3lr3F2uQffIjTr_QXw,5864
1027
1027
  django_cfg/modules/nextjs_admin/templatetags/__init__.py,sha256=ChVBnJggCIY8rMhfyJFoA8k0qKo-8FtJknrk54Vx4wM,51
@@ -1051,7 +1051,6 @@ django_cfg/static/admin/js/alpine/dashboard-tabs.js,sha256=ob8Q_I9lFLDv_hFERXgTy
1051
1051
  django_cfg/static/admin/js/alpine/system-metrics.js,sha256=m-Fg55K_vpHXToD46PXL9twl4OBF_V9MONvbSWbQqDw,440
1052
1052
  django_cfg/static/admin/js/alpine/toggle-section.js,sha256=T141NFmy0fRJyGGuuaCJRjJXwPam-xxtQNW1hi8BJbc,672
1053
1053
  django_cfg/static/frontend/admin.zip,sha256=n2G8ajruLqojcjqvw2pMEPjgpSTTwcmJxkeIQxJVw9U,7626768
1054
- django_cfg/static/frontend/nextjs_admin.zip,sha256=GyHg6vnDOAXyanu-EwWZoSqT3Qz-765ZZ5QCSMbC1ig,7548295
1055
1054
  django_cfg/static/js/api-loader.mjs,sha256=boGqqRGnFR-Mzo_RQOjhAzNvsb7QxZddSwMKROzkk9Q,5163
1056
1055
  django_cfg/static/js/api/base.mjs,sha256=KUxZHHdELAV8mNnACpwJRvaQhdJxp-n5LFEQ4oUZxBo,4707
1057
1056
  django_cfg/static/js/api/index.mjs,sha256=_-Q04jjHcgwi4CGfiaLyiOR6NW7Yu1HBhJWp2J1cjpc,2538
@@ -1077,7 +1076,7 @@ django_cfg/templates/admin/index.html,sha256=RidRvZwc6LFzRi8l6vHBgyM_CD0yvhPWvr4
1077
1076
  django_cfg/templates/emails/base_email.html,sha256=TWcvYa2IHShlF_E8jf1bWZStRO0v8G4L_GexPxvz6XQ,8836
1078
1077
  django_cfg/templates/unfold/layouts/skeleton.html,sha256=2ArkcNZ34mFs30cOAsTQ1EZiDXcB0aVxkO71lJq9SLE,718
1079
1078
  django_cfg/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1080
- django_cfg/templatetags/django_cfg.py,sha256=CXer0Jx7GVX8jZG1NBTGPZ2858BV4d81NklyaLnflOo,9844
1079
+ django_cfg/templatetags/django_cfg.py,sha256=Q1-dQV8SPOuQu1dFg5eT1gT88eEUXloYMz-7_-OHT0c,9522
1081
1080
  django_cfg/utils/__init__.py,sha256=64wwXJuXytvwt8Ze_erSR2HmV07nGWJ6DV5wloRBvYE,435
1082
1081
  django_cfg/utils/path_resolution.py,sha256=2n0I04lQkSssFaELu3A93YyMAl1K10KPdpxMt5k4Iy0,13341
1083
1082
  django_cfg/utils/smart_defaults.py,sha256=ZUj6K_Deq-fp5O0Dy_Emt257UWFn0f9bkgFv9YCR58U,9239
@@ -1085,9 +1084,9 @@ django_cfg/utils/version_check.py,sha256=WO51J2m2e-wVqWCRwbultEwu3q1lQasV67Mw2aa
1085
1084
  django_cfg/CHANGELOG.md,sha256=jtT3EprqEJkqSUh7IraP73vQ8PmKUMdRtznQsEnqDZk,2052
1086
1085
  django_cfg/CONTRIBUTING.md,sha256=DU2kyQ6PU0Z24ob7O_OqKWEYHcZmJDgzw-lQCmu6uBg,3041
1087
1086
  django_cfg/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1088
- django_cfg/pyproject.toml,sha256=De2uKACzSxO9s9tWIBeYa9veWb8OHwuuP0eZhdjOnoI,8572
1089
- django_cfg-1.4.95.dist-info/METADATA,sha256=wHT5AT-_FxOZSLhJnQUA3JPDDHYmoBiAGj1g9m_Hiic,23733
1090
- django_cfg-1.4.95.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1091
- django_cfg-1.4.95.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1092
- django_cfg-1.4.95.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1093
- django_cfg-1.4.95.dist-info/RECORD,,
1087
+ django_cfg/pyproject.toml,sha256=0AbWzGlCW_MemwzdLfIeySO-sNszpPYNvkPOTe_DUF4,8572
1088
+ django_cfg-1.4.97.dist-info/METADATA,sha256=cb_cXupXvXVnnbikccyCZZwHhzgsBSOa8OWbx76N524,23733
1089
+ django_cfg-1.4.97.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1090
+ django_cfg-1.4.97.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1091
+ django_cfg-1.4.97.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1092
+ django_cfg-1.4.97.dist-info/RECORD,,
Binary file