maildeno 2.0.0__tar.gz
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.
- maildeno-2.0.0/.gitignore +55 -0
- maildeno-2.0.0/CHANGELOG.md +107 -0
- maildeno-2.0.0/LICENSE +21 -0
- maildeno-2.0.0/PKG-INFO +974 -0
- maildeno-2.0.0/README.md +937 -0
- maildeno-2.0.0/maildeno/__init__.py +47 -0
- maildeno-2.0.0/maildeno/_async_client.py +243 -0
- maildeno-2.0.0/maildeno/_cache.py +291 -0
- maildeno-2.0.0/maildeno/_client.py +276 -0
- maildeno-2.0.0/maildeno/_error.py +115 -0
- maildeno-2.0.0/maildeno/_internal.py +96 -0
- maildeno-2.0.0/maildeno/_minify.py +74 -0
- maildeno-2.0.0/maildeno/_renderer.py +213 -0
- maildeno-2.0.0/maildeno/_types.py +144 -0
- maildeno-2.0.0/maildeno/engine.wasm +0 -0
- maildeno-2.0.0/maildeno/internal.py +96 -0
- maildeno-2.0.0/maildeno/py.typed +0 -0
- maildeno-2.0.0/pyproject.toml +104 -0
- maildeno-2.0.0/tests/test_async_client.py +224 -0
- maildeno-2.0.0/tests/test_client.py +472 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.so
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
.Python
|
|
8
|
+
build/
|
|
9
|
+
develop-eggs/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
lib/
|
|
15
|
+
lib64/
|
|
16
|
+
parts/
|
|
17
|
+
sdist/
|
|
18
|
+
var/
|
|
19
|
+
wheels/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
*.egg
|
|
22
|
+
MANIFEST
|
|
23
|
+
.pypirc
|
|
24
|
+
|
|
25
|
+
# Virtual environments
|
|
26
|
+
.venv/
|
|
27
|
+
venv/
|
|
28
|
+
env/
|
|
29
|
+
ENV/
|
|
30
|
+
|
|
31
|
+
# Testing / coverage
|
|
32
|
+
.pytest_cache/
|
|
33
|
+
.coverage
|
|
34
|
+
.coverage.*
|
|
35
|
+
htmlcov/
|
|
36
|
+
.tox/
|
|
37
|
+
coverage.xml
|
|
38
|
+
*.cover
|
|
39
|
+
|
|
40
|
+
# Type checkers
|
|
41
|
+
.mypy_cache/
|
|
42
|
+
.pyright/
|
|
43
|
+
.ruff_cache/
|
|
44
|
+
|
|
45
|
+
# IDE
|
|
46
|
+
.vscode/
|
|
47
|
+
.idea/
|
|
48
|
+
*.swp
|
|
49
|
+
|
|
50
|
+
# OS
|
|
51
|
+
.DS_Store
|
|
52
|
+
|
|
53
|
+
# Env files
|
|
54
|
+
.env
|
|
55
|
+
.env.local
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
This project adheres to [Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## [2.0.0] - 2026-06-07
|
|
9
|
+
|
|
10
|
+
### Changed — breaking
|
|
11
|
+
|
|
12
|
+
- **Rendering is now local.** The SDK no longer sends `dynamic_data` to the
|
|
13
|
+
Maildeno server. Template JSON is fetched once via
|
|
14
|
+
`GET /v1/sdk/template/{id}` and rendered in-process using the embedded
|
|
15
|
+
Wasm engine. Merge tags and visibility context never leave your server.
|
|
16
|
+
- `POST /v1/sdk/render` is no longer called. Existing direct HTTP integrations
|
|
17
|
+
continue to work until the endpoint is removed (see sunset date in the
|
|
18
|
+
`Deprecation` response header).
|
|
19
|
+
- `RenderResult` now has an optional `from_stale_cache: bool = False` field.
|
|
20
|
+
Code that accesses `.output`, `.target`, `.template_id` is unaffected.
|
|
21
|
+
- `invalidate(template_id)` is deprecated. Use `delete_cached(template_id)`.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **`wasmtime`** added as a runtime dependency — the Wasm engine that runs
|
|
26
|
+
the embedded renderer. Supports Linux, macOS, and Windows on Python 3.9+.
|
|
27
|
+
- **`engine.wasm`** shipped inside the `maildeno` package. Located via
|
|
28
|
+
`importlib.resources` — works correctly in wheels, editable installs,
|
|
29
|
+
virtualenvs, Lambda layers, and Docker containers.
|
|
30
|
+
- **In-process template cache.** Template JSON is cached after the first
|
|
31
|
+
fetch. Subsequent calls to the same `template_id` render with zero network
|
|
32
|
+
overhead.
|
|
33
|
+
- **Stale-on-error fallback.** If the cache TTL expires and the server cannot
|
|
34
|
+
be reached, the SDK renders from the last known-good cached copy and sets
|
|
35
|
+
`result.from_stale_cache = True`. Your send pipeline continues uninterrupted
|
|
36
|
+
during Maildeno downtime. Only throws when the server is unreachable *and*
|
|
37
|
+
no prior cached copy exists for that template.
|
|
38
|
+
- **`cache=` constructor parameter.** Controls the caching strategy — memory
|
|
39
|
+
(default, zero config) or disk (survives process restarts).
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
# Memory — default
|
|
43
|
+
client = MaildenoClient(api_key="...", cache={"ttl": 60_000})
|
|
44
|
+
|
|
45
|
+
# Disk — persists across restarts
|
|
46
|
+
client = MaildenoClient(
|
|
47
|
+
api_key="...",
|
|
48
|
+
cache={"type": "disk", "path": "/var/cache/maildeno", "ttl": 300_000},
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- **`list_cached()`** — return the IDs of all templates currently in the cache.
|
|
53
|
+
- **`delete_cached(template_id)`** — remove a single template from the cache
|
|
54
|
+
immediately, bypassing TTL. Replaces `invalidate()`.
|
|
55
|
+
- **`clear_cache()`** — wipe the entire cache.
|
|
56
|
+
- **`CacheConfig` TypedDict** exported from the package root.
|
|
57
|
+
- **`TemplateJson` TypedDict** exported from the package root — the shape of
|
|
58
|
+
the raw template payload returned by `GET /v1/sdk/template/{id}`.
|
|
59
|
+
- **Thread-safe Wasm singleton.** The Wasm engine instance is loaded lazily on
|
|
60
|
+
the first render call and reused for the process lifetime. A lock ensures
|
|
61
|
+
exactly-once initialisation even under concurrent startup.
|
|
62
|
+
- **Async Wasm via thread executor.** `AsyncMaildenoClient` dispatches Wasm
|
|
63
|
+
renders to `asyncio`'s default thread-pool executor so the event loop is
|
|
64
|
+
never blocked.
|
|
65
|
+
|
|
66
|
+
### Migration from v1
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# v1 — server rendered
|
|
70
|
+
result = client.render(
|
|
71
|
+
template_id="...",
|
|
72
|
+
target="html",
|
|
73
|
+
dynamic_data={"merge_tags": {"text": {"name": "Noruwa"}}},
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# v2 — local render, identical call site
|
|
77
|
+
result = client.render(
|
|
78
|
+
template_id="...",
|
|
79
|
+
target="html",
|
|
80
|
+
dynamic_data={"merge_tags": {"text": {"name": "Noruwa"}}},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Optional: check if rendered from stale cache
|
|
84
|
+
if result.from_stale_cache:
|
|
85
|
+
logger.warning("Stale cache used", extra={"template_id": result.template_id})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
No changes required to `render_html()`, `render_react()`, or `render_mjml()`.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## [1.0.0] - 2026-05-29
|
|
93
|
+
|
|
94
|
+
### Added
|
|
95
|
+
|
|
96
|
+
- Initial public release
|
|
97
|
+
- `MaildenoClient` — synchronous client with `render()`, `render_html()`,
|
|
98
|
+
`render_react()`, `render_mjml()`
|
|
99
|
+
- `AsyncMaildenoClient` — async mirror of the sync client, fully `await`-able
|
|
100
|
+
- Both clients support context-manager usage (`with` / `async with`)
|
|
101
|
+
- Both clients accept an injected `httpx.Client` / `httpx.AsyncClient`
|
|
102
|
+
- `MaildenoError` with `code`, `message`, `status`, and `issues`
|
|
103
|
+
- Structured `ValidationIssue` list on `err.issues` for 422 pydantic errors
|
|
104
|
+
- Full type hints, PEP 561 `py.typed` marker
|
|
105
|
+
- `DynamicData` and `MergeTagGroup` as `TypedDict`s
|
|
106
|
+
- Single runtime dependency: `httpx>=0.28.1,<1.0`
|
|
107
|
+
- Python 3.9 – 3.13 support
|
maildeno-2.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maildeno
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|