appflare 0.2.47 → 0.2.48

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 (139) hide show
  1. package/Documentation.md +898 -898
  2. package/cli/commands/index.ts +247 -247
  3. package/cli/generate.ts +360 -360
  4. package/cli/index.ts +120 -120
  5. package/cli/load-config.ts +184 -184
  6. package/cli/schema-compiler.ts +1366 -1366
  7. package/cli/templates/auth/README.md +156 -156
  8. package/cli/templates/auth/config.ts +61 -61
  9. package/cli/templates/auth/route-config.ts +1 -1
  10. package/cli/templates/auth/route-handler.ts +1 -1
  11. package/cli/templates/auth/route-request-utils.ts +5 -5
  12. package/cli/templates/auth/route.config.ts +18 -18
  13. package/cli/templates/auth/route.handler.ts +18 -18
  14. package/cli/templates/auth/route.request-utils.ts +55 -55
  15. package/cli/templates/auth/route.ts +14 -14
  16. package/cli/templates/core/README.md +266 -266
  17. package/cli/templates/core/app-creation.ts +19 -19
  18. package/cli/templates/core/client/appflare.ts +112 -112
  19. package/cli/templates/core/client/handlers/index.ts +763 -763
  20. package/cli/templates/core/client/handlers.ts +1 -1
  21. package/cli/templates/core/client/index.ts +7 -7
  22. package/cli/templates/core/client/storage.ts +195 -195
  23. package/cli/templates/core/client/types.ts +187 -187
  24. package/cli/templates/core/client-modules/appflare.ts +1 -1
  25. package/cli/templates/core/client-modules/handlers.ts +1 -1
  26. package/cli/templates/core/client-modules/index.ts +1 -1
  27. package/cli/templates/core/client-modules/storage.ts +1 -1
  28. package/cli/templates/core/client-modules/types.ts +1 -1
  29. package/cli/templates/core/client.artifacts.ts +39 -39
  30. package/cli/templates/core/client.ts +4 -4
  31. package/cli/templates/core/drizzle.ts +15 -15
  32. package/cli/templates/core/export.ts +14 -14
  33. package/cli/templates/core/handlers.route.ts +24 -24
  34. package/cli/templates/core/handlers.ts +1 -1
  35. package/cli/templates/core/imports.ts +9 -9
  36. package/cli/templates/core/server.ts +38 -38
  37. package/cli/templates/core/types.ts +6 -6
  38. package/cli/templates/core/wrangler.ts +109 -109
  39. package/cli/templates/dashboard/builders/functions/index.ts +17 -17
  40. package/cli/templates/dashboard/builders/functions/render-page/header.ts +20 -20
  41. package/cli/templates/dashboard/builders/functions/render-page/index.ts +33 -33
  42. package/cli/templates/dashboard/builders/functions/render-page/request-panel.ts +271 -271
  43. package/cli/templates/dashboard/builders/functions/render-page/result-panel.ts +85 -85
  44. package/cli/templates/dashboard/builders/functions/render-page/scripts.ts +703 -703
  45. package/cli/templates/dashboard/builders/functions/tree-builder.ts +47 -47
  46. package/cli/templates/dashboard/builders/navigation.ts +155 -155
  47. package/cli/templates/dashboard/builders/storage/index.ts +13 -13
  48. package/cli/templates/dashboard/builders/storage/routes/create-directory-route.ts +29 -29
  49. package/cli/templates/dashboard/builders/storage/routes/delete-route.ts +18 -18
  50. package/cli/templates/dashboard/builders/storage/routes/download-route.ts +23 -23
  51. package/cli/templates/dashboard/builders/storage/routes/index.ts +22 -22
  52. package/cli/templates/dashboard/builders/storage/routes/list-route.ts +25 -25
  53. package/cli/templates/dashboard/builders/storage/routes/preview-route.ts +21 -21
  54. package/cli/templates/dashboard/builders/storage/routes/upload-route.ts +21 -21
  55. package/cli/templates/dashboard/builders/storage/runtime/helpers.ts +72 -72
  56. package/cli/templates/dashboard/builders/storage/runtime/storage-page.ts +130 -130
  57. package/cli/templates/dashboard/builders/table-routes/common/drawer-panel.ts +27 -27
  58. package/cli/templates/dashboard/builders/table-routes/common/pagination.ts +30 -30
  59. package/cli/templates/dashboard/builders/table-routes/common/search-bar.ts +23 -23
  60. package/cli/templates/dashboard/builders/table-routes/fragments.ts +217 -217
  61. package/cli/templates/dashboard/builders/table-routes/helpers.ts +45 -45
  62. package/cli/templates/dashboard/builders/table-routes/index.ts +8 -8
  63. package/cli/templates/dashboard/builders/table-routes/table/actions-cell.ts +71 -71
  64. package/cli/templates/dashboard/builders/table-routes/table/get-route.ts +291 -291
  65. package/cli/templates/dashboard/builders/table-routes/table/index.ts +80 -80
  66. package/cli/templates/dashboard/builders/table-routes/table/post-routes.ts +163 -163
  67. package/cli/templates/dashboard/builders/table-routes/table-route.ts +7 -7
  68. package/cli/templates/dashboard/builders/table-routes/users/get-route.ts +69 -69
  69. package/cli/templates/dashboard/builders/table-routes/users/html/modals.ts +57 -57
  70. package/cli/templates/dashboard/builders/table-routes/users/html/page.ts +27 -27
  71. package/cli/templates/dashboard/builders/table-routes/users/html/table.ts +128 -128
  72. package/cli/templates/dashboard/builders/table-routes/users/index.ts +32 -32
  73. package/cli/templates/dashboard/builders/table-routes/users/post-routes.ts +150 -150
  74. package/cli/templates/dashboard/builders/table-routes/users/redirect.ts +14 -14
  75. package/cli/templates/dashboard/builders/table-routes/users-route.ts +10 -10
  76. package/cli/templates/dashboard/components/dashboard-home.ts +23 -23
  77. package/cli/templates/dashboard/components/layout.ts +420 -420
  78. package/cli/templates/dashboard/components/login-page.ts +65 -65
  79. package/cli/templates/dashboard/index.ts +61 -61
  80. package/cli/templates/dashboard/types.ts +9 -9
  81. package/cli/templates/handlers/README.md +353 -353
  82. package/cli/templates/handlers/auth.ts +37 -37
  83. package/cli/templates/handlers/execution.ts +42 -42
  84. package/cli/templates/handlers/generators/context/context-creation.ts +101 -101
  85. package/cli/templates/handlers/generators/context/error-helpers.ts +11 -11
  86. package/cli/templates/handlers/generators/context/scheduler.ts +24 -24
  87. package/cli/templates/handlers/generators/context/storage-api.ts +82 -82
  88. package/cli/templates/handlers/generators/context/storage-helpers.ts +59 -59
  89. package/cli/templates/handlers/generators/context/types.ts +40 -40
  90. package/cli/templates/handlers/generators/context.ts +43 -43
  91. package/cli/templates/handlers/generators/execution.ts +15 -15
  92. package/cli/templates/handlers/generators/handlers.ts +14 -14
  93. package/cli/templates/handlers/generators/registration/modules/cron.ts +35 -35
  94. package/cli/templates/handlers/generators/registration/modules/realtime/auth.ts +75 -75
  95. package/cli/templates/handlers/generators/registration/modules/realtime/durable-object.ts +144 -144
  96. package/cli/templates/handlers/generators/registration/modules/realtime/index.ts +14 -14
  97. package/cli/templates/handlers/generators/registration/modules/realtime/publisher.ts +102 -102
  98. package/cli/templates/handlers/generators/registration/modules/realtime/routes.ts +164 -164
  99. package/cli/templates/handlers/generators/registration/modules/realtime/types.ts +30 -30
  100. package/cli/templates/handlers/generators/registration/modules/realtime/utils.ts +510 -510
  101. package/cli/templates/handlers/generators/registration/modules/scheduler.ts +65 -65
  102. package/cli/templates/handlers/generators/registration/modules/storage.ts +199 -199
  103. package/cli/templates/handlers/generators/registration/sections.ts +210 -210
  104. package/cli/templates/handlers/generators/types/context.ts +121 -121
  105. package/cli/templates/handlers/generators/types/core.ts +108 -106
  106. package/cli/templates/handlers/generators/types/operations.ts +135 -135
  107. package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +291 -291
  108. package/cli/templates/handlers/generators/types/query-definitions/query-api-types.ts +135 -135
  109. package/cli/templates/handlers/generators/types/query-definitions/query-helper-functions.ts +1382 -1382
  110. package/cli/templates/handlers/generators/types/query-definitions/schema-and-table-types.ts +278 -278
  111. package/cli/templates/handlers/generators/types/query-definitions.ts +13 -13
  112. package/cli/templates/handlers/generators/types/query-runtime/handled-error.ts +13 -13
  113. package/cli/templates/handlers/generators/types/query-runtime/runtime-aggregate-and-footer.ts +174 -174
  114. package/cli/templates/handlers/generators/types/query-runtime/runtime-read.ts +158 -157
  115. package/cli/templates/handlers/generators/types/query-runtime/runtime-setup.ts +45 -45
  116. package/cli/templates/handlers/generators/types/query-runtime/runtime-write.ts +958 -958
  117. package/cli/templates/handlers/generators/types/query-runtime.ts +15 -15
  118. package/cli/templates/handlers/index.ts +47 -47
  119. package/cli/templates/handlers/operations.ts +116 -116
  120. package/cli/templates/handlers/registration.ts +91 -91
  121. package/cli/templates/handlers/types.ts +17 -17
  122. package/cli/templates/handlers/utils.ts +48 -48
  123. package/cli/types.ts +110 -110
  124. package/cli/utils/handler-discovery.ts +501 -501
  125. package/cli/utils/json-utils.ts +24 -24
  126. package/cli/utils/path-utils.ts +19 -19
  127. package/cli/utils/schema-discovery.ts +399 -399
  128. package/dist/cli/index.js +6 -4
  129. package/dist/cli/index.mjs +6 -4
  130. package/index.ts +18 -18
  131. package/package.json +58 -58
  132. package/react/index.ts +5 -5
  133. package/react/use-infinite-query.ts +255 -255
  134. package/react/use-mutation.ts +89 -89
  135. package/react/use-query.ts +210 -210
  136. package/schema.ts +641 -641
  137. package/test-better-auth-hash.ts +2 -2
  138. package/tsconfig.json +6 -6
  139. package/tsup.config.ts +82 -82
