statezero 0.1.0b9__tar.gz → 0.1.0b10__tar.gz

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 statezero might be problematic. Click here for more details.

Files changed (55) hide show
  1. {statezero-0.1.0b9 → statezero-0.1.0b10}/PKG-INFO +1 -13
  2. {statezero-0.1.0b9 → statezero-0.1.0b10}/README.md +1 -13
  3. {statezero-0.1.0b9 → statezero-0.1.0b10}/pyproject.toml +1 -1
  4. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/actions.py +54 -11
  5. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/exception_handler.py +1 -1
  6. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/ast_parser.py +26 -26
  7. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/interfaces.py +3 -6
  8. statezero-0.1.0b10/statezero/core/types.py +29 -0
  9. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero.egg-info/PKG-INFO +1 -13
  10. statezero-0.1.0b9/statezero/core/types.py +0 -63
  11. {statezero-0.1.0b9 → statezero-0.1.0b10}/license.md +0 -0
  12. {statezero-0.1.0b9 → statezero-0.1.0b10}/requirements.txt +0 -0
  13. {statezero-0.1.0b9 → statezero-0.1.0b10}/setup.cfg +0 -0
  14. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/__init__.py +0 -0
  15. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/__init__.py +0 -0
  16. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/__init__.py +0 -0
  17. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/apps.py +0 -0
  18. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/config.py +0 -0
  19. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/context_manager.py +0 -0
  20. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/event_emitters.py +0 -0
  21. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/extensions/__init__.py +0 -0
  22. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/extensions/custom_field_serializers/__init__.py +0 -0
  23. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/extensions/custom_field_serializers/file_fields.py +0 -0
  24. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/extensions/custom_field_serializers/money_field.py +0 -0
  25. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/f_handler.py +0 -0
  26. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/helpers.py +0 -0
  27. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/middleware.py +0 -0
  28. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/migrations/0001_initial.py +0 -0
  29. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/migrations/0002_delete_modelviewsubscription.py +0 -0
  30. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/migrations/__init__.py +0 -0
  31. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/orm.py +0 -0
  32. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/permissions.py +0 -0
  33. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/query_optimizer.py +0 -0
  34. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/schemas.py +0 -0
  35. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/search_providers/__init__.py +0 -0
  36. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/search_providers/basic_search.py +0 -0
  37. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/search_providers/postgres_search.py +0 -0
  38. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/serializers.py +0 -0
  39. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/urls.py +0 -0
  40. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/adaptors/django/views.py +0 -0
  41. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/__init__.py +0 -0
  42. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/actions.py +0 -0
  43. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/ast_validator.py +0 -0
  44. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/classes.py +0 -0
  45. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/config.py +0 -0
  46. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/context_storage.py +0 -0
  47. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/event_bus.py +0 -0
  48. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/event_emitters.py +0 -0
  49. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/exceptions.py +0 -0
  50. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/hook_checks.py +0 -0
  51. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero/core/process_request.py +0 -0
  52. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero.egg-info/SOURCES.txt +0 -0
  53. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero.egg-info/dependency_links.txt +0 -0
  54. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero.egg-info/requires.txt +0 -0
  55. {statezero-0.1.0b9 → statezero-0.1.0b10}/statezero.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statezero
3
- Version: 0.1.0b9
3
+ Version: 0.1.0b10
4
4
  Summary: Connect your Python backend to a modern JavaScript SPA frontend with 90% less complexity.
5
5
  Author-email: Robert <robert.herring@statezero.dev>
6
6
  Project-URL: homepage, https://www.statezero.dev
