framework-m-core 0.4.2__py3-none-any.whl → 0.5.0__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.
- framework_m_core/cli/init.py +137 -0
- framework_m_core/cli/main.py +4 -0
- framework_m_core/cli/plugin_loader.py +5 -0
- framework_m_core/domain/outbox.py +2 -2
- framework_m_core/pii.py +3 -3
- {framework_m_core-0.4.2.dist-info → framework_m_core-0.5.0.dist-info}/METADATA +1 -1
- {framework_m_core-0.4.2.dist-info → framework_m_core-0.5.0.dist-info}/RECORD +8 -7
- {framework_m_core-0.4.2.dist-info → framework_m_core-0.5.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Init CLI Command - Scaffold frontend from template.
|
|
2
|
+
|
|
3
|
+
This module provides commands to initialize new components of Framework M.
|
|
4
|
+
|
|
5
|
+
Architecture:
|
|
6
|
+
- No global state - all paths passed explicitly
|
|
7
|
+
- Template resources via importlib.resources (Python 3.9+)
|
|
8
|
+
- Explicit error handling for file operations
|
|
9
|
+
- User feedback with print statements
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
m init:frontend # Initialize in ./frontend
|
|
13
|
+
m init:frontend ../my-frontend # Initialize in custom location
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import shutil
|
|
19
|
+
import subprocess
|
|
20
|
+
from importlib.resources import files
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Annotated
|
|
23
|
+
|
|
24
|
+
import cyclopts
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _copy_template(source_template: Path, target: Path) -> None:
|
|
28
|
+
"""Copy template directory to target location.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
source_template: Source template directory
|
|
32
|
+
target: Target directory
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
FileExistsError: If target already exists
|
|
36
|
+
RuntimeError: If copy fails
|
|
37
|
+
"""
|
|
38
|
+
if target.exists():
|
|
39
|
+
msg = f"Target directory already exists: {target}"
|
|
40
|
+
raise FileExistsError(msg)
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
shutil.copytree(source_template, target)
|
|
44
|
+
print(f"✓ Copied template to {target}")
|
|
45
|
+
except Exception as e:
|
|
46
|
+
msg = f"Failed to copy template: {e}"
|
|
47
|
+
raise RuntimeError(msg) from e
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _install_dependencies(target: Path) -> None:
|
|
51
|
+
"""Install pnpm dependencies in target directory.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
target: Frontend directory
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
RuntimeError: If pnpm install fails
|
|
58
|
+
"""
|
|
59
|
+
print("\nInstalling dependencies with pnpm...")
|
|
60
|
+
try:
|
|
61
|
+
subprocess.run(
|
|
62
|
+
["pnpm", "install"],
|
|
63
|
+
cwd=str(target),
|
|
64
|
+
check=True,
|
|
65
|
+
)
|
|
66
|
+
print("✓ Dependencies installed")
|
|
67
|
+
except subprocess.CalledProcessError as e:
|
|
68
|
+
msg = f"Failed to install dependencies: {e}"
|
|
69
|
+
raise RuntimeError(msg) from e
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def init_frontend_command(
|
|
73
|
+
target: Annotated[
|
|
74
|
+
Path | None,
|
|
75
|
+
cyclopts.Parameter(
|
|
76
|
+
name=["target"],
|
|
77
|
+
help="Target directory for frontend (default: ./frontend)",
|
|
78
|
+
),
|
|
79
|
+
] = None,
|
|
80
|
+
) -> None:
|
|
81
|
+
"""Initialize a new frontend from template.
|
|
82
|
+
|
|
83
|
+
This scaffolds the bundled Desk frontend template to a target directory
|
|
84
|
+
and installs dependencies. The template includes:
|
|
85
|
+
- React + TypeScript + Vite
|
|
86
|
+
- @framework-m/desk npm package integration
|
|
87
|
+
- Refine.dev setup with authProvider, dataProvider, liveProvider
|
|
88
|
+
- Tailwind CSS + shadcn/ui components
|
|
89
|
+
|
|
90
|
+
Examples:
|
|
91
|
+
m init:frontend # Initialize in ./frontend
|
|
92
|
+
m init:frontend ../my-frontend # Custom location
|
|
93
|
+
|
|
94
|
+
Architecture Notes:
|
|
95
|
+
- Uses apps/studio/frontend as template source
|
|
96
|
+
- No global state - all paths explicit
|
|
97
|
+
- Validates target doesn't exist (prevents overwrites)
|
|
98
|
+
- Runs pnpm install automatically
|
|
99
|
+
"""
|
|
100
|
+
# Default to ./frontend
|
|
101
|
+
if target is None:
|
|
102
|
+
target = Path("frontend")
|
|
103
|
+
|
|
104
|
+
# Resolve to absolute path
|
|
105
|
+
target = target.resolve()
|
|
106
|
+
|
|
107
|
+
# Find template source from framework_m package
|
|
108
|
+
try:
|
|
109
|
+
template_resource = files("framework_m") / "templates" / "frontend"
|
|
110
|
+
if not template_resource.is_dir():
|
|
111
|
+
msg = "Frontend template not found in framework_m package"
|
|
112
|
+
raise RuntimeError(msg)
|
|
113
|
+
|
|
114
|
+
# Convert to Path for file operations
|
|
115
|
+
template_source = Path(str(template_resource))
|
|
116
|
+
except Exception as e:
|
|
117
|
+
msg = f"Failed to locate template: {e}"
|
|
118
|
+
raise RuntimeError(msg) from e
|
|
119
|
+
|
|
120
|
+
print(f"Initializing frontend at {target}")
|
|
121
|
+
print(f"Template source: {template_source}")
|
|
122
|
+
|
|
123
|
+
# Copy template
|
|
124
|
+
_copy_template(template_source, target)
|
|
125
|
+
|
|
126
|
+
# Install dependencies
|
|
127
|
+
_install_dependencies(target)
|
|
128
|
+
|
|
129
|
+
print("\n✓ Frontend initialized successfully!")
|
|
130
|
+
print("\nNext steps:")
|
|
131
|
+
print(f" cd {target}")
|
|
132
|
+
print(" pnpm dev")
|
|
133
|
+
print("\nOr start with:")
|
|
134
|
+
print(" m start --with-frontend")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
__all__ = ["init_frontend_command"]
|
framework_m_core/cli/main.py
CHANGED
|
@@ -2,6 +2,7 @@ import cyclopts
|
|
|
2
2
|
|
|
3
3
|
from framework_m_core import __version__
|
|
4
4
|
from framework_m_core.cli.config import config_set_command, config_show_command
|
|
5
|
+
from framework_m_core.cli.init import init_frontend_command
|
|
5
6
|
from framework_m_core.cli.plugin_loader import load_plugins
|
|
6
7
|
from framework_m_core.cli.utility import info_command
|
|
7
8
|
|
|
@@ -18,6 +19,9 @@ app.command(info_command, name="info")
|
|
|
18
19
|
app.command(config_show_command, name="config:show")
|
|
19
20
|
app.command(config_set_command, name="config:set")
|
|
20
21
|
|
|
22
|
+
# Init commands
|
|
23
|
+
app.command(init_frontend_command, name="init:frontend")
|
|
24
|
+
|
|
21
25
|
# Load plugins
|
|
22
26
|
load_plugins(app)
|
|
23
27
|
|
|
@@ -94,6 +94,11 @@ def register_plugins(app: cyclopts.App, plugins: list[EntryPoint]) -> None:
|
|
|
94
94
|
logger.warning(
|
|
95
95
|
f"Plugin '{ep.name}' is not a cyclopts App or callable, skipping"
|
|
96
96
|
)
|
|
97
|
+
except cyclopts.exceptions.CommandCollisionError:
|
|
98
|
+
# Command already registered (core takes precedence over plugins)
|
|
99
|
+
logger.debug(
|
|
100
|
+
f"CLI plugin '{ep.name}' skipped - command already registered by core"
|
|
101
|
+
)
|
|
97
102
|
except Exception as e:
|
|
98
103
|
logger.warning(
|
|
99
104
|
f"Failed to register CLI plugin '{ep.name}': {e}",
|
|
@@ -27,7 +27,7 @@ Example:
|
|
|
27
27
|
from __future__ import annotations
|
|
28
28
|
|
|
29
29
|
from datetime import UTC, datetime
|
|
30
|
-
from enum import
|
|
30
|
+
from enum import StrEnum
|
|
31
31
|
from typing import Any
|
|
32
32
|
from uuid import UUID, uuid4
|
|
33
33
|
|
|
@@ -39,7 +39,7 @@ def _utc_now() -> datetime:
|
|
|
39
39
|
return datetime.now(UTC)
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
class OutboxStatus(
|
|
42
|
+
class OutboxStatus(StrEnum):
|
|
43
43
|
"""Status of an outbox entry.
|
|
44
44
|
|
|
45
45
|
Attributes:
|
framework_m_core/pii.py
CHANGED
|
@@ -23,7 +23,7 @@ Example:
|
|
|
23
23
|
|
|
24
24
|
from __future__ import annotations
|
|
25
25
|
|
|
26
|
-
from enum import
|
|
26
|
+
from enum import StrEnum
|
|
27
27
|
from typing import Any
|
|
28
28
|
|
|
29
29
|
from pydantic import BaseModel, Field
|
|
@@ -35,7 +35,7 @@ from framework_m_core.config import load_config
|
|
|
35
35
|
# =============================================================================
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class AuthMode(
|
|
38
|
+
class AuthMode(StrEnum):
|
|
39
39
|
"""Available authentication modes.
|
|
40
40
|
|
|
41
41
|
Attributes:
|
|
@@ -142,7 +142,7 @@ def is_sensitive_pii(field_name: str) -> bool:
|
|
|
142
142
|
# =============================================================================
|
|
143
143
|
|
|
144
144
|
|
|
145
|
-
class DeletionMode(
|
|
145
|
+
class DeletionMode(StrEnum):
|
|
146
146
|
"""How to handle user data on account deletion.
|
|
147
147
|
|
|
148
148
|
Attributes:
|
|
@@ -5,7 +5,7 @@ framework_m_core/decorators.py,sha256=UFY1HbiSLVmjKO8Ov7C6jdfMq7ND5OgwHb8WfIHiwT
|
|
|
5
5
|
framework_m_core/exceptions.py,sha256=BwFviTUBB4FW2_fzDWYB5eAYzf0z1GVVX-vjcU4Fi78,4098
|
|
6
6
|
framework_m_core/permission_lookup.py,sha256=dhJHiazQQ8SvC_MYizmFfwKgrTgT5g0qxa4Vq9thk8Q,6370
|
|
7
7
|
framework_m_core/permissions.py,sha256=e7RRA1xbIO1WfFPEiTO4lDmvxkhW29QBisHPYqbzbt0,7177
|
|
8
|
-
framework_m_core/pii.py,sha256=
|
|
8
|
+
framework_m_core/pii.py,sha256=lu3dk52SAS0TLAEDnLgiWbvHIzZtceidv5-SOVh-Z6A,5884
|
|
9
9
|
framework_m_core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
framework_m_core/registry.py,sha256=FHgzaczGQjxIQU2JosnKppDyl3DB6ud09Ukgx-6GTVA,10279
|
|
11
11
|
framework_m_core/rls.py,sha256=iBKGiWepYGW3CrObJeRjSra6Xv7xODRjEF6J--2NhsU,6158
|
|
@@ -15,8 +15,9 @@ framework_m_core/system_context.py,sha256=Axf5zixlBc81BcXLUuiiUBH2YzuRJ8BHj2CX2y
|
|
|
15
15
|
framework_m_core/unit_of_work.py,sha256=FtOtsW0IKuxelyb_h2Hjszhr6ZWwQLIoVI4ZJle3bv4,4158
|
|
16
16
|
framework_m_core/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
framework_m_core/cli/config.py,sha256=iePjU1xcsMSvK5M6QpO_FpossOOetRb9NdaK4T4ym2Y,3457
|
|
18
|
-
framework_m_core/cli/
|
|
19
|
-
framework_m_core/cli/
|
|
18
|
+
framework_m_core/cli/init.py,sha256=Ru6DIeMW42akVnkV70Q6HWd9jp6M8UbrvO1gCLpGawg,4022
|
|
19
|
+
framework_m_core/cli/main.py,sha256=5J7BTrFZSgUfHtbOhnXS8aKqZ6B0jkicZ5U8ZP5pOeY,770
|
|
20
|
+
framework_m_core/cli/plugin_loader.py,sha256=-ITJk3DVyRjUl1PwNcjXNk6padDfM6dxmalRb4mf4b4,4210
|
|
20
21
|
framework_m_core/cli/utility.py,sha256=CHPIs5_yfKb-xLL-H2NKM_FOQkFSVTlZGBL1C1njgYU,1275
|
|
21
22
|
framework_m_core/doctypes/__init__.py,sha256=vC7opN27HoEKiRDuPwN-tq-dXwG3retVoDKeBlbiTrg,1847
|
|
22
23
|
framework_m_core/doctypes/activity_log.py,sha256=_5XYRqe0NC98u1_9aWpZk1DdjHaTNPKp-Qb02_aE8pI,3564
|
|
@@ -49,7 +50,7 @@ framework_m_core/domain/base_controller.py,sha256=fXBMtogBxI-Igw8ntXH2iBe-OUFM1Y
|
|
|
49
50
|
framework_m_core/domain/base_doctype.py,sha256=NShhuo_4rLruFLG4NL26swZ-bd3P-f4EIO2K5CLeaWc,9258
|
|
50
51
|
framework_m_core/domain/mixins.py,sha256=Yk8fwYWyq9MJp_uU_r5jxf1nj2q5lYN3i1Ip_1ORFe8,3596
|
|
51
52
|
framework_m_core/domain/naming_counter.py,sha256=UWQeOSeGqFHCFc-XqxWnudCsZHgSM43f5u93lvj3HSY,1626
|
|
52
|
-
framework_m_core/domain/outbox.py,sha256=
|
|
53
|
+
framework_m_core/domain/outbox.py,sha256=s39lpeTIni3Olr9K28a1PhEb19V6Qvj6WxfF5Td_DK0,2779
|
|
53
54
|
framework_m_core/events/__init__.py,sha256=3qB9L7DjPTArt6OQnehsGXLJbLJ91DpWPMdsMsvtBS8,2760
|
|
54
55
|
framework_m_core/interfaces/__init__.py,sha256=mfYjbV4Iwc2l_iaj2iVf2AbElc9LO-3a9WIqCBRXLaE,672
|
|
55
56
|
framework_m_core/interfaces/audit.py,sha256=tRD63O6vCjOHt4R3XtBsQM-BeCjIjATSqDP5O1-plP8,6740
|
|
@@ -82,6 +83,6 @@ framework_m_core/interfaces/workflow.py,sha256=C1LDohdvmWF9P_PMq0FVHySHb2URHzxAO
|
|
|
82
83
|
framework_m_core/services/__init__.py,sha256=xcv_UWRyhBSt70yJBVBEyrtRSpaINj-JC1Rs8KIpnUw,351
|
|
83
84
|
framework_m_core/services/user_manager.py,sha256=PYI0URoVOc0cU4bQUlrIT1nK1VTwy2ThsvChhDeJ2Ss,5696
|
|
84
85
|
framework_m_core/types/job_context.py,sha256=9Xhqidj--eiJZNASENRsQi_qZkFj-Nsy_-fDj1uMP9E,2766
|
|
85
|
-
framework_m_core-0.
|
|
86
|
-
framework_m_core-0.
|
|
87
|
-
framework_m_core-0.
|
|
86
|
+
framework_m_core-0.5.0.dist-info/METADATA,sha256=y6_og0PoEMz5hxI1vFomNLFwpxr1Qs8ASIEhTI66V-8,2663
|
|
87
|
+
framework_m_core-0.5.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
88
|
+
framework_m_core-0.5.0.dist-info/RECORD,,
|
|
File without changes
|