@fui-org/fui-cli 2.1.3 → 2.2.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.
Files changed (219) hide show
  1. package/README.md +27 -7
  2. package/dist/fui-qrb1j9jz.js +397 -0
  3. package/dist/fui.js +1 -1
  4. package/package.json +1 -1
  5. package/skills/fui/SKILL.md +143 -38
  6. package/skills/fui/agents/fui-builder.md +62 -0
  7. package/skills/fui/agents/fui-tester.md +77 -0
  8. package/skills/{fui-skill → fui}/assets/projectdefaultstyle.css +489 -489
  9. package/skills/{fui-skill → fui}/examples/component.vue +162 -162
  10. package/skills/{fui-skill → fui}/references/INDEX.md +5 -4
  11. package/skills/{fui-skill → fui}/references/advanced-techniques.md +170 -168
  12. package/skills/{fui-skill → fui}/references/component-design.md +2 -2
  13. package/skills/{fui-skill → fui}/references/component-quickref.md +78 -78
  14. package/skills/{fui-skill → fui}/references/component-table.md +287 -287
  15. package/skills/{fui-skill → fui}/references/components-dialog.md +191 -191
  16. package/skills/{fui-skill → fui}/references/components-display.md +141 -141
  17. package/skills/{fui-skill → fui}/references/components-echart.md +316 -316
  18. package/skills/{fui-skill → fui}/references/components-input.md +335 -335
  19. package/skills/{fui-skill → fui}/references/controls-patterns.md +2 -2
  20. package/skills/{fui-skill → fui}/references/controls-styling-vocabulary.md +173 -173
  21. package/skills/{fui-skill → fui}/references/db-workflow.md +9 -9
  22. package/skills/{fui-skill → fui}/references/default-function.md +456 -456
  23. package/skills/{fui-skill → fui}/references/design-modes.md +57 -57
  24. package/skills/{fui-skill → fui}/references/echart-templates.md +489 -489
  25. package/skills/{fui-skill → fui}/references/fastproject.md +99 -99
  26. package/skills/{fui-skill → fui}/references/fullstack-workflow.md +314 -313
  27. package/skills/{fui-skill → fui}/references/module-data-patterns.md +117 -117
  28. package/skills/{fui-skill → fui}/references/module-json-anatomy.md +132 -132
  29. package/skills/{fui-skill → fui}/references/module-structure.md +144 -144
  30. package/skills/{fui-skill → fui}/references/new-session.md +1 -2
  31. package/skills/{fui-skill → fui}/references/pdfmake.md +60 -60
  32. package/skills/{fui-skill → fui}/references/permission-system.md +1 -1
  33. package/skills/{fui-skill → fui}/references/platform-architecture.md +269 -269
  34. package/skills/{fui-skill → fui}/references/project-config.md +303 -303
  35. package/skills/{fui-skill → fui}/references/project-provisioning.md +4 -4
  36. package/skills/{fui-skill → fui}/references/script-map.md +262 -262
  37. package/skills/{fui-skill → fui}/references/sql-clr-functions.md +225 -225
  38. package/skills/{fui-skill → fui}/references/system-design.md +2 -2
  39. package/skills/{fui-skill → fui}/references/tapi-permission-patterns.md +156 -156
  40. package/skills/{fui-skill → fui}/references/tapi-reference.md +474 -474
  41. package/skills/fui/references/team-run.md +141 -0
  42. package/skills/fui/references/tools-registry.md +211 -0
  43. package/skills/{fui-skill → fui}/references/ui-crosswindow-patterns.md +321 -321
  44. package/skills/{fui-skill → fui}/references/ui-dialog-patterns.md +255 -255
  45. package/skills/{fui-skill → fui}/references/ui-layout-patterns.md +176 -176
  46. package/skills/{fui-skill → fui}/references/ui-patterns.md +303 -303
  47. package/skills/{fui-skill → fui}/references/ui-screenshot-review.md +95 -95
  48. package/skills/{fui-skill → fui}/references/ui-table-cell-patterns.md +318 -318
  49. package/skills/{fui-skill → fui}/references/verification.md +11 -11
  50. package/skills/{fui-skill → fui}/references/watcher-patterns.md +163 -163
  51. package/skills/{fui-skill → fui}/references/websocket-realtime.md +271 -271
  52. package/skills/{fui-skill → fui}/scripts/component-3.0.js +2715 -2715
  53. package/skills/{fui-skill → fui}/scripts/component.js +3147 -3147
  54. package/skills/{fui-skill → fui}/scripts/componentTable-3.0.js +1038 -1038
  55. package/skills/{fui-skill → fui}/scripts/componentTable.js +891 -891
  56. package/skills/{fui-skill → fui}/scripts/defaultfunction-3.0.js +866 -866
  57. package/skills/{fui-skill → fui}/scripts/defaultfunction.js +1051 -1051
  58. package/skills/{fui-skill → fui}/scripts/fastproject-3.0.js +870 -870
  59. package/skills/{fui-skill → fui}/scripts/fastproject.js +828 -828
  60. package/skills/fui-target/hooks/register.tsx +2 -0
  61. package/skills/fui-target/types/index.d.ts +1 -0
  62. package/dist/fui-hk5frcn2.js +0 -381
  63. package/skills/fui-skill/SKILL.md +0 -139
  64. package/skills/fui-skill/references/tools-registry.md +0 -84
  65. /package/skills/{fui-skill → fui}/assets/projectdefaultstyle-3.0.css +0 -0
  66. /package/skills/{fui-skill → fui}/design-md/airbnb/DESIGN.md +0 -0
  67. /package/skills/{fui-skill → fui}/design-md/airbnb/README.md +0 -0
  68. /package/skills/{fui-skill → fui}/design-md/airtable/DESIGN.md +0 -0
  69. /package/skills/{fui-skill → fui}/design-md/airtable/README.md +0 -0
  70. /package/skills/{fui-skill → fui}/design-md/apple/DESIGN.md +0 -0
  71. /package/skills/{fui-skill → fui}/design-md/apple/README.md +0 -0
  72. /package/skills/{fui-skill → fui}/design-md/asu/DESIGN.md +0 -0
  73. /package/skills/{fui-skill → fui}/design-md/asu/README.md +0 -0
  74. /package/skills/{fui-skill → fui}/design-md/binance/DESIGN.md +0 -0
  75. /package/skills/{fui-skill → fui}/design-md/binance/README.md +0 -0
  76. /package/skills/{fui-skill → fui}/design-md/bmw/DESIGN.md +0 -0
  77. /package/skills/{fui-skill → fui}/design-md/bmw/README.md +0 -0
  78. /package/skills/{fui-skill → fui}/design-md/bmw-m/DESIGN.md +0 -0
  79. /package/skills/{fui-skill → fui}/design-md/bmw-m/README.md +0 -0
  80. /package/skills/{fui-skill → fui}/design-md/bugatti/DESIGN.md +0 -0
  81. /package/skills/{fui-skill → fui}/design-md/bugatti/README.md +0 -0
  82. /package/skills/{fui-skill → fui}/design-md/cal/DESIGN.md +0 -0
  83. /package/skills/{fui-skill → fui}/design-md/cal/README.md +0 -0
  84. /package/skills/{fui-skill → fui}/design-md/claude/DESIGN.md +0 -0
  85. /package/skills/{fui-skill → fui}/design-md/claude/README.md +0 -0
  86. /package/skills/{fui-skill → fui}/design-md/clay/DESIGN.md +0 -0
  87. /package/skills/{fui-skill → fui}/design-md/clay/README.md +0 -0
  88. /package/skills/{fui-skill → fui}/design-md/clickhouse/DESIGN.md +0 -0
  89. /package/skills/{fui-skill → fui}/design-md/clickhouse/README.md +0 -0
  90. /package/skills/{fui-skill → fui}/design-md/cohere/DESIGN.md +0 -0
  91. /package/skills/{fui-skill → fui}/design-md/cohere/README.md +0 -0
  92. /package/skills/{fui-skill → fui}/design-md/coinbase/DESIGN.md +0 -0
  93. /package/skills/{fui-skill → fui}/design-md/coinbase/README.md +0 -0
  94. /package/skills/{fui-skill → fui}/design-md/composio/DESIGN.md +0 -0
  95. /package/skills/{fui-skill → fui}/design-md/composio/README.md +0 -0
  96. /package/skills/{fui-skill → fui}/design-md/cursor/DESIGN.md +0 -0
  97. /package/skills/{fui-skill → fui}/design-md/cursor/README.md +0 -0
  98. /package/skills/{fui-skill → fui}/design-md/elevenlabs/DESIGN.md +0 -0
  99. /package/skills/{fui-skill → fui}/design-md/elevenlabs/README.md +0 -0
  100. /package/skills/{fui-skill → fui}/design-md/expo/DESIGN.md +0 -0
  101. /package/skills/{fui-skill → fui}/design-md/expo/README.md +0 -0
  102. /package/skills/{fui-skill → fui}/design-md/ferrari/DESIGN.md +0 -0
  103. /package/skills/{fui-skill → fui}/design-md/ferrari/README.md +0 -0
  104. /package/skills/{fui-skill → fui}/design-md/figma/DESIGN.md +0 -0
  105. /package/skills/{fui-skill → fui}/design-md/figma/README.md +0 -0
  106. /package/skills/{fui-skill → fui}/design-md/framer/DESIGN.md +0 -0
  107. /package/skills/{fui-skill → fui}/design-md/framer/README.md +0 -0
  108. /package/skills/{fui-skill → fui}/design-md/fui/DESIGN.md +0 -0
  109. /package/skills/{fui-skill → fui}/design-md/hashicorp/DESIGN.md +0 -0
  110. /package/skills/{fui-skill → fui}/design-md/hashicorp/README.md +0 -0
  111. /package/skills/{fui-skill → fui}/design-md/ibm/DESIGN.md +0 -0
  112. /package/skills/{fui-skill → fui}/design-md/ibm/README.md +0 -0
  113. /package/skills/{fui-skill → fui}/design-md/intercom/DESIGN.md +0 -0
  114. /package/skills/{fui-skill → fui}/design-md/intercom/README.md +0 -0
  115. /package/skills/{fui-skill → fui}/design-md/kraken/DESIGN.md +0 -0
  116. /package/skills/{fui-skill → fui}/design-md/kraken/README.md +0 -0
  117. /package/skills/{fui-skill → fui}/design-md/lamborghini/DESIGN.md +0 -0
  118. /package/skills/{fui-skill → fui}/design-md/lamborghini/README.md +0 -0
  119. /package/skills/{fui-skill → fui}/design-md/linear.app/DESIGN.md +0 -0
  120. /package/skills/{fui-skill → fui}/design-md/linear.app/README.md +0 -0
  121. /package/skills/{fui-skill → fui}/design-md/lovable/DESIGN.md +0 -0
  122. /package/skills/{fui-skill → fui}/design-md/lovable/README.md +0 -0
  123. /package/skills/{fui-skill → fui}/design-md/mastercard/DESIGN.md +0 -0
  124. /package/skills/{fui-skill → fui}/design-md/mastercard/README.md +0 -0
  125. /package/skills/{fui-skill → fui}/design-md/meta/DESIGN.md +0 -0
  126. /package/skills/{fui-skill → fui}/design-md/meta/README.md +0 -0
  127. /package/skills/{fui-skill → fui}/design-md/minimax/DESIGN.md +0 -0
  128. /package/skills/{fui-skill → fui}/design-md/minimax/README.md +0 -0
  129. /package/skills/{fui-skill → fui}/design-md/mintlify/DESIGN.md +0 -0
  130. /package/skills/{fui-skill → fui}/design-md/mintlify/README.md +0 -0
  131. /package/skills/{fui-skill → fui}/design-md/miro/DESIGN.md +0 -0
  132. /package/skills/{fui-skill → fui}/design-md/miro/README.md +0 -0
  133. /package/skills/{fui-skill → fui}/design-md/mistral.ai/DESIGN.md +0 -0
  134. /package/skills/{fui-skill → fui}/design-md/mistral.ai/README.md +0 -0
  135. /package/skills/{fui-skill → fui}/design-md/mongodb/DESIGN.md +0 -0
  136. /package/skills/{fui-skill → fui}/design-md/mongodb/README.md +0 -0
  137. /package/skills/{fui-skill → fui}/design-md/nike/DESIGN.md +0 -0
  138. /package/skills/{fui-skill → fui}/design-md/nike/README.md +0 -0
  139. /package/skills/{fui-skill → fui}/design-md/notion/DESIGN.md +0 -0
  140. /package/skills/{fui-skill → fui}/design-md/notion/README.md +0 -0
  141. /package/skills/{fui-skill → fui}/design-md/nvidia/DESIGN.md +0 -0
  142. /package/skills/{fui-skill → fui}/design-md/nvidia/README.md +0 -0
  143. /package/skills/{fui-skill → fui}/design-md/ollama/DESIGN.md +0 -0
  144. /package/skills/{fui-skill → fui}/design-md/ollama/README.md +0 -0
  145. /package/skills/{fui-skill → fui}/design-md/opencode.ai/DESIGN.md +0 -0
  146. /package/skills/{fui-skill → fui}/design-md/opencode.ai/README.md +0 -0
  147. /package/skills/{fui-skill → fui}/design-md/pinterest/DESIGN.md +0 -0
  148. /package/skills/{fui-skill → fui}/design-md/pinterest/README.md +0 -0
  149. /package/skills/{fui-skill → fui}/design-md/playstation/DESIGN.md +0 -0
  150. /package/skills/{fui-skill → fui}/design-md/playstation/README.md +0 -0
  151. /package/skills/{fui-skill → fui}/design-md/posthog/DESIGN.md +0 -0
  152. /package/skills/{fui-skill → fui}/design-md/posthog/README.md +0 -0
  153. /package/skills/{fui-skill → fui}/design-md/raycast/DESIGN.md +0 -0
  154. /package/skills/{fui-skill → fui}/design-md/raycast/README.md +0 -0
  155. /package/skills/{fui-skill → fui}/design-md/renault/DESIGN.md +0 -0
  156. /package/skills/{fui-skill → fui}/design-md/renault/README.md +0 -0
  157. /package/skills/{fui-skill → fui}/design-md/replicate/DESIGN.md +0 -0
  158. /package/skills/{fui-skill → fui}/design-md/replicate/README.md +0 -0
  159. /package/skills/{fui-skill → fui}/design-md/resend/DESIGN.md +0 -0
  160. /package/skills/{fui-skill → fui}/design-md/resend/README.md +0 -0
  161. /package/skills/{fui-skill → fui}/design-md/revolut/DESIGN.md +0 -0
  162. /package/skills/{fui-skill → fui}/design-md/revolut/README.md +0 -0
  163. /package/skills/{fui-skill → fui}/design-md/runwayml/DESIGN.md +0 -0
  164. /package/skills/{fui-skill → fui}/design-md/runwayml/README.md +0 -0
  165. /package/skills/{fui-skill → fui}/design-md/sanity/DESIGN.md +0 -0
  166. /package/skills/{fui-skill → fui}/design-md/sanity/README.md +0 -0
  167. /package/skills/{fui-skill → fui}/design-md/sentry/DESIGN.md +0 -0
  168. /package/skills/{fui-skill → fui}/design-md/sentry/README.md +0 -0
  169. /package/skills/{fui-skill → fui}/design-md/shopify/DESIGN.md +0 -0
  170. /package/skills/{fui-skill → fui}/design-md/shopify/README.md +0 -0
  171. /package/skills/{fui-skill → fui}/design-md/slack/DESIGN.md +0 -0
  172. /package/skills/{fui-skill → fui}/design-md/spacex/DESIGN.md +0 -0
  173. /package/skills/{fui-skill → fui}/design-md/spacex/README.md +0 -0
  174. /package/skills/{fui-skill → fui}/design-md/spotify/DESIGN.md +0 -0
  175. /package/skills/{fui-skill → fui}/design-md/spotify/README.md +0 -0
  176. /package/skills/{fui-skill → fui}/design-md/starbucks/DESIGN.md +0 -0
  177. /package/skills/{fui-skill → fui}/design-md/starbucks/README.md +0 -0
  178. /package/skills/{fui-skill → fui}/design-md/stripe/DESIGN.md +0 -0
  179. /package/skills/{fui-skill → fui}/design-md/stripe/README.md +0 -0
  180. /package/skills/{fui-skill → fui}/design-md/supabase/DESIGN.md +0 -0
  181. /package/skills/{fui-skill → fui}/design-md/supabase/README.md +0 -0
  182. /package/skills/{fui-skill → fui}/design-md/superhuman/DESIGN.md +0 -0
  183. /package/skills/{fui-skill → fui}/design-md/superhuman/README.md +0 -0
  184. /package/skills/{fui-skill → fui}/design-md/tesla/DESIGN.md +0 -0
  185. /package/skills/{fui-skill → fui}/design-md/tesla/README.md +0 -0
  186. /package/skills/{fui-skill → fui}/design-md/theverge/DESIGN.md +0 -0
  187. /package/skills/{fui-skill → fui}/design-md/theverge/README.md +0 -0
  188. /package/skills/{fui-skill → fui}/design-md/together.ai/DESIGN.md +0 -0
  189. /package/skills/{fui-skill → fui}/design-md/together.ai/README.md +0 -0
  190. /package/skills/{fui-skill → fui}/design-md/uber/DESIGN.md +0 -0
  191. /package/skills/{fui-skill → fui}/design-md/uber/README.md +0 -0
  192. /package/skills/{fui-skill → fui}/design-md/vercel/DESIGN.md +0 -0
  193. /package/skills/{fui-skill → fui}/design-md/vercel/README.md +0 -0
  194. /package/skills/{fui-skill → fui}/design-md/vodafone/DESIGN.md +0 -0
  195. /package/skills/{fui-skill → fui}/design-md/vodafone/README.md +0 -0
  196. /package/skills/{fui-skill → fui}/design-md/voltagent/DESIGN.md +0 -0
  197. /package/skills/{fui-skill → fui}/design-md/voltagent/README.md +0 -0
  198. /package/skills/{fui-skill → fui}/design-md/warp/DESIGN.md +0 -0
  199. /package/skills/{fui-skill → fui}/design-md/warp/README.md +0 -0
  200. /package/skills/{fui-skill → fui}/design-md/webflow/DESIGN.md +0 -0
  201. /package/skills/{fui-skill → fui}/design-md/webflow/README.md +0 -0
  202. /package/skills/{fui-skill → fui}/design-md/wired/DESIGN.md +0 -0
  203. /package/skills/{fui-skill → fui}/design-md/wired/README.md +0 -0
  204. /package/skills/{fui-skill → fui}/design-md/wise/DESIGN.md +0 -0
  205. /package/skills/{fui-skill → fui}/design-md/wise/README.md +0 -0
  206. /package/skills/{fui-skill → fui}/design-md/x.ai/DESIGN.md +0 -0
  207. /package/skills/{fui-skill → fui}/design-md/x.ai/README.md +0 -0
  208. /package/skills/{fui-skill → fui}/design-md/zapier/DESIGN.md +0 -0
  209. /package/skills/{fui-skill → fui}/design-md/zapier/README.md +0 -0
  210. /package/skills/{fui-skill → fui}/examples/f-table-patterns.json +0 -0
  211. /package/skills/{fui-skill → fui}/examples/module-patterns.json +0 -0
  212. /package/skills/{fui-skill → fui}/examples/project-patterns.json +0 -0
  213. /package/skills/{fui-skill → fui}/references/coding-standards.md +0 -0
  214. /package/skills/{fui-skill → fui}/references/db-table-design.md +0 -0
  215. /package/skills/{fui-skill → fui}/references/fsheet.md +0 -0
  216. /package/skills/{fui-skill → fui}/references/tapi-file-api.md +0 -0
  217. /package/skills/{fui-skill → fui}/references/ui-templates.md +0 -0
  218. /package/skills/{fui-skill → fui}/scripts/fechart.js +0 -0
  219. /package/skills/{fui-skill → fui}/scripts/fsheet.js +0 -0
