juxscript 1.0.62 → 1.0.63

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.
Files changed (141) hide show
  1. package/bin/cli.js +161 -293
  2. package/docs/v2comps/HEADLESS.md +83 -0
  3. package/docs/v2comps/ISOMORPHISM.md +10 -0
  4. package/juxconfig.example.js +63 -58
  5. package/lib/componentsv2/base/BaseEngine.js +258 -0
  6. package/lib/componentsv2/base/BaseEngine.js.map +1 -0
  7. package/lib/componentsv2/base/BaseEngine.ts +303 -0
  8. package/lib/componentsv2/base/BaseSkin.js +108 -0
  9. package/lib/componentsv2/base/BaseSkin.js.map +1 -0
  10. package/lib/componentsv2/base/BaseSkin.ts +137 -0
  11. package/lib/componentsv2/base/GlobalBus.js +56 -0
  12. package/lib/componentsv2/base/GlobalBus.js.map +1 -0
  13. package/lib/componentsv2/base/GlobalBus.ts +60 -0
  14. package/lib/componentsv2/base/State.js +68 -0
  15. package/lib/componentsv2/base/State.js.map +1 -0
  16. package/lib/componentsv2/base/State.ts +62 -0
  17. package/lib/componentsv2/grid/component.js +41 -0
  18. package/lib/componentsv2/grid/component.js.map +1 -0
  19. package/lib/componentsv2/grid/component.ts +67 -0
  20. package/lib/componentsv2/grid/engine.js +73 -0
  21. package/lib/componentsv2/grid/engine.js.map +1 -0
  22. package/lib/componentsv2/grid/engine.ts +110 -0
  23. package/lib/componentsv2/grid/skin.js +95 -0
  24. package/lib/componentsv2/grid/skin.js.map +1 -0
  25. package/lib/componentsv2/grid/skin.ts +105 -0
  26. package/lib/componentsv2/grid/structure.css +58 -0
  27. package/lib/componentsv2/index.js +218 -0
  28. package/lib/componentsv2/index.js.map +1 -0
  29. package/lib/componentsv2/index.ts +253 -0
  30. package/lib/componentsv2/input/component.js +21 -0
  31. package/lib/componentsv2/input/component.js.map +1 -0
  32. package/lib/componentsv2/input/component.ts +28 -0
  33. package/lib/componentsv2/input/engine.js +50 -0
  34. package/lib/componentsv2/input/engine.js.map +1 -0
  35. package/lib/componentsv2/input/engine.ts +76 -0
  36. package/lib/componentsv2/input/skin.js +91 -0
  37. package/lib/componentsv2/input/skin.js.map +1 -0
  38. package/lib/componentsv2/input/skin.ts +91 -0
  39. package/lib/componentsv2/input/structure.css +47 -0
  40. package/lib/componentsv2/list/component.js +83 -0
  41. package/lib/componentsv2/list/component.js.map +1 -0
  42. package/lib/componentsv2/list/component.ts +97 -0
  43. package/lib/componentsv2/list/engine.js +261 -0
  44. package/lib/componentsv2/list/engine.js.map +1 -0
  45. package/lib/componentsv2/list/engine.ts +345 -0
  46. package/lib/componentsv2/list/skin.js +343 -0
  47. package/lib/componentsv2/list/skin.js.map +1 -0
  48. package/lib/componentsv2/list/skin.ts +367 -0
  49. package/lib/componentsv2/list/structure.css +359 -0
  50. package/lib/componentsv2/plugins/ClientSQLitePlugin.js +130 -0
  51. package/lib/componentsv2/plugins/ClientSQLitePlugin.js.map +1 -0
  52. package/lib/componentsv2/plugins/ClientSQLitePlugin.ts +154 -0
  53. package/lib/componentsv2/plugins/IndexedDBPlugin.js +75 -0
  54. package/lib/componentsv2/plugins/IndexedDBPlugin.js.map +1 -0
  55. package/lib/componentsv2/plugins/IndexedDBPlugin.ts +96 -0
  56. package/lib/componentsv2/plugins/LocalStoragePlugin.js +65 -0
  57. package/lib/componentsv2/plugins/LocalStoragePlugin.js.map +1 -0
  58. package/lib/componentsv2/plugins/LocalStoragePlugin.ts +86 -0
  59. package/lib/componentsv2/plugins/ServerSQLitePlugin.js +70 -0
  60. package/lib/componentsv2/plugins/ServerSQLitePlugin.js.map +1 -0
  61. package/lib/componentsv2/plugins/ServerSQLitePlugin.ts +99 -0
  62. package/lib/componentsv2/stubs/ComponentComposition.ts.stub +32 -0
  63. package/lib/componentsv2/stubs/ComponentEngine.ts.stub +36 -0
  64. package/lib/componentsv2/stubs/ComponentSkin.ts.stub +34 -0
  65. package/lib/componentsv2/stubs/ComponentStructure.css.stub +13 -0
  66. package/lib/componentsv2/tools/CreateSkin.js +62 -0
  67. package/lib/componentsv2/tools/DocSpam.js +134 -0
  68. package/lib/componentsv2/tools/FluencyAudit.js +141 -0
  69. package/lib/componentsv2/tools/OptionsAudit.js +177 -0
  70. package/lib/componentsv2/tools/Scaffold.js +140 -0
  71. package/lib/utils/fetch.js +428 -0
  72. package/lib/utils/fetch.js.map +1 -0
  73. package/machinery/build.js +2 -1
  74. package/machinery/compiler.js +200 -37
  75. package/machinery/config.js +93 -6
  76. package/machinery/diagnose.js +72 -0
  77. package/machinery/jux-module-pattern.md +118 -0
  78. package/machinery/server.js +23 -7
  79. package/machinery/verifier.js +143 -0
  80. package/machinery/watcher.js +53 -64
  81. package/package.json +11 -2
  82. package/lib/components/alert.ts +0 -200
  83. package/lib/components/app.ts +0 -258
  84. package/lib/components/badge.ts +0 -101
  85. package/lib/components/base/BaseComponent.ts +0 -417
  86. package/lib/components/base/FormInput.ts +0 -227
  87. package/lib/components/button.ts +0 -178
  88. package/lib/components/card.ts +0 -173
  89. package/lib/components/chart.ts +0 -231
  90. package/lib/components/checkbox.ts +0 -242
  91. package/lib/components/code.ts +0 -123
  92. package/lib/components/container.ts +0 -140
  93. package/lib/components/data.ts +0 -135
  94. package/lib/components/datepicker.ts +0 -234
  95. package/lib/components/dialog.ts +0 -172
  96. package/lib/components/divider.ts +0 -100
  97. package/lib/components/dropdown.ts +0 -186
  98. package/lib/components/element.ts +0 -267
  99. package/lib/components/error-handler.ts +0 -285
  100. package/lib/components/fileupload.ts +0 -309
  101. package/lib/components/grid.ts +0 -291
  102. package/lib/components/guard.ts +0 -92
  103. package/lib/components/heading.ts +0 -96
  104. package/lib/components/helpers.ts +0 -41
  105. package/lib/components/hero.ts +0 -224
  106. package/lib/components/icon.ts +0 -160
  107. package/lib/components/icons.ts +0 -175
  108. package/lib/components/include.ts +0 -440
  109. package/lib/components/input.ts +0 -457
  110. package/lib/components/list.ts +0 -419
  111. package/lib/components/loading.ts +0 -100
  112. package/lib/components/menu.ts +0 -260
  113. package/lib/components/modal.ts +0 -239
  114. package/lib/components/nav.ts +0 -257
  115. package/lib/components/paragraph.ts +0 -97
  116. package/lib/components/progress.ts +0 -139
  117. package/lib/components/radio.ts +0 -278
  118. package/lib/components/req.ts +0 -302
  119. package/lib/components/script.ts +0 -43
  120. package/lib/components/select.ts +0 -252
  121. package/lib/components/sidebar.ts +0 -167
  122. package/lib/components/style.ts +0 -43
  123. package/lib/components/switch.ts +0 -246
  124. package/lib/components/table.ts +0 -1249
  125. package/lib/components/tabs.ts +0 -250
  126. package/lib/components/theme-toggle.ts +0 -300
  127. package/lib/components/token-calculator.ts +0 -313
  128. package/lib/components/tooltip.ts +0 -144
  129. package/lib/components/view.ts +0 -190
  130. package/lib/components/write.ts +0 -272
  131. package/lib/jux.ts +0 -365
  132. package/lib/layouts/default.css +0 -260
  133. package/lib/layouts/figma.css +0 -334
  134. package/lib/reactivity/state.ts +0 -78
  135. package/machinery/bundleAssets.js +0 -0
  136. package/machinery/bundleJux.js +0 -0
  137. package/machinery/bundleVendors.js +0 -0
  138. package/presets/default/all.jux +0 -343
  139. package/presets/default/index.jux +0 -90
  140. package/presets/default/layout.jux +0 -57
  141. package/presets/default/style.css +0 -1612
