jac-client 0.2.13__py3-none-any.whl → 0.2.14__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 (41) hide show
  1. jac_client/examples/all-in-one/components/Header.jac +1 -1
  2. jac_client/examples/all-in-one/components/ProfitOverview.jac +1 -1
  3. jac_client/examples/all-in-one/components/Summary.jac +1 -1
  4. jac_client/examples/all-in-one/components/TransactionList.jac +2 -2
  5. jac_client/examples/all-in-one/components/navigation.jac +3 -9
  6. jac_client/examples/all-in-one/context/BudgetContext.jac +1 -1
  7. jac_client/examples/all-in-one/main.jac +5 -386
  8. jac_client/examples/all-in-one/pages/(auth)/index.jac +299 -0
  9. jac_client/examples/all-in-one/pages/{nestedDemo.jac → (auth)/nested.jac} +3 -13
  10. jac_client/examples/all-in-one/pages/{loginPage.jac → (public)/login.jac} +1 -1
  11. jac_client/examples/all-in-one/pages/{signupPage.jac → (public)/signup.jac} +1 -1
  12. jac_client/examples/all-in-one/pages/{notFound.jac → [...notFound].jac} +2 -1
  13. jac_client/examples/all-in-one/pages/budget.jac +11 -0
  14. jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +1 -1
  15. jac_client/examples/all-in-one/pages/features.jac +8 -0
  16. jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +7 -7
  17. jac_client/examples/all-in-one/pages/{LandingPage.jac → landing.jac} +4 -9
  18. jac_client/examples/all-in-one/pages/layout.jac +20 -0
  19. jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +1 -1
  20. jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +1 -1
  21. jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +1 -1
  22. jac_client/plugin/client_runtime.cl.jac +4 -2
  23. jac_client/plugin/impl/client_runtime.impl.jac +12 -1
  24. jac_client/plugin/plugin_config.jac +4 -11
  25. jac_client/plugin/src/compiler.jac +15 -1
  26. jac_client/plugin/src/impl/compiler.impl.jac +216 -23
  27. jac_client/plugin/src/impl/package_installer.impl.jac +3 -2
  28. jac_client/plugin/src/impl/route_scanner.impl.jac +201 -0
  29. jac_client/plugin/src/impl/vite_bundler.impl.jac +15 -11
  30. jac_client/plugin/src/route_scanner.jac +44 -0
  31. jac_client/plugin/utils/impl/bun_installer.impl.jac +16 -19
  32. jac_client/plugin/utils/impl/client_deps.impl.jac +12 -16
  33. jac_client/templates/fullstack.jacpack +3 -2
  34. jac_client/tests/test_e2e.py +19 -28
  35. {jac_client-0.2.13.dist-info → jac_client-0.2.14.dist-info}/METADATA +2 -2
  36. {jac_client-0.2.13.dist-info → jac_client-0.2.14.dist-info}/RECORD +39 -35
  37. jac_client/examples/all-in-one/pages/BudgetPlanner.jac +0 -140
  38. jac_client/examples/all-in-one/pages/FeaturesTest.jac +0 -157
  39. {jac_client-0.2.13.dist-info → jac_client-0.2.14.dist-info}/WHEEL +0 -0
  40. {jac_client-0.2.13.dist-info → jac_client-0.2.14.dist-info}/entry_points.txt +0 -0
  41. {jac_client-0.2.13.dist-info → jac_client-0.2.14.dist-info}/top_level.txt +0 -0
