enigmatic 0.26.0 → 0.29.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,322 @@
1
+ # client.js Documentation
2
+
3
+ ## Overview
4
+
5
+ `client.js` is a client-side JavaScript library that provides utilities for DOM manipulation, reactive state management, and API interactions with a backend server. It automatically initializes custom HTML elements and provides a simple API for key-value storage, file operations, and authentication.
6
+
7
+ ## Core Utilities
8
+
9
+ ### DOM Selectors
10
+
11
+ ```javascript
12
+ window.$ // Alias for document.querySelector
13
+ window.$$ // Alias for document.querySelectorAll
14
+ window.$c // Alias for element.closest (requires $0 context)
15
+ ```
16
+
17
+ **Usage:**
18
+ ```javascript
19
+ const element = window.$('#my-id');
20
+ const elements = window.$$('.my-class');
21
+ ```
22
+
23
+ ### API Base URL
24
+
25
+ ```javascript
26
+ window.api_url = "https://localhost:3000"
27
+ ```
28
+
29
+ Configures the base URL for all API requests. Modify this to point to your server.
30
+
31
+ ## Reactive State Management
32
+
33
+ `window.state` is a Proxy object that automatically updates DOM elements when properties change.
34
+
35
+ **How it works:**
36
+ - Set a property: `window.state.myKey = 'value'`
37
+ - Elements with `data="myKey"` attribute are automatically updated
38
+ - The system looks for custom element handlers in `window.custom[tagName]`
39
+ - Supports both function and object-based custom elements
40
+
41
+ **Example:**
42
+ ```html
43
+ <div data="message">Initial</div>
44
+ <script>
45
+ window.state.message = "Updated!"; // Automatically updates the div
46
+ </script>
47
+ ```
48
+
49
+ **Custom Element Integration:**
50
+ - If `window.custom[tagName]` is a function: calls `f(value)` and sets `innerHTML`
51
+ - If `window.custom[tagName]` is an object: calls `f.render(value)` and sets `innerHTML`
52
+
53
+ ## API Functions
54
+
55
+ All API functions are async and return Promises. They use `window.api_url` as the base URL.
56
+
57
+ ### KV Storage Operations
58
+
59
+ #### `window.get(key)`
60
+ Retrieves a value from the server's key-value store.
61
+
62
+ ```javascript
63
+ const value = await window.get('my-key');
64
+ ```
65
+
66
+ **HTTP Method:** GET
67
+ **Endpoint:** `{api_url}/{key}`
68
+ **Returns:** Parsed JSON response
69
+
70
+ #### `window.set(key, value)`
71
+ Stores a value in the server's key-value store.
72
+
73
+ ```javascript
74
+ await window.set('my-key', 'my-value');
75
+ await window.set('my-key', { json: 'object' });
76
+ ```
77
+
78
+ **HTTP Method:** POST
79
+ **Endpoint:** `{api_url}/{key}`
80
+ **Body:** String values sent as-is, objects are JSON stringified
81
+ **Returns:** Parsed JSON response
82
+
83
+ #### `window.delete(key)`
84
+ Deletes a key from the server's key-value store.
85
+
86
+ ```javascript
87
+ await window.delete('my-key');
88
+ ```
89
+
90
+ **HTTP Method:** DELETE
91
+ **Endpoint:** `{api_url}/{key}`
92
+ **Returns:** Parsed JSON response
93
+
94
+ ### R2 Storage Operations (File Storage)
95
+
96
+ #### `window.put(key, body)`
97
+ Uploads a file or data to R2 storage.
98
+
99
+ ```javascript
100
+ await window.put('filename.txt', 'file content');
101
+ await window.put('image.png', blob);
102
+ await window.put('data.json', { json: 'data' });
103
+ ```
104
+
105
+ **HTTP Method:** PUT
106
+ **Endpoint:** `{api_url}/{key}`
107
+ **Body:** Accepts Blob, string, or JSON-serializable objects
108
+ **Returns:** Parsed JSON response
109
+
110
+ #### `window.purge(key)`
111
+ Deletes a file from R2 storage.
112
+
113
+ ```javascript
114
+ await window.purge('filename.txt');
115
+ ```
116
+
117
+ **HTTP Method:** PURGE
118
+ **Endpoint:** `{api_url}/{key}`
119
+ **Returns:** Parsed JSON response
120
+
121
+ #### `window.list()`
122
+ Lists all files in the current user's R2 storage.
123
+
124
+ ```javascript
125
+ const files = await window.list();
126
+ // Returns: [{ name: 'file1.txt', lastModified: '...', size: 123 }, ...]
127
+ ```
128
+
129
+ **HTTP Method:** PROPFIND
130
+ **Endpoint:** `{api_url}/` (base URL, no key)
131
+ **Returns:** Array of file objects with `name`, `lastModified`, and `size` properties
132
+
133
+ #### `window.download(key)`
134
+ Downloads a file from R2 storage and triggers browser download.
135
+
136
+ ```javascript
137
+ await window.download('filename.txt');
138
+ ```
139
+
140
+ **HTTP Method:** PATCH
141
+ **Endpoint:** `{api_url}/{key}`
142
+ **Behavior:**
143
+ - Fetches file as blob
144
+ - Creates temporary download URL
145
+ - Triggers browser download
146
+ - Cleans up temporary URL
147
+
148
+ **Note:** Uses PATCH method due to browser limitations with custom HTTP methods.
149
+
150
+ ### Authentication
151
+
152
+ #### `window.login()`
153
+ Redirects to the server's login endpoint.
154
+
155
+ ```javascript
156
+ window.login();
157
+ ```
158
+
159
+ **Behavior:** Sets `window.location.href` to `{api_url}/login`
160
+
161
+ #### `window.logout()`
162
+ Redirects to the server's logout endpoint.
163
+
164
+ ```javascript
165
+ window.logout();
166
+ ```
167
+
168
+ **Behavior:** Sets `window.location.href` to `{api_url}/logout`
169
+
170
+ ## Custom Elements System
171
+
172
+ Custom elements are defined in `window.custom` object and automatically initialized when the DOM loads.
173
+
174
+ ### Initialization
175
+
176
+ The library automatically:
177
+ 1. Waits for DOM to be ready (`DOMContentLoaded` or immediate if already loaded)
178
+ 2. Iterates through all keys in `window.custom`
179
+ 3. Finds all matching HTML elements by tag name
180
+ 4. Calls the custom element handler and sets `innerHTML`
181
+
182
+ ### Defining Custom Elements
183
+
184
+ #### Function-based Custom Element
185
+
186
+ ```javascript
187
+ window.custom = {
188
+ "my-element": async (data) => {
189
+ return `<div>Content: ${data}</div>`;
190
+ }
191
+ };
192
+ ```
193
+
194
+ **HTML Usage:**
195
+ ```html
196
+ <my-element></my-element>
197
+ ```
198
+
199
+ When `window.state.myKey = 'value'` is set and an element has `data="myKey"`:
200
+ ```html
201
+ <my-element data="myKey"></my-element>
202
+ ```
203
+
204
+ The function receives the state value as the first parameter.
205
+
206
+ #### Object-based Custom Element
207
+
208
+ ```javascript
209
+ window.custom = {
210
+ "my-element": {
211
+ prop: (data) => `Processed: ${data}`,
212
+ render: function(data) {
213
+ return `<div>${this.prop(data)}</div>`;
214
+ }
215
+ }
216
+ };
217
+ ```
218
+
219
+ **HTML Usage:**
220
+ ```html
221
+ <my-element></my-element>
222
+ ```
223
+
224
+ When used with reactive state, the `render` method is called with the state value.
225
+
226
+ ### Example: File Widget
227
+
228
+ ```javascript
229
+ window.custom = {
230
+ "file-widget": async () => {
231
+ const list = await window.list();
232
+ // Returns HTML string with file list and upload button
233
+ return `<div>...</div>`;
234
+ }
235
+ };
236
+ ```
237
+
238
+ **HTML Usage:**
239
+ ```html
240
+ <file-widget></file-widget>
241
+ ```
242
+
243
+ This custom element:
244
+ - Fetches file list using `window.list()`
245
+ - Renders file items with download and delete buttons
246
+ - Includes an upload button
247
+ - Uses inline event handlers that call `window.download()`, `window.purge()`, and `window.put()`
248
+
249
+ ## Error Handling
250
+
251
+ All API functions throw errors if the request fails. Use try-catch or `.catch()`:
252
+
253
+ ```javascript
254
+ try {
255
+ await window.get('nonexistent');
256
+ } catch (err) {
257
+ console.error('Error:', err);
258
+ }
259
+
260
+ // Or with promises
261
+ window.get('key').catch(err => console.error(err));
262
+ ```
263
+
264
+ ## Complete Example
265
+
266
+ ```html
267
+ <!DOCTYPE html>
268
+ <html>
269
+ <head>
270
+ <script src="custom.js"></script>
271
+ <script src="client.js"></script>
272
+ </head>
273
+ <body>
274
+ <!-- Custom element -->
275
+ <file-widget></file-widget>
276
+
277
+ <!-- Reactive state element -->
278
+ <div data="message">Initial</div>
279
+
280
+ <script>
281
+ // Set reactive state
282
+ window.state.message = "Hello World";
283
+
284
+ // Use API functions
285
+ (async () => {
286
+ await window.set('test', 'value');
287
+ const value = await window.get('test');
288
+ console.log(value);
289
+
290
+ // Upload file
291
+ const fileInput = document.querySelector('input[type="file"]');
292
+ fileInput.onchange = async (e) => {
293
+ const file = e.target.files[0];
294
+ await window.put(file.name, file);
295
+ };
296
+
297
+ // List files
298
+ const files = await window.list();
299
+ console.log(files);
300
+ })();
301
+ </script>
302
+ </body>
303
+ </html>
304
+ ```
305
+
306
+ ## Dependencies
307
+
308
+ - Requires `custom.js` to be loaded before `client.js` if using custom elements
309
+ - Requires a backend server that implements the API endpoints
310
+ - Requires browser support for:
311
+ - `fetch` API
312
+ - `Proxy` API
313
+ - `Blob` API
314
+ - `URL.createObjectURL`
315
+
316
+ ## Notes
317
+
318
+ - All API functions automatically encode keys using `encodeURIComponent`
319
+ - The `window.download()` function uses PATCH method internally (browsers don't support custom HTTP methods)
320
+ - Custom elements are initialized once on page load; use `location.reload()` to refresh
321
+ - The reactive state system only updates elements with matching `data` attributes
322
+ - Custom element handlers can be async functions