tidepool 0.1.1__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.
tidepool-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Tidepool
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.
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.4
2
+ Name: tidepool
3
+ Version: 0.1.1
4
+ Summary: Deploy full-featured web apps in minutes. Auth, payments, admin, email, database — one import.
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://tidepool.sh
7
+ Project-URL: Documentation, https://tidepool.sh/api
8
+ Keywords: paas,web,deploy,cli,ai
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Internet :: WWW/HTTP
13
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: requests>=2.28
18
+ Requires-Dist: argon2-cffi>=21.0
19
+ Requires-Dist: jinja2>=3.1
20
+ Requires-Dist: itsdangerous>=2.0
21
+ Requires-Dist: markdown>=3.4
22
+ Dynamic: license-file
23
+
24
+ # Tidepool
25
+
26
+ ```
27
+ ┌─────────────────────────────────────────────┐
28
+ │ build locally → deploy in 30s → live │
29
+ │ ↑ ↓ │
30
+ │ iterate auth · payments · email │
31
+ │ ↑ db · files · http │
32
+ │ └────────── <you>.tidepool.sh ──┘ │
33
+ └─────────────────────────────────────────────┘
34
+ ```
35
+
36
+ Build and deploy full-featured web apps in minutes. Auth, payments, admin, email, file storage, database, background tasks — all from a single `tp` object.
37
+
38
+ Designed for AI agents and humans with CLI coding tools like Claude Code. The API is self-documenting (`curl https://tidepool.sh/api`), and every feature is available via the command line.
39
+
40
+ ## Quickstart
41
+
42
+ ### Install
43
+
44
+ ```bash
45
+ pip install tidepool
46
+ ```
47
+
48
+ ### Init a pod
49
+
50
+ ```bash
51
+ tidepool init my-blog
52
+ cd my-blog
53
+ ```
54
+
55
+ Creates a directory with a default `main.py`:
56
+ ```python
57
+ import tp
58
+ tp.page('/', '<h1>Hello from Tidepool!</h1>')
59
+ ```
60
+
61
+ ### Develop locally
62
+
63
+ ```bash
64
+ tidepool dev
65
+ # Pod runs at http://localhost:8000
66
+ ```
67
+
68
+ The dev server replicates production pod behavior: file I/O goes to `./tp_data/files/`, `tp.db` persists to a JSON file, `tp.state` is readable at `?format=json`. Stripe, R2, and email are optional — the app runs without them.
69
+
70
+ ### Deploy
71
+
72
+ ```bash
73
+ tidepool --url https://tidepool.sh register --email you@example.com
74
+ # verify email, then:
75
+ tidepool deploy
76
+ # Pod is live at https://my-blog.tidepool.sh in ~30 seconds
77
+ ```
78
+
79
+ `deploy` auto-discovers source files and uploads `tp_data/files/` (images, media) and `tp_data/secrets.json` automatically.
80
+
81
+ ### Push & Pull
82
+
83
+ Pull a live pod to develop locally — all state comes with it:
84
+
85
+ ```bash
86
+ tidepool pull abc123
87
+ # Creates my-blog/ with source files + tp_data/ (db, secrets, files)
88
+ cd my-blog
89
+ tidepool dev
90
+ # Edit code, add data, test locally...
91
+ tidepool push # pushes everything back (hash remembered from pull)
92
+ ```
93
+
94
+ `pull` downloads source files, `tp.db` → `tp_data/db.json`, `tp.secrets` → `tp_data/secrets.json`, and pod files → `tp_data/files/`. The dev server reads all of these natively — no conversion needed.
95
+
96
+ `push` auto-discovers source files (same as deploy) and uploads them along with `tp_data/db.json` (merge by default), `tp_data/secrets.json`, and all files in `tp_data/files/`. Pushing source files triggers a pod restart. Use `-y` to skip the confirmation prompt.
97
+
98
+ ```bash
99
+ tidepool pull abc123 --dir . # pull into current directory instead of a subdirectory
100
+ tidepool push abc123 # explicit hash (overrides remembered hash)
101
+ tidepool push --file main.py # push specific files instead of auto-discover
102
+ tidepool push --secret STRIPE_KEY=sk_xxx # override a secret
103
+ tidepool push --replace-db # replace all db keys instead of merging
104
+ tidepool push --sync # delete remote files not present locally
105
+ tidepool push -y # skip confirmation prompt
106
+ ```
107
+
108
+ ### .tpignore
109
+
110
+ Create a `.tpignore` file to exclude files from `deploy` and `push` (same syntax as `.gitignore`):
111
+
112
+ ```
113
+ # Directories (trailing slash)
114
+ data/
115
+ notebooks/
116
+
117
+ # File patterns
118
+ *.csv
119
+ *.npy
120
+ *.pkl
121
+ *.parquet
122
+ pipeline.py
123
+ build_log.*
124
+ ```
125
+
126
+ Always ignored regardless of `.tpignore`: `tp_data/`, `__pycache__/`, `.git/`, `venv/`, `.venv/`, `node_modules/`, dotfiles, and `build_log.*`. Files over 50MB are skipped automatically with a warning.
127
+
128
+ `tidepool init` generates a starter `.tpignore`. For existing projects, create one before your first `deploy` or `push` to avoid uploading data files, models, or build artifacts.
129
+
130
+ ### Eject Mode
131
+
132
+ For full control over the runtime, eject the internals into your project:
133
+
134
+ ```bash
135
+ tidepool eject
136
+ # Copies tp_runtime.py, tp_server.py, tp_backend.py, tp_templates/ into your project
137
+ ```
138
+
139
+ These files are now yours to modify. `tidepool dev`, `deploy`, and `push` auto-detect eject mode when `tp_server.py` exists in the project directory — no flags needed. To undo, delete the ejected files.
140
+
141
+ ## Runtime Tools
142
+
143
+ Use `import tp` at the top of every `.py` file. `main.py` runs once at startup to configure the pod — set auth, payments, seed data, register routes. The server dispatches requests directly to handlers.
144
+
145
+ | Name | Description | Usage |
146
+ | ---- | ----------- | ----- |
147
+ | `tp.route` | Register a request handler with path params | `@tp.route('/post/:slug', methods=['GET'])` |
148
+ | `tp.page` | Register a static HTML page (no handler) | `tp.page('/about', '<h1>About</h1>')` |
149
+ | `tp.auth` | Full auth system. Presets: `'paywall'` (pay-first + magic link) or `'standard'` (email/password) | `tp.auth = 'paywall'` or `tp.auth = 'standard'` |
150
+ | `tp.payments` | Stripe subscriptions and one-time purchases (in cents) | `tp.payments = {products: [{id: 'pro', price: 500, recurring: 'month'}]}` |
151
+ | `tp.admin` | Auto-generated admin panel at `/_admin/` | `tp.admin = {users: ['admin@example.com']}` |
152
+ | `tp.create_user` | Create user with hashed password (idempotent) | `tp.create_user('sam@x.com', 'pass', subscriptions={'pro': True})` |
153
+ | `tp.db` | Key-value store, 1GB limit, persisted across runs | `tp.db.set('post:slug', {...})` / `tp.db.get('post:slug')` |
154
+ | `tp.files` | File storage (R2 in prod, 50GB), served at `/_files/` | `tp.files.write('photo.jpg', data)` / `tp.files.read('photo.jpg')` |
155
+ | `tp.email` | Send email with optional HTML and attachments | `tp.email('user@x.com', 'Subject', 'body', html='<p>hi</p>')` |
156
+ | `tp.http` | HTTP client (same API as `requests`), 200 req/60s, SSRF-protected | `tp.http.post(url, json=payload, headers={...})` |
157
+ | `tp.markdown` | Convert markdown to HTML (tables, code, footnotes) | `html = tp.markdown('# Hello\n\nWorld')` |
158
+ | `tp.secrets` | Read-only dict of deploy-time credentials | `api_key = tp.secrets['STRIPE_KEY']` |
159
+ | `tp.state` | Public app state dict, readable at `?format=json` | `tp.state = {'status': 'live'}` |
160
+ | `tp.publish` | Update public JSON state (ETag-supported polling) | `tp.publish({'messages': msgs})` |
161
+ | `tp.background` | Background tasks (max 5). `seconds<=0`: once, `>0`: loop | `@tp.background(seconds=3600)` |
162
+
163
+ **Handler return values:** `str` → 200 HTML, `dict`/`list` → 200 JSON, `int` → status code, `tuple(body, status)` → body + status, `None` → 303 redirect, `generator` → SSE stream.
164
+
165
+ **Request object:** Handler receives `(req, **params)`. Attributes: `req.path`, `req.method`, `req.query`, `req.user` (dict or None), `req.body` (dict), `req.files` (dict of upload metadata — file data auto-saved to `tp.files`).
166
+
167
+ ### Auth details
168
+
169
+ **Auth presets:** `tp.auth = 'paywall'` for payment-first apps (no signup form, accounts auto-created at checkout, magic link for return logins — pair with `tp.payments`). `tp.auth = 'standard'` for traditional email/password signup with confirmation, reset, and magic link. Customize after setting a preset: `tp.auth['required'] = ['/dash/*']`, `tp.auth['theme'] = {'accent': '#e74c3c'}`. Or pass a full dict for manual control: `tp.auth = {required: ['/dash/*'], signup: True, reset: True, oauth: ['google']}`.
170
+
171
+ Email confirmation on by default — `signup_confirm: False` to disable. `req.user` in handlers gives the logged-in user including subscriptions, purchases, and `avatar_url` (from Google profile). Theme: `theme: {page: '<html>...{content}...</html>'}` wraps auth pages in your layout; `{content}` receives the form, `{title}` the page title. Simpler: `theme: {accent: '#color', css: '...'}`.
172
+
173
+ **Google OAuth setup:** Add `google_client_id` and `google_client_secret` to `tp_data/secrets.json`. Get credentials at [Google Cloud Console](https://console.cloud.google.com/apis/credentials) → Create OAuth 2.0 Client ID (Web application). Add authorized redirect URI: `http://localhost:8000/_auth/oauth/google/callback` for dev, `https://yourdomain.com/_auth/oauth/google/callback` for prod. That's it — the server handles the rest.
174
+
175
+ ### Payments details
176
+
177
+ Set `tp.payments = {products: [{id: 'pro', name: 'Pro', price: 500, recurring: 'month'}]}`. Users pay at `/_pay/pro`, manage subscriptions at `/_pay/portal`. `recurring: 'month'`/`'year'` for subscriptions; omit for one-time. Dev mode simulates purchases instantly. Prod requires `tidepool stripe-connect` (one-time setup).
178
+
179
+ ### Admin details
180
+
181
+ Set `tp.admin = {models: {post: {fields: {title: 'string', body: 'text', tier: 'choice:free,pro'}, display: ['title']}}}`. Field types: `string`, `text`, `bool`, `number`, `date`, `choice:a,b,c`. Plus read-only views of users, payments, emails, files. If not set, auto-inferred from `tp.db` key patterns. Admin access control: with `tp.auth` configured, set `users: ['admin@example.com']` to restrict to specific emails (otherwise any logged-in user can access). Without `tp.auth`, admin is open in dev and key-gated in prod (key printed in server logs at startup, access via `/_admin?key=<key>`).
182
+
183
+ ### Background tasks
184
+
185
+ ```python
186
+ @tp.background() # runs once at startup
187
+ def migrate(tp):
188
+ if not tp.db.get('_migrated_v2'):
189
+ for key, val in tp.db.prefix('post:'):
190
+ val['version'] = 2
191
+ tp.db.set(key, val)
192
+ tp.db.set('_migrated_v2', True)
193
+
194
+ @tp.background(seconds=3600) # every hour
195
+ def send_digest(tp):
196
+ for email, user in tp.users().items():
197
+ if user.get('subscriptions', {}).get('digest'):
198
+ posts = tp.db.prefix('post:', reverse=True, limit=5)
199
+ tp.email(email, 'Hourly Digest', '\n'.join(t for _, t in posts))
200
+ ```
201
+
202
+ ### Server-Sent Events (SSE)
203
+
204
+ Return a generator from any route handler to stream real-time events:
205
+
206
+ ```python
207
+ @tp.route('/feed/live')
208
+ def live_feed(req):
209
+ def stream():
210
+ last_count = 0
211
+ while True:
212
+ messages = tp.db.prefix('msg:', reverse=True, limit=20)
213
+ if len(messages) != last_count:
214
+ last_count = len(messages)
215
+ yield {'messages': [m for _, m in messages]}
216
+ time.sleep(2)
217
+ return stream()
218
+ ```
219
+
220
+ Client-side: `new EventSource('/feed/live')`. Max 100 concurrent SSE connections per pod.
221
+
222
+ ### Static files & templating
223
+
224
+ - **Static files** — Files in `static/` alongside `main.py` are served at `/static/<path>`.
225
+ - **Jinja2** — Pre-installed. `from jinja2 import Environment, FileSystemLoader; env = Environment(loader=FileSystemLoader('templates'), autoescape=True)`. Render: `env.get_template('page.html').render(posts=p)`.
226
+
227
+ ## Full API reference
228
+
229
+ ```bash
230
+ curl https://tidepool.sh/api
231
+ ```