@@ -1,271 +1,271 @@
1
- import { DiscoveredArgField, DiscoveredHandlerOperation } from "../../../../../utils/handler-discovery";
2
-
3
- const fieldInputClass =
4
- "input input-xs input-bordered font-mono flex-1 bg-base-200/30 focus:bg-base-100 focus:border-primary transition-all rounded-lg border-base-200";
5
-
6
- function renderObjectFieldInputs(fields: DiscoveredArgField[]): string {
7
- return fields
8
- .map((f) => {
9
- const badge =
10
- f.type !== "unknown"
11
- ? `<span class="badge badge-xs badge-ghost font-mono opacity-25 ml-1">${f.type}</span>`
12
- : "";
13
-
14
- if (f.type === "boolean") {
15
- return `
16
- <div class="flex items-center gap-2">
17
- <input
18
- type="checkbox"
19
- data-obj-key="${f.name}"
20
- data-obj-field-type="boolean"
21
- class="checkbox checkbox-xs checkbox-primary shrink-0"
22
- ${f.defaultValue === "true" ? "checked" : ""}
23
- />
24
- <span class="text-[10px] font-mono opacity-50">${f.name}${badge}</span>
25
- </div>`;
26
- }
27
-
28
- const inputType =
29
- f.type === "number" ? "number" : f.type === "date" ? "datetime-local" : "text";
30
-
31
- return `
32
- <div class="flex items-center gap-2">
33
- <span class="text-[10px] font-mono opacity-50 w-20 shrink-0 truncate" title="${f.name}">${f.name}${badge}</span>
34
- <input
35
- type="${inputType}"
36
- data-obj-key="${f.name}"
37
- data-obj-field-type="${f.type}"
38
- placeholder="${f.defaultValue ?? ""}"
39
- value="${f.defaultValue ?? ""}"
40
- class="${fieldInputClass}"
41
- />
42
- </div>`;
43
- })
44
- .join("\n");
45
- }
46
-
47
- function renderArgRows(h: DiscoveredHandlerOperation): string {
48
- const fields = h.args ?? [];
49
- if (fields.length === 0) {
50
- return `
51
- <div class="text-[11px] opacity-30 italic py-2">No arguments defined for this ${h.kind}.</div>
52
- `;
53
- }
54
-
55
- const inputClass =
56
- "input input-sm input-bordered font-mono w-full bg-base-200/30 focus:bg-base-100 focus:border-primary transition-all rounded-xl border-base-200";
57
-
58
- return fields
59
- .map((field) => {
60
- const label = `${field.name}${field.optional ? "" : " *"}`;
61
- const badge =
62
- field.type !== "unknown"
63
- ? `<span class="badge badge-xs badge-ghost font-mono opacity-40 ml-1">${field.type}</span>`
64
- : "";
65
- const optionalAlt = field.optional
66
- ? '<span class="label-text-alt text-[10px] opacity-30 italic">optional</span>'
67
- : "";
68
- const labelRow = `
69
- <label class="label py-0.5">
70
- <span class="label-text text-[11px] font-mono font-semibold">${label}${badge}</span>
71
- ${optionalAlt}
72
- </label>`;
73
-
74
- if (field.type === "boolean") {
75
- return `
76
- <div class="flex items-center gap-3 py-1">
77
- <input
78
- type="checkbox"
79
- data-arg-key="${field.name}"
80
- data-arg-type="boolean"
81
- class="checkbox checkbox-sm checkbox-primary"
82
- ${field.defaultValue === "true" ? "checked" : ""}
83
- />
84
- <span class="text-sm font-mono opacity-70">${field.name}${badge}</span>
85
- ${field.optional ? '<span class="text-[10px] opacity-30 italic ml-auto">optional</span>' : ""}
86
- </div>`;
87
- }
88
-
89
- if (field.type === "object") {
90
- if (field.fields && field.fields.length > 0) {
91
- return `
92
- <div class="form-control">
93
- ${labelRow}
94
- <div
95
- data-arg-key="${field.name}"
96
- data-arg-type="object"
97
- class="border border-base-200 rounded-xl p-3 bg-base-200/20 flex flex-col gap-2"
98
- >
99
- ${renderObjectFieldInputs(field.fields)}
100
- </div>
101
- </div>`;
102
- }
103
- // Fallback when schema isn't statically known
104
- return `
105
- <div class="form-control">
106
- ${labelRow}
107
- <textarea
108
- data-arg-key="${field.name}"
109
- data-arg-type="object"
110
- placeholder="{}"
111
- rows="4"
112
- class="textarea textarea-sm textarea-bordered font-mono w-full bg-base-200/30 focus:bg-base-100 focus:border-primary transition-all rounded-xl border-base-200 resize-y text-xs"
113
- ${!field.optional ? "required" : ""}
114
- >${field.defaultValue ?? ""}</textarea>
115
- </div>`;
116
- }
117
-
118
- if (field.type === "array") {
119
- const itemType = field.itemType ?? "string";
120
- const itemFieldsJson = JSON.stringify(
121
- (field.itemFields ?? []).map((f) => ({ name: f.name, type: f.type })),
122
- );
123
- return `
124
- <div class="form-control">
125
- ${labelRow}
126
- <div
127
- data-arg-key="${field.name}"
128
- data-arg-type="array"
129
- data-item-type="${itemType}"
130
- data-item-fields='${itemFieldsJson}'
131
- class="flex flex-col gap-2"
132
- ></div>
133
- <button
134
- type="button"
135
- onclick="addArrayItem('${field.name}')"
136
- class="btn btn-xs btn-ghost gap-1 mt-1 self-start opacity-50 hover:opacity-100"
137
- >
138
- <iconify-icon icon="solar:add-circle-linear" width="12" height="12"></iconify-icon>
139
- Add item
140
- </button>
141
- </div>`;
142
- }
143
-
144
- const inputType =
145
- field.type === "number" ? "number" : field.type === "date" ? "datetime-local" : "text";
146
-
147
- return `
148
- <div class="form-control">
149
- ${labelRow}
150
- <input
151
- type="${inputType}"
152
- placeholder="${field.defaultValue ?? ""}"
153
- data-arg-key="${field.name}"
154
- data-arg-type="${field.type}"
155
- value="${field.defaultValue ?? ""}"
156
- class="${inputClass}"
157
- ${!field.optional ? "required" : ""}
158
- />
159
- </div>`;
160
- })
161
- .join("\n");
162
- }
163
-
164
- function renderArgumentsSection(h: DiscoveredHandlerOperation): string {
165
- return `
166
- <div class="space-y-4">
167
- <div class="flex items-center justify-between">
168
- <div class="flex flex-col">
169
- <span class="text-[11px] font-bold uppercase tracking-wider opacity-40">Arguments</span>
170
- <span class="text-[10px] font-mono opacity-30">${h.kind}</span>
171
- </div>
172
- <label class="flex items-center gap-2 cursor-pointer select-none group" title="Enable realtime updates via WebSocket">
173
- <span class="text-[10px] font-bold uppercase tracking-wider opacity-40 group-hover:opacity-70 transition-opacity">Realtime</span>
174
- <input type="checkbox" id="realtime-toggle" class="toggle toggle-xs toggle-success" onchange="toggleRealtime(this.checked)" />
175
- </label>
176
- </div>
177
- <div id="args-rows" class="flex flex-col gap-3">
178
- ${renderArgRows(h)}
179
- </div>
180
- <p class="text-[11px] opacity-30 mt-2 italic">Values are sent as ${h.kind === "query" ? "query string params" : "JSON request body"}.</p>
181
- </div>
182
- `;
183
- }
184
-
185
- function renderBearerTokenSection(): string {
186
- return `
187
- <div class="space-y-4">
188
- <label class="text-[11px] font-bold uppercase tracking-wider opacity-40 block">Bearer Token <span class="font-normal normal-case">(optional)</span></label>
189
- <div class="relative group">
190
- <input
191
- type="password"
192
- name="token"
193
- id="bearer-token-input"
194
- class="input input-sm input-bordered font-mono text-sm w-full bg-base-200/30 focus:bg-base-100 focus:border-primary transition-all rounded-xl border-base-200 pr-9"
195
- placeholder="eyJhbGciOi..."
196
- />
197
- <button type="button" onclick="toggleTokenVisibility()" class="absolute right-2.5 top-1/2 -translate-y-1/2 opacity-30 hover:opacity-70 transition-opacity">
198
- <iconify-icon id="token-eye-icon" icon="solar:eye-linear" width="15" height="15"></iconify-icon>
199
- </button>
200
- </div>
201
- <p class="text-[10px] opacity-30 italic">Token will be included in the Authorization header.</p>
202
- </div>
203
- `;
204
- }
205
-
206
- function renderCustomHeadersSection(): string {
207
- return `
208
- <div class="space-y-4">
209
- <div class="flex items-center justify-between">
210
- <span class="text-[11px] font-bold uppercase tracking-wider opacity-40">Custom Headers</span>
211
- <button type="button" onclick="addHeaderRow()" class="btn btn-xs btn-ghost gap-1 opacity-50 hover:opacity-100">
212
- <iconify-icon icon="solar:add-circle-linear" width="14" height="14"></iconify-icon>
213
- Add
214
- </button>
215
- </div>
216
- <div id="headers-rows" class="flex flex-col gap-2">
217
- <!-- populated by addHeaderRow() -->
218
- </div>
219
- <p id="headers-error" class="text-[11px] text-error mt-1.5 hidden"></p>
220
- </div>
221
- `;
222
- }
223
-
224
- export function renderRequestPanel(h: DiscoveredHandlerOperation): string {
225
- return `
226
- <div class="card bg-base-100 border border-base-200 shadow-sm overflow-hidden flex flex-col h-full">
227
- <div class="px-5 py-3 border-b border-base-200 bg-base-200/20 flex items-center justify-between flex-none">
228
- <h3 class="text-xs font-bold uppercase tracking-widest opacity-40">Request</h3>
229
- <iconify-icon icon="solar:settings-linear" width="16" height="16" class="opacity-30"></iconify-icon>
230
- </div>
231
-
232
- <div class="flex-1 overflow-hidden flex flex-col">
233
- <form id="fn-form" onsubmit="return false" class="flex flex-col h-full">
234
- <!-- Tabs Navigation -->
235
- <div class="px-5 pt-4 flex-none">
236
- <div role="tablist" class="tabs tabs-box bg-base-200/50 p-1 rounded-xl">
237
- <a role="tab" id="req-tab-btn-args" class="tab tab-active !text-[10px] !font-bold uppercase tracking-wider h-8" onclick="switchRequestTab('args')">Args</a>
238
- <a role="tab" id="req-tab-btn-auth" class="tab !text-[10px] !font-bold uppercase tracking-wider h-8" onclick="switchRequestTab('auth')">Auth</a>
239
- <a role="tab" id="req-tab-btn-headers" class="tab !text-[10px] !font-bold uppercase tracking-wider h-8" onclick="switchRequestTab('headers')">Headers</a>
240
- </div>
241
- </div>
242
-
243
- <!-- Tab Content -->
244
- <div class="flex-1 overflow-y-auto">
245
- <div id="request-tab-args" class="p-5">
246
- ${renderArgumentsSection(h)}
247
- </div>
248
- <div id="request-tab-auth" class="p-5 hidden">
249
- ${renderBearerTokenSection()}
250
- </div>
251
- <div id="request-tab-headers" class="p-5 hidden">
252
- ${renderCustomHeadersSection()}
253
- </div>
254
- </div>
255
-
256
- <!-- Submit Button (Fixed at bottom) -->
257
- <div class="p-5 border-t border-base-200 mt-auto bg-base-200/5">
258
- <button
259
- type="button"
260
- onclick="executeFn()"
261
- class="btn btn-primary w-full gap-2 rounded-xl shadow-lg shadow-primary/20 hover:shadow-xl hover:shadow-primary/30 transition-all font-semibold"
262
- >
263
- <iconify-icon icon="solar:play-bold" width="18" height="18"></iconify-icon>
264
- Run ${h.kind}
265
- </button>
266
- </div>
267
- </form>
268
- </div>
269
- </div>
270
- `;
271
- }
1
+ import { DiscoveredArgField, DiscoveredHandlerOperation } from "../../../../../utils/handler-discovery";
2
+
3
+ const fieldInputClass =
4
+ "input input-xs input-bordered font-mono flex-1 bg-base-200/30 focus:bg-base-100 focus:border-primary transition-all rounded-lg border-base-200";
5
+
6
+ function renderObjectFieldInputs(fields: DiscoveredArgField[]): string {
7
+ return fields
8
+ .map((f) => {
9
+ const badge =
10
+ f.type !== "unknown"
11
+ ? `<span class="badge badge-xs badge-ghost font-mono opacity-25 ml-1">${f.type}</span>`
12
+ : "";
13
+
14
+ if (f.type === "boolean") {
15
+ return `
16
+ <div class="flex items-center gap-2">
17
+ <input
18
+ type="checkbox"
19
+ data-obj-key="${f.name}"
20
+ data-obj-field-type="boolean"
21
+ class="checkbox checkbox-xs checkbox-primary shrink-0"
22
+ ${f.defaultValue === "true" ? "checked" : ""}
23
+ />
24
+ <span class="text-[10px] font-mono opacity-50">${f.name}${badge}</span>
25
+ </div>`;
26
+ }
27
+
28
+ const inputType =
29
+ f.type === "number" ? "number" : f.type === "date" ? "datetime-local" : "text";
30
+
31
+ return `
32
+ <div class="flex items-center gap-2">
33
+ <span class="text-[10px] font-mono opacity-50 w-20 shrink-0 truncate" title="${f.name}">${f.name}${badge}</span>
34
+ <input
35
+ type="${inputType}"
36
+ data-obj-key="${f.name}"
37
+ data-obj-field-type="${f.type}"
38
+ placeholder="${f.defaultValue ?? ""}"
39
+ value="${f.defaultValue ?? ""}"
40
+ class="${fieldInputClass}"
41
+ />
42
+ </div>`;
43
+ })
44
+ .join("\n");
45
+ }
46
+
47
+ function renderArgRows(h: DiscoveredHandlerOperation): string {
48
+ const fields = h.args ?? [];
49
+ if (fields.length === 0) {
50
+ return `
51
+ <div class="text-[11px] opacity-30 italic py-2">No arguments defined for this ${h.kind}.</div>
52
+ `;
53
+ }
54
+
55
+ const inputClass =
56
+ "input input-sm input-bordered font-mono w-full bg-base-200/30 focus:bg-base-100 focus:border-primary transition-all rounded-xl border-base-200";
57
+
58
+ return fields
59
+ .map((field) => {
60
+ const label = `${field.name}${field.optional ? "" : " *"}`;
61
+ const badge =
62
+ field.type !== "unknown"
63
+ ? `<span class="badge badge-xs badge-ghost font-mono opacity-40 ml-1">${field.type}</span>`
64
+ : "";
65
+ const optionalAlt = field.optional
66
+ ? '<span class="label-text-alt text-[10px] opacity-30 italic">optional</span>'
67
+ : "";
68
+ const labelRow = `
69
+ <label class="label py-0.5">
70
+ <span class="label-text text-[11px] font-mono font-semibold">${label}${badge}</span>
71
+ ${optionalAlt}
72
+ </label>`;
73
+
74
+ if (field.type === "boolean") {
75
+ return `
76
+ <div class="flex items-center gap-3 py-1">
77
+ <input
78
+ type="checkbox"
79
+ data-arg-key="${field.name}"
80
+ data-arg-type="boolean"
81
+ class="checkbox checkbox-sm checkbox-primary"
82
+ ${field.defaultValue === "true" ? "checked" : ""}
83
+ />
84
+ <span class="text-sm font-mono opacity-70">${field.name}${badge}</span>
85
+ ${field.optional ? '<span class="text-[10px] opacity-30 italic ml-auto">optional</span>' : ""}
86
+ </div>`;
87
+ }
88
+
89
+ if (field.type === "object") {
90
+ if (field.fields && field.fields.length > 0) {
91
+ return `
92
+ <div class="form-control">
93
+ ${labelRow}
94
+ <div
95
+ data-arg-key="${field.name}"
96
+ data-arg-type="object"
97
+ class="border border-base-200 rounded-xl p-3 bg-base-200/20 flex flex-col gap-2"
98
+ >
99
+ ${renderObjectFieldInputs(field.fields)}
100
+ </div>
101
+ </div>`;
102
+ }
103
+ // Fallback when schema isn't statically known
104
+ return `
105
+ <div class="form-control">
106
+ ${labelRow}
107
+ <textarea
108
+ data-arg-key="${field.name}"
109
+ data-arg-type="object"
110
+ placeholder="{}"
111
+ rows="4"
112
+ class="textarea textarea-sm textarea-bordered font-mono w-full bg-base-200/30 focus:bg-base-100 focus:border-primary transition-all rounded-xl border-base-200 resize-y text-xs"
113
+ ${!field.optional ? "required" : ""}
114
+ >${field.defaultValue ?? ""}</textarea>
115
+ </div>`;
116
+ }
117
+
118
+ if (field.type === "array") {
119
+ const itemType = field.itemType ?? "string";
120
+ const itemFieldsJson = JSON.stringify(
121
+ (field.itemFields ?? []).map((f) => ({ name: f.name, type: f.type })),
122
+ );
123
+ return `
124
+ <div class="form-control">
125
+ ${labelRow}
126
+ <div
127
+ data-arg-key="${field.name}"
128
+ data-arg-type="array"
129
+ data-item-type="${itemType}"
130
+ data-item-fields='${itemFieldsJson}'
131
+ class="flex flex-col gap-2"
132
+ ></div>
133
+ <button
134
+ type="button"
135
+ onclick="addArrayItem('${field.name}')"
136
+ class="btn btn-xs btn-ghost gap-1 mt-1 self-start opacity-50 hover:opacity-100"
137
+ >
138
+ <iconify-icon icon="solar:add-circle-linear" width="12" height="12"></iconify-icon>
139
+ Add item
140
+ </button>
141
+ </div>`;
142
+ }
143
+
144
+ const inputType =
145
+ field.type === "number" ? "number" : field.type === "date" ? "datetime-local" : "text";
146
+
147
+ return `
148
+ <div class="form-control">
149
+ ${labelRow}
150
+ <input
151
+ type="${inputType}"
152
+ placeholder="${field.defaultValue ?? ""}"
153
+ data-arg-key="${field.name}"
154
+ data-arg-type="${field.type}"
155
+ value="${field.defaultValue ?? ""}"
156
+ class="${inputClass}"
157
+ ${!field.optional ? "required" : ""}
158
+ />
159
+ </div>`;
160
+ })
161
+ .join("\n");
162
+ }
163
+
164
+ function renderArgumentsSection(h: DiscoveredHandlerOperation): string {
165
+ return `
166
+ <div class="space-y-4">
167
+ <div class="flex items-center justify-between">
168
+ <div class="flex flex-col">
169
+ <span class="text-[11px] font-bold uppercase tracking-wider opacity-40">Arguments</span>
170
+ <span class="text-[10px] font-mono opacity-30">${h.kind}</span>
171
+ </div>
172
+ <label class="flex items-center gap-2 cursor-pointer select-none group" title="Enable realtime updates via WebSocket">
173
+ <span class="text-[10px] font-bold uppercase tracking-wider opacity-40 group-hover:opacity-70 transition-opacity">Realtime</span>
174
+ <input type="checkbox" id="realtime-toggle" class="toggle toggle-xs toggle-success" onchange="toggleRealtime(this.checked)" />
175
+ </label>
176
+ </div>
177
+ <div id="args-rows" class="flex flex-col gap-3">
178
+ ${renderArgRows(h)}
179
+ </div>
180
+ <p class="text-[11px] opacity-30 mt-2 italic">Values are sent as ${h.kind === "query" ? "query string params" : "JSON request body"}.</p>
181
+ </div>
182
+ `;
183
+ }
184
+
185
+ function renderBearerTokenSection(): string {
186
+ return `
187
+ <div class="space-y-4">
188
+ <label class="text-[11px] font-bold uppercase tracking-wider opacity-40 block">Bearer Token <span class="font-normal normal-case">(optional)</span></label>
189
+ <div class="relative group">
190
+ <input
191
+ type="password"
192
+ name="token"
193
+ id="bearer-token-input"
194
+ class="input input-sm input-bordered font-mono text-sm w-full bg-base-200/30 focus:bg-base-100 focus:border-primary transition-all rounded-xl border-base-200 pr-9"
195
+ placeholder="eyJhbGciOi..."
196
+ />
197
+ <button type="button" onclick="toggleTokenVisibility()" class="absolute right-2.5 top-1/2 -translate-y-1/2 opacity-30 hover:opacity-70 transition-opacity">
198
+ <iconify-icon id="token-eye-icon" icon="solar:eye-linear" width="15" height="15"></iconify-icon>
199
+ </button>
200
+ </div>
201
+ <p class="text-[10px] opacity-30 italic">Token will be included in the Authorization header.</p>
202
+ </div>
203
+ `;
204
+ }
205
+
206
+ function renderCustomHeadersSection(): string {
207
+ return `
208
+ <div class="space-y-4">
209
+ <div class="flex items-center justify-between">
210
+ <span class="text-[11px] font-bold uppercase tracking-wider opacity-40">Custom Headers</span>
211
+ <button type="button" onclick="addHeaderRow()" class="btn btn-xs btn-ghost gap-1 opacity-50 hover:opacity-100">
212
+ <iconify-icon icon="solar:add-circle-linear" width="14" height="14"></iconify-icon>
213
+ Add
214
+ </button>
215
+ </div>
216
+ <div id="headers-rows" class="flex flex-col gap-2">
217
+ <!-- populated by addHeaderRow() -->
218
+ </div>
219
+ <p id="headers-error" class="text-[11px] text-error mt-1.5 hidden"></p>
220
+ </div>
221
+ `;
222
+ }
223
+
224
+ export function renderRequestPanel(h: DiscoveredHandlerOperation): string {
225
+ return `
226
+ <div class="card bg-base-100 border border-base-200 shadow-sm overflow-hidden flex flex-col h-full">
227
+ <div class="px-5 py-3 border-b border-base-200 bg-base-200/20 flex items-center justify-between flex-none">
228
+ <h3 class="text-xs font-bold uppercase tracking-widest opacity-40">Request</h3>
229
+ <iconify-icon icon="solar:settings-linear" width="16" height="16" class="opacity-30"></iconify-icon>
230
+ </div>
231
+
232
+ <div class="flex-1 overflow-hidden flex flex-col">
233
+ <form id="fn-form" onsubmit="return false" class="flex flex-col h-full">
234
+ <!-- Tabs Navigation -->
235
+ <div class="px-5 pt-4 flex-none">
236
+ <div role="tablist" class="tabs tabs-box bg-base-200/50 p-1 rounded-xl">
237
+ <a role="tab" id="req-tab-btn-args" class="tab tab-active !text-[10px] !font-bold uppercase tracking-wider h-8" onclick="switchRequestTab('args')">Args</a>
238
+ <a role="tab" id="req-tab-btn-auth" class="tab !text-[10px] !font-bold uppercase tracking-wider h-8" onclick="switchRequestTab('auth')">Auth</a>
239
+ <a role="tab" id="req-tab-btn-headers" class="tab !text-[10px] !font-bold uppercase tracking-wider h-8" onclick="switchRequestTab('headers')">Headers</a>
240
+ </div>
241
+ </div>
242
+
243
+ <!-- Tab Content -->
244
+ <div class="flex-1 overflow-y-auto">
245
+ <div id="request-tab-args" class="p-5">
246
+ ${renderArgumentsSection(h)}
247
+ </div>
248
+ <div id="request-tab-auth" class="p-5 hidden">
249
+ ${renderBearerTokenSection()}
250
+ </div>
251
+ <div id="request-tab-headers" class="p-5 hidden">
252
+ ${renderCustomHeadersSection()}
253
+ </div>
254
+ </div>
255
+
256
+ <!-- Submit Button (Fixed at bottom) -->
257
+ <div class="p-5 border-t border-base-200 mt-auto bg-base-200/5">
258
+ <button
259
+ type="button"
260
+ onclick="executeFn()"
261
+ class="btn btn-primary w-full gap-2 rounded-xl shadow-lg shadow-primary/20 hover:shadow-xl hover:shadow-primary/30 transition-all font-semibold"
262
+ >
263
+ <iconify-icon icon="solar:play-bold" width="18" height="18"></iconify-icon>
264
+ Run ${h.kind}
265
+ </button>
266
+ </div>
267
+ </form>
268
+ </div>
269
+ </div>
270
+ `;
271
+ }