flet-box 1.0.4 → 1.0.5
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 +10 -0
- package/bin/cli.js +15 -0
- package/bin/commands/checkApi.js +761 -0
- package/bin/commands/create.js +77 -5
- package/bin/utils/dtsProps.js +191 -0
- package/bin/utils/moduleExports.js +165 -0
- package/bin/utils/neutralise.js +64 -0
- package/bin/utils/templates.js +366 -27
- package/docs/CONTRIBUTING.md +156 -0
- package/docs/README.md +68 -0
- package/docs/arquitectura.md +164 -0
- package/docs/cli/README.md +542 -0
- package/docs/guides/README.md +33 -0
- package/docs/guides/app-templates.md +197 -0
- package/docs/guides/compatibility.md +95 -0
- package/docs/guides/embedding.md +284 -0
- package/docs/guides/first-app.md +208 -0
- package/docs/guides/frontend-services.md +171 -0
- package/docs/guides/minification-and-protection.md +260 -0
- package/docs/guides/mobile-and-platforms.md +439 -0
- package/docs/guides/online-updates.md +196 -0
- package/docs/guides/router.md +282 -0
- package/docs/guides/state.md +283 -0
- package/docs/guides/syntax-philosophy.md +150 -0
- package/docs/guides/utilities.md +241 -0
- package/docs/guides/widget-structure.md +163 -0
- package/docs/server/README.md +68 -0
- package/docs/server/START_HERE.md +121 -0
- package/docs/server/api.md +225 -0
- package/docs/server/data-services.md +300 -0
- package/docs/server/deployment.md +113 -0
- package/docs/server/security.md +196 -0
- package/docs/services/README.md +79 -0
- package/docs/services/RamStore.md +84 -0
- package/docs/services/Session.md +84 -0
- package/docs/services/Storage.md +88 -0
- package/docs/services/http.md +86 -0
- package/docs/tools/README.md +138 -0
- package/docs/tools/animation.md +87 -0
- package/docs/tools/async-ids-log.md +80 -0
- package/docs/tools/device.md +132 -0
- package/docs/tools/introspection.md +67 -0
- package/docs/tools/lists.md +96 -0
- package/docs/tools/markdown.md +79 -0
- package/docs/tools/reactivity.md +288 -0
- package/docs/tools/state.md +103 -0
- package/docs/tools/styling.md +92 -0
- package/docs/tools/text-and-time.md +89 -0
- package/docs/tools/theme.md +102 -0
- package/docs/tools/validation.md +75 -0
- package/docs/tools/visual-effects.md +84 -0
- package/docs/widget/Accordion.md +144 -0
- package/docs/widget/AdMob.md +144 -0
- package/docs/widget/AdSense.md +133 -0
- package/docs/widget/AdaptiveScaffold.md +105 -0
- package/docs/widget/AlertDialog.md +103 -0
- package/docs/widget/AnimatedBox.md +130 -0
- package/docs/widget/AnimatedText.md +123 -0
- package/docs/widget/AppBar.md +118 -0
- package/docs/widget/Audio.md +135 -0
- package/docs/widget/Avatar.md +119 -0
- package/docs/widget/Badge.md +128 -0
- package/docs/widget/BottomNavigation.md +116 -0
- package/docs/widget/BottomSheet.md +125 -0
- package/docs/widget/Button.md +137 -0
- package/docs/widget/COMMON_PROPS.md +150 -0
- package/docs/widget/Card.md +125 -0
- package/docs/widget/Carousel.md +151 -0
- package/docs/widget/Chart.md +151 -0
- package/docs/widget/Checkbox.md +118 -0
- package/docs/widget/Chip.md +126 -0
- package/docs/widget/CircularBar.md +168 -0
- package/docs/widget/CircularChart.md +164 -0
- package/docs/widget/CodeViewer.md +130 -0
- package/docs/widget/CollapsibleSideBar.md +112 -0
- package/docs/widget/Column.md +129 -0
- package/docs/widget/Container.md +136 -0
- package/docs/widget/DataTable.md +150 -0
- package/docs/widget/Divider.md +109 -0
- package/docs/widget/DraggBox.md +129 -0
- package/docs/widget/Drawer.md +135 -0
- package/docs/widget/DrawerItem.md +98 -0
- package/docs/widget/Dropdown.md +139 -0
- package/docs/widget/DroppBox.md +152 -0
- package/docs/widget/FloatingActionButton.md +120 -0
- package/docs/widget/GridView.md +140 -0
- package/docs/widget/Icon.md +117 -0
- package/docs/widget/Image.md +125 -0
- package/docs/widget/Input.md +151 -0
- package/docs/widget/Inspector.md +92 -0
- package/docs/widget/InstallButton.md +128 -0
- package/docs/widget/ListTile.md +132 -0
- package/docs/widget/ListView.md +138 -0
- package/docs/widget/Markdown.md +175 -0
- package/docs/widget/MatrixRain.md +122 -0
- package/docs/widget/Modal.md +129 -0
- package/docs/widget/Pagination.md +135 -0
- package/docs/widget/ParallaxBox.md +128 -0
- package/docs/widget/ProgressBar.md +130 -0
- package/docs/widget/QRCode.md +117 -0
- package/docs/widget/README.md +188 -0
- package/docs/widget/Radio.md +123 -0
- package/docs/widget/Rating.md +131 -0
- package/docs/widget/Row.md +131 -0
- package/docs/widget/START_HERE.md +146 -0
- package/docs/widget/Scaffold.md +152 -0
- package/docs/widget/Skeleton.md +130 -0
- package/docs/widget/Slider.md +146 -0
- package/docs/widget/SnackBar.md +102 -0
- package/docs/widget/Stack.md +128 -0
- package/docs/widget/Stepper.md +145 -0
- package/docs/widget/Switch.md +118 -0
- package/docs/widget/Tabs.md +124 -0
- package/docs/widget/Text.md +133 -0
- package/docs/widget/Tooltip.md +145 -0
- package/docs/widget/TreeView.md +176 -0
- package/docs/widget/Video.md +127 -0
- package/docs/widget/_template.md +61 -0
- package/package.json +3 -2
- package/src/animations/animate.js +180 -70
- package/src/animations/index.js +1 -0
- package/src/create-index.sh +1 -1
- package/src/index.d.ts +45 -22
- package/src/index.js +1 -0
- package/src/navigations/AdaptiveScaffold.js +11 -2
- package/src/navigations/BottomNavigation.js +10 -3
- package/src/navigations/Tabs.js +6 -2
- package/src/tools/useState.js +54 -63
- package/src/widget-factory/assignProps.js +23 -1
- package/src/widget-factory/processProps.js +10 -3
- package/src/widget-factory/reactiveValue.js +43 -18
- package/src/widget-factory/translateProps.js +777 -901
- package/src/widgets/AlertDialog.js +7 -5
- package/src/widgets/Checkbox.js +9 -7
- package/src/widgets/CircularBar.js +12 -1
- package/src/widgets/FloatingActionButton.js +4 -7
- package/src/widgets/Image.js +5 -7
- package/src/widgets/Input.js +92 -0
- package/src/widgets/ListView.js +73 -29
- package/src/widgets/Pagination.js +6 -3
- package/src/widgets/ProgressBar.js +16 -13
- package/src/widgets/Radio.js +6 -7
- package/src/widgets/Slider.js +23 -4
- package/src/widgets/Stack.js +36 -3
- package/src/widgets/Stepper.js +4 -3
- package/src/widgets/Switch.js +3 -4
- package/src/widgets/TreeView.js +0 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Chapter 0 · Your first FletBox app, step by step
|
|
2
|
+
|
|
3
|
+
This page is for **absolute beginners**. You do not need to know any trick — you only need a text editor, a terminal, and the [FletBox CLI](../cli/README.md) installed. At the end you will have a working app with a button and a list.
|
|
4
|
+
|
|
5
|
+
## Before you start
|
|
6
|
+
|
|
7
|
+
FletBox is not published to the npm registry, so first you install the CLI once and link it globally:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/kuko53348/flet-box.git
|
|
11
|
+
cd flet-box
|
|
12
|
+
npm link # link the flet-box CLI globally
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then create an empty project, link the framework into it, and start the dev server:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cd ..
|
|
19
|
+
flet-box create my-app
|
|
20
|
+
cd my-app
|
|
21
|
+
npm link flet-box # or: npm install ../path/to/flet-box
|
|
22
|
+
npm run dev
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Open your browser at `http://localhost:8000`. You should see "Hello FletBox" (or a small welcome screen). If you do, everything is working.
|
|
26
|
+
|
|
27
|
+
## Step 1: the smallest app
|
|
28
|
+
|
|
29
|
+
Open the file the CLI created (usually `src/app.js`). Edit it to this:
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
import { runApp, Text } from "flet-box";
|
|
33
|
+
|
|
34
|
+
const app = Text({ text: "Hello, world!" });
|
|
35
|
+
|
|
36
|
+
runApp(app, "root");
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`Text` makes a piece of text. `runApp` puts your app on the page, inside an element with `id="root"`. Save the file and look at the browser: it says "Hello, world!".
|
|
40
|
+
|
|
41
|
+
## Step 2: a button that counts
|
|
42
|
+
|
|
43
|
+
A button is a `Button` widget. It can run code when you press it. Inside that code we build a counter.
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
import { runApp, Column, Text, Button } from "flet-box";
|
|
47
|
+
|
|
48
|
+
async function main() {
|
|
49
|
+
const [count, setCount] = useState("counter", 0);
|
|
50
|
+
|
|
51
|
+
const app = Column({
|
|
52
|
+
gap: 12,
|
|
53
|
+
children: [
|
|
54
|
+
Text({ text: `You pressed ${count} times` }),
|
|
55
|
+
Button({
|
|
56
|
+
text: "Add one",
|
|
57
|
+
onPress: () => setCount((previous) => previous + 1),
|
|
58
|
+
}),
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
runApp(app, "root");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
main();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Wait — we used `useState` but did not import it. Fix the import line:
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
import { runApp, Column, Text, Button, useState } from "flet-box";
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
What just happened?
|
|
75
|
+
|
|
76
|
+
- `useState("counter", 0)` creates a value stored under the key `"counter"`, starting at `0`.
|
|
77
|
+
- `count` is the current value.
|
|
78
|
+
- `setCount` is the function that changes it.
|
|
79
|
+
- When we press the button, we tell FletBox to run the screen again with the new value, so the text updates.
|
|
80
|
+
|
|
81
|
+
`Column` stacks its `children` vertically, one under the other, with `gap` space between them.
|
|
82
|
+
|
|
83
|
+
## Step 3: write and add tasks
|
|
84
|
+
|
|
85
|
+
Now the fun part — a real todo list. A `Row` puts widgets side by side. An `Input` is a text box, and its `.value` holds what the user typed.
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
import { runApp, Column, Row, Text, Input, Button, useState } from "flet-box";
|
|
89
|
+
|
|
90
|
+
async function main() {
|
|
91
|
+
const tasks = Column({ gap: 8, children: [] });
|
|
92
|
+
const [allTasks, setAllTasks] = useState("tasks", [], tasks, "children");
|
|
93
|
+
|
|
94
|
+
const input = Input({ placeholder: "Write a task" });
|
|
95
|
+
|
|
96
|
+
const app = Column({
|
|
97
|
+
gap: 16,
|
|
98
|
+
children: [
|
|
99
|
+
Text({ text: "My tasks", type: "h2" }),
|
|
100
|
+
Row({
|
|
101
|
+
gap: 8,
|
|
102
|
+
children: [
|
|
103
|
+
input,
|
|
104
|
+
Button({
|
|
105
|
+
text: "Add",
|
|
106
|
+
onPress: () => {
|
|
107
|
+
if (input.value.trim() === "") return;
|
|
108
|
+
setAllTasks([...allTasks, input.value]);
|
|
109
|
+
input.value = "";
|
|
110
|
+
},
|
|
111
|
+
}),
|
|
112
|
+
],
|
|
113
|
+
}),
|
|
114
|
+
tasks,
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
runApp(app, "root");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
main();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Type something in the box, press **Add**, and watch it appear in the list below.
|
|
125
|
+
|
|
126
|
+
How it works:
|
|
127
|
+
|
|
128
|
+
- `tasks` is an empty `Column` — it will hold the rows of the list.
|
|
129
|
+
- The 4th argument of `useState` (“children”) tells FletBox: *when this state changes, update the `children` of `tasks`*.
|
|
130
|
+
- `setAllTasks([...allTasks, input.value])` puts the new task at the end of the list. FletBox then re-renders `tasks` with the new rows.
|
|
131
|
+
- Clearing `input.value` empties the box so you can write the next task.
|
|
132
|
+
|
|
133
|
+
## Step 4: make each task removable
|
|
134
|
+
|
|
135
|
+
Giving every task its own "x" button means each row is a small `Row` with a `Text` and a `Button`. We rebuild the whole list whenever it changes, using the widget's own `update` method:
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
import { runApp, Column, Row, Text, Input, Button } from "flet-box";
|
|
139
|
+
|
|
140
|
+
let allTasks = [];
|
|
141
|
+
const tasks = Column({ gap: 8, children: [] });
|
|
142
|
+
const input = Input({ placeholder: "Write a task" });
|
|
143
|
+
|
|
144
|
+
const taskRow = (task) =>
|
|
145
|
+
Row({
|
|
146
|
+
gap: 8,
|
|
147
|
+
children: [
|
|
148
|
+
Text({ text: task }),
|
|
149
|
+
Button({
|
|
150
|
+
text: "x",
|
|
151
|
+
size: "small",
|
|
152
|
+
onPress: () => {
|
|
153
|
+
allTasks = allTasks.filter((item) => item !== task);
|
|
154
|
+
render();
|
|
155
|
+
},
|
|
156
|
+
}),
|
|
157
|
+
],
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const render = () => tasks.update({ children: allTasks.map(taskRow) });
|
|
161
|
+
|
|
162
|
+
const addTask = () => {
|
|
163
|
+
if (input.value.trim() === "") return;
|
|
164
|
+
allTasks.push(input.value);
|
|
165
|
+
input.value = "";
|
|
166
|
+
render();
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const app = Column({
|
|
170
|
+
gap: 16,
|
|
171
|
+
children: [
|
|
172
|
+
Text({ text: "My tasks", type: "h2" }),
|
|
173
|
+
Row({
|
|
174
|
+
gap: 8,
|
|
175
|
+
children: [
|
|
176
|
+
input,
|
|
177
|
+
Button({ text: "Add", onPress: addTask }),
|
|
178
|
+
],
|
|
179
|
+
}),
|
|
180
|
+
tasks,
|
|
181
|
+
],
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
runApp(app, "root");
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
- `taskRow` builds one line: the task name plus a small **x** button.
|
|
188
|
+
- `render()` asks `tasks` to replace its children with the current list, rebuilt as rows.
|
|
189
|
+
- Pressing **x** removes that task from `allTasks` and re-renders.
|
|
190
|
+
|
|
191
|
+
This app grows with you: the widgets you used — `Row`, `Column`, `Text`, `Input`, `Button` — are the same ones every FletBox screen is built from. To go deeper into shared, automatic state, read the [State guide](state.md).
|
|
192
|
+
|
|
193
|
+
## What is next
|
|
194
|
+
|
|
195
|
+
You just built a real app. Now learn what widgets look like and how to mix them:
|
|
196
|
+
|
|
197
|
+
- **[Start here](../widget/START_HERE.md)** — how to read the widget book.
|
|
198
|
+
- **[The widget book](../widget/README.md)** — all widgets, in order, from basic to advanced.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Continue reading
|
|
203
|
+
|
|
204
|
+
- **Previous:** [The FletBox Book](../README.md) — front cover.
|
|
205
|
+
- **Next:** [Start here](../widget/START_HERE.md) — how to read the widget book.
|
|
206
|
+
- **Index:** [Guides index](README.md).
|
|
207
|
+
|
|
208
|
+
You are reading **Chapter 0 · Your first app** (before Chapter 1).
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Frontend services
|
|
2
|
+
|
|
3
|
+
FletBox includes three browser storage layers and a small HTTP client. Choose the layer based on how long the data should live.
|
|
4
|
+
|
|
5
|
+
## Choose a storage layer
|
|
6
|
+
|
|
7
|
+
| Service | Lifetime | Best for |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `RamStore` | Until the page is reloaded | Shared reactive UI state. |
|
|
10
|
+
| `Session` | Until the browser tab/session ends | Temporary session data. |
|
|
11
|
+
| `Storage` | Until the user or app removes it | Small persistent preferences. |
|
|
12
|
+
|
|
13
|
+
None of these services should be used for passwords, private keys, or security secrets.
|
|
14
|
+
|
|
15
|
+
## RamStore
|
|
16
|
+
|
|
17
|
+
`RamStore` keeps values in memory and can notify subscribers:
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
import { getRam, saveRam, subscribeRam } from "flet-box";
|
|
21
|
+
|
|
22
|
+
saveRam("cart", [{ id: 1, name: "Book" }]);
|
|
23
|
+
const cart = getRam("cart");
|
|
24
|
+
|
|
25
|
+
const unsubscribe = subscribeRam((key, newValue, oldValue) => {
|
|
26
|
+
if (key === "cart") {
|
|
27
|
+
console.log({ newValue, oldValue });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
unsubscribe();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Values disappear when the page reloads.
|
|
35
|
+
|
|
36
|
+
## Session storage
|
|
37
|
+
|
|
38
|
+
Use `Session` for data that should survive navigation but not remain as a permanent preference:
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import { getSession, saveSession, deleteSession } from "flet-box";
|
|
42
|
+
|
|
43
|
+
saveSession("checkout-step", 2);
|
|
44
|
+
const step = getSession("checkout-step");
|
|
45
|
+
deleteSession("checkout-step");
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The data is backed by `sessionStorage`.
|
|
49
|
+
|
|
50
|
+
## Persistent storage
|
|
51
|
+
|
|
52
|
+
Use `Storage` for small preferences:
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
import { getData, saveData, deleteData } from "flet-box";
|
|
56
|
+
|
|
57
|
+
saveData("preferences", {
|
|
58
|
+
theme: "dark",
|
|
59
|
+
language: "en",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const preferences = getData("preferences");
|
|
63
|
+
deleteData("preferences");
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The data is backed by `localStorage` and serialized as JSON.
|
|
67
|
+
|
|
68
|
+
## Storage rules
|
|
69
|
+
|
|
70
|
+
- Use namespaced keys such as `fletbox:preferences`.
|
|
71
|
+
- Store small values only.
|
|
72
|
+
- Handle `null` when a key does not exist.
|
|
73
|
+
- Do not store credentials or sensitive tokens unless you understand the security tradeoff.
|
|
74
|
+
- Do not call `clearAllData()` in a shared page unless you intentionally want to clear every local-storage key for that origin.
|
|
75
|
+
- Do not call `clearAllSession()` in a shared page unless you intentionally want to clear every session-storage key for that origin.
|
|
76
|
+
- Use a server-side session or secure platform storage for sensitive authentication data.
|
|
77
|
+
|
|
78
|
+
## Check availability
|
|
79
|
+
|
|
80
|
+
Storage can be disabled by browser privacy settings, sandboxing, or restricted contexts:
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
import { isStorageAvailable, isSessionAvailable } from "flet-box";
|
|
84
|
+
|
|
85
|
+
if (isStorageAvailable()) {
|
|
86
|
+
saveData("visited", true);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (isSessionAvailable()) {
|
|
90
|
+
saveSession("step", 1);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## HTTP requests
|
|
95
|
+
|
|
96
|
+
The HTTP helpers use `fetch` and return parsed JSON when the response content type is JSON:
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
import { httpGet, httpPost } from "flet-box";
|
|
100
|
+
|
|
101
|
+
const users = await httpGet("/api/users", {
|
|
102
|
+
params: { page: 1 },
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const created = await httpPost("/api/users", {
|
|
106
|
+
body: { name: "Ada" },
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Available helpers:
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
httpGet
|
|
114
|
+
httpPost
|
|
115
|
+
httpPut
|
|
116
|
+
httpPatch
|
|
117
|
+
httpDelete
|
|
118
|
+
httpRequest
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Headers and authentication
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
const data = await httpGet("/api/private", {
|
|
125
|
+
headers: {
|
|
126
|
+
Authorization: `Bearer ${token}`,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Never hardcode a private secret in frontend source code. A bearer token in browser code must be treated as accessible to the user and protected by server-side authorization.
|
|
132
|
+
|
|
133
|
+
## Timeout and errors
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
try {
|
|
137
|
+
const data = await httpGet("/api/slow", {
|
|
138
|
+
timeout: 5000,
|
|
139
|
+
});
|
|
140
|
+
console.log(data);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error("Request failed:", error.message);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Non-2xx responses throw an error containing `response` and, when available, parsed response data in `error.data`.
|
|
147
|
+
|
|
148
|
+
## Query parameters
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
const result = await httpGet("/api/search", {
|
|
152
|
+
params: {
|
|
153
|
+
query: "widgets",
|
|
154
|
+
page: 2,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Use encoded values through the `params` option instead of manually concatenating user input into URLs.
|
|
160
|
+
|
|
161
|
+
## Service API status
|
|
162
|
+
|
|
163
|
+
Every storage and HTTP function is declared in `src/index.d.ts`, exported from `src/index.js`, and detailed on the [services reference pages](../services/README.md). The runtime also exports namespace objects (`RamStore`, `Session`, `Storage`) that group each layer.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Continue reading
|
|
168
|
+
|
|
169
|
+
- **Previous:** [Routing with FletBox](router.md)
|
|
170
|
+
- **Next:** [Services index](../services/README.md) — the full reference
|
|
171
|
+
- **Index:** [Guides index](README.md) · [The FletBox Book](../README.md)
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# Minify and protect a FletBox build
|
|
2
|
+
|
|
3
|
+
> **Level: Advanced.** This page is for shipping — skip it on a first read. It follows [the FletBox CLI](../cli/README.md) in the book's reading path.
|
|
4
|
+
|
|
5
|
+
FletBox can produce a smaller production build by bundling only the code used by the application, removing development whitespace, and applying tree shaking.
|
|
6
|
+
|
|
7
|
+
This guide explains two different goals:
|
|
8
|
+
|
|
9
|
+
- **Minification** reduces file size and load time.
|
|
10
|
+
- **Code protection** makes the generated JavaScript harder to read, but cannot make browser code truly secret.
|
|
11
|
+
|
|
12
|
+
## What `createBundle` does
|
|
13
|
+
|
|
14
|
+
The FletBox bundle process uses esbuild with production-oriented options:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
flet-box createBundle
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Aliases:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
flet-box build
|
|
24
|
+
flet-box bundle
|
|
25
|
+
npm run build
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The build process bundles `src/app.js` and writes the result to `dist/src/app.js` with options equivalent to:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
--bundle
|
|
32
|
+
--format=esm
|
|
33
|
+
--minify
|
|
34
|
+
--tree-shaking=true
|
|
35
|
+
--target=es2020
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
It also copies the HTML file, assets, fonts, service worker, and `run.sh` when they exist.
|
|
39
|
+
|
|
40
|
+
## Basic production workflow
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Install dependencies
|
|
44
|
+
npm install
|
|
45
|
+
|
|
46
|
+
# Create the optimized web build
|
|
47
|
+
npm run build
|
|
48
|
+
|
|
49
|
+
# Inspect the generated output
|
|
50
|
+
find dist -maxdepth 3 -type f
|
|
51
|
+
|
|
52
|
+
# Preview the production build
|
|
53
|
+
cd dist
|
|
54
|
+
./run.sh
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Always test the `dist` directory, not only the development server. A development build and a production build can behave differently.
|
|
58
|
+
|
|
59
|
+
## What minification changes
|
|
60
|
+
|
|
61
|
+
Minification normally:
|
|
62
|
+
|
|
63
|
+
- Removes unnecessary whitespace.
|
|
64
|
+
- Removes comments that are not required at runtime.
|
|
65
|
+
- Shortens local variable names when safe.
|
|
66
|
+
- Compresses expressions.
|
|
67
|
+
- Removes unreachable code.
|
|
68
|
+
- Combines imported modules into the application bundle.
|
|
69
|
+
- Removes unused exports through tree shaking.
|
|
70
|
+
|
|
71
|
+
Minification does not change the intended public behavior of the application.
|
|
72
|
+
|
|
73
|
+
## Reduce bundle size
|
|
74
|
+
|
|
75
|
+
### Import only what you use
|
|
76
|
+
|
|
77
|
+
Prefer focused imports:
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
import { Button, Container, Text } from "flet-box";
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Avoid importing a large internal surface when a focused import is enough. The bundler can remove unused code more effectively when imports are statically analyzable.
|
|
84
|
+
|
|
85
|
+
### Avoid unnecessary dependencies
|
|
86
|
+
|
|
87
|
+
Every dependency can add code, assets, initialization time, and security maintenance. Add a package only when it solves a real problem.
|
|
88
|
+
|
|
89
|
+
### Keep assets outside JavaScript
|
|
90
|
+
|
|
91
|
+
Images, fonts, videos, and large data files should usually remain assets instead of being embedded into JavaScript strings. Copy only the assets required by the application.
|
|
92
|
+
|
|
93
|
+
### Use lazy loading for large features
|
|
94
|
+
|
|
95
|
+
For large optional screens, load code only when the user needs it:
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
const openReports = async () => {
|
|
99
|
+
const module = await import("./screens/ReportsScreen.js");
|
|
100
|
+
return module.ReportsScreen();
|
|
101
|
+
};
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The exact loading strategy depends on your application and server. Test lazy routes in the final `dist` directory.
|
|
105
|
+
|
|
106
|
+
### Remove development-only code
|
|
107
|
+
|
|
108
|
+
Keep debug panels, test data, verbose logs, and development-only routes out of the production entry point.
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
if (import.meta.env?.DEV) {
|
|
112
|
+
console.log("Development information");
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Use a build setup that actually replaces environment flags before relying on them. Do not assume every bundler provides `import.meta.env` automatically.
|
|
117
|
+
|
|
118
|
+
## Protecting browser code: the honest rule
|
|
119
|
+
|
|
120
|
+
JavaScript shipped to a browser can be downloaded, inspected, and debugged by the user. Minification and obfuscation can slow down casual inspection, but they cannot provide real secrecy.
|
|
121
|
+
|
|
122
|
+
Never put these values in frontend code:
|
|
123
|
+
|
|
124
|
+
- Passwords.
|
|
125
|
+
- Private API keys.
|
|
126
|
+
- JWT signing secrets.
|
|
127
|
+
- Database credentials.
|
|
128
|
+
- SMTP passwords.
|
|
129
|
+
- Cloud service private keys.
|
|
130
|
+
- Internal security rules that must remain secret.
|
|
131
|
+
|
|
132
|
+
Keep sensitive operations in FletBox Server or another backend and expose only the minimum API needed by the frontend.
|
|
133
|
+
|
|
134
|
+
## Minification versus obfuscation
|
|
135
|
+
|
|
136
|
+
### Minification
|
|
137
|
+
|
|
138
|
+
Recommended for every production build:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
flet-box build
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
It reduces size and removes readable formatting. It is usually fast and has low runtime risk.
|
|
145
|
+
|
|
146
|
+
### Obfuscation
|
|
147
|
+
|
|
148
|
+
Obfuscation changes code structure and names to make manual reading more difficult. It can increase bundle size, reduce performance, make debugging harder, and break code that depends on function names or dynamic property access.
|
|
149
|
+
|
|
150
|
+
Use obfuscation only after measuring its impact. It is not a replacement for authentication, authorization, or server-side protection.
|
|
151
|
+
|
|
152
|
+
If an obfuscator is introduced, run it only on the final browser bundle and test:
|
|
153
|
+
|
|
154
|
+
- Application startup.
|
|
155
|
+
- Router navigation.
|
|
156
|
+
- Event handlers.
|
|
157
|
+
- Dynamic imports.
|
|
158
|
+
- PWA installation.
|
|
159
|
+
- Capacitor Android builds.
|
|
160
|
+
- Capacitor iOS builds.
|
|
161
|
+
|
|
162
|
+
## Source maps
|
|
163
|
+
|
|
164
|
+
Source maps make production debugging easier but also make original source code easier to recover. Decide intentionally whether to publish them.
|
|
165
|
+
|
|
166
|
+
For a private staging build, source maps can be useful. For a public production build, do not publish source maps unless you understand the exposure and have access controls.
|
|
167
|
+
|
|
168
|
+
The current FletBox shell build does not enable source maps by default. If you add them, keep them out of public hosting when the source is private.
|
|
169
|
+
|
|
170
|
+
## Protect API calls
|
|
171
|
+
|
|
172
|
+
The frontend is not a safe place for secrets. Protect backend routes instead:
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
175
|
+
import { authenticate, hasRole } from "flet-box-server";
|
|
176
|
+
|
|
177
|
+
const routes = Api({
|
|
178
|
+
"/admin/report": {
|
|
179
|
+
GET: {
|
|
180
|
+
handler: createReport,
|
|
181
|
+
middleware: [authenticate, hasRole("admin")],
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The browser may know that this endpoint exists, but it should not be able to use it without a valid token and role.
|
|
188
|
+
|
|
189
|
+
## Protect the production build pipeline
|
|
190
|
+
|
|
191
|
+
Use a repeatable build process:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
rm -rf dist
|
|
195
|
+
npm ci
|
|
196
|
+
npm run build
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Then verify:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Check that the bundle exists
|
|
203
|
+
test -s dist/src/app.js
|
|
204
|
+
|
|
205
|
+
# Check that development secrets are not present
|
|
206
|
+
grep -R "JWT_SECRET\|password\|apiKey" dist || true
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Do not print or commit secret values while checking files. The example searches names only; inspect matches carefully.
|
|
210
|
+
|
|
211
|
+
## Capacitor builds
|
|
212
|
+
|
|
213
|
+
Build the web code before synchronizing native projects:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
npm run build
|
|
217
|
+
npx cap sync android
|
|
218
|
+
npx cap sync ios
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Then create a debug Android APK:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
cd android
|
|
225
|
+
./gradlew assembleDebug
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
For a production APK or an Android App Bundle, use the release tasks:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
./gradlew assembleRelease
|
|
232
|
+
./gradlew bundleRelease
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The AAB is the usual format for Google Play. Release artifacts must be signed with a production key.
|
|
236
|
+
|
|
237
|
+
Minification reduces the web payload inside the native application. It does not replace Android or iOS signing, secure storage, transport security, or native permission review.
|
|
238
|
+
|
|
239
|
+
## Production checklist
|
|
240
|
+
|
|
241
|
+
- Run `npm run build` from a clean state.
|
|
242
|
+
- Confirm tree shaking is active.
|
|
243
|
+
- Inspect the size of `dist/src/app.js`.
|
|
244
|
+
- Remove development routes and debug data.
|
|
245
|
+
- Do not ship secrets in frontend code.
|
|
246
|
+
- Decide whether source maps should be private.
|
|
247
|
+
- Test the actual `dist` output.
|
|
248
|
+
- Test direct navigation to SPA routes.
|
|
249
|
+
- Test PWA and Capacitor builds when used.
|
|
250
|
+
- Use HTTPS for API calls.
|
|
251
|
+
- Protect backend routes with authentication and authorization.
|
|
252
|
+
- Keep signing keys and deployment credentials outside Git.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Continue reading
|
|
257
|
+
|
|
258
|
+
- **Previous:** [The FletBox CLI](../cli/README.md)
|
|
259
|
+
- **Next:** [Build FletBox for mobile and other platforms](mobile-and-platforms.md) — Appendix B
|
|
260
|
+
- **Index:** [Guides index](README.md) · [The FletBox Book](../README.md)
|