create-aziz-app 3.1.1 → 3.1.3

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 (33) hide show
  1. package/README.md +658 -207
  2. package/bin/create-aziz-app.js +302 -351
  3. package/package.json +1 -1
  4. package/template/README.md +14 -279
  5. package/template/_framework/api.js +219 -219
  6. package/template/_framework/boot.js +33 -33
  7. package/template/_framework/hooks.js +367 -291
  8. package/template/_framework/next-response.js +128 -128
  9. package/template/_framework/route-match.js +145 -145
  10. package/template/_framework/router.js +465 -259
  11. package/template/_framework/runtime.js +264 -264
  12. package/template/app/layout.js +3 -5
  13. package/template/app/page.js +9 -32
  14. package/template/docs/01-getting-started.md +151 -151
  15. package/template/docs/02-project-structure.md +109 -109
  16. package/template/docs/03-pages-and-layouts.md +140 -140
  17. package/template/docs/04-routing.md +120 -120
  18. package/template/docs/05-hooks.md +169 -169
  19. package/template/docs/06-api-routes.md +210 -210
  20. package/template/docs/07-api-client.md +151 -151
  21. package/template/docs/08-styling.md +94 -94
  22. package/template/docs/09-imports-and-modules.md +97 -97
  23. package/template/docs/10-database-and-models.md +154 -154
  24. package/template/docs/11-authentication.md +171 -171
  25. package/template/docs/12-production-and-deployment.md +121 -121
  26. package/template/docs/13-internals.md +179 -179
  27. package/template/docs/README.md +67 -67
  28. package/template/index.html +20 -20
  29. package/template/package.json +16 -16
  30. package/template/scripts/dev-server.js +493 -381
  31. package/template/scripts/lib/api-router.js +525 -525
  32. package/template/scripts/lib/js-transform.js +213 -141
  33. package/template/scripts/lib/module-resolver.js +132 -132