@@ -0,0 +1,359 @@
1
+ .jux-list {
2
+ list-style: none; /* Default reset */
3
+ padding: 0;
4
+ margin: 0;
5
+ }
6
+ .jux-list.dark{
7
+ background: blue;
8
+ }
9
+ /* Explicit type styling based on dynamic class */
10
+ .jux-list-type-ol {
11
+ list-style: decimal;
12
+ padding-left: 1.5rem; /* Make room for numbers */
13
+ }
14
+
15
+ .jux-list-type-ul {
16
+ list-style: none;
17
+ }
18
+
19
+ .jux-list-selectable .jux-list-item {
20
+ cursor: pointer;
21
+ }
22
+
23
+ /* Visual reflection of the disabled state */
24
+ .jux-list[aria-disabled="true"] {
25
+ opacity: 0.5;
26
+ pointer-events: none;
27
+ cursor: not-allowed;
28
+ user-select: none;
29
+ }
30
+
31
+ .jux-list-item {
32
+ padding: var(--space-md) var(--space-lg);
33
+ border-bottom: var(--border-width);
34
+ border-color: var(--color-border);
35
+ transition: background-color var(--transition-fast);
36
+ margin: var(--space-sm);
37
+ cursor: default; /* Base cursor */
38
+ /* ✨ Uplift: Flex layout for content <-> controls separation */
39
+ display: flex;
40
+ justify-content: space-between;
41
+ align-items: center;
42
+ }
43
+ .jux-list-item-highlight{
44
+
45
+ background-color: var(--color-surface-secondary-subtle, #ee76781a);
46
+
47
+ }
48
+ /* Standard Hover Effect (Native CSS) */
49
+ /* Apply to all items that are NOT disabled */
50
+ .jux-list:not([aria-disabled="true"]) .jux-list-item:not(.jux-selected):hover {
51
+ background-color: var(--color-surface-hover, #f5f5f5);
52
+ border-radius: var(--radius-sm);
53
+ }
54
+
55
+ .jux-list-item:last-child {
56
+ border-bottom: none;
57
+ }
58
+
59
+ /* REMOVED: .jux-list-item-hoverable logic */
60
+
61
+ .jux-list-item-icon {
62
+ display: inline-flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ width: 24px;
66
+ min-width: 24px;
67
+ height: 24px;
68
+ flex-shrink: 0;
69
+ margin-right: var(--space-lg);
70
+ color: var(--color-text-secondary);
71
+ }
72
+
73
+ .jux-list-item-content {
74
+ display: flex;
75
+ flex-direction: row;
76
+ align-items: center;
77
+ gap: var(--space-lg);
78
+ flex: 1; /* Take available width */
79
+ }
80
+
81
+ /* Selected State Styling */
82
+ .jux-selected {
83
+ background-color: var(--color-surface-active, #f0f7ff); /* fallback light blue */
84
+ border-color: var(--color-primary, #0066cc);
85
+ font-weight: 500;
86
+ color: var(--color-primary, #0066cc);
87
+ }
88
+
89
+ /* Checkmark indicator for selected items */
90
+ .jux-selected .jux-list-item-content::before {
91
+ content: "✓";
92
+ display: inline-flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ width: 1.25rem;
96
+ height: 1.25rem;
97
+ margin-right: 0.5rem;
98
+ color: inherit;
99
+ font-weight: bold;
100
+ }
101
+
102
+ /* ═════════════════════════════════════════════════════════════════
103
+ ✨ CONTROLS (Edit/Delete)
104
+ ═════════════════════════════════════════════════════════════════ */
105
+ .jux-item-controls {
106
+ display: flex;
107
+ gap: var(--space-sm);
108
+ opacity: 0;
109
+ transform: translateX(10px);
110
+ transition: opacity 0.2s, transform 0.2s;
111
+ }
112
+
113
+ /* Uplift: Only show controls on hover for a cleaner UI */
114
+ .jux-list-item:hover .jux-item-controls {
115
+ opacity: 1;
116
+ transform: translateX(0);
117
+ }
118
+
119
+ .jux-control {
120
+ background: transparent;
121
+ border: none;
122
+ cursor: pointer;
123
+ opacity: 0.5;
124
+ transition: opacity 0.2s, background-color 0.2s;
125
+ font-size: 1.1rem;
126
+ padding: 4px 8px;
127
+ border-radius: var(--radius-sm);
128
+ display: flex;
129
+ align-items: center;
130
+ justify-content: center;
131
+ }
132
+
133
+ .jux-control:hover { opacity: 1; background-color: var(--color-surface-hover); }
134
+ .jux-action-delete:hover { color: #d32f2f; background-color: #fee2e2; }
135
+ .jux-action-edit:hover { color: var(--color-primary, #0066cc); background-color: #e0f2fe; }
136
+
137
+ /* ═════════════════════════════════════════════════════════════════
138
+ ✨ MODAL (Uplifted with Animations & Tokens)
139
+ ═════════════════════════════════════════════════════════════════ */
140
+ .jux-modal-overlay {
141
+ position: fixed;
142
+ top: 0; left: 0; right: 0; bottom: 0;
143
+ background: rgba(0, 0, 0, 0.4);
144
+ backdrop-filter: blur(4px); /* Glass effect */
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ z-index: 1000;
149
+ opacity: 0;
150
+ animation: jux-fade-in 0.2s forwards;
151
+ }
152
+
153
+ .jux-modal {
154
+ background: var(--color-surface-elevated, #fff);
155
+ padding: var(--space-xl);
156
+ border-radius: var(--radius-sm);
157
+ box-shadow: 0 10px 40px rgba(0,0,0,0.2);
158
+ min-width: 360px;
159
+ display: flex;
160
+ flex-direction: column;
161
+ gap: var(--space-lg);
162
+ transform: scale(0.95);
163
+ opacity: 0;
164
+ animation: jux-scale-in 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards;
165
+ }
166
+
167
+ .jux-modal h3 {
168
+ margin: 0;
169
+ font-size: var(--font-size-lg);
170
+ font-weight: 600;
171
+ color: var(--color-text-primary);
172
+ }
173
+
174
+ .jux-modal input {
175
+ padding: var(--space-md);
176
+ border: 1px solid var(--color-border);
177
+ border-radius: var(--radius-md);
178
+ font-size: var(--font-size-base);
179
+ width: 100%;
180
+ outline: none;
181
+ transition: border-color 0.2s, box-shadow 0.2s;
182
+ background: var(--color-surface-base);
183
+ }
184
+
185
+ .jux-modal input:focus {
186
+ border-color: var(--color-brand, #085156);
187
+ box-shadow: 0 0 0 3px var(--color-brand-subtle);
188
+ background: var(--color-background);
189
+ }
190
+
191
+ .jux-modal-actions {
192
+ display: flex;
193
+ justify-content: flex-end;
194
+ gap: var(--space-md);
195
+ margin-top: var(--space-sm);
196
+ }
197
+
198
+ .jux-modal-btn {
199
+ padding: 0.6rem 1.25rem;
200
+ cursor: pointer;
201
+ border: none;
202
+ border-radius: var(--radius-sm);
203
+ font-weight: 500;
204
+ font-size: var(--font-size-sm);
205
+ transition: all 0.2s;
206
+ }
207
+
208
+ .jux-btn-cancel {
209
+ background: transparent;
210
+ color: var(--color-text-secondary);
211
+ }
212
+ .jux-btn-cancel:hover {
213
+ background: var(--color-surface-hover);
214
+ color: var(--color-text-primary);
215
+ }
216
+
217
+ .jux-btn-save {
218
+ background: var(--color-brand, #085156);
219
+ color: #fff;
220
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
221
+ }
222
+ .jux-btn-save:hover {
223
+ background: var(--color-brand-hover, #066666);
224
+ transform: translateY(-1px);
225
+ box-shadow: 0 4px 8px rgba(0,0,0,0.15);
226
+ }
227
+
228
+ @keyframes jux-fade-in { from { opacity: 0; } to { opacity: 1; } }
229
+ @keyframes jux-scale-in { from { transform: scale(0.95); opacity: 0; } to { transform: scale(1); opacity: 1; } }
230
+
231
+ /* ═════════════════════════════════════════════════════════════════
232
+ ✨ TOOLBAR (Search & Actions)
233
+ ═════════════════════════════════════════════════════════════════ */
234
+ .jux-list-toolbar {
235
+ display: flex;
236
+ gap: var(--space-sm);
237
+ padding: var(--space-md);
238
+ background-color: var(--color-surface-base);
239
+ border-bottom: var(--border-width);
240
+ border-color: var(--color-border);
241
+ border-radius: var(--radius-sm) var(--radius-sm) 0 0;
242
+ }
243
+
244
+ .jux-search-input {
245
+ flex: 1;
246
+ padding: var(--space-sm) var(--space-md);
247
+ border: 1px solid var(--color-border);
248
+ border-radius: var(--radius-sm);
249
+ font-family: inherit;
250
+ font-size: var(--font-size-sm);
251
+ outline: none;
252
+ transition: box-shadow 0.2s, border-color 0.2s;
253
+ }
254
+
255
+ .jux-search-input:focus {
256
+ border-color: var(--color-brand, #085156);
257
+ box-shadow: 0 0 0 2px var(--color-brand-subtle);
258
+ }
259
+
260
+ .jux-action-add {
261
+ display: inline-flex;
262
+ align-items: center;
263
+ justify-content: center;
264
+ width: 36px;
265
+ height: 36px;
266
+ background-color: var(--color-surface-elevated, #fff);
267
+ border: 1px solid var(--color-border);
268
+ border-radius: var(--radius-sm);
269
+ cursor: pointer;
270
+ font-size: 1.25rem;
271
+ line-height: 1;
272
+ color: var(--color-text-secondary);
273
+ transition: all 0.2s;
274
+ }
275
+
276
+ .jux-action-add:hover {
277
+ background-color: var(--color-surface-hover);
278
+ color: var(--color-brand);
279
+ border-color: var(--color-brand-subtle);
280
+ }
281
+
282
+ .jux-action-add:active {
283
+ transform: translateY(1px);
284
+ }
285
+
286
+ .jux-action-sort {
287
+ display: inline-flex;
288
+ align-items: center;
289
+ justify-content: center;
290
+ width: 36px;
291
+ height: 36px;
292
+ background-color: var(--color-surface-elevated, #fff);
293
+ border: 1px solid var(--color-border);
294
+ border-radius: var(--radius-sm);
295
+ cursor: pointer;
296
+ font-size: 1.1rem;
297
+ line-height: 1;
298
+ color: var(--color-text-secondary);
299
+ transition: all 0.2s;
300
+ }
301
+
302
+ .jux-action-sort:hover {
303
+ background-color: var(--color-surface-hover);
304
+ color: var(--color-brand);
305
+ border-color: var(--color-brand-subtle);
306
+ }
307
+
308
+ .jux-action-sort:active {
309
+ transform: translateY(1px);
310
+ }
311
+
312
+ /* ═════════════════════════════════════════════════════════════════
313
+ ✨ DRAG & DROP
314
+ ═════════════════════════════════════════════════════════════════ */
315
+ .jux-list-item[draggable="true"] {
316
+ cursor: grab;
317
+ }
318
+ .jux-list-item[draggable="true"]:active {
319
+ cursor: grabbing;
320
+ }
321
+
322
+ /* Aspect of the item currently being dragged (in the list, not the ghost) */
323
+ .jux-list-item.jux-dragging {
324
+ opacity: 0.4;
325
+ background-color: var(--color-surface-secondary);
326
+ border: 1px dashed var(--color-brand);
327
+ }
328
+
329
+ /* Aspect of the item under the cursor (drop target) */
330
+ .jux-item-drop-target {
331
+ border-top: 2px solid var(--color-brand);
332
+ transition: none; /* Instant feedback */
333
+ }
334
+
335
+ .jux-action-drag {
336
+ cursor: grab;
337
+ font-size: 1.25rem;
338
+ line-height: 1;
339
+ color: var(--color-text-secondary);
340
+ display: flex;
341
+ align-items: center;
342
+ }
343
+ .jux-action-drag:active {
344
+ cursor: grabbing;
345
+ }
346
+
347
+ /* ═════════════════════════════════════════════════════════════════
348
+ ✨ EMPTY STATE
349
+ ═════════════════════════════════════════════════════════════════ */
350
+ .jux-list-empty-state {
351
+ padding: var(--space-xl);
352
+ text-align: center;
353
+ color: var(--color-text-secondary);
354
+ font-style: italic;
355
+ background-color: var(--color-surface-base);
356
+ border-radius: var(--radius-sm);
357
+ margin: var(--space-sm);
358
+ }
359
+
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Client-Side SQLite Plugin
3
+ * Runs a real SQLite engine via WebAssembly (WASM) in the browser, persisted to IndexedDB.
4
+ */
5
+ export const ClientSQLitePlugin = (config) => ({
6
+ name: 'client-sqlite-wasm',
7
+ version: '1.0.0',
8
+ targetEnv: 'client',
9
+ install: (engine) => {
10
+ let db = null;
11
+ const IDB_NAME = 'JuxSqliteStorage';
12
+ const IDB_STORE = 'files';
13
+ // --- 1. IndexedDB HELPER (The "Hard Drive") ---
14
+ const getFile = (key) => {
15
+ return new Promise((resolve) => {
16
+ const r = indexedDB.open(IDB_NAME, 1);
17
+ r.onupgradeneeded = (e) => e.target.result.createObjectStore(IDB_STORE);
18
+ r.onsuccess = (e) => {
19
+ const t = e.target.result.transaction([IDB_STORE], 'readonly').objectStore(IDB_STORE).get(key);
20
+ t.onsuccess = () => resolve(t.result);
21
+ t.onerror = () => resolve(null);
22
+ };
23
+ });
24
+ };
25
+ const saveFile = (key, data) => {
26
+ const r = indexedDB.open(IDB_NAME, 1);
27
+ r.onsuccess = (e) => {
28
+ const tx = e.target.result.transaction([IDB_STORE], 'readwrite');
29
+ tx.objectStore(IDB_STORE).put(data, key);
30
+ };
31
+ };
32
+ // --- 2. SQL Helper (Result Transformation) ---
33
+ // sql.js return format: [{ columns: ['id', 'name'], values: [[1, 'Bob']] }]
34
+ // We convert this to: [{ id: 1, name: 'Bob' }]
35
+ const normalizeResults = (res) => {
36
+ if (!res || !res.length)
37
+ return [];
38
+ const columns = res[0].columns;
39
+ const values = res[0].values;
40
+ return values.map((row) => {
41
+ const obj = {};
42
+ columns.forEach((col, i) => {
43
+ obj[col] = row[i];
44
+ });
45
+ return obj;
46
+ });
47
+ };
48
+ // --- 3. REFRESH LOGIC ---
49
+ const refresh = () => {
50
+ if (!db || !config.query || !config.bindTo)
51
+ return;
52
+ try {
53
+ // @ts-ignore
54
+ if (engine.loading)
55
+ engine.loading(true);
56
+ // Run Query
57
+ const raw = db.exec(config.query);
58
+ const rows = normalizeResults(raw);
59
+ // Map & Update State
60
+ const items = config.mapRow ? rows.map(config.mapRow) : rows;
61
+ // @ts-ignore
62
+ engine.updateState({ [config.bindTo]: items });
63
+ // @ts-ignore
64
+ if (engine.loading)
65
+ engine.loading(false);
66
+ // @ts-ignore
67
+ if (engine.emit)
68
+ engine.emit('sql:refresh', { count: items.length });
69
+ }
70
+ catch (e) {
71
+ console.error('[ClientSQLite] Query Error:', e);
72
+ // @ts-ignore
73
+ if (engine.loading)
74
+ engine.loading(false);
75
+ }
76
+ };
77
+ // --- 4. INITIALIZATION ---
78
+ const loadSqlJs = async () => {
79
+ // Lazy load sql.js from CDN if not present
80
+ if (!window.initSqlJs) {
81
+ await new Promise(resolve => {
82
+ const script = document.createElement('script');
83
+ // sql-wasm.js is the glue code that loads the .wasm binary
84
+ script.src = 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js';
85
+ script.onload = resolve;
86
+ document.head.appendChild(script);
87
+ });
88
+ }
89
+ const SQL = await window.initSqlJs({
90
+ locateFile: (file) => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/${file}`
91
+ });
92
+ // "Mount" the file from IDB
93
+ const binary = await getFile(config.dbName);
94
+ if (binary) {
95
+ db = new SQL.Database(binary);
96
+ // @ts-ignore
97
+ if (engine.emit)
98
+ engine.emit('sql:mounted', { size: binary.length });
99
+ }
100
+ else {
101
+ db = new SQL.Database();
102
+ if (config.initSql)
103
+ db.run(config.initSql);
104
+ if (config.autoSave)
105
+ saveFile(config.dbName, db.export());
106
+ // @ts-ignore
107
+ if (engine.emit)
108
+ engine.emit('sql:created');
109
+ }
110
+ // Expose SQL methods to Engine (Mixins)
111
+ Object.assign(engine, {
112
+ sqlRun: (sql) => {
113
+ db.run(sql);
114
+ if (config.autoSave)
115
+ saveFile(config.dbName, db.export());
116
+ refresh();
117
+ },
118
+ sqlExec: (sql) => {
119
+ return normalizeResults(db.exec(sql));
120
+ },
121
+ sqlExport: () => {
122
+ return db.export();
123
+ }
124
+ });
125
+ refresh();
126
+ };
127
+ loadSqlJs();
128
+ }
129
+ });
130
+ //# sourceMappingURL=ClientSQLitePlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClientSQLitePlugin.js","sourceRoot":"","sources":["ClientSQLitePlugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,154 @@
1
+ import { JuxServiceContract, BaseEngine } from '../base/BaseEngine.js';
2
+
3
+ export interface ClientSQLiteConfig {
4
+ /** Key used for IndexedDB storage (the "filename") */
5
+ dbName: string;
6
+
7
+ /** SQL to run if creating a brand new DB (Schema) */
8
+ initSql?: string;
9
+
10
+ /** Main Query to bind to the Engine State */
11
+ query?: string;
12
+
13
+ /** State property to bind results to (e.g. 'items') */
14
+ bindTo?: string;
15
+
16
+ /** Transform row object to State Item shape */
17
+ mapRow?: (row: any) => any;
18
+
19
+ /** If true, persists to IDB after every write operation */
20
+ autoSave?: boolean;
21
+ }
22
+
23
+ /**
24
+ * Client-Side SQLite Plugin
25
+ * Runs a real SQLite engine via WebAssembly (WASM) in the browser, persisted to IndexedDB.
26
+ */
27
+ export const ClientSQLitePlugin = (config: ClientSQLiteConfig): JuxServiceContract<BaseEngine<any>> => ({
28
+ name: 'client-sqlite-wasm',
29
+ version: '1.0.0',
30
+ targetEnv: 'client',
31
+
32
+ install: (engine) => {
33
+ let db: any = null;
34
+ const IDB_NAME = 'JuxSqliteStorage';
35
+ const IDB_STORE = 'files';
36
+
37
+ // --- 1. IndexedDB HELPER (The "Hard Drive") ---
38
+ const getFile = (key: string): Promise<Uint8Array | null> => {
39
+ return new Promise((resolve) => {
40
+ const r = indexedDB.open(IDB_NAME, 1);
41
+ r.onupgradeneeded = (e: any) => e.target.result.createObjectStore(IDB_STORE);
42
+ r.onsuccess = (e: any) => {
43
+ const t = e.target.result.transaction([IDB_STORE], 'readonly').objectStore(IDB_STORE).get(key);
44
+ t.onsuccess = () => resolve(t.result);
45
+ t.onerror = () => resolve(null);
46
+ };
47
+ });
48
+ };
49
+
50
+ const saveFile = (key: string, data: Uint8Array) => {
51
+ const r = indexedDB.open(IDB_NAME, 1);
52
+ r.onsuccess = (e: any) => {
53
+ const tx = e.target.result.transaction([IDB_STORE], 'readwrite');
54
+ tx.objectStore(IDB_STORE).put(data, key);
55
+ };
56
+ };
57
+
58
+ // --- 2. SQL Helper (Result Transformation) ---
59
+ // sql.js return format: [{ columns: ['id', 'name'], values: [[1, 'Bob']] }]
60
+ // We convert this to: [{ id: 1, name: 'Bob' }]
61
+ const normalizeResults = (res: any[]) => {
62
+ if (!res || !res.length) return [];
63
+ const columns = res[0].columns;
64
+ const values = res[0].values;
65
+ return values.map((row: any[]) => {
66
+ const obj: any = {};
67
+ columns.forEach((col: string, i: number) => {
68
+ obj[col] = row[i];
69
+ });
70
+ return obj;
71
+ });
72
+ };
73
+
74
+ // --- 3. REFRESH LOGIC ---
75
+ const refresh = () => {
76
+ if (!db || !config.query || !config.bindTo) return;
77
+ try {
78
+ // @ts-ignore
79
+ if (engine.loading) engine.loading(true);
80
+
81
+ // Run Query
82
+ const raw = db.exec(config.query);
83
+ const rows = normalizeResults(raw);
84
+
85
+ // Map & Update State
86
+ const items = config.mapRow ? rows.map(config.mapRow) : rows;
87
+ // @ts-ignore
88
+ engine.updateState({ [config.bindTo]: items });
89
+
90
+ // @ts-ignore
91
+ if (engine.loading) engine.loading(false);
92
+ // @ts-ignore
93
+ if (engine.emit) engine.emit('sql:refresh', { count: items.length });
94
+
95
+ } catch (e) {
96
+ console.error('[ClientSQLite] Query Error:', e);
97
+ // @ts-ignore
98
+ if (engine.loading) engine.loading(false);
99
+ }
100
+ };
101
+
102
+ // --- 4. INITIALIZATION ---
103
+ const loadSqlJs = async () => {
104
+ // Lazy load sql.js from CDN if not present
105
+ if (!(window as any).initSqlJs) {
106
+ await new Promise(resolve => {
107
+ const script = document.createElement('script');
108
+ // sql-wasm.js is the glue code that loads the .wasm binary
109
+ script.src = 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js';
110
+ script.onload = resolve;
111
+ document.head.appendChild(script);
112
+ });
113
+ }
114
+
115
+ const SQL = await (window as any).initSqlJs({
116
+ locateFile: (file: string) => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/${file}`
117
+ });
118
+
119
+ // "Mount" the file from IDB
120
+ const binary = await getFile(config.dbName);
121
+
122
+ if (binary) {
123
+ db = new SQL.Database(binary);
124
+ // @ts-ignore
125
+ if (engine.emit) engine.emit('sql:mounted', { size: binary.length });
126
+ } else {
127
+ db = new SQL.Database();
128
+ if (config.initSql) db.run(config.initSql);
129
+ if (config.autoSave) saveFile(config.dbName, db.export());
130
+ // @ts-ignore
131
+ if (engine.emit) engine.emit('sql:created');
132
+ }
133
+
134
+ // Expose SQL methods to Engine (Mixins)
135
+ Object.assign(engine, {
136
+ sqlRun: (sql: string) => {
137
+ db.run(sql);
138
+ if (config.autoSave) saveFile(config.dbName, db.export());
139
+ refresh();
140
+ },
141
+ sqlExec: (sql: string) => {
142
+ return normalizeResults(db.exec(sql));
143
+ },
144
+ sqlExport: () => {
145
+ return db.export();
146
+ }
147
+ });
148
+
149
+ refresh();
150
+ };
151
+
152
+ loadSqlJs();
153
+ }
154
+ });