@lowdefy/docs-content 0.0.0-experimental-20260928120643 → 6.1.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.
- package/README.md +0 -1
- package/content/actions/link.md +0 -17
- package/content/actions/login.md +5 -23
- package/content/actions/logout.md +5 -5
- package/content/actions/pdfmake.md +2 -2
- package/content/actions/setdarkmode.md +2 -2
- package/content/agents/agent-properties.md +2 -4
- package/content/agents/agentchat.md +6 -21
- package/content/agents/agentconversations.md +2 -2
- package/content/concepts/ai-agent-docs.md +101 -313
- package/content/concepts/cli.md +3 -34
- package/content/concepts/connections-and-requests.md +0 -402
- package/content/concepts/custom-html.md +0 -40
- package/content/concepts/custom-styling.md +5 -50
- package/content/concepts/events-and-actions.md +2 -64
- package/content/concepts/i18n.md +2 -7
- package/content/concepts/keyboard-shortcuts.md +2 -2
- package/content/concepts/layout-overview.md +1 -1
- package/content/concepts/logger.md +0 -81
- package/content/concepts/lowdefy-schema.md +1 -4
- package/content/concepts/menus.md +7 -7
- package/content/concepts/module-authoring.md +2 -2
- package/content/connections/anthropic.md +2 -162
- package/content/connections/awss3.md +0 -54
- package/content/connections/google.md +2 -162
- package/content/connections/mongodb.md +3 -121
- package/content/connections/openai-connection.md +2 -162
- package/content/container-blocks/affix.md +7 -47
- package/content/container-blocks/alert.md +24 -76
- package/content/container-blocks/badge.md +24 -100
- package/content/container-blocks/box.md +9 -9
- package/content/container-blocks/card.md +25 -80
- package/content/container-blocks/carousel.md +7 -60
- package/content/container-blocks/collapse.md +9 -87
- package/content/container-blocks/configprovider.md +13 -35
- package/content/container-blocks/confirmmodal.md +23 -69
- package/content/container-blocks/content.md +98 -62
- package/content/container-blocks/descriptions.md +4 -45
- package/content/container-blocks/drawer.md +28 -92
- package/content/container-blocks/dropdownmenu.md +31 -119
- package/content/container-blocks/flex.md +62 -40
- package/content/container-blocks/footer.md +11 -15
- package/content/container-blocks/googlemapsheatmap.md +1 -1
- package/content/container-blocks/googlemapsscript.md +2 -2
- package/content/container-blocks/header.md +36 -71
- package/content/container-blocks/label.md +7 -34
- package/content/container-blocks/layout.md +34 -52
- package/content/container-blocks/masonry.md +71 -17
- package/content/container-blocks/mobilemenu.md +88 -131
- package/content/container-blocks/modal.md +26 -114
- package/content/container-blocks/pageheadermenu.md +31 -81
- package/content/container-blocks/pagesidebarlayout.md +26 -169
- package/content/container-blocks/pagesidermenu.md +42 -95
- package/content/container-blocks/popover.md +31 -34
- package/content/container-blocks/result.md +21 -32
- package/content/container-blocks/sider.md +122 -85
- package/content/container-blocks/span.md +7 -7
- package/content/container-blocks/spinner.md +3 -3
- package/content/container-blocks/splitter.md +68 -150
- package/content/container-blocks/tabs.md +12 -64
- package/content/container-blocks/tooltip.md +46 -22
- package/content/container-blocks/watermark.md +4 -22
- package/content/controls/reject.md +2 -4
- package/content/controls/throw.md +0 -1
- package/content/controls/try.md +2 -3
- package/content/deployment/deployment-environments.md +6 -8
- package/content/deployment/docker.md +7 -22
- package/content/deployment/node-server.md +0 -4
- package/content/deployment/vercel.md +6 -13
- package/content/display-blocks/aggrid.md +11 -26
- package/content/display-blocks/anchor.md +15 -15
- package/content/display-blocks/avatar.md +37 -57
- package/content/display-blocks/breadcrumb.md +21 -70
- package/content/display-blocks/button.md +18 -92
- package/content/display-blocks/calendar.md +0 -12
- package/content/display-blocks/dangeroushtml.md +1 -1
- package/content/display-blocks/difflist.md +1 -1
- package/content/display-blocks/difftimeline.md +1 -1
- package/content/display-blocks/divider.md +3 -64
- package/content/display-blocks/dropdownbutton.md +28 -79
- package/content/display-blocks/echart.md +1 -35
- package/content/display-blocks/floatbutton.md +42 -129
- package/content/display-blocks/html.md +1 -61
- package/content/display-blocks/icon.md +67 -250
- package/content/display-blocks/menu.md +73 -145
- package/content/display-blocks/message.md +124 -38
- package/content/display-blocks/notification.md +222 -94
- package/content/display-blocks/paragraph.md +9 -59
- package/content/display-blocks/progress.md +17 -96
- package/content/display-blocks/qrcode.md +4 -19
- package/content/display-blocks/s3download.md +1 -1
- package/content/display-blocks/search.md +7 -17
- package/content/display-blocks/skeletonbutton.md +0 -1
- package/content/display-blocks/skeletoninput.md +0 -1
- package/content/display-blocks/statistic.md +17 -75
- package/content/display-blocks/steps.md +25 -58
- package/content/display-blocks/tag.md +17 -102
- package/content/display-blocks/title.md +11 -50
- package/content/display-blocks/tour.md +23 -33
- package/content/input-blocks/autocomplete.md +9 -81
- package/content/input-blocks/buttonselector.md +8 -45
- package/content/input-blocks/checkboxselector.md +7 -97
- package/content/input-blocks/checkboxswitch.md +3 -33
- package/content/input-blocks/colorselector.md +14 -63
- package/content/input-blocks/daterangeselector.md +45 -165
- package/content/input-blocks/dateselector.md +41 -139
- package/content/input-blocks/datetimeselector.md +49 -158
- package/content/input-blocks/inputgallery.md +3 -3
- package/content/input-blocks/monthselector.md +52 -120
- package/content/input-blocks/multipleselector.md +39 -150
- package/content/input-blocks/numberinput.md +12 -98
- package/content/input-blocks/pagination.md +14 -70
- package/content/input-blocks/paragraphinput.md +37 -51
- package/content/input-blocks/passwordinput.md +6 -60
- package/content/input-blocks/phonenumberinput.md +26 -59
- package/content/input-blocks/radioselector.md +7 -83
- package/content/input-blocks/ratingslider.md +26 -57
- package/content/input-blocks/s3uploadbutton.md +24 -24
- package/content/input-blocks/s3uploaddragger.md +3 -3
- package/content/input-blocks/s3uploadphoto.md +5 -5
- package/content/input-blocks/segmentedselector.md +32 -72
- package/content/input-blocks/selector.md +21 -109
- package/content/input-blocks/switch.md +17 -65
- package/content/input-blocks/textarea.md +3 -32
- package/content/input-blocks/textinput.md +28 -69
- package/content/input-blocks/tiptapmentioninput.md +1 -1
- package/content/input-blocks/titleinput.md +12 -39
- package/content/input-blocks/treeinput.md +25 -49
- package/content/input-blocks/treemultipleselector.md +10 -127
- package/content/input-blocks/treeselector.md +9 -97
- package/content/input-blocks/upload.md +15 -64
- package/content/input-blocks/uploaddragger.md +1 -17
- package/content/input-blocks/uploadphoto.md +1 -18
- package/content/input-blocks/weekselector.md +55 -120
- package/content/list-blocks/controlledlist.md +15 -24
- package/content/list-blocks/list.md +11 -11
- package/content/list-blocks/masonrylist.md +34 -7
- package/content/migration/upgrade-guide.md +0 -1
- package/content/operators/_app.md +1 -2
- package/content/operators/_error.md +1 -23
- package/content/operators/_get.md +6 -6
- package/content/operators/_operator.md +3 -17
- package/content/operators/_ref.md +0 -258
- package/content/operators/_type.md +3 -14
- package/content/operators/_user.md +3 -3
- package/content/other/introduction.md +1 -1
- package/content/plugins/plugins-dev.md +0 -66
- package/content/plugins/plugins-introduction.md +1 -27
- package/content/testing/e2e-configuration.md +1 -8
- package/content/testing/e2e-getting-started.md +1 -1
- package/content/testing/e2e-introduction.md +1 -1
- package/content/tutorial/tutorial-actions-operators.md +2 -2
- package/content/tutorial/tutorial-add-blocks.md +2 -2
- package/content/tutorial/tutorial-create-page.md +2 -48
- package/content/tutorial/tutorial-display-data-page.md +3 -3
- package/content/tutorial/tutorial-requests-sql.md +0 -153
- package/content/tutorial/tutorial-start.md +1 -66
- package/content/user-authentication/login-and-logout.md +8 -158
- package/content/user-authentication/users-introduction.md +0 -8
- package/index.json +2 -6065
- package/package.json +1 -7
- package/scripts/extractAgentDocs.js +4 -36
- package/content/actions/emailotpsend.md +0 -58
- package/content/actions/emailotpverify.md +0 -77
- package/content/actions/getlocalstorage.md +0 -41
- package/content/actions/magiclinkverify.md +0 -113
- package/content/actions/posthogalias.md +0 -29
- package/content/actions/posthogcapture.md +0 -49
- package/content/actions/posthogcapturepageleave.md +0 -38
- package/content/actions/posthogfeatureflag.md +0 -50
- package/content/actions/posthoggroup.md +0 -37
- package/content/actions/posthogidentify.md +0 -54
- package/content/actions/posthoginit.md +0 -66
- package/content/actions/posthogoptin.md +0 -29
- package/content/actions/posthogoptout.md +0 -29
- package/content/actions/posthogpageview.md +0 -37
- package/content/actions/posthogreloadfeatureflags.md +0 -47
- package/content/actions/posthogreset.md +0 -28
- package/content/actions/posthogsetpersonproperties.md +0 -36
- package/content/actions/removelocalstorage.md +0 -26
- package/content/actions/setlocalstorage.md +0 -32
- package/content/adapter-reference/mongodbauthadapter.md +0 -46
- package/content/agents/agents-introduction.md +0 -107
- package/content/callback-reference/auth0logoutcallback.md +0 -54
- package/content/concepts/blocks.md +0 -150
- package/content/concepts/dynamic-page-content.md +0 -304
- package/content/concepts/html-attributes.md +0 -285
- package/content/concepts/lowdefy-api.md +0 -1110
- package/content/concepts/notifications.md +0 -155
- package/content/concepts/page-and-app-state.md +0 -244
- package/content/concepts/theming.md +0 -582
- package/content/connections/aigateway.md +0 -368
- package/content/connections/axioshttp.md +0 -181
- package/content/controls/while.md +0 -46
- package/content/display-blocks/clickablehtml.md +0 -239
- package/content/display-blocks/eventlog.md +0 -363
- package/content/display-blocks/pollingtimer.md +0 -263
- package/content/input-blocks/captcha.md +0 -40
- package/content/input-blocks/placesautocomplete.md +0 -319
- package/content/input-blocks/tagmultipleselector.md +0 -114
- package/content/input-blocks/tagselector.md +0 -114
- package/content/migration/auth-upgrade.md +0 -327
- package/content/migration/v3-to-v4.md +0 -417
- package/content/migration/v6-to-v7.md +0 -222
- package/content/operators/_cron.md +0 -292
- package/content/other/mongodbadapter.md +0 -3
- package/content/plugins/plugins-icon-sets.md +0 -267
- package/content/plugins/posthog.md +0 -275
- package/content/provider-reference/auth-js-providers.md +0 -107
- package/content/provider-reference/openidconnectprovider.md +0 -44
- package/content/testing/config-tests.md +0 -278
- package/content/tutorial/tutorial-requests-api.md +0 -155
- package/content/user-authentication/auth-configuration.md +0 -266
- package/content/user-authentication/auth-steps.md +0 -138
- package/content/user-authentication/mcp-oauth.md +0 -292
- package/content/user-authentication/organizations.md +0 -221
- package/content/user-authentication/protected-pages-apis.md +0 -97
- package/content/user-authentication/roles.md +0 -49
- package/content/user-authentication/two-factor.md +0 -229
- package/content/user-authentication/user-object.md +0 -58
- package/content/websockets/websocket-publish.md +0 -108
- package/content/websockets/websocket-subscriptions.md +0 -100
- package/content/websockets/websockets-introduction.md +0 -97
- package/hazards.json +0 -44
- package/scripts/hashDocSources.js +0 -38
- package/scripts/recordSourceHashes.js +0 -88
|
@@ -25,7 +25,7 @@ Dot notation is supported for reading fields of the error, such as `_error: stat
|
|
|
25
25
|
Inside a `:catch`, `_error` is an Error rebuilt from the caught error with only these fields:
|
|
26
26
|
|
|
27
27
|
- `name`: The error class name, such as `RequestError`.
|
|
28
|
-
- `message`: The real error message, with the values of known secrets (`_secret` values, `CRON_SECRET` and `
|
|
28
|
+
- `message`: The real error message, with the values of known secrets (`_secret` values, `CRON_SECRET` and `AUTH_SECRET`) replaced by `[REDACTED]`.
|
|
29
29
|
- `code`: The error's `code`, if it has one.
|
|
30
30
|
- `statusCode`: The error's HTTP status, if it has one.
|
|
31
31
|
- `handled`: `true` once the server has logged the error.
|
|
@@ -60,28 +60,6 @@ error:
|
|
|
60
60
|
|
|
61
61
|
`code` and `statusCode` follow one rule across connection libraries. `code` is the error's own `code`, such as `ERR_BAD_REQUEST` from AxiosHttp or `11000` from MongoDB. `statusCode` is the first number among the error's `statusCode`, `status` and `response.status`. A request error takes both from the error the connection threw, so `_error: statusCode` reads the HTTP status of a failed `AxiosHttp` request without reading the cause.
|
|
62
62
|
|
|
63
|
-
##### Service errors and rate limits
|
|
64
|
-
|
|
65
|
-
A request that fails because of the service it calls, not its config, is a `ServiceError`: a network failure, a timeout, a 5xx response, or a rate limit (HTTP 429, including a provider's throttling error). Every other request failure is a `RequestError`. A routine can tell a rate limit from a fault and try again later:
|
|
66
|
-
|
|
67
|
-
```yaml
|
|
68
|
-
- :try:
|
|
69
|
-
- id: summarize
|
|
70
|
-
type: GenerateText
|
|
71
|
-
connectionId: claude
|
|
72
|
-
properties:
|
|
73
|
-
model: claude-haiku-4-5
|
|
74
|
-
prompt:
|
|
75
|
-
_payload: text
|
|
76
|
-
:catch:
|
|
77
|
-
- :if:
|
|
78
|
-
_eq: [{ _error: statusCode }, 429]
|
|
79
|
-
:then:
|
|
80
|
-
- :return:
|
|
81
|
-
status: busy
|
|
82
|
-
- :throw: Summary failed
|
|
83
|
-
```
|
|
84
|
-
|
|
85
63
|
#### Arguments
|
|
86
64
|
|
|
87
65
|
###### string
|
|
@@ -37,12 +37,12 @@ _get:
|
|
|
37
37
|
key:
|
|
38
38
|
_state: status
|
|
39
39
|
from:
|
|
40
|
-
new:
|
|
41
|
-
escalated:
|
|
42
|
-
investigation_started:
|
|
43
|
-
client_contacted:
|
|
44
|
-
awaiting_confirmation:
|
|
45
|
-
closed:
|
|
40
|
+
new: AiTwoTonePlusCircle
|
|
41
|
+
escalated: AiOutlineExclamationCircle
|
|
42
|
+
investigation_started: AiTwoToneTool
|
|
43
|
+
client_contacted: AiTwoToneSound
|
|
44
|
+
awaiting_confirmation: AiOutlineLike
|
|
45
|
+
closed: AiOutlineStop
|
|
46
46
|
```
|
|
47
47
|
Returns: The icon corresponding to the status in state.
|
|
48
48
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# _operator
|
|
2
2
|
|
|
3
3
|
```
|
|
4
|
-
(arguments: {
|
|
4
|
+
(arguments: {operator: string, params: any): any
|
|
5
5
|
```
|
|
6
6
|
|
|
7
7
|
The `_operator` operator evaluates an operator with the given params. This is useful if the operator needs to be chosen dynamically. The `_operator` cannot evaluate itself.
|
|
@@ -11,16 +11,15 @@ The `_operator` operator evaluates an operator with the given params. This is us
|
|
|
11
11
|
#### Arguments
|
|
12
12
|
|
|
13
13
|
###### object
|
|
14
|
-
- `
|
|
14
|
+
- `operator: string`: The name of the operator to evaluate.
|
|
15
15
|
- `params: any`: The params to give to the operator.
|
|
16
|
-
- `operators: string[]`: The operators the call may evaluate. Required when `name` is read at runtime, from state, a request or a payload: the build fails without it, loads exactly these operators with the page (or the server), and `_operator` refuses any other name. A listed operator (`_number`) allows all its methods; a listed method (`_number.round`) allows only that method.
|
|
17
16
|
|
|
18
17
|
#### Examples
|
|
19
18
|
|
|
20
19
|
###### Get a value from `urlQuery` if specified, else use the value in `state`:
|
|
21
20
|
```yaml
|
|
22
21
|
_operator:
|
|
23
|
-
|
|
22
|
+
operator:
|
|
24
23
|
_if:
|
|
25
24
|
test:
|
|
26
25
|
_eq:
|
|
@@ -32,16 +31,3 @@ _operator:
|
|
|
32
31
|
key: field_to_get
|
|
33
32
|
```
|
|
34
33
|
Returns: Value from `urlQuery` if `location_selector == url_query`, else the value from `state`.
|
|
35
|
-
|
|
36
|
-
###### Apply an aggregation chosen by the user:
|
|
37
|
-
```yaml
|
|
38
|
-
_operator:
|
|
39
|
-
name:
|
|
40
|
-
_state: aggregation # _sum or _product
|
|
41
|
-
operators:
|
|
42
|
-
- _sum
|
|
43
|
-
- _product
|
|
44
|
-
params:
|
|
45
|
-
_state: values
|
|
46
|
-
```
|
|
47
|
-
Returns: The sum or product of `values`. Any other name in `aggregation` is refused.
|
|
@@ -78,261 +78,3 @@ The file path to the referenced file, from the root of the project directory.
|
|
|
78
78
|
> Within module content, the `module` field can reference a declared dependency name (from the module's `dependencies` in `module.lowdefy.yaml`). The build resolves abstract names to concrete entry IDs via the dependency wiring. From app-level config, `module` is always a concrete entry ID.
|
|
79
79
|
|
|
80
80
|
#### Examples
|
|
81
|
-
|
|
82
|
-
###### Reference pages:
|
|
83
|
-
```yaml
|
|
84
|
-
# lowdefy.yaml
|
|
85
|
-
lowdefy: 6.0.0
|
|
86
|
-
pages:
|
|
87
|
-
- _ref: pages/page1.yaml
|
|
88
|
-
- _ref: pages/page2.yaml
|
|
89
|
-
```
|
|
90
|
-
```yaml
|
|
91
|
-
# pages/page1.yaml
|
|
92
|
-
id: page1
|
|
93
|
-
type: PageHeaderMenu
|
|
94
|
-
blocks:
|
|
95
|
-
# ...
|
|
96
|
-
```
|
|
97
|
-
```yaml
|
|
98
|
-
# pages/page2.yaml
|
|
99
|
-
id: page2
|
|
100
|
-
type: PageHeaderMenu
|
|
101
|
-
blocks:
|
|
102
|
-
# ...
|
|
103
|
-
```
|
|
104
|
-
Returns:
|
|
105
|
-
```
|
|
106
|
-
lowdefy: 6.0.0
|
|
107
|
-
pages:
|
|
108
|
-
- id: page1
|
|
109
|
-
type: PageHeaderMenu
|
|
110
|
-
blocks:
|
|
111
|
-
# ...
|
|
112
|
-
- id: page2
|
|
113
|
-
type: PageHeaderMenu
|
|
114
|
-
blocks:
|
|
115
|
-
# ...
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
###### Using a standardized input label template:
|
|
119
|
-
```yaml
|
|
120
|
-
blocks:
|
|
121
|
-
- id: name
|
|
122
|
-
type: TextInput
|
|
123
|
-
properties:
|
|
124
|
-
label:
|
|
125
|
-
_ref:
|
|
126
|
-
path: label.yaml
|
|
127
|
-
vars:
|
|
128
|
-
title: Name
|
|
129
|
-
description: Your name and surname.
|
|
130
|
-
- id: age
|
|
131
|
-
type: NumberInput
|
|
132
|
-
properties:
|
|
133
|
-
label:
|
|
134
|
-
_ref:
|
|
135
|
-
path: label.yaml
|
|
136
|
-
vars:
|
|
137
|
-
title: Age
|
|
138
|
-
description: Your age.
|
|
139
|
-
```
|
|
140
|
-
```yaml
|
|
141
|
-
# label.yaml
|
|
142
|
-
title:
|
|
143
|
-
_var: title
|
|
144
|
-
extra:
|
|
145
|
-
_var: description
|
|
146
|
-
span: 8
|
|
147
|
-
colon: false
|
|
148
|
-
extraStyle:
|
|
149
|
-
color: '#546358'
|
|
150
|
-
```
|
|
151
|
-
Returns:
|
|
152
|
-
```yaml
|
|
153
|
-
blocks:
|
|
154
|
-
- id: name
|
|
155
|
-
type: TextInput
|
|
156
|
-
properties:
|
|
157
|
-
label:
|
|
158
|
-
title: Name
|
|
159
|
-
extra: Your name and surname.
|
|
160
|
-
span: 8
|
|
161
|
-
colon: false
|
|
162
|
-
extraStyle:
|
|
163
|
-
color: '#546358'
|
|
164
|
-
- id: age
|
|
165
|
-
type: NumberInput
|
|
166
|
-
properties:
|
|
167
|
-
label:
|
|
168
|
-
title: Age
|
|
169
|
-
extra: Your age.
|
|
170
|
-
span: 8
|
|
171
|
-
colon: false
|
|
172
|
-
extraStyle:
|
|
173
|
-
color: '#546358'
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
###### Use key:
|
|
177
|
-
```yaml
|
|
178
|
-
# lowdefy.yaml
|
|
179
|
-
lowdefy: 6.0.0
|
|
180
|
-
version:
|
|
181
|
-
_ref:
|
|
182
|
-
path: package.json
|
|
183
|
-
field: version
|
|
184
|
-
```
|
|
185
|
-
```json
|
|
186
|
-
// package.json
|
|
187
|
-
{
|
|
188
|
-
"version": "1.0.0"
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
Returns:
|
|
192
|
-
```
|
|
193
|
-
lowdefy: 6.0.0
|
|
194
|
-
version: 1.0.0
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
###### Local or shared resolver:
|
|
198
|
-
|
|
199
|
-
This resolver function will first look for the configuration file in the current working directory, but if the file is not found it will be read from an adjacent "shared" directory. This pattern can be used to build apps that mostly use a shared configuration, with a few components that are customised per app.
|
|
200
|
-
|
|
201
|
-
```js
|
|
202
|
-
// resolvers/useLocalOrSharedConfig.js
|
|
203
|
-
const fs = require('fs');
|
|
204
|
-
const path = require('path');
|
|
205
|
-
const { promisify } = require('util');
|
|
206
|
-
|
|
207
|
-
const readFilePromise = promisify(fs.readFile);
|
|
208
|
-
|
|
209
|
-
async function useLocalOrSharedConfig(refPath, vars, context) {
|
|
210
|
-
let fileContent
|
|
211
|
-
try {
|
|
212
|
-
fileContent = await readFilePromise(path.resolve(refPath), 'utf8');
|
|
213
|
-
return fileContent;
|
|
214
|
-
} catch (error) {
|
|
215
|
-
if (error.code === 'ENOENT') {
|
|
216
|
-
fileContent = readFilePromise(path.resolve('../shared', refPath), 'utf8');
|
|
217
|
-
return fileContent;
|
|
218
|
-
}
|
|
219
|
-
throw error;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
module.exports = useLocalOrSharedConfig;
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
```yaml
|
|
229
|
-
// lowdefy.yaml
|
|
230
|
-
lowdefy: 6.0.0
|
|
231
|
-
|
|
232
|
-
cli:
|
|
233
|
-
refResolver: resolvers/useLocalOrSharedConfig.js
|
|
234
|
-
|
|
235
|
-
pages:
|
|
236
|
-
- _ref: pages/local-page.yaml
|
|
237
|
-
- _ref: pages/shared-page.yaml
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
###### This transformer adds a standard footer to each page:
|
|
241
|
-
|
|
242
|
-
```js
|
|
243
|
-
// transformers/addFooter.js
|
|
244
|
-
|
|
245
|
-
function addFooter(page, vars) {
|
|
246
|
-
const footer = {
|
|
247
|
-
// ...
|
|
248
|
-
};
|
|
249
|
-
page.areas.footer = footer;
|
|
250
|
-
return page;
|
|
251
|
-
}
|
|
252
|
-
module.exports = addFooter;
|
|
253
|
-
```
|
|
254
|
-
```yaml
|
|
255
|
-
// lowdefy.yaml
|
|
256
|
-
lowdefy: 6.0.0
|
|
257
|
-
|
|
258
|
-
pages:
|
|
259
|
-
- _ref:
|
|
260
|
-
path: pages/page1.yaml
|
|
261
|
-
transformer: transformers/addFooter.js
|
|
262
|
-
```
|
|
263
|
-
###### Reference a module component:
|
|
264
|
-
```yaml
|
|
265
|
-
blocks:
|
|
266
|
-
- _ref:
|
|
267
|
-
module: notifications
|
|
268
|
-
component: notification-badge
|
|
269
|
-
vars:
|
|
270
|
-
position: top-right
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
###### Data export with key extraction:
|
|
274
|
-
```yaml
|
|
275
|
-
icon:
|
|
276
|
-
_ref:
|
|
277
|
-
module: events
|
|
278
|
-
component: event_types
|
|
279
|
-
key: login.icon
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
###### Cross-module component ref within a module:
|
|
283
|
-
```yaml
|
|
284
|
-
# contacts module embedding companies' selector
|
|
285
|
-
blocks:
|
|
286
|
-
- _ref:
|
|
287
|
-
module: companies # abstract dependency name
|
|
288
|
-
component: company-selector
|
|
289
|
-
vars:
|
|
290
|
-
field_id: company_id
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
###### Module menu inside a MenuGroup:
|
|
294
|
-
```yaml
|
|
295
|
-
menus:
|
|
296
|
-
- id: main
|
|
297
|
-
links:
|
|
298
|
-
- id: home
|
|
299
|
-
type: MenuLink
|
|
300
|
-
pageId: home
|
|
301
|
-
- id: team-admin
|
|
302
|
-
type: MenuGroup
|
|
303
|
-
properties:
|
|
304
|
-
title: Team Admin
|
|
305
|
-
links:
|
|
306
|
-
_ref:
|
|
307
|
-
module: team-users
|
|
308
|
-
menu: default
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
###### Using ES Modules with `.mjs` file extension:
|
|
312
|
-
|
|
313
|
-
```js
|
|
314
|
-
// resolvers/useLocalOrSharedConfig.mjs
|
|
315
|
-
import fs from 'fs';
|
|
316
|
-
import path from 'path';
|
|
317
|
-
import { promisify } from 'util';
|
|
318
|
-
|
|
319
|
-
const readFilePromise = promisify(fs.readFile);
|
|
320
|
-
|
|
321
|
-
async function useLocalOrSharedConfig(refPath, vars, context) {
|
|
322
|
-
let fileContent
|
|
323
|
-
try {
|
|
324
|
-
fileContent = await readFilePromise(path.resolve(refPath), 'utf8');
|
|
325
|
-
return fileContent;
|
|
326
|
-
} catch (error) {
|
|
327
|
-
if (error.code === 'ENOENT') {
|
|
328
|
-
fileContent = readFilePromise(path.resolve('../shared', refPath), 'utf8');
|
|
329
|
-
return fileContent;
|
|
330
|
-
}
|
|
331
|
-
throw error;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
export default useLocalOrSharedConfig;
|
|
338
|
-
```
|
|
@@ -29,7 +29,6 @@ The regex operator has shorthand argument definitions that can be used on web cl
|
|
|
29
29
|
- `null`
|
|
30
30
|
- `undefined`
|
|
31
31
|
- `none` (`null` or `undefined`)
|
|
32
|
-
- `empty` (`null`, `undefined`, `''`, or `[]` - note that `0`, `false` and `{}` are not empty)
|
|
33
32
|
- `primitive` (`undefined`, `null`, `string`, `number`, `boolean`, or `date`)
|
|
34
33
|
- `on: any`: The value to test. One of `on` or `key` must be specified unless the operator is used in an input block.
|
|
35
34
|
- `key: string`: The key of a value in `state` to test. One of `on` or `key` must be specified unless the operator is used in an input block.
|
|
@@ -61,23 +60,13 @@ Returns: `true` if a number.
|
|
|
61
60
|
id: input
|
|
62
61
|
type: TextInput
|
|
63
62
|
validate:
|
|
64
|
-
- message: This field
|
|
63
|
+
- message: This field id required.
|
|
65
64
|
status: error
|
|
66
65
|
pass:
|
|
67
66
|
_not:
|
|
68
|
-
_type:
|
|
67
|
+
_type: none
|
|
69
68
|
```
|
|
70
|
-
Returns: `true` if the input
|
|
71
|
-
|
|
72
|
-
###### Check if a value is empty:
|
|
73
|
-
```yaml
|
|
74
|
-
_type:
|
|
75
|
-
type: empty
|
|
76
|
-
on:
|
|
77
|
-
_state: selected_tags
|
|
78
|
-
```
|
|
79
|
-
Returns: `true` if `selected_tags` is `null`, `undefined`, `''` or `[]`, and `false` for any
|
|
80
|
-
other value, including `0`, `false` and `{}`.
|
|
69
|
+
Returns: `true` if the input is none.
|
|
81
70
|
|
|
82
71
|
###### Test if an id in the `urlQuery` is undefined or null:
|
|
83
72
|
```yaml
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
}): any
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
The `_user` operator gets a value from the [`user`](/user-object) object
|
|
13
|
+
The `_user` operator gets a value from the [`user`](/user-object) object. The `user` object contains the data in the user idToken if OpenID Connect authentication is configured and a user is logged in.
|
|
14
14
|
|
|
15
15
|
#### Arguments
|
|
16
16
|
|
|
@@ -50,7 +50,7 @@ Returns: The entire `user` object.
|
|
|
50
50
|
###### Dot notation:
|
|
51
51
|
Assuming user:
|
|
52
52
|
```yaml
|
|
53
|
-
|
|
53
|
+
sub: abc123
|
|
54
54
|
name: User Name
|
|
55
55
|
my_object:
|
|
56
56
|
subfield: 'Value'
|
|
@@ -76,7 +76,7 @@ Returns: The value of `might_not_exist`, or `"Default value"`.
|
|
|
76
76
|
###### Block list indices:
|
|
77
77
|
Assuming `user`:
|
|
78
78
|
```yaml
|
|
79
|
-
|
|
79
|
+
sub: abc123
|
|
80
80
|
name: User Name
|
|
81
81
|
my_array:
|
|
82
82
|
- value: 0
|
|
@@ -14,7 +14,7 @@ pnpx lowdefy@5 init && pnpx lowdefy@5 dev
|
|
|
14
14
|
|
|
15
15
|
This will create a file called `lowdefy.yaml` in the current working directory that contains the configuration for a Lowdefy app (as well as a `.gitignore`) and launch a local development server at http://localhost:3000. Make changes in the `lowdefy.yaml` file to see them reflect in the app.
|
|
16
16
|
|
|
17
|
-
> You need Node
|
|
17
|
+
> You need Node v22 or newer and pnpm installed to run Lowdefy. For more details see [the tutorial](/tutorial-start).
|
|
18
18
|
|
|
19
19
|
## Why create web apps using a YAML schema?
|
|
20
20
|
|
|
@@ -31,7 +31,6 @@ Lowdefy uses the [`exports`](https://nodejs.org/api/packages.html#exports) packa
|
|
|
31
31
|
"./auth/providers": "./src/auth/providers.js",
|
|
32
32
|
"./blocks": "./src/blocks.js",
|
|
33
33
|
"./connections": "./src/connections.js",
|
|
34
|
-
"./iconSets": "./src/iconSets.js",
|
|
35
34
|
"./metas": "./src/metas.js",
|
|
36
35
|
"./operators/client": "./src/operators/client.js",
|
|
37
36
|
"./operators/server": "./src/operators/server.js",
|
|
@@ -68,7 +67,6 @@ export default {
|
|
|
68
67
|
},
|
|
69
68
|
blocks: ['Block'],
|
|
70
69
|
connections: ['Connection'],
|
|
71
|
-
iconSets: ['my-icons'],
|
|
72
70
|
requests: ['Request'],
|
|
73
71
|
operators: {
|
|
74
72
|
build: ['_build_operator', '_shared_operator'],
|
|
@@ -78,8 +76,6 @@ export default {
|
|
|
78
76
|
};
|
|
79
77
|
```
|
|
80
78
|
|
|
81
|
-
`iconSets` declares icon sets, which the `./iconSets` entry point exports as data. See [Icon Sets](/plugins-icon-sets).
|
|
82
|
-
|
|
83
79
|
The type names do not need to be hard coded, and can be generated by importing the plugin types.
|
|
84
80
|
|
|
85
81
|
###### Example `types.js` for connections
|
|
@@ -115,20 +111,6 @@ export default extractBlockTypes(metas);
|
|
|
115
111
|
|
|
116
112
|
The `extractBlockTypes` function returns `{ blocks, icons, blockMetas }` which the build pipeline uses to resolve plugin types without loading React components or heavy dependencies.
|
|
117
113
|
|
|
118
|
-
A block that runs actions itself, through events it registers with `methods.registerEvent`, lists those action types in `meta.actions` and the operators their params use in `meta.operators`. Each page loads only the plugin code it uses, so the build adds these types to every page that uses the block:
|
|
119
|
-
|
|
120
|
-
```js
|
|
121
|
-
// src/blocks/MyUpload/meta.js
|
|
122
|
-
export default {
|
|
123
|
-
category: 'input',
|
|
124
|
-
icons: [],
|
|
125
|
-
// Registers { id: 'policy', type: 'Request', params: { _event: 'requestId' } }
|
|
126
|
-
actions: ['Request'],
|
|
127
|
-
operators: ['_event'],
|
|
128
|
-
// ...events, properties
|
|
129
|
-
};
|
|
130
|
-
```
|
|
131
|
-
|
|
132
114
|
See the [default Lowdefy block packages](https://github.com/lowdefy/lowdefy/tree/main/packages/plugins/blocks) for full examples.
|
|
133
115
|
|
|
134
116
|
### Named export files
|
|
@@ -151,54 +133,6 @@ Most types are written as standard Javascript functions, and blocks are written
|
|
|
151
133
|
|
|
152
134
|
Lowdefy uses object [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) to provide parameters to the type functions. These parameters include the parameters or properties defined by the user in the Lowdefy configuration. Any operators used in the configuration will be evaluated before it is passed to the type function. Other relevant metadata and helper functions are also passed to the plugin function.
|
|
153
135
|
|
|
154
|
-
### Lazy loading heavy blocks
|
|
155
|
-
|
|
156
|
-
A page loads the code of every block type it uses before it renders. A block that pulls in a large library, such as a chat, a chart engine or a rich text editor, can make that code much bigger, even when the block sits in a closed drawer, an unopened tab or a modal. `createLazyBlock` from `@lowdefy/block-utils` loads such a block's implementation only when the block first mounts.
|
|
157
|
-
|
|
158
|
-
Make a block lazy when it adds more than about 100 kB gzip to the page, **and** it is commonly off-screen at first paint or the page shows useful content without it. Otherwise keep it eager: a lazy block that is visible at first paint appears a moment later.
|
|
159
|
-
|
|
160
|
-
Move the block's component into a file named `*.lazy.js`, and export a wrapper in its place:
|
|
161
|
-
|
|
162
|
-
```js
|
|
163
|
-
// src/blocks/MyChart/MyChart.js - exported from blocks.js as before
|
|
164
|
-
import { createLazyBlock } from '@lowdefy/block-utils';
|
|
165
|
-
import meta from './meta.js';
|
|
166
|
-
import MyChartFallback from './MyChartFallback.js';
|
|
167
|
-
|
|
168
|
-
export default createLazyBlock({
|
|
169
|
-
load: () => import('./MyChart.lazy.js'),
|
|
170
|
-
meta,
|
|
171
|
-
Fallback: MyChartFallback, // optional
|
|
172
|
-
});
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
- `load` returns `import()` of the implementation, whose default export is the block component. Do not import the `*.lazy.js` file statically anywhere else, or it is bundled with the page again.
|
|
176
|
-
- `meta` is the block's `meta.js` object. The wrapper copies it to `.meta`.
|
|
177
|
-
- `Fallback` is optional. It renders while the implementation loads and receives the same props as the block. Use it to keep the block's size, or to show the part users see first, such as a launcher button. Without it the block renders nothing until it has loaded.
|
|
178
|
-
|
|
179
|
-
The wrapper also has a `preload()` function, which starts the load and returns its promise. A `Fallback` can call it on hover.
|
|
180
|
-
|
|
181
|
-
The `.lazy.js` name matters. In production, the Lowdefy server prefetches the `*.lazy.js` chunks of the blocks on a page, so the code is often in the browser cache by the time the block mounts. Other dynamic imports are never prefetched.
|
|
182
|
-
|
|
183
|
-
###### Block methods
|
|
184
|
-
|
|
185
|
-
Declare every method the block registers with `methods.registerMethod` in `meta.methods`:
|
|
186
|
-
|
|
187
|
-
```js
|
|
188
|
-
// src/blocks/MyChart/meta.js
|
|
189
|
-
export default {
|
|
190
|
-
category: 'display',
|
|
191
|
-
methods: {
|
|
192
|
-
reset: 'Reset the zoom.',
|
|
193
|
-
download: 'Download the chart as an image.',
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
The wrapper registers these methods as soon as the block mounts, so `CallMethod` works before the implementation has loaded. Calls made before then wait in order and run once the implementation has registered its methods. After that, calls go straight to the block, as for any other block. A waiting call fails if the block never registers that method, if the load fails, or if the block is removed first. A method that is not declared in `meta.methods` cannot be called before the block loads, and the dev server logs a warning when the block registers it.
|
|
199
|
-
|
|
200
|
-
Do not spread the `methods` prop (`{ ...methods }`) in a lazy block's implementation; pass it on as it is.
|
|
201
|
-
|
|
202
136
|
### Localizing plugin strings
|
|
203
137
|
|
|
204
138
|
Plugins can ship locale-keyed default messages so consumers see translated strings out-of-the-box, and translate strings at runtime via `methods.translate(key, values)` in block / action code. See [i18n](/i18n) for the full plugin-author contract — `./messages` export shape, `package.json` `exports` registration, namespacing convention, and merge precedence.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Lowdefy plugins provides an interface to extend the platform functionality with custom javascript code. Plugins are installed into the Lowdefy app during the Lowdefy build process, and as a result are included as part of the Vite client build output. This enables plugin developers to use any npm packages when building Lowdefy plugins.
|
|
4
4
|
|
|
5
|
-
Lowdefy plugins can be used to create custom blocks, connections, requests, action, operators and auth providers, adapters, callbacks and events.
|
|
5
|
+
Lowdefy plugins can be used to create custom blocks, connections, requests, action, operators and auth providers, adapters, callbacks and events. These plugins are written as standard [npm modules](https://docs.npmjs.com/about-packages-and-modules#about-modules), so community plugins can be published to npm, and the workspace and git protocols can be used for project specific or private plugins. Since plugins are npm packages most Javascript features and third-party npm packages can be used in plugins.
|
|
6
6
|
|
|
7
7
|
## Using a plugin
|
|
8
8
|
|
|
@@ -27,29 +27,3 @@ plugins:
|
|
|
27
27
|
version: 1.0.0
|
|
28
28
|
typePrefix: Other
|
|
29
29
|
```
|
|
30
|
-
|
|
31
|
-
## Plugin meta
|
|
32
|
-
|
|
33
|
-
Every plugin type can describe itself to the build and to AI coding agents through a `meta` object. For blocks it is the `meta.js` next to the block (`category`, `valueType`, `initValue`, `cssKeys`, `events`, `properties`, ...), for requests it is the `meta` property on the request function (`checkRead`, `checkWrite`), and for operators a package may export a `./metas` module with one object per operator, mirroring `./schemas`.
|
|
34
|
-
|
|
35
|
-
All three accept `meta.hazards`: a list of behaviours of the type that its schema cannot express, which the dev server's docs and MCP endpoint return to agents alongside the schema (see [AI agent docs](/ai-agent-docs)). Each hazard is:
|
|
36
|
-
|
|
37
|
-
```yaml
|
|
38
|
-
hazards:
|
|
39
|
-
- id: html-style-stripped # kebab-case, unique within the type
|
|
40
|
-
message: Content is sanitised with DOMPurify, so <style>, <script> and inline event handlers are removed. Use DangerousHtml for a trusted <style> block.
|
|
41
|
-
see: display-blocks/html # a docs slug, or null
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
The `message` is one or two plain sentences saying what happens and what to do instead. Only add a hazard for a behaviour that is verified in the plugin's code — an agent will rely on it.
|
|
45
|
-
|
|
46
|
-
A block marks each property that holds a URL with `urlKind` in its `properties` schema: `url` for a link the engine opens (a colon-less value is read as `https://`), `href` for a link the browser opens, `src` for a resource the block loads, and `srcSet` for a list of sources. A [dynamic blocks policy](/dynamic-page-content#dynamic-blocks-policies) checks marked properties against its pages and origins whatever they are called. Every other property is judged by its name (`url`, `href`, `src`, `image`, ...), so a property whose name looks like a URL key but holds something else, such as a field name, is marked `urlKind: false`.
|
|
47
|
-
|
|
48
|
-
```js
|
|
49
|
-
properties: {
|
|
50
|
-
type: 'object',
|
|
51
|
-
properties: {
|
|
52
|
-
indexUrl: { type: 'string', urlKind: 'src', description: 'URL of the search index.' },
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
```
|
|
@@ -14,7 +14,7 @@ export default createConfig();
|
|
|
14
14
|
|
|
15
15
|
This will:
|
|
16
16
|
- Look for your Lowdefy app in the current directory
|
|
17
|
-
- Build and start
|
|
17
|
+
- Build and start on port 3000
|
|
18
18
|
- Run tests from the `e2e/` directory matching `**/*.spec.js`
|
|
19
19
|
- Take screenshots only on failure
|
|
20
20
|
|
|
@@ -42,10 +42,6 @@ export default createConfig({
|
|
|
42
42
|
});
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
## Reusing a running server
|
|
46
|
-
|
|
47
|
-
Playwright reuses a server that is already listening on the configured port, so starting it once with `pnpm e2e:server` in another terminal skips the build on later runs. Before any test runs, the config asks that server which build it serves. Any other server on the port, such as `lowdefy dev`, a production build, or the e2e server of another app or git worktree, stops the run with an error that names the port, instead of the tests running against it. Stop that server, or give each app or worktree its own `port`.
|
|
48
|
-
|
|
49
45
|
## Multi-app configuration
|
|
50
46
|
|
|
51
47
|
If your project has multiple Lowdefy apps (for example, in a monorepo), use `createMultiAppConfig`:
|
|
@@ -85,8 +81,6 @@ export default {
|
|
|
85
81
|
};
|
|
86
82
|
```
|
|
87
83
|
|
|
88
|
-
Keep `globalSetup` from the base configuration (spreading `base` does), since it runs the server check. To add your own global setup, pass both: `globalSetup: [base.globalSetup, './global-setup.js']`.
|
|
89
|
-
|
|
90
84
|
## Environment variables
|
|
91
85
|
|
|
92
86
|
The configuration sets these environment variables automatically:
|
|
@@ -95,7 +89,6 @@ The configuration sets these environment variables automatically:
|
|
|
95
89
|
|----------|---------|
|
|
96
90
|
| `LOWDEFY_BUILD_DIR` | Absolute path to build artifacts — used by test fixtures to generate the block manifest |
|
|
97
91
|
| `LOWDEFY_E2E_MOCKS_FILE` | Absolute path to `mocks.yaml` — used by test fixtures to load static mocks |
|
|
98
|
-
| `LOWDEFY_E2E_SERVERS` | Port and build directory of each app server — used by the global setup to check a reused server |
|
|
99
92
|
|
|
100
93
|
## Assertion timeouts
|
|
101
94
|
|
|
@@ -40,7 +40,7 @@ Run tests with the Playwright UI (useful for debugging):
|
|
|
40
40
|
pnpm e2e:ui
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
> The first run will take longer because Playwright needs to build and start your Lowdefy app.
|
|
43
|
+
> The first run will take longer because Playwright needs to build and start your Lowdefy app. Subsequent runs reuse the server if it is still running.
|
|
44
44
|
|
|
45
45
|
## Your first test
|
|
46
46
|
|
|
@@ -24,7 +24,7 @@ The e2e server build exposes internal state on `window.lowdefy`, so your tests c
|
|
|
24
24
|
|
|
25
25
|
## Prerequisites
|
|
26
26
|
|
|
27
|
-
- **Node.js
|
|
27
|
+
- **Node.js 22+**
|
|
28
28
|
- A working Lowdefy app (your app must build successfully)
|
|
29
29
|
- **Playwright** (installed automatically during setup)
|
|
30
30
|
|
|
@@ -29,7 +29,7 @@ blocks:
|
|
|
29
29
|
properties:
|
|
30
30
|
title: Reset
|
|
31
31
|
block: true
|
|
32
|
-
icon:
|
|
32
|
+
icon: AiOutlineClear
|
|
33
33
|
################ -------- Copy from here -------- ################
|
|
34
34
|
events:
|
|
35
35
|
onClick:
|
|
@@ -92,7 +92,7 @@ blocks:
|
|
|
92
92
|
block: true
|
|
93
93
|
color: primary
|
|
94
94
|
variant: solid
|
|
95
|
-
icon:
|
|
95
|
+
icon: AiOutlineSave
|
|
96
96
|
################ -------- Copy from here -------- ################
|
|
97
97
|
events:
|
|
98
98
|
onClick:
|
|
@@ -50,7 +50,7 @@ blocks:
|
|
|
50
50
|
title: Reset
|
|
51
51
|
block: true # Make the button fill all the space available to it
|
|
52
52
|
type: default # Make the button a plain button
|
|
53
|
-
icon:
|
|
53
|
+
icon: AiOutlineClear
|
|
54
54
|
- id: submit_button
|
|
55
55
|
type: Button
|
|
56
56
|
layout:
|
|
@@ -59,7 +59,7 @@ blocks:
|
|
|
59
59
|
title: Submit
|
|
60
60
|
block: true
|
|
61
61
|
type: primary # Make the button a primary button with color
|
|
62
|
-
icon:
|
|
62
|
+
icon: AiOutlineSave
|
|
63
63
|
################ -------- Copy to here ---------- ################
|
|
64
64
|
```
|
|
65
65
|
|