enigmatic 0.33.0 → 0.35.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.
- package/{CLIENT_JS_DOCS.md → README.md} +163 -36
- package/bin/enigmatic.js +3 -0
- package/client/public/AGENTS.md +314 -0
- package/client/public/client.js +125 -0
- package/client/public/custom.js +34 -0
- package/client/public/index.html +197 -0
- package/clientserver.png +0 -0
- package/package.json +7 -9
- package/server/bun-server.js +119 -0
- package/__tests__/e2.test.js +0 -310
- package/__tests__/jest.config.js +0 -7
- package/__tests__/jest.setup.js +0 -9
- package/bun-server.js +0 -130
- package/public/client.css +0 -286
- package/public/client.js +0 -80
- package/public/custom.js +0 -29
- package/public/index.html +0 -45
- package/public/index2.html +0 -9
- package/public/theme.css +0 -9
package/public/client.css
DELETED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
html {
|
|
2
|
-
max-width: 70ch;
|
|
3
|
-
/* larger spacing on larger screens, very small spacing on tiny screens */
|
|
4
|
-
padding: calc(1vmin + .5rem);
|
|
5
|
-
/* shorthand for margin-left/margin-right */
|
|
6
|
-
margin-inline: auto;
|
|
7
|
-
/* fluid sizing: https://frontaid.io/blog/fluid-typography-2d-css-locks-clamp/ */
|
|
8
|
-
font-size: clamp(1em, 0.909em + 0.45vmin, 1.25em);
|
|
9
|
-
/* use system font stack: https://developer.mozilla.org/en-US/docs/Web/CSS/font-family */
|
|
10
|
-
font-family: system-ui
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/* increase line-height for everything except headings */
|
|
14
|
-
body :not(:is(h1, h2, h3, h4, h5, h6)) {
|
|
15
|
-
line-height: 1.75;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
h1,
|
|
19
|
-
h2,
|
|
20
|
-
h3,
|
|
21
|
-
h4,
|
|
22
|
-
h5,
|
|
23
|
-
h6 {
|
|
24
|
-
margin: 3em 0 1em;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
p,
|
|
28
|
-
ul,
|
|
29
|
-
ol {
|
|
30
|
-
margin-bottom: 2em;
|
|
31
|
-
color: #1d1d1d;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
body,
|
|
35
|
-
section {
|
|
36
|
-
display: grid;
|
|
37
|
-
margin: 0;
|
|
38
|
-
grid-template-columns: var(--cols, 1fr 4fr 1fr);
|
|
39
|
-
grid-template-rows: var(--rows, 1fr 9fr 1fr);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
* {
|
|
43
|
-
grid-column: span var(--span, 1);
|
|
44
|
-
grid-row: span var(--span-rows, 1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.flex {
|
|
48
|
-
display: flex;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** positioning ***/
|
|
52
|
-
|
|
53
|
-
.center {
|
|
54
|
-
position: fixed;
|
|
55
|
-
top: 50%;
|
|
56
|
-
left: 50%;
|
|
57
|
-
margin-top: -50px;
|
|
58
|
-
margin-left: -100px;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.right {
|
|
62
|
-
float: right;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.left {
|
|
66
|
-
float: left;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.fixed {
|
|
70
|
-
position: fixed;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.top {
|
|
74
|
-
top: 0;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.bottom {
|
|
78
|
-
bottom: 0
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.fill {
|
|
82
|
-
height: 100vh;
|
|
83
|
-
width: 100wh
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.hide {
|
|
87
|
-
opacity: 0;
|
|
88
|
-
transition: opacity 0.25s linear;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.show {
|
|
92
|
-
opacity: 1;
|
|
93
|
-
transition: opacity 0.25s linear;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.slide-in {
|
|
97
|
-
animation: slide-in 0.1s forwards;
|
|
98
|
-
-webkit-animation: slide-in 0.1s forwards;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.slide-out {
|
|
102
|
-
animation: slide-out 0.1s forwards;
|
|
103
|
-
-webkit-animation: slide-out 0.1s forwards;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
@keyframes slide-in {
|
|
107
|
-
100% {
|
|
108
|
-
transform: translateX(0%);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
@-webkit-keyframes slide-in {
|
|
113
|
-
100% {
|
|
114
|
-
-webkit-transform: translateX(0%);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
@keyframes slide-out {
|
|
119
|
-
0% {
|
|
120
|
-
transform: translateX(0%);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
100% {
|
|
124
|
-
transform: translateX(-100%);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
@-webkit-keyframes slide-out {
|
|
129
|
-
0% {
|
|
130
|
-
-webkit-transform: translateX(0%);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
100% {
|
|
134
|
-
-webkit-transform: translateX(-100%);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
a {
|
|
139
|
-
text-decoration: none;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
.shadow {
|
|
143
|
-
box-shadow: 6px 6px 6px #dbdbdb;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
.cursor {
|
|
147
|
-
cursor: default;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.margins {
|
|
151
|
-
margin: var(--margins, 15px);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
.padding {
|
|
155
|
-
padding: var(--padding, 15px);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
.rem {
|
|
159
|
-
font-size: var(--rem, 2rem);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.faded {
|
|
163
|
-
opacity: 0.5;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
.fade {
|
|
167
|
-
opacity: 1;
|
|
168
|
-
transition: opacity 0.25s ease-in-out;
|
|
169
|
-
-moz-transition: opacity 0.25s ease-in-out;
|
|
170
|
-
-webkit-transition: opacity 0.25s ease-in-out;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
.fade:hover {
|
|
174
|
-
opacity: 0.5;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
.unfade {
|
|
178
|
-
opacity: 0.5;
|
|
179
|
-
transition: opacity 0.25s ease-in-out;
|
|
180
|
-
-moz-transition: opacity 0.25s ease-in-out;
|
|
181
|
-
-webkit-transition: opacity 0.25s ease-in-out;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
.unfade:hover {
|
|
185
|
-
opacity: 1;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.rounded {
|
|
189
|
-
-moz-border-radius: 10px;
|
|
190
|
-
-webkit-border-radius: 10px;
|
|
191
|
-
border-radius: 10px;
|
|
192
|
-
-khtml-border-radius: 10px;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
.round {
|
|
196
|
-
vertical-align: middle;
|
|
197
|
-
width: 50px;
|
|
198
|
-
height: 50px;
|
|
199
|
-
border-radius: 50%;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/** html elements **/
|
|
203
|
-
|
|
204
|
-
canvas {
|
|
205
|
-
position: fixed;
|
|
206
|
-
top: 0;
|
|
207
|
-
left: 0;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
ul {
|
|
211
|
-
list-style-type: none;
|
|
212
|
-
border: 20px;
|
|
213
|
-
padding: 20px;
|
|
214
|
-
width: 50%;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
li {
|
|
218
|
-
list-style-type: none;
|
|
219
|
-
border: 10px;
|
|
220
|
-
padding: 10px;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
li:hover {
|
|
224
|
-
background-color: rgb(243, 241, 241);
|
|
225
|
-
cursor: pointer;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
.opacity1 {
|
|
229
|
-
opacity: .1
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
.opacity2 {
|
|
233
|
-
opacity: .2
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
.opacity3 {
|
|
237
|
-
opacity: .3
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
.opacity4 {
|
|
241
|
-
opacity: .4
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.opacity5 {
|
|
245
|
-
opacity: .5
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
.opacity6 {
|
|
249
|
-
opacity: .6
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
.opacity7 {
|
|
253
|
-
opacity: .7
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
.opacity8 {
|
|
257
|
-
opacity: .8
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.opacity9 {
|
|
261
|
-
opacity: .9
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
.bg-red {
|
|
265
|
-
background-color: red;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
.bg-blue {
|
|
269
|
-
background-color: blue;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
.bg-yellow {
|
|
273
|
-
background-color: yellow;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
.bg-green {
|
|
277
|
-
background-color: green;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
.bg-black {
|
|
281
|
-
background-color: black;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
.white {
|
|
285
|
-
color: white;
|
|
286
|
-
}
|
package/public/client.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
const D = document, W = window, Enc = encodeURIComponent;
|
|
2
|
-
|
|
3
|
-
// 1. Unified Render Logic (Handles both State & Custom Elements)
|
|
4
|
-
const ren = async (el, v) => {
|
|
5
|
-
const f = W.custom?.[el.tagName.toLowerCase()];
|
|
6
|
-
if (f) {
|
|
7
|
-
const dataAttr = el.getAttribute('data');
|
|
8
|
-
const val = v !== undefined ? v : (dataAttr ? W.state[dataAttr] : undefined);
|
|
9
|
-
try { el.innerHTML = await (f.render || f)(val) } catch(e) { console.error(e) }
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
// 2. Proxies setup
|
|
14
|
-
const cProx = new Proxy({}, {
|
|
15
|
-
set(t, p, v) {
|
|
16
|
-
t[p] = v;
|
|
17
|
-
setTimeout(() => W.$$(p).forEach(el => ren(el)), 0);
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
Object.defineProperty(W, 'custom', { get: () => cProx, set: v => Object.assign(cProx, v) });
|
|
22
|
-
|
|
23
|
-
const sProx = new Proxy({}, {
|
|
24
|
-
set(o, p, v) {
|
|
25
|
-
o[p] = v;
|
|
26
|
-
W.$$(`[data="${p}"]`).forEach(el => ren(el, v));
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// 3. API & DOM Helpers
|
|
32
|
-
const req = (m, k, b) => fetch(`${W.api_url}/${k ? Enc(k) : ''}`, {
|
|
33
|
-
method: m, body: b instanceof Blob || typeof b === 'string' ? b : JSON.stringify(b)
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
Object.assign(W, {
|
|
37
|
-
$: s => D.querySelector(s),
|
|
38
|
-
$$: s => D.querySelectorAll(s),
|
|
39
|
-
$c: s => $0.closest(s),
|
|
40
|
-
state: sProx,
|
|
41
|
-
get: k => req('GET', k).then(r => r.json()),
|
|
42
|
-
set: (k, v) => req('POST', k, v).then(r => r.json()),
|
|
43
|
-
put: (k, v) => req('PUT', k, v).then(r => r.json()),
|
|
44
|
-
delete: k => req('DELETE', k).then(r => r.json()),
|
|
45
|
-
purge: k => req('PURGE', k).then(r => r.json()),
|
|
46
|
-
list: () => req('PROPFIND').then(r => r.json()),
|
|
47
|
-
login: () => W.location.href = `${W.api_url}/login`,
|
|
48
|
-
logout: () => W.location.href = `${W.api_url}/logout`,
|
|
49
|
-
download: async (k) => {
|
|
50
|
-
const r = await req('PATCH', k);
|
|
51
|
-
if (!r.ok) throw new Error('Download failed');
|
|
52
|
-
const a = D.createElement('a');
|
|
53
|
-
a.href = URL.createObjectURL(await r.blob());
|
|
54
|
-
a.download = k;
|
|
55
|
-
a.click();
|
|
56
|
-
URL.revokeObjectURL(a.href);
|
|
57
|
-
},
|
|
58
|
-
initCustomElements: () => Object.keys(W.custom || {}).forEach(t => W.$$(t).forEach(el => ren(el)))
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// 4. Initialization & Observers
|
|
62
|
-
const boot = () => {
|
|
63
|
-
W.initCustomElements();
|
|
64
|
-
new MutationObserver((mutations) => {
|
|
65
|
-
mutations.forEach(m => {
|
|
66
|
-
m.addedNodes.forEach(node => {
|
|
67
|
-
if (node.nodeType === 1) { // Element node
|
|
68
|
-
const tag = node.tagName?.toLowerCase();
|
|
69
|
-
if (tag && W.custom?.[tag]) ren(node);
|
|
70
|
-
// Also check children
|
|
71
|
-
node.querySelectorAll && Array.from(node.querySelectorAll('*')).forEach(child => {
|
|
72
|
-
const childTag = child.tagName?.toLowerCase();
|
|
73
|
-
if (childTag && W.custom?.[childTag]) ren(child);
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
}).observe(D.body, { childList: true, subtree: true });
|
|
79
|
-
};
|
|
80
|
-
D.readyState === 'loading' ? D.addEventListener('DOMContentLoaded', boot) : boot();
|
package/public/custom.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
window.custom = {
|
|
2
|
-
"hello-world": (data) => `Hello ${data}`,
|
|
3
|
-
"hello-world-2": {
|
|
4
|
-
prop: (data) => `${data} World`,
|
|
5
|
-
render: function(data) {
|
|
6
|
-
return this.prop(data);
|
|
7
|
-
}
|
|
8
|
-
},
|
|
9
|
-
"file-widget": async () => {
|
|
10
|
-
const list = await window.list();
|
|
11
|
-
const style = `<style>.w-c{font:13px sans-serif;border:1px solid #ddd;border-radius:6px;overflow:hidden;max-width:320px}.w-i{display:flex;justify-content:space-between;padding:8px 12px;border-bottom:1px solid #f0f0f0;align-items:center}.w-i:hover{background:#f9f9f9}.w-d{border:none;background:none;cursor:pointer;opacity:.5;transition:.2s}.w-d:hover{opacity:1}.w-u{display:block;padding:10px;background:#f5f5f5;text-align:center;cursor:pointer;color:#555;font-weight:600;transition:.2s}.w-u:hover{background:#eee}</style>`;
|
|
12
|
-
|
|
13
|
-
const items = list.map(item => `
|
|
14
|
-
<div class="w-i">
|
|
15
|
-
<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-right:10px">${item.name}</span>
|
|
16
|
-
<button class="w-d" onclick="window.download('${item.name}')" title="Download">⬇️</button>
|
|
17
|
-
<button class="w-d" onclick="(async()=>{await window.purge('${item.name}');location.reload()})()" title="Delete">🗑️</button>
|
|
18
|
-
</div>`
|
|
19
|
-
).join('');
|
|
20
|
-
|
|
21
|
-
const upload = `
|
|
22
|
-
<label class="w-u">
|
|
23
|
-
📂 Upload
|
|
24
|
-
<input type="file" style="display:none" onchange="(async()=>{const f=this.files[0];if(f){await window.put(f.name,f);location.reload()}})()">
|
|
25
|
-
</label>`;
|
|
26
|
-
|
|
27
|
-
return style + `<div class="w-c">${items}${upload}</div>`;
|
|
28
|
-
}
|
|
29
|
-
}
|
package/public/index.html
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>API Test</title>
|
|
7
|
-
<script src="custom.js"></script>
|
|
8
|
-
<script src="client.js"></script>
|
|
9
|
-
<style>
|
|
10
|
-
body { font-family: sans-serif; max-width: 800px; margin: 20px auto; padding: 20px; }
|
|
11
|
-
.section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
|
|
12
|
-
input, button { margin: 5px; padding: 8px; }
|
|
13
|
-
input { width: 200px; }
|
|
14
|
-
button { cursor: pointer; }
|
|
15
|
-
#result { margin-top: 10px; padding: 10px; background: #f5f5f5; border-radius: 3px; white-space: pre-wrap; }
|
|
16
|
-
.error { color: red; }
|
|
17
|
-
.success { color: green; }
|
|
18
|
-
</style>
|
|
19
|
-
</head>
|
|
20
|
-
<body>
|
|
21
|
-
<h1>Server API Test</h1>
|
|
22
|
-
|
|
23
|
-
<div class="section">
|
|
24
|
-
<h2>KV Storage</h2>
|
|
25
|
-
<input type="text" id="kv-key" placeholder="Key" value="test-key">
|
|
26
|
-
<input type="text" id="kv-value" placeholder="Value" value="test-value">
|
|
27
|
-
<button onclick="window.set(document.querySelector('#kv-key').value, document.querySelector('#kv-value').value).then(r => window.$('#result').textContent = JSON.stringify(r)).catch(err => window.$('#result').textContent = 'Error: ' + err.message)">POST</button>
|
|
28
|
-
<button onclick="window.get(document.querySelector('#kv-key').value).then(r => window.$('#result').textContent = JSON.stringify(r)).catch(err => window.$('#result').textContent = 'Error: ' + err.message)">GET</button>
|
|
29
|
-
<button onclick="window.delete(document.querySelector('#kv-key').value).then(r => window.$('#result').textContent = JSON.stringify(r)).catch(err => window.$('#result').textContent = 'Error: ' + err.message)">DELETE</button>
|
|
30
|
-
</div>
|
|
31
|
-
|
|
32
|
-
<div class="section">
|
|
33
|
-
<h2>R2 Storage</h2>
|
|
34
|
-
<file-widget></file-widget>
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<div class="section">
|
|
38
|
-
<h2>Auth</h2>
|
|
39
|
-
<button onclick="window.login()">Login</button>
|
|
40
|
-
<button onclick="window.logout()">Logout</button>
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<pre id="result"></pre>
|
|
44
|
-
</body>
|
|
45
|
-
</html>
|
package/public/index2.html
DELETED