@statezero/core 0.2.16 → 0.2.17
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.
- package/LICENSE +116 -116
- package/dist/adaptors/vue/composables.js +4 -4
- package/dist/cli/commands/syncActions.js +189 -189
- package/dist/cli/commands/syncModels.js +391 -391
- package/dist/core/eventReceivers.d.ts +1 -2
- package/dist/core/eventReceivers.js +16 -16
- package/dist/flavours/django/f.js +1 -1
- package/dist/flavours/django/model.js +1 -1
- package/dist/flavours/django/operationFactory.d.ts +1 -1
- package/dist/flavours/django/operationFactory.js +1 -1
- package/dist/syncEngine/metrics/metricOptCalcs.js +1 -1
- package/dist/syncEngine/registries/metricRegistry.d.ts +1 -1
- package/dist/syncEngine/registries/metricRegistry.js +3 -3
- package/dist/syncEngine/stores/metricStore.d.ts +1 -1
- package/dist/syncEngine/stores/metricStore.js +1 -1
- package/dist/syncEngine/stores/operation.d.ts +1 -1
- package/dist/syncEngine/stores/operation.js +1 -1
- package/dist/syncEngine/stores/reactivity.d.ts +3 -3
- package/dist/syncEngine/stores/utils.js +1 -1
- package/dist/syncEngine/sync.d.ts +2 -1
- package/dist/syncEngine/sync.js +10 -9
- package/package.json +126 -126
- package/readme.md +210 -210
- package/dist/actions/backend1/django_app/calculate-hash.d.ts +0 -57
- package/dist/actions/backend1/django_app/calculate-hash.js +0 -80
- package/dist/actions/backend1/django_app/get-current-username.d.ts +0 -29
- package/dist/actions/backend1/django_app/get-current-username.js +0 -65
- package/dist/actions/backend1/django_app/get-server-status.d.ts +0 -38
- package/dist/actions/backend1/django_app/get-server-status.js +0 -68
- package/dist/actions/backend1/django_app/get-user-info.d.ts +0 -44
- package/dist/actions/backend1/django_app/get-user-info.js +0 -70
- package/dist/actions/backend1/django_app/index.d.ts +0 -6
- package/dist/actions/backend1/django_app/index.js +0 -6
- package/dist/actions/backend1/django_app/process-data.d.ts +0 -51
- package/dist/actions/backend1/django_app/process-data.js +0 -78
- package/dist/actions/backend1/django_app/send-notification.d.ts +0 -55
- package/dist/actions/backend1/django_app/send-notification.js +0 -81
- package/dist/actions/backend1/index.d.ts +0 -1
- package/dist/actions/backend1/index.js +0 -1
- package/dist/actions/default/django_app/calculate-hash.d.ts +0 -57
- package/dist/actions/default/django_app/calculate-hash.js +0 -80
- package/dist/actions/default/django_app/get-current-username.d.ts +0 -29
- package/dist/actions/default/django_app/get-current-username.js +0 -65
- package/dist/actions/default/django_app/get-server-status.d.ts +0 -38
- package/dist/actions/default/django_app/get-server-status.js +0 -68
- package/dist/actions/default/django_app/get-user-info.d.ts +0 -44
- package/dist/actions/default/django_app/get-user-info.js +0 -70
- package/dist/actions/default/django_app/index.d.ts +0 -6
- package/dist/actions/default/django_app/index.js +0 -6
- package/dist/actions/default/django_app/process-data.d.ts +0 -51
- package/dist/actions/default/django_app/process-data.js +0 -78
- package/dist/actions/default/django_app/send-notification.d.ts +0 -55
- package/dist/actions/default/django_app/send-notification.js +0 -81
- package/dist/actions/default/index.d.ts +0 -1
- package/dist/actions/default/index.js +0 -1
- package/dist/actions/index.d.ts +0 -1
- package/dist/actions/index.js +0 -5
- package/dist/models/backend1/django_app/comprehensivemodel.d.ts +0 -47
- package/dist/models/backend1/django_app/comprehensivemodel.js +0 -71
- package/dist/models/backend1/django_app/custompkmodel.d.ts +0 -44
- package/dist/models/backend1/django_app/custompkmodel.js +0 -69
- package/dist/models/backend1/django_app/dailyrate.d.ts +0 -47
- package/dist/models/backend1/django_app/dailyrate.js +0 -71
- package/dist/models/backend1/django_app/deepmodellevel1.d.ts +0 -47
- package/dist/models/backend1/django_app/deepmodellevel1.js +0 -72
- package/dist/models/backend1/django_app/deepmodellevel2.d.ts +0 -47
- package/dist/models/backend1/django_app/deepmodellevel2.js +0 -71
- package/dist/models/backend1/django_app/deepmodellevel3.d.ts +0 -44
- package/dist/models/backend1/django_app/deepmodellevel3.js +0 -69
- package/dist/models/backend1/django_app/dummymodel.d.ts +0 -47
- package/dist/models/backend1/django_app/dummymodel.js +0 -71
- package/dist/models/backend1/django_app/dummyrelatedmodel.d.ts +0 -44
- package/dist/models/backend1/django_app/dummyrelatedmodel.js +0 -69
- package/dist/models/backend1/django_app/filetest.d.ts +0 -44
- package/dist/models/backend1/django_app/filetest.js +0 -69
- package/dist/models/backend1/django_app/index.d.ts +0 -16
- package/dist/models/backend1/django_app/index.js +0 -16
- package/dist/models/backend1/django_app/modelwithcustompkrelation.d.ts +0 -47
- package/dist/models/backend1/django_app/modelwithcustompkrelation.js +0 -71
- package/dist/models/backend1/django_app/namefiltercustompkmodel.d.ts +0 -44
- package/dist/models/backend1/django_app/namefiltercustompkmodel.js +0 -69
- package/dist/models/backend1/django_app/order.d.ts +0 -47
- package/dist/models/backend1/django_app/order.js +0 -71
- package/dist/models/backend1/django_app/orderitem.d.ts +0 -47
- package/dist/models/backend1/django_app/orderitem.js +0 -72
- package/dist/models/backend1/django_app/product.d.ts +0 -47
- package/dist/models/backend1/django_app/product.js +0 -71
- package/dist/models/backend1/django_app/productcategory.d.ts +0 -44
- package/dist/models/backend1/django_app/productcategory.js +0 -69
- package/dist/models/backend1/django_app/rateplan.d.ts +0 -44
- package/dist/models/backend1/django_app/rateplan.js +0 -69
- package/dist/models/backend1/fileobject.d.ts +0 -4
- package/dist/models/backend1/fileobject.js +0 -9
- package/dist/models/backend1/index.d.ts +0 -2
- package/dist/models/backend1/index.js +0 -2
- package/dist/models/default/django_app/comprehensivemodel.d.ts +0 -47
- package/dist/models/default/django_app/comprehensivemodel.js +0 -71
- package/dist/models/default/django_app/custompkmodel.d.ts +0 -44
- package/dist/models/default/django_app/custompkmodel.js +0 -69
- package/dist/models/default/django_app/dailyrate.d.ts +0 -47
- package/dist/models/default/django_app/dailyrate.js +0 -71
- package/dist/models/default/django_app/deepmodellevel1.d.ts +0 -47
- package/dist/models/default/django_app/deepmodellevel1.js +0 -72
- package/dist/models/default/django_app/deepmodellevel2.d.ts +0 -47
- package/dist/models/default/django_app/deepmodellevel2.js +0 -71
- package/dist/models/default/django_app/deepmodellevel3.d.ts +0 -44
- package/dist/models/default/django_app/deepmodellevel3.js +0 -69
- package/dist/models/default/django_app/dummymodel.d.ts +0 -47
- package/dist/models/default/django_app/dummymodel.js +0 -71
- package/dist/models/default/django_app/dummyrelatedmodel.d.ts +0 -44
- package/dist/models/default/django_app/dummyrelatedmodel.js +0 -69
- package/dist/models/default/django_app/filetest.d.ts +0 -44
- package/dist/models/default/django_app/filetest.js +0 -69
- package/dist/models/default/django_app/index.d.ts +0 -16
- package/dist/models/default/django_app/index.js +0 -16
- package/dist/models/default/django_app/modelwithcustompkrelation.d.ts +0 -47
- package/dist/models/default/django_app/modelwithcustompkrelation.js +0 -71
- package/dist/models/default/django_app/namefiltercustompkmodel.d.ts +0 -44
- package/dist/models/default/django_app/namefiltercustompkmodel.js +0 -69
- package/dist/models/default/django_app/order.d.ts +0 -47
- package/dist/models/default/django_app/order.js +0 -71
- package/dist/models/default/django_app/orderitem.d.ts +0 -47
- package/dist/models/default/django_app/orderitem.js +0 -72
- package/dist/models/default/django_app/product.d.ts +0 -47
- package/dist/models/default/django_app/product.js +0 -71
- package/dist/models/default/django_app/productcategory.d.ts +0 -44
- package/dist/models/default/django_app/productcategory.js +0 -69
- package/dist/models/default/django_app/rateplan.d.ts +0 -44
- package/dist/models/default/django_app/rateplan.js +0 -69
- package/dist/models/default/fileobject.d.ts +0 -4
- package/dist/models/default/fileobject.js +0 -9
- package/dist/models/default/index.d.ts +0 -2
- package/dist/models/default/index.js +0 -2
- package/dist/models/index.d.ts +0 -1
- package/dist/models/index.js +0 -5
package/readme.md
CHANGED
|
@@ -1,210 +1,210 @@
|
|
|
1
|
-
# StateZero
|
|
2
|
-
|
|
3
|
-
**The Real-Time Django to JavaScript Data Bridge**
|
|
4
|
-
|
|
5
|
-
Connect your Django backend to React/Vue frontends with 90% less code. No repetitive serializers, views, or tight coupling.
|
|
6
|
-
|
|
7
|
-
## Why StateZero?
|
|
8
|
-
|
|
9
|
-
**The Problem:** Building modern web apps means writing the same CRUD logic three times - Django models, REST API serializers/views, and frontend data fetching. This creates:
|
|
10
|
-
|
|
11
|
-
- 80% of app complexity in data shuttling
|
|
12
|
-
- 50% of your codebase devoted to API glue
|
|
13
|
-
- Hundreds of hours maintaining sync between frontend and backend
|
|
14
|
-
|
|
15
|
-
**The Solution:** StateZero eliminates the API layer entirely. Write Django models once, query them directly from JavaScript with the same ORM syntax you already know.
|
|
16
|
-
|
|
17
|
-
## Features
|
|
18
|
-
|
|
19
|
-
✨ **Django ORM Syntax in JavaScript** - Use `.filter()`, `.exclude()`, `.orderBy()` exactly like Django
|
|
20
|
-
⚡ **Real-Time Updates** - UI automatically updates when backend data changes
|
|
21
|
-
🔒 **Django Permissions** - Your existing permission classes work on the frontend
|
|
22
|
-
📝 **Auto-Generated TypeScript** - Perfect type safety from your Django models
|
|
23
|
-
🚀 **Optimistic Updates** - UI feels instant, syncs in background
|
|
24
|
-
🔗 **Deep Relationships** - Traverse foreign keys naturally: `todo.category.name`
|
|
25
|
-
|
|
26
|
-
## Quick Example
|
|
27
|
-
|
|
28
|
-
### 1. Register Your Django Model
|
|
29
|
-
|
|
30
|
-
```python
|
|
31
|
-
# todos/crud.py
|
|
32
|
-
from statezero.adaptors.django.config import registry
|
|
33
|
-
from .models import Todo
|
|
34
|
-
|
|
35
|
-
registry.register(Todo)
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### 2. Query From JavaScript Like Django
|
|
39
|
-
|
|
40
|
-
```javascript
|
|
41
|
-
// Get all incomplete todos, ordered by priority
|
|
42
|
-
const todos = Todo.objects
|
|
43
|
-
.filter({ is_completed: false })
|
|
44
|
-
.orderBy("-priority", "created_at");
|
|
45
|
-
|
|
46
|
-
// Complex queries with relationships
|
|
47
|
-
const urgentWorkTodos = Todo.objects.filter({
|
|
48
|
-
priority: "high",
|
|
49
|
-
category__name: "Work",
|
|
50
|
-
due_date__lt: "2024-12-31",
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// Django-style field lookups
|
|
54
|
-
const searchResults = Todo.objects.filter({
|
|
55
|
-
title__icontains: "meeting",
|
|
56
|
-
created_by__email__endswith: "@company.com",
|
|
57
|
-
});
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### 3. Real-Time Updates in One Line
|
|
61
|
-
|
|
62
|
-
```vue
|
|
63
|
-
<script setup>
|
|
64
|
-
import { useQueryset } from "@statezero/core/vue";
|
|
65
|
-
|
|
66
|
-
// This list automatically updates when todos change
|
|
67
|
-
const todos = useQueryset(() => Todo.objects.filter({ is_completed: false }));
|
|
68
|
-
</script>
|
|
69
|
-
|
|
70
|
-
<template>
|
|
71
|
-
<div v-for="todo in todos.fetch({ limit: 10 })" :key="todo.id">
|
|
72
|
-
{{ todo.title }}
|
|
73
|
-
</div>
|
|
74
|
-
</template>
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
## The Magic: Optimistic vs Confirmed
|
|
78
|
-
|
|
79
|
-
### Optimistic (Instant UI)
|
|
80
|
-
|
|
81
|
-
```javascript
|
|
82
|
-
// UI updates immediately, syncs later
|
|
83
|
-
const newTodo = Todo.objects.create({
|
|
84
|
-
title: "Buy groceries",
|
|
85
|
-
priority: "medium",
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// Edit optimistically
|
|
89
|
-
todo.title = "Buy organic groceries";
|
|
90
|
-
todo.save(); // UI updates instantly
|
|
91
|
-
|
|
92
|
-
// Delete optimistically
|
|
93
|
-
todo.delete(); // Gone from UI immediately
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Confirmed (Wait for Server)
|
|
97
|
-
|
|
98
|
-
```javascript
|
|
99
|
-
// Wait for server confirmation
|
|
100
|
-
const confirmedTodo = await Todo.objects.create({
|
|
101
|
-
title: "Important meeting",
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// Wait for update confirmation
|
|
105
|
-
await todo.save();
|
|
106
|
-
|
|
107
|
-
// Wait for deletion confirmation
|
|
108
|
-
await todo.delete();
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Advanced Django ORM Features
|
|
112
|
-
|
|
113
|
-
### Complex Filtering with Q Objects
|
|
114
|
-
|
|
115
|
-
```javascript
|
|
116
|
-
import { Q } from "@statezero/core";
|
|
117
|
-
|
|
118
|
-
// Multiple OR conditions
|
|
119
|
-
const urgentTodos = Todo.objects.filter({
|
|
120
|
-
Q: [Q("OR", { priority: "high" }, { due_date__lt: "tomorrow" })],
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// Nested conditions
|
|
124
|
-
const myImportantTodos = Todo.objects.filter({
|
|
125
|
-
Q: [
|
|
126
|
-
Q(
|
|
127
|
-
"AND",
|
|
128
|
-
{ assigned_to: currentUser.id },
|
|
129
|
-
Q("OR", { priority: "high" }, { is_flagged: true })
|
|
130
|
-
),
|
|
131
|
-
],
|
|
132
|
-
});
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### Aggregation & F Expressions
|
|
136
|
-
|
|
137
|
-
```javascript
|
|
138
|
-
import { F } from "@statezero/core";
|
|
139
|
-
|
|
140
|
-
// Count, sum, average like Django
|
|
141
|
-
const todoCount = await Todo.objects.count();
|
|
142
|
-
const avgPriority = await Todo.objects.avg("priority_score");
|
|
143
|
-
|
|
144
|
-
// Database-level calculations
|
|
145
|
-
await Product.objects.update({
|
|
146
|
-
view_count: F("view_count + 1"),
|
|
147
|
-
popularity: F("likes * 2 + shares"),
|
|
148
|
-
});
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Get or Create
|
|
152
|
-
|
|
153
|
-
```javascript
|
|
154
|
-
// Just like Django's get_or_create
|
|
155
|
-
const [todo, created] = await Todo.objects.getOrCreate(
|
|
156
|
-
{ title: "Daily standup" },
|
|
157
|
-
{ priority: "medium", category: workCategory }
|
|
158
|
-
);
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### Relationship Traversal
|
|
162
|
-
|
|
163
|
-
```javascript
|
|
164
|
-
// Access related objects naturally
|
|
165
|
-
const todo = await Todo.objects.get({ id: 1 });
|
|
166
|
-
console.log(todo.category.name); // Foreign key
|
|
167
|
-
console.log(todo.created_by.username); // Another FK
|
|
168
|
-
console.log(todo.comments.length); // Reverse FK
|
|
169
|
-
|
|
170
|
-
// Filter by relationships
|
|
171
|
-
const workTodos = Todo.objects.filter({
|
|
172
|
-
category__name: "Work",
|
|
173
|
-
assigned_to__department__name: "Engineering",
|
|
174
|
-
});
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
## Installation
|
|
178
|
-
|
|
179
|
-
### Backend
|
|
180
|
-
|
|
181
|
-
```bash
|
|
182
|
-
pip install statezero
|
|
183
|
-
pip install django-cors-headers pusher
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Frontend
|
|
187
|
-
|
|
188
|
-
```bash
|
|
189
|
-
npm i @statezero/core
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### Generate TypeScript Models
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
npx @statezero/core sync
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## Why Choose StateZero Over...
|
|
199
|
-
|
|
200
|
-
**🆚 HTMX:** Use modern React/Vue with full JavaScript ecosystem while keeping backend simplicity
|
|
201
|
-
|
|
202
|
-
**🆚 Firebase/Supabase:** Keep your Django backend, models, and business logic. No vendor lock-in.
|
|
203
|
-
|
|
204
|
-
**🆚 OpenAPI/GraphQL:** Get real-time updates and Django ORM power, not just basic CRUD
|
|
205
|
-
|
|
206
|
-
**🆚 Traditional REST APIs:** Write 90% less boilerplate. Focus on features, not data plumbing.
|
|
207
|
-
|
|
208
|
-
## Get Started
|
|
209
|
-
|
|
210
|
-
Run `pip install statezero` and `npm install @statezero/core` to begin.
|
|
1
|
+
# StateZero
|
|
2
|
+
|
|
3
|
+
**The Real-Time Django to JavaScript Data Bridge**
|
|
4
|
+
|
|
5
|
+
Connect your Django backend to React/Vue frontends with 90% less code. No repetitive serializers, views, or tight coupling.
|
|
6
|
+
|
|
7
|
+
## Why StateZero?
|
|
8
|
+
|
|
9
|
+
**The Problem:** Building modern web apps means writing the same CRUD logic three times - Django models, REST API serializers/views, and frontend data fetching. This creates:
|
|
10
|
+
|
|
11
|
+
- 80% of app complexity in data shuttling
|
|
12
|
+
- 50% of your codebase devoted to API glue
|
|
13
|
+
- Hundreds of hours maintaining sync between frontend and backend
|
|
14
|
+
|
|
15
|
+
**The Solution:** StateZero eliminates the API layer entirely. Write Django models once, query them directly from JavaScript with the same ORM syntax you already know.
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
✨ **Django ORM Syntax in JavaScript** - Use `.filter()`, `.exclude()`, `.orderBy()` exactly like Django
|
|
20
|
+
⚡ **Real-Time Updates** - UI automatically updates when backend data changes
|
|
21
|
+
🔒 **Django Permissions** - Your existing permission classes work on the frontend
|
|
22
|
+
📝 **Auto-Generated TypeScript** - Perfect type safety from your Django models
|
|
23
|
+
🚀 **Optimistic Updates** - UI feels instant, syncs in background
|
|
24
|
+
🔗 **Deep Relationships** - Traverse foreign keys naturally: `todo.category.name`
|
|
25
|
+
|
|
26
|
+
## Quick Example
|
|
27
|
+
|
|
28
|
+
### 1. Register Your Django Model
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# todos/crud.py
|
|
32
|
+
from statezero.adaptors.django.config import registry
|
|
33
|
+
from .models import Todo
|
|
34
|
+
|
|
35
|
+
registry.register(Todo)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Query From JavaScript Like Django
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
// Get all incomplete todos, ordered by priority
|
|
42
|
+
const todos = Todo.objects
|
|
43
|
+
.filter({ is_completed: false })
|
|
44
|
+
.orderBy("-priority", "created_at");
|
|
45
|
+
|
|
46
|
+
// Complex queries with relationships
|
|
47
|
+
const urgentWorkTodos = Todo.objects.filter({
|
|
48
|
+
priority: "high",
|
|
49
|
+
category__name: "Work",
|
|
50
|
+
due_date__lt: "2024-12-31",
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Django-style field lookups
|
|
54
|
+
const searchResults = Todo.objects.filter({
|
|
55
|
+
title__icontains: "meeting",
|
|
56
|
+
created_by__email__endswith: "@company.com",
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 3. Real-Time Updates in One Line
|
|
61
|
+
|
|
62
|
+
```vue
|
|
63
|
+
<script setup>
|
|
64
|
+
import { useQueryset } from "@statezero/core/vue";
|
|
65
|
+
|
|
66
|
+
// This list automatically updates when todos change
|
|
67
|
+
const todos = useQueryset(() => Todo.objects.filter({ is_completed: false }));
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<template>
|
|
71
|
+
<div v-for="todo in todos.fetch({ limit: 10 })" :key="todo.id">
|
|
72
|
+
{{ todo.title }}
|
|
73
|
+
</div>
|
|
74
|
+
</template>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## The Magic: Optimistic vs Confirmed
|
|
78
|
+
|
|
79
|
+
### Optimistic (Instant UI)
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
// UI updates immediately, syncs later
|
|
83
|
+
const newTodo = Todo.objects.create({
|
|
84
|
+
title: "Buy groceries",
|
|
85
|
+
priority: "medium",
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Edit optimistically
|
|
89
|
+
todo.title = "Buy organic groceries";
|
|
90
|
+
todo.save(); // UI updates instantly
|
|
91
|
+
|
|
92
|
+
// Delete optimistically
|
|
93
|
+
todo.delete(); // Gone from UI immediately
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Confirmed (Wait for Server)
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
// Wait for server confirmation
|
|
100
|
+
const confirmedTodo = await Todo.objects.create({
|
|
101
|
+
title: "Important meeting",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Wait for update confirmation
|
|
105
|
+
await todo.save();
|
|
106
|
+
|
|
107
|
+
// Wait for deletion confirmation
|
|
108
|
+
await todo.delete();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Advanced Django ORM Features
|
|
112
|
+
|
|
113
|
+
### Complex Filtering with Q Objects
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
import { Q } from "@statezero/core";
|
|
117
|
+
|
|
118
|
+
// Multiple OR conditions
|
|
119
|
+
const urgentTodos = Todo.objects.filter({
|
|
120
|
+
Q: [Q("OR", { priority: "high" }, { due_date__lt: "tomorrow" })],
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Nested conditions
|
|
124
|
+
const myImportantTodos = Todo.objects.filter({
|
|
125
|
+
Q: [
|
|
126
|
+
Q(
|
|
127
|
+
"AND",
|
|
128
|
+
{ assigned_to: currentUser.id },
|
|
129
|
+
Q("OR", { priority: "high" }, { is_flagged: true })
|
|
130
|
+
),
|
|
131
|
+
],
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Aggregation & F Expressions
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
import { F } from "@statezero/core";
|
|
139
|
+
|
|
140
|
+
// Count, sum, average like Django
|
|
141
|
+
const todoCount = await Todo.objects.count();
|
|
142
|
+
const avgPriority = await Todo.objects.avg("priority_score");
|
|
143
|
+
|
|
144
|
+
// Database-level calculations
|
|
145
|
+
await Product.objects.update({
|
|
146
|
+
view_count: F("view_count + 1"),
|
|
147
|
+
popularity: F("likes * 2 + shares"),
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Get or Create
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
// Just like Django's get_or_create
|
|
155
|
+
const [todo, created] = await Todo.objects.getOrCreate(
|
|
156
|
+
{ title: "Daily standup" },
|
|
157
|
+
{ priority: "medium", category: workCategory }
|
|
158
|
+
);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Relationship Traversal
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
// Access related objects naturally
|
|
165
|
+
const todo = await Todo.objects.get({ id: 1 });
|
|
166
|
+
console.log(todo.category.name); // Foreign key
|
|
167
|
+
console.log(todo.created_by.username); // Another FK
|
|
168
|
+
console.log(todo.comments.length); // Reverse FK
|
|
169
|
+
|
|
170
|
+
// Filter by relationships
|
|
171
|
+
const workTodos = Todo.objects.filter({
|
|
172
|
+
category__name: "Work",
|
|
173
|
+
assigned_to__department__name: "Engineering",
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Installation
|
|
178
|
+
|
|
179
|
+
### Backend
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
pip install statezero
|
|
183
|
+
pip install django-cors-headers pusher
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Frontend
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
npm i @statezero/core
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Generate TypeScript Models
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
npx @statezero/core sync
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Why Choose StateZero Over...
|
|
199
|
+
|
|
200
|
+
**🆚 HTMX:** Use modern React/Vue with full JavaScript ecosystem while keeping backend simplicity
|
|
201
|
+
|
|
202
|
+
**🆚 Firebase/Supabase:** Keep your Django backend, models, and business logic. No vendor lock-in.
|
|
203
|
+
|
|
204
|
+
**🆚 OpenAPI/GraphQL:** Get real-time updates and Django ORM power, not just basic CRUD
|
|
205
|
+
|
|
206
|
+
**🆚 Traditional REST APIs:** Write 90% less boilerplate. Focus on features, not data plumbing.
|
|
207
|
+
|
|
208
|
+
## Get Started
|
|
209
|
+
|
|
210
|
+
Run `pip install statezero` and `npm install @statezero/core` to begin.
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Calculate hash of input text (demonstrates string processing)
|
|
3
|
-
*
|
|
4
|
-
* @param string text - Text to hash
|
|
5
|
-
* @param 'md5' | 'sha1' | 'sha256' algorithm - Hash algorithm to use
|
|
6
|
-
* @param {Object} [axiosOverrides] - Allows overriding Axios request parameters.
|
|
7
|
-
* @returns {Promise<Object>} A promise that resolves with the action's result.
|
|
8
|
-
*/
|
|
9
|
-
export function calculateHash(text: any, algorithm?: string, axiosOverrides?: Object): Promise<Object>;
|
|
10
|
-
export namespace calculateHash {
|
|
11
|
-
let actionName: string;
|
|
12
|
-
let title: string;
|
|
13
|
-
let app: string;
|
|
14
|
-
let permissions: string[];
|
|
15
|
-
let configKey: string;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Zod schema for the input of calculateHash.
|
|
19
|
-
* NOTE: This is an object schema for validating the data payload.
|
|
20
|
-
*/
|
|
21
|
-
export const calculateHashInputSchema: z.ZodObject<{
|
|
22
|
-
text: z.ZodString;
|
|
23
|
-
algorithm: z.ZodDefault<z.ZodOptional<z.ZodEnum<["md5", "sha1", "sha256"]>>>;
|
|
24
|
-
}, "strip", z.ZodTypeAny, {
|
|
25
|
-
algorithm: "md5" | "sha1" | "sha256";
|
|
26
|
-
text: string;
|
|
27
|
-
}, {
|
|
28
|
-
text: string;
|
|
29
|
-
algorithm?: "md5" | "sha1" | "sha256" | undefined;
|
|
30
|
-
}>;
|
|
31
|
-
/**
|
|
32
|
-
* Zod schema for the response of calculateHash.
|
|
33
|
-
*/
|
|
34
|
-
export const calculateHashResponseSchema: z.ZodObject<{
|
|
35
|
-
original_text: z.ZodString;
|
|
36
|
-
algorithm: z.ZodString;
|
|
37
|
-
hash: z.ZodString;
|
|
38
|
-
text_length: z.ZodNumber;
|
|
39
|
-
processed_by: z.ZodString;
|
|
40
|
-
processed_at: z.ZodString;
|
|
41
|
-
}, "strip", z.ZodTypeAny, {
|
|
42
|
-
original_text: string;
|
|
43
|
-
algorithm: string;
|
|
44
|
-
hash: string;
|
|
45
|
-
text_length: number;
|
|
46
|
-
processed_by: string;
|
|
47
|
-
processed_at: string;
|
|
48
|
-
}, {
|
|
49
|
-
original_text: string;
|
|
50
|
-
algorithm: string;
|
|
51
|
-
hash: string;
|
|
52
|
-
text_length: number;
|
|
53
|
-
processed_by: string;
|
|
54
|
-
processed_at: string;
|
|
55
|
-
}>;
|
|
56
|
-
export default calculateHash;
|
|
57
|
-
import { z } from 'zod';
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file was auto-generated. Do not make direct changes to the file.
|
|
3
|
-
* Action: Calculate Hash
|
|
4
|
-
* App: django_app
|
|
5
|
-
*/
|
|
6
|
-
import axios from 'axios';
|
|
7
|
-
import { z } from 'zod';
|
|
8
|
-
import { configInstance, parseStateZeroError, serializeActionPayload } from '../../../../src';
|
|
9
|
-
import actionSchema from './calculate-hash.schema.json' assert { type: 'json' };
|
|
10
|
-
/**
|
|
11
|
-
* Zod schema for the input of calculateHash.
|
|
12
|
-
* NOTE: This is an object schema for validating the data payload.
|
|
13
|
-
*/
|
|
14
|
-
export const calculateHashInputSchema = z.object({ text: z.string(),
|
|
15
|
-
algorithm: z.enum(["md5", "sha1", "sha256"]).optional().default("sha256") });
|
|
16
|
-
/**
|
|
17
|
-
* Zod schema for the response of calculateHash.
|
|
18
|
-
*/
|
|
19
|
-
export const calculateHashResponseSchema = z.object({ original_text: z.string(),
|
|
20
|
-
algorithm: z.string(),
|
|
21
|
-
hash: z.string(),
|
|
22
|
-
text_length: z.number().int(),
|
|
23
|
-
processed_by: z.string(),
|
|
24
|
-
processed_at: z.string().datetime({ message: "Invalid ISO 8601 datetime string" }) });
|
|
25
|
-
/**
|
|
26
|
-
* Calculate hash of input text (demonstrates string processing)
|
|
27
|
-
*
|
|
28
|
-
* @param string text - Text to hash
|
|
29
|
-
* @param 'md5' | 'sha1' | 'sha256' algorithm - Hash algorithm to use
|
|
30
|
-
* @param {Object} [axiosOverrides] - Allows overriding Axios request parameters.
|
|
31
|
-
* @returns {Promise<Object>} A promise that resolves with the action's result.
|
|
32
|
-
*/
|
|
33
|
-
export async function calculateHash(text, algorithm = "sha256", axiosOverrides = {}) {
|
|
34
|
-
// Construct the data payload from the function arguments
|
|
35
|
-
const rawPayload = {
|
|
36
|
-
text,
|
|
37
|
-
algorithm
|
|
38
|
-
};
|
|
39
|
-
// Serialize payload - handles model instances (extracts PK), files, dates, etc.
|
|
40
|
-
const payload = serializeActionPayload(rawPayload, actionSchema.input_properties);
|
|
41
|
-
const config = configInstance.getConfig();
|
|
42
|
-
const backend = config.backendConfigs['backend1'];
|
|
43
|
-
if (!backend) {
|
|
44
|
-
throw new Error(`No backend configuration found for key: backend1`);
|
|
45
|
-
}
|
|
46
|
-
const baseUrl = backend.API_URL.replace(/\/+$/, '');
|
|
47
|
-
const actionUrl = `${baseUrl}/actions/calculate_hash/`;
|
|
48
|
-
const headers = backend.getAuthHeaders ? backend.getAuthHeaders() : {};
|
|
49
|
-
try {
|
|
50
|
-
const response = await axios.post(actionUrl, payload, {
|
|
51
|
-
headers: { 'Content-Type': 'application/json', ...headers },
|
|
52
|
-
...axiosOverrides,
|
|
53
|
-
});
|
|
54
|
-
return calculateHashResponseSchema.parse(response.data);
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
if (error instanceof z.ZodError) {
|
|
58
|
-
throw new Error(`Calculate Hash failed: Invalid response from server. Details: ${error.message}`);
|
|
59
|
-
}
|
|
60
|
-
if (error.response && error.response.data) {
|
|
61
|
-
const parsedError = parseStateZeroError(error.response.data);
|
|
62
|
-
if (Error.captureStackTrace) {
|
|
63
|
-
Error.captureStackTrace(parsedError, calculateHash);
|
|
64
|
-
}
|
|
65
|
-
throw parsedError;
|
|
66
|
-
}
|
|
67
|
-
else if (error.request) {
|
|
68
|
-
throw new Error(`Calculate Hash failed: No response received from server.`);
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
throw new Error(`Calculate Hash failed: ${error.message}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
export default calculateHash;
|
|
76
|
-
calculateHash.actionName = 'calculate_hash';
|
|
77
|
-
calculateHash.title = 'Calculate Hash';
|
|
78
|
-
calculateHash.app = 'django_app';
|
|
79
|
-
calculateHash.permissions = ['IsAuthenticated'];
|
|
80
|
-
calculateHash.configKey = 'backend1';
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Get current user's username - simple focused action
|
|
3
|
-
*
|
|
4
|
-
* @param {Object} [axiosOverrides] - Allows overriding Axios request parameters.
|
|
5
|
-
* @returns {Promise<Object>} A promise that resolves with the action's result.
|
|
6
|
-
*/
|
|
7
|
-
export function getCurrentUsername(axiosOverrides?: Object): Promise<Object>;
|
|
8
|
-
export namespace getCurrentUsername {
|
|
9
|
-
let actionName: string;
|
|
10
|
-
let title: string;
|
|
11
|
-
let app: string;
|
|
12
|
-
let permissions: string[];
|
|
13
|
-
let configKey: string;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Zod schema for the response of getCurrentUsername.
|
|
17
|
-
*/
|
|
18
|
-
export const getCurrentUsernameResponseSchema: z.ZodObject<{
|
|
19
|
-
username: z.ZodString;
|
|
20
|
-
retrieved_at: z.ZodString;
|
|
21
|
-
}, "strip", z.ZodTypeAny, {
|
|
22
|
-
username: string;
|
|
23
|
-
retrieved_at: string;
|
|
24
|
-
}, {
|
|
25
|
-
username: string;
|
|
26
|
-
retrieved_at: string;
|
|
27
|
-
}>;
|
|
28
|
-
export default getCurrentUsername;
|
|
29
|
-
import { z } from 'zod';
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file was auto-generated. Do not make direct changes to the file.
|
|
3
|
-
* Action: Get Current Username
|
|
4
|
-
* App: django_app
|
|
5
|
-
*/
|
|
6
|
-
import axios from 'axios';
|
|
7
|
-
import { z } from 'zod';
|
|
8
|
-
import { configInstance, parseStateZeroError, serializeActionPayload } from '../../../../src';
|
|
9
|
-
import actionSchema from './get-current-username.schema.json' assert { type: 'json' };
|
|
10
|
-
/**
|
|
11
|
-
* Zod schema for the response of getCurrentUsername.
|
|
12
|
-
*/
|
|
13
|
-
export const getCurrentUsernameResponseSchema = z.object({ username: z.string(),
|
|
14
|
-
retrieved_at: z.string().datetime({ message: "Invalid ISO 8601 datetime string" }) });
|
|
15
|
-
/**
|
|
16
|
-
* Get current user's username - simple focused action
|
|
17
|
-
*
|
|
18
|
-
* @param {Object} [axiosOverrides] - Allows overriding Axios request parameters.
|
|
19
|
-
* @returns {Promise<Object>} A promise that resolves with the action's result.
|
|
20
|
-
*/
|
|
21
|
-
export async function getCurrentUsername(axiosOverrides = {}) {
|
|
22
|
-
// Construct the data payload from the function arguments
|
|
23
|
-
const rawPayload = {};
|
|
24
|
-
// Serialize payload - handles model instances (extracts PK), files, dates, etc.
|
|
25
|
-
const payload = serializeActionPayload(rawPayload, actionSchema.input_properties);
|
|
26
|
-
const config = configInstance.getConfig();
|
|
27
|
-
const backend = config.backendConfigs['backend1'];
|
|
28
|
-
if (!backend) {
|
|
29
|
-
throw new Error(`No backend configuration found for key: backend1`);
|
|
30
|
-
}
|
|
31
|
-
const baseUrl = backend.API_URL.replace(/\/+$/, '');
|
|
32
|
-
const actionUrl = `${baseUrl}/actions/get_current_username/`;
|
|
33
|
-
const headers = backend.getAuthHeaders ? backend.getAuthHeaders() : {};
|
|
34
|
-
try {
|
|
35
|
-
const response = await axios.post(actionUrl, payload, {
|
|
36
|
-
headers: { 'Content-Type': 'application/json', ...headers },
|
|
37
|
-
...axiosOverrides,
|
|
38
|
-
});
|
|
39
|
-
return getCurrentUsernameResponseSchema.parse(response.data);
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
if (error instanceof z.ZodError) {
|
|
43
|
-
throw new Error(`Get Current Username failed: Invalid response from server. Details: ${error.message}`);
|
|
44
|
-
}
|
|
45
|
-
if (error.response && error.response.data) {
|
|
46
|
-
const parsedError = parseStateZeroError(error.response.data);
|
|
47
|
-
if (Error.captureStackTrace) {
|
|
48
|
-
Error.captureStackTrace(parsedError, getCurrentUsername);
|
|
49
|
-
}
|
|
50
|
-
throw parsedError;
|
|
51
|
-
}
|
|
52
|
-
else if (error.request) {
|
|
53
|
-
throw new Error(`Get Current Username failed: No response received from server.`);
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
throw new Error(`Get Current Username failed: ${error.message}`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
export default getCurrentUsername;
|
|
61
|
-
getCurrentUsername.actionName = 'get_current_username';
|
|
62
|
-
getCurrentUsername.title = 'Get Current Username';
|
|
63
|
-
getCurrentUsername.app = 'django_app';
|
|
64
|
-
getCurrentUsername.permissions = ['IsAuthenticated'];
|
|
65
|
-
getCurrentUsername.configKey = 'backend1';
|