@@ -1,225 +1,225 @@
1
- # SQL CLR Functions — DLL-backed SQL Server functions
2
-
3
- > Owns: **SQL CLR functions: httpCall, sendMail, ImageResize, FileReader, RegexMatch...**
4
-
5
- A project DB may have **SQL CLR scalar functions** (precompiled .NET DLL, registered via `CREATE ASSEMBLY` + `CREATE FUNCTION ... EXTERNAL NAME`). If they appear in `fui schema` or in an existing SP definition, **call them directly in T-SQL** like built-ins — no install, no need to know the C#.
6
-
7
- A needed function is not in the DB → **do not invent it**; tell the user the DB lacks it.
8
-
9
- ---
10
-
11
- ## 1. `SQLImageHelper` — images
12
-
13
- ### `dbo.ImageResize`
14
-
15
- ```sql
16
- dbo.ImageResize(@extension nvarchar(10), @OriginalImage varbinary(max), @maxWidth int, @maxHeight int, @preserverAspectRatio bit, @quality int)
17
- RETURNS varbinary(max)
18
- ```
19
-
20
- Resizes to at most `@maxWidth` x `@maxHeight`.
21
-
22
- | Param | Meaning |
23
- | ------------------------- | ------------------------------------------------------------------------------ |
24
- | `@extension` | Output format, e.g. `'jpg'`, `'png'` |
25
- | `@OriginalImage` | Source image `varbinary` |
26
- | `@maxWidth`, `@maxHeight` | Max size (px) |
27
- | `@preserverAspectRatio` | `1` = keep ratio, fit longest side; `0` = force exactly `maxWidth`x`maxHeight` |
28
- | `@quality` | Compression quality (usually 1-100) |
29
-
30
- Returns resized `varbinary` — write to a `varbinary` column/file or return via tAPI File.
31
-
32
- ### `dbo.ImageCrop`
33
-
34
- ```sql
35
- dbo.ImageCrop(@OriginalImage varbinary(max), @StartAtX int, @StartAtY int, @Width int, @Height int)
36
- RETURNS varbinary(max)
37
- ```
38
-
39
- Crops a `@Width` x `@Height` rectangle starting at `(@StartAtX, @StartAtY)`.
40
-
41
- ---
42
-
43
- ## 2. `SQLHttpHelper` — HTTP, public value store, mail
44
-
45
- ### `dbo.publicValueUUID` / `publicValueGet` / `publicValueSet`
46
-
47
- Server-level shared key-value store (not a SQL table) — keeps temp state across SP calls without a table.
48
-
49
- ```sql
50
- dbo.publicValueUUID() RETURNS nvarchar(max) -- sinh 1 UUID mới, dùng làm key
51
- dbo.publicValueGet(@name nvarchar(max)) RETURNS nvarchar(max) -- đọc value theo key
52
- dbo.publicValueSet(@name nvarchar(max), @Data nvarchar(max)) RETURNS int -- ghi value theo key
53
- ```
54
-
55
- ### `dbo.httpCall` / `dbo.httpAPI` — HTTP from T-SQL
56
-
57
- ```sql
58
- dbo.httpCall(@Method nvarchar(max), @URL nvarchar(max), @Header nvarchar(max), @Data nvarchar(max))
59
- RETURNS nvarchar(max)
60
- ```
61
-
62
- `httpAPI` has the **identical** signature (different internals): use `httpAPI` for internal tAPI calls, `httpCall` for general outbound HTTP.
63
-
64
- | Param | Meaning |
65
- | --------- | -------------------------------------------------------------------------------------------------------------------------------- |
66
- | `@Method` | `'GET'`, `'POST'`, `'PUT'`, `'DELETE'`... (case-insensitive; empty → `GET`) |
67
- | `@URL` | Full, valid absolute URL |
68
- | `@Header` | Header list, syntax below — `''` for none |
69
- | `@Data` | JSON string body — **sent only when Method is not GET/HEAD**; always `Content-Type: application/json` unless `@Header` overrides |
70
-
71
- **Example:**
72
-
73
- ```sql
74
- SELECT dbo.httpCall('POST', 'https://tapi.example.vn/me/auth/ApplicationList', '', '')
75
- ```
76
-
77
- #### `@Header` syntax — pipe-delimited `Key: Value`
78
-
79
- Most important part. One string; pairs separated by `|`; each pair `Key: Value`, split on the **first** `:` (the rest, including further `:`, is the value):
80
-
81
- ```
82
- "Authorization: Bearer eyJhbGciOi...|X-Custom-Header: abc123"
83
- ```
84
-
85
- Header names are **case-insensitive**; special handling:
86
-
87
- | Header name | Behavior |
88
- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
89
- | `Authorization`, `X-*`, any other | Added to request headers as-is (`TryAddWithoutValidation`) — tokens, API keys |
90
- | `referer` / `referrer` | Parsed to URI → `request.Headers.Referrer` |
91
- | `user-agent` | Overrides default `User-Agent` |
92
- | `accept` | Overrides `Accept` |
93
- | `accept-language` | Overrides `Accept-Language` |
94
- | `accept-encoding` | Overrides `Accept-Encoding` |
95
- | `content-type` | With body (Method not GET/HEAD) → `Content.Headers.Content-Type` (overrides default `application/json`); no body → normal request headers |
96
- | Any other `content-*` | With body → routed to `Content.Headers` instead of request headers |
97
-
98
- **Bearer token + custom header:**
99
-
100
- ```sql
101
- SELECT dbo.httpCall(
102
- 'POST',
103
- 'https://api.example.com/data',
104
- 'Authorization: Bearer abc123xyz|Content-Type: application/json; charset=utf-8',
105
- '{"key":"value"}'
106
- )
107
- ```
108
-
109
- **Joining headers:** exactly one `|` between pairs, no extra/missing `|`. A pair without `:` is **silently skipped** (no error).
110
-
111
- #### `httpCall`/`httpAPI` response
112
-
113
- - Status **200**: raw response body verbatim (JSON, text, anything).
114
- - Status **not 200** (400, 401, 404, 500...): wrapped JSON:
115
- ```json
116
- { "httpErrorCode": 404, "responseData": "<nội dung body, hoặc escaped string nếu không phải JSON>" }
117
- ```
118
- → in SPs, always check for an `httpErrorCode` field to tell error from success.
119
- - Invalid URL: `{"httpErrorCode":0,"responseData":"Invalid URL"}`.
120
- - Timeout/other exception: plain-text error message (not JSON) — check the format before `OPENJSON`.
121
-
122
- ### `dbo.sendMail`
123
-
124
- ```sql
125
- dbo.sendMail(@email_to nvarchar(max), @subject nvarchar(max), @content nvarchar(max), @from nvarchar(max), @fromDisplayName nvarchar(max), @smtp_server nvarchar(max), @smtp_port int, @userName nvarchar(max), @Password nvarchar(max), @EnableSSL bit)
126
- RETURNS nvarchar(max)
127
- ```
128
-
129
- Sends email over SMTP from T-SQL.
130
-
131
- | Param | Meaning |
132
- | ---------------------------- | ---------------------------------------------------------- |
133
- | `@email_to` | Recipient |
134
- | `@subject` | Subject |
135
- | `@content` | Body (HTML or text) |
136
- | `@from` | Sender address (must match/be allowed by the SMTP account) |
137
- | `@fromDisplayName` | Sender display name |
138
- | `@smtp_server`, `@smtp_port` | e.g. `'smtp.gmail.com'`, `587` |
139
- | `@userName`, `@Password` | SMTP auth account |
140
- | `@EnableSSL` | `1` = SSL/TLS (required for Gmail SMTP) |
141
-
142
- ```sql
143
- SELECT dbo.sendMail('user@example.vn','Tiêu đề','Nội dung','noreply@example.vn','My System','smtp.gmail.com',587,'noreply@example.vn','app-password',1)
144
- ```
145
-
146
- > **Security:** avoid hardcoding the SMTP `@Password` in the SP — store it via `publicValueSet`/`publicValueGet` or a permission-restricted config table so it is not exposed in the SP definition.
147
-
148
- ---
149
-
150
- ## 3. `SQLFileSystem` — server files
151
-
152
- ```sql
153
- dbo.FileCreateFolder(@Path nvarchar(max)) RETURNS nvarchar(max)
154
- dbo.FileReader(@Path nvarchar(max)) RETURNS varbinary(max)
155
- dbo.FileWriter(@Content varbinary(max), @Path nvarchar(max)) RETURNS nvarchar(max)
156
- dbo.FileExists(@Path nvarchar(max)) RETURNS bit
157
- dbo.FileDelete(@Path nvarchar(max)) RETURNS nvarchar(max)
158
- dbo.FileMove(@SourceFileName nvarchar(max), @DestFileName nvarchar(max)) RETURNS nvarchar(max)
159
- ```
160
-
161
- Operates on the **SQL Server machine's** filesystem (not the client); `@Path` is an absolute server path. Use when an SP saves output (e.g. `ImageResize` result, exported PDF) to a fixed folder served by tAPI File API or IIS.
162
-
163
- | Function | Meaning |
164
- | ------------------ | ------------------------------------------------------------- |
165
- | `FileCreateFolder` | Creates folder (nested too), returns result message |
166
- | `FileReader` | Reads whole file as `varbinary` |
167
- | `FileWriter` | Writes `@Content` (varbinary) to `@Path`, create or overwrite |
168
- | `FileExists` | Returns `bit` |
169
- | `FileDelete` | Deletes file |
170
- | `FileMove` | Moves/renames `@SourceFileName` → `@DestFileName` |
171
-
172
- > Real server disk: add a permission check in the SP before letting client input drive `FileDelete`/`FileWriter` — same DDL/DML guard principle as `fui exec`.
173
-
174
- ---
175
-
176
- ## 4. `SQLExpansionFunction` — regex & string compare
177
-
178
- ```sql
179
- dbo.RegexReplace(@Input nvarchar(max), @Pattern nvarchar(1000), @Replacement nvarchar(4000)) RETURNS nvarchar(max)
180
- dbo.RegexIsMatch(@Input nvarchar(max), @Pattern nvarchar(1000)) RETURNS bit
181
- dbo.RegexMatch(@Input nvarchar(max), @Pattern nvarchar(1000)) RETURNS nvarchar(max)
182
- dbo.RegexMatchArray(@Input nvarchar(max), @Pattern nvarchar(1000)) RETURNS nvarchar(max)
183
- dbo.CompareWord(@String1 nvarchar(max), @String2 nvarchar(max)) RETURNS int
184
- ```
185
-
186
- | Function | Meaning |
187
- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
188
- | `RegexReplace` | .NET regex replace — when T-SQL `REPLACE` (literal only) is not enough |
189
- | `RegexIsMatch` | `bit` match test — validate format (email, phone, codes...) in the SP |
190
- | `RegexMatch` | **First match** only (`Match.Value`, whole match, no groups); empty if none |
191
- | `RegexMatchArray` | **All matches with each capture group**, as a **flat JSON object** (not array) — see below |
192
- | `CompareWord` | Similarity of 2 strings as `int` (likely differing-char count or a score — check the scale with `SELECT dbo.CompareWord('a','b')` if needed) |
193
-
194
- `@Pattern` is standard .NET regex (`\d`, `\w`, `^$`, groups `()`...), not T-SQL `LIKE`. All 4 regex functions **always** use `IgnoreCase | Multiline | Singleline`: case-insensitive, `^`/`$` per line, `.` matches newlines — cannot be turned off.
195
-
196
- #### `RegexMatchArray` shape — JSON object, not array
197
-
198
- Flat object (`{...}`, not `[...]`), keys `"M{m}G{g}"`:
199
-
200
- - `m` = match index in `@Input`, from 1.
201
- - `g` = group index **within that match**, from 1: `G1` is always the **whole match** (`Match.Groups[0]`, same as `RegexMatch`); `G2`, `G3`... are the capture groups `(...)` in `@Pattern`, if any.
202
- - No match → `"{}"`.
203
- - Values have tab/newline/CR (`\t`, `\n`, `\r`) removed and `"` escaped.
204
-
205
- `RegexMatchArray('SDT: 0901 - 0902', '(\d{4})')` (one group) returns:
206
-
207
- ```json
208
- { "M1G1": "0901", "M1G2": "0901", "M2G1": "0902", "M2G2": "0902" }
209
- ```
210
-
211
- (G1 = G2 here because the whole match equals the only group; with no groups each match yields only `G1`.)
212
-
213
- Parsing in T-SQL (it is an object):
214
-
215
- - `OPENJSON(@result)` still works but returns `key/value/type` rows (one row per key like `"M1G1"`), not one structured row per match.
216
- - One known match/group: `JSON_VALUE(@result, '$."M1G1"')`.
217
- - Iterate all: `OPENJSON(@result)` without path, parse the `M{m}G{g}` prefix from `key` to group by match.
218
-
219
- ---
220
-
221
- ## 5. When to use them in SPs
222
-
223
- - Always confirm the function exists first (`fui schema` filtered to functions, or `SELECT * FROM sys.objects WHERE type='FS'`) — usable only if the assembly was `CREATE ASSEMBLY`'d on that DB. Not every DB has them.
224
- - Use in `spAPI_*` when needed: external API (`httpCall`), uploaded image processing (`ImageResize`/`ImageCrop`), notification email (`sendMail`), regex validation (`RegexIsMatch`), saving files to server disk (`FileWriter`).
225
- - JSON `nvarchar(max)` results (`httpCall`, `RegexMatchArray`) → parse with `OPENJSON`/`JSON_VALUE` in T-SQL before returning via tAPI.
1
+ # SQL CLR Functions — DLL-backed SQL Server functions
2
+
3
+ > Owns: **SQL CLR functions: httpCall, sendMail, ImageResize, FileReader, RegexMatch...**
4
+
5
+ A project DB may have **SQL CLR scalar functions** (precompiled .NET DLL, registered via `CREATE ASSEMBLY` + `CREATE FUNCTION ... EXTERNAL NAME`). If they appear in `fui schema` or in an existing SP definition, **call them directly in T-SQL** like built-ins — no install, no need to know the C#.
6
+
7
+ A needed function is not in the DB → **do not invent it**; tell the user the DB lacks it.
8
+
9
+ ---
10
+
11
+ ## 1. `SQLImageHelper` — images
12
+
13
+ ### `dbo.ImageResize`
14
+
15
+ ```sql
16
+ dbo.ImageResize(@extension nvarchar(10), @OriginalImage varbinary(max), @maxWidth int, @maxHeight int, @preserverAspectRatio bit, @quality int)
17
+ RETURNS varbinary(max)
18
+ ```
19
+
20
+ Resizes to at most `@maxWidth` x `@maxHeight`.
21
+
22
+ | Param | Meaning |
23
+ | ------------------------- | ------------------------------------------------------------------------------ |
24
+ | `@extension` | Output format, e.g. `'jpg'`, `'png'` |
25
+ | `@OriginalImage` | Source image `varbinary` |
26
+ | `@maxWidth`, `@maxHeight` | Max size (px) |
27
+ | `@preserverAspectRatio` | `1` = keep ratio, fit longest side; `0` = force exactly `maxWidth`x`maxHeight` |
28
+ | `@quality` | Compression quality (usually 1-100) |
29
+
30
+ Returns resized `varbinary` — write to a `varbinary` column/file or return via tAPI File.
31
+
32
+ ### `dbo.ImageCrop`
33
+
34
+ ```sql
35
+ dbo.ImageCrop(@OriginalImage varbinary(max), @StartAtX int, @StartAtY int, @Width int, @Height int)
36
+ RETURNS varbinary(max)
37
+ ```
38
+
39
+ Crops a `@Width` x `@Height` rectangle starting at `(@StartAtX, @StartAtY)`.
40
+
41
+ ---
42
+
43
+ ## 2. `SQLHttpHelper` — HTTP, public value store, mail
44
+
45
+ ### `dbo.publicValueUUID` / `publicValueGet` / `publicValueSet`
46
+
47
+ Server-level shared key-value store (not a SQL table) — keeps temp state across SP calls without a table.
48
+
49
+ ```sql
50
+ dbo.publicValueUUID() RETURNS nvarchar(max) -- sinh 1 UUID mới, dùng làm key
51
+ dbo.publicValueGet(@name nvarchar(max)) RETURNS nvarchar(max) -- đọc value theo key
52
+ dbo.publicValueSet(@name nvarchar(max), @Data nvarchar(max)) RETURNS int -- ghi value theo key
53
+ ```
54
+
55
+ ### `dbo.httpCall` / `dbo.httpAPI` — HTTP from T-SQL
56
+
57
+ ```sql
58
+ dbo.httpCall(@Method nvarchar(max), @URL nvarchar(max), @Header nvarchar(max), @Data nvarchar(max))
59
+ RETURNS nvarchar(max)
60
+ ```
61
+
62
+ `httpAPI` has the **identical** signature (different internals): use `httpAPI` for internal tAPI calls, `httpCall` for general outbound HTTP.
63
+
64
+ | Param | Meaning |
65
+ | --------- | -------------------------------------------------------------------------------------------------------------------------------- |
66
+ | `@Method` | `'GET'`, `'POST'`, `'PUT'`, `'DELETE'`... (case-insensitive; empty → `GET`) |
67
+ | `@URL` | Full, valid absolute URL |
68
+ | `@Header` | Header list, syntax below — `''` for none |
69
+ | `@Data` | JSON string body — **sent only when Method is not GET/HEAD**; always `Content-Type: application/json` unless `@Header` overrides |
70
+
71
+ **Example:**
72
+
73
+ ```sql
74
+ SELECT dbo.httpCall('POST', 'https://tapi.example.vn/me/auth/ApplicationList', '', '')
75
+ ```
76
+
77
+ #### `@Header` syntax — pipe-delimited `Key: Value`
78
+
79
+ Most important part. One string; pairs separated by `|`; each pair `Key: Value`, split on the **first** `:` (the rest, including further `:`, is the value):
80
+
81
+ ```
82
+ "Authorization: Bearer eyJhbGciOi...|X-Custom-Header: abc123"
83
+ ```
84
+
85
+ Header names are **case-insensitive**; special handling:
86
+
87
+ | Header name | Behavior |
88
+ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
89
+ | `Authorization`, `X-*`, any other | Added to request headers as-is (`TryAddWithoutValidation`) — tokens, API keys |
90
+ | `referer` / `referrer` | Parsed to URI → `request.Headers.Referrer` |
91
+ | `user-agent` | Overrides default `User-Agent` |
92
+ | `accept` | Overrides `Accept` |
93
+ | `accept-language` | Overrides `Accept-Language` |
94
+ | `accept-encoding` | Overrides `Accept-Encoding` |
95
+ | `content-type` | With body (Method not GET/HEAD) → `Content.Headers.Content-Type` (overrides default `application/json`); no body → normal request headers |
96
+ | Any other `content-*` | With body → routed to `Content.Headers` instead of request headers |
97
+
98
+ **Bearer token + custom header:**
99
+
100
+ ```sql
101
+ SELECT dbo.httpCall(
102
+ 'POST',
103
+ 'https://api.example.com/data',
104
+ 'Authorization: Bearer abc123xyz|Content-Type: application/json; charset=utf-8',
105
+ '{"key":"value"}'
106
+ )
107
+ ```
108
+
109
+ **Joining headers:** exactly one `|` between pairs, no extra/missing `|`. A pair without `:` is **silently skipped** (no error).
110
+
111
+ #### `httpCall`/`httpAPI` response
112
+
113
+ - Status **200**: raw response body verbatim (JSON, text, anything).
114
+ - Status **not 200** (400, 401, 404, 500...): wrapped JSON:
115
+ ```json
116
+ { "httpErrorCode": 404, "responseData": "<nội dung body, hoặc escaped string nếu không phải JSON>" }
117
+ ```
118
+ → in SPs, always check for an `httpErrorCode` field to tell error from success.
119
+ - Invalid URL: `{"httpErrorCode":0,"responseData":"Invalid URL"}`.
120
+ - Timeout/other exception: plain-text error message (not JSON) — check the format before `OPENJSON`.
121
+
122
+ ### `dbo.sendMail`
123
+
124
+ ```sql
125
+ dbo.sendMail(@email_to nvarchar(max), @subject nvarchar(max), @content nvarchar(max), @from nvarchar(max), @fromDisplayName nvarchar(max), @smtp_server nvarchar(max), @smtp_port int, @userName nvarchar(max), @Password nvarchar(max), @EnableSSL bit)
126
+ RETURNS nvarchar(max)
127
+ ```
128
+
129
+ Sends email over SMTP from T-SQL.
130
+
131
+ | Param | Meaning |
132
+ | ---------------------------- | ---------------------------------------------------------- |
133
+ | `@email_to` | Recipient |
134
+ | `@subject` | Subject |
135
+ | `@content` | Body (HTML or text) |
136
+ | `@from` | Sender address (must match/be allowed by the SMTP account) |
137
+ | `@fromDisplayName` | Sender display name |
138
+ | `@smtp_server`, `@smtp_port` | e.g. `'smtp.gmail.com'`, `587` |
139
+ | `@userName`, `@Password` | SMTP auth account |
140
+ | `@EnableSSL` | `1` = SSL/TLS (required for Gmail SMTP) |
141
+
142
+ ```sql
143
+ SELECT dbo.sendMail('user@example.vn','Tiêu đề','Nội dung','noreply@example.vn','My System','smtp.gmail.com',587,'noreply@example.vn','app-password',1)
144
+ ```
145
+
146
+ > **Security:** avoid hardcoding the SMTP `@Password` in the SP — store it via `publicValueSet`/`publicValueGet` or a permission-restricted config table so it is not exposed in the SP definition.
147
+
148
+ ---
149
+
150
+ ## 3. `SQLFileSystem` — server files
151
+
152
+ ```sql
153
+ dbo.FileCreateFolder(@Path nvarchar(max)) RETURNS nvarchar(max)
154
+ dbo.FileReader(@Path nvarchar(max)) RETURNS varbinary(max)
155
+ dbo.FileWriter(@Content varbinary(max), @Path nvarchar(max)) RETURNS nvarchar(max)
156
+ dbo.FileExists(@Path nvarchar(max)) RETURNS bit
157
+ dbo.FileDelete(@Path nvarchar(max)) RETURNS nvarchar(max)
158
+ dbo.FileMove(@SourceFileName nvarchar(max), @DestFileName nvarchar(max)) RETURNS nvarchar(max)
159
+ ```
160
+
161
+ Operates on the **SQL Server machine's** filesystem (not the client); `@Path` is an absolute server path. Use when an SP saves output (e.g. `ImageResize` result, exported PDF) to a fixed folder served by tAPI File API or IIS.
162
+
163
+ | Function | Meaning |
164
+ | ------------------ | ------------------------------------------------------------- |
165
+ | `FileCreateFolder` | Creates folder (nested too), returns result message |
166
+ | `FileReader` | Reads whole file as `varbinary` |
167
+ | `FileWriter` | Writes `@Content` (varbinary) to `@Path`, create or overwrite |
168
+ | `FileExists` | Returns `bit` |
169
+ | `FileDelete` | Deletes file |
170
+ | `FileMove` | Moves/renames `@SourceFileName` → `@DestFileName` |
171
+
172
+ > Real server disk: add a permission check in the SP before letting client input drive `FileDelete`/`FileWriter` — same DDL/DML guard principle as `fui exec`.
173
+
174
+ ---
175
+
176
+ ## 4. `SQLExpansionFunction` — regex & string compare
177
+
178
+ ```sql
179
+ dbo.RegexReplace(@Input nvarchar(max), @Pattern nvarchar(1000), @Replacement nvarchar(4000)) RETURNS nvarchar(max)
180
+ dbo.RegexIsMatch(@Input nvarchar(max), @Pattern nvarchar(1000)) RETURNS bit
181
+ dbo.RegexMatch(@Input nvarchar(max), @Pattern nvarchar(1000)) RETURNS nvarchar(max)
182
+ dbo.RegexMatchArray(@Input nvarchar(max), @Pattern nvarchar(1000)) RETURNS nvarchar(max)
183
+ dbo.CompareWord(@String1 nvarchar(max), @String2 nvarchar(max)) RETURNS int
184
+ ```
185
+
186
+ | Function | Meaning |
187
+ | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
188
+ | `RegexReplace` | .NET regex replace — when T-SQL `REPLACE` (literal only) is not enough |
189
+ | `RegexIsMatch` | `bit` match test — validate format (email, phone, codes...) in the SP |
190
+ | `RegexMatch` | **First match** only (`Match.Value`, whole match, no groups); empty if none |
191
+ | `RegexMatchArray` | **All matches with each capture group**, as a **flat JSON object** (not array) — see below |
192
+ | `CompareWord` | Similarity of 2 strings as `int` (likely differing-char count or a score — check the scale with `SELECT dbo.CompareWord('a','b')` if needed) |
193
+
194
+ `@Pattern` is standard .NET regex (`\d`, `\w`, `^$`, groups `()`...), not T-SQL `LIKE`. All 4 regex functions **always** use `IgnoreCase | Multiline | Singleline`: case-insensitive, `^`/`$` per line, `.` matches newlines — cannot be turned off.
195
+
196
+ #### `RegexMatchArray` shape — JSON object, not array
197
+
198
+ Flat object (`{...}`, not `[...]`), keys `"M{m}G{g}"`:
199
+
200
+ - `m` = match index in `@Input`, from 1.
201
+ - `g` = group index **within that match**, from 1: `G1` is always the **whole match** (`Match.Groups[0]`, same as `RegexMatch`); `G2`, `G3`... are the capture groups `(...)` in `@Pattern`, if any.
202
+ - No match → `"{}"`.
203
+ - Values have tab/newline/CR (`\t`, `\n`, `\r`) removed and `"` escaped.
204
+
205
+ `RegexMatchArray('SDT: 0901 - 0902', '(\d{4})')` (one group) returns:
206
+
207
+ ```json
208
+ { "M1G1": "0901", "M1G2": "0901", "M2G1": "0902", "M2G2": "0902" }
209
+ ```
210
+
211
+ (G1 = G2 here because the whole match equals the only group; with no groups each match yields only `G1`.)
212
+
213
+ Parsing in T-SQL (it is an object):
214
+
215
+ - `OPENJSON(@result)` still works but returns `key/value/type` rows (one row per key like `"M1G1"`), not one structured row per match.
216
+ - One known match/group: `JSON_VALUE(@result, '$."M1G1"')`.
217
+ - Iterate all: `OPENJSON(@result)` without path, parse the `M{m}G{g}` prefix from `key` to group by match.
218
+
219
+ ---
220
+
221
+ ## 5. When to use them in SPs
222
+
223
+ - Always confirm the function exists first (`fui schema` filtered to functions, or `SELECT * FROM sys.objects WHERE type='FS'`) — usable only if the assembly was `CREATE ASSEMBLY`'d on that DB. Not every DB has them.
224
+ - Use in `spAPI_*` when needed: external API (`httpCall`), uploaded image processing (`ImageResize`/`ImageCrop`), notification email (`sendMail`), regex validation (`RegexIsMatch`), saving files to server disk (`FileWriter`).
225
+ - JSON `nvarchar(max)` results (`httpCall`, `RegexMatchArray`) → parse with `OPENJSON`/`JSON_VALUE` in T-SQL before returning via tAPI.
@@ -36,7 +36,7 @@ Before any module.json:
36
36
 
