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
@@ -1,242 +0,0 @@
1
- import { FormInput, FormInputState } from './base/FormInput.js';
2
- import { renderIcon } from './icons.js';
3
-
4
- // Event definitions
5
- const TRIGGER_EVENTS = [] as const;
6
- const CALLBACK_EVENTS = ['change'] as const;
7
-
8
- export interface CheckboxOptions {
9
- checked?: boolean;
10
- label?: string;
11
- required?: boolean;
12
- disabled?: boolean;
13
- name?: string;
14
- value?: string;
15
- style?: string;
16
- class?: string;
17
- onValidate?: (checked: boolean) => boolean | string;
18
- }
19
-
20
- interface CheckboxState extends FormInputState {
21
- checked: boolean;
22
- value: string;
23
- }
24
-
25
- export class Checkbox extends FormInput<CheckboxState> {
26
- constructor(id: string, options: CheckboxOptions = {}) {
27
- super(id, {
28
- checked: options.checked ?? false,
29
- value: options.value ?? 'on',
30
- label: options.label ?? '',
31
- required: options.required ?? false,
32
- disabled: options.disabled ?? false,
33
- name: options.name ?? id,
34
- style: options.style ?? '',
35
- class: options.class ?? '',
36
- errorMessage: undefined
37
- });
38
-
39
- if (options.onValidate) {
40
- this._onValidate = options.onValidate;
41
- }
42
- }
43
-
44
- protected getTriggerEvents(): readonly string[] {
45
- return TRIGGER_EVENTS;
46
- }
47
-
48
- protected getCallbackEvents(): readonly string[] {
49
- return CALLBACK_EVENTS;
50
- }
51
-
52
- /* ═════════════════════════════════════════════════════════════════
53
- * FLUENT API
54
- * ═════════════════════════════════════════════════════════════════ */
55
-
56
- // ✅ Inherited from FormInput/BaseComponent:
57
- // - label(), required(), name(), onValidate()
58
- // - validate(), isValid()
59
- // - style(), class()
60
- // - bind(), sync(), renderTo()
61
- // - disabled(), enable(), disable()
62
-
63
- checked(value: boolean): this {
64
- return this.setValue(value);
65
- }
66
-
67
- value(value: string): this {
68
- this.state.value = value;
69
- return this;
70
- }
71
-
72
- /* ═════════════════════════════════════════════════════════════════
73
- * FORM INPUT IMPLEMENTATION
74
- * ═════════════════════════════════════════════════════════════════ */
75
-
76
- getValue(): boolean {
77
- return this.state.checked;
78
- }
79
-
80
- setValue(value: boolean): this {
81
- this.state.checked = value;
82
- if (this._inputElement) {
83
- (this._inputElement as HTMLInputElement).checked = value;
84
- }
85
- return this;
86
- }
87
-
88
- protected _validateValue(checked: boolean): boolean | string {
89
- const { required } = this.state;
90
-
91
- if (required && !checked) {
92
- return 'This field must be checked';
93
- }
94
-
95
- if (this._onValidate) {
96
- const result = this._onValidate(checked);
97
- if (result !== true) {
98
- return result || 'Invalid value';
99
- }
100
- }
101
-
102
- return true;
103
- }
104
-
105
- protected _buildInputElement(): HTMLElement {
106
- const { checked, value, required, disabled, name } = this.state;
107
-
108
- const input = document.createElement('input');
109
- input.type = 'checkbox';
110
- input.className = 'jux-checkbox-input';
111
- input.id = `${this._id}-input`;
112
- input.name = name;
113
- input.value = value;
114
- input.checked = checked;
115
- input.required = required;
116
- input.disabled = disabled;
117
-
118
- return input;
119
- }
120
-
121
- /* ═════════════════════════════════════════════════════════════════
122
- * RENDER
123
- * ═════════════════════════════════════════════════════════════════ */
124
-
125
- render(targetId?: string): this {
126
- const container = this._setupContainer(targetId);
127
-
128
- const { style, class: className } = this.state;
129
-
130
- // Build wrapper
131
- const wrapper = document.createElement('div');
132
- wrapper.className = 'jux-checkbox';
133
- wrapper.id = this._id;
134
- if (className) wrapper.className += ` ${className}`;
135
- if (style) wrapper.setAttribute('style', style);
136
-
137
- // Checkbox container
138
- const checkboxContainer = document.createElement('label');
139
- checkboxContainer.className = 'jux-checkbox-container';
140
- checkboxContainer.htmlFor = `${this._id}-input`;
141
-
142
- // Input element
143
- const inputEl = this._buildInputElement() as HTMLInputElement;
144
- this._inputElement = inputEl;
145
- checkboxContainer.appendChild(inputEl);
146
-
147
- // Checkmark (custom styled)
148
- const checkmark = document.createElement('span');
149
- checkmark.className = 'jux-checkbox-checkmark';
150
- checkmark.appendChild(renderIcon('check'));
151
- checkboxContainer.appendChild(checkmark);
152
-
153
- // Label text
154
- if (this.state.label) {
155
- const labelText = document.createElement('span');
156
- labelText.className = 'jux-checkbox-label-text';
157
- labelText.textContent = this.state.label;
158
- if (this.state.required) {
159
- const requiredSpan = document.createElement('span');
160
- requiredSpan.className = 'jux-input-required';
161
- requiredSpan.textContent = ' *';
162
- labelText.appendChild(requiredSpan);
163
- }
164
- checkboxContainer.appendChild(labelText);
165
- }
166
-
167
- wrapper.appendChild(checkboxContainer);
168
-
169
- // Error element
170
- wrapper.appendChild(this._renderError());
171
-
172
- // Wire events
173
- this._wireStandardEvents(wrapper);
174
-
175
- // Wire checkbox-specific sync (maps 'checked' to 'value' property for consistency)
176
- const valueSync = this._syncBindings.find(b => b.property === 'value' || b.property === 'checked');
177
-
178
- if (valueSync) {
179
- const { stateObj, toState, toComponent } = valueSync;
180
-
181
- const transformToState = toState || ((v: boolean) => v);
182
- const transformToComponent = toComponent || ((v: any) => Boolean(v));
183
-
184
- let isUpdating = false;
185
-
186
- // State → Component
187
- stateObj.subscribe((val: any) => {
188
- if (isUpdating) return;
189
- const transformed = transformToComponent(val);
190
- this.setValue(transformed);
191
- });
192
-
193
- // Component → State
194
- inputEl.addEventListener('change', () => {
195
- if (isUpdating) return;
196
- isUpdating = true;
197
-
198
- const checked = inputEl.checked;
199
- this.state.checked = checked;
200
- this._clearError();
201
-
202
- const transformed = transformToState(checked);
203
- stateObj.set(transformed);
204
-
205
- // 🎯 Fire the change callback event
206
- this._triggerCallback('change', checked);
207
-
208
- setTimeout(() => { isUpdating = false; }, 0);
209
- });
210
- } else {
211
- // Default behavior without sync
212
- inputEl.addEventListener('change', () => {
213
- this.state.checked = inputEl.checked;
214
- this._clearError();
215
-
216
- // 🎯 Fire the change callback event
217
- this._triggerCallback('change', inputEl.checked);
218
- });
219
- }
220
-
221
- // Always add blur validation
222
- inputEl.addEventListener('blur', () => {
223
- this.validate();
224
- });
225
-
226
- // Sync label changes
227
- const labelSync = this._syncBindings.find(b => b.property === 'label');
228
- if (labelSync) {
229
- const transform = labelSync.toComponent || ((v: any) => String(v));
230
- labelSync.stateObj.subscribe((val: any) => {
231
- this.label(transform(val));
232
- });
233
- }
234
-
235
- container.appendChild(wrapper);
236
- return this;
237
- }
238
- }
239
-
240
- export function checkbox(id: string, options: CheckboxOptions = {}): Checkbox {
241
- return new Checkbox(id, options);
242
- }
@@ -1,123 +0,0 @@
1
- import { BaseComponent } from './base/BaseComponent.js';
2
-
3
- // Event definitions
4
- const TRIGGER_EVENTS = [] as const;
5
- const CALLBACK_EVENTS = [] as const;
6
-
7
- export interface CodeOptions {
8
- code?: string;
9
- language?: string;
10
- style?: string;
11
- class?: string;
12
- }
13
-
14
- type CodeState = {
15
- code: string;
16
- language: string;
17
- style: string;
18
- class: string;
19
- };
20
-
21
- export class Code extends BaseComponent<CodeState> {
22
- constructor(id: string, options: CodeOptions = {}) {
23
- super(id, {
24
- code: options.code ?? '',
25
- language: options.language ?? 'javascript',
26
- style: options.style ?? '',
27
- class: options.class ?? ''
28
- });
29
- }
30
-
31
- protected getTriggerEvents(): readonly string[] {
32
- return TRIGGER_EVENTS;
33
- }
34
-
35
- protected getCallbackEvents(): readonly string[] {
36
- return CALLBACK_EVENTS;
37
- }
38
-
39
- /* ═════════════════════════════════════════════════════════════════
40
- * FLUENT API
41
- * ═════════════════════════════════════════════════════════════════ */
42
-
43
- // ✅ Inherited from BaseComponent
44
-
45
- code(value: string): this {
46
- this.state.code = value;
47
- return this;
48
- }
49
-
50
- language(value: string): this {
51
- this.state.language = value;
52
- return this;
53
- }
54
-
55
- /* ═════════════════════════════════════════════════════════════════
56
- * RENDER
57
- * ═════════════════════════════════════════════════════════════════ */
58
-
59
- render(targetId?: string): this {
60
- const container = this._setupContainer(targetId);
61
-
62
- const { code, language, style, class: className } = this.state;
63
-
64
- const wrapper = document.createElement('div');
65
- wrapper.className = 'jux-code';
66
- wrapper.id = this._id;
67
- if (className) wrapper.className += ` ${className}`;
68
- if (style) wrapper.setAttribute('style', style);
69
-
70
- const pre = document.createElement('pre');
71
- const codeEl = document.createElement('code');
72
- codeEl.className = `language-${language}`;
73
- codeEl.textContent = code;
74
- pre.appendChild(codeEl);
75
- wrapper.appendChild(pre);
76
-
77
- this._wireStandardEvents(wrapper);
78
-
79
- // Wire sync bindings
80
- this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
81
- if (property === 'code') {
82
- const transform = toComponent || ((v: any) => String(v));
83
-
84
- stateObj.subscribe((val: any) => {
85
- const transformed = transform(val);
86
- codeEl.textContent = transformed;
87
- this.state.code = transformed;
88
-
89
- if ((window as any).Prism) {
90
- (window as any).Prism.highlightElement(codeEl);
91
- }
92
- });
93
- }
94
- else if (property === 'language') {
95
- const transform = toComponent || ((v: any) => String(v));
96
-
97
- stateObj.subscribe((val: any) => {
98
- const transformed = transform(val);
99
- codeEl.className = `language-${transformed}`;
100
- this.state.language = transformed;
101
-
102
- if ((window as any).Prism) {
103
- (window as any).Prism.highlightElement(codeEl);
104
- }
105
- });
106
- }
107
- });
108
-
109
- container.appendChild(wrapper);
110
-
111
- requestAnimationFrame(() => {
112
- if ((window as any).Prism) {
113
- (window as any).Prism.highlightElement(codeEl);
114
- }
115
- });
116
-
117
- return this;
118
- }
119
- }
120
-
121
- export function code(id: string, options: CodeOptions = {}): Code {
122
- return new Code(id, options);
123
- }
@@ -1,140 +0,0 @@
1
- import { BaseComponent } from './base/BaseComponent.js';
2
-
3
- // Event definitions - Container is a layout wrapper that can be clicked
4
- const TRIGGER_EVENTS = [] as const;
5
- const CALLBACK_EVENTS = ['click'] as const;
6
-
7
- /**
8
- * Container options
9
- */
10
- export interface ContainerOptions {
11
- class?: string;
12
- style?: string;
13
- direction?: 'row' | 'column';
14
- gap?: string | number;
15
- align?: 'start' | 'center' | 'end' | 'stretch';
16
- justify?: 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly';
17
- }
18
-
19
- /**
20
- * Container state
21
- */
22
- type ContainerState = {
23
- class: string;
24
- style: string;
25
- direction?: 'row' | 'column';
26
- gap?: string | number;
27
- align?: string;
28
- justify?: string;
29
- };
30
-
31
- /**
32
- * Container component - a simple div container for grouping elements
33
- */
34
- export class Container extends BaseComponent<ContainerState> {
35
- constructor(id: string, options: ContainerOptions = {}) {
36
- super(id, {
37
- class: options.class ?? '',
38
- style: options.style ?? '',
39
- direction: options.direction,
40
- gap: options.gap,
41
- align: options.align,
42
- justify: options.justify
43
- });
44
- }
45
-
46
- protected getTriggerEvents(): readonly string[] {
47
- return TRIGGER_EVENTS;
48
- }
49
-
50
- protected getCallbackEvents(): readonly string[] {
51
- return CALLBACK_EVENTS;
52
- }
53
-
54
- /* -------------------------
55
- * Fluent API
56
- * ------------------------- */
57
-
58
- // ✅ Inherited from BaseComponent:
59
- // - style(), class()
60
- // - bind(), sync(), renderTo()
61
- // - addClass(), removeClass(), toggleClass()
62
- // - visible(), show(), hide()
63
- // - attr(), attrs(), removeAttr()
64
- // - disabled(), enable(), disable()
65
-
66
- direction(value: 'row' | 'column'): this {
67
- this.state.direction = value;
68
- return this;
69
- }
70
-
71
- gap(value: string | number): this {
72
- this.state.gap = value;
73
- return this;
74
- }
75
-
76
- align(value: 'start' | 'center' | 'end' | 'stretch'): this {
77
- this.state.align = value;
78
- return this;
79
- }
80
-
81
- justify(value: 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly'): this {
82
- this.state.justify = value;
83
- return this;
84
- }
85
-
86
- /* -------------------------
87
- * Render
88
- * ------------------------- */
89
-
90
- render(targetId?: string): this {
91
- const container = this._setupContainer(targetId);
92
-
93
- const { class: className, style, direction, gap, align, justify } = this.state;
94
-
95
- const div = document.createElement('div');
96
- div.id = this._id;
97
-
98
- // Always include jux-container class, append custom classes
99
- div.className = className ? `jux-container ${className}` : 'jux-container';
100
-
101
- // Only apply flex styles if any flex properties are set
102
- const usesFlexbox = direction || gap !== undefined || align || justify;
103
-
104
- let computedStyle = style;
105
-
106
- if (usesFlexbox) {
107
- const flexStyles: string[] = ['display: flex', 'width: auto'];
108
-
109
- if (direction) flexStyles.push(`flex-direction: ${direction}`);
110
-
111
- if (gap !== undefined) {
112
- const gapValue = typeof gap === 'number' ? `${gap}px` : gap;
113
- flexStyles.push(`gap: ${gapValue}`);
114
- }
115
- // Only set align-items if explicitly specified
116
- if (align) flexStyles.push(`align-items: ${align}`);
117
- // Only set justify-content if explicitly specified
118
- if (justify) flexStyles.push(`justify-content: ${justify}`);
119
-
120
- computedStyle = `${flexStyles.join('; ')}; ${style}`.trim();
121
- }
122
-
123
- if (computedStyle) {
124
- div.setAttribute('style', computedStyle);
125
- }
126
-
127
- this._wireStandardEvents(div);
128
-
129
- container.appendChild(div);
130
-
131
- return this;
132
- }
133
- }
134
-
135
- /**
136
- * Factory helper
137
- */
138
- export function container(id: string, options: ContainerOptions = {}): Container {
139
- return new Container(id, options);
140
- }
@@ -1,135 +0,0 @@
1
- /**
2
- * Data component - SQL query execution
3
- * Note: No id needed - this is a data-only component
4
- *
5
- * Usage:
6
- * const posts = jux.data('SELECT * FROM posts WHERE id = ?', [1]);
7
- * await posts.execute();
8
- * console.log(posts.data);
9
- */
10
-
11
- export interface DataOptions {
12
- sql: string;
13
- params?: any[];
14
- apiUrl?: string;
15
- }
16
-
17
- export class Data {
18
- private _data: any[] = [];
19
- private _executed: boolean = false;
20
- private _loading: boolean = false;
21
- private _sql: string;
22
- private _params: any[];
23
- private _apiUrl: string;
24
-
25
- constructor(sql: string, params: any[] = [], apiUrl: string = '/api/query') {
26
- this._sql = sql;
27
- this._params = params;
28
- this._apiUrl = apiUrl;
29
- }
30
-
31
- /* -------------------------
32
- * Getters
33
- * ------------------------- */
34
-
35
- get data(): any[] {
36
- return this._data;
37
- }
38
-
39
- get executed(): boolean {
40
- return this._executed;
41
- }
42
-
43
- get loading(): boolean {
44
- return this._loading;
45
- }
46
-
47
- get sql(): string {
48
- return this._sql;
49
- }
50
-
51
- set sql(value: string) {
52
- this._sql = value;
53
- }
54
-
55
- /* -------------------------
56
- * Methods
57
- * ------------------------- */
58
-
59
- async execute(): Promise<void> {
60
- if (this._executed) return;
61
- this._loading = true;
62
-
63
- try {
64
- const response = await fetch(this._apiUrl, {
65
- method: 'POST',
66
- headers: {
67
- 'Content-Type': 'application/json'
68
- },
69
- body: JSON.stringify({
70
- sql: this._sql,
71
- params: this._params
72
- })
73
- });
74
-
75
- if (!response.ok) {
76
- throw new Error(`API error: ${response.statusText}`);
77
- }
78
-
79
- const result = await response.json();
80
- this._data = result.data || [];
81
- this._executed = true;
82
- } catch (error) {
83
- console.error('Data execution error:', error);
84
- this._data = [];
85
- throw error;
86
- } finally {
87
- this._loading = false;
88
- }
89
- }
90
-
91
- reset(): void {
92
- this._executed = false;
93
- this._data = [];
94
- this._loading = false;
95
- }
96
-
97
- // Remove id requirement, make it optional for caching
98
- static async fetch<T = any>(url: string, options?: RequestInit): Promise<T> {
99
- const response = await fetch(url, options);
100
- if (!response.ok) {
101
- throw new Error(`API error: ${response.statusText}`);
102
- }
103
- return response.json();
104
- }
105
-
106
- // ✅ Static methods - no ID needed
107
- static async post<T = any>(url: string, body: any, options?: RequestInit): Promise<T> {
108
- const response = await fetch(url, options);
109
- if (!response.ok) {
110
- throw new Error(`API error: ${response.statusText}`);
111
- }
112
- return response.json();
113
- }
114
-
115
- static async put<T = any>(url: string, body: any, options?: RequestInit): Promise<T> {
116
- const response = await fetch(url, options);
117
- if (!response.ok) {
118
- throw new Error(`API error: ${response.statusText}`);
119
- }
120
- return response.json();
121
- }
122
-
123
- static async delete<T = any>(url: string, options?: RequestInit): Promise<T> {
124
- const response = await fetch(url, options);
125
- if (!response.ok) {
126
- throw new Error(`API error: ${response.statusText}`);
127
- }
128
- return response.json();
129
- }
130
- }
131
-
132
- // ✅ No ID needed - pure data fetching
133
- export function data<T = any>(url: string, options?: RequestInit): Promise<T> {
134
- return Data.fetch<T>(url, options);
135
- }