@famgia/omnify-typescript 0.0.67 → 0.0.69
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/dist/{chunk-4L77AHAC.js → chunk-6I4O23X6.js} +521 -66
- package/dist/chunk-6I4O23X6.js.map +1 -0
- package/dist/index.cjs +761 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +138 -2
- package/dist/index.d.ts +138 -2
- package/dist/index.js +227 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin.cjs +624 -75
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +6 -0
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.js +96 -11
- package/dist/plugin.js.map +1 -1
- package/package.json +3 -3
- package/scripts/postinstall.js +29 -40
- package/stubs/JapaneseAddressField.tsx.stub +289 -0
- package/stubs/JapaneseBankField.tsx.stub +212 -0
- package/stubs/JapaneseNameField.tsx.stub +194 -0
- package/stubs/ai-guides/checklists/react.md.stub +108 -0
- package/stubs/ai-guides/cursor/react-design.mdc.stub +289 -0
- package/stubs/ai-guides/cursor/react-form.mdc.stub +277 -0
- package/stubs/ai-guides/cursor/react-services.mdc.stub +304 -0
- package/stubs/ai-guides/cursor/react.mdc.stub +254 -0
- package/stubs/ai-guides/react/README.md.stub +221 -0
- package/stubs/ai-guides/react/antd-guide.md.stub +294 -0
- package/stubs/ai-guides/react/checklist.md.stub +108 -0
- package/stubs/ai-guides/react/datetime-guide.md.stub +137 -0
- package/stubs/ai-guides/react/design-philosophy.md.stub +363 -0
- package/stubs/ai-guides/react/i18n-guide.md.stub +211 -0
- package/stubs/ai-guides/react/laravel-integration.md.stub +181 -0
- package/stubs/ai-guides/react/service-pattern.md.stub +180 -0
- package/stubs/ai-guides/react/tanstack-query.md.stub +339 -0
- package/stubs/ai-guides/react/types-guide.md.stub +524 -0
- package/stubs/components-index.ts.stub +13 -0
- package/stubs/form-validation.ts.stub +106 -0
- package/stubs/rules/index.ts.stub +48 -0
- package/stubs/rules/kana.ts.stub +291 -0
- package/stubs/use-form-mutation.ts.stub +117 -0
- package/stubs/zod-i18n.ts.stub +32 -0
- package/ai-guides/antdesign-guide.md +0 -401
- package/ai-guides/typescript-guide.md +0 -310
- package/dist/chunk-4L77AHAC.js.map +0 -1
|
@@ -1,401 +0,0 @@
|
|
|
1
|
-
# Omnify + Ant Design Integration Guide
|
|
2
|
-
|
|
3
|
-
This guide shows how to use Omnify-generated validation rules with Ant Design Forms.
|
|
4
|
-
|
|
5
|
-
## Generated Files
|
|
6
|
-
|
|
7
|
-
Omnify generates validation rules in `rules/` directory:
|
|
8
|
-
- `rules/{Model}.rules.ts` - Validation rules for each model
|
|
9
|
-
|
|
10
|
-
## File Structure
|
|
11
|
-
|
|
12
|
-
```typescript
|
|
13
|
-
// rules/User.rules.ts
|
|
14
|
-
|
|
15
|
-
// Display name for model (multi-locale)
|
|
16
|
-
export const UserDisplayName: LocaleMap = {
|
|
17
|
-
ja: 'ユーザー',
|
|
18
|
-
en: 'User',
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// Display names for properties (multi-locale)
|
|
22
|
-
export const UserPropertyDisplayNames: Record<string, LocaleMap> = {
|
|
23
|
-
name: { ja: '名前', en: 'Name' },
|
|
24
|
-
email: { ja: 'メールアドレス', en: 'Email' },
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
// Validation rules with multi-locale messages
|
|
28
|
-
export const UserRules: Record<string, ValidationRule[]> = {
|
|
29
|
-
name: [
|
|
30
|
-
{ required: true, message: { ja: '名前は必須です', en: 'Name is required' } },
|
|
31
|
-
{ max: 100, message: { ja: '名前は100文字以内で入力してください', en: 'Name must be at most 100 characters' } },
|
|
32
|
-
],
|
|
33
|
-
email: [
|
|
34
|
-
{ required: true, message: { ja: 'メールアドレスは必須です', en: 'Email is required' } },
|
|
35
|
-
{ type: 'email', message: { ja: 'メールアドレスの形式が正しくありません', en: 'Email is not a valid email address' } },
|
|
36
|
-
],
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// Helper functions
|
|
40
|
-
export function getUserRules(locale: string): Record<string, Rule[]>;
|
|
41
|
-
export function getUserDisplayName(locale: string): string;
|
|
42
|
-
export function getUserPropertyDisplayName(property: string, locale: string): string;
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Basic Usage
|
|
46
|
-
|
|
47
|
-
```tsx
|
|
48
|
-
import { Form, Input, Button } from 'antd';
|
|
49
|
-
import {
|
|
50
|
-
getUserRules,
|
|
51
|
-
getUserDisplayName,
|
|
52
|
-
getUserPropertyDisplayName
|
|
53
|
-
} from '@/types/model/rules/User.rules';
|
|
54
|
-
|
|
55
|
-
interface UserFormProps {
|
|
56
|
-
locale?: string;
|
|
57
|
-
onSubmit: (values: User) => void;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function UserForm({ locale = 'ja', onSubmit }: UserFormProps) {
|
|
61
|
-
const [form] = Form.useForm();
|
|
62
|
-
const rules = getUserRules(locale);
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<Form
|
|
66
|
-
form={form}
|
|
67
|
-
layout="vertical"
|
|
68
|
-
onFinish={onSubmit}
|
|
69
|
-
>
|
|
70
|
-
<Form.Item
|
|
71
|
-
name="name"
|
|
72
|
-
label={getUserPropertyDisplayName('name', locale)}
|
|
73
|
-
rules={rules.name}
|
|
74
|
-
>
|
|
75
|
-
<Input placeholder={getUserPropertyDisplayName('name', locale)} />
|
|
76
|
-
</Form.Item>
|
|
77
|
-
|
|
78
|
-
<Form.Item
|
|
79
|
-
name="email"
|
|
80
|
-
label={getUserPropertyDisplayName('email', locale)}
|
|
81
|
-
rules={rules.email}
|
|
82
|
-
>
|
|
83
|
-
<Input type="email" />
|
|
84
|
-
</Form.Item>
|
|
85
|
-
|
|
86
|
-
<Form.Item>
|
|
87
|
-
<Button type="primary" htmlType="submit">
|
|
88
|
-
{locale === 'ja' ? '送信' : 'Submit'}
|
|
89
|
-
</Button>
|
|
90
|
-
</Form.Item>
|
|
91
|
-
</Form>
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## With Edit Mode (Initial Values)
|
|
97
|
-
|
|
98
|
-
```tsx
|
|
99
|
-
import { Form, Input, Button, Spin } from 'antd';
|
|
100
|
-
import { getUserRules, getUserPropertyDisplayName } from '@/types/model/rules/User.rules';
|
|
101
|
-
import { User } from '@/types/model';
|
|
102
|
-
|
|
103
|
-
interface UserEditFormProps {
|
|
104
|
-
user: User;
|
|
105
|
-
locale?: string;
|
|
106
|
-
onSubmit: (values: Partial<User>) => void;
|
|
107
|
-
loading?: boolean;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function UserEditForm({ user, locale = 'ja', onSubmit, loading }: UserEditFormProps) {
|
|
111
|
-
const [form] = Form.useForm();
|
|
112
|
-
const rules = getUserRules(locale);
|
|
113
|
-
|
|
114
|
-
// Set initial values when user data changes
|
|
115
|
-
React.useEffect(() => {
|
|
116
|
-
form.setFieldsValue(user);
|
|
117
|
-
}, [user, form]);
|
|
118
|
-
|
|
119
|
-
return (
|
|
120
|
-
<Spin spinning={loading}>
|
|
121
|
-
<Form
|
|
122
|
-
form={form}
|
|
123
|
-
layout="vertical"
|
|
124
|
-
initialValues={user}
|
|
125
|
-
onFinish={onSubmit}
|
|
126
|
-
>
|
|
127
|
-
<Form.Item
|
|
128
|
-
name="name"
|
|
129
|
-
label={getUserPropertyDisplayName('name', locale)}
|
|
130
|
-
rules={rules.name}
|
|
131
|
-
>
|
|
132
|
-
<Input />
|
|
133
|
-
</Form.Item>
|
|
134
|
-
|
|
135
|
-
<Form.Item
|
|
136
|
-
name="email"
|
|
137
|
-
label={getUserPropertyDisplayName('email', locale)}
|
|
138
|
-
rules={rules.email}
|
|
139
|
-
>
|
|
140
|
-
<Input type="email" />
|
|
141
|
-
</Form.Item>
|
|
142
|
-
|
|
143
|
-
<Form.Item>
|
|
144
|
-
<Button type="primary" htmlType="submit" loading={loading}>
|
|
145
|
-
{locale === 'ja' ? '更新' : 'Update'}
|
|
146
|
-
</Button>
|
|
147
|
-
</Form.Item>
|
|
148
|
-
</Form>
|
|
149
|
-
</Spin>
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Dynamic Locale Switching
|
|
155
|
-
|
|
156
|
-
```tsx
|
|
157
|
-
import { Form, Input, Select } from 'antd';
|
|
158
|
-
import { useState, useMemo } from 'react';
|
|
159
|
-
import { getUserRules, getUserPropertyDisplayName, getUserDisplayName } from '@/types/model/rules/User.rules';
|
|
160
|
-
|
|
161
|
-
export function UserFormWithLocale() {
|
|
162
|
-
const [locale, setLocale] = useState('ja');
|
|
163
|
-
const [form] = Form.useForm();
|
|
164
|
-
|
|
165
|
-
// Memoize rules to avoid recalculation
|
|
166
|
-
const rules = useMemo(() => getUserRules(locale), [locale]);
|
|
167
|
-
|
|
168
|
-
return (
|
|
169
|
-
<>
|
|
170
|
-
<Select value={locale} onChange={setLocale} style={{ marginBottom: 16 }}>
|
|
171
|
-
<Select.Option value="ja">日本語</Select.Option>
|
|
172
|
-
<Select.Option value="en">English</Select.Option>
|
|
173
|
-
<Select.Option value="vi">Tiếng Việt</Select.Option>
|
|
174
|
-
</Select>
|
|
175
|
-
|
|
176
|
-
<h2>{getUserDisplayName(locale)}</h2>
|
|
177
|
-
|
|
178
|
-
<Form form={form} layout="vertical">
|
|
179
|
-
<Form.Item
|
|
180
|
-
name="name"
|
|
181
|
-
label={getUserPropertyDisplayName('name', locale)}
|
|
182
|
-
rules={rules.name}
|
|
183
|
-
>
|
|
184
|
-
<Input />
|
|
185
|
-
</Form.Item>
|
|
186
|
-
|
|
187
|
-
<Form.Item
|
|
188
|
-
name="email"
|
|
189
|
-
label={getUserPropertyDisplayName('email', locale)}
|
|
190
|
-
rules={rules.email}
|
|
191
|
-
>
|
|
192
|
-
<Input type="email" />
|
|
193
|
-
</Form.Item>
|
|
194
|
-
</Form>
|
|
195
|
-
</>
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
## With Table Columns
|
|
201
|
-
|
|
202
|
-
```tsx
|
|
203
|
-
import { Table, TableColumnsType } from 'antd';
|
|
204
|
-
import { getUserPropertyDisplayName } from '@/types/model/rules/User.rules';
|
|
205
|
-
import { User } from '@/types/model';
|
|
206
|
-
|
|
207
|
-
interface UserTableProps {
|
|
208
|
-
users: User[];
|
|
209
|
-
locale?: string;
|
|
210
|
-
loading?: boolean;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export function UserTable({ users, locale = 'ja', loading }: UserTableProps) {
|
|
214
|
-
const columns: TableColumnsType<User> = [
|
|
215
|
-
{
|
|
216
|
-
title: getUserPropertyDisplayName('name', locale),
|
|
217
|
-
dataIndex: 'name',
|
|
218
|
-
key: 'name',
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
title: getUserPropertyDisplayName('email', locale),
|
|
222
|
-
dataIndex: 'email',
|
|
223
|
-
key: 'email',
|
|
224
|
-
},
|
|
225
|
-
];
|
|
226
|
-
|
|
227
|
-
return (
|
|
228
|
-
<Table
|
|
229
|
-
columns={columns}
|
|
230
|
-
dataSource={users}
|
|
231
|
-
rowKey="id"
|
|
232
|
-
loading={loading}
|
|
233
|
-
/>
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
## Custom Form Hook
|
|
239
|
-
|
|
240
|
-
```tsx
|
|
241
|
-
import { Form, FormInstance } from 'antd';
|
|
242
|
-
import { useMemo } from 'react';
|
|
243
|
-
|
|
244
|
-
// Generic hook for any model's rules
|
|
245
|
-
export function useModelForm<T>(
|
|
246
|
-
getRules: (locale: string) => Record<string, any[]>,
|
|
247
|
-
locale: string = 'ja'
|
|
248
|
-
): {
|
|
249
|
-
form: FormInstance<T>;
|
|
250
|
-
rules: Record<string, any[]>;
|
|
251
|
-
} {
|
|
252
|
-
const [form] = Form.useForm<T>();
|
|
253
|
-
const rules = useMemo(() => getRules(locale), [getRules, locale]);
|
|
254
|
-
|
|
255
|
-
return { form, rules };
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Usage
|
|
259
|
-
import { getUserRules } from '@/types/model/rules/User.rules';
|
|
260
|
-
|
|
261
|
-
function MyComponent() {
|
|
262
|
-
const { form, rules } = useModelForm(getUserRules, 'ja');
|
|
263
|
-
|
|
264
|
-
return (
|
|
265
|
-
<Form form={form}>
|
|
266
|
-
<Form.Item name="name" rules={rules.name}>
|
|
267
|
-
<Input />
|
|
268
|
-
</Form.Item>
|
|
269
|
-
</Form>
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
## Modal Form
|
|
275
|
-
|
|
276
|
-
```tsx
|
|
277
|
-
import { Modal, Form, Input, message } from 'antd';
|
|
278
|
-
import { getUserRules, getUserDisplayName, getUserPropertyDisplayName } from '@/types/model/rules/User.rules';
|
|
279
|
-
import { User } from '@/types/model';
|
|
280
|
-
|
|
281
|
-
interface UserModalFormProps {
|
|
282
|
-
open: boolean;
|
|
283
|
-
onClose: () => void;
|
|
284
|
-
onSubmit: (values: Partial<User>) => Promise<void>;
|
|
285
|
-
initialValues?: Partial<User>;
|
|
286
|
-
locale?: string;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export function UserModalForm({
|
|
290
|
-
open,
|
|
291
|
-
onClose,
|
|
292
|
-
onSubmit,
|
|
293
|
-
initialValues,
|
|
294
|
-
locale = 'ja'
|
|
295
|
-
}: UserModalFormProps) {
|
|
296
|
-
const [form] = Form.useForm();
|
|
297
|
-
const [loading, setLoading] = useState(false);
|
|
298
|
-
const rules = getUserRules(locale);
|
|
299
|
-
|
|
300
|
-
const handleSubmit = async () => {
|
|
301
|
-
try {
|
|
302
|
-
const values = await form.validateFields();
|
|
303
|
-
setLoading(true);
|
|
304
|
-
await onSubmit(values);
|
|
305
|
-
message.success(locale === 'ja' ? '保存しました' : 'Saved successfully');
|
|
306
|
-
form.resetFields();
|
|
307
|
-
onClose();
|
|
308
|
-
} catch (error) {
|
|
309
|
-
if (error instanceof Error) {
|
|
310
|
-
message.error(error.message);
|
|
311
|
-
}
|
|
312
|
-
} finally {
|
|
313
|
-
setLoading(false);
|
|
314
|
-
}
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
return (
|
|
318
|
-
<Modal
|
|
319
|
-
title={getUserDisplayName(locale)}
|
|
320
|
-
open={open}
|
|
321
|
-
onCancel={onClose}
|
|
322
|
-
onOk={handleSubmit}
|
|
323
|
-
confirmLoading={loading}
|
|
324
|
-
okText={locale === 'ja' ? '保存' : 'Save'}
|
|
325
|
-
cancelText={locale === 'ja' ? 'キャンセル' : 'Cancel'}
|
|
326
|
-
>
|
|
327
|
-
<Form
|
|
328
|
-
form={form}
|
|
329
|
-
layout="vertical"
|
|
330
|
-
initialValues={initialValues}
|
|
331
|
-
>
|
|
332
|
-
<Form.Item
|
|
333
|
-
name="name"
|
|
334
|
-
label={getUserPropertyDisplayName('name', locale)}
|
|
335
|
-
rules={rules.name}
|
|
336
|
-
>
|
|
337
|
-
<Input />
|
|
338
|
-
</Form.Item>
|
|
339
|
-
|
|
340
|
-
<Form.Item
|
|
341
|
-
name="email"
|
|
342
|
-
label={getUserPropertyDisplayName('email', locale)}
|
|
343
|
-
rules={rules.email}
|
|
344
|
-
>
|
|
345
|
-
<Input type="email" />
|
|
346
|
-
</Form.Item>
|
|
347
|
-
</Form>
|
|
348
|
-
</Modal>
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
## Validation Rule Types
|
|
354
|
-
|
|
355
|
-
Omnify generates these rule types based on schema:
|
|
356
|
-
|
|
357
|
-
| Schema | Generated Rule |
|
|
358
|
-
|--------|---------------|
|
|
359
|
-
| `required: true` | `{ required: true, message: {...} }` |
|
|
360
|
-
| `type: Email` | `{ type: 'email', message: {...} }` |
|
|
361
|
-
| `maxLength: N` | `{ max: N, message: {...} }` |
|
|
362
|
-
| `minLength: N` | `{ min: N, message: {...} }` |
|
|
363
|
-
| `max: N` (numeric) | `{ max: N, type: 'number', message: {...} }` |
|
|
364
|
-
| `min: N` (numeric) | `{ min: N, type: 'number', message: {...} }` |
|
|
365
|
-
| `pattern: regex` | `{ pattern: /regex/, message: {...} }` |
|
|
366
|
-
|
|
367
|
-
## Built-in Validation Messages
|
|
368
|
-
|
|
369
|
-
Omnify includes templates for 5 languages:
|
|
370
|
-
- Japanese (ja)
|
|
371
|
-
- English (en)
|
|
372
|
-
- Vietnamese (vi)
|
|
373
|
-
- Korean (ko)
|
|
374
|
-
- Chinese (zh)
|
|
375
|
-
|
|
376
|
-
Custom templates can be configured in `omnify.config.ts`:
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
export default defineConfig({
|
|
380
|
-
output: {
|
|
381
|
-
typescript: {
|
|
382
|
-
validationTemplates: {
|
|
383
|
-
required: {
|
|
384
|
-
ja: '${displayName}を入力してください',
|
|
385
|
-
en: '${displayName} is required',
|
|
386
|
-
vi: '${displayName} là bắt buộc',
|
|
387
|
-
},
|
|
388
|
-
maxLength: {
|
|
389
|
-
ja: '${displayName}は${max}文字以内です',
|
|
390
|
-
en: '${displayName} must be at most ${max} characters',
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
},
|
|
394
|
-
},
|
|
395
|
-
});
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
Available placeholders:
|
|
399
|
-
- `${displayName}` - Property display name
|
|
400
|
-
- `${min}` - Minimum value/length
|
|
401
|
-
- `${max}` - Maximum value/length
|
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
# Omnify TypeScript Generator Guide
|
|
2
|
-
|
|
3
|
-
This guide covers TypeScript-specific features and generated code patterns for Omnify.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Create new project (recommended)
|
|
9
|
-
npx @famgia/omnify create-laravel-project my-app
|
|
10
|
-
cd my-app
|
|
11
|
-
|
|
12
|
-
# Or initialize in existing project
|
|
13
|
-
npx @famgia/omnify init
|
|
14
|
-
|
|
15
|
-
# Generate TypeScript types
|
|
16
|
-
npx @famgia/omnify generate
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Generated Files
|
|
20
|
-
|
|
21
|
-
When you run `npx @famgia/omnify generate`, the following TypeScript files are generated:
|
|
22
|
-
|
|
23
|
-
- `base/*.ts` - Base model interfaces
|
|
24
|
-
- `enum/*.ts` - Enum types with multi-locale labels
|
|
25
|
-
- `rules/*.ts` - Ant Design compatible validation rules
|
|
26
|
-
|
|
27
|
-
## Type Generation
|
|
28
|
-
|
|
29
|
-
### Object Schema → Interface
|
|
30
|
-
|
|
31
|
-
```yaml
|
|
32
|
-
# yaml-language-server: $schema=./node_modules/.omnify/combined-schema.json
|
|
33
|
-
name: User
|
|
34
|
-
properties:
|
|
35
|
-
id:
|
|
36
|
-
type: BigInt
|
|
37
|
-
required: true
|
|
38
|
-
name:
|
|
39
|
-
type: String
|
|
40
|
-
required: true
|
|
41
|
-
maxLength: 255
|
|
42
|
-
email:
|
|
43
|
-
type: String
|
|
44
|
-
required: true
|
|
45
|
-
unique: true
|
|
46
|
-
profile:
|
|
47
|
-
type: Json
|
|
48
|
-
createdAt:
|
|
49
|
-
type: DateTime
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Generated:
|
|
53
|
-
```typescript
|
|
54
|
-
export interface User {
|
|
55
|
-
id: number;
|
|
56
|
-
name: string;
|
|
57
|
-
email: string;
|
|
58
|
-
profile: Record<string, unknown> | null;
|
|
59
|
-
createdAt: Date | null;
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Type Mapping
|
|
64
|
-
|
|
65
|
-
| Schema Type | TypeScript Type |
|
|
66
|
-
|-------------|-----------------|
|
|
67
|
-
| `String` | `string` |
|
|
68
|
-
| `Text` | `string` |
|
|
69
|
-
| `MediumText` | `string` |
|
|
70
|
-
| `LongText` | `string` |
|
|
71
|
-
| `TinyInt` | `number` |
|
|
72
|
-
| `Int` | `number` |
|
|
73
|
-
| `BigInt` | `number` |
|
|
74
|
-
| `Float` | `number` |
|
|
75
|
-
| `Decimal` | `number` |
|
|
76
|
-
| `Boolean` | `boolean` |
|
|
77
|
-
| `Date` | `Date` |
|
|
78
|
-
| `DateTime` | `Date` |
|
|
79
|
-
| `Timestamp` | `Date` |
|
|
80
|
-
| `Json` | `Record<string, unknown>` |
|
|
81
|
-
| `EnumRef` | Generated enum type |
|
|
82
|
-
| `Association` | Related model type / array |
|
|
83
|
-
|
|
84
|
-
## Hidden Schemas
|
|
85
|
-
|
|
86
|
-
Schemas with `options.hidden: true` are skipped for TypeScript generation:
|
|
87
|
-
|
|
88
|
-
```yaml
|
|
89
|
-
name: AppCache
|
|
90
|
-
options:
|
|
91
|
-
hidden: true # No TypeScript interface generated
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Use cases:
|
|
95
|
-
- System tables (cache, sessions, jobs)
|
|
96
|
-
- Tables that don't need frontend types
|
|
97
|
-
|
|
98
|
-
## Enum Generation (Multi-locale)
|
|
99
|
-
|
|
100
|
-
```yaml
|
|
101
|
-
# schemas/PostStatus.yaml
|
|
102
|
-
name: PostStatus
|
|
103
|
-
kind: enum
|
|
104
|
-
displayName:
|
|
105
|
-
ja: 投稿ステータス
|
|
106
|
-
en: Post Status
|
|
107
|
-
values:
|
|
108
|
-
draft:
|
|
109
|
-
ja: 下書き
|
|
110
|
-
en: Draft
|
|
111
|
-
published:
|
|
112
|
-
ja: 公開済み
|
|
113
|
-
en: Published
|
|
114
|
-
archived:
|
|
115
|
-
ja: アーカイブ
|
|
116
|
-
en: Archived
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Generated:
|
|
120
|
-
```typescript
|
|
121
|
-
export const PostStatus = {
|
|
122
|
-
draft: 'draft',
|
|
123
|
-
published: 'published',
|
|
124
|
-
archived: 'archived',
|
|
125
|
-
} as const;
|
|
126
|
-
|
|
127
|
-
export type PostStatus = typeof PostStatus[keyof typeof PostStatus];
|
|
128
|
-
|
|
129
|
-
// Multi-locale labels
|
|
130
|
-
export const PostStatusLabels: Record<PostStatus, Record<string, string>> = {
|
|
131
|
-
draft: { ja: '下書き', en: 'Draft' },
|
|
132
|
-
published: { ja: '公開済み', en: 'Published' },
|
|
133
|
-
archived: { ja: 'アーカイブ', en: 'Archived' },
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// Get label for specific locale
|
|
137
|
-
export function getPostStatusLabel(value: PostStatus, locale: string = 'en'): string {
|
|
138
|
-
return PostStatusLabels[value]?.[locale] ?? PostStatusLabels[value]?.['en'] ?? value;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Helper functions
|
|
142
|
-
export const PostStatusValues = Object.values(PostStatus);
|
|
143
|
-
export function isPostStatus(value: unknown): value is PostStatus {
|
|
144
|
-
return PostStatusValues.includes(value as PostStatus);
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Validation Rules (Ant Design)
|
|
149
|
-
|
|
150
|
-
Omnify generates Ant Design compatible validation rules in `rules/` directory.
|
|
151
|
-
|
|
152
|
-
**See detailed guide:** `.claude/omnify/antdesign-guide.md`
|
|
153
|
-
|
|
154
|
-
Quick example:
|
|
155
|
-
```tsx
|
|
156
|
-
import { Form, Input } from 'antd';
|
|
157
|
-
import { getUserRules, getUserPropertyDisplayName } from './types/model/rules/User.rules';
|
|
158
|
-
|
|
159
|
-
function UserForm({ locale = 'ja' }) {
|
|
160
|
-
const rules = getUserRules(locale);
|
|
161
|
-
return (
|
|
162
|
-
<Form>
|
|
163
|
-
<Form.Item name="name" label={getUserPropertyDisplayName('name', locale)} rules={rules.name}>
|
|
164
|
-
<Input />
|
|
165
|
-
</Form.Item>
|
|
166
|
-
</Form>
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## Association Types
|
|
172
|
-
|
|
173
|
-
### ManyToOne
|
|
174
|
-
```yaml
|
|
175
|
-
author:
|
|
176
|
-
type: Association
|
|
177
|
-
relation: ManyToOne
|
|
178
|
-
target: User
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
Generated:
|
|
182
|
-
```typescript
|
|
183
|
-
export interface Post {
|
|
184
|
-
authorId: number;
|
|
185
|
-
author?: User; // Optional: loaded relation
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### OneToMany
|
|
190
|
-
```yaml
|
|
191
|
-
posts:
|
|
192
|
-
type: Association
|
|
193
|
-
relation: OneToMany
|
|
194
|
-
target: Post
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
Generated:
|
|
198
|
-
```typescript
|
|
199
|
-
export interface User {
|
|
200
|
-
posts?: Post[]; // Optional: loaded relation array
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### ManyToMany
|
|
205
|
-
```yaml
|
|
206
|
-
tags:
|
|
207
|
-
type: Association
|
|
208
|
-
relation: ManyToMany
|
|
209
|
-
target: Tag
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
Generated:
|
|
213
|
-
```typescript
|
|
214
|
-
export interface Post {
|
|
215
|
-
tags?: Tag[]; // Optional: loaded relation array
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
## Nullable Fields
|
|
220
|
-
|
|
221
|
-
Fields without `required: true` are nullable:
|
|
222
|
-
|
|
223
|
-
```yaml
|
|
224
|
-
description:
|
|
225
|
-
type: LongText # No required: true
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
Generated:
|
|
229
|
-
```typescript
|
|
230
|
-
description: string | null;
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
## Using Generated Types
|
|
234
|
-
|
|
235
|
-
```typescript
|
|
236
|
-
import { User, Post, PostStatus, isPostStatus } from './types/omnify-types';
|
|
237
|
-
|
|
238
|
-
// Type-safe object creation
|
|
239
|
-
const user: User = {
|
|
240
|
-
id: 1,
|
|
241
|
-
name: 'John',
|
|
242
|
-
email: 'john@example.com',
|
|
243
|
-
profile: null,
|
|
244
|
-
createdAt: new Date(),
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
// Enum usage
|
|
248
|
-
const status: PostStatus = PostStatus.draft;
|
|
249
|
-
|
|
250
|
-
// Type guard
|
|
251
|
-
function handleStatus(value: unknown) {
|
|
252
|
-
if (isPostStatus(value)) {
|
|
253
|
-
console.log('Valid status:', value);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
## Commands
|
|
259
|
-
|
|
260
|
-
```bash
|
|
261
|
-
# Create new project
|
|
262
|
-
npx @famgia/omnify create-laravel-project my-app
|
|
263
|
-
|
|
264
|
-
# Generate TypeScript types
|
|
265
|
-
npx @famgia/omnify generate
|
|
266
|
-
|
|
267
|
-
# Force regenerate all files
|
|
268
|
-
npx @famgia/omnify generate --force
|
|
269
|
-
|
|
270
|
-
# Only generate TypeScript types
|
|
271
|
-
npx @famgia/omnify generate --types-only
|
|
272
|
-
|
|
273
|
-
# Validate schemas
|
|
274
|
-
npx @famgia/omnify validate
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
## Configuration
|
|
278
|
-
|
|
279
|
-
```typescript
|
|
280
|
-
// omnify.config.ts
|
|
281
|
-
import { defineConfig } from '@famgia/omnify';
|
|
282
|
-
import typescript from '@famgia/omnify-typescript/plugin';
|
|
283
|
-
|
|
284
|
-
export default defineConfig({
|
|
285
|
-
schemasDir: './schemas',
|
|
286
|
-
lockFilePath: './.omnify.lock',
|
|
287
|
-
|
|
288
|
-
plugins: [
|
|
289
|
-
typescript({
|
|
290
|
-
// Output path for TypeScript files
|
|
291
|
-
path: './resources/ts/types/models',
|
|
292
|
-
|
|
293
|
-
// Generate Ant Design validation rules
|
|
294
|
-
generateRules: true,
|
|
295
|
-
}),
|
|
296
|
-
],
|
|
297
|
-
|
|
298
|
-
locale: {
|
|
299
|
-
locales: ['ja', 'en'],
|
|
300
|
-
defaultLocale: 'ja',
|
|
301
|
-
},
|
|
302
|
-
});
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### Configuration Options
|
|
306
|
-
|
|
307
|
-
| Option | Type | Default | Description |
|
|
308
|
-
|--------|------|---------|-------------|
|
|
309
|
-
| `path` | `string` | `./src/types/model` | TypeScript output directory |
|
|
310
|
-
| `generateRules` | `boolean` | `true` | Generate Ant Design validation rules |
|