django-cfg 1.4.17__py3-none-any.whl → 1.4.19__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.
@@ -327,7 +327,7 @@ class TypeScriptGenerator(BaseGenerator):
327
327
 
328
328
  # Generate individual schema files
329
329
  for schema_name, schema in sorted(all_schemas.items()):
330
- # Skip enum schemas (they use z.nativeEnum from enums.ts)
330
+ # Skip enum schemas (they use z.enum() with literal values)
331
331
  if schema.enum:
332
332
  continue
333
333
 
@@ -162,17 +162,22 @@ class ModelsGenerator:
162
162
  if ts_type.endswith(" | null"):
163
163
  ts_type = ts_type[:-7] # Remove " | null"
164
164
  else:
165
- # Get TypeScript type and remove | null suffix if present
165
+ # Get TypeScript type
166
166
  ts_type = schema.typescript_type
167
+ # Remove | null suffix to rebuild it properly based on schema.nullable
167
168
  if ts_type.endswith(" | null"):
168
169
  ts_type = ts_type[:-7] # Remove " | null"
169
170
 
170
171
  # Check if required
171
172
  is_required = name in required_fields
172
173
 
173
- # Optional marker - use for both non-required AND nullable fields
174
- # This converts Django's nullable=True to TypeScript's optional (undefined)
175
- optional_marker = "" if is_required and not schema.nullable else "?"
174
+ # Handle nullable and optional separately
175
+ # - nullable: add | null to type
176
+ # - not required: add ? optional marker
177
+ if schema.nullable:
178
+ ts_type = f"{ts_type} | null"
179
+
180
+ optional_marker = "" if is_required else "?"
176
181
 
177
182
  # Comment
178
183
  if schema.description:
@@ -98,7 +98,9 @@ class OperationsGenerator:
98
98
  # Return type
99
99
  primary_response = operation.primary_success_response
100
100
  if primary_response and primary_response.schema_name:
101
- if operation.is_list_operation:
101
+ # Check if response is paginated (has 'results' field)
102
+ is_paginated = primary_response.schema_name.startswith('Paginated')
103
+ if operation.is_list_operation and not is_paginated:
102
104
  return_type = f"Models.{primary_response.schema_name}[]"
103
105
  else:
104
106
  return_type = f"Models.{primary_response.schema_name}"
@@ -242,8 +244,14 @@ class OperationsGenerator:
242
244
 
243
245
  # Handle response
244
246
  if operation.is_list_operation and primary_response:
245
- # Extract results from paginated response
246
- body_lines.append("return (response as any).results || [];")
247
+ # Check if response is paginated
248
+ is_paginated = primary_response.schema_name.startswith('Paginated')
249
+ if is_paginated:
250
+ # Return full paginated response object
251
+ body_lines.append("return response;")
252
+ else:
253
+ # Extract results from array response
254
+ body_lines.append("return (response as any).results || [];")
247
255
  elif return_type != "void":
248
256
  body_lines.append("return response;")
249
257
  else:
@@ -3,7 +3,7 @@ Zod Schemas Generator - Generates Zod validation schemas from IR.
3
3
 
4
4
  This generator creates Zod schemas for runtime validation:
5
5
  - Object schemas (z.object)
6
- - Enum schemas (z.nativeEnum)
6
+ - Enum schemas (z.enum)
7
7
  - Array schemas (z.array)
8
8
  - Type inference (z.infer<typeof Schema>)
9
9
  """
@@ -22,7 +22,7 @@ class SchemasGenerator:
22
22
  Features:
23
23
  - Runtime validation with Zod
24
24
  - Type inference from schemas
25
- - Enum validation with z.nativeEnum()
25
+ - Enum validation with z.enum()
26
26
  - Nested object validation
27
27
  - Array and nullable types
28
28
  """
@@ -46,7 +46,7 @@ class SchemasGenerator:
46
46
  >>> generate_schema(User)
47
47
  export const UserSchema = z.object({
48
48
  id: z.number(),
49
- email: z.string().email(),
49
+ email: z.email(),
50
50
  username: z.string().min(1).max(150),
51
51
  })