@@ -240,15 +240,3 @@ npx statezero sync
240
240
  Check out the docs at [Statezero Docs](https://statezero.dev)
241
241
 
242
242
  Run `pip install statezero` and `npm i @statezero/core` to begin.
243
-
244
- ## Pricing
245
-
246
- StateZero uses a no-rugpull license model:
247
-
248
- - **$0/month** for companies with revenue up to $3M
249
- - **$75/month** for companies with revenue up to $7.5M
250
- - **$200/month** for companies with revenue up to $20M
251
- - **$500/month** for companies with revenue up to $100M
252
- - **$1,000/month** for companies with revenue above $100M
253
-
254
- Lock in your rate forever by signing up early. We can't change your fee or cancel your license.
@@ -209,16 +209,4 @@ npx statezero sync
209
209
 
210
210
  Check out the docs at [Statezero Docs](https://statezero.dev)
211
211
 
212
- Run `pip install statezero` and `npm i @statezero/core` to begin.
213
-
214
- ## Pricing
215
-
216
- StateZero uses a no-rugpull license model:
217
-
218
- - **$0/month** for companies with revenue up to $3M
219
- - **$75/month** for companies with revenue up to $7.5M
220
- - **$200/month** for companies with revenue up to $20M
221
- - **$500/month** for companies with revenue up to $100M
222
- - **$1,000/month** for companies with revenue above $100M
223
-
224
- Lock in your rate forever by signing up early. We can't change your fee or cancel your license.
212
+ Run `pip install statezero` and `npm i @statezero/core` to begin.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "statezero"
7
- version = "0.1.0b9"
7
+ version = "0.1.0b10"
8
8
  description = "Connect your Python backend to a modern JavaScript SPA frontend with 90% less complexity."
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -1,10 +1,10 @@
1
1
  import os
2
2
  from django.apps import apps
3
3
  from rest_framework.response import Response
4
- from rest_framework import fields
4
+ from rest_framework import fields, serializers
5
+ from django.db import models
5
6
  from statezero.core.actions import action_registry
6
7
 
7
-
8
8
  class DjangoActionSchemaGenerator:
9
9
  """Django-specific action schema generator that matches StateZero model schema format"""
10
10
 
@@ -41,6 +41,13 @@ class DjangoActionSchemaGenerator:
41
41
  app_name = found_app.label
42
42
  docstring = action_config.get("docstring")
43
43
 
44
+ input_properties, input_relationships = DjangoActionSchemaGenerator._get_serializer_schema(
45
+ action_config["serializer"]
46
+ )
47
+ response_properties, response_relationships = DjangoActionSchemaGenerator._get_serializer_schema(
48
+ action_config["response_serializer"]
49
+ )
50
+
44
51
  schema_info = {
45
52
  "action_name": action_name,
46
53
  "app": app_name,
@@ -49,12 +56,9 @@ class DjangoActionSchemaGenerator:
49
56
  "class_name": "".join(
50
57
  word.capitalize() for word in action_name.split("_")
51
58
  ),
52
- "input_properties": DjangoActionSchemaGenerator._get_serializer_schema(
53
- action_config["serializer"]
54
- ),
55
- "response_properties": DjangoActionSchemaGenerator._get_serializer_schema(
56
- action_config["response_serializer"]
57
- ),
59
+ "input_properties": input_properties,
60
+ "response_properties": response_properties,
61
+ "relationships": {**input_relationships, **response_relationships},
58
62
  "permissions": [
59
63
  perm.__name__ for perm in action_config.get("permissions", [])
60
64
  ],
@@ -66,11 +70,16 @@ class DjangoActionSchemaGenerator:
66
70
  @staticmethod
67
71
  def _get_serializer_schema(serializer_class):
68
72
  if not serializer_class:
69
- return {}
73
+ return {}, {}
70
74
  try:
71
75
  serializer_instance = serializer_class()
72
76
  properties = {}
77
+ relationships = {}
73
78
  for field_name, field in serializer_instance.fields.items():
79
+ relation_info = DjangoActionSchemaGenerator._get_relation_info(field)
80
+ if relation_info:
81
+ relationships[field_name] = relation_info
82
+
74
83
  field_info = {
75
84
  "type": DjangoActionSchemaGenerator._get_field_type(field),
76
85
  "title": getattr(field, "label")
@@ -95,12 +104,19 @@ class DjangoActionSchemaGenerator:
95
104
  if hasattr(field, "min_length") and field.min_length is not None:
96
105
  field_info["min_length"] = field.min_length
97
106
  properties[field_name] = field_info
98
- return properties
107
+ return properties, relationships
99
108
  except Exception as e:
100
- return {"error": f"Could not inspect serializer: {str(e)}"}
109
+ print(f"Could not inspect serializer: {str(e)}")
110
+ raise e
101
111
 
102
112
  @staticmethod
103
113
  def _get_field_type(field):
114
+ if isinstance(field, serializers.PrimaryKeyRelatedField):
115
+ pk_field = field.queryset.model._meta.pk
116
+ if isinstance(pk_field, (models.UUIDField, models.CharField)):
117
+ return "string"
118
+ return "integer"
119
+
104
120
  type_mapping = {
105
121
  fields.BooleanField: "boolean",
106
122
  fields.CharField: "string",
@@ -116,6 +132,7 @@ class DjangoActionSchemaGenerator:
116
132
  fields.JSONField: "object",
117
133
  fields.DictField: "object",
118
134
  fields.ListField: "array",
135
+ serializers.ManyRelatedField: "array",
119
136
  }
120
137
  return type_mapping.get(type(field), "string")
121
138
 
@@ -128,6 +145,8 @@ class DjangoActionSchemaGenerator:
128
145
  fields.DateField: "date",
129
146
  fields.DateTimeField: "date-time",
130
147
  fields.TimeField: "time",
148
+ serializers.ManyRelatedField: "many-to-many",
149
+ serializers.PrimaryKeyRelatedField: "foreign-key",
131
150
  }
132
151
  return format_mapping.get(type(field))
133
152
 
@@ -169,3 +188,27 @@ class DjangoActionSchemaGenerator:
169
188
  return None
170
189
  return default
171
190
  return None
191
+
192
+ @staticmethod
193
+ def _get_relation_info(field):
194
+ relation_type = DjangoActionSchemaGenerator._get_field_format(field)
195
+ if not relation_type in ["foreign-key", "one-to-one", "many-to-many"]:
196
+ return None
197
+
198
+ if isinstance(field, serializers.PrimaryKeyRelatedField):
199
+ model = field.queryset.model
200
+ return {
201
+ "type": relation_type,
202
+ "model": f"{model._meta.app_label}.{model._meta.model_name}",
203
+ "class_name": model.__name__,
204
+ "primary_key_field": model._meta.pk.name,
205
+ }
206
+ if isinstance(field, serializers.ManyRelatedField):
207
+ model = field.child_relation.queryset.model
208
+ return {
209
+ "type": relation_type,
210
+ "model": f"{model._meta.app_label}.{model._meta.model_name}",
211
+ "class_name": model.__name__,
212
+ "primary_key_field": model._meta.pk.name,
213
+ }
214
+ return None
@@ -62,7 +62,7 @@ def map_exception(exc):
62
62
  def explicit_exception_handler(exc):
63
63
  """
64
64
  Extended explicit exception handler that builds a structured JSON response.
65
- It maps known Django/DRF exceptions to your library's standard errors and
65
+ It maps known Django/DRF exceptions to StateZero's standard errors and
66
66
  uses jsonable_encoder to ensure the output is JSON serializable.
67
67
  """
68
68
  traceback.print_exc()
@@ -416,7 +416,7 @@ class ASTParser:
416
416
  return handler(ast)
417
417
 
418
418
  def _apply_related(self, ast: Dict[str, Any]) -> None:
419
- """UPDATED: Apply select_related and prefetch_related, updating current queryset."""
419
+ """ Apply select_related and prefetch_related, updating current queryset."""
420
420
  if "selectRelated" in ast and isinstance(ast["selectRelated"], list):
421
421
  self.current_queryset = self.engine.select_related(
422
422
  self.current_queryset, ast["selectRelated"]
@@ -427,28 +427,28 @@ class ASTParser:
427
427
  )
428
428
 
429
429
  def _apply_filter(self, ast: Dict[str, Any]) -> None:
430
- """UPDATED: Apply filter from AST to the queryset, updating current queryset."""
430
+ """ Apply filter from AST to the queryset, updating current queryset."""
431
431
  if "filter" in ast and ast["filter"]:
432
432
  self.current_queryset = self.engine.filter_node(
433
433
  self.current_queryset, ast["filter"]
434
434
  )
435
435
 
436
436
  def _apply_exclude(self, ast: Dict[str, Any]) -> None:
437
- """UPDATED: Apply exclude from AST to the queryset, updating current queryset."""
437
+ """ Apply exclude from AST to the queryset, updating current queryset."""
438
438
  if "exclude" in ast and ast["exclude"]:
439
439
  self.current_queryset = self.engine.exclude_node(
440
440
  self.current_queryset, ast["exclude"]
441
441
  )
442
442
 
443
443
  def _apply_ordering(self, ast: Dict[str, Any]) -> None:
444
- """UPDATED: Apply ordering, updating current queryset."""
444
+ """ Apply ordering, updating current queryset."""
445
445
  if "orderBy" in ast:
446
446
  self.current_queryset = self.engine.order_by(
447
447
  self.current_queryset, ast["orderBy"]
448
448
  )
449
449
 
450
450
  def _apply_field_selection(self, ast: Dict[str, Any]) -> None:
451
- """UPDATED: Apply field selection, updating current queryset."""
451
+ """ Apply field selection, updating current queryset."""
452
452
  if "fields" in ast and isinstance(ast["fields"], list):
453
453
  self.current_queryset = self.engine.select_fields(
454
454
  self.current_queryset, ast["fields"]
@@ -456,7 +456,7 @@ class ASTParser:
456
456
 
457
457
  def _apply_search(self, ast: Dict[str, Any]) -> None:
458
458
  """
459
- UPDATED: If search properties are present at the top level of the AST,
459
+ If search properties are present at the top level of the AST,
460
460
  apply the search using the adapter's search_node() method.
461
461
 
462
462
  Expects the AST to have a top-level "search" key containing:
@@ -489,7 +489,7 @@ class ASTParser:
489
489
  else:
490
490
  final_search_fields = config_search_fields
491
491
 
492
- # UPDATED: Delegate to the ORM adapter's search_node() method with queryset.
492
+ # Delegate to the ORM adapter's search_node() method with queryset.
493
493
  self.current_queryset = self.engine.search_node(
494
494
  self.current_queryset, search_query, final_search_fields
495
495
  )
@@ -497,7 +497,7 @@ class ASTParser:
497
497
  # --- Operation Handlers with Hard-Coded Response Types ---
498
498
 
499
499
  def _handle_create(self, ast: Dict[str, Any]) -> Dict[str, Any]:
500
- """UPDATED: Pass model explicitly to create method."""
500
+ """ Pass model explicitly to create method."""
501
501
  data = ast.get("data", {})
502
502
  validated_data = self.serializer.deserialize(
503
503
  model=self.model,
@@ -526,7 +526,7 @@ class ASTParser:
526
526
  }
527
527
 
528
528
  def _handle_update(self, ast: Dict[str, Any]) -> Dict[str, Any]:
529
- """UPDATED: Pass current queryset to update method."""
529
+ """ Pass current queryset to update method."""
530
530
  data = ast.get("data", {})
531
531
  validated_data = self.serializer.deserialize(
532
532
  model=self.model,
@@ -543,7 +543,7 @@ class ASTParser:
543
543
  # Get the readable fields for this model using our existing method
544
544
  readable_fields = self._get_operation_fields(self.model, "read")
545
545
 
546
- # UPDATED: Update records and get the count and affected instance IDs
546
+ # Update records and get the count and affected instance IDs
547
547
  updated_count, updated_instances = self.engine.update(
548
548
  self.current_queryset, # Pass current queryset
549
549
  ast,
@@ -570,7 +570,7 @@ class ASTParser:
570
570
  }
571
571
 
572
572
  def _handle_delete(self, ast: Dict[str, Any]) -> Dict[str, Any]:
573
- """UPDATED: Pass current queryset to delete method."""
573
+ """ Pass current queryset to delete method."""
574
574
  permissions = self.registry.get_config(self.model).permissions
575
575
  deleted_count, rows_deleted = self.engine.delete(
576
576
  self.current_queryset, ast, self.request, permissions
@@ -586,7 +586,7 @@ class ASTParser:
586
586
  }
587
587
 
588
588
  def _handle_update_instance(self, ast: Dict[str, Any]) -> Dict[str, Any]:
589
- """UPDATED: Pass model explicitly to update_instance method."""
589
+ """ Pass model explicitly to update_instance method."""
590
590
  # Extract and deserialize the data.
591
591
  raw_data = ast.get("data", {})
592
592
  # Allow partial updates.
@@ -603,7 +603,7 @@ class ASTParser:
603
603
  # Retrieve permissions from the self.registry.
604
604
  permissions = self.registry.get_config(self.model).permissions
605
605
 
606
- # UPDATED: Delegate to the engine's instance-based update method.
606
+ # Delegate to the engine's instance-based update method.
607
607
  updated_instance = self.engine.update_instance(
608
608
  self.model,
609
609
  ast,
@@ -628,7 +628,7 @@ class ASTParser:
628
628
 
629
629
  def _handle_delete_instance(self, ast: Dict[str, Any]) -> Dict[str, Any]:
630
630
  """
631
- UPDATED: Handles deletion of a single instance.
631
+ Handles deletion of a single instance.
632
632
  Typically, no additional data deserialization is needed beyond the filter,
633
633
  so we simply verify that a filter is provided and then delegate to the engine.
634
634
  """
@@ -643,7 +643,7 @@ class ASTParser:
643
643
  # Retrieve permissions from the self.registry.
644
644
  permissions = self.registry.get_config(self.model).permissions
645
645
 
646
- # UPDATED: Delegate to the engine's instance-based delete method.
646
+ # Delegate to the engine's instance-based delete method.
647
647
  deleted_count = self.engine.delete_instance(
648
648
  self.model, ast, self.request, permissions
649
649
  )
@@ -654,7 +654,7 @@ class ASTParser:
654
654
  }
655
655
 
656
656
  def _handle_get(self, ast: Dict[str, Any]) -> Dict[str, Any]:
657
- """UPDATED: Pass current queryset to get method."""
657
+ """ Pass current queryset to get method."""
658
658
  # Retrieve permissions from the registry
659
659
  permissions = self.registry.get_config(self.model).permissions
660
660
  record = self.engine.get(self.current_queryset, ast, self.request, permissions)
@@ -671,7 +671,7 @@ class ASTParser:
671
671
  }
672
672
 
673
673
  def _handle_get_or_create(self, ast: Dict[str, Any]) -> Dict[str, Any]:
674
- """UPDATED: Pass current queryset to get_or_create method."""
674
+ """ Pass current queryset to get_or_create method."""
675
675
  # Validate and split lookup/defaults (without extra wrapping)
676
676
  validated_lookup, validated_defaults = self._validate_and_split_lookup_defaults(
677
677
  ast, partial=True
@@ -684,7 +684,7 @@ class ASTParser:
684
684
  # Retrieve permissions from configuration
685
685
  permissions = self.registry.get_config(self.model).permissions
686
686
 
687
- # UPDATED: Call the ORM layer and pass the serializer and request/permissions
687
+ # Call the ORM layer and pass the serializer and request/permissions
688
688
  record, created = self.engine.get_or_create(
689
689
  self.current_queryset, # Pass current queryset
690
690
  {"lookup": ast.get("lookup", {}), "defaults": ast.get("defaults", {})},
@@ -710,7 +710,7 @@ class ASTParser:
710
710
  }
711
711
 
712
712
  def _handle_update_or_create(self, ast: Dict[str, Any]) -> Dict[str, Any]:
713
- """UPDATED: Pass current queryset to update_or_create method."""
713
+ """ Pass current queryset to update_or_create method."""
714
714
  # Validate and split lookup/defaults.
715
715
  validated_lookup, validated_defaults = self._validate_and_split_lookup_defaults(
716
716
  ast, partial=True
@@ -723,7 +723,7 @@ class ASTParser:
723
723
  # Retrieve permissions from configuration.
724
724
  permissions = self.registry.get_config(self.model).permissions
725
725
 
726
- # UPDATED: Call the ORM update_or_create method, passing the serializer, request, and permissions.
726
+ # Call the ORM update_or_create method, passing the serializer, request, and permissions.
727
727
  record, created = self.engine.update_or_create(
728
728
  self.current_queryset, # Pass current queryset
729
729
  {"lookup": ast.get("lookup", {}), "defaults": ast.get("defaults", {})},
@@ -750,7 +750,7 @@ class ASTParser:
750
750
  }
751
751
 
752
752
  def _handle_first(self, ast: Dict[str, Any]) -> Dict[str, Any]:
753
- """UPDATED: Pass current queryset to first method."""
753
+ """ Pass current queryset to first method."""
754
754
  record = self.engine.first(self.current_queryset)
755
755
  serialized = self.serializer.serialize(
756
756
  record,
@@ -765,7 +765,7 @@ class ASTParser:
765
765
  }
766
766
 
767
767
  def _handle_last(self, ast: Dict[str, Any]) -> Dict[str, Any]:
768
- """UPDATED: Pass current queryset to last method."""
768
+ """ Pass current queryset to last method."""
769
769
  record = self.engine.last(self.current_queryset)
770
770
  serialized = self.serializer.serialize(
771
771
  record,
@@ -780,7 +780,7 @@ class ASTParser:
780
780
  }
781
781
 
782
782
  def _handle_exists(self, ast: Dict[str, Any]) -> Dict[str, Any]:
783
- """UPDATED: Pass current queryset to exists method."""
783
+ """ Pass current queryset to exists method."""
784
784
  exists_flag = self.engine.exists(self.current_queryset)
785
785
  return {
786
786
  "data": exists_flag,
@@ -791,7 +791,7 @@ class ASTParser:
791
791
  }
792
792
 
793
793
  def _handle_aggregate(self, ast: Dict[str, Any]) -> Dict[str, Any]:
794
- """UPDATED: Pass current queryset to all aggregate methods."""
794
+ """ Pass current queryset to all aggregate methods."""
795
795
  op_type = ast.get("type")
796
796
  if op_type == "aggregate":
797
797
  aggs = ast.get("aggregates", {})
@@ -859,7 +859,7 @@ class ASTParser:
859
859
  }
860
860
 
861
861
  def _handle_read(self, ast: Dict[str, Any]) -> Dict[str, Any]:
862
- """UPDATED: Pass current queryset to fetch_list method."""
862
+ """ Pass current queryset to fetch_list method."""
863
863
  offset_raw = self.serializer_options.get("offset", 0)
864
864
  limit_raw = self.serializer_options.get("limit", self.config.default_limit)
865
865
  offset_val = int(offset_raw) if offset_raw is not None else None
@@ -868,7 +868,7 @@ class ASTParser:
868
868
  # Retrieve permissions from configuration
869
869
  permissions = self.registry.get_config(self.model).permissions
870
870
 
871
- # UPDATED: Fetch list with bulk permission checks
871
+ # Fetch list with bulk permission checks
872
872
  rows = self.engine.fetch_list(
873
873
  self.current_queryset, # Pass current queryset
874
874
  offset=offset_val,
@@ -30,12 +30,9 @@ class AbstractORMProvider(ABC):
30
30
  A merged ORM engine interface that combines both query building (filtering,
31
31
  ordering, aggregation, etc.) and ORM provider responsibilities (queryset assembly,
32
32
  event signal registration, model graph construction, etc.).
33
-
34
- UPDATED: All query manipulation methods now take queryset parameters and return
35
- new querysets instead of mutating internal state.
36
33
  """
37
34
 
38
- # === Query Engine Methods (UPDATED: Now stateless) ===
35
+ # === Query Engine Methods ===
39
36
 
40
37
  @abstractmethod
41
38
  def validate(
@@ -145,7 +142,7 @@ class AbstractORMProvider(ABC):
145
142
  """
146
143
  pass
147
144
 
148
- # === Aggregate Methods (UPDATED: Take queryset parameter) ===
145
+ # === Aggregate Methods ===
149
146
 
150
147
  @abstractmethod
151
148
  def aggregate(
@@ -201,7 +198,7 @@ class AbstractORMProvider(ABC):
201
198
  """Return True if the queryset has any results; otherwise False."""
202
199
  pass
203
200
 
204
- # === CRUD Methods (UPDATED: Take model or queryset parameters explicitly) ===
201
+ # === CRUD Methods ===
205
202
 
206
203
  @abstractmethod
207
204
  def create(
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from enum import Enum, auto
5
+ from typing import (Any, Callable, Dict, List, Optional, Set, Type, TypeVar,
6
+ Union)
7
+
8
+ # Django imports
9
+ from django.db.models import Field as DjangoField
10
+ from django.db.models import Model as DjangoModel
11
+ from django.db.models.query import QuerySet as DjangoQuerySet
12
+ from rest_framework.request import Request as DRFRequest
13
+
14
+ # Type definitions, when we add FastAPI support, we wil turn these into unions
15
+ ORMField = DjangoField
16
+ ORMModel = DjangoModel
17
+ ORMQuerySet = DjangoQuerySet
18
+ RequestType = DRFRequest
19
+
20
+ class ActionType(Enum):
21
+ CREATE = "create"
22
+ READ = "read"
23
+ UPDATE = "update"
24
+ DELETE = "delete"
25
+ BULK_UPDATE = "bulk_update"
26
+ BULK_DELETE = "bulk_delete"
27
+ # new pre-operation types
28
+ PRE_UPDATE = "pre_update"
29
+ PRE_DELETE = "pre_delete"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statezero
3
- Version: 0.1.0b9
3
+ Version: 0.1.0b10
4
4
  Summary: Connect your Python backend to a modern JavaScript SPA frontend with 90% less complexity.
5
5
  Author-email: Robert <robert.herring@statezero.dev>
6
6
  Project-URL: homepage, https://www.statezero.dev
@@ -240,15 +240,3 @@ npx statezero sync
240
240
  Check out the docs at [Statezero Docs](https://statezero.dev)
241
241
 
242
242
  Run `pip install statezero` and `npm i @statezero/core` to begin.
243
-
244
- ## Pricing
245
-
246
- StateZero uses a no-rugpull license model:
247
-
248
- - **$0/month** for companies with revenue up to $3M
249
- - **$75/month** for companies with revenue up to $7.5M
250
- - **$200/month** for companies with revenue up to $20M
251
- - **$500/month** for companies with revenue up to $100M
252
- - **$1,000/month** for companies with revenue above $100M
253
-
254
- Lock in your rate forever by signing up early. We can't change your fee or cancel your license.
@@ -1,63 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from abc import ABC, abstractmethod
4
- from enum import Enum, auto
5
- from typing import (Any, Callable, Dict, List, Optional, Set, Type, TypeVar,
6
- Union)
7
-
8
- # Django imports
9
- try:
10
- from django.db.models import Field as DjangoField
11
- from django.db.models import Model as DjangoModel
12
- from django.db.models.query import QuerySet as DjangoQuerySet
13
- from rest_framework.request import Request as DRFRequest
14
- except ImportError:
15
- DjangoField = None
16
- DjangoQuerySet = None
17
- DjangoModel = None
18
- DRFRequest = object
19
-
20
- # SQLAlchemy imports
21
- try:
22
- from sqlalchemy.ext.declarative import \
23
- DeclarativeMeta as SQLAlchemyDeclarativeMeta
24
- from sqlalchemy.orm.query import Query as SQLAlchemyQuery
25
- from sqlalchemy.sql.schema import Column as SQLAlchemyColumn # type:ignore
26
- except ImportError:
27
- SQLAlchemyColumn = None
28
- SQLAlchemyQuery = None
29
- SQLAlchemyDeclarativeMeta = None
30
-
31
- # FastAPI & Flask imports
32
- try:
33
- from fastapi import Request as FastAPIRequest
34
- except ImportError:
35
- FastAPIRequest = object
36
-
37
- try:
38
- from flask import Request as FlaskRequest
39
- except ImportError:
40
- FlaskRequest = object
41
-
42
- # Type definitions
43
- # Explicitly list all possible types. Including 'object' as a fallback ensures the type remains valid
44
- ORMField = Union[object, DjangoField, SQLAlchemyColumn]
45
- ORMModel = Union[object, DjangoModel, SQLAlchemyDeclarativeMeta]
46
- ORMQuerySet = Union[Any, DjangoQuerySet, SQLAlchemyQuery]
47
- RequestType = Union[DRFRequest, FastAPIRequest, FlaskRequest]
48
-
49
- class HotPathActionType(Enum):
50
- CREATED = "created"
51
- COMPLETED = "completed"
52
- REJECTED = "rejected"
53
-
54
- class ActionType(Enum):
55
- CREATE = "create"
56
- READ = "read"
57
- UPDATE = "update"
58
- DELETE = "delete"
59
- BULK_UPDATE = "bulk_update"
60
- BULK_DELETE = "bulk_delete"
61
- # new pre-operation types
62
- PRE_UPDATE = "pre_update"
63
- PRE_DELETE = "pre_delete"
File without changes
File without changes