devlinker 1.3.6__tar.gz → 1.3.8__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.
- devlinker-1.3.8/MANIFEST.in +3 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/PKG-INFO +1 -1
- devlinker-1.3.8/devlinker/devlinker_loader_instant.html +109 -0
- devlinker-1.3.8/devlinker/devlinker_loader_snippet.html +168 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/proxy.py +51 -9
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker.egg-info/PKG-INFO +1 -1
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker.egg-info/SOURCES.txt +3 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/pyproject.toml +1 -1
- devlinker-1.3.8/setup.py +9 -0
- devlinker-1.3.6/setup.py +0 -3
- {devlinker-1.3.6 → devlinker-1.3.8}/LICENSE +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/README.md +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/__init__.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/config.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/detection_state.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/detector.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/detector_ai.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/doctor.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/fix.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/fixer.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/global_state.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/inspect.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/logger.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/main.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/monitor.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/runner.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/share.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker/tunnel.py +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker.egg-info/dependency_links.txt +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker.egg-info/entry_points.txt +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker.egg-info/requires.txt +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/devlinker.egg-info/top_level.txt +0 -0
- {devlinker-1.3.6 → devlinker-1.3.8}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devlinker
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.8
|
|
4
4
|
Summary: A lightweight proxy that combines your frontend and backend into one link for easy development and sharing.
|
|
5
5
|
Author-email: Mani <mani1028@users.noreply.github.com>
|
|
6
6
|
Requires-Python: >=3.7
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<!-- DevLinker Instant Loader for Local/WLAN Users -->
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>DevLinker Loading</title>
|
|
8
|
+
<style>
|
|
9
|
+
* { box-sizing: border-box; }
|
|
10
|
+
body {
|
|
11
|
+
margin: 0;
|
|
12
|
+
min-height: 100vh;
|
|
13
|
+
background: #0b1220;
|
|
14
|
+
font-family: "Segoe UI", Arial, sans-serif;
|
|
15
|
+
color: #e2e8f0;
|
|
16
|
+
}
|
|
17
|
+
#devlinker-loader-min {
|
|
18
|
+
position: fixed;
|
|
19
|
+
inset: 0;
|
|
20
|
+
z-index: 99999;
|
|
21
|
+
display: grid;
|
|
22
|
+
place-items: center;
|
|
23
|
+
padding: 24px;
|
|
24
|
+
}
|
|
25
|
+
.devlinker-center {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: 10px;
|
|
30
|
+
}
|
|
31
|
+
.devlinker-logo {
|
|
32
|
+
font-size: 1.15rem;
|
|
33
|
+
font-weight: 700;
|
|
34
|
+
letter-spacing: 0.01em;
|
|
35
|
+
margin-bottom: 4px;
|
|
36
|
+
color: #f8fafc;
|
|
37
|
+
}
|
|
38
|
+
.devlinker-spinner {
|
|
39
|
+
width: 40px;
|
|
40
|
+
height: 40px;
|
|
41
|
+
border: 4px solid rgba(148, 163, 184, 0.25);
|
|
42
|
+
border-top: 4px solid #22d3ee;
|
|
43
|
+
border-radius: 50%;
|
|
44
|
+
animation: devlinker-spin 1s linear infinite;
|
|
45
|
+
}
|
|
46
|
+
.devlinker-text {
|
|
47
|
+
font-size: 1rem;
|
|
48
|
+
color: #cbd5e1;
|
|
49
|
+
font-weight: 600;
|
|
50
|
+
}
|
|
51
|
+
.devlinker-health {
|
|
52
|
+
font-size: 0.84rem;
|
|
53
|
+
color: #94a3b8;
|
|
54
|
+
opacity: 0.9;
|
|
55
|
+
}
|
|
56
|
+
.devlinker-powered {
|
|
57
|
+
position: fixed;
|
|
58
|
+
bottom: 18px;
|
|
59
|
+
left: 0;
|
|
60
|
+
width: 100vw;
|
|
61
|
+
text-align: center;
|
|
62
|
+
color: #94a3b8;
|
|
63
|
+
font-size: 0.92rem;
|
|
64
|
+
font-weight: 500;
|
|
65
|
+
opacity: 0.9;
|
|
66
|
+
pointer-events: none;
|
|
67
|
+
user-select: none;
|
|
68
|
+
}
|
|
69
|
+
@keyframes devlinker-spin {
|
|
70
|
+
to { transform: rotate(360deg); }
|
|
71
|
+
}
|
|
72
|
+
</style>
|
|
73
|
+
</head>
|
|
74
|
+
<body>
|
|
75
|
+
<div id="devlinker-loader-min">
|
|
76
|
+
<div class="devlinker-center">
|
|
77
|
+
<div class="devlinker-logo">DevLinker</div>
|
|
78
|
+
<div class="devlinker-spinner"></div>
|
|
79
|
+
<div class="devlinker-text">Loading your app...</div>
|
|
80
|
+
<div class="devlinker-health">Frontend connected • Backend connected</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="devlinker-powered">Powered by DevLinker</div>
|
|
84
|
+
<script>
|
|
85
|
+
// Fetch the real page and replace the loader with a short minimum display for smoothness.
|
|
86
|
+
(async function() {
|
|
87
|
+
const minTime = 500;
|
|
88
|
+
const start = Date.now();
|
|
89
|
+
try {
|
|
90
|
+
const resp = await fetch(window.location.href, { headers: { "X-DevLinker-Instant": "1" } });
|
|
91
|
+
const html = await resp.text();
|
|
92
|
+
const elapsed = Date.now() - start;
|
|
93
|
+
const delay = Math.max(0, minTime - elapsed);
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
document.open();
|
|
96
|
+
document.write(html);
|
|
97
|
+
document.close();
|
|
98
|
+
}, delay);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
// Show error if fetch fails
|
|
101
|
+
var loader = document.getElementById("devlinker-loader-min");
|
|
102
|
+
if (loader) {
|
|
103
|
+
loader.innerHTML += '<div style="color:#b91c1c;font-size:1.05rem;margin-top:1.5rem;">Failed to load app. Please check your connection.</div>';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
})();
|
|
107
|
+
</script>
|
|
108
|
+
</body>
|
|
109
|
+
</html>
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<!-- DevLinker Loader Overlay -->
|
|
2
|
+
<style>
|
|
3
|
+
#devlinker-loader {
|
|
4
|
+
position: fixed;
|
|
5
|
+
z-index: 99999;
|
|
6
|
+
inset: 0;
|
|
7
|
+
display: grid;
|
|
8
|
+
place-items: center;
|
|
9
|
+
min-height: 100vh;
|
|
10
|
+
background:
|
|
11
|
+
radial-gradient(700px 360px at 18% 12%, rgba(34, 211, 238, 0.18), transparent 60%),
|
|
12
|
+
radial-gradient(520px 280px at 82% 18%, rgba(59, 130, 246, 0.16), transparent 58%),
|
|
13
|
+
#0f172a;
|
|
14
|
+
font-family: "Segoe UI", Arial, sans-serif;
|
|
15
|
+
transition: opacity 0.4s cubic-bezier(.4,0,.2,1);
|
|
16
|
+
color: #e2e8f0;
|
|
17
|
+
}
|
|
18
|
+
.devlinker-panel {
|
|
19
|
+
width: min(640px, 92vw);
|
|
20
|
+
border: 1px solid rgba(148, 163, 184, 0.25);
|
|
21
|
+
border-radius: 18px;
|
|
22
|
+
background: rgba(15, 23, 42, 0.75);
|
|
23
|
+
box-shadow: 0 24px 70px rgba(2, 6, 23, 0.55);
|
|
24
|
+
backdrop-filter: blur(8px);
|
|
25
|
+
padding: 24px 22px;
|
|
26
|
+
}
|
|
27
|
+
.devlinker-top {
|
|
28
|
+
font-weight: 700;
|
|
29
|
+
font-size: 1.2rem;
|
|
30
|
+
color: #f8fafc;
|
|
31
|
+
margin-bottom: 12px;
|
|
32
|
+
}
|
|
33
|
+
.devlinker-title {
|
|
34
|
+
font-size: 1.15rem;
|
|
35
|
+
font-weight: 650;
|
|
36
|
+
margin-bottom: 8px;
|
|
37
|
+
color: #f8fafc;
|
|
38
|
+
}
|
|
39
|
+
.devlinker-stage {
|
|
40
|
+
min-height: 1.45rem;
|
|
41
|
+
font-size: 0.98rem;
|
|
42
|
+
color: #a5b4fc;
|
|
43
|
+
margin-bottom: 14px;
|
|
44
|
+
}
|
|
45
|
+
.devlinker-center {
|
|
46
|
+
display: flex;
|
|
47
|
+
flex-direction: column;
|
|
48
|
+
align-items: center;
|
|
49
|
+
gap: 10px;
|
|
50
|
+
padding: 14px 0 16px;
|
|
51
|
+
}
|
|
52
|
+
.devlinker-spinner {
|
|
53
|
+
width: 52px;
|
|
54
|
+
height: 52px;
|
|
55
|
+
border: 4px solid rgba(148, 163, 184, 0.25);
|
|
56
|
+
border-top: 4px solid #22d3ee;
|
|
57
|
+
border-right: 4px solid #60a5fa;
|
|
58
|
+
border-radius: 50%;
|
|
59
|
+
animation: devlinker-spin 1s linear infinite;
|
|
60
|
+
}
|
|
61
|
+
.devlinker-text {
|
|
62
|
+
font-size: 1rem;
|
|
63
|
+
color: #cbd5e1;
|
|
64
|
+
font-weight: 600;
|
|
65
|
+
}
|
|
66
|
+
.devlinker-divider {
|
|
67
|
+
height: 1px;
|
|
68
|
+
width: 100%;
|
|
69
|
+
background: rgba(148, 163, 184, 0.22);
|
|
70
|
+
margin: 8px 0 14px;
|
|
71
|
+
}
|
|
72
|
+
.devlinker-info {
|
|
73
|
+
display: grid;
|
|
74
|
+
grid-template-columns: 1fr;
|
|
75
|
+
gap: 10px;
|
|
76
|
+
color: #cbd5e1;
|
|
77
|
+
font-size: 0.92rem;
|
|
78
|
+
}
|
|
79
|
+
.devlinker-tip {
|
|
80
|
+
color: #cbd5e1;
|
|
81
|
+
line-height: 1.4;
|
|
82
|
+
}
|
|
83
|
+
.devlinker-upgrade {
|
|
84
|
+
color: #93c5fd;
|
|
85
|
+
text-decoration: none;
|
|
86
|
+
font-weight: 600;
|
|
87
|
+
display: inline-block;
|
|
88
|
+
margin-top: 2px;
|
|
89
|
+
}
|
|
90
|
+
.devlinker-upgrade:hover {
|
|
91
|
+
color: #bfdbfe;
|
|
92
|
+
}
|
|
93
|
+
.devlinker-powered {
|
|
94
|
+
margin-top: 14px;
|
|
95
|
+
text-align: center;
|
|
96
|
+
color: #94a3b8;
|
|
97
|
+
font-size: 0.9rem;
|
|
98
|
+
font-weight: 500;
|
|
99
|
+
opacity: 0.95;
|
|
100
|
+
}
|
|
101
|
+
@keyframes devlinker-spin {
|
|
102
|
+
to { transform: rotate(360deg); }
|
|
103
|
+
}
|
|
104
|
+
@media (max-width: 640px) {
|
|
105
|
+
.devlinker-panel {
|
|
106
|
+
padding: 18px 16px;
|
|
107
|
+
}
|
|
108
|
+
.devlinker-title {
|
|
109
|
+
font-size: 1.04rem;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
</style>
|
|
113
|
+
<div id="devlinker-loader">
|
|
114
|
+
<div class="devlinker-panel">
|
|
115
|
+
<div class="devlinker-top">DevLinker</div>
|
|
116
|
+
<div class="devlinker-title">Connecting your app...</div>
|
|
117
|
+
<div class="devlinker-stage" id="devlinker-stage">Establishing secure tunnel...</div>
|
|
118
|
+
<div class="devlinker-center">
|
|
119
|
+
<div class="devlinker-spinner"></div>
|
|
120
|
+
<div class="devlinker-text">Preparing full experience</div>
|
|
121
|
+
</div>
|
|
122
|
+
<div class="devlinker-divider"></div>
|
|
123
|
+
<div class="devlinker-info">
|
|
124
|
+
<div class="devlinker-tip">Tip: Share your app instantly with one link. No CORS issues, no port confusion.</div>
|
|
125
|
+
<div class="devlinker-tip">Go Pro: custom domains, faster sharing, and no branding.</div>
|
|
126
|
+
<a class="devlinker-upgrade" href="https://devlinker.app" target="_blank" rel="noopener">Explore Pro at devlinker.app</a>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="devlinker-powered">Powered by DevLinker</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<script>
|
|
132
|
+
const stageMessages = [
|
|
133
|
+
"Establishing secure tunnel...",
|
|
134
|
+
"Routing frontend assets...",
|
|
135
|
+
"Connecting backend services...",
|
|
136
|
+
"Optimizing connection...",
|
|
137
|
+
"Almost ready..."
|
|
138
|
+
];
|
|
139
|
+
let stageIndex = 0;
|
|
140
|
+
const stageElement = document.getElementById("devlinker-stage");
|
|
141
|
+
const stageTimer = setInterval(() => {
|
|
142
|
+
if (!stageElement) return;
|
|
143
|
+
stageIndex = (stageIndex + 1) % stageMessages.length;
|
|
144
|
+
stageElement.textContent = stageMessages[stageIndex];
|
|
145
|
+
}, 1000);
|
|
146
|
+
|
|
147
|
+
const start = Date.now();
|
|
148
|
+
const MIN_TIME = 800;
|
|
149
|
+
const MAX_TIME = 5000;
|
|
150
|
+
let hidden = false;
|
|
151
|
+
|
|
152
|
+
function hideLoader() {
|
|
153
|
+
if (hidden) return;
|
|
154
|
+
hidden = true;
|
|
155
|
+
clearInterval(stageTimer);
|
|
156
|
+
const elapsed = Date.now() - start;
|
|
157
|
+
const delay = Math.max(0, MIN_TIME - elapsed);
|
|
158
|
+
const el = document.getElementById("devlinker-loader");
|
|
159
|
+
setTimeout(() => {
|
|
160
|
+
if (el) {
|
|
161
|
+
el.style.opacity = "0";
|
|
162
|
+
setTimeout(() => el.remove(), 400);
|
|
163
|
+
}
|
|
164
|
+
}, delay);
|
|
165
|
+
}
|
|
166
|
+
window.addEventListener("load", hideLoader);
|
|
167
|
+
setTimeout(hideLoader, MAX_TIME);
|
|
168
|
+
</script>
|
|
@@ -142,12 +142,17 @@ def _build_target_ws_url(port: int, path: str, query: str) -> str:
|
|
|
142
142
|
|
|
143
143
|
|
|
144
144
|
async def _forward_http(request: Request) -> Response:
|
|
145
|
-
# Serve instant loader for
|
|
145
|
+
# Serve instant loader only for localhost HTML document navigations.
|
|
146
146
|
client_ip = request.client.host if request.client else None
|
|
147
|
-
|
|
147
|
+
host_header = request.headers.get("host", "").lower()
|
|
148
|
+
|
|
149
|
+
def is_localhost_ip(ip):
|
|
148
150
|
if not ip:
|
|
149
151
|
return False
|
|
150
|
-
|
|
152
|
+
return ip.startswith("127.") or ip == "localhost" or ip == "::1"
|
|
153
|
+
|
|
154
|
+
def is_lan_ip(ip):
|
|
155
|
+
if not ip:
|
|
151
156
|
return False
|
|
152
157
|
if ip.startswith("192.168.") or ip.startswith("10."):
|
|
153
158
|
return True
|
|
@@ -158,9 +163,46 @@ async def _forward_http(request: Request) -> Response:
|
|
|
158
163
|
except Exception:
|
|
159
164
|
return False
|
|
160
165
|
return False
|
|
161
|
-
|
|
166
|
+
|
|
167
|
+
def classify_mode(host: str, ip: str | None) -> str:
|
|
168
|
+
host_only = host.split(":", 1)[0] if host else ""
|
|
169
|
+
if host_only in ("localhost", "127.0.0.1", "::1"):
|
|
170
|
+
return "localhost"
|
|
171
|
+
if host_only.startswith("192.168.") or host_only.startswith("10."):
|
|
172
|
+
return "lan"
|
|
173
|
+
if host_only.startswith("172."):
|
|
174
|
+
try:
|
|
175
|
+
second = int(host_only.split(".")[1])
|
|
176
|
+
if 16 <= second <= 31:
|
|
177
|
+
return "lan"
|
|
178
|
+
except Exception:
|
|
179
|
+
pass
|
|
180
|
+
if host_only and host_only not in ("", "0.0.0.0"):
|
|
181
|
+
return "public"
|
|
182
|
+
if is_localhost_ip(ip):
|
|
183
|
+
return "localhost"
|
|
184
|
+
if is_lan_ip(ip):
|
|
185
|
+
return "lan"
|
|
186
|
+
return "public" if ip else "unknown"
|
|
187
|
+
|
|
188
|
+
mode = classify_mode(host_header, client_ip)
|
|
189
|
+
is_localhost = mode == "localhost"
|
|
190
|
+
is_lan = mode == "lan"
|
|
191
|
+
is_public = mode == "public"
|
|
162
192
|
is_instant = request.headers.get("x-devlinker-instant") == "1"
|
|
163
|
-
|
|
193
|
+
accept_header = request.headers.get("accept", "")
|
|
194
|
+
sec_fetch_dest = request.headers.get("sec-fetch-dest", "")
|
|
195
|
+
is_html_request = "text/html" in accept_header.lower()
|
|
196
|
+
is_document_navigation = sec_fetch_dest in ("", "document")
|
|
197
|
+
is_api_path = request.url.path == "/api" or request.url.path.startswith("/api/")
|
|
198
|
+
if (
|
|
199
|
+
is_localhost
|
|
200
|
+
and not is_instant
|
|
201
|
+
and request.method == "GET"
|
|
202
|
+
and is_html_request
|
|
203
|
+
and is_document_navigation
|
|
204
|
+
and not is_api_path
|
|
205
|
+
):
|
|
164
206
|
import os
|
|
165
207
|
loader_path = os.path.join(os.path.dirname(__file__), "devlinker_loader_instant.html")
|
|
166
208
|
with open(loader_path, encoding="utf-8") as f:
|
|
@@ -231,19 +273,19 @@ async def _forward_http(request: Request) -> Response:
|
|
|
231
273
|
for s in ai_suggestions:
|
|
232
274
|
print_fix(s)
|
|
233
275
|
|
|
234
|
-
#
|
|
276
|
+
# Inject loader overlay only for LAN/WiFi/public HTML responses.
|
|
235
277
|
headers = _filter_response_headers(dict(upstream.headers))
|
|
236
278
|
content_type = headers.get("content-type", "")
|
|
237
279
|
is_html = "text/html" in content_type
|
|
238
|
-
is_public = client_ip and not is_local and (not client_ip.startswith("127.") and client_ip != "localhost" and client_ip != "::1")
|
|
239
280
|
content = upstream.content
|
|
240
|
-
if
|
|
281
|
+
# Only inject loader if NOT an instant loader background fetch
|
|
282
|
+
if is_html and (is_lan or is_public) and not is_instant:
|
|
241
283
|
try:
|
|
242
284
|
html = content.decode(upstream.encoding or "utf-8", errors="replace")
|
|
243
285
|
# Only inject if </body> exists
|
|
244
286
|
if "</body>" in html:
|
|
245
287
|
import os
|
|
246
|
-
loader_file = "devlinker_loader_snippet.html"
|
|
288
|
+
loader_file = "devlinker_loader_snippet.html"
|
|
247
289
|
with open(os.path.join(os.path.dirname(__file__), loader_file), encoding="utf-8") as f:
|
|
248
290
|
loader = f.read()
|
|
249
291
|
html = html.replace("</body>", loader + "</body>")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devlinker
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.8
|
|
4
4
|
Summary: A lightweight proxy that combines your frontend and backend into one link for easy development and sharing.
|
|
5
5
|
Author-email: Mani <mani1028@users.noreply.github.com>
|
|
6
6
|
Requires-Python: >=3.7
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
2
3
|
README.md
|
|
3
4
|
pyproject.toml
|
|
4
5
|
setup.py
|
|
@@ -7,6 +8,8 @@ devlinker/config.py
|
|
|
7
8
|
devlinker/detection_state.py
|
|
8
9
|
devlinker/detector.py
|
|
9
10
|
devlinker/detector_ai.py
|
|
11
|
+
devlinker/devlinker_loader_instant.html
|
|
12
|
+
devlinker/devlinker_loader_snippet.html
|
|
10
13
|
devlinker/doctor.py
|
|
11
14
|
devlinker/fix.py
|
|
12
15
|
devlinker/fixer.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devlinker"
|
|
7
|
-
version = "1.3.
|
|
7
|
+
version = "1.3.8"
|
|
8
8
|
description = "A lightweight proxy that combines your frontend and backend into one link for easy development and sharing."
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Mani", email = "mani1028@users.noreply.github.com" }
|
devlinker-1.3.8/setup.py
ADDED
devlinker-1.3.6/setup.py
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|