52
52
  """
@@ -92,20 +92,20 @@ class SchemasGenerator:
92
92
 
93
93
  Examples:
94
94
  id: z.number()
95
- email: z.string().email()
95
+ email: z.email()
96
96
  username: z.string().min(1).max(150)
97
- status: z.nativeEnum(Enums.StatusEnum)
98
- created_at: z.string().datetime()
97
+ status: z.nativeEnum(Enums.StatusEnum) # Reference to TypeScript enum
98
+ created_at: z.iso.datetime()
99
99
  """
100
100
  # Check if this field is an enum
101
101
  if schema.enum and schema.name:
102
- # Use enum validation
102
+ # Use nativeEnum to reference TypeScript enum from enums.ts
103
103
  zod_type = f"z.nativeEnum(Enums.{self.base.sanitize_enum_name(schema.name)})"
104
104
  # Check if this field is a reference to an enum
105
105
  elif schema.ref and schema.ref in self.context.schemas:
106
106
  ref_schema = self.context.schemas[schema.ref]
107
107
  if ref_schema.enum:
108
- # Reference to enum component
108
+ # Reference to enum component - use nativeEnum
109
109
  zod_type = f"z.nativeEnum(Enums.{self.base.sanitize_enum_name(schema.ref)})"
110
110
  else:
111
111
  # Reference to another schema
@@ -117,9 +117,13 @@ class SchemasGenerator:
117
117
  # Check if required
118
118
  is_required = name in required_fields
119
119
 
120
- # Handle optional fields - use .optional() for both non-required AND nullable
121
- # This converts Django's nullable=True to TypeScript's optional (undefined)
122
- if not is_required or schema.nullable:
120
+ # Handle nullable and optional separately
121
+ # - nullable: field can be null (use .nullable())
122
+ # - not required: field can be undefined (use .optional())
123
+ if schema.nullable:
124
+ zod_type = f"{zod_type}.nullable()"
125
+
126
+ if not is_required:
123
127
  zod_type = f"{zod_type}.optional()"
124
128
 
125
129
  return f"{name}: {zod_type}"
@@ -136,10 +140,10 @@ class SchemasGenerator:
136
140
 
137
141
  Examples:
138
142
  string -> z.string()
139
- string (format: email) -> z.string().email()
140
- string (format: date-time) -> z.string().datetime()
141
- string (format: uri) -> z.string().url()
142
- integer -> z.number().int()
143
+ string (format: email) -> z.email()
144
+ string (format: date-time) -> z.iso.datetime()
145
+ string (format: uri) -> z.url()
146
+ integer -> z.int()
143
147
  number -> z.number()
144
148
  boolean -> z.boolean()
145
149
  array -> z.array(...)
@@ -151,35 +155,36 @@ class SchemasGenerator:
151
155
  if schema_type == "string":
152
156
  base_type = "z.string()"
153
157
 
154
- # Add format validation
158
+ # Add format validation - use new Zod v4 format APIs
155
159
  if schema_format == "email":
156
- base_type = "z.string().email()"
160
+ base_type = "z.email()"
157
161
  elif schema_format in ("date-time", "datetime"):
158
- base_type = "z.string().datetime()"
162
+ base_type = "z.iso.datetime()"
159
163
  elif schema_format == "date":
160
- base_type = "z.string().date()"
164
+ base_type = "z.iso.date()"
161
165
  elif schema_format in ("uri", "url"):
162
- base_type = "z.string().url()"
166
+ base_type = "z.url()"
163
167
  elif schema_format == "uuid":
164
- base_type = "z.string().uuid()"
168
+ base_type = "z.uuid()"
165
169
 
166
- # Add length constraints
167
- if schema.min_length is not None:
168
- base_type = f"{base_type}.min({schema.min_length})"
169
- if schema.max_length is not None:
170
- base_type = f"{base_type}.max({schema.max_length})"
170
+ # Add length constraints (only for plain strings, not formatted ones)
171
+ if base_type == "z.string()":
172
+ if schema.min_length is not None:
173
+ base_type = f"{base_type}.min({schema.min_length})"
174
+ if schema.max_length is not None:
175
+ base_type = f"{base_type}.max({schema.max_length})"
171
176
 
