bank20baht-cli 0.5.0 → 0.6.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 +2 -2
- package/dist/domain/form-dsl.js +2 -0
- package/package.json +1 -1
- package/templates/create/README.md.hbs +1 -1
- package/templates/create/apps/web/index.html.hbs +8 -0
- package/templates/create/apps/web/package.json.hbs +1 -0
- package/templates/create/apps/web/src/App.tsx.hbs +18 -7
- package/templates/create/apps/web/src/app.css +27 -0
- package/templates/create/apps/web/src/health.test.ts.hbs +18 -4
- package/templates/create/apps/web/src/health.ts.hbs +8 -0
- package/templates/create/apps/web/src/main.tsx.hbs +12 -1
- package/templates/create/package.json.hbs +14 -4
- package/templates/new/package.json.hbs +1 -1
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ Add a matching entry to `knip.ts` so dead-code checks cover it.
|
|
|
72
72
|
Same flags as `new`, but scaffolds a complete stack:
|
|
73
73
|
|
|
74
74
|
```
|
|
75
|
-
apps/web React 19 + Vite
|
|
75
|
+
apps/web React 19 + Vite on bank20baht-ui (tokens, <baht-*> elements, dark toggle)
|
|
76
76
|
apps/api Elysia — exports `type App`, the Eden type anchor
|
|
77
77
|
libs/shared/eden treaty<App> — the one typed HTTP client
|
|
78
78
|
bahtui.json generator config (see below)
|
|
@@ -126,7 +126,7 @@ Field DSL — comma-separated `key:type[:modifier…]`:
|
|
|
126
126
|
| Part | Values |
|
|
127
127
|
| ---------- | -------------------------------------------------------------------------------------------------------------- |
|
|
128
128
|
| `key` | `[a-z][a-z0-9_]*`; the label is derived (`due_date` → "Due Date") |
|
|
129
|
-
| `type` | `text` `textarea` `markdown` `number` `email` `password` `dropdown` `multiselect` `checkbox` `radio` `toggle` `date` `file` `rating` `range` `reference` |
|
|
129
|
+
| `type` | `text` `textarea` `markdown` `number` `currency` `email` `password` `dropdown` `multiselect` `checkbox` `radio` `toggle` `date` `file` `rating` `range` `reference` `tags` |
|
|
130
130
|
| modifiers | `required` · `options=a\|b\|c` · `min=N` · `max=N` · `step=N` · `ref=LOOKUP` · `flag=NAME` |
|
|
131
131
|
|
|
132
132
|
Invalid DSL (unknown type, empty options, non-numeric `min`) fails before anything is
|
package/dist/domain/form-dsl.js
CHANGED
|
@@ -15,6 +15,7 @@ const FIELD_TYPES = new Set([
|
|
|
15
15
|
'textarea',
|
|
16
16
|
'markdown',
|
|
17
17
|
'number',
|
|
18
|
+
'currency',
|
|
18
19
|
'email',
|
|
19
20
|
'password',
|
|
20
21
|
'dropdown',
|
|
@@ -27,6 +28,7 @@ const FIELD_TYPES = new Set([
|
|
|
27
28
|
'rating',
|
|
28
29
|
'range',
|
|
29
30
|
'reference',
|
|
31
|
+
'tags',
|
|
30
32
|
]);
|
|
31
33
|
const KEY_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
32
34
|
const titleCase = (key) => key
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@ bun run dev # web on :5173, api on :3001
|
|
|
13
13
|
## Layout (DDD taxonomy)
|
|
14
14
|
|
|
15
15
|
```
|
|
16
|
-
apps/web React 19 + Vite
|
|
16
|
+
apps/web React 19 + Vite on bank20baht-ui (import each <baht-*> you use in src/main.tsx)
|
|
17
17
|
apps/api Elysia — exports `type App`, the Eden type anchor
|
|
18
18
|
libs/shared/eden treaty<App> — the ONE typed http client
|
|
19
19
|
libs/domain/<d>/model (later) entities + form JSON, framework-free
|
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>{{name}}</title>
|
|
7
|
+
<script>
|
|
8
|
+
// apply the saved <baht-theme-toggle> choice before paint (no flash)
|
|
9
|
+
try {
|
|
10
|
+
var t = localStorage.getItem('baht-theme');
|
|
11
|
+
if (t === 'light' || t === 'dark')
|
|
12
|
+
document.documentElement.setAttribute('data-baht-theme', t);
|
|
13
|
+
} catch (e) {}
|
|
14
|
+
</script>
|
|
7
15
|
</head>
|
|
8
16
|
<body>
|
|
9
17
|
<div id="root"></div>
|
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
import { api } from '@{{name}}/eden';
|
|
3
|
-
import { describeHealth } from './health';
|
|
3
|
+
import { describeHealth, healthTone, type HealthTone } from './health';
|
|
4
|
+
|
|
5
|
+
type Status = { label: string; tone: HealthTone | 'neutral' };
|
|
4
6
|
|
|
5
7
|
export default function App() {
|
|
6
|
-
const [status, setStatus] = useState('checking api…');
|
|
8
|
+
const [status, setStatus] = useState<Status>({ label: 'checking api…', tone: 'neutral' });
|
|
7
9
|
|
|
8
10
|
useEffect(() => {
|
|
9
|
-
api.health.get().then(({ data, error }) => {
|
|
10
|
-
setStatus(
|
|
11
|
+
void api.health.get().then(({ data, error }) => {
|
|
12
|
+
setStatus(
|
|
13
|
+
error
|
|
14
|
+
? { label: 'api unreachable (start it: bun run dev)', tone: 'danger' }
|
|
15
|
+
: { label: describeHealth(data), tone: healthTone(data) },
|
|
16
|
+
);
|
|
11
17
|
});
|
|
12
18
|
}, []);
|
|
13
19
|
|
|
14
20
|
return (
|
|
15
|
-
<main
|
|
16
|
-
<
|
|
17
|
-
|
|
21
|
+
<main className="app">
|
|
22
|
+
<header className="app-bar">
|
|
23
|
+
<h1>{{name}}</h1>
|
|
24
|
+
<baht-theme-toggle />
|
|
25
|
+
</header>
|
|
26
|
+
<baht-card heading="API" subtitle="apps/api via libs/shared/eden">
|
|
27
|
+
<baht-badge tone={status.tone} label={status.label} />
|
|
28
|
+
</baht-card>
|
|
18
29
|
</main>
|
|
19
30
|
);
|
|
20
31
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* Page chrome only. Components style themselves from the --baht-* tokens. */
|
|
2
|
+
body {
|
|
3
|
+
margin: 0;
|
|
4
|
+
background: var(--baht-color-bg);
|
|
5
|
+
color: var(--baht-color-text);
|
|
6
|
+
font-family: var(--baht-font-family);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.app {
|
|
10
|
+
max-width: 720px;
|
|
11
|
+
margin: 0 auto;
|
|
12
|
+
padding: var(--baht-space-8) var(--baht-space-4);
|
|
13
|
+
display: grid;
|
|
14
|
+
gap: var(--baht-space-4);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.app-bar {
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: space-between;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.app-bar h1 {
|
|
24
|
+
margin: 0;
|
|
25
|
+
font-family: var(--baht-font-display);
|
|
26
|
+
font-size: 1.5rem;
|
|
27
|
+
}
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import { describe, expect, it } from 'bun:test';
|
|
2
|
-
import { describeHealth } from './health';
|
|
2
|
+
import { describeHealth, healthTone } from './health';
|
|
3
|
+
|
|
4
|
+
const service = '{{name}}-api';
|
|
3
5
|
|
|
4
6
|
describe('describeHealth', () => {
|
|
5
7
|
it('formats a healthy response', () => {
|
|
6
|
-
expect(describeHealth({ status: 'ok', service
|
|
7
|
-
'{{name}}-api is ok',
|
|
8
|
-
);
|
|
8
|
+
expect(describeHealth({ status: 'ok', service })).toBe(`${service} is ok`);
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
it('handles a missing response', () => {
|
|
12
12
|
expect(describeHealth(null)).toBe('no response from api');
|
|
13
13
|
});
|
|
14
14
|
});
|
|
15
|
+
|
|
16
|
+
describe('healthTone', () => {
|
|
17
|
+
it('is success when the api says ok', () => {
|
|
18
|
+
expect(healthTone({ status: 'ok', service })).toBe('success');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('is warning for any other status', () => {
|
|
22
|
+
expect(healthTone({ status: 'degraded', service })).toBe('warning');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('is danger with no response', () => {
|
|
26
|
+
expect(healthTone(null)).toBe('danger');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export type Health = { status: string; service: string } | null;
|
|
2
2
|
|
|
3
|
+
/** `<baht-badge>` status tones the health line uses. */
|
|
4
|
+
export type HealthTone = 'success' | 'danger' | 'warning';
|
|
5
|
+
|
|
3
6
|
export function describeHealth(health: Health): string {
|
|
4
7
|
if (!health) return 'no response from api';
|
|
5
8
|
return `${health.service} is ${health.status}`;
|
|
6
9
|
}
|
|
10
|
+
|
|
11
|
+
export function healthTone(health: Health): HealthTone {
|
|
12
|
+
if (!health) return 'danger';
|
|
13
|
+
return health.status === 'ok' ? 'success' : 'warning';
|
|
14
|
+
}
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import { StrictMode } from 'react';
|
|
2
2
|
import { createRoot } from 'react-dom/client';
|
|
3
|
+
// Register only the <baht-*> elements this app renders (one subpath per tag).
|
|
4
|
+
// `import 'bank20baht-ui'` registers all of them, at ~4x the bundle.
|
|
5
|
+
import 'bank20baht-ui/components/badge';
|
|
6
|
+
import 'bank20baht-ui/components/card';
|
|
7
|
+
import 'bank20baht-ui/components/theme-toggle';
|
|
8
|
+
import 'bank20baht-ui/react'; // JSX typings for every <baht-*>
|
|
9
|
+
import 'bank20baht-ui/tokens.css';
|
|
10
|
+
import './app.css';
|
|
3
11
|
import App from './App';
|
|
4
12
|
|
|
5
|
-
|
|
13
|
+
const root = document.getElementById('root');
|
|
14
|
+
if (!root) throw new Error('#root missing from index.html');
|
|
15
|
+
|
|
16
|
+
createRoot(root).render(
|
|
6
17
|
<StrictMode>
|
|
7
18
|
<App />
|
|
8
19
|
</StrictMode>,
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
"name": "{{name}}-monorepo",
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
|
-
"workspaces": [
|
|
5
|
+
"workspaces": [
|
|
6
|
+
"apps/*",
|
|
7
|
+
"libs/**"
|
|
8
|
+
],
|
|
6
9
|
"scripts": {
|
|
7
10
|
"dev": "bun run --filter './apps/*' dev",
|
|
8
11
|
"build": "bun run --filter './apps/*' build",
|
|
@@ -16,10 +19,17 @@
|
|
|
16
19
|
"prepare": "husky"
|
|
17
20
|
},
|
|
18
21
|
"lint-staged": {
|
|
19
|
-
"*.{ts,tsx}": [
|
|
20
|
-
|
|
22
|
+
"*.{ts,tsx}": [
|
|
23
|
+
"eslint --fix",
|
|
24
|
+
"prettier --write"
|
|
25
|
+
],
|
|
26
|
+
"*.{css,json,js,mjs}": [
|
|
27
|
+
"prettier --write"
|
|
28
|
+
]
|
|
21
29
|
},
|
|
22
|
-
"trustedDependencies": [
|
|
30
|
+
"trustedDependencies": [
|
|
31
|
+
"esbuild"
|
|
32
|
+
],
|
|
23
33
|
"devDependencies": {
|
|
24
34
|
"@eslint/js": "^10.0.1",
|
|
25
35
|
"eslint": "^10.6.0",
|