37
37
  1. Tables + PK + FK for **all** entities. Per [db-table-design.md](db-table-design.md): `tbl` + PascalCase, PK `[Entity]ID`, audit `CreateUser/CreateTime/UpdateUser/UpdateTime`.
38
38
  2. n-n → junction table (e.g. `tblUserRole`).
39
- 3. **`CREATE TABLE` is run by the developer** — fui blocks it; present DDL for review. Existing tables, gated by data loss: `ALTER TABLE ... ADD` and `DROP CONSTRAINT` run directly; `ALTER COLUMN` needs `--confirm-write`; `DROP COLUMN`/`DROP TABLE`/`SWITCH` hard-blocked.
39
+ 3. **`CREATE TABLE` is run by the developer** — fui has no guard for it, so do not run it yourself; present DDL for review. Existing tables, gated by data loss: `ALTER TABLE ... ADD` and `DROP CONSTRAINT` run directly; `ALTER COLUMN` needs `--confirm-write`; `DROP COLUMN`/`DROP TABLE`/`SWITCH` hard-blocked.
40
40
  4. Design lookup/catalog tables early (shared dropdowns).
41
41
 
42
42
  **Output:** tables + relations, user-confirmed.
@@ -57,7 +57,7 @@ Each entity → standard set `List/Get/Insert/Update/Delete` + `AUTH_*Options` f
57
57
 
58
58
  ## Step 4b — Create right DEFINITIONS before any guarded module.json
59
59
 
60
- A code exists only as a row in the project's `acc.tblSysRight`/`acc.tblFunction`. `FunctionRight.includes('42')` **does not create** 42; a missing code silently hides the control ([permission-system.md](permission-system.md) §"Mandatory: a right code must EXIST before you guard with it"). Often forgotten: it is not in the project DB, schema, or any workspace file.
60
+ A code exists only as a row in the project's `acc.tblSysRight`/`acc.tblFunction`. `FunctionRight.includes('42')` **does not create** 42; a missing code silently hides the control ([permission-system.md](permission-system.md) §"A right code must EXIST before guarding with it"). Often forgotten: it is not in the project DB, schema, or any workspace file.
61
61
 
62
62
  1. `fui right system list` + `fui right function list` — see what exists (new projects usually empty); writes cache `_db/rights.json` **keyed by `apiName`** for `fui module validate`. Multi-alias → run **per alias**; never reuse one alias's cache for another.
63
63
  2. Each Step-0 role → a `SystemRight` level (`fui right system new`, level + note). `9` = Project Admin exists already.