somewhere-tech 0.2.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 somewhere.tech
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,150 @@
1
+ Metadata-Version: 2.4
2
+ Name: somewhere-tech
3
+ Version: 0.2.0
4
+ Summary: Official Python SDK for the somewhere.tech platform API. Supabase-compatible interface.
5
+ Author: somewhere.tech
6
+ License: MIT
7
+ Project-URL: Homepage, https://somewhere.tech
8
+ Project-URL: Repository, https://github.com/somewhere-tech/somewhere-sdk-python
9
+ Keywords: somewhere,somewhere.tech,backend,sdk,cloudflare,d1,r2,supabase
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Intended Audience :: Developers
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: httpx>=0.24
24
+ Dynamic: license-file
25
+
26
+ # somewhere — Python SDK
27
+
28
+ Official Python SDK for the [somewhere.tech](https://somewhere.tech) platform API. Thin wrapper around the REST API at `https://api.somewhere.tech/v1`.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pip install somewhere
34
+ ```
35
+
36
+ ## Quick start
37
+
38
+ ```python
39
+ from somewhere import Somewhere, SomewhereError
40
+
41
+ sw = Somewhere(key="smt_...") # or token="eyJ..." for app-user mode
42
+
43
+ # Create a project
44
+ project = sw.projects.create(name="Booking App")
45
+
46
+ # Run a migration
47
+ sw.db.migrate(
48
+ """
49
+ CREATE TABLE bookings (
50
+ id INTEGER PRIMARY KEY,
51
+ name TEXT NOT NULL,
52
+ slot TEXT NOT NULL,
53
+ created_at TEXT DEFAULT (datetime('now'))
54
+ );
55
+ """,
56
+ project["id"],
57
+ )
58
+
59
+ # Insert + query
60
+ sw.db.query(
61
+ "INSERT INTO bookings (name, slot) VALUES (?, ?)",
62
+ ["Alice", "2026-05-01 18:00"],
63
+ project["id"],
64
+ )
65
+ rows = sw.db.query("SELECT * FROM bookings", [], project["id"])
66
+
67
+ # Deploy a static site
68
+ sw.deploy(
69
+ files={"index.html": "<h1>Book a table</h1>"},
70
+ project_id=project["id"],
71
+ )
72
+
73
+ print(f"Live at https://{project['subdomain']}.somewhere.tech")
74
+ ```
75
+
76
+ ## Auth modes
77
+
78
+ **Developer mode** — full access, server-side only:
79
+
80
+ ```python
81
+ sw = Somewhere(key="smt_...")
82
+ ```
83
+
84
+ **App-user mode** — app JWT, browser-safe operations only (db, storage, fs, auth.me):
85
+
86
+ ```python
87
+ sw = Somewhere(token="eyJ...", project_id="booking-app")
88
+ me = sw.auth.me()
89
+ rows = sw.db.query("SELECT * FROM bookings WHERE email = ?", [me["user"]["email"]])
90
+ ```
91
+
92
+ **Default project**:
93
+
94
+ ```python
95
+ sw = Somewhere(key="smt_...", project_id="booking-app")
96
+ sw.db.query("SELECT * FROM bookings") # uses 'booking-app'
97
+ ```
98
+
99
+ ## Error handling
100
+
101
+ Every API error raises a typed `SomewhereError`:
102
+
103
+ ```python
104
+ from somewhere import SomewhereError
105
+
106
+ try:
107
+ sw.email.send(to="user@example.com", subject="Hi", text="Welcome")
108
+ except SomewhereError as err:
109
+ print(err.code) # "QUOTA_EXCEEDED"
110
+ print(err.message) # human-readable
111
+ print(err.status_code) # 429
112
+ print(err.retry) # True if client should retry
113
+ print(err.retry_after_ms) # ms to wait
114
+ ```
115
+
116
+ ## Method reference
117
+
118
+ | Namespace | Methods |
119
+ |---|---|
120
+ | `sw.projects` | `create`, `list`, `get`, `delete`, `undeploy`, `archive`, `unarchive`, `rename`, `deploys` |
121
+ | `sw.deploy` | `sw.deploy(files=..., functions=..., project_id=...)`, `sw.deploy.status(project_id)` |
122
+ | `sw.promote` | `sw.promote(project_id)`, `sw.promote.rollback(project_id)` |
123
+ | `sw.db` | `query`, `migrate`, `tables`, `schema` |
124
+ | `sw.storage` | `put`, `get`, `delete`, `list` |
125
+ | `sw.fs` | `write`, `read`, `delete`, `move`, `copy`, `stat`, `versions`, `restore` |
126
+ | `sw.auth` | `signup`, `login`, `logout`, `me`, `forgot`, `reset`, `users`, `verify_email`, `request_verification`, `delete_account`, `update_me` |
127
+ | `sw.email` | `send` |
128
+ | `sw.ai` | `complete`, `embed`*, `image`*, `tts`*, `transcribe`* |
129
+ | `sw.jobs` | `create`, `status`, `list`, `cancel`, `progress` |
130
+ | `sw.cron` | `create`, `list`, `update`, `delete` |
131
+ | `sw.queue` | `push` |
132
+ | `sw.logs` | `write`, `read` |
133
+ | `sw.env` | `set`, `list`, `delete` |
134
+ | `sw.domains` | `add`, `verify`, `list`, `delete` |
135
+ | `sw.preview` | `invite`, `revoke`, `viewers` |
136
+ | `sw.feedback` | `submit`, `list` |
137
+ | `sw.billing` | `status`, `checkout`, `portal` |
138
+ | `sw.usage` | `get`, `summary` |
139
+
140
+ `*` = stub for not-yet-shipped endpoints; calling returns `UNSUPPORTED_FEATURE`.
141
+
142
+ ## Test
143
+
144
+ ```bash
145
+ SMT_KEY=smt_... python tests/test_basic.py
146
+ ```
147
+
148
+ ## License
149
+
150
+ MIT © somewhere.tech
@@ -0,0 +1,125 @@
1
+ # somewhere — Python SDK
2
+
3
+ Official Python SDK for the [somewhere.tech](https://somewhere.tech) platform API. Thin wrapper around the REST API at `https://api.somewhere.tech/v1`.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install somewhere
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```python
14
+ from somewhere import Somewhere, SomewhereError
15
+
16
+ sw = Somewhere(key="smt_...") # or token="eyJ..." for app-user mode
17
+
18
+ # Create a project
19
+ project = sw.projects.create(name="Booking App")
20
+
21
+ # Run a migration
22
+ sw.db.migrate(
23
+ """
24
+ CREATE TABLE bookings (
25
+ id INTEGER PRIMARY KEY,
26
+ name TEXT NOT NULL,
27
+ slot TEXT NOT NULL,
28
+ created_at TEXT DEFAULT (datetime('now'))
29
+ );
30
+ """,
31
+ project["id"],
32
+ )
33
+
34
+ # Insert + query
35
+ sw.db.query(
36
+ "INSERT INTO bookings (name, slot) VALUES (?, ?)",
37
+ ["Alice", "2026-05-01 18:00"],
38
+ project["id"],
39
+ )
40
+ rows = sw.db.query("SELECT * FROM bookings", [], project["id"])
41
+
42
+ # Deploy a static site
43
+ sw.deploy(
44
+ files={"index.html": "<h1>Book a table</h1>"},
45
+ project_id=project["id"],
46
+ )
47
+
48
+ print(f"Live at https://{project['subdomain']}.somewhere.tech")
49
+ ```
50
+
51
+ ## Auth modes
52
+
53
+ **Developer mode** — full access, server-side only:
54
+
55
+ ```python
56
+ sw = Somewhere(key="smt_...")
57
+ ```
58
+
59
+ **App-user mode** — app JWT, browser-safe operations only (db, storage, fs, auth.me):
60
+
61
+ ```python
62
+ sw = Somewhere(token="eyJ...", project_id="booking-app")
63
+ me = sw.auth.me()
64
+ rows = sw.db.query("SELECT * FROM bookings WHERE email = ?", [me["user"]["email"]])
65
+ ```
66
+
67
+ **Default project**:
68
+
69
+ ```python
70
+ sw = Somewhere(key="smt_...", project_id="booking-app")
71
+ sw.db.query("SELECT * FROM bookings") # uses 'booking-app'
72
+ ```
73
+
74
+ ## Error handling
75
+
76
+ Every API error raises a typed `SomewhereError`:
77
+
78
+ ```python
79
+ from somewhere import SomewhereError
80
+
81
+ try:
82
+ sw.email.send(to="user@example.com", subject="Hi", text="Welcome")
83
+ except SomewhereError as err:
84
+ print(err.code) # "QUOTA_EXCEEDED"
85
+ print(err.message) # human-readable
86
+ print(err.status_code) # 429
87
+ print(err.retry) # True if client should retry
88
+ print(err.retry_after_ms) # ms to wait
89
+ ```
90
+
91
+ ## Method reference
92
+
93
+ | Namespace | Methods |
94
+ |---|---|
95
+ | `sw.projects` | `create`, `list`, `get`, `delete`, `undeploy`, `archive`, `unarchive`, `rename`, `deploys` |
96
+ | `sw.deploy` | `sw.deploy(files=..., functions=..., project_id=...)`, `sw.deploy.status(project_id)` |
97
+ | `sw.promote` | `sw.promote(project_id)`, `sw.promote.rollback(project_id)` |
98
+ | `sw.db` | `query`, `migrate`, `tables`, `schema` |
99
+ | `sw.storage` | `put`, `get`, `delete`, `list` |
100
+ | `sw.fs` | `write`, `read`, `delete`, `move`, `copy`, `stat`, `versions`, `restore` |
101
+ | `sw.auth` | `signup`, `login`, `logout`, `me`, `forgot`, `reset`, `users`, `verify_email`, `request_verification`, `delete_account`, `update_me` |
102
+ | `sw.email` | `send` |
103
+ | `sw.ai` | `complete`, `embed`*, `image`*, `tts`*, `transcribe`* |
104
+ | `sw.jobs` | `create`, `status`, `list`, `cancel`, `progress` |
105
+ | `sw.cron` | `create`, `list`, `update`, `delete` |
106
+ | `sw.queue` | `push` |
107
+ | `sw.logs` | `write`, `read` |
108
+ | `sw.env` | `set`, `list`, `delete` |
109
+ | `sw.domains` | `add`, `verify`, `list`, `delete` |
110
+ | `sw.preview` | `invite`, `revoke`, `viewers` |
111
+ | `sw.feedback` | `submit`, `list` |
112
+ | `sw.billing` | `status`, `checkout`, `portal` |
113
+ | `sw.usage` | `get`, `summary` |
114
+
115
+ `*` = stub for not-yet-shipped endpoints; calling returns `UNSUPPORTED_FEATURE`.
116
+
117
+ ## Test
118
+
119
+ ```bash
120
+ SMT_KEY=smt_... python tests/test_basic.py
121
+ ```
122
+
123
+ ## License
124
+
125
+ MIT © somewhere.tech
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "somewhere-tech"
7
+ version = "0.2.0"
8
+ description = "Official Python SDK for the somewhere.tech platform API. Supabase-compatible interface."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [{ name = "somewhere.tech" }]
12
+ keywords = ["somewhere", "somewhere.tech", "backend", "sdk", "cloudflare", "d1", "r2", "supabase"]
13
+ requires-python = ">=3.8"
14
+ classifiers = [
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Programming Language :: Python :: 3.8",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Intended Audience :: Developers",
25
+ ]
26
+ dependencies = [
27
+ "httpx>=0.24",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://somewhere.tech"
32
+ Repository = "https://github.com/somewhere-tech/somewhere-sdk-python"
33
+
34
+ [tool.setuptools.packages.find]
35
+ include = ["somewhere*"]
36
+ exclude = ["tests*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,168 @@
1
+ """Official Python SDK for the somewhere.tech platform API.
2
+
3
+ Supabase-compatible interface with query builder, auth, storage, email,
4
+ and OpenAI-compatible chat completions.
5
+
6
+ Usage:
7
+ from somewhere import Somewhere
8
+
9
+ sw = Somewhere(key='smt_...', project_id='my-project')
10
+
11
+ # Database (Supabase-style query builder)
12
+ result = sw.from_('users').select('*').eq('role', 'admin').execute()
13
+
14
+ # Auth
15
+ result = sw.auth.sign_up(email='u@test.com', password='pass123')
16
+
17
+ # Storage
18
+ result = sw.storage.from_('avatars').upload('photo.png', file_bytes)
19
+
20
+ # Email
21
+ result = sw.emails.send(to='user@test.com', subject='Hi', html='<h1>Hello</h1>')
22
+
23
+ # AI (OpenAI-compatible)
24
+ completion = sw.chat.completions.create(model='claude-sonnet-4-6', messages=[...])
25
+ """
26
+ from __future__ import annotations
27
+
28
+ from typing import Dict, Optional
29
+
30
+ import httpx
31
+
32
+ from .auth import AuthClient
33
+ from .chat import ChatClient
34
+ from .client import (
35
+ BillingClient,
36
+ CronClient,
37
+ DeployClient,
38
+ DomainsClient,
39
+ EnvClient,
40
+ FeedbackClient,
41
+ HttpClient,
42
+ JobsClient,
43
+ LogsClient,
44
+ PreviewClient,
45
+ ProjectsClient,
46
+ QueueClient,
47
+ UsageClient,
48
+ )
49
+ from .emails import EmailsClient
50
+ from .errors import SomewhereError
51
+ from .query_builder import QueryBuilder, Result
52
+ from .storage import StorageClient
53
+
54
+ __all__ = ["Somewhere", "SomewhereError", "Result"]
55
+ __version__ = "0.2.0"
56
+
57
+
58
+ class Somewhere:
59
+ """Entry point for the somewhere.tech SDK.
60
+
61
+ Provides a Supabase-compatible interface with query builder, auth,
62
+ storage, email, and OpenAI-compatible chat completions.
63
+
64
+ Args:
65
+ key: Developer API key (smt_...). Mutually exclusive with token.
66
+ token: App-user JWT. Mutually exclusive with key.
67
+ project_id: Default project ID for all operations.
68
+ base_url: API base URL (defaults to https://api.somewhere.tech/v1).
69
+ http_client: Custom httpx.Client instance (optional).
70
+ headers: Extra headers to send with every request (optional).
71
+ timeout: Request timeout in seconds (default 60).
72
+
73
+ Example:
74
+ sw = Somewhere(key='smt_...', project_id='booking-app')
75
+ result = sw.from_('users').select('*').eq('role', 'admin').execute()
76
+ print(result.data)
77
+ """
78
+
79
+ def __init__(
80
+ self,
81
+ *,
82
+ key: Optional[str] = None,
83
+ token: Optional[str] = None,
84
+ project_id: Optional[str] = None,
85
+ base_url: str = "https://api.somewhere.tech/v1",
86
+ http_client: Optional[httpx.Client] = None,
87
+ headers: Optional[Dict[str, str]] = None,
88
+ timeout: float = 60.0,
89
+ ) -> None:
90
+ self._http = HttpClient(
91
+ key=key,
92
+ token=token,
93
+ project_id=project_id,
94
+ base_url=base_url,
95
+ http_client=http_client,
96
+ headers=headers,
97
+ timeout=timeout,
98
+ )
99
+
100
+ # Supabase-compatible clients
101
+ self.auth = AuthClient(
102
+ project_id=self._http.project_id,
103
+ http_client=self._http.client,
104
+ base_url=self._http.base_url,
105
+ auth_header=self._http.auth_header,
106
+ extra_headers=self._http.extra_headers,
107
+ )
108
+ self.storage = StorageClient(
109
+ project_id=self._http.project_id,
110
+ http_client=self._http.client,
111
+ base_url=self._http.base_url,
112
+ auth_header=self._http.auth_header,
113
+ extra_headers=self._http.extra_headers,
114
+ )
115
+ self.emails = EmailsClient(
116
+ project_id=self._http.project_id,
117
+ http_client=self._http.client,
118
+ base_url=self._http.base_url,
119
+ auth_header=self._http.auth_header,
120
+ extra_headers=self._http.extra_headers,
121
+ )
122
+ self.chat = ChatClient(
123
+ project_id=self._http.project_id,
124
+ http_client=self._http.client,
125
+ base_url=self._http.base_url,
126
+ auth_header=self._http.auth_header,
127
+ extra_headers=self._http.extra_headers,
128
+ )
129
+
130
+ # Simple resource clients
131
+ self.env = EnvClient(self._http)
132
+ self.jobs = JobsClient(self._http)
133
+ self.cron = CronClient(self._http)
134
+ self.domains = DomainsClient(self._http)
135
+ self.projects = ProjectsClient(self._http)
136
+ self.deploy = DeployClient(self._http)
137
+ self.logs = LogsClient(self._http)
138
+ self.queue = QueueClient(self._http)
139
+ self.billing = BillingClient(self._http)
140
+ self.usage = UsageClient(self._http)
141
+ self.feedback = FeedbackClient(self._http)
142
+ self.preview = PreviewClient(self._http)
143
+
144
+ def from_(self, table: str) -> QueryBuilder:
145
+ """Start a Supabase-style query builder for the given table.
146
+
147
+ Uses `from_` because `from` is a reserved word in Python.
148
+
149
+ Args:
150
+ table: The database table name.
151
+
152
+ Returns:
153
+ A QueryBuilder that supports .select(), .insert(), .update(),
154
+ .delete(), .upsert(), plus filter chains (.eq(), .gt(), etc.)
155
+ ending with .execute().
156
+
157
+ Example:
158
+ result = sw.from_('users').select('*').eq('role', 'admin').execute()
159
+ result = sw.from_('users').insert({'name': 'A'}).execute()
160
+ """
161
+ return QueryBuilder(
162
+ table=table,
163
+ project_id=self._http.project_id,
164
+ http_client=self._http.client,
165
+ base_url=self._http.base_url,
166
+ auth_header=self._http.auth_header,
167
+ extra_headers=self._http.extra_headers,
168
+ )