jac-client 0.2.3__py3-none-any.whl → 0.2.5__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.
Files changed (202) hide show
  1. jac_client/examples/all-in-one/assets/workers/worker.py +5 -0
  2. jac_client/examples/all-in-one/src/app.jac +841 -0
  3. jac_client/examples/all-in-one/{button.jac → src/button.jac} +1 -1
  4. jac_client/examples/all-in-one/{components → src/components}/button.jac +1 -1
  5. jac_client/examples/asset-serving/css-with-image/{app.jac → src/app.jac} +2 -2
  6. jac_client/examples/asset-serving/image-asset/{app.jac → src/app.jac} +2 -2
  7. jac_client/examples/asset-serving/import-alias/{app.jac → src/app.jac} +3 -3
  8. jac_client/examples/basic/{app.jac → src/app.jac} +2 -2
  9. jac_client/examples/basic-auth/src/app.jac +377 -0
  10. jac_client/examples/basic-auth-with-router/{app.jac → src/app.jac} +18 -18
  11. jac_client/examples/basic-full-stack/{app.jac → src/app.jac} +175 -130
  12. jac_client/examples/css-styling/js-styling/{app.jac → src/app.jac} +6 -6
  13. jac_client/examples/css-styling/material-ui/{app.jac → src/app.jac} +5 -5
  14. jac_client/examples/css-styling/pure-css/{app.jac → src/app.jac} +6 -6
  15. jac_client/examples/css-styling/sass-example/{app.jac → src/app.jac} +6 -6
  16. jac_client/examples/css-styling/styled-components/{app.jac → src/app.jac} +5 -5
  17. jac_client/examples/css-styling/tailwind-example/{app.jac → src/app.jac} +6 -6
  18. jac_client/examples/full-stack-with-auth/{app.jac → src/app.jac} +37 -37
  19. jac_client/examples/little-x/{app.jac → src/app.jac} +27 -32
  20. jac_client/examples/little-x/src/submit-button.jac +16 -0
  21. jac_client/examples/nested-folders/nested-advance/{ButtonRoot.jac → src/ButtonRoot.jac} +1 -1
  22. jac_client/examples/nested-folders/nested-advance/{app.jac → src/app.jac} +1 -1
  23. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/ButtonSecondL.jac +1 -1
  24. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/Card.jac +1 -1
  25. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/level2/ButtonThirdL.jac +1 -1
  26. jac_client/examples/nested-folders/nested-basic/{app.jac → src/app.jac} +2 -2
  27. jac_client/examples/nested-folders/nested-basic/{button.jac → src/button.jac} +1 -1
  28. jac_client/examples/nested-folders/nested-basic/{components → src/components}/button.jac +1 -1
  29. jac_client/examples/ts-support/src/app.jac +35 -0
  30. jac_client/examples/with-router/{app.jac → src/app.jac} +11 -11
  31. jac_client/plugin/cli.jac +547 -0
  32. jac_client/plugin/client.jac +52 -0
  33. jac_client/plugin/client_runtime.cl.jac +38 -0
  34. jac_client/plugin/impl/client.impl.jac +134 -0
  35. jac_client/plugin/impl/client_runtime.impl.jac +177 -0
  36. jac_client/plugin/impl/vite_client_bundle.impl.jac +72 -0
  37. jac_client/plugin/plugin_config.jac +195 -0
  38. jac_client/plugin/src/__init__.jac +20 -0
  39. jac_client/plugin/src/asset_processor.jac +33 -0
  40. jac_client/plugin/src/babel_processor.jac +18 -0
  41. jac_client/plugin/src/compiler.jac +66 -0
  42. jac_client/plugin/src/config_loader.jac +32 -0
  43. jac_client/plugin/src/impl/asset_processor.impl.jac +127 -0
  44. jac_client/plugin/src/impl/babel_processor.impl.jac +84 -0
  45. jac_client/plugin/src/impl/compiler.impl.jac +251 -0
  46. jac_client/plugin/src/impl/config_loader.impl.jac +119 -0
  47. jac_client/plugin/src/impl/import_processor.impl.jac +33 -0
  48. jac_client/plugin/src/impl/jac_to_js.impl.jac +41 -0
  49. jac_client/plugin/src/impl/package_installer.impl.jac +105 -0
  50. jac_client/plugin/src/impl/vite_bundler.impl.jac +513 -0
  51. jac_client/plugin/src/import_processor.jac +19 -0
  52. jac_client/plugin/src/jac_to_js.jac +35 -0
  53. jac_client/plugin/src/package_installer.jac +26 -0
  54. jac_client/plugin/src/vite_bundler.jac +36 -0
  55. jac_client/plugin/vite_client_bundle.jac +31 -0
  56. jac_client/tests/conftest.py +281 -0
  57. jac_client/tests/fixtures/basic-app/app.jac +2 -2
  58. jac_client/tests/fixtures/cl_file/app.cl.jac +2 -2
  59. jac_client/tests/fixtures/client_app_with_antd/app.jac +1 -1
  60. jac_client/tests/fixtures/js_import/app.jac +5 -5
  61. jac_client/tests/fixtures/spawn_test/app.jac +7 -7
  62. jac_client/tests/fixtures/with-ts/app.jac +35 -0
  63. jac_client/tests/test_cli.py +755 -0
  64. jac_client/tests/test_it.py +347 -67
  65. {jac_client-0.2.3.dist-info → jac_client-0.2.5.dist-info}/METADATA +28 -30
  66. jac_client-0.2.5.dist-info/RECORD +74 -0
  67. {jac_client-0.2.3.dist-info → jac_client-0.2.5.dist-info}/WHEEL +2 -1
  68. jac_client-0.2.5.dist-info/entry_points.txt +4 -0
  69. jac_client-0.2.5.dist-info/top_level.txt +1 -0
  70. jac_client/docs/README.md +0 -689
  71. jac_client/docs/advanced-state.md +0 -1265
  72. jac_client/docs/asset-serving/intro.md +0 -209
  73. jac_client/docs/assets/pipe_line-v2.svg +0 -32
  74. jac_client/docs/assets/pipe_line.png +0 -0
  75. jac_client/docs/file-system/app.jac.md +0 -121
  76. jac_client/docs/file-system/backend-frontend.md +0 -217
  77. jac_client/docs/file-system/intro.md +0 -72
  78. jac_client/docs/file-system/nested-imports.md +0 -348
  79. jac_client/docs/guide-example/intro.md +0 -115
  80. jac_client/docs/guide-example/step-01-setup.md +0 -270
  81. jac_client/docs/guide-example/step-02-components.md +0 -416
  82. jac_client/docs/guide-example/step-03-styling.md +0 -478
  83. jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
  84. jac_client/docs/guide-example/step-05-local-state.md +0 -530
  85. jac_client/docs/guide-example/step-06-events.md +0 -749
  86. jac_client/docs/guide-example/step-07-effects.md +0 -468
  87. jac_client/docs/guide-example/step-08-walkers.md +0 -534
  88. jac_client/docs/guide-example/step-09-authentication.md +0 -586
  89. jac_client/docs/guide-example/step-10-routing.md +0 -539
  90. jac_client/docs/guide-example/step-11-final.md +0 -963
  91. jac_client/docs/imports.md +0 -1141
  92. jac_client/docs/lifecycle-hooks.md +0 -773
  93. jac_client/docs/routing.md +0 -659
  94. jac_client/docs/styling/intro.md +0 -249
  95. jac_client/docs/styling/js-styling.md +0 -367
  96. jac_client/docs/styling/material-ui.md +0 -341
  97. jac_client/docs/styling/pure-css.md +0 -299
  98. jac_client/docs/styling/sass.md +0 -403
  99. jac_client/docs/styling/styled-components.md +0 -395
  100. jac_client/docs/styling/tailwind.md +0 -298
  101. jac_client/examples/all-in-one/.babelrc +0 -9
  102. jac_client/examples/all-in-one/README.md +0 -16
  103. jac_client/examples/all-in-one/app.jac +0 -426
  104. jac_client/examples/all-in-one/assets/burger.png +0 -0
  105. jac_client/examples/all-in-one/package.json +0 -29
  106. jac_client/examples/all-in-one/styles.css +0 -26
  107. jac_client/examples/all-in-one/vite.config.js +0 -28
  108. jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
  109. jac_client/examples/asset-serving/css-with-image/README.md +0 -91
  110. jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
  111. jac_client/examples/asset-serving/css-with-image/package.json +0 -28
  112. jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
  113. jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
  114. jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
  115. jac_client/examples/asset-serving/image-asset/README.md +0 -119
  116. jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
  117. jac_client/examples/asset-serving/image-asset/package.json +0 -28
  118. jac_client/examples/asset-serving/image-asset/styles.css +0 -26
  119. jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
  120. jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
  121. jac_client/examples/asset-serving/import-alias/README.md +0 -83
  122. jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
  123. jac_client/examples/asset-serving/import-alias/package.json +0 -28
  124. jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
  125. jac_client/examples/basic/.babelrc +0 -9
  126. jac_client/examples/basic/README.md +0 -16
  127. jac_client/examples/basic/package.json +0 -27
  128. jac_client/examples/basic/vite.config.js +0 -27
  129. jac_client/examples/basic-auth/.babelrc +0 -9
  130. jac_client/examples/basic-auth/README.md +0 -16
  131. jac_client/examples/basic-auth/app.jac +0 -308
  132. jac_client/examples/basic-auth/package.json +0 -27
  133. jac_client/examples/basic-auth/vite.config.js +0 -27
  134. jac_client/examples/basic-auth-with-router/.babelrc +0 -9
  135. jac_client/examples/basic-auth-with-router/README.md +0 -60
  136. jac_client/examples/basic-auth-with-router/package.json +0 -28
  137. jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
  138. jac_client/examples/basic-full-stack/.babelrc +0 -9
  139. jac_client/examples/basic-full-stack/README.md +0 -18
  140. jac_client/examples/basic-full-stack/package.json +0 -28
  141. jac_client/examples/basic-full-stack/vite.config.js +0 -27
  142. jac_client/examples/css-styling/js-styling/.babelrc +0 -9
  143. jac_client/examples/css-styling/js-styling/README.md +0 -183
  144. jac_client/examples/css-styling/js-styling/package.json +0 -28
  145. jac_client/examples/css-styling/js-styling/styles.js +0 -100
  146. jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
  147. jac_client/examples/css-styling/material-ui/.babelrc +0 -9
  148. jac_client/examples/css-styling/material-ui/README.md +0 -16
  149. jac_client/examples/css-styling/material-ui/package.json +0 -32
  150. jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
  151. jac_client/examples/css-styling/pure-css/.babelrc +0 -9
  152. jac_client/examples/css-styling/pure-css/README.md +0 -16
  153. jac_client/examples/css-styling/pure-css/package.json +0 -28
  154. jac_client/examples/css-styling/pure-css/styles.css +0 -111
  155. jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
  156. jac_client/examples/css-styling/sass-example/.babelrc +0 -9
  157. jac_client/examples/css-styling/sass-example/README.md +0 -16
  158. jac_client/examples/css-styling/sass-example/package.json +0 -29
  159. jac_client/examples/css-styling/sass-example/styles.scss +0 -153
  160. jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
  161. jac_client/examples/css-styling/styled-components/.babelrc +0 -9
  162. jac_client/examples/css-styling/styled-components/README.md +0 -16
  163. jac_client/examples/css-styling/styled-components/package.json +0 -29
  164. jac_client/examples/css-styling/styled-components/styled.js +0 -90
  165. jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
  166. jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
  167. jac_client/examples/css-styling/tailwind-example/README.md +0 -16
  168. jac_client/examples/css-styling/tailwind-example/global.css +0 -1
  169. jac_client/examples/css-styling/tailwind-example/package.json +0 -30
  170. jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
  171. jac_client/examples/full-stack-with-auth/.babelrc +0 -9
  172. jac_client/examples/full-stack-with-auth/README.md +0 -16
  173. jac_client/examples/full-stack-with-auth/package.json +0 -28
  174. jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
  175. jac_client/examples/little-x/package.json +0 -23
  176. jac_client/examples/little-x/submit-button.jac +0 -8
  177. jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
  178. jac_client/examples/nested-folders/nested-advance/README.md +0 -77
  179. jac_client/examples/nested-folders/nested-advance/package.json +0 -29
  180. jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
  181. jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
  182. jac_client/examples/nested-folders/nested-basic/README.md +0 -183
  183. jac_client/examples/nested-folders/nested-basic/app.js +0 -7
  184. jac_client/examples/nested-folders/nested-basic/package.json +0 -28
  185. jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
  186. jac_client/examples/with-router/.babelrc +0 -9
  187. jac_client/examples/with-router/README.md +0 -17
  188. jac_client/examples/with-router/package.json +0 -28
  189. jac_client/examples/with-router/vite.config.js +0 -27
  190. jac_client/plugin/cli.py +0 -244
  191. jac_client/plugin/client.py +0 -152
  192. jac_client/plugin/client_runtime.jac +0 -234
  193. jac_client/plugin/vite_client_bundle.py +0 -503
  194. jac_client/tests/fixtures/js_import/utils.js +0 -21
  195. jac_client/tests/fixtures/package-lock.json +0 -329
  196. jac_client/tests/fixtures/package.json +0 -11
  197. jac_client/tests/test_asset_examples.py +0 -322
  198. jac_client/tests/test_cl.py +0 -530
  199. jac_client/tests/test_create_jac_app.py +0 -131
  200. jac_client/tests/test_nested_file.py +0 -374
  201. jac_client-0.2.3.dist-info/RECORD +0 -171
  202. 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)**