172
- # Add pattern validation
173
- if schema.pattern:
174
- # Escape regex pattern for JS
175
- escaped_pattern = schema.pattern.replace('\\', '\\\\')
176
- base_type = f"{base_type}.regex(/{escaped_pattern}/)"
177
+ # Add pattern validation
178
+ if schema.pattern:
179
+ # Escape regex pattern for JS
180
+ escaped_pattern = schema.pattern.replace('\\', '\\\\')
181
+ base_type = f"{base_type}.regex(/{escaped_pattern}/)"
177
182
 
178
183
  return base_type
179
184
 
180
185
  # Integer type
181
186
  elif schema_type == "integer":
182
- base_type = "z.number().int()"
187
+ base_type = "z.int()"
183
188
 
184
189
  # Add range constraints
185
190
  if schema.minimum is not None:
@@ -246,7 +251,7 @@ class SchemasGenerator:
246
251
  return "\n".join(lines)
247
252
 
248
253
  def _generate_enum_schema(self, schema: IRSchemaObject) -> str:
249
- """Generate z.nativeEnum() schema."""
254
+ """Generate z.nativeEnum() schema that references TypeScript enum."""
250
255
  enum_name = self.base.sanitize_enum_name(schema.name)
251
256
 
252
257
  lines = []
@@ -254,6 +259,8 @@ class SchemasGenerator:
254
259
  if schema.description:
255
260
  lines.append(f"/**\n * {schema.description}\n */")
256
261
 
262
+ # Use z.nativeEnum to reference TypeScript enum from enums.ts
263
+ # This ensures type compatibility with models.ts
257
264
  lines.append(f"export const {enum_name}Schema = z.nativeEnum(Enums.{enum_name})")
258
265
 
259
266
  return "\n".join(lines)
@@ -17,6 +17,7 @@
17
17
  import useSWR from 'swr'
18
18
  import { useSWRConfig } from 'swr'
19
19
  import * as Fetchers from '../fetchers/{{ tag_file }}'
20
+ import type { API } from '../../index'
20
21
  {% if has_schemas %}
21
22
  {% for schema_name in schema_names %}
22
23
  import type { {{ schema_name }} } from '../schemas/{{ schema_name }}.schema'
