jac-client 0.2.3__py3-none-any.whl → 0.2.4__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.
- jac_client/examples/all-in-one/assets/workers/worker.py +5 -0
- jac_client/tests/conftest.py +281 -0
- jac_client/tests/test_cli.py +755 -0
- jac_client/tests/test_it.py +347 -67
- {jac_client-0.2.3.dist-info → jac_client-0.2.4.dist-info}/METADATA +28 -30
- jac_client-0.2.4.dist-info/RECORD +10 -0
- {jac_client-0.2.3.dist-info → jac_client-0.2.4.dist-info}/WHEEL +2 -1
- jac_client-0.2.4.dist-info/entry_points.txt +4 -0
- jac_client-0.2.4.dist-info/top_level.txt +1 -0
- jac_client/docs/README.md +0 -689
- jac_client/docs/advanced-state.md +0 -1265
- jac_client/docs/asset-serving/intro.md +0 -209
- jac_client/docs/assets/pipe_line-v2.svg +0 -32
- jac_client/docs/assets/pipe_line.png +0 -0
- jac_client/docs/file-system/app.jac.md +0 -121
- jac_client/docs/file-system/backend-frontend.md +0 -217
- jac_client/docs/file-system/intro.md +0 -72
- jac_client/docs/file-system/nested-imports.md +0 -348
- jac_client/docs/guide-example/intro.md +0 -115
- jac_client/docs/guide-example/step-01-setup.md +0 -270
- jac_client/docs/guide-example/step-02-components.md +0 -416
- jac_client/docs/guide-example/step-03-styling.md +0 -478
- jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
- jac_client/docs/guide-example/step-05-local-state.md +0 -530
- jac_client/docs/guide-example/step-06-events.md +0 -749
- jac_client/docs/guide-example/step-07-effects.md +0 -468
- jac_client/docs/guide-example/step-08-walkers.md +0 -534
- jac_client/docs/guide-example/step-09-authentication.md +0 -586
- jac_client/docs/guide-example/step-10-routing.md +0 -539
- jac_client/docs/guide-example/step-11-final.md +0 -963
- jac_client/docs/imports.md +0 -1141
- jac_client/docs/lifecycle-hooks.md +0 -773
- jac_client/docs/routing.md +0 -659
- jac_client/docs/styling/intro.md +0 -249
- jac_client/docs/styling/js-styling.md +0 -367
- jac_client/docs/styling/material-ui.md +0 -341
- jac_client/docs/styling/pure-css.md +0 -299
- jac_client/docs/styling/sass.md +0 -403
- jac_client/docs/styling/styled-components.md +0 -395
- jac_client/docs/styling/tailwind.md +0 -298
- jac_client/examples/all-in-one/.babelrc +0 -9
- jac_client/examples/all-in-one/README.md +0 -16
- jac_client/examples/all-in-one/app.jac +0 -426
- jac_client/examples/all-in-one/assets/burger.png +0 -0
- jac_client/examples/all-in-one/button.jac +0 -7
- jac_client/examples/all-in-one/components/button.jac +0 -7
- jac_client/examples/all-in-one/package.json +0 -29
- jac_client/examples/all-in-one/styles.css +0 -26
- jac_client/examples/all-in-one/vite.config.js +0 -28
- jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
- jac_client/examples/asset-serving/css-with-image/README.md +0 -91
- jac_client/examples/asset-serving/css-with-image/app.jac +0 -88
- jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
- jac_client/examples/asset-serving/css-with-image/package.json +0 -28
- jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
- jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
- jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
- jac_client/examples/asset-serving/image-asset/README.md +0 -119
- jac_client/examples/asset-serving/image-asset/app.jac +0 -55
- jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
- jac_client/examples/asset-serving/image-asset/package.json +0 -28
- jac_client/examples/asset-serving/image-asset/styles.css +0 -26
- jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
- jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
- jac_client/examples/asset-serving/import-alias/README.md +0 -83
- jac_client/examples/asset-serving/import-alias/app.jac +0 -111
- jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
- jac_client/examples/asset-serving/import-alias/package.json +0 -28
- jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
- jac_client/examples/basic/.babelrc +0 -9
- jac_client/examples/basic/README.md +0 -16
- jac_client/examples/basic/app.jac +0 -21
- jac_client/examples/basic/package.json +0 -27
- jac_client/examples/basic/vite.config.js +0 -27
- jac_client/examples/basic-auth/.babelrc +0 -9
- jac_client/examples/basic-auth/README.md +0 -16
- jac_client/examples/basic-auth/app.jac +0 -308
- jac_client/examples/basic-auth/package.json +0 -27
- jac_client/examples/basic-auth/vite.config.js +0 -27
- jac_client/examples/basic-auth-with-router/.babelrc +0 -9
- jac_client/examples/basic-auth-with-router/README.md +0 -60
- jac_client/examples/basic-auth-with-router/app.jac +0 -464
- jac_client/examples/basic-auth-with-router/package.json +0 -28
- jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
- jac_client/examples/basic-full-stack/.babelrc +0 -9
- jac_client/examples/basic-full-stack/README.md +0 -18
- jac_client/examples/basic-full-stack/app.jac +0 -320
- jac_client/examples/basic-full-stack/package.json +0 -28
- jac_client/examples/basic-full-stack/vite.config.js +0 -27
- jac_client/examples/css-styling/js-styling/.babelrc +0 -9
- jac_client/examples/css-styling/js-styling/README.md +0 -183
- jac_client/examples/css-styling/js-styling/app.jac +0 -84
- jac_client/examples/css-styling/js-styling/package.json +0 -28
- jac_client/examples/css-styling/js-styling/styles.js +0 -100
- jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
- jac_client/examples/css-styling/material-ui/.babelrc +0 -9
- jac_client/examples/css-styling/material-ui/README.md +0 -16
- jac_client/examples/css-styling/material-ui/app.jac +0 -122
- jac_client/examples/css-styling/material-ui/package.json +0 -32
- jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
- jac_client/examples/css-styling/pure-css/.babelrc +0 -9
- jac_client/examples/css-styling/pure-css/README.md +0 -16
- jac_client/examples/css-styling/pure-css/app.jac +0 -64
- jac_client/examples/css-styling/pure-css/package.json +0 -28
- jac_client/examples/css-styling/pure-css/styles.css +0 -111
- jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
- jac_client/examples/css-styling/sass-example/.babelrc +0 -9
- jac_client/examples/css-styling/sass-example/README.md +0 -16
- jac_client/examples/css-styling/sass-example/app.jac +0 -64
- jac_client/examples/css-styling/sass-example/package.json +0 -29
- jac_client/examples/css-styling/sass-example/styles.scss +0 -153
- jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
- jac_client/examples/css-styling/styled-components/.babelrc +0 -9
- jac_client/examples/css-styling/styled-components/README.md +0 -16
- jac_client/examples/css-styling/styled-components/app.jac +0 -71
- jac_client/examples/css-styling/styled-components/package.json +0 -29
- jac_client/examples/css-styling/styled-components/styled.js +0 -90
- jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
- jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
- jac_client/examples/css-styling/tailwind-example/README.md +0 -16
- jac_client/examples/css-styling/tailwind-example/app.jac +0 -63
- jac_client/examples/css-styling/tailwind-example/global.css +0 -1
- jac_client/examples/css-styling/tailwind-example/package.json +0 -30
- jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
- jac_client/examples/full-stack-with-auth/.babelrc +0 -9
- jac_client/examples/full-stack-with-auth/README.md +0 -16
- jac_client/examples/full-stack-with-auth/app.jac +0 -722
- jac_client/examples/full-stack-with-auth/package.json +0 -28
- jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
- jac_client/examples/little-x/app.jac +0 -724
- jac_client/examples/little-x/package.json +0 -23
- jac_client/examples/little-x/submit-button.jac +0 -8
- jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
- jac_client/examples/nested-folders/nested-advance/ButtonRoot.jac +0 -11
- jac_client/examples/nested-folders/nested-advance/README.md +0 -77
- jac_client/examples/nested-folders/nested-advance/app.jac +0 -35
- jac_client/examples/nested-folders/nested-advance/level1/ButtonSecondL.jac +0 -19
- jac_client/examples/nested-folders/nested-advance/level1/Card.jac +0 -43
- jac_client/examples/nested-folders/nested-advance/level1/level2/ButtonThirdL.jac +0 -25
- jac_client/examples/nested-folders/nested-advance/package.json +0 -29
- jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
- jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
- jac_client/examples/nested-folders/nested-basic/README.md +0 -183
- jac_client/examples/nested-folders/nested-basic/app.jac +0 -13
- jac_client/examples/nested-folders/nested-basic/app.js +0 -7
- jac_client/examples/nested-folders/nested-basic/button.jac +0 -7
- jac_client/examples/nested-folders/nested-basic/components/button.jac +0 -7
- jac_client/examples/nested-folders/nested-basic/package.json +0 -28
- jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
- jac_client/examples/with-router/.babelrc +0 -9
- jac_client/examples/with-router/README.md +0 -17
- jac_client/examples/with-router/app.jac +0 -323
- jac_client/examples/with-router/package.json +0 -28
- jac_client/examples/with-router/vite.config.js +0 -27
- jac_client/plugin/cli.py +0 -244
- jac_client/plugin/client.py +0 -152
- jac_client/plugin/client_runtime.jac +0 -234
- jac_client/plugin/vite_client_bundle.py +0 -503
- jac_client/tests/fixtures/basic-app/app.jac +0 -23
- jac_client/tests/fixtures/cl_file/app.cl.jac +0 -48
- jac_client/tests/fixtures/cl_file/app.jac +0 -15
- jac_client/tests/fixtures/client_app_with_antd/app.jac +0 -34
- jac_client/tests/fixtures/js_import/app.jac +0 -34
- jac_client/tests/fixtures/js_import/utils.js +0 -21
- jac_client/tests/fixtures/package-lock.json +0 -329
- jac_client/tests/fixtures/package.json +0 -11
- jac_client/tests/fixtures/relative_import/app.jac +0 -11
- jac_client/tests/fixtures/relative_import/button.jac +0 -7
- jac_client/tests/fixtures/spawn_test/app.jac +0 -129
- jac_client/tests/fixtures/test_fragments_spread/app.jac +0 -67
- jac_client/tests/test_asset_examples.py +0 -322
- jac_client/tests/test_cl.py +0 -530
- jac_client/tests/test_create_jac_app.py +0 -131
- jac_client/tests/test_nested_file.py +0 -374
- jac_client-0.2.3.dist-info/RECORD +0 -171
- jac_client-0.2.3.dist-info/entry_points.txt +0 -4
|
@@ -1,534 +0,0 @@
|
|
|
1
|
-
# Step 8: Backend with Walkers
|
|
2
|
-
|
|
3
|
-
> ** Quick Tip:** Each step has two parts. **Part 1** shows you what to build. **Part 2** explains why it works. Want to just build? Skip all Part 2 sections!
|
|
4
|
-
|
|
5
|
-
In this step, you'll add a **real backend** to your app using walkers - so your todos are stored on a server!
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Part 1: Building the App
|
|
10
|
-
|
|
11
|
-
### Step 8.1: Define the Todo Node
|
|
12
|
-
|
|
13
|
-
First, let's define our data structure. Add this **OUTSIDE** the `cl { }` block (at the top of your file):
|
|
14
|
-
|
|
15
|
-
```jac
|
|
16
|
-
# Backend - Data Model
|
|
17
|
-
node Todo {
|
|
18
|
-
has text: str;
|
|
19
|
-
has done: bool = False;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
# Backend - We'll add walkers here soon
|
|
23
|
-
|
|
24
|
-
cl import from react {useState, useEffect}
|
|
25
|
-
|
|
26
|
-
cl {
|
|
27
|
-
# ... your frontend code
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### Step 8.2: Create Your First Walker - Read Todos
|
|
32
|
-
|
|
33
|
-
Add these walkers **AFTER** the node definition but **BEFORE** the `cl {` block:
|
|
34
|
-
|
|
35
|
-
```jac
|
|
36
|
-
# Backend - Data Model
|
|
37
|
-
node Todo {
|
|
38
|
-
has text: str;
|
|
39
|
-
has done: bool = False;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
# Backend - Walkers
|
|
43
|
-
walker read_todos {
|
|
44
|
-
can read with `root entry {
|
|
45
|
-
visit [-->(`?Todo)];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
can report_todos with Todo entry {
|
|
49
|
-
report here;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
cl import from react {useState, useEffect}
|
|
54
|
-
|
|
55
|
-
cl {
|
|
56
|
-
# ... your frontend code
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Step 8.3: Create Walker for Adding Todos
|
|
61
|
-
|
|
62
|
-
Add this walker:
|
|
63
|
-
|
|
64
|
-
```jac
|
|
65
|
-
walker create_todo {
|
|
66
|
-
has text: str;
|
|
67
|
-
|
|
68
|
-
can create with `root entry {
|
|
69
|
-
new_todo = here ++> Todo(text=self.text);
|
|
70
|
-
report new_todo;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### Step 8.4: Create Walkers for Toggle and Delete
|
|
76
|
-
|
|
77
|
-
Add these walkers:
|
|
78
|
-
|
|
79
|
-
```jac
|
|
80
|
-
walker toggle_todo {
|
|
81
|
-
can toggle with Todo entry {
|
|
82
|
-
here.done = not here.done;
|
|
83
|
-
report here;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Your complete backend should now look like this:
|
|
89
|
-
|
|
90
|
-
```jac
|
|
91
|
-
# Backend - Data Model
|
|
92
|
-
node Todo {
|
|
93
|
-
has text: str;
|
|
94
|
-
has done: bool = False;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
# Backend - Walkers
|
|
98
|
-
walker create_todo {
|
|
99
|
-
has text: str;
|
|
100
|
-
|
|
101
|
-
can create with `root entry {
|
|
102
|
-
new_todo = here ++> Todo(text=self.text);
|
|
103
|
-
report new_todo;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
walker read_todos {
|
|
108
|
-
can read with `root entry {
|
|
109
|
-
visit [-->(`?Todo)];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
can report_todos with Todo entry {
|
|
113
|
-
report here;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
walker toggle_todo {
|
|
118
|
-
can toggle with Todo entry {
|
|
119
|
-
here.done = not here.done;
|
|
120
|
-
report here;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
# Frontend (keep all your existing code)
|
|
125
|
-
cl import from react {useState, useEffect}
|
|
126
|
-
|
|
127
|
-
cl {
|
|
128
|
-
# ... all your frontend components
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### Step 8.5: Call Walkers from Frontend - Load Todos
|
|
133
|
-
|
|
134
|
-
Update your `useEffect` to load todos from the backend:
|
|
135
|
-
|
|
136
|
-
```jac
|
|
137
|
-
def app() -> any {
|
|
138
|
-
let [todos, setTodos] = useState([]);
|
|
139
|
-
let [input, setInput] = useState("");
|
|
140
|
-
let [filter, setFilter] = useState("all");
|
|
141
|
-
|
|
142
|
-
# Load todos from backend when app mounts
|
|
143
|
-
useEffect(lambda -> None {
|
|
144
|
-
async def loadTodos() -> None {
|
|
145
|
-
result = root spawn read_todos();
|
|
146
|
-
setTodos(result.reports if result.reports else []);
|
|
147
|
-
}
|
|
148
|
-
loadTodos();
|
|
149
|
-
}, []);
|
|
150
|
-
|
|
151
|
-
# ... rest of your code
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Step 8.6: Call Walkers from Frontend - Add Todo
|
|
156
|
-
|
|
157
|
-
Update your `addTodo` function:
|
|
158
|
-
|
|
159
|
-
```jac
|
|
160
|
-
# Add todo
|
|
161
|
-
async def addTodo() -> None {
|
|
162
|
-
if not input.trim() {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
# Call backend walker
|
|
167
|
-
result = root spawn create_todo(text=input.trim());
|
|
168
|
-
|
|
169
|
-
# Add the new todo to local state
|
|
170
|
-
setTodos(todos.concat([result.reports[0][0]]));
|
|
171
|
-
setInput("");
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Step 8.7: Call Walkers from Frontend - Toggle and Delete
|
|
176
|
-
|
|
177
|
-
Update your toggle and delete functions:
|
|
178
|
-
|
|
179
|
-
```jac
|
|
180
|
-
# Toggle todo
|
|
181
|
-
async def toggleTodo(id: any) -> None {
|
|
182
|
-
# Call backend walker
|
|
183
|
-
id spawn toggle_todo();
|
|
184
|
-
|
|
185
|
-
# Update local state
|
|
186
|
-
setTodos(todos.map(lambda todo: any -> any {
|
|
187
|
-
if todo._jac_id == id {
|
|
188
|
-
return {
|
|
189
|
-
"_jac_id": todo._jac_id,
|
|
190
|
-
"text": todo.text,
|
|
191
|
-
"done": not todo.done
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
return todo;
|
|
195
|
-
}));
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
# Delete todo
|
|
199
|
-
async def deleteTodo(id: any) -> None {
|
|
200
|
-
# Call backend walker
|
|
201
|
-
#id spawn delete_todo();
|
|
202
|
-
|
|
203
|
-
# Update local state
|
|
204
|
-
setTodos(todos.filter(lambda todo: any -> bool {
|
|
205
|
-
return todo._jac_id != id;
|
|
206
|
-
}));
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Step 8.8: Update TodoItem to Use _jac_id
|
|
211
|
-
|
|
212
|
-
When rendering todos, use `_jac_id` instead of custom id:
|
|
213
|
-
|
|
214
|
-
```jac
|
|
215
|
-
# In your app() function
|
|
216
|
-
<div>
|
|
217
|
-
{filteredTodos.map(lambda todo: any -> any {
|
|
218
|
-
return <TodoItem
|
|
219
|
-
key={todo._jac_id}
|
|
220
|
-
id={todo._jac_id}
|
|
221
|
-
text={todo.text}
|
|
222
|
-
done={todo.done}
|
|
223
|
-
toggleTodo={toggleTodo}
|
|
224
|
-
deleteTodo={deleteTodo}
|
|
225
|
-
/>;
|
|
226
|
-
})}
|
|
227
|
-
</div>
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
**Try it!**
|
|
231
|
-
1. Add some todos
|
|
232
|
-
2. Check/uncheck them
|
|
233
|
-
3. Delete some
|
|
234
|
-
4. **Refresh the page** - your todos persist!
|
|
235
|
-
|
|
236
|
-
---
|
|
237
|
-
|
|
238
|
-
**⏭ Want to skip the theory?** Jump to [Step 9: Authentication](./step-09-authentication.md)
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## Part 2: Understanding the Concepts
|
|
243
|
-
|
|
244
|
-
### What Are Walkers?
|
|
245
|
-
|
|
246
|
-
Walkers are **backend functions** that:
|
|
247
|
-
- Run on the **server** (not in the browser)
|
|
248
|
-
- Can traverse your data graph
|
|
249
|
-
- Automatically become API endpoints
|
|
250
|
-
- Are called from your frontend
|
|
251
|
-
|
|
252
|
-
**Traditional way (Flask):**
|
|
253
|
-
|
|
254
|
-
```python
|
|
255
|
-
# Backend - separate file
|
|
256
|
-
@app.route("/api/todos", methods=["GET"])
|
|
257
|
-
def get_todos():
|
|
258
|
-
todos = db.query(Todo).all()
|
|
259
|
-
return jsonify(todos)
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
**Jac way:**
|
|
263
|
-
|
|
264
|
-
```jac
|
|
265
|
-
# Backend - same file!
|
|
266
|
-
walker read_todos {
|
|
267
|
-
can read with `root entry {
|
|
268
|
-
visit [-->(`?Todo)];
|
|
269
|
-
}
|
|
270
|
-
can report_todos with Todo entry {
|
|
271
|
-
report here;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
No routes, no manual API setup - it just works!
|
|
277
|
-
### The `spawn` Syntax
|
|
278
|
-
|
|
279
|
-
This is how you call walkers from your frontend:
|
|
280
|
-
|
|
281
|
-
```jac
|
|
282
|
-
# Syntax
|
|
283
|
-
node_reference spawn walker_name(parameters);
|
|
284
|
-
|
|
285
|
-
# Examples
|
|
286
|
-
root spawn read_todos(); # On root node
|
|
287
|
-
root spawn create_todo(text="New todo"); # With parameters
|
|
288
|
-
todoId spawn toggle_todo(); # On specific node
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
**What happens:**
|
|
292
|
-
1. Request sent to server
|
|
293
|
-
2. Walker runs on server
|
|
294
|
-
3. Data stored in backend
|
|
295
|
-
4. Response sent back to frontend
|
|
296
|
-
|
|
297
|
-
### Graph Structure
|
|
298
|
-
|
|
299
|
-
Your data is stored as a graph:
|
|
300
|
-
|
|
301
|
-
```
|
|
302
|
-
root (your root node)
|
|
303
|
-
|
|
|
304
|
-
+---> Todo("Learn Jac")
|
|
305
|
-
|
|
|
306
|
-
+---> Todo("Build app")
|
|
307
|
-
|
|
|
308
|
-
+---> Todo("Deploy")
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
When you call `read_todos`:
|
|
312
|
-
1. Walker starts at `root`
|
|
313
|
-
2. Follows edges (`-->`) to find Todo nodes
|
|
314
|
-
3. Reports each Todo found
|
|
315
|
-
|
|
316
|
-
### Creating Connections (++>)
|
|
317
|
-
|
|
318
|
-
```jac
|
|
319
|
-
new_todo = here ++> Todo(text=self.text);
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
**Breakdown:**
|
|
323
|
-
- `here` - Current node (root)
|
|
324
|
-
- `++>` - Create node and connect it
|
|
325
|
-
- `Todo(...)` - New node to create
|
|
326
|
-
- Result: New Todo connected to root
|
|
327
|
-
|
|
328
|
-
### Visiting Nodes
|
|
329
|
-
|
|
330
|
-
```jac
|
|
331
|
-
visit [-->(`?Todo)];
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
**Breakdown:**
|
|
335
|
-
- `visit` - Traverse to these nodes
|
|
336
|
-
- `-->` - Follow outgoing edges
|
|
337
|
-
- `` `?Todo `` - Find nodes of type Todo
|
|
338
|
-
- `[...]` - Array of nodes to visit
|
|
339
|
-
|
|
340
|
-
### Reporting Data
|
|
341
|
-
|
|
342
|
-
```jac
|
|
343
|
-
report new_todo; # Report a node
|
|
344
|
-
report here; # Report current node
|
|
345
|
-
report {"success": True}; # Report an object
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
`report` sends data back to the frontend. All reports are collected in the `result.reports` array.
|
|
349
|
-
|
|
350
|
-
### The `_jac_id` Field
|
|
351
|
-
|
|
352
|
-
Every node gets a unique `_jac_id`:
|
|
353
|
-
|
|
354
|
-
```jac
|
|
355
|
-
let todo = result.reports[0][0];
|
|
356
|
-
console.log(todo._jac_id); # "urn:uuid:abc123..."
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
Use this ID to reference specific nodes:
|
|
360
|
-
|
|
361
|
-
```jac
|
|
362
|
-
todoId spawn toggle_todo(); # Operates on that specific todo
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
### Backend vs Frontend Code
|
|
366
|
-
|
|
367
|
-
```jac
|
|
368
|
-
# Backend (runs on server)
|
|
369
|
-
node Todo {
|
|
370
|
-
has text: str;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
walker create_todo {
|
|
374
|
-
has text: str;
|
|
375
|
-
can create with `root entry {
|
|
376
|
-
# This code runs on the server
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
# Frontend (runs in browser)
|
|
381
|
-
cl {
|
|
382
|
-
def app() -> any {
|
|
383
|
-
# This code runs in the browser
|
|
384
|
-
let result = root spawn create_todo(text="Todo");
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### Data Persistence
|
|
390
|
-
|
|
391
|
-
**localStorage (Step 7):**
|
|
392
|
-
- Stored in browser only
|
|
393
|
-
- Lost when you clear browser data
|
|
394
|
-
- Not shared across devices
|
|
395
|
-
|
|
396
|
-
**Walkers (Step 8):**
|
|
397
|
-
- Stored on server
|
|
398
|
-
- Persists forever
|
|
399
|
-
- Accessible from any device
|
|
400
|
-
- Per-user (each user sees only their data)
|
|
401
|
-
|
|
402
|
-
### async/await for Walkers
|
|
403
|
-
|
|
404
|
-
Walker calls are asynchronous:
|
|
405
|
-
|
|
406
|
-
```jac
|
|
407
|
-
# Must use async/await
|
|
408
|
-
async def addTodo() -> None {
|
|
409
|
-
result = await root spawn create_todo(text="Todo");
|
|
410
|
-
# Wait for result before continuing
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
# Or use it in a lambda
|
|
414
|
-
useEffect(lambda -> None {
|
|
415
|
-
async def loadTodos() -> None {
|
|
416
|
-
result = await root spawn read_todos();
|
|
417
|
-
setTodos(result.reports);
|
|
418
|
-
}
|
|
419
|
-
loadTodos();
|
|
420
|
-
}, []);
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
---
|
|
424
|
-
|
|
425
|
-
## What You've Learned
|
|
426
|
-
|
|
427
|
-
- What walkers are (backend functions)
|
|
428
|
-
- How to define data models with nodes
|
|
429
|
-
- Creating walkers for CRUD operations
|
|
430
|
-
- Calling walkers from frontend with `spawn`
|
|
431
|
-
- Graph traversal (`-->`, `` `?Node ``)
|
|
432
|
-
- Creating node connections (`++>`)
|
|
433
|
-
- Reporting data to frontend
|
|
434
|
-
- Using `_jac_id` for node references
|
|
435
|
-
- Data persistence on the server
|
|
436
|
-
|
|
437
|
-
---
|
|
438
|
-
|
|
439
|
-
## Common Issues
|
|
440
|
-
|
|
441
|
-
### Issue: Walker not found
|
|
442
|
-
|
|
443
|
-
**Check:** Is the walker defined OUTSIDE the `cl { }` block?
|
|
444
|
-
|
|
445
|
-
```jac
|
|
446
|
-
# Correct
|
|
447
|
-
walker read_todos {
|
|
448
|
-
# ...
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
cl {
|
|
452
|
-
# frontend code
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
# Wrong
|
|
456
|
-
cl {
|
|
457
|
-
walker read_todos { # Can't define walkers in frontend!
|
|
458
|
-
# ...
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
### Issue: Empty reports array
|
|
464
|
-
|
|
465
|
-
**Check:** Did you call `report` in your walker?
|
|
466
|
-
|
|
467
|
-
```jac
|
|
468
|
-
# Wrong - no report
|
|
469
|
-
can read with `root entry {
|
|
470
|
-
visit [-->(`?Todo)];
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
# Correct - report in Todo entry
|
|
474
|
-
can report_todos with Todo entry {
|
|
475
|
-
report here;
|
|
476
|
-
}
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
### Issue: "Cannot read property '_jac_id'"
|
|
480
|
-
|
|
481
|
-
**Check:** Is `result.reports` empty? Does the todo exist?
|
|
482
|
-
|
|
483
|
-
```jac
|
|
484
|
-
# Safe access
|
|
485
|
-
if result.reports and result.reports.length > 0 {
|
|
486
|
-
let todo = result.reports[0][0];
|
|
487
|
-
console.log(todo._jac_id);
|
|
488
|
-
}
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
### Issue: Data not persisting
|
|
492
|
-
|
|
493
|
-
**Check:**
|
|
494
|
-
- Are you calling the walker? `root spawn create_todo(...)`
|
|
495
|
-
- Is the walker running successfully? Check browser console
|
|
496
|
-
- Did you remove the localStorage code? (We don't need it anymore!)
|
|
497
|
-
|
|
498
|
-
---
|
|
499
|
-
|
|
500
|
-
## Quick Exercise
|
|
501
|
-
|
|
502
|
-
Try adding a walker to clear all completed todos:
|
|
503
|
-
|
|
504
|
-
```jac
|
|
505
|
-
walker clear_completed {
|
|
506
|
-
can clear with `root entry {
|
|
507
|
-
visit [-->(`?Todo)];
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
can delete_if_done with Todo entry {
|
|
511
|
-
if here.done {
|
|
512
|
-
here.destroy();
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
# Call from frontend
|
|
518
|
-
async def clearCompleted() -> None {
|
|
519
|
-
await root spawn clear_completed();
|
|
520
|
-
setTodos(todos.filter(lambda todo: any -> bool {
|
|
521
|
-
return not todo.done;
|
|
522
|
-
}));
|
|
523
|
-
}
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
---
|
|
527
|
-
|
|
528
|
-
## Next Step
|
|
529
|
-
|
|
530
|
-
Excellent! Your app now has a real backend. But there's a problem: **everyone can see everyone's todos!**
|
|
531
|
-
|
|
532
|
-
In the next step, we'll add **authentication** to make your app secure and private!
|
|
533
|
-
|
|
534
|
-
**[Continue to Step 9: Authentication](./step-09-authentication.md)**
|