wepost 1.0.0

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,299 @@
1
+ (function(r,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(r=typeof globalThis<"u"?globalThis:r||self,d(r.WePost={}))})(this,function(r){"use strict";var S=Object.defineProperty;var T=(r,d,m)=>d in r?S(r,d,{enumerable:!0,configurable:!0,writable:!0,value:m}):r[d]=m;var g=(r,d,m)=>(T(r,typeof d!="symbol"?d+"":d,m),m);async function d(o){let t=o.url,e={...o.headers};o.proxyUrl&&(e["x-wepost-target"]=t,t=o.proxyUrl);const a={method:o.method,headers:e};!["GET","HEAD"].includes(o.method)&&o.body&&(a.body=JSON.stringify(o.body),e["Content-Type"]||(e["Content-Type"]="application/json"));const i=await fetch(t,a),l=await i.text();return{status:i.status,statusText:i.statusText,data:l,headers:Object.fromEntries(i.headers.entries())}}const m="wepost_history";function B(o){const t=E();t.length>0&&t[0].url===o.url&&t[0].method===o.method?t[0]=o:t.unshift(o),t.length>10&&t.pop(),localStorage.setItem(m,JSON.stringify(t))}function E(){try{const o=localStorage.getItem(m);return o?JSON.parse(o):[]}catch{return[]}}function L(){return`
2
+ :host {
3
+ /* Default: Dark Theme */
4
+ --bg: #1e1e1e;
5
+ --bg-light: #2d2d2d;
6
+ --border: #3c3c3c;
7
+ --text: #d4d4d4;
8
+ --text-muted: #858585;
9
+ --accent: #007acc;
10
+ --accent-hover: #005f9e;
11
+ --success: #89d185;
12
+ --error: #f48771;
13
+ --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
14
+ }
15
+
16
+ /* Light Theme Overrides */
17
+ .light-theme {
18
+ --bg: #ffffff;
19
+ --bg-light: #f3f3f3;
20
+ --border: #e0e0e0;
21
+ --text: #333333;
22
+ --text-muted: #666666;
23
+ --accent: #007acc;
24
+ --accent-hover: #005f9e;
25
+ --success: #2ea44f;
26
+ --error: #cf222e;
27
+ }
28
+
29
+ * { box-sizing: border-box; }
30
+
31
+ /* Floating Button (FAB) */
32
+ .wp-fab {
33
+ position: fixed;
34
+ bottom: 20px;
35
+ right: 20px;
36
+ width: 50px;
37
+ height: 50px;
38
+ background: var(--accent);
39
+ color: white;
40
+ border-radius: 50%;
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ font-family: var(--font);
45
+ font-weight: bold;
46
+ cursor: move;
47
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
48
+ z-index: 999999;
49
+ user-select: none;
50
+ touch-action: none;
51
+ }
52
+ .wp-fab:hover {
53
+ background: var(--accent-hover);
54
+ }
55
+
56
+ /* Panel UI */
57
+ .wp-panel {
58
+ position: fixed;
59
+ bottom: 80px;
60
+ right: 20px;
61
+ width: 450px;
62
+ max-width: calc(100vw - 40px);
63
+ background: var(--bg);
64
+ color: var(--text);
65
+ border: 1px solid var(--border);
66
+ border-radius: 8px;
67
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5);
68
+ font-family: var(--font);
69
+ font-size: 13px;
70
+ z-index: 999998;
71
+ display: flex;
72
+ flex-direction: column;
73
+ opacity: 0;
74
+ pointer-events: none;
75
+ transform: translateY(10px);
76
+ transition: opacity 0.2s ease, transform 0.2s ease;
77
+ max-height: calc(100vh - 100px);
78
+ touch-action: none;
79
+ }
80
+
81
+ .wp-panel.open {
82
+ opacity: 1;
83
+ pointer-events: all;
84
+ transform: translateY(0);
85
+ }
86
+
87
+ .wp-header {
88
+ display: flex;
89
+ justify-content: space-between;
90
+ align-items: center;
91
+ padding: 10px 15px;
92
+ border-bottom: 1px solid var(--border);
93
+ background: var(--bg-light);
94
+ border-radius: 8px 8px 0 0;
95
+ cursor: move;
96
+ user-select: none;
97
+ }
98
+ .wp-header h3 {
99
+ margin: 0;
100
+ font-size: 14px;
101
+ font-weight: 600;
102
+ pointer-events: none;
103
+ }
104
+
105
+ .wp-actions {
106
+ display: flex;
107
+ align-items: center;
108
+ gap: 12px;
109
+ }
110
+
111
+ /* Theme Toggle Button */
112
+ .wp-theme-toggle {
113
+ background: none;
114
+ border: none;
115
+ color: var(--text-muted);
116
+ font-size: 14px;
117
+ cursor: pointer;
118
+ padding: 2px 6px;
119
+ border-radius: 4px;
120
+ }
121
+ .wp-theme-toggle:hover {
122
+ color: var(--text);
123
+ background: var(--border);
124
+ }
125
+
126
+ .wp-header #wp-close {
127
+ background: none;
128
+ border: none;
129
+ color: var(--text-muted);
130
+ font-size: 20px;
131
+ cursor: pointer;
132
+ }
133
+ .wp-header #wp-close:hover { color: var(--text); }
134
+
135
+ /* Layout Request Bar */
136
+ .wp-row {
137
+ display: flex;
138
+ gap: 8px;
139
+ padding: 15px;
140
+ border-bottom: 1px solid var(--border);
141
+ }
142
+
143
+ input, select, textarea {
144
+ background: var(--bg-light);
145
+ color: var(--text);
146
+ border: 1px solid var(--border);
147
+ padding: 8px;
148
+ border-radius: 4px;
149
+ font-family: monospace;
150
+ outline: none;
151
+ font-size: 14px;
152
+ }
153
+ input:focus, select:focus, textarea:focus {
154
+ border-color: var(--accent);
155
+ }
156
+
157
+ #wp-method { width: 90px; }
158
+ #wp-url { flex: 1; min-width: 0; }
159
+
160
+ .btn-primary {
161
+ background: var(--accent);
162
+ color: white;
163
+ border: none;
164
+ padding: 8px 16px;
165
+ border-radius: 4px;
166
+ cursor: pointer;
167
+ font-weight: 600;
168
+ }
169
+ .btn-primary:hover:not(:disabled) { background: var(--accent-hover); }
170
+ .btn-primary:disabled { opacity: 0.6; cursor: not-allowed; }
171
+
172
+ .wp-section {
173
+ padding: 10px 15px;
174
+ border-bottom: 1px solid var(--border);
175
+ display: flex;
176
+ flex-direction: column;
177
+ gap: 5px;
178
+ }
179
+ .wp-section label {
180
+ font-size: 11px;
181
+ text-transform: uppercase;
182
+ color: var(--text-muted);
183
+ font-weight: bold;
184
+ display: flex;
185
+ justify-content: space-between;
186
+ align-items: center;
187
+ }
188
+
189
+ /* Copy Button Style */
190
+ .btn-copy {
191
+ background: none;
192
+ border: none;
193
+ color: var(--accent);
194
+ font-size: 11px;
195
+ cursor: pointer;
196
+ padding: 2px 6px;
197
+ border-radius: 3px;
198
+ text-transform: none;
199
+ font-weight: 600;
200
+ }
201
+ .btn-copy:hover {
202
+ background: var(--bg-light);
203
+ text-decoration: underline;
204
+ }
205
+
206
+ textarea {
207
+ height: 65px;
208
+ resize: vertical;
209
+ font-size: 12px;
210
+ }
211
+
212
+ .wp-response-section {
213
+ flex: 1;
214
+ overflow: hidden;
215
+ border-bottom: none;
216
+ }
217
+ #wp-response {
218
+ margin: 0;
219
+ background: var(--bg-light);
220
+ padding: 10px;
221
+ border-radius: 4px;
222
+ overflow-y: auto;
223
+ max-height: 200px;
224
+ font-family: monospace;
225
+ font-size: 12px;
226
+ white-space: pre-wrap;
227
+ word-break: break-all;
228
+ }
229
+
230
+ .status-ok { color: var(--success); }
231
+ .status-err { color: var(--error); }
232
+
233
+ /* RESPONSIVE / MOBILE MODE (Screen < 500px) */
234
+ @media (max-width: 500px) {
235
+ .wp-panel {
236
+ width: 100% !important;
237
+ max-width: 100% !important;
238
+ left: 0 !important;
239
+ right: 0 !important;
240
+ bottom: 0 !important;
241
+ top: 0 !important;
242
+ max-height: 100vh !important;
243
+ border-radius: 0;
244
+ transform: translateY(100%);
245
+ }
246
+ .wp-panel.open {
247
+ transform: translateY(0);
248
+ }
249
+ .wp-header {
250
+ border-radius: 0;
251
+ cursor: default;
252
+ }
253
+ .wp-row {
254
+ flex-direction: column;
255
+ gap: 10px;
256
+ }
257
+ #wp-method { width: 100%; }
258
+ #wp-url { width: 100%; }
259
+ .btn-primary { width: 100%; }
260
+ #wp-response {
261
+ max-height: calc(100vh - 380px);
262
+ }
263
+ }
264
+ `}class I{constructor(t){g(this,"container");g(this,"shadow");g(this,"config");g(this,"isOpen",!1);g(this,"isLightMode",!1);this.config=t,this.container=document.createElement("div"),this.container.id="wepost-container",this.shadow=this.container.attachShadow({mode:"closed"}),this.isLightMode=localStorage.getItem("wepost_theme")==="light",this.render(),this.applyTheme()}mount(){document.getElementById("wepost-container")||(document.body.appendChild(this.container),this.attachEvents(),this.loadLastRequest())}unmount(){this.container.remove()}render(){this.shadow.innerHTML=`
265
+ <style>${L()}</style>
266
+ <div class="wp-fab" id="wp-btn">WP</div>
267
+ <div class="wp-panel" id="wp-panel">
268
+ <div class="wp-header" id="wp-header">
269
+ <h3>WEPOST DevTools</h3>
270
+ <div class="wp-actions">
271
+ <button id="wp-theme-btn" class="wp-theme-toggle">☀️ Light</button>
272
+ <button id="wp-close">&times;</button>
273
+ </div>
274
+ </div>
275
+ <div class="wp-row">
276
+ <select id="wp-method">
277
+ <option>GET</option><option>POST</option><option>PUT</option>
278
+ <option>PATCH</option><option>DELETE</option>
279
+ </select>
280
+ <input type="text" id="wp-url" placeholder="https://api.example.com/v1/data" value="${this.config.defaultUrl||""}" />
281
+ <button id="wp-send" class="btn-primary">Send</button>
282
+ </div>
283
+ <div class="wp-section">
284
+ <label>Headers (JSON)</label>
285
+ <textarea id="wp-headers" placeholder='{"Authorization": "Bearer token", "Content-Type": "application/json"}'></textarea>
286
+ </div>
287
+ <div class="wp-section">
288
+ <label>Body (JSON)</label>
289
+ <textarea id="wp-body" placeholder='{"key": "value"}'></textarea>
290
+ </div>
291
+ <div class="wp-section wp-response-section">
292
+ <label>
293
+ <span>Response <span id="wp-status"></span></span>
294
+ <button id="wp-copy-btn" class="btn-copy">📋 Copy</button>
295
+ </label>
296
+ <pre id="wp-response">Awaiting request...</pre>
297
+ </div>
298
+ </div>
299
+ `}attachEvents(){const t=this.shadow.getElementById("wp-btn"),e=this.shadow.getElementById("wp-close"),a=this.shadow.getElementById("wp-theme-btn"),i=this.shadow.getElementById("wp-copy-btn"),l=this.shadow.getElementById("wp-send"),s=this.shadow.getElementById("wp-panel"),u=this.shadow.getElementById("wp-header");this.makeElementDraggable(t,t,!0),this.makeElementDraggable(s,u,!1),t.addEventListener("click",()=>{if(t.getAttribute("data-dragged")==="true"){t.removeAttribute("data-dragged");return}this.isOpen=!this.isOpen,this.isOpen?(s.classList.add("open"),this.syncPanelPosition()):s.classList.remove("open")}),a.addEventListener("click",c=>{c.stopPropagation(),this.isLightMode=!this.isLightMode,localStorage.setItem("wepost_theme",this.isLightMode?"light":"dark"),this.applyTheme()}),i.addEventListener("click",()=>{const h=this.shadow.getElementById("wp-response").textContent||"";h&&h!=="Awaiting request..."&&h!=="Loading..."&&navigator.clipboard.writeText(h).then(()=>{i.textContent="✅ Copied!",setTimeout(()=>{i.textContent="📋 Copy"},2e3)}).catch(p=>{console.error("Failed to copy text: ",p)})}),e.addEventListener("click",c=>{c.stopPropagation(),this.isOpen=!1,s.classList.remove("open")}),l.addEventListener("click",()=>this.handleRequest())}applyTheme(){const t=this.shadow.getElementById("wp-panel"),e=this.shadow.getElementById("wp-theme-btn");!t||!e||(this.isLightMode?(t.classList.add("light-theme"),e.textContent="🌙 Dark"):(t.classList.remove("light-theme"),e.textContent="☀️ Light"))}syncPanelPosition(){if(window.innerWidth<=500)return;const t=this.shadow.getElementById("wp-btn"),e=this.shadow.getElementById("wp-panel"),a=t.getBoundingClientRect();e.style.top="auto",e.style.left="auto",e.style.right=`${window.innerWidth-a.right}px`,e.style.bottom=`${window.innerHeight-a.top+10}px`}makeElementDraggable(t,e,a){let i=0,l=0,s=!1,u=0,c=0;const h=n=>{if(!a&&window.innerWidth<=500)return;const f=n.target.tagName.toLowerCase();if(n.target.id==="wp-close"||n.target.id==="wp-theme-btn"||n.target.id==="wp-copy-btn"||f==="input"||f==="select"||f==="textarea")return;s=!1;const x="touches"in n?n.touches[0].clientX:n.clientX,b="touches"in n?n.touches[0].clientY:n.clientY;u=x,c=b;const y=t.getBoundingClientRect();i=x-y.left,l=b-y.top,document.addEventListener("mousemove",p,{passive:!1}),document.addEventListener("mouseup",w),document.addEventListener("touchmove",p,{passive:!1}),document.addEventListener("touchend",w)},p=n=>{const f="touches"in n?n.touches[0].clientX:n.clientX,x="touches"in n?n.touches[0].clientY:n.clientY;if(!s&&(Math.abs(f-u)>5||Math.abs(x-c)>5)&&(s=!0,a&&t.setAttribute("data-dragged","true")),s){n.cancelable&&n.preventDefault();let b=f-i,y=x-l;t.style.position="fixed",t.style.left=`${b}px`,t.style.top=`${y}px`,t.style.right="auto",t.style.bottom="auto",a&&this.isOpen&&this.syncPanelPosition()}},w=()=>{document.removeEventListener("mousemove",p),document.removeEventListener("mouseup",w),document.removeEventListener("touchmove",p),document.removeEventListener("touchend",w),s&&setTimeout(()=>{s||t.removeAttribute("data-dragged")},50)};e.addEventListener("mousedown",h),e.addEventListener("touchstart",h,{passive:!0})}async handleRequest(){const t=this.shadow.getElementById("wp-url").value,e=this.shadow.getElementById("wp-method").value,a=this.shadow.getElementById("wp-headers").value,i=this.shadow.getElementById("wp-body").value,l=this.shadow.getElementById("wp-status"),s=this.shadow.getElementById("wp-response"),u=this.shadow.getElementById("wp-send");try{u.disabled=!0,u.textContent="Sending...",s.textContent="Loading...",l.textContent="",l.className="";let c={},h=null;a&&(c=JSON.parse(a)),i&&(h=JSON.parse(i)),B({url:t,method:e,headers:a,body:i});const p=await d({url:t,method:e,headers:c,body:h,proxyUrl:this.config.proxyUrl});l.textContent=`${p.status} ${p.statusText}`,l.className=p.status>=200&&p.status<300?"status-ok":"status-err";try{const w=JSON.parse(p.data);s.textContent=JSON.stringify(w,null,2)}catch{s.textContent=p.data}}catch(c){l.textContent="ERROR",l.className="status-err",s.textContent=c.message}finally{u.disabled=!1,u.textContent="Send"}}loadLastRequest(){const t=E();if(t.length>0){const e=t[0];this.shadow.getElementById("wp-url").value=e.url,this.shadow.getElementById("wp-method").value=e.method,this.shadow.getElementById("wp-headers").value=e.headers||"",this.shadow.getElementById("wp-body").value=e.body||""}}}class v{constructor(t={}){g(this,"ui");this.ui=new I(t)}mount(){this.ui.mount()}unmount(){this.ui.unmount()}}if(typeof window<"u"){const o=document.currentScript||document.querySelector('script[src*="wepost"]');if(o&&o.getAttribute("data-auto-init")==="true"){const t=o.getAttribute("data-proxy")||void 0,e=new v({proxyUrl:t});e.mount(),window.WePostInstance=e}}r.WePost=v,r.default=v,Object.defineProperties(r,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "wepost",
3
+ "version": "1.0.0",
4
+ "description": "Lightweight embedded API DevTools widget",
5
+ "main": "./dist/wepost.umd.js",
6
+ "module": "./dist/wepost.es.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/wepost.es.js",
11
+ "require": "./dist/wepost.umd.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "proxy",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "dev": "vite",
22
+ "build": "tsc && vite build",
23
+ "proxy": "node proxy/server.js"
24
+ },
25
+ "keywords": ["api", "devtools", "postman", "embeddable", "fetch"],
26
+ "author": "",
27
+ "license": "MIT",
28
+ "devDependencies": {
29
+ "typescript": "^5.0.2",
30
+ "vite": "^4.4.5",
31
+ "vite-plugin-dts": "^3.6.0",
32
+ "cors": "^2.8.5",
33
+ "express": "^4.18.2"
34
+ }
35
+ }
@@ -0,0 +1,38 @@
1
+ const express = require('express');
2
+ const cors = require('cors');
3
+ const app = express();
4
+ const PORT = process.env.PORT || 4000;
5
+
6
+ // Enable CORS for the widget to connect
7
+ app.use(cors());
8
+ app.use(express.json());
9
+
10
+ app.all('/proxy', async (req, res) => {
11
+ const targetUrl = req.headers['x-wepost-target'];
12
+ if (!targetUrl) {
13
+ return res.status(400).send('Missing x-wepost-target header');
14
+ }
15
+
16
+ // Remove proxy specific headers before forwarding
17
+ const headers = { ...req.headers };
18
+ delete headers['x-wepost-target'];
19
+ delete headers['host'];
20
+
21
+ try {
22
+ const fetch = (await import('node-fetch')).default;
23
+ const response = await fetch(targetUrl, {
24
+ method: req.method,
25
+ headers: headers,
26
+ body: ['GET', 'HEAD'].includes(req.method) ? undefined : JSON.stringify(req.body)
27
+ });
28
+
29
+ const data = await response.text();
30
+ res.status(response.status).send(data);
31
+ } catch (error) {
32
+ res.status(500).send(error.message);
33
+ }
34
+ });
35
+
36
+ app.listen(PORT, () => {
37
+ console.log(`[WEPOST PROXY] Listening on http://localhost:${PORT}/proxy`);
38
+ });