@@ -7,11 +7,11 @@
7
7
  export function {{ hook_name }}() {
8
8
  const { mutate } = useSWRConfig()
9
9
 
10
- return async ({% if func_params %}{{ func_params }}{% endif %}): Promise<{{ response_type }}> => {
10
+ return async ({% if func_params %}{{ func_params }}, client?: API{% else %}client?: API{% endif %}): Promise<{{ response_type }}> => {
11
11
  {% if fetcher_params %}
12
- const result = await Fetchers.{{ fetcher_name }}({{ fetcher_params }})
12
+ const result = await Fetchers.{{ fetcher_name }}({{ fetcher_params }}, client)
13
13
  {% else %}
14
- const result = await Fetchers.{{ fetcher_name }}()
14
+ const result = await Fetchers.{{ fetcher_name }}(client)
15
15
  {% endif %}
16
16
  {% if revalidation_keys %}
17
17
  // Revalidate related queries
@@ -6,16 +6,15 @@
6
6
  */
7
7
  export function {{ hook_name }}(
8
8
  {%- if func_params -%}
9
- {{ func_params }}
10
- {%- endif -%}
11
- ) {
12
- return useSWR<{{ response_type }}>(
13
- {{ swr_key }},
14
- {%- if fetcher_params %}
15
- () => Fetchers.{{ fetcher_name }}({{ fetcher_params }})
9
+ {{ func_params }}, client?: API
16
10
  {%- else %}
17
- () => Fetchers.{{ fetcher_name }}()
11
+ client?: API
18
12
  {%- endif %}
19
- )
13
+ ): ReturnType<typeof useSWR<{{ response_type }}>> {
14
+ return useSWR<{{ response_type }}>(
15
+ {{ swr_key }},
16
+ {% if fetcher_params %} () => Fetchers.{{ fetcher_name }}({{ fetcher_params }}, client)
17
+ {% else %} () => Fetchers.{{ fetcher_name }}(client)
18
+ {% endif %} )
20
19
  }
21
20
 
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.17"
7
+ version = "1.4.19"
8
8
  description = "Django AI framework with built-in agents, type-safe Pydantic v2 configuration, and 8 enterprise apps. Replace settings.py, validate at startup, 90% less code. Production-ready AI workflows for Django."
9
9
  readme = "README.md"
10
10
  keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "ai-agents", "enterprise-django", "django-settings", "type-safe-config",]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.4.17
3
+ Version: 1.4.19
4
4
  Summary: Django AI framework with built-in agents, type-safe Pydantic v2 configuration, and 8 enterprise apps. Replace settings.py, validate at startup, 90% less code. Production-ready AI workflows for Django.
5
5
  Project-URL: Homepage, https://djangocfg.com
6
6
  Project-URL: Documentation, https://djangocfg.com
@@ -762,12 +762,12 @@ django_cfg/modules/django_client/core/generator/typescript/__init__.py,sha256=eH
762
762
  django_cfg/modules/django_client/core/generator/typescript/client_generator.py,sha256=7ql-m59YVt6zGKfVBCxy1OR3CNy6C9lkaMEUqexiRvo,5878
763
763
  django_cfg/modules/django_client/core/generator/typescript/fetchers_generator.py,sha256=ZiSXMyT7HV28IK0AE8p2Sma2xwFrWnzSurBZEaWw_Z8,13791
764
764
  django_cfg/modules/django_client/core/generator/typescript/files_generator.py,sha256=faRdhVVf9GQ-0esVz94dsaQMB56zK3csyNkhEHL4al4,7044
765
- django_cfg/modules/django_client/core/generator/typescript/generator.py,sha256=_xuQC10SJ1ZKwA2_h3te4ZkSmH2jlnjUr__EJuBwNVE,16982
765
+ django_cfg/modules/django_client/core/generator/typescript/generator.py,sha256=oBM7CKlsD7arzIt0dSeow7Ujuo6RWl6h5to221Q19Gc,16984
766
766
  django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py,sha256=urnmej5isaPwnyo-kH_VVGP9kehcdeI2hR9mqHmVN4s,15133
767
- django_cfg/modules/django_client/core/generator/typescript/models_generator.py,sha256=2cBA-YzeBkGekoKz0pzT-Rt56MCvRTT-lSb44AAujfk,8787
767
+ django_cfg/modules/django_client/core/generator/typescript/models_generator.py,sha256=i7I1PJPx4r6KLaLJDJUJ_euDnKWnRQSiNFosUkP1eoM,8870
768
768
  django_cfg/modules/django_client/core/generator/typescript/naming.py,sha256=uUh2XVmorQ8gzfdzZJB8KNPng6UI4axe3lEaFtTqYks,2592
769
- django_cfg/modules/django_client/core/generator/typescript/operations_generator.py,sha256=lY56TkDafI-CL6wv7GXWNpvDr7087W3oWRTC4bJLJ9k,13493
770
- django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py,sha256=tmFZ8yFJ_Nk_iT7QGDDUVSFJhg8WTF79lYDOqQDzz8c,10877
769
+ django_cfg/modules/django_client/core/generator/typescript/operations_generator.py,sha256=6S2xs04UTIC6dOAa9h2Yjhsp4CnPjmYRvRTAof9BHQY,13947
770
+ django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py,sha256=jM_3wzpQL3IgFGtDw4zWpCPYfiMfc1X1Yf6wEumamU4,11233
771
771
  django_cfg/modules/django_client/core/generator/typescript/templates/api_instance.ts.jinja,sha256=OPUjnz6Dk3kY97UFIRcgvxkEIKd6fUGqBzXJWOXKykE,2906
772
772
  django_cfg/modules/django_client/core/generator/typescript/templates/app_index.ts.jinja,sha256=gLsoYyEzKD6Gv64vsO9sQHMPiFMGdaB5XVufLHeRyvQ,62
773
773
  django_cfg/modules/django_client/core/generator/typescript/templates/client_file.ts.jinja,sha256=LHUt72fO2eRNAHYEscIYvqVR69GC6mxqjcgSlUzeCtc,251
@@ -784,10 +784,10 @@ django_cfg/modules/django_client/core/generator/typescript/templates/client/sub_
784
784
  django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja,sha256=HNeYh8LnTTmeKTQS7uup37Oq-_HwBPZH8qE1kneaJsg,1296
785
785
  django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/function.ts.jinja,sha256=cR4Y4eJ1ENcON9BcfI-ttnJfKxv61efMW2oVDrmJga8,580
786
786
  django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/index.ts.jinja,sha256=DoAVm8EoglJKtLrE917Fk7Na6rn5Bt9L1nI39o9AwzM,747
787
- django_cfg/modules/django_client/core/generator/typescript/templates/hooks/hooks.ts.jinja,sha256=VS9_Pl1IemR2mPA2df9h-5xXSE9suvttpSC1YPClagE,763
787
+ django_cfg/modules/django_client/core/generator/typescript/templates/hooks/hooks.ts.jinja,sha256=_iyvJBEQviLLtZ5z1qXNUM976zXPW-v9DXFyHJdrpCQ,802
788
788
  django_cfg/modules/django_client/core/generator/typescript/templates/hooks/index.ts.jinja,sha256=c4Jru6DAKD-y8nzig1my0iE-BO3OwjSCENALbHvAp7E,694
789
- django_cfg/modules/django_client/core/generator/typescript/templates/hooks/mutation_hook.ts.jinja,sha256=iVB8hXAt0VEw5Y4OsWsPJJmpxflr5LLid3l7xASxvRU,647
790
- django_cfg/modules/django_client/core/generator/typescript/templates/hooks/query_hook.ts.jinja,sha256=hdNU62F_xL4m9Lz5gEkkw2tny9dHguvpDRK9dz7CRy0,432
789
+ django_cfg/modules/django_client/core/generator/typescript/templates/hooks/mutation_hook.ts.jinja,sha256=pOeyEY9nkYKVARTN6P4dfEj_mg-CIuPd8rrxzfaCfF8,697
790
+ django_cfg/modules/django_client/core/generator/typescript/templates/hooks/query_hook.ts.jinja,sha256=_Qit1DTZQTYIM_l6gmBkS2Fy7358EsGrUWr0iwCwyhw,526
791
791
  django_cfg/modules/django_client/core/generator/typescript/templates/models/app_models.ts.jinja,sha256=hRJ06Z-L4M5IEB23dd_rVgwSzSEGmqaSsKVrg5NasY0,122
792
792
  django_cfg/modules/django_client/core/generator/typescript/templates/models/enums.ts.jinja,sha256=8BguyHxV4r5JdJD9wYYgZrClysIdlkjf_Bp5toU81u8,49
793
793
  django_cfg/modules/django_client/core/generator/typescript/templates/models/models.ts.jinja,sha256=mycvqd12K2LsnN4mnwMdQ_SepiIWx6e9gDlUMAGOoBA,121
@@ -1100,9 +1100,9 @@ django_cfg/utils/version_check.py,sha256=jI4v3YMdQriUEeb_TvRl511sDghy6I75iKRDUaN
1100
1100
  django_cfg/CHANGELOG.md,sha256=jtT3EprqEJkqSUh7IraP73vQ8PmKUMdRtznQsEnqDZk,2052
1101
1101
  django_cfg/CONTRIBUTING.md,sha256=DU2kyQ6PU0Z24ob7O_OqKWEYHcZmJDgzw-lQCmu6uBg,3041
1102
1102
  django_cfg/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1103
- django_cfg/pyproject.toml,sha256=cS3SRfVShKr8ndIdMpiDOJiH3gFLLZ_5MdWAlCU3vpU,8210
1104
- django_cfg-1.4.17.dist-info/METADATA,sha256=5kh9Jcmaml3LflWBK4tDei8CwFXViH-ZwapA6bumVBc,22533
1105
- django_cfg-1.4.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1106
- django_cfg-1.4.17.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1107
- django_cfg-1.4.17.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1108
- django_cfg-1.4.17.dist-info/RECORD,,
1103
+ django_cfg/pyproject.toml,sha256=UGYbf8bxudDXVeR9helgvuKB60_CZ7DJKvSdZbGti3c,8210
1104
+ django_cfg-1.4.19.dist-info/METADATA,sha256=OSuPmdcBTsGy2UJfTMgA1aAzdpwoZmDKaW3bch5XKmM,22533
1105
+ django_cfg-1.4.19.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1106
+ django_cfg-1.4.19.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1107
+ django_cfg-1.4.19.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1108
+ django_cfg-1.4.19.dist-info/RECORD,,