@@ -27,22 +27,22 @@ impl prompt_install_bun -> bool {
27
27
  import subprocess;
28
28
  import sys;
29
29
  import shutil;
30
- print("\n ⚠ Bun is required but not installed.", file=sys.stderr);
31
- print(
32
- " Bun is a fast JavaScript runtime used for package management and bundling.",
33
- file=sys.stderr
30
+ import from jaclang.cli.console { console }
31
+ console.error("\n ⚠ Bun is required but not installed.");
32
+ console.error(
33
+ " Bun is a fast JavaScript runtime used for package management and bundling."
34
34
  );
35
- print(" Learn more: https://bun.sh\n", file=sys.stderr);
35
+ console.error(" Learn more: https://bun.sh\n");
36
36
  try {
37
37
  response = input(" Install Bun now? [Y/n]: ").strip().lower();
38
38
  } except (EOFError, KeyboardInterrupt) {
39
- print("", file=sys.stderr);
39
+ console.error("");
40
40
  return False;
41
41
  }
42
42
  if response and response not in ('y', 'yes', '') {
43
43
  return False;
44
44
  }
45
- print("\n ⏳ Installing Bun...", flush=True);
45
+ console.print("\n ⏳ Installing Bun...");
46
46
  try {
47
47
  # Check if curl is available
48
48
  subprocess.run(
@@ -57,7 +57,7 @@ impl prompt_install_bun -> bool {
57
57
  );
58
58
 
59
59
  if result.returncode == 0 {
60
- print(" ✔ Bun installed successfully!", flush=True);
60
+ console.print(" ✔ Bun installed successfully!");
61
61
  # Add Bun to PATH for current session
62
62
  bun_bin = os.path.expanduser("~/.bun/bin");
63
63
  if os.path.exists(bun_bin) {
@@ -69,31 +69,28 @@ impl prompt_install_bun -> bool {
69
69
  ["bun", "--version"], capture_output=True, text=True
70
70
  );
71
71
  if verify.returncode == 0 {
72
- print(f" ✔ Verified: Bun {verify.stdout.strip()}", flush=True);
72
+ console.print(f" ✔ Verified: Bun {verify.stdout.strip()}");
73
73
  return True;
74
74
  }
75
75
  }
76
76
  }
77
- print(
78
- " Bun installed but may require terminal restart.", file=sys.stderr
79
- );
80
- print(" Run: source ~/.bashrc (or restart terminal)", file=sys.stderr);
77
+ console.error(" ⚠ Bun installed but may require terminal restart.");
78
+ console.error(" Run: source ~/.bashrc (or restart terminal)");
81
79
  return True;
82
80
  } else {
83
- print(" ✖ Bun installation failed.", file=sys.stderr);
81
+ console.error(" ✖ Bun installation failed.");
84
82
  return False;
85
83
  }
86
84
  } except subprocess.TimeoutExpired {
87
- print(" ✖ Installation timed out.", file=sys.stderr);
85
+ console.error(" ✖ Installation timed out.");
88
86
  return False;
89
87
  } except FileNotFoundError {
90
- print(
91
- " ✖ curl not found. Please install Bun manually: https://bun.sh",
92
- file=sys.stderr
88
+ console.error(
89
+ " ✖ curl not found. Please install Bun manually: https://bun.sh"
93
90
  );
94
91
  return False;
95
92
  } except Exception as e {
96
- print(f" ✖ Installation failed: {e}", file=sys.stderr);
93
+ console.error(f" ✖ Installation failed: {e}");
97
94
  return False;
98
95
  }
99
96
  }
@@ -16,27 +16,23 @@ impl ensure_client_deps(config_loader: object) -> bool {
16
16
  """Prompt user to install default jac-client npm dependencies."""
17
17
  def prompt_install_client_deps(config_loader: object) -> bool {
18
18
  import sys;
19
- print("\n ⚠ Client dependencies are not configured.", file=sys.stderr);
20
- print(
21
- " jac-client requires npm packages (react, vite, etc.) to build client pages.",
22
- file=sys.stderr
19
+ import from jaclang.cli.console { console }
20
+ console.error("\n ⚠ Client dependencies are not configured.");
21
+ console.error(
22
+ " jac-client requires npm packages (react, vite, etc.) to build client pages."
23
23
  );
24
- print(" These will be added to your jac.toml file.\n", file=sys.stderr);
24
+ console.error(" These will be added to your jac.toml file.\n");
25
25
  try {
26
26
  response = input(" Install default client dependencies now? [Y/n]: ").strip().lower();
27
27
  } except (EOFError, KeyboardInterrupt) {
28
- print("", file=sys.stderr);
28
+ console.error("");
29
29
  return False;
30
30
  }
31
31
  if response and response not in ('y', 'yes', '') {
32
- print(
33
- "\n To configure manually, add [dependencies.npm] to your jac.toml.",
34
- file=sys.stderr
35
- );
36
- print(
37
- " Or create a new project with: jac create --use client\n",
38
- file=sys.stderr
32
+ console.error(
33
+ "\n To configure manually, add [dependencies.npm] to your jac.toml."
39
34
  );
35
+ console.error(" Or create a new project with: jac create --use client\n");
40
36
  return False;
41
37
  }
42
38
  # Default runtime dependencies
@@ -54,7 +50,7 @@ def prompt_install_client_deps(config_loader: object) -> bool {
54
50
  '@types/react': '^18.2.0',
55
51
  '@types/react-dom': '^18.2.0'
56
52
  };
57
- print("\n ⏳ Adding default client dependencies to jac.toml...", flush=True);
53
+ console.print("\n ⏳ Adding default client dependencies to jac.toml...");
58
54
  try {
59
55
  for (name, version) in default_deps.items() {
60
56
  config_loader.add_dependency(name, version, is_dev=False);
@@ -64,10 +60,10 @@ def prompt_install_client_deps(config_loader: object) -> bool {
64
60
  }
65
61
  config_loader.save();
66
62
  config_loader.invalidate();
67
- print(" ✔ Default client dependencies added to jac.toml\n", flush=True);
63
+ console.print(" ✔ Default client dependencies added to jac.toml\n");
68
64
  return True;
69
65
  } except Exception as e {
70
- print(f" ✖ Failed to add dependencies: {e}", file=sys.stderr);
66
+ console.error(f" ✖ Failed to add dependencies: {e}");
71
67
  return False;
72
68
  }
73
69
  }
@@ -26,11 +26,12 @@
26
26
  "files": {
27
27
  "main.jac": "\"\"\"{{name}} - Entry Point.\n\nCombines server-side endpoints with client-side UI.\n\"\"\"\n\n# Import server-side data models and walkers\nsv {\n import from endpoints { Todo, AddTodo, ListTodos, ToggleTodo, DeleteTodo }\n}\n\n# Client-side UI\ncl {\n import from .frontend { app as ClientApp }\n\n def:pub app -> any {\n return\n <ClientApp />;\n }\n}\n",
28
28
  "endpoints.sv.jac": "\"\"\"Todo App - Server-Side Data Layer.\n\nEach user gets their own todo list automatically!\nWhen authenticated, `root spawn` uses the user's personal root node.\n\"\"\"\n\n# ========================================\n# Data Models\n# ========================================\nnode Todo {\n has id: str,\n title: str,\n completed: bool = False;\n}\n\nglob todo_counter: int = 0;\n\n# ========================================\n# Walkers (API Operations)\n# ========================================\nwalker:priv AddTodo {\n \"\"\"Add a new todo item to the user's list.\"\"\"\n has title: str;\n\n can create with `root entry {\n global todo_counter;\n todo_counter += 1;\n new_id = \"todo_\" + str(todo_counter);\n new_todo = here ++> Todo(id=new_id, title=self.title, completed=False);\n if new_todo {\n report new_todo[0] ;\n }\n }\n}\n\nwalker:priv ListTodos {\n \"\"\"List all todo items for the current user.\"\"\"\n has todos: list = [];\n\n can collect with `root entry {\n visit [-->];\n }\n\n can gather with Todo entry {\n self.todos.append(\n {\"id\": here.id, \"title\": here.title, \"completed\": here.completed}\n );\n }\n\n can report_all with `root exit {\n report self.todos ;\n }\n}\n\nwalker:priv ToggleTodo {\n \"\"\"Toggle a todo's completed status.\"\"\"\n has todo_id: str;\n\n can search with `root entry {\n visit [-->];\n }\n\n can toggle with Todo entry {\n if here.id == self.todo_id {\n here.completed = not here.completed;\n report {\"id\": here.id, \"completed\": here.completed} ;\n }\n }\n}\n\nwalker:priv DeleteTodo {\n \"\"\"Delete a todo item.\"\"\"\n has todo_id: str;\n\n can search with `root entry {\n visit [-->];\n }\n\n can delete with Todo entry {\n if here.id == self.todo_id {\n del here ;\n report {\"deleted\": self.todo_id} ;\n }\n }\n}\n",
29
- "frontend.cl.jac": "\"\"\"Todo App - Client-Side UI.\"\"\"\n\nimport from react { useEffect }\nimport from \"@jac/runtime\" { jacSignup, jacLogin, jacLogout, jacIsLoggedIn }\n\nimport from .components.TodoItem { TodoItem }\nimport from .components.AuthForm { AuthForm }\n\nsv import from endpoints { AddTodo, ListTodos, ToggleTodo, DeleteTodo }\n\ndef:pub app -> any {\n has isLoggedIn: bool = False,\n checkingAuth: bool = True,\n isSignup: bool = False,\n username: str = \"\",\n password: str = \"\",\n error: str = \"\",\n loading: bool = False,\n todos: list = [],\n newTodoText: str = \"\",\n todosLoading: bool = True;\n\n # Check login status on mount\n useEffect(lambda -> None { isLoggedIn = jacIsLoggedIn();checkingAuth = False;}, []);\n\n # Load todos when logged in\n useEffect(\n lambda -> None { if isLoggedIn {\n fetchTodos();\n }},\n [isLoggedIn]\n );\n\n # Fetch todos from server\n async def fetchTodos -> None {\n todosLoading = True;\n result = root spawn ListTodos();\n todos = result.reports[0] if result.reports else [];\n todosLoading = False;\n }\n\n # Add a new todo\n async def addTodo -> None {\n if not newTodoText.trim() {\n return;\n }\n response = root spawn AddTodo(title=newTodoText);\n newTodo = response.reports[0];\n todos = todos.concat(\n [\n {\n \"id\": newTodo.id,\n \"title\": newTodo.title,\n \"completed\": newTodo.completed\n }\n ]\n );\n newTodoText = \"\";\n }\n\n # Toggle todo completion\n async def toggleTodo(todoId: str) -> None {\n root spawn ToggleTodo(todo_id=todoId);\n todos = todos.map(\n lambda t: any -> any { if t.id == todoId {\n return {\"id\": t.id, \"title\": t.title, \"completed\": not t.completed};\n }return t; }\n );\n }\n\n # Delete a todo\n async def deleteTodo(todoId: str) -> None {\n root spawn DeleteTodo(todo_id=todoId);\n todos = todos.filter(lambda t: any -> bool { return t.id != todoId; });\n }\n\n # Handle login\n async def handleLogin -> None {\n error = \"\";\n if not username.trim() or not password {\n error = \"Please fill in all fields\";\n return;\n }\n loading = True;\n success = await jacLogin(username, password);\n loading = False;\n if success {\n isLoggedIn = True;\n username = \"\";\n password = \"\";\n } else {\n error = \"Invalid username or password\";\n }\n }\n\n # Handle signup\n async def handleSignup -> None {\n error = \"\";\n if not username.trim() or not password {\n error = \"Please fill in all fields\";\n return;\n }\n if password.length < 4 {\n error = \"Password must be at least 4 characters\";\n return;\n }\n loading = True;\n result = await jacSignup(username, password);\n loading = False;\n if result[\"success\"] {\n isLoggedIn = True;\n username = \"\";\n password = \"\";\n } else {\n error = result[\"error\"] if result[\"error\"] else \"Signup failed\";\n }\n }\n\n # Handle logout\n def handleLogout -> None {\n jacLogout();\n isLoggedIn = False;\n todos = [];\n username = \"\";\n password = \"\";\n error = \"\";\n }\n\n # Handle form submit\n async def handleSubmit(e: any) -> None {\n e.preventDefault();\n if isSignup {\n await handleSignup();\n } else {\n await handleLogin();\n }\n }\n\n # Handle enter key for todo input\n def handleTodoKeyPress(e: any) -> None {\n if e.key == \"Enter\" {\n addTodo();\n }\n }\n\n # Loading screen\n if checkingAuth {\n return\n <div\n style={{\n \"display\": \"flex\",\n \"justifyContent\": \"center\",\n \"alignItems\": \"center\",\n \"height\": \"100vh\",\n \"fontFamily\": \"system-ui, -apple-system, sans-serif\",\n \"color\": \"#666\"\n }}\n >\n Loading...\n </div>;\n }\n\n # Logged in - Show Todo List\n if isLoggedIn {\n return\n <div\n style={{\n \"maxWidth\": \"600px\",\n \"margin\": \"2rem auto\",\n \"padding\": \"2rem\",\n \"fontFamily\": \"system-ui, -apple-system, sans-serif\"\n }}\n >\n <div\n style={{\n \"display\": \"flex\",\n \"justifyContent\": \"space-between\",\n \"alignItems\": \"center\",\n \"marginBottom\": \"1.5rem\"\n }}\n >\n <h1 style={{\"margin\": \"0\", \"color\": \"#1a1a2e\"}}>\n My Todos\n </h1>\n <button\n onClick={handleLogout}\n style={{\n \"padding\": \"0.5rem 1rem\",\n \"fontSize\": \"0.9rem\",\n \"backgroundColor\": \"#f8f9fa\",\n \"color\": \"#666\",\n \"border\": \"1px solid #ddd\",\n \"borderRadius\": \"6px\",\n \"cursor\": \"pointer\"\n }}\n >\n Log Out\n </button>\n </div>\n <div\n style={{\n \"display\": \"flex\",\n \"gap\": \"0.5rem\",\n \"marginBottom\": \"1.5rem\"\n }}\n >\n <input\n type=\"text\"\n value={newTodoText}\n onChange={lambda e: any -> None { newTodoText = e.target.value;}}\n onKeyPress={handleTodoKeyPress}\n placeholder=\"What needs to be done?\"\n style={{\n \"flex\": \"1\",\n \"padding\": \"0.75rem 1rem\",\n \"fontSize\": \"1rem\",\n \"border\": \"2px solid #e0e0e0\",\n \"borderRadius\": \"8px\",\n \"outline\": \"none\"\n }}\n />\n <button\n onClick={lambda -> None { addTodo();}}\n style={{\n \"padding\": \"0.75rem 1.5rem\",\n \"fontSize\": \"1rem\",\n \"backgroundColor\": \"#4CAF50\",\n \"color\": \"white\",\n \"border\": \"none\",\n \"borderRadius\": \"8px\",\n \"cursor\": \"pointer\"\n }}\n >\n Add\n </button>\n </div>\n {(\n <div style={{\"color\": \"#666\", \"textAlign\": \"center\"}}>\n Loading todos...\n </div>\n )\n if todosLoading\n else None}\n {(\n <div>\n {(\n <p\n style={{\n \"color\": \"#888\",\n \"textAlign\": \"center\",\n \"padding\": \"2rem\"\n }}\n >\n No todos yet. Add one above!\n </p>\n )\n if todos.length == 0\n else None}{todos.map(\n lambda todo: any -> any { return\n <TodoItem\n key={todo.id}\n todo={todo}\n onToggle={toggleTodo}\n onDelete={deleteTodo}\n />; }\n )}\n </div>\n )\n if not todosLoading\n else None}\n <div\n style={{\n \"marginTop\": \"2rem\",\n \"paddingTop\": \"1rem\",\n \"borderTop\": \"1px solid #eee\",\n \"color\": \"#888\",\n \"fontSize\": \"0.9rem\"\n }}\n >\n {todos.filter(lambda t: any -> bool { return not t.completed; }).length} items left\n </div>\n </div>;\n }\n\n # Not logged in - Show Auth Form\n return\n <AuthForm\n isSignup={isSignup}\n username={username}\n password={password}\n error={error}\n loading={loading}\n onUsernameChange={lambda e: any -> None { username = e.target.value;}}\n onPasswordChange={lambda e: any -> None { password = e.target.value;}}\n onSubmit={handleSubmit}\n onToggleMode={lambda -> None { isSignup = not isSignup;error = \"\";}}\n />;\n}\n",
29
+ "frontend.cl.jac": "\"\"\"Todo App - Client-Side UI.\"\"\"\n\nimport from \"@jac/runtime\" { jacSignup, jacLogin, jacLogout, jacIsLoggedIn }\n\nimport from .components.TodoItem { TodoItem }\nimport from .components.AuthForm { AuthForm }\n\nsv import from endpoints { AddTodo, ListTodos, ToggleTodo, DeleteTodo }\n\ndef:pub app -> any {\n has isLoggedIn: bool = False,\n checkingAuth: bool = True,\n isSignup: bool = False,\n username: str = \"\",\n password: str = \"\",\n error: str = \"\",\n loading: bool = False,\n todos: list = [],\n newTodoText: str = \"\",\n todosLoading: bool = True;\n\n can with entry {\n isLoggedIn = jacIsLoggedIn();\n checkingAuth = False;\n }\n\n can with [isLoggedIn] entry {\n if isLoggedIn {\n fetchTodos();\n }\n }\n\n async def fetchTodos -> None;\n async def addTodo -> None;\n async def toggleTodo(todoId: str) -> None;\n async def deleteTodo(todoId: str) -> None;\n async def handleLogin -> None;\n async def handleSignup -> None;\n def handleLogout -> None;\n async def handleSubmit(e: any) -> None;\n def handleTodoKeyPress(e: any) -> None;\n\n # Loading screen\n if checkingAuth {\n return\n <div\n style={{\n \"display\": \"flex\",\n \"justifyContent\": \"center\",\n \"alignItems\": \"center\",\n \"height\": \"100vh\",\n \"fontFamily\": \"system-ui, -apple-system, sans-serif\",\n \"color\": \"#666\"\n }}\n >\n Loading...\n </div>;\n }\n\n # Logged in - Show Todo List\n if isLoggedIn {\n return\n <div\n style={{\n \"maxWidth\": \"600px\",\n \"margin\": \"2rem auto\",\n \"padding\": \"2rem\",\n \"fontFamily\": \"system-ui, -apple-system, sans-serif\"\n }}\n >\n <div\n style={{\n \"display\": \"flex\",\n \"justifyContent\": \"space-between\",\n \"alignItems\": \"center\",\n \"marginBottom\": \"1.5rem\"\n }}\n >\n <h1 style={{\"margin\": \"0\", \"color\": \"#1a1a2e\"}}>\n My Todos\n </h1>\n <button\n onClick={handleLogout}\n style={{\n \"padding\": \"0.5rem 1rem\",\n \"fontSize\": \"0.9rem\",\n \"backgroundColor\": \"#f8f9fa\",\n \"color\": \"#666\",\n \"border\": \"1px solid #ddd\",\n \"borderRadius\": \"6px\",\n \"cursor\": \"pointer\"\n }}\n >\n Log Out\n </button>\n </div>\n <div\n style={{\n \"display\": \"flex\",\n \"gap\": \"0.5rem\",\n \"marginBottom\": \"1.5rem\"\n }}\n >\n <input\n type=\"text\"\n value={newTodoText}\n onChange={lambda e: any -> None { newTodoText = e.target.value;}}\n onKeyPress={handleTodoKeyPress}\n placeholder=\"What needs to be done?\"\n style={{\n \"flex\": \"1\",\n \"padding\": \"0.75rem 1rem\",\n \"fontSize\": \"1rem\",\n \"border\": \"2px solid #e0e0e0\",\n \"borderRadius\": \"8px\",\n \"outline\": \"none\"\n }}\n />\n <button\n onClick={lambda -> None { addTodo();}}\n style={{\n \"padding\": \"0.75rem 1.5rem\",\n \"fontSize\": \"1rem\",\n \"backgroundColor\": \"#4CAF50\",\n \"color\": \"white\",\n \"border\": \"none\",\n \"borderRadius\": \"8px\",\n \"cursor\": \"pointer\"\n }}\n >\n Add\n </button>\n </div>\n {(\n <div style={{\"color\": \"#666\", \"textAlign\": \"center\"}}>\n Loading todos...\n </div>\n )\n if todosLoading\n else None}\n {(\n <div>\n {(\n <p\n style={{\n \"color\": \"#888\",\n \"textAlign\": \"center\",\n \"padding\": \"2rem\"\n }}\n >\n No todos yet. Add one above!\n </p>\n )\n if todos.length == 0\n else None}{[\n <TodoItem\n key={todo.id}\n todo={todo}\n onToggle={toggleTodo}\n onDelete={deleteTodo}\n /> for todo in todos\n ]}\n </div>\n )\n if not todosLoading\n else None}\n <div\n style={{\n \"marginTop\": \"2rem\",\n \"paddingTop\": \"1rem\",\n \"borderTop\": \"1px solid #eee\",\n \"color\": \"#888\",\n \"fontSize\": \"0.9rem\"\n }}\n >\n {todos.filter(lambda t: any -> bool { return not t.completed; }).length} items left\n </div>\n </div>;\n }\n\n # Not logged in - Show Auth Form\n return\n <AuthForm\n isSignup={isSignup}\n username={username}\n password={password}\n error={error}\n loading={loading}\n onUsernameChange={lambda e: any -> None { username = e.target.value;}}\n onPasswordChange={lambda e: any -> None { password = e.target.value;}}\n onSubmit={handleSubmit}\n onToggleMode={lambda -> None { isSignup = not isSignup;error = \"\";}}\n />;\n}\n",
30
+ "frontend.impl.jac": "\"\"\"Implementations for the Todo App frontend component.\"\"\"\n\nimpl app.fetchTodos -> None {\n todosLoading = True;\n result = root spawn ListTodos();\n todos = result.reports[0] if result.reports else [];\n todosLoading = False;\n}\n\nimpl app.addTodo -> None {\n if not newTodoText.trim() {\n return;\n }\n response = root spawn AddTodo(title=newTodoText);\n newTodo = response.reports[0];\n todos = todos.concat(\n [\n {\n \"id\": newTodo.id,\n \"title\": newTodo.title,\n \"completed\": newTodo.completed\n }\n ]\n );\n newTodoText = \"\";\n}\n\nimpl app.toggleTodo(todoId: str) -> None {\n root spawn ToggleTodo(todo_id=todoId);\n todos = todos.map(\n lambda t: any -> any {\n if t.id == todoId {\n return {\"id\": t.id, \"title\": t.title, \"completed\": not t.completed};\n }\n return t;\n }\n );\n}\n\nimpl app.deleteTodo(todoId: str) -> None {\n root spawn DeleteTodo(todo_id=todoId);\n todos = todos.filter(lambda t: any -> bool { return t.id != todoId; });\n}\n\nimpl app.handleLogin -> None {\n error = \"\";\n if not username.trim() or not password {\n error = \"Please fill in all fields\";\n return;\n }\n loading = True;\n success = await jacLogin(username, password);\n loading = False;\n if success {\n isLoggedIn = True;\n username = \"\";\n password = \"\";\n } else {\n error = \"Invalid username or password\";\n }\n}\n\nimpl app.handleSignup -> None {\n error = \"\";\n if not username.trim() or not password {\n error = \"Please fill in all fields\";\n return;\n }\n if password.length < 4 {\n error = \"Password must be at least 4 characters\";\n return;\n }\n loading = True;\n result = await jacSignup(username, password);\n loading = False;\n if result[\"success\"] {\n isLoggedIn = True;\n username = \"\";\n password = \"\";\n } else {\n error = result[\"error\"] if result[\"error\"] else \"Signup failed\";\n }\n}\n\nimpl app.handleLogout -> None {\n jacLogout();\n isLoggedIn = False;\n todos = [];\n username = \"\";\n password = \"\";\n error = \"\";\n}\n\nimpl app.handleSubmit(e: any) -> None {\n e.preventDefault();\n if isSignup {\n await handleSignup();\n } else {\n await handleLogin();\n }\n}\n\nimpl app.handleTodoKeyPress(e: any) -> None {\n if e.key == \"Enter\" {\n addTodo();\n }\n}\n",
30
31
  "components/AuthForm.cl.jac": "\"\"\"Authentication form component for login/signup.\"\"\"\n\ndef:pub AuthForm(\n isSignup: bool,\n username: str,\n password: str,\n error: str,\n loading: bool,\n onUsernameChange: any,\n onPasswordChange: any,\n onSubmit: any,\n onToggleMode: any\n) -> any {\n return\n <div\n style={{\n \"maxWidth\": \"400px\",\n \"margin\": \"4rem auto\",\n \"padding\": \"2rem\",\n \"fontFamily\": \"system-ui, -apple-system, sans-serif\",\n \"backgroundColor\": \"#fff\",\n \"borderRadius\": \"12px\",\n \"boxShadow\": \"0 4px 6px rgba(0,0,0,0.1)\"\n }}\n >\n <h1\n style={{\n \"marginBottom\": \"0.5rem\",\n \"color\": \"#1a1a2e\",\n \"textAlign\": \"center\"\n }}\n >\n Jac Todo App\n </h1>\n <p\n style={{\n \"marginBottom\": \"1.5rem\",\n \"color\": \"#666\",\n \"textAlign\": \"center\"\n }}\n >\n {(\"Create an account\" if isSignup else \"Sign in to your account\")}\n </p>\n {(\n <div\n style={{\n \"padding\": \"0.75rem\",\n \"marginBottom\": \"1rem\",\n \"backgroundColor\": \"#fee\",\n \"color\": \"#c00\",\n \"borderRadius\": \"6px\",\n \"fontSize\": \"0.9rem\"\n }}\n >\n {error}\n </div>\n )\n if error\n else None}\n <form onSubmit={onSubmit}>\n <input\n type=\"text\"\n value={username}\n onChange={onUsernameChange}\n placeholder=\"Username\"\n style={{\n \"width\": \"100%\",\n \"padding\": \"0.75rem 1rem\",\n \"marginBottom\": \"0.75rem\",\n \"fontSize\": \"1rem\",\n \"border\": \"2px solid #e0e0e0\",\n \"borderRadius\": \"8px\",\n \"outline\": \"none\",\n \"boxSizing\": \"border-box\"\n }}\n />\n <input\n type=\"password\"\n value={password}\n onChange={onPasswordChange}\n placeholder=\"Password\"\n style={{\n \"width\": \"100%\",\n \"padding\": \"0.75rem 1rem\",\n \"marginBottom\": \"1rem\",\n \"fontSize\": \"1rem\",\n \"border\": \"2px solid #e0e0e0\",\n \"borderRadius\": \"8px\",\n \"outline\": \"none\",\n \"boxSizing\": \"border-box\"\n }}\n />\n <button\n type=\"submit\"\n disabled={loading}\n style={{\n \"width\": \"100%\",\n \"padding\": \"0.75rem\",\n \"fontSize\": \"1rem\",\n \"backgroundColor\": (\"#999\" if loading else \"#4CAF50\"),\n \"color\": \"white\",\n \"border\": \"none\",\n \"borderRadius\": \"8px\",\n \"cursor\": (\"not-allowed\" if loading else \"pointer\"),\n \"marginBottom\": \"1rem\"\n }}\n >\n {(\n (\n \"Processing...\"\n if loading\n else (\"Sign Up\" if isSignup else \"Log In\")\n )\n )}\n </button>\n </form>\n <p style={{\"textAlign\": \"center\", \"color\": \"#666\", \"fontSize\": \"0.9rem\"}}>\n {(\n \"Already have an account? \"\n if isSignup\n else \"Don't have an account? \"\n )}\n <span\n onClick={onToggleMode}\n style={{\n \"color\": \"#4CAF50\",\n \"cursor\": \"pointer\",\n \"fontWeight\": \"bold\"\n }}\n >\n {(\"Log In\" if isSignup else \"Sign Up\")}\n </span>\n </p>\n </div>;\n}\n",
31
32
  "components/TodoItem.cl.jac": "\"\"\"Todo item component for displaying a single todo.\"\"\"\n\ndef:pub TodoItem(todo: dict, onToggle: any, onDelete: any) -> any {\n return\n <div\n key={todo.id}\n style={{\n \"display\": \"flex\",\n \"alignItems\": \"center\",\n \"gap\": \"0.75rem\",\n \"padding\": \"1rem\",\n \"marginBottom\": \"0.5rem\",\n \"backgroundColor\": \"#f8f9fa\",\n \"borderRadius\": \"8px\",\n \"border\": \"1px solid #e9ecef\"\n }}\n >\n <input\n type=\"checkbox\"\n checked={todo.completed}\n onChange={lambda -> None { onToggle(todo.id);}}\n style={{\"width\": \"20px\", \"height\": \"20px\", \"cursor\": \"pointer\"}}\n />\n <span\n style={{\n \"flex\": \"1\",\n \"textDecoration\": (\"line-through\" if todo.completed else \"none\"),\n \"color\": (\"#888\" if todo.completed else \"#333\")\n }}\n >\n {todo.title}\n </span>\n <button\n onClick={lambda -> None { onDelete(todo.id);}}\n style={{\n \"padding\": \"0.5rem\",\n \"backgroundColor\": \"transparent\",\n \"color\": \"#dc3545\",\n \"border\": \"none\",\n \"cursor\": \"pointer\",\n \"fontSize\": \"1.2rem\"\n }}\n >\n x\n </button>\n </div>;\n}\n",
32
33
  "components/Button.cl.jac": "\"\"\"Button component for the Jac client application.\"\"\"\n\ndef:pub Button(\n label: str, onClick: any, variant: str = \"primary\", disabled: bool = False\n) -> any {\n base_styles = {\n \"padding\": \"0.75rem 1.5rem\",\n \"fontSize\": \"1rem\",\n \"fontWeight\": \"600\",\n \"borderRadius\": \"0.5rem\",\n \"border\": \"none\",\n \"cursor\": \"not-allowed\" if disabled else \"pointer\",\n \"transition\": \"all 0.2s ease\"\n };\n\n variant_styles = {\n \"primary\": {\n \"backgroundColor\": \"#9ca3af\" if disabled else \"#3b82f6\",\n \"color\": \"#ffffff\"\n },\n \"secondary\": {\n \"backgroundColor\": \"#e5e7eb\" if disabled else \"#6b7280\",\n \"color\": \"#ffffff\"\n }\n };\n\n return\n <button\n style={{** base_styles, ** variant_styles[variant]}}\n onClick={onClick}\n disabled={disabled}\n >\n {label}\n </button>;\n}\n",
33
- "README.md": "# {{name}}\n\nA full-stack Jac application with user authentication and todo list functionality.\n\n## Project Structure\n\n```\n{{name}}/\n\u251c\u2500\u2500 jac.toml # Project configuration\n\u251c\u2500\u2500 main.jac # Main entry point (combines server + client)\n\u251c\u2500\u2500 endpoints.sv.jac # Server-side data models and walkers\n\u251c\u2500\u2500 frontend.cl.jac # Client-side React UI\n\u251c\u2500\u2500 components/ # Reusable client components\n\u2502 \u251c\u2500\u2500 AuthForm.cl.jac # Login/signup form\n\u2502 \u251c\u2500\u2500 TodoItem.cl.jac # Individual todo display\n\u2502 \u2514\u2500\u2500 Button.cl.jac # Reusable button component\n\u2514\u2500\u2500 assets/ # Static assets (images, fonts, etc.)\n```\n\n## Getting Started\n\nStart the development server:\n\n```bash\njac start main.jac\n```\n\nThen open your browser to the URL shown in the terminal.\n\n## Features\n\n- **User Authentication**: Sign up and log in with username/password\n- **Personal Todo Lists**: Each user gets their own isolated todo list\n- **CRUD Operations**: Create, read, update (toggle), and delete todos\n- **Real-time UI**: Responsive React frontend with instant updates\n\n## Architecture\n\nThis template demonstrates the full-stack Jac architecture:\n\n- **Server (`.sv.jac`)**: Defines data models (`node Todo`) and walkers for API operations\n- **Client (`.cl.jac`)**: React components with hooks and state management\n- **Entry Point (`main.jac`)**: Combines server and client imports\n\n## Adding Dependencies\n\nAdd npm packages with the --cl flag:\n\n```bash\njac add --cl react-router-dom\n```\n"
34
+ "README.md": "# {{name}}\n\nA full-stack Jac application with user authentication and todo list functionality.\n\n## Project Structure\n\n```\n{{name}}/\n\u251c\u2500\u2500 jac.toml # Project configuration\n\u251c\u2500\u2500 main.jac # Main entry point (combines server + client)\n\u251c\u2500\u2500 endpoints.sv.jac # Server-side data models and walkers\n\u251c\u2500\u2500 frontend.cl.jac # Client-side React UI (declarations + JSX)\n\u251c\u2500\u2500 frontend.impl.jac # Client-side function implementations\n\u251c\u2500\u2500 components/ # Reusable client components\n\u2502 \u251c\u2500\u2500 AuthForm.cl.jac # Login/signup form\n\u2502 \u251c\u2500\u2500 TodoItem.cl.jac # Individual todo display\n\u2502 \u2514\u2500\u2500 Button.cl.jac # Reusable button component\n\u2514\u2500\u2500 assets/ # Static assets (images, fonts, etc.)\n```\n\n## Getting Started\n\nStart the development server:\n\n```bash\njac start main.jac\n```\n\nThen open your browser to the URL shown in the terminal.\n\n## Features\n\n- **User Authentication**: Sign up and log in with username/password\n- **Personal Todo Lists**: Each user gets their own isolated todo list\n- **CRUD Operations**: Create, read, update (toggle), and delete todos\n- **Real-time UI**: Responsive React frontend with instant updates\n\n## Architecture\n\nThis template demonstrates the full-stack Jac architecture:\n\n- **Server (`.sv.jac`)**: Defines data models (`node Todo`) and walkers for API operations\n- **Client (`.cl.jac`)**: React components with state declarations and JSX templates\n- **Implementations (`.impl.jac`)**: Separated function bodies for clean code organization\n- **Entry Point (`main.jac`)**: Combines server and client imports\n\n### Jac Patterns Used\n\n- **`can with entry`**: Lifecycle effects that replace React's `useEffect`\n- **JSX Comprehensions**: `{[<Component /> for item in items]}` instead of `.map()`\n- **Impl Separation**: Declarations in `.cl.jac`, implementations in `.impl.jac`\n\n## Adding Dependencies\n\nAdd npm packages with the --cl flag:\n\n```bash\njac add --cl react-router-dom\n```\n"
34
35
  },
35
36
  "directories": [
36
37
  ".jac",
@@ -4,10 +4,9 @@ from __future__ import annotations
4
4
 
5
5
  import gc
6
6
  import os
7
- import shutil
8
7
  import tempfile
9
8
  import time
10
- from subprocess import PIPE, Popen, run
9
+ from subprocess import Popen, run
11
10
 
12
11
  import pytest
13
12
 
@@ -27,6 +26,8 @@ from .test_helpers import (
27
26
  def running_server():
28
27
  """Start the all-in-one jac server for the test module and yield its URL.
29
28
 
29
+ Uses jacpack to bundle and extract the all-in-one example.
30
+
30
31
  Yields a dict with keys `port` and `url`.
31
32
  """
32
33
  tests_dir = os.path.dirname(__file__)
@@ -44,43 +45,33 @@ def running_server():
44
45
  original_cwd = os.getcwd()
45
46
  try:
46
47
  os.chdir(temp_dir)
47
- process = Popen(
48
- [*jac_cmd, "create", "--use", "client", app_name],
49
- stdin=PIPE,
50
- stdout=PIPE,
51
- stderr=PIPE,
48
+
49
+ # Create jacpack file from all-in-one example
50
+ jacpack_path = os.path.join(temp_dir, "all-in-one.jacpack")
51
+ pack_result = run(
52
+ [*jac_cmd, "jacpack", "pack", all_in_one_path, "-o", jacpack_path],
53
+ capture_output=True,
52
54
  text=True,
53
55
  env=env,
54
56
  )
55
- stdout, stderr = process.communicate()
56
- if process.returncode != 0:
57
- pytest.fail(f"jac create --use client failed: {stderr}")
58
-
59
- project_path = os.path.join(temp_dir, app_name)
57
+ if pack_result.returncode != 0:
58
+ pytest.fail(f"jac jacpack pack failed: {pack_result.stderr}")
60
59
 
61
- for entry in os.listdir(all_in_one_path):
62
- if entry in {"node_modules", "build", "dist", ".pytest_cache"}:
63
- continue
64
- src = os.path.join(all_in_one_path, entry)
65
- dst = os.path.join(project_path, entry)
66
- if os.path.isdir(src):
67
- shutil.copytree(src, dst, dirs_exist_ok=True)
68
- else:
69
- shutil.copy2(src, dst)
70
-
71
- jac_add_result = run(
72
- [*jac_cmd, "add", "--npm"],
73
- cwd=project_path,
60
+ # Create project from jacpack file
61
+ create_result = run(
62
+ [*jac_cmd, "create", app_name, "--use", jacpack_path],
74
63
  capture_output=True,
75
64
  text=True,
76
65
  env=env,
77
66
  )
78
- if jac_add_result.returncode != 0:
79
- pytest.fail(f"jac add --npm failed: {jac_add_result.stderr}")
67
+ if create_result.returncode != 0:
68
+ pytest.fail(f"jac create --use jacpack failed: {create_result.stderr}")
69
+
70
+ project_path = os.path.join(temp_dir, app_name)
80
71
 
81
72
  server_port = get_free_port()
82
73
  server = Popen(
83
- [*jac_cmd, "start", "main.jac", "-p", str(server_port)],
74
+ [*jac_cmd, "start", "-p", str(server_port)],
84
75
  cwd=project_path,
85
76
  env=env,
86
77
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jac-client
3
- Version: 0.2.13
3
+ Version: 0.2.14
4
4
  Summary: Build full-stack web applications with Jac - one language for frontend and backend.
5
5
  Author-email: Jason Mars <jason@mars.ninja>
6
6
  Maintainer-email: Jason Mars <jason@mars.ninja>
@@ -11,7 +11,7 @@ Project-URL: Documentation, https://jac-lang.org
11
11
  Keywords: jac,jaclang,jaseci,frontend,full-stack,web-development
12
12
  Requires-Python: >=3.12
13
13
  Description-Content-Type: text/markdown
14
- Requires-Dist: jaclang>=0.9.13
14
+ Requires-Dist: jaclang>=0.9.14
15
15
  Provides-Extra: dev
16
16
  Requires-Dist: python-dotenv==1.0.1; extra == "dev"
17
17
  Requires-Dist: pytest==8.3.5; extra == "dev"
@@ -1,29 +1,31 @@
1
1
  jac_client/examples/all-in-one/button.jac,sha256=KzMM8B8kjPMZmNXImmi8QwNSKhNLBP38kLmX_NQyz20,142
2
- jac_client/examples/all-in-one/main.jac,sha256=y-LmSXHDqhzVlRbjD_MIS5RkKoKUB1EPQHtY08PT3UE,17829
2
+ jac_client/examples/all-in-one/main.jac,sha256=FlpzztN1J7dOMdBvwBZuPlvnDTuzrrZFg584wVAp7t8,3853
3
3
  jac_client/examples/all-in-one/assets/workers/worker.py,sha256=erXICb1WvDKYlReUIzgZf9v0Pey6pmWDbvAm9vtGQmg,131
4
4
  jac_client/examples/all-in-one/components/CategoryFilter.jac,sha256=YQh1OqqZf89lZxKOKum-fK8ep5h8itNcQC0rbaAlMl4,1838
5
- jac_client/examples/all-in-one/components/Header.jac,sha256=NYRnyQKo8NyVFNfAjpEQkBe-Qc9nwHZy9cXGV24nKUI,503
6
- jac_client/examples/all-in-one/components/ProfitOverview.jac,sha256=zF-chUWBK_6pjTuVpzgv76-1Y3cBE7L9tSx8HyIbtGI,2532
7
- jac_client/examples/all-in-one/components/Summary.jac,sha256=ZKH9JzSpShgjVyyWeCnQDVLrSYYkqo0h3fsGBpothFA,3015
5
+ jac_client/examples/all-in-one/components/Header.jac,sha256=TCgz6XBEB9rAkzFSurTOIiFB3NV_I2nmuS7Zdy2PXV8,510
6
+ jac_client/examples/all-in-one/components/ProfitOverview.jac,sha256=MItkmvvaXi8-fcCUen1Pz0tFYwOjmL4_yGamBYeVD14,2539
7
+ jac_client/examples/all-in-one/components/Summary.jac,sha256=C0cxF-aXnp6PEFveik4Oqmv7Jrv20YCqfZ7Cv7vv44c,3022
8
8
  jac_client/examples/all-in-one/components/TransactionForm.jac,sha256=g7tbmUSX5gw2hobhTvMhCAshC4YbIyJ6yTs937iuPsc,7553
9
9
  jac_client/examples/all-in-one/components/TransactionItem.jac,sha256=OJmdWgYTOa8SsBIfUEEdUWkFRWQT_Cfczdm8O8f-vDY,2133
10
- jac_client/examples/all-in-one/components/TransactionList.jac,sha256=rBm0o3zn7VYfPmRwZVdluwkQCfikBlTYGzEd-qBg9-M,1560
10
+ jac_client/examples/all-in-one/components/TransactionList.jac,sha256=__H-RDBtyYDrMDVMT8dxgQYsRb43IsiKqQhg-pcTnsM,1574
11
11
  jac_client/examples/all-in-one/components/button.jac,sha256=uev3QkRyZSe1iBIc0RC-8evbMrStAbTJHP0_4h4R1Mg,140
12
- jac_client/examples/all-in-one/components/navigation.jac,sha256=jLRToiqtiQXuDxGm9FlBT6O1bJQCts0orypcTFfiaVA,4256
12
+ jac_client/examples/all-in-one/components/navigation.jac,sha256=k9dwIGLiNWA5Scep9dFPIKfIYfyO7gu3t8A1fbd-v24,4027
13
13
  jac_client/examples/all-in-one/constants/categories.jac,sha256=5HrX2JN4OhcQpoJg6ylcE1lzuWaSIaJAn8Ebo9DeYzo,1092
14
14
  jac_client/examples/all-in-one/constants/clients.jac,sha256=8M4AhooSTsxEeC2QTn6UwrNHkLitXUWczCaOwQxrbtw,316
15
- jac_client/examples/all-in-one/context/BudgetContext.jac,sha256=YnAGc60OcKxPYZ2WdKz_0VVf_Lf6z_x3XYql5xFSLYk,890
15
+ jac_client/examples/all-in-one/context/BudgetContext.jac,sha256=pva1ZSKD4EPbZXICld-g771NMu4-dLjqZnsnVMlBqD4,897
16
16
  jac_client/examples/all-in-one/hooks/useBudget.jac,sha256=TmidLwmJUhX_mqD0gKSAXfikwkCKXOfhm9P96LpHbqM,4026
17
17
  jac_client/examples/all-in-one/hooks/useLocalStorage.jac,sha256=3DElBqJvpQhbZNyAfScuZGOXhZM73YPqqF5NzPrvNWo,1196
18
- jac_client/examples/all-in-one/pages/BudgetPlanner.jac,sha256=uHil0s-7otuj7Sn7cobwhWMOMRrMCssbFVD-PPQew2E,3742
19
- jac_client/examples/all-in-one/pages/FeaturesTest.jac,sha256=E2jIt5ivBJSDbhEUzw2_JVywLyWx0bnNO5SB5qyW4n8,4333
20
- jac_client/examples/all-in-one/pages/LandingPage.jac,sha256=KUvow59dbolydimTkX9Ibr6lWPMyE27IbH-zJIUO93o,5428
21
- jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac,sha256=M-h2IkjhP357WpYUWRgrvRSHK5tjZe4RYeogw3LKK-o,2250
22
- jac_client/examples/all-in-one/pages/features_test_ui.cl.jac,sha256=RM7Re1XPpS5fjlmU5aXL-AFaBr77_CcJ1K-C1oFmv7s,26109
23
- jac_client/examples/all-in-one/pages/loginPage.jac,sha256=PBrMTbt3e379nduOEfXGaIpYibXRAivDrxZXubtkaXE,4499
24
- jac_client/examples/all-in-one/pages/nestedDemo.jac,sha256=NJoBLDajJFLSH7h8KQeaWEq66ARgl7Xnj6dCsWIsRGM,1686
25
- jac_client/examples/all-in-one/pages/notFound.jac,sha256=urlwejdhGu4Gf8JyBNyeS7io-htduvEKdJSZjmKVg58,506
26
- jac_client/examples/all-in-one/pages/signupPage.jac,sha256=8gLRXQo1gW_RjPAsydimTrQ9vB8VpqzXP-i_wUrEcqY,4548
18
+ jac_client/examples/all-in-one/pages/[...notFound].jac,sha256=7Em5HP2UVCUncwjKDXDTIGjEV10wmoQGWaLdzietsf0,510
19
+ jac_client/examples/all-in-one/pages/budget.jac,sha256=e1f7zehStW4RYpQue5pCQ0Ltn1_3kdhcVBL50gVJ-Sk,289
20
+ jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac,sha256=kgi4LyUkJmvSO14TuRKDIejzg0nq84isUBPEzVR_D1g,2257
21
+ jac_client/examples/all-in-one/pages/features.jac,sha256=pthjFEgI-vgA4SfvKlExgOYRfAiDMc_4ZYrge3Wm7L0,163
22
+ jac_client/examples/all-in-one/pages/features_test_ui.cl.jac,sha256=DlPS5vvcvffA2GvjU-KBRJQk8i03l1rDzQbTEr0V3vQ,26144
23
+ jac_client/examples/all-in-one/pages/landing.jac,sha256=jxnH_keYDrSVTDUBjo14cOst8-8Ty120DQnTDYrlR14,5259
24
+ jac_client/examples/all-in-one/pages/layout.jac,sha256=z6OAulH9O1cnBNJUrJrs3n0U390QIJHhQlVDpnNx0tA,510
25
+ jac_client/examples/all-in-one/pages/(auth)/index.jac,sha256=eUMqTonmLKOtXLWmp71lcgWhi3aZ9zUsvBRetBU6d8w,11132
26
+ jac_client/examples/all-in-one/pages/(auth)/nested.jac,sha256=0DYTalcZ71T2inB3LhVPVvMnJ08oIK4Sj_dBivwheg8,1405
27
+ jac_client/examples/all-in-one/pages/(public)/login.jac,sha256=YIyXhBu-TgrDMits6JJMXsTl1PExb-zAOdiOC11v4Dc,4501
28
+ jac_client/examples/all-in-one/pages/(public)/signup.jac,sha256=s155Arbc0iLVyIQukflGn_rjUy96RtBuKcGzu8qod8Q,4549
27
29
  jac_client/examples/all-in-one/utils/formatters.jac,sha256=QfOdGgFf2PWcg6ALbHKk5bAO0P4zqBLoM_2nJltCXGY,1544
28
30
  jac_client/examples/asset-serving/css-with-image/main.jac,sha256=eSYYyyY7S5BcioeVtEoGKTOAINAck2oycOEKD3kq_3E,3140
29
31
  jac_client/examples/asset-serving/image-asset/main.jac,sha256=r4FgBiO_Tg4MRxvXVbbpmrY8x_tqr9YUtYcPgNJmQI4,1764
@@ -42,10 +44,10 @@ jac_client/examples/full-stack-with-auth/main.jac,sha256=16n-w20Uf3R2RsD5WLPxjKP
42
44
  jac_client/examples/little-x/main.jac,sha256=XQiFsuzMYPIr3nky0pDtesylDzMjoA7gokVLI1eDZEA,19704
43
45
  jac_client/examples/little-x/src/submit-button.jac,sha256=2FUBiTkZwvZpPQA1YFcjtso4bpvLniit35vliT5rrXY,430
44
46
  jac_client/examples/nested-folders/nested-advance/main.jac,sha256=EvkJeEkCX2lGTSC_VlD6I7He5IwZe4aoyCa6opb5P_M,775
45
- jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac,sha256=MhNU_hQgksEdz6QMPf9T58KmG2H2Hiy4U8bIbWFc7vo,190
46
- jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac,sha256=W85gvda3tysc6O-tZuDv_TeQczAnNSa1FVuOVJ6dAqQ,407
47
+ jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac,sha256=OljvOh5mXxeYtv8cw2E2EzYp78DIEgyVKdLpmKi4vEI,197
48
+ jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac,sha256=5o9czFZ245wKLN_ts0RavF_np1K3LwbYaJ6qOqQ59pE,414
47
49
  jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac,sha256=GWsVPHPpF5gxlkhB4rSPOxaLRT-usEjs0ELoHVvKL9g,1076
48
- jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac,sha256=SRTEvSY5N5bbK_wlk0xawNzdF1XCmokvitUdkKEJuNY,576
50
+ jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac,sha256=bAnL52jkE87U7zxGTrYhpn3JhWett2FBI_4iPoelNc8,583
49
51
  jac_client/examples/nested-folders/nested-basic/main.jac,sha256=fV3UymEAORR_1RsU5z2JeNZ4yFr7B9MmlolQ2MhpIgw,298
50
52
  jac_client/examples/nested-folders/nested-basic/src/button.jac,sha256=BUaKr2Pm6RsgQCRVTZXojMfY8HoIB4S3YiItEpjoxCs,139
51
53
  jac_client/examples/nested-folders/nested-basic/src/components/button.jac,sha256=mvAbsu5Q6DQY4Jfo375k3dBm0rMmKAk6E-o3m5-YmNc,135
@@ -53,29 +55,31 @@ jac_client/examples/ts-support/main.jac,sha256=KcNOi0fZj7tBhWJvusbZ_q3z0cHjlYYfD
53
55
  jac_client/examples/with-router/main.jac,sha256=Aanl3a880KQ-ki8SfskRu8QJRvvm-_pQIvxcfOHB52k,8460
54
56
  jac_client/plugin/cli.jac,sha256=54qGU_7WOKIlE9ltY-0V33Yfu1LkBFs4SSJwCsgQstg,20161
55
57
  jac_client/plugin/client.jac,sha256=WVn8IwQO2Kgr8XLDI8D4FSVj9kdewBXB3Mgi9ifCVTI,2095
56
- jac_client/plugin/client_runtime.cl.jac,sha256=K8u-XwGr998XO2o-dm0OkistqH_odY5GGzcua0VZUW0,2010
57
- jac_client/plugin/plugin_config.jac,sha256=sijvX5C7-5c2T8VWgbGW6G6IYi1k6AP_bdbagfd9jNc,13308
58
+ jac_client/plugin/client_runtime.cl.jac,sha256=njf4m86QGS1uZNFuHGg1Carj7cumzt2Ex57_fS5HGXs,2109
59
+ jac_client/plugin/plugin_config.jac,sha256=el5h0z2cTfqg0WUhTuyZw0BHJncvUPa2rNXrW2iQ4HM,13185
58
60
  jac_client/plugin/vite_client_bundle.jac,sha256=l8EY8O2sdsqhtO8I32Of01dgShRQBfMBNWXyl-y-vtY,1016
59
61
  jac_client/plugin/impl/client.impl.jac,sha256=UOmY3Pzi00Cvqdze_BnoIX1QS5_QWEPDQATNp5VDh4k,8956
60
- jac_client/plugin/impl/client_runtime.impl.jac,sha256=FhBo_GrnjVBLmFw2-DFz4UwBiHDmQLbIWAHQ_qHbxww,11181
62
+ jac_client/plugin/impl/client_runtime.impl.jac,sha256=_YlTwJd3cd4sqHwkYgn_ujFWEDg4N7TPYpKODj46n1Y,11524
61
63
  jac_client/plugin/impl/vite_client_bundle.impl.jac,sha256=KXFqXSZaa08Z6CrEqebfdjo7TvdakTNJHVnGOhj4e0s,2620
62
64
  jac_client/plugin/src/__init__.jac,sha256=pdkHciVYLG1UnE-Pez1X4SULUG2ACMYrUYwWq-CZ3Sw,727
63
65
  jac_client/plugin/src/asset_processor.jac,sha256=Qpm1a174PPJl32nH_eDZYR-6vx59V2NEWfj0knUMwXY,961
64
- jac_client/plugin/src/compiler.jac,sha256=vTkxjjFwykhWMvfGF9jY6XC5KHXJ-LjHUc6EsRqSjjs,2441
66
+ jac_client/plugin/src/compiler.jac,sha256=cH6rd8DCbHSeHc8iNwRAkBvwDBL3YZTyeC1mJthVJUA,3059
65
67
  jac_client/plugin/src/config_loader.jac,sha256=kgVpeiQdEzMs5vgdHyfAR08N55AJjOobJonaotRaJdU,1367
66
68
  jac_client/plugin/src/desktop_config.jac,sha256=bFGW5lnLeUZgjbjd1BMHvf38Qbsb8VZ0YcbUf78LZ1Q,1249
67
69
  jac_client/plugin/src/import_processor.jac,sha256=wYg8bD5qHxpvFyXkQxNpqUIV3ei5aMoevA2bWyq9VG8,589
68
70
  jac_client/plugin/src/jac_to_js.jac,sha256=U2_v95tdsbw1oMPGPY0HOhDcAsHg3uQIcGWzLmFdWyc,1115
69
71
  jac_client/plugin/src/package_installer.jac,sha256=5maePqEe3xIys9Ufn54m423tAr54z-urtXc84Ki3bIY,850
72
+ jac_client/plugin/src/route_scanner.jac,sha256=UtMYbXzR7YF-DMQEGnzdinsZn_cHox22QccqOP1YAiI,1472
70
73
  jac_client/plugin/src/vite_bundler.jac,sha256=3Xk6mesoOp76d31qG_xQJfB_a8wI6xydM-rBsSIzdqU,2390
71
74
  jac_client/plugin/src/impl/asset_processor.impl.jac,sha256=-ljWYzKLKyAdfJybr8sjJlYSK57KzueeNvTo_WjAFAY,4256
72
- jac_client/plugin/src/impl/compiler.impl.jac,sha256=QPKV5UoBcScC6oSOceFM7Y9gsiID-pw_vkKeJXLs080,12054
75
+ jac_client/plugin/src/impl/compiler.impl.jac,sha256=gLd2VeLgHhDIDiRq081DYlRU-3Y2tW43MvFRU5S1wIg,19458
73
76
  jac_client/plugin/src/impl/config_loader.impl.jac,sha256=F-blKeVWtuh382cazxrMdmpA8lpDFjVawkZBhKRtMhI,4731
74
77
  jac_client/plugin/src/impl/desktop_config.impl.jac,sha256=mitiS3RfHjMstUqPJs2OnB6lFk27GGSrKXCrJK5Sceg,6758
75
78
  jac_client/plugin/src/impl/import_processor.impl.jac,sha256=BMkkZXlF-6jbC-r3tuFj8PDFvDZrbfzuhnaw8qraWIQ,1214
76
79
  jac_client/plugin/src/impl/jac_to_js.impl.jac,sha256=KHblc0PloMJqsX2DRdp3tyJ0k5d39ArPxBLKXsw7mjA,1692
77
- jac_client/plugin/src/impl/package_installer.impl.jac,sha256=YbXlzqmrzNtNSyfDcVY3-975Pqbc9H9wxQ1LQxO6ypE,4020
78
- jac_client/plugin/src/impl/vite_bundler.impl.jac,sha256=ANywKi6hQWYcFyXbEKovy05gaHCjEtzaf4VXRdUP8D4,30700
80
+ jac_client/plugin/src/impl/package_installer.impl.jac,sha256=rnY4uCzLTMMJ1LvCwGEZyyin4eJOeHDFM-aCqFggWjw,4060
81
+ jac_client/plugin/src/impl/route_scanner.impl.jac,sha256=iRKp819wq303Bnawc_9DW37Xyj7YCMlFJ0w51AgwWSs,7088
82
+ jac_client/plugin/src/impl/vite_bundler.impl.jac,sha256=ivmt1Cs-LLsUsCcWKSSdbY2lc4Xa5VXUqWt8hK4ejpU,30888
79
83
  jac_client/plugin/src/targets/desktop_target.jac,sha256=dtfVf6ZirIi4ghTON3Touv_0LGcH2-6-IJP1xzTb_ug,1344
80
84
  jac_client/plugin/src/targets/register.jac,sha256=tiHmOKEgOWlYdY5DYko6M0Ktp2kZr7TZFWKq18Tmp24,835
81
85
  jac_client/plugin/src/targets/registry.jac,sha256=wl1QJEWAyI7KAfTSjIw3PdxpYgtUCpaj7D0c09jZxFM,2664
@@ -87,15 +91,15 @@ jac_client/plugin/src/targets/impl/web_target.impl.jac,sha256=fQQ1zahAnvsD4BFr8J
87
91
  jac_client/plugin/utils/__init__.jac,sha256=u_rH9sxR8EgLUsbaNpv0tgYmt5sLlrb41LhPRk0w0g4,166
88
92
  jac_client/plugin/utils/bun_installer.jac,sha256=irvOnt2WF6sB_aOH5v6L9Jde-NoP39JDjysXB2V0nv0,473
89
93
  jac_client/plugin/utils/client_deps.jac,sha256=BLJ7UaBi2aC-V-Vl4IsfGYfnRI75QtaO1JNYo_ut0UI,569
90
- jac_client/plugin/utils/impl/bun_installer.impl.jac,sha256=S6yqaxNvQ6HYgSfO7bRu8NiNsYvG8mvK8j4d1_Vf_FM,3483
91
- jac_client/plugin/utils/impl/client_deps.impl.jac,sha256=u2APi7hYTTCzDuiG2wTlsM4Dfi1QPY9FNO6xAIq_gPw,2774
94
+ jac_client/plugin/utils/impl/bun_installer.impl.jac,sha256=AdO65jsrdndbD5ru8xUG_IOQRtSG_N8mnq4Xu7fIPiY,3379
95
+ jac_client/plugin/utils/impl/client_deps.impl.jac,sha256=bcxnv6h8QO6hVdZaoIaTxB4LgaVSp0GwIlQgR_GV4g8,2697
92
96
  jac_client/templates/client.jacpack,sha256=o3aVPZTI1T52gvLHv5bGK0czX8cAx0GgWfMjQP5yA3w,4187
93
- jac_client/templates/fullstack.jacpack,sha256=qS5vlhHjpInxVFkAD8T0FftqWFVW6hAbexl9LufW0eE,23109
97
+ jac_client/templates/fullstack.jacpack,sha256=XjyEO5WJtgWSvZssD680SFrZORDR-Wy9FCRSAHLeoFs,23279
94
98
  jac_client/tests/__init__.py,sha256=HSr78dvKyvNmP7bLmAssfYItAn4puFwAe1oWnV8l7pA,36
95
99
  jac_client/tests/conftest.py,sha256=PU-eYPagFRXSKeXaGadES3s2LlNjzy5VSsXuUPse8_4,11136
96
100
  jac_client/tests/test_cli.py,sha256=YtpFzdQca264ZIXO9RBfvV6LVBGaeZ2B5iKFsvVMx4E,37995
97
101
  jac_client/tests/test_desktop_api_url.py,sha256=x81jMYxSnIFheU2dvDyqqfoKnm3i0LxY5BBY8lObxfU,32513
98
- jac_client/tests/test_e2e.py,sha256=QNVKkgwLY1WDmc6EKJ9Vznuu0Q2A64HZAfzDZe0dEaQ,8184
102
+ jac_client/tests/test_e2e.py,sha256=icRlO6LYBdsS7p7HqaPQj9n6C2TMviggdiv7wmvrmvw,7874
99
103
  jac_client/tests/test_helpers.py,sha256=UqXYIl0Vi_8IDJ1efsJh1BcaJPudINfpLojhwDxxtNg,1615
100
104
  jac_client/tests/test_it.py,sha256=p8840vOmvhzS56BQIiAvpcK6gkavENoI5pxhFwDd5Ns,46381
101
105
  jac_client/tests/test_it_desktop.py,sha256=mzaDfFFhmN762FrFH1WEXQh99cq5bS-eZqwmI-ousvM,32143
@@ -109,8 +113,8 @@ jac_client/tests/fixtures/relative_import/button.jac,sha256=kCDNaHEcxMEFdezfnecy
109
113
  jac_client/tests/fixtures/spawn_test/app.jac,sha256=cooYrB7xhHMk9xWOZ6MIiY4I6kmodW2v-nyVnd1RR74,3863
110
114
  jac_client/tests/fixtures/test_fragments_spread/app.jac,sha256=CMzAz4Ydx_5eAV82-io8sJdy8_xy-mR7YOVc7FcozlU,1277
111
115
  jac_client/tests/fixtures/with-ts/app.jac,sha256=1j1IH_4mfgLtgPq2Xc5PrGHBvmLQl6U0ddbb2L5KwuA,981
112
- jac_client-0.2.13.dist-info/METADATA,sha256=husYWbw76gE444kA_UzCcenhK8FfAkFY0UeX_YFMpXs,5552
113
- jac_client-0.2.13.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
114
- jac_client-0.2.13.dist-info/entry_points.txt,sha256=fVrlaJKcSa2DK2hcfR6bNaQDB9mszMpZeEa6pitMdt4,154
115
- jac_client-0.2.13.dist-info/top_level.txt,sha256=u1VEBfiqwRrZEopKraIh-Ym55qSnDZR3Q5xdw2HinhU,11
116
- jac_client-0.2.13.dist-info/RECORD,,
116
+ jac_client-0.2.14.dist-info/METADATA,sha256=ypYp-09-em17YgPMvusfREm-870m3IH_UWDzt3nwPfo,5552
117
+ jac_client-0.2.14.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
118
+ jac_client-0.2.14.dist-info/entry_points.txt,sha256=fVrlaJKcSa2DK2hcfR6bNaQDB9mszMpZeEa6pitMdt4,154
119
+ jac_client-0.2.14.dist-info/top_level.txt,sha256=u1VEBfiqwRrZEopKraIh-Ym55qSnDZR3Q5xdw2HinhU,11
120
+ jac_client-0.2.14.dist-info/RECORD,,
@@ -1,140 +0,0 @@
1
- # Features Test Page - Backend Logic
2
- # This file is intentionally kept empty to demonstrate the separation of concerns
3
- # where UI logic resides in .cl.jac files and backend walker logic in app.jac
4
-
5
- # All walker implementations for this feature are in main.jac
6
- # This demonstrates that you can have separate .jac files for different purposes
7
- # Features Test - Backend Walkers
8
- # This file demonstrates walker functionality for testing various JAC features
9
- import from datetime {
10
- datetime,
11
- timedelta
12
- }
13
-
14
- # Node definition for storing test data
15
- node TestData {
16
- has message: str,
17
- count: int = 0,
18
- created_at: str = "";
19
- }
20
-
21
- # Walker: Create test data
22
- walker create_test_data {
23
- has message: str;
24
-
25
- can create with `root entry {
26
- new_data = here ++> TestData(
27
- message=self.message,
28
- count=1,
29
- created_at=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
30
- );
31
- report new_data;
32
- }
33
- }
34
-
35
- # Walker: Read all test data
36
- walker read_test_data {
37
- can read with `root entry {
38
- visit [-->(`?TestData)];
39
- }
40
-
41
- can report_data with exit {
42
- report here;
43
- }
44
- }
45
-
46
- # Walker: Update test data
47
- walker update_test_data {
48
- has new_message: str;
49
-
50
- can update with TestData entry {
51
- here.message = self.new_message;
52
- here.count = here.count + 1;
53
- report here;
54
- }
55
- }
56
-
57
- # Walker: Delete test data
58
- walker delete_test_data {
59
- can delete with TestData entry {
60
- del here;
61
- report {"status": "deleted"};
62
- }
63
- }
64
-
65
- # Walker: String manipulation test
66
- walker test_string_methods {
67
- has input_text: str;
68
-
69
- can process with `root entry {
70
- result = {
71
- "original": self.input_text,
72
- "uppercase": self.input_text.upper(),
73
- "lowercase": self.input_text.lower(),
74
- "capitalized": self.input_text.capitalize(),
75
- "reversed": self.input_text[::-1],
76
- "length": len(self.input_text),
77
- "words": self.input_text.split(),
78
- "trimmed": self.input_text.strip(),
79
- "replaced": self.input_text.replace("test", "demo")
80
- };
81
- report result;
82
- }
83
- }
84
-
85
- # Walker: Array/List operations test
86
- walker test_list_operations {
87
- has numbers: list;
88
-
89
- can process with `root entry {
90
- result = {
91
- "original": self.numbers,
92
- "sorted": sorted(self.numbers),
93
- "reversed": list(reversed(self.numbers)),
94
- "sum": sum(self.numbers),
95
- "max": max(self.numbers) if len(self.numbers) > 0 else 0,
96
- "min": min(self.numbers) if len(self.numbers) > 0 else 0,
97
- "length": len(self.numbers),
98
- "doubled": [x * 2 for x in self.numbers]
99
- };
100
- report result;
101
- }
102
- }
103
-
104
- # Walker: Complex data processing
105
- walker process_complex_data {
106
- has items: list;
107
-
108
- can process with `root entry {
109
- processed = [];
110
- for item in self.items {
111
- processed.append(
112
- {
113
- "id": item.get("id", 0),
114
- "name": item.get("name", "").upper(),
115
- "value": item.get("value", 0) * 2,
116
- "processed_at": datetime.now().strftime("%H:%M:%S")
117
- }
118
- );
119
- }
120
-
121
- result = {
122
- "original_count": len(self.items),
123
- "processed_count": len(processed),
124
- "items": processed,
125
- "total_value": sum([p["value"] for p in processed])
126
- };
127
-
128
- report result;
129
- }
130
- }
131
-
132
- # Re-export client components from the standalone client module
133
- cl {
134
- import from .budget_planner_ui { BudgetPlanner as BudgetPlannerUI }
135
-
136
- def:pub BudgetPlanner -> any {
137
- return
138
- <BudgetPlannerUI />;
139
- }
140
- }