@famgia/omnify-laravel 0.0.88 → 0.0.90
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-YVVAJA3T.js → chunk-2QSKZS63.js} +188 -12
- package/dist/chunk-2QSKZS63.js.map +1 -0
- package/dist/index.cjs +190 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.js +5 -1
- package/dist/plugin.cjs +186 -11
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.js +1 -1
- package/package.json +5 -5
- package/scripts/postinstall.js +29 -36
- package/stubs/ai-guides/README.md.stub +95 -0
- package/stubs/ai-guides/claude-agents/architect.md.stub +150 -0
- package/stubs/ai-guides/claude-agents/developer.md.stub +190 -0
- package/stubs/ai-guides/claude-agents/reviewer.md.stub +134 -0
- package/stubs/ai-guides/claude-agents/tester.md.stub +196 -0
- package/stubs/ai-guides/claude-checklists/backend.md.stub +112 -0
- package/stubs/ai-guides/claude-omnify/antdesign-guide.md.stub +401 -0
- package/stubs/ai-guides/claude-omnify/config-guide.md.stub +253 -0
- package/stubs/ai-guides/claude-omnify/japan-guide.md.stub +186 -0
- package/stubs/ai-guides/claude-omnify/laravel-guide.md.stub +61 -0
- package/stubs/ai-guides/claude-omnify/react-form-guide.md.stub +259 -0
- package/stubs/ai-guides/claude-omnify/schema-guide.md.stub +115 -0
- package/stubs/ai-guides/claude-omnify/typescript-guide.md.stub +310 -0
- package/stubs/ai-guides/claude-rules/naming.md.stub +364 -0
- package/stubs/ai-guides/claude-rules/performance.md.stub +251 -0
- package/stubs/ai-guides/claude-rules/security.md.stub +159 -0
- package/stubs/ai-guides/claude-workflows/bug-fix.md.stub +201 -0
- package/stubs/ai-guides/claude-workflows/code-review.md.stub +164 -0
- package/stubs/ai-guides/claude-workflows/new-feature.md.stub +327 -0
- package/stubs/ai-guides/cursor/laravel-controller.mdc.stub +391 -0
- package/stubs/ai-guides/cursor/laravel-request.mdc.stub +112 -0
- package/stubs/ai-guides/cursor/laravel-resource.mdc.stub +73 -0
- package/stubs/ai-guides/cursor/laravel-review.mdc.stub +69 -0
- package/stubs/ai-guides/cursor/laravel-testing.mdc.stub +138 -0
- package/stubs/ai-guides/cursor/laravel.mdc.stub +82 -0
- package/stubs/ai-guides/cursor/omnify.mdc.stub +58 -0
- package/stubs/ai-guides/laravel/README.md.stub +59 -0
- package/stubs/ai-guides/laravel/architecture.md.stub +424 -0
- package/stubs/ai-guides/laravel/controller.md.stub +484 -0
- package/stubs/ai-guides/laravel/datetime.md.stub +334 -0
- package/stubs/ai-guides/laravel/openapi.md.stub +369 -0
- package/stubs/ai-guides/laravel/request.md.stub +450 -0
- package/stubs/ai-guides/laravel/resource.md.stub +516 -0
- package/stubs/ai-guides/laravel/service.md.stub +503 -0
- package/stubs/ai-guides/laravel/testing.md.stub +1504 -0
- package/ai-guides/laravel-guide.md +0 -461
- package/dist/chunk-YVVAJA3T.js.map +0 -1
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Omnify React Form Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to build forms using Omnify generated schemas with **Ant Design** and **TanStack Query**.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Form } from 'antd';
|
|
9
|
+
import { useFormMutation } from '@/omnify/hooks';
|
|
10
|
+
import { type CustomerCreate } from '@/omnify/schemas';
|
|
11
|
+
import { CustomerForm } from '@/features/customers';
|
|
12
|
+
import { api } from '@/lib/api';
|
|
13
|
+
|
|
14
|
+
function CreateCustomerPage() {
|
|
15
|
+
const [form] = Form.useForm<CustomerCreate>();
|
|
16
|
+
|
|
17
|
+
const mutation = useFormMutation<CustomerCreate>({
|
|
18
|
+
form,
|
|
19
|
+
mutationFn: (data) => api.post('/customers', data),
|
|
20
|
+
invalidateKeys: [['customers']],
|
|
21
|
+
successMessage: 'Customer created successfully',
|
|
22
|
+
onSuccess: () => form.resetFields(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<CustomerForm
|
|
27
|
+
form={form}
|
|
28
|
+
onSubmit={mutation.mutate}
|
|
29
|
+
loading={mutation.isPending}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## useFormMutation Hook
|
|
36
|
+
|
|
37
|
+
Location: `resources/ts/omnify/hooks/use-form-mutation.ts`
|
|
38
|
+
|
|
39
|
+
### Usage with Types
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { useFormMutation } from '@/omnify/hooks';
|
|
43
|
+
import { type CustomerCreate } from '@/omnify/schemas';
|
|
44
|
+
|
|
45
|
+
// With type parameter for type safety
|
|
46
|
+
const mutation = useFormMutation<CustomerCreate>({
|
|
47
|
+
form,
|
|
48
|
+
mutationFn: (data) => api.post('/customers', data),
|
|
49
|
+
invalidateKeys: [['customers']],
|
|
50
|
+
successMessage: 'Saved successfully',
|
|
51
|
+
onSuccess: () => form.resetFields(),
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Options
|
|
56
|
+
|
|
57
|
+
| Option | Type | Description |
|
|
58
|
+
|--------|------|-------------|
|
|
59
|
+
| `form` | `FormInstance<T>` | Ant Design form instance (required) |
|
|
60
|
+
| `mutationFn` | `(data: T) => Promise` | API call function (required) |
|
|
61
|
+
| `invalidateKeys` | `string[][]` | Query keys to invalidate on success |
|
|
62
|
+
| `successMessage` | `string` | Toast message on success |
|
|
63
|
+
| `onSuccess` | `(data) => void` | Callback after success |
|
|
64
|
+
| `onError` | `(error) => void` | Callback after error |
|
|
65
|
+
|
|
66
|
+
### Laravel Error Handling
|
|
67
|
+
|
|
68
|
+
The hook automatically handles Laravel validation errors (422):
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"message": "The given data was invalid.",
|
|
73
|
+
"errors": {
|
|
74
|
+
"email": ["The email has already been taken."]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Japanese Form Components
|
|
80
|
+
|
|
81
|
+
Omnify provides specialized components for Japanese data input.
|
|
82
|
+
|
|
83
|
+
### OmnifyForm.JapaneseName
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { OmnifyForm } from '@/omnify/components';
|
|
87
|
+
|
|
88
|
+
<OmnifyForm.JapaneseName
|
|
89
|
+
schemas={customerSchemas}
|
|
90
|
+
i18n={customerI18n}
|
|
91
|
+
prefix="name" // name_lastname, name_firstname, etc.
|
|
92
|
+
required // Show required asterisk
|
|
93
|
+
showKana={true} // Show kana fields (default)
|
|
94
|
+
/>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Fields:** `{prefix}_lastname`, `{prefix}_firstname`, `{prefix}_kana_lastname`, `{prefix}_kana_firstname`
|
|
98
|
+
|
|
99
|
+
### OmnifyForm.JapaneseAddress
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
import { OmnifyForm } from '@/omnify/components';
|
|
103
|
+
|
|
104
|
+
<OmnifyForm.JapaneseAddress
|
|
105
|
+
form={form}
|
|
106
|
+
schemas={customerSchemas}
|
|
107
|
+
i18n={customerI18n}
|
|
108
|
+
prefix="address"
|
|
109
|
+
enablePostalLookup={true} // Postal code → address lookup
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Fields:** `{prefix}_postal_code`, `{prefix}_prefecture`, `{prefix}_address1`, `{prefix}_address2`, `{prefix}_address3`
|
|
114
|
+
|
|
115
|
+
**Options:**
|
|
116
|
+
- `enablePostalLookup` - Enable postal code search
|
|
117
|
+
- `autoSearch` - Auto-search when 7 digits entered
|
|
118
|
+
- `usePrefectureId` - Use numeric ID instead of string enum
|
|
119
|
+
|
|
120
|
+
### OmnifyForm.JapaneseBank
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { OmnifyForm } from '@/omnify/components';
|
|
124
|
+
|
|
125
|
+
<OmnifyForm.JapaneseBank
|
|
126
|
+
schemas={customerSchemas}
|
|
127
|
+
i18n={customerI18n}
|
|
128
|
+
prefix="bank"
|
|
129
|
+
/>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Fields:** `{prefix}_bank_code`, `{prefix}_branch_code`, `{prefix}_account_type`, `{prefix}_account_number`, `{prefix}_account_holder`
|
|
133
|
+
|
|
134
|
+
Account type options auto-loaded from `BankAccountType` enum with i18n.
|
|
135
|
+
|
|
136
|
+
## Complete Form Example
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
import { Form, Input, Button, Card, Space, Divider } from 'antd';
|
|
140
|
+
import type { FormInstance } from 'antd';
|
|
141
|
+
import { zodRule, setZodLocale } from '@/omnify/lib';
|
|
142
|
+
import { OmnifyForm } from '@/omnify/components';
|
|
143
|
+
import {
|
|
144
|
+
type Customer, type CustomerCreate,
|
|
145
|
+
customerSchemas, customerI18n,
|
|
146
|
+
getCustomerFieldLabel, getCustomerFieldPlaceholder,
|
|
147
|
+
} from '@/omnify/schemas';
|
|
148
|
+
|
|
149
|
+
const LOCALE = 'ja';
|
|
150
|
+
|
|
151
|
+
interface CustomerFormProps {
|
|
152
|
+
form: FormInstance<CustomerCreate>;
|
|
153
|
+
initialValues?: Partial<Customer>;
|
|
154
|
+
onSubmit: (values: CustomerCreate) => void;
|
|
155
|
+
loading?: boolean;
|
|
156
|
+
isEdit?: boolean;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function CustomerForm({ form, initialValues, onSubmit, loading, isEdit }: CustomerFormProps) {
|
|
160
|
+
setZodLocale(LOCALE);
|
|
161
|
+
|
|
162
|
+
const label = (key: string) => getCustomerFieldLabel(key, LOCALE);
|
|
163
|
+
const placeholder = (key: string) => getCustomerFieldPlaceholder(key, LOCALE);
|
|
164
|
+
const rule = (key: keyof typeof customerSchemas) => zodRule(customerSchemas[key], label(key));
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<Card>
|
|
168
|
+
<Form form={form} layout="horizontal" labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}
|
|
169
|
+
initialValues={initialValues} onFinish={onSubmit} style={{ maxWidth: 800 }}>
|
|
170
|
+
|
|
171
|
+
<Divider orientation="left">Name</Divider>
|
|
172
|
+
<OmnifyForm.JapaneseName schemas={customerSchemas} i18n={customerI18n} prefix="name" required />
|
|
173
|
+
|
|
174
|
+
<Divider orientation="left">Contact</Divider>
|
|
175
|
+
<Form.Item name="phone" label={label('phone')} rules={[rule('phone')]}>
|
|
176
|
+
<Input placeholder={placeholder('phone')} />
|
|
177
|
+
</Form.Item>
|
|
178
|
+
<Form.Item name="email" label={label('email')} rules={[rule('email')]}>
|
|
179
|
+
<Input type="email" placeholder={placeholder('email')} />
|
|
180
|
+
</Form.Item>
|
|
181
|
+
|
|
182
|
+
<Divider orientation="left">Address</Divider>
|
|
183
|
+
<OmnifyForm.JapaneseAddress form={form} schemas={customerSchemas} i18n={customerI18n}
|
|
184
|
+
prefix="address" enablePostalLookup={true} />
|
|
185
|
+
|
|
186
|
+
<Divider orientation="left">Bank Account</Divider>
|
|
187
|
+
<OmnifyForm.JapaneseBank schemas={customerSchemas} i18n={customerI18n} prefix="bank" />
|
|
188
|
+
|
|
189
|
+
<Form.Item wrapperCol={{ offset: 6, span: 18 }}>
|
|
190
|
+
<Space>
|
|
191
|
+
<Button type="primary" htmlType="submit" loading={loading}>
|
|
192
|
+
{isEdit ? 'Update' : 'Create'}
|
|
193
|
+
</Button>
|
|
194
|
+
</Space>
|
|
195
|
+
</Form.Item>
|
|
196
|
+
</Form>
|
|
197
|
+
</Card>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Zod Validation with i18n
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
import { zodRule, setZodLocale } from '@/omnify/lib';
|
|
206
|
+
|
|
207
|
+
// Set locale once at component level
|
|
208
|
+
setZodLocale('ja');
|
|
209
|
+
|
|
210
|
+
// Use zodRule for field validation
|
|
211
|
+
<Form.Item name="email" label={label('email')} rules={[zodRule(customerSchemas.email, label('email'))]}>
|
|
212
|
+
<Input />
|
|
213
|
+
</Form.Item>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Kana Validation Rules
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
import { kanaString, KATAKANA_PATTERN } from '@/omnify/lib/rules';
|
|
220
|
+
|
|
221
|
+
// Full-width katakana (default)
|
|
222
|
+
const kanaSchema = kanaString();
|
|
223
|
+
|
|
224
|
+
// Custom options
|
|
225
|
+
const kanaSchema = kanaString({
|
|
226
|
+
fullWidthKatakana: true,
|
|
227
|
+
hiragana: true,
|
|
228
|
+
allowNumbers: true,
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## File Structure
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
resources/ts/omnify/
|
|
236
|
+
├── components/
|
|
237
|
+
│ ├── JapaneseNameField.tsx
|
|
238
|
+
│ ├── JapaneseAddressField.tsx
|
|
239
|
+
│ └── JapaneseBankField.tsx
|
|
240
|
+
├── enum/
|
|
241
|
+
│ └── plugin/
|
|
242
|
+
│ ├── Prefecture.ts
|
|
243
|
+
│ └── BankAccountType.ts
|
|
244
|
+
├── hooks/
|
|
245
|
+
│ └── use-form-mutation.ts
|
|
246
|
+
├── lib/
|
|
247
|
+
│ ├── form-validation.ts
|
|
248
|
+
│ ├── zod-i18n.ts
|
|
249
|
+
│ └── rules/kana.ts
|
|
250
|
+
└── schemas/
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Tips
|
|
254
|
+
|
|
255
|
+
1. **Use type generics** - `useFormMutation<CustomerCreate>` for type safety
|
|
256
|
+
2. **Use `setZodLocale`** - Call once for localized validation messages
|
|
257
|
+
3. **Use OmnifyForm.* components** - Built-in i18n, validation, postal lookup
|
|
258
|
+
4. **Set `invalidateKeys`** - Auto-refresh lists after mutations
|
|
259
|
+
5. **Don't pass `form` to Name/Bank components** - Only Address needs it for postal lookup
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Omnify Schema Guide
|
|
2
|
+
|
|
3
|
+
## Schema File Format
|
|
4
|
+
|
|
5
|
+
Schemas are YAML files defining data models. Each file represents one entity.
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
name: User
|
|
9
|
+
displayName:
|
|
10
|
+
ja: ユーザー
|
|
11
|
+
en: User
|
|
12
|
+
kind: object
|
|
13
|
+
|
|
14
|
+
properties:
|
|
15
|
+
email:
|
|
16
|
+
type: Email
|
|
17
|
+
unique: true
|
|
18
|
+
name:
|
|
19
|
+
type: String
|
|
20
|
+
bio:
|
|
21
|
+
type: Text
|
|
22
|
+
nullable: true
|
|
23
|
+
|
|
24
|
+
options:
|
|
25
|
+
timestamps: true
|
|
26
|
+
softDelete: true
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Property Types
|
|
30
|
+
|
|
31
|
+
### String Types
|
|
32
|
+
- `String` - VARCHAR(255)
|
|
33
|
+
- `Text` - TEXT
|
|
34
|
+
- `LongText` - LONGTEXT
|
|
35
|
+
- `Email` - VARCHAR(255) for email addresses
|
|
36
|
+
- `Password` - VARCHAR(255), hidden in serialization
|
|
37
|
+
|
|
38
|
+
### Numeric Types
|
|
39
|
+
- `Int` - INTEGER
|
|
40
|
+
- `BigInt` - BIGINT
|
|
41
|
+
- `TinyInt` - TINYINT
|
|
42
|
+
- `Float` - DOUBLE
|
|
43
|
+
- `Decimal` - DECIMAL(precision, scale)
|
|
44
|
+
|
|
45
|
+
### Date/Time Types
|
|
46
|
+
- `Date` - DATE
|
|
47
|
+
- `Time` - TIME
|
|
48
|
+
- `DateTime` - DATETIME
|
|
49
|
+
- `Timestamp` - TIMESTAMP
|
|
50
|
+
|
|
51
|
+
### Other Types
|
|
52
|
+
- `Boolean` - BOOLEAN
|
|
53
|
+
- `Json` - JSON
|
|
54
|
+
- `Enum` - ENUM with values
|
|
55
|
+
- `EnumRef` - Reference to enum schema
|
|
56
|
+
|
|
57
|
+
## Property Options
|
|
58
|
+
|
|
59
|
+
```yaml
|
|
60
|
+
propertyName:
|
|
61
|
+
type: String
|
|
62
|
+
nullable: true # Can be NULL
|
|
63
|
+
unique: true # Unique constraint
|
|
64
|
+
default: "value" # Default value
|
|
65
|
+
length: 100 # VARCHAR length
|
|
66
|
+
hidden: true # Hide from JSON output
|
|
67
|
+
fillable: false # Exclude from mass assignment
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Associations
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
# Many-to-One (belongsTo)
|
|
74
|
+
author:
|
|
75
|
+
type: Association
|
|
76
|
+
relation: ManyToOne
|
|
77
|
+
target: User
|
|
78
|
+
onDelete: CASCADE
|
|
79
|
+
|
|
80
|
+
# One-to-Many (hasMany)
|
|
81
|
+
posts:
|
|
82
|
+
type: Association
|
|
83
|
+
relation: OneToMany
|
|
84
|
+
target: Post
|
|
85
|
+
|
|
86
|
+
# Many-to-Many (belongsToMany)
|
|
87
|
+
tags:
|
|
88
|
+
type: Association
|
|
89
|
+
relation: ManyToMany
|
|
90
|
+
target: Tag
|
|
91
|
+
joinTable: post_tags
|
|
92
|
+
|
|
93
|
+
# Polymorphic
|
|
94
|
+
commentable:
|
|
95
|
+
type: Association
|
|
96
|
+
relation: MorphTo
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Indexes
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
indexes:
|
|
103
|
+
- columns: [status, published_at]
|
|
104
|
+
- columns: [email]
|
|
105
|
+
unique: true
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Schema Options
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
options:
|
|
112
|
+
timestamps: true # Add created_at, updated_at
|
|
113
|
+
softDelete: true # Add deleted_at
|
|
114
|
+
idType: BigInt # Primary key type (Int, BigInt, Uuid)
|
|
115
|
+
```
|
|
@@ -0,0 +1,310 @@
|
|
|
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 |
|