@@ -1,151 +1,151 @@
1
- # 07 · API Client
2
-
3
- The API client is the browser-side companion to [API routes](./06-api-routes.md). It's a tiny, dependency-free `fetch` wrapper with JSON handling, timeouts, retries, and typed errors.
4
-
5
- A global `api` is available everywhere:
6
-
7
- ```js
8
- const users = await api.get('/api/users');
9
- await api.post('/api/users', { name: 'Aziz' });
10
- ```
11
-
12
- You can also import it explicitly:
13
-
14
- ```js
15
- import api, { ApiError } from '/_framework/api.js';
16
- ```
17
-
18
- ## Methods
19
-
20
- ```js
21
- await api.get('/api/users');
22
- await api.post('/api/users', { name: 'Aziz' });
23
- await api.put('/api/users?id=7', { name: 'B' });
24
- await api.patch('/api/users/7', { email: 'new@x.com' });
25
- await api.delete('/api/users/7');
26
- await api.del('/api/users/7'); // alias
27
- await api.head('/api/users'); // returns the raw Response
28
- await api.form('/api/upload', formData);// multipart/form-data
29
- await api.raw('/api/users'); // returns the raw Response object
30
-
31
- // Low-level escape hatch:
32
- await api.request('/api/users', { method: 'POST', body: { a: 1 } });
33
- ```
34
-
35
- ### Options (all methods)
36
-
37
- ```js
38
- await api.get('/api/search', {
39
- query: { q: 'aziz', tag: ['a', 'b'], skip: undefined }, // → ?q=aziz&tag=a&tag=b
40
- headers: { Authorization: 'Bearer …' },
41
- timeout: 5000, // ms, 0 disables
42
- retries: 2, // for 5xx/429 only
43
- signal, // external AbortSignal
44
- raw: false, // true → return the Response, not parsed data
45
- });
46
- ```
47
-
48
- - **`query`** serializes objects; arrays become repeated params; `undefined`/`null` are skipped.
49
- - **`raw`** returns the untouched `Response` instead of parsed JSON.
50
-
51
- ## Responses
52
-
53
- - JSON responses are parsed automatically (`Content-Type: application/json`).
54
- - Text-ish responses (`text/*`, html, xml) return the text.
55
- - **`204`/`205` return `null`.**
56
-
57
- ## Errors
58
-
59
- Failures throw an **`ApiError`** with `status`, `data`, and `url`:
60
-
61
- ```js
62
- import { ApiError } from '/_framework/api.js';
63
-
64
- try {
65
- await api.post('/api/users', bad);
66
- } catch (err) {
67
- if (err instanceof ApiError) {
68
- if (err.status === 400) console.warn(err.message, err.data);
69
- else if (err.status === 0) console.error('Network/timeout:', err.message);
70
- else console.error('Server error', err.status);
71
- }
72
- }
73
- ```
74
-
75
- - `err.message` is extracted from the server's `error` / `message` / `detail` field when present.
76
- - `err.status === 0` means network failure or **timeout** (`"Request timed out after …ms"`).
77
-
78
- ## Retry policy
79
-
80
- | Status | Retried? |
81
- |--------|----------|
82
- | `4xx` | **No** (client errors are your bug — retrying won't help) |
83
- | `429` | Yes (rate limit) |
84
- | `5xx` | Yes |
85
- | Network / timeout | Yes |
86
-
87
- Backoff is `200ms, 400ms, 800ms…`. `retries` defaults to `0`; set it per request or globally.
88
-
89
- ```js
90
- api.configure({ retries: 1, timeout: 10000 });
91
- ```
92
-
93
- ## Configuration
94
-
95
- ```js
96
- api.setBaseURL('https://api.example.com'); // prefix for relative paths
97
- api.setHeaders({ Authorization: `Bearer ${token}` });
98
- api.configure({
99
- baseURL: 'https://api.example.com',
100
- timeout: 15000,
101
- retries: 0,
102
- headers: { 'X-App': 'aziz' },
103
- });
104
-
105
- console.log(api.defaults); // { baseURL, timeout, retries, headers }
106
- ```
107
-
108
- Defaults are merged under each request's own headers, so per-call `headers` win.
109
-
110
- ## Declarative fetching: `useFetch`
111
-
112
- For read-only data with loading/error state built in, use the [hook](./05-hooks.md#usefetch--useapi):
113
-
114
- ```js
115
- const { data, loading, error, refetch } = useFetch('/api/users');
116
- ```
117
-
118
- Use **`api`** for imperative flows (POST/PUT, custom retries, non-JSON bodies); use **`useFetch`** for declarative reads.
119
-
120
- ## Full example: form → API → UI
121
-
122
- ```js
123
- function Contact() {
124
- const [form, setForm] = useState({ name: '', message: '' });
125
- const [status, setStatus] = useState('idle');
126
-
127
- async function submit(e) {
128
- e.preventDefault();
129
- setStatus('sending');
130
- try {
131
- await api.post('/api/contact', form);
132
- setStatus('sent');
133
- } catch (err) {
134
- setStatus('error: ' + err.message);
135
- }
136
- }
137
-
138
- return (
139
- <form onSubmit={submit}>
140
- <input value={form.name} onInput={e => setForm({ ...form, name: e.target.value })} />
141
- <textarea value={form.message} onInput={e => setForm({ ...form, message: e.target.value })} />
142
- <button disabled={status === 'sending'}>Send</button>
143
- <p>{status}</p>
144
- </form>
145
- );
146
- }
147
- ```
148
-
149
- ## Continue
150
-
151
- → [Styling](./08-styling.md)
1
+ # 07 · API Client
2
+
3
+ The API client is the browser-side companion to [API routes](./06-api-routes.md). It's a tiny, dependency-free `fetch` wrapper with JSON handling, timeouts, retries, and typed errors.
4
+
5
+ A global `api` is available everywhere:
6
+
7
+ ```js
8
+ const users = await api.get('/api/users');
9
+ await api.post('/api/users', { name: 'Aziz' });
10
+ ```
11
+
12
+ You can also import it explicitly:
13
+
14
+ ```js
15
+ import api, { ApiError } from '/_framework/api.js';
16
+ ```
17
+
18
+ ## Methods
19
+
20
+ ```js
21
+ await api.get('/api/users');
22
+ await api.post('/api/users', { name: 'Aziz' });
23
+ await api.put('/api/users?id=7', { name: 'B' });
24
+ await api.patch('/api/users/7', { email: 'new@x.com' });
25
+ await api.delete('/api/users/7');
26
+ await api.del('/api/users/7'); // alias
27
+ await api.head('/api/users'); // returns the raw Response
28
+ await api.form('/api/upload', formData);// multipart/form-data
29
+ await api.raw('/api/users'); // returns the raw Response object
30
+
31
+ // Low-level escape hatch:
32
+ await api.request('/api/users', { method: 'POST', body: { a: 1 } });
33
+ ```
34
+
35
+ ### Options (all methods)
36
+
37
+ ```js
38
+ await api.get('/api/search', {
39
+ query: { q: 'aziz', tag: ['a', 'b'], skip: undefined }, // → ?q=aziz&tag=a&tag=b
40
+ headers: { Authorization: 'Bearer …' },
41
+ timeout: 5000, // ms, 0 disables
42
+ retries: 2, // for 5xx/429 only
43
+ signal, // external AbortSignal
44
+ raw: false, // true → return the Response, not parsed data
45
+ });
46
+ ```
47
+
48
+ - **`query`** serializes objects; arrays become repeated params; `undefined`/`null` are skipped.
49
+ - **`raw`** returns the untouched `Response` instead of parsed JSON.
50
+
51
+ ## Responses
52
+
53
+ - JSON responses are parsed automatically (`Content-Type: application/json`).
54
+ - Text-ish responses (`text/*`, html, xml) return the text.
55
+ - **`204`/`205` return `null`.**
56
+
57
+ ## Errors
58
+
59
+ Failures throw an **`ApiError`** with `status`, `data`, and `url`:
60
+
61
+ ```js
62
+ import { ApiError } from '/_framework/api.js';
63
+
64
+ try {
65
+ await api.post('/api/users', bad);
66
+ } catch (err) {
67
+ if (err instanceof ApiError) {
68
+ if (err.status === 400) console.warn(err.message, err.data);
69
+ else if (err.status === 0) console.error('Network/timeout:', err.message);
70
+ else console.error('Server error', err.status);
71
+ }
72
+ }
73
+ ```
74
+
75
+ - `err.message` is extracted from the server's `error` / `message` / `detail` field when present.
76
+ - `err.status === 0` means network failure or **timeout** (`"Request timed out after …ms"`).
77
+
78
+ ## Retry policy
79
+
80
+ | Status | Retried? |
81
+ |--------|----------|
82
+ | `4xx` | **No** (client errors are your bug — retrying won't help) |
83
+ | `429` | Yes (rate limit) |
84
+ | `5xx` | Yes |
85
+ | Network / timeout | Yes |
86
+
87
+ Backoff is `200ms, 400ms, 800ms…`. `retries` defaults to `0`; set it per request or globally.
88
+
89
+ ```js
90
+ api.configure({ retries: 1, timeout: 10000 });
91
+ ```
92
+
93
+ ## Configuration
94
+
95
+ ```js
96
+ api.setBaseURL('https://api.example.com'); // prefix for relative paths
97
+ api.setHeaders({ Authorization: `Bearer ${token}` });
98
+ api.configure({
99
+ baseURL: 'https://api.example.com',
100
+ timeout: 15000,
101
+ retries: 0,
102
+ headers: { 'X-App': 'aziz' },
103
+ });
104
+
105
+ console.log(api.defaults); // { baseURL, timeout, retries, headers }
106
+ ```
107
+
108
+ Defaults are merged under each request's own headers, so per-call `headers` win.
109
+
110
+ ## Declarative fetching: `useFetch`
111
+
112
+ For read-only data with loading/error state built in, use the [hook](./05-hooks.md#usefetch--useapi):
113
+
114
+ ```js
115
+ const { data, loading, error, refetch } = useFetch('/api/users');
116
+ ```
117
+
118
+ Use **`api`** for imperative flows (POST/PUT, custom retries, non-JSON bodies); use **`useFetch`** for declarative reads.
119
+
120
+ ## Full example: form → API → UI
121
+
122
+ ```js
123
+ function Contact() {
124
+ const [form, setForm] = useState({ name: '', message: '' });
125
+ const [status, setStatus] = useState('idle');
126
+
127
+ async function submit(e) {
128
+ e.preventDefault();
129
+ setStatus('sending');
130
+ try {
131
+ await api.post('/api/contact', form);
132
+ setStatus('sent');
133
+ } catch (err) {
134
+ setStatus('error: ' + err.message);
135
+ }
136
+ }
137
+
138
+ return (
139
+ <form onSubmit={submit}>
140
+ <input value={form.name} onInput={e => setForm({ ...form, name: e.target.value })} />
141
+ <textarea value={form.message} onInput={e => setForm({ ...form, message: e.target.value })} />
142
+ <button disabled={status === 'sending'}>Send</button>
143
+ <p>{status}</p>
144
+ </form>
145
+ );
146
+ }
147
+ ```
148
+
149
+ ## Continue
150
+
151
+ → [Styling](./08-styling.md)
@@ -1,94 +1,94 @@
1
- # 08 · Styling
2
-
3
- Aziz.js ships plain CSS. You can also opt into Bootstrap or Tailwind from the CLI.
4
-
5
- ## Global stylesheet
6
-
7
- `index.html` links your main stylesheet:
8
-
9
- ```html
10
- <link rel="stylesheet" href="/styles/globals.css">
11
- ```
12
-
13
- Add more by editing `index.html` (or inject `<link>` tags dynamically):
14
-
15
- ```html
16
- <head>
17
- <link rel="stylesheet" href="/styles/globals.css">
18
- <link rel="stylesheet" href="/styles/theme.css">
19
- </head>
20
- ```
21
-
22
- ## Inline styles
23
-
24
- Use the `style` prop with an object (React-style), or a string:
25
-
26
- ```jsx
27
- <div style={{ padding: '40px', maxWidth: '900px', margin: '0 auto' }} />
28
- <div style="color: red" />
29
- ```
30
-
31
- ## Class names
32
-
33
- Both `class` and `className` work:
34
-
35
- ```jsx
36
- <div class="card" />
37
- <div className="card" />
38
- ```
39
-
40
- ## CSS frameworks via the CLI
41
-
42
- Pick one at creation time:
43
-
44
- ```bash
45
- npx create-aziz-app my-app --css bootstrap
46
- npx create-aziz-app my-app --css tailwind
47
- npx create-aziz-app my-app --css none
48
- ```
49
-
50
- - **bootstrap** — adds Bootstrap 5 + Bootstrap Icons (CDN `<link>`/`<script>` injected into `index.html`).
51
- - **tailwind** — adds the Tailwind CDN `<script>` plus a `tailwind.config` block with `primary`/`dark` colors.
52
- - **none** — just your `globals.css`.
53
-
54
- The flag injects tags into `index.html` before `</head>` / `</body>`. Because these are CDNs, an internet connection is required at runtime.
55
-
56
- > Prefer to install CSS locally (e.g. `npm i tailwindcss` + a build step) if you need offline/production builds — the CDN approach is for speed in development.
57
-
58
- ## Scoped styles?
59
-
60
- There's no built-in CSS Modules / scoped styles. Options:
61
-
62
- 1. **BEM or a naming convention** in `globals.css`.
63
- 2. **Inline `style` objects** for truly one-off layout.
64
- 3. **A `<style>` tag** rendered inside a component (works, but it's global unless you namespace the selectors).
65
-
66
- ## Theming with context
67
-
68
- Because hooks and context are global, a dark-mode toggle is easy:
69
-
70
- ```js
71
- const ThemeContext = createContext('light');
72
-
73
- function Root({ children }) {
74
- const [theme, setTheme] = useLocalStorage('theme', 'light');
75
- return (
76
- <ThemeContext.Provider value={theme}>
77
- <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
78
- Toggle theme
79
- </button>
80
- {children}
81
- </ThemeContext.Provider>
82
- );
83
- }
84
- ```
85
-
86
- Set `document.body.dataset.theme = theme` and switch colors in CSS:
87
-
88
- ```css
89
- [data-theme="dark"] { background: #111; color: #eee; }
90
- ```
91
-
92
- ## Continue
93
-
94
- → [Imports & Modules](./09-imports-and-modules.md)
1
+ # 08 · Styling
2
+
3
+ Aziz.js ships plain CSS. You can also opt into Bootstrap or Tailwind from the CLI.
4
+
5
+ ## Global stylesheet
6
+
7
+ `index.html` links your main stylesheet:
8
+
9
+ ```html
10
+ <link rel="stylesheet" href="/styles/globals.css">
11
+ ```
12
+
13
+ Add more by editing `index.html` (or inject `<link>` tags dynamically):
14
+
15
+ ```html
16
+ <head>
17
+ <link rel="stylesheet" href="/styles/globals.css">
18
+ <link rel="stylesheet" href="/styles/theme.css">
19
+ </head>
20
+ ```
21
+
22
+ ## Inline styles
23
+
24
+ Use the `style` prop with an object (React-style), or a string:
25
+
26
+ ```jsx
27
+ <div style={{ padding: '40px', maxWidth: '900px', margin: '0 auto' }} />
28
+ <div style="color: red" />
29
+ ```
30
+
31
+ ## Class names
32
+
33
+ Both `class` and `className` work:
34
+
35
+ ```jsx
36
+ <div class="card" />
37
+ <div className="card" />
38
+ ```
39
+
40
+ ## CSS frameworks via the CLI
41
+
42
+ Pick one at creation time:
43
+
44
+ ```bash
45
+ npx create-aziz-app my-app --css bootstrap
46
+ npx create-aziz-app my-app --css tailwind
47
+ npx create-aziz-app my-app --css none
48
+ ```
49
+
50
+ - **bootstrap** — adds Bootstrap 5 + Bootstrap Icons (CDN `<link>`/`<script>` injected into `index.html`).
51
+ - **tailwind** — adds the Tailwind CDN `<script>` plus a `tailwind.config` block with `primary`/`dark` colors.
52
+ - **none** — just your `globals.css`.
53
+
54
+ The flag injects tags into `index.html` before `</head>` / `</body>`. Because these are CDNs, an internet connection is required at runtime.
55
+
56
+ > Prefer to install CSS locally (e.g. `npm i tailwindcss` + a build step) if you need offline/production builds — the CDN approach is for speed in development.
57
+
58
+ ## Scoped styles?
59
+
60
+ There's no built-in CSS Modules / scoped styles. Options:
61
+
62
+ 1. **BEM or a naming convention** in `globals.css`.
63
+ 2. **Inline `style` objects** for truly one-off layout.
64
+ 3. **A `<style>` tag** rendered inside a component (works, but it's global unless you namespace the selectors).
65
+
66
+ ## Theming with context
67
+
68
+ Because hooks and context are global, a dark-mode toggle is easy:
69
+
70
+ ```js
71
+ const ThemeContext = createContext('light');
72
+
73
+ function Root({ children }) {
74
+ const [theme, setTheme] = useLocalStorage('theme', 'light');
75
+ return (
76
+ <ThemeContext.Provider value={theme}>
77
+ <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
78
+ Toggle theme
79
+ </button>
80
+ {children}
81
+ </ThemeContext.Provider>
82
+ );
83
+ }
84
+ ```
85
+
86
+ Set `document.body.dataset.theme = theme` and switch colors in CSS:
87
+
88
+ ```css
89
+ [data-theme="dark"] { background: #111; color: #eee; }
90
+ ```
91
+
92
+ ## Continue
93
+
94
+ → [Imports & Modules](./09-imports-and-modules.md)