@omnifyjp/ts 8.0.6 → 8.1.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 +3 -1
- package/dist/cli.js +4 -0
- package/dist/generator.d.ts +7 -0
- package/dist/generator.js +3 -1
- package/dist/interface-generator.js +1 -0
- package/dist/metadata-generator.js +1 -1
- package/dist/php/faker-mapper.js +2 -0
- package/dist/php/openapi-generator.js +1 -0
- package/dist/php/schema-reader.js +49 -1
- package/dist/php/type-mapper.js +3 -1
- package/dist/ts-hooks-generator.d.ts +9 -1
- package/dist/ts-hooks-generator.js +5 -7
- package/dist/zod-generator.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,9 @@ Per model with `options.service` or `options.api` (in `<output>/base/`):
|
|
|
26
26
|
- **`<Model>QueryKeys.ts`** — TanStack Query key factory per model
|
|
27
27
|
- **`<Model>Hooks.ts`** — React Query hooks (`useList`, `useDetail`,
|
|
28
28
|
`useCreate`, `useUpdate`, `useDelete`, `useRestore`) with auto-invalidation
|
|
29
|
-
and toast notifications via `sonner`
|
|
29
|
+
and toast notifications via `sonner` (configurable with `hooks.toastImport`,
|
|
30
|
+
or skipped with `hooks.enable: false` — see
|
|
31
|
+
[`docs/reference/typescript-hooks.md`](../../docs/reference/typescript-hooks.md))
|
|
30
32
|
|
|
31
33
|
Plus shared infrastructure at the codegen output root:
|
|
32
34
|
|
package/dist/cli.js
CHANGED
|
@@ -113,6 +113,7 @@ function resolveFromConfig(configPath) {
|
|
|
113
113
|
tsPlatform: tsConfig?.platform,
|
|
114
114
|
tsAuth: tsConfig?.auth,
|
|
115
115
|
tsEnumStyle: tsConfig?.enumStyle,
|
|
116
|
+
tsHooks: tsConfig?.hooks,
|
|
116
117
|
laravelEnabled,
|
|
117
118
|
laravelOverrides,
|
|
118
119
|
};
|
|
@@ -134,6 +135,7 @@ program
|
|
|
134
135
|
let tsPlatform;
|
|
135
136
|
let tsAuth;
|
|
136
137
|
let tsEnumStyle;
|
|
138
|
+
let tsHooks;
|
|
137
139
|
let laravelEnabled = false;
|
|
138
140
|
let laravelOverrides;
|
|
139
141
|
let configDir = process.cwd();
|
|
@@ -159,6 +161,7 @@ program
|
|
|
159
161
|
tsPlatform = resolved.tsPlatform;
|
|
160
162
|
tsAuth = resolved.tsAuth;
|
|
161
163
|
tsEnumStyle = resolved.tsEnumStyle;
|
|
164
|
+
tsHooks = resolved.tsHooks;
|
|
162
165
|
laravelEnabled = resolved.laravelEnabled;
|
|
163
166
|
laravelOverrides = resolved.laravelOverrides;
|
|
164
167
|
}
|
|
@@ -187,6 +190,7 @@ program
|
|
|
187
190
|
platform: tsPlatform,
|
|
188
191
|
auth: tsAuth,
|
|
189
192
|
enumStyle: tsEnumStyle,
|
|
193
|
+
hooks: tsHooks,
|
|
190
194
|
});
|
|
191
195
|
mkdirSync(join(tsOutput, 'base'), { recursive: true });
|
|
192
196
|
mkdirSync(join(tsOutput, 'enum'), { recursive: true });
|
package/dist/generator.d.ts
CHANGED
|
@@ -20,6 +20,13 @@ export interface GenerateExtraConfig {
|
|
|
20
20
|
auth?: 'cookie' | 'secureStore';
|
|
21
21
|
/** Enum emission style — see GeneratorOptions.enumStyle. Issue #103. */
|
|
22
22
|
enumStyle?: 'enum' | 'const';
|
|
23
|
+
/** Hooks layer (`{Entity}Hooks.ts`). Issue #327. */
|
|
24
|
+
hooks?: {
|
|
25
|
+
/** false skips `{Entity}Hooks.ts` entirely. Defaults to true. */
|
|
26
|
+
enable?: boolean;
|
|
27
|
+
/** Module the hooks import `toast` from. Defaults to "sonner". */
|
|
28
|
+
toastImport?: string;
|
|
29
|
+
};
|
|
23
30
|
}
|
|
24
31
|
/**
|
|
25
32
|
* Generate all TypeScript files from schemas.json input.
|
package/dist/generator.js
CHANGED
|
@@ -538,7 +538,9 @@ export function generateTypeScript(input, extra) {
|
|
|
538
538
|
// Only for schemas with options.api or options.service.
|
|
539
539
|
files.push(...generateTsServices(schemas, options));
|
|
540
540
|
files.push(...generateTsQueryKeys(schemas));
|
|
541
|
-
|
|
541
|
+
if (extra?.hooks?.enable !== false) {
|
|
542
|
+
files.push(...generateTsHooks(schemas, { toastImport: extra?.hooks?.toastImport }));
|
|
543
|
+
}
|
|
542
544
|
// React Native / Expo query setup (issue #63)
|
|
543
545
|
if (options.platform === 'expo') {
|
|
544
546
|
files.push(generateRnQuerySetup(options.auth ?? 'secureStore'));
|
|
@@ -21,7 +21,7 @@ import { formatTsObjectKey, tsObjectLiteral } from './ts-literal.js';
|
|
|
21
21
|
// ---------------------------------------------------------------------------
|
|
22
22
|
const TEXT_TYPES = new Set(['Text', 'MediumText', 'LongText']);
|
|
23
23
|
const STRING_TYPES = new Set([
|
|
24
|
-
'String', 'Email', 'Password', 'Phone', 'Slug', 'Url', 'Uuid',
|
|
24
|
+
'String', 'Char', 'Email', 'Password', 'Phone', 'Slug', 'Url', 'Uuid',
|
|
25
25
|
]);
|
|
26
26
|
const NUMBER_TYPES = new Set([
|
|
27
27
|
'TinyInt', 'Int', 'BigInt', 'Float', 'Decimal',
|
package/dist/php/faker-mapper.js
CHANGED
|
@@ -13,6 +13,8 @@ export function toFaker(propName, property, schemas = {}) {
|
|
|
13
13
|
return null;
|
|
14
14
|
switch (type) {
|
|
15
15
|
case 'String': return stringFaker(propName, snakeName, property);
|
|
16
|
+
// Fixed-length: exactly `length` characters, as a CHAR column holds (#319).
|
|
17
|
+
case 'Char': return `'${snakeName}' => fake()->regexify('[A-Z]{${property['length'] ?? 255}}'),`;
|
|
16
18
|
case 'Email': return `'${snakeName}' => fake()->unique()->safeEmail(),`;
|
|
17
19
|
case 'Password': return `'${snakeName}' => bcrypt('password'),`;
|
|
18
20
|
case 'Int':
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
export class SchemaReader {
|
|
5
5
|
data;
|
|
6
6
|
constructor(data) {
|
|
7
|
-
this.data = data;
|
|
7
|
+
this.data = normalizeEnumValues(data);
|
|
8
8
|
}
|
|
9
9
|
static fromData(data) {
|
|
10
10
|
return new SchemaReader(data);
|
|
@@ -371,3 +371,51 @@ export class SchemaReader {
|
|
|
371
371
|
return fields;
|
|
372
372
|
}
|
|
373
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* Bring enum values into the one shape every PHP generator reads (#322).
|
|
376
|
+
*
|
|
377
|
+
* The schema language accepts both forms in both places — `values: [draft]`
|
|
378
|
+
* and `values: [{value: draft, label: …}]` on a `kind: enum` schema, and the
|
|
379
|
+
* same two on an inline `enum:` property — and schemas.json carries them as
|
|
380
|
+
* written. The PHP generators were each written against one form: enum
|
|
381
|
+
* classes read `value.value` (so the README's plain-string example crashed),
|
|
382
|
+
* inline-enum rules interpolate the entry itself (so an object became
|
|
383
|
+
* `[object Object]`). Normalising here, once, keeps them from disagreeing.
|
|
384
|
+
*/
|
|
385
|
+
function normalizeEnumValues(data) {
|
|
386
|
+
if (!data.schemas)
|
|
387
|
+
return data;
|
|
388
|
+
const schemas = {};
|
|
389
|
+
for (const [name, schema] of Object.entries(data.schemas)) {
|
|
390
|
+
let next = schema;
|
|
391
|
+
const rawValues = schema.values;
|
|
392
|
+
if (Array.isArray(rawValues) && rawValues.some(v => typeof v === 'string')) {
|
|
393
|
+
next = {
|
|
394
|
+
...next,
|
|
395
|
+
values: rawValues.map(v => (typeof v === 'string' ? { value: v } : v)),
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
const props = schema.properties;
|
|
399
|
+
if (props) {
|
|
400
|
+
let changed = false;
|
|
401
|
+
const nextProps = {};
|
|
402
|
+
for (const [propName, prop] of Object.entries(props)) {
|
|
403
|
+
const rawEnum = prop.enum;
|
|
404
|
+
if (Array.isArray(rawEnum) && rawEnum.some(v => typeof v !== 'string')) {
|
|
405
|
+
changed = true;
|
|
406
|
+
nextProps[propName] = {
|
|
407
|
+
...prop,
|
|
408
|
+
enum: rawEnum.map(v => (typeof v === 'string' ? v : String(v?.value ?? ''))),
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
nextProps[propName] = prop;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (changed)
|
|
416
|
+
next = { ...next, properties: nextProps };
|
|
417
|
+
}
|
|
418
|
+
schemas[name] = next;
|
|
419
|
+
}
|
|
420
|
+
return { ...data, schemas };
|
|
421
|
+
}
|
package/dist/php/type-mapper.js
CHANGED
|
@@ -67,6 +67,7 @@ export function toPhpDocType(type, nullable = false) {
|
|
|
67
67
|
let phpType;
|
|
68
68
|
switch (type) {
|
|
69
69
|
case 'String':
|
|
70
|
+
case 'Char':
|
|
70
71
|
case 'Email':
|
|
71
72
|
case 'Password':
|
|
72
73
|
case 'Text':
|
|
@@ -108,7 +109,7 @@ export function toPhpDocType(type, nullable = false) {
|
|
|
108
109
|
}
|
|
109
110
|
/** Type categories for rule applicability. */
|
|
110
111
|
const STRING_TYPES = new Set([
|
|
111
|
-
'String', 'Email', 'Password', 'Text', 'MediumText', 'LongText',
|
|
112
|
+
'String', 'Char', 'Email', 'Password', 'Text', 'MediumText', 'LongText',
|
|
112
113
|
'Slug', 'Url', 'Phone', 'Uuid',
|
|
113
114
|
]);
|
|
114
115
|
const NUMERIC_TYPES = new Set(['Int', 'BigInt', 'TinyInt', 'Float', 'Decimal']);
|
|
@@ -148,6 +149,7 @@ export function toStoreRules(property, tableName, fkRuleType = 'integer') {
|
|
|
148
149
|
}
|
|
149
150
|
switch (type) {
|
|
150
151
|
case 'String':
|
|
152
|
+
case 'Char':
|
|
151
153
|
case 'Slug':
|
|
152
154
|
case 'Url':
|
|
153
155
|
case 'Phone': {
|
|
@@ -7,4 +7,12 @@
|
|
|
7
7
|
* notifications and cache invalidation.
|
|
8
8
|
*/
|
|
9
9
|
import type { SchemaDefinition, TypeScriptFile } from './types.js';
|
|
10
|
-
export
|
|
10
|
+
export interface TsHooksOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Module the generated hooks import `toast` from. Defaults to "sonner".
|
|
13
|
+
* Point it at a design-system wrapper (e.g. "@godxjp/ui") whose Toaster
|
|
14
|
+
* listens to its own copy of sonner. Issue #327.
|
|
15
|
+
*/
|
|
16
|
+
toastImport?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function generateTsHooks(schemas: Record<string, SchemaDefinition>, hooksOptions?: TsHooksOptions): TypeScriptFile[];
|
|
@@ -6,11 +6,9 @@
|
|
|
6
6
|
* that returns TanStack Query hooks (useQuery + useMutation) with toast
|
|
7
7
|
* notifications and cache invalidation.
|
|
8
8
|
*/
|
|
9
|
-
|
|
10
|
-
// Public API
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
export function generateTsHooks(schemas) {
|
|
9
|
+
export function generateTsHooks(schemas, hooksOptions = {}) {
|
|
13
10
|
const files = [];
|
|
11
|
+
const toastImport = hooksOptions.toastImport || 'sonner';
|
|
14
12
|
for (const [name, schema] of Object.entries(schemas)) {
|
|
15
13
|
if (schema.kind === 'enum')
|
|
16
14
|
continue;
|
|
@@ -18,14 +16,14 @@ export function generateTsHooks(schemas) {
|
|
|
18
16
|
continue;
|
|
19
17
|
if (!schema.options?.api && !schema.options?.service)
|
|
20
18
|
continue;
|
|
21
|
-
files.push(generateHooksFile(name, schema));
|
|
19
|
+
files.push(generateHooksFile(name, schema, toastImport));
|
|
22
20
|
}
|
|
23
21
|
return files;
|
|
24
22
|
}
|
|
25
23
|
// ---------------------------------------------------------------------------
|
|
26
24
|
// Per-schema generator
|
|
27
25
|
// ---------------------------------------------------------------------------
|
|
28
|
-
function generateHooksFile(name, schema) {
|
|
26
|
+
function generateHooksFile(name, schema, toastImport) {
|
|
29
27
|
const hasSoftDelete = schema.options?.softDelete ?? false;
|
|
30
28
|
const hasRestore = schema.options?.api?.restore ?? hasSoftDelete;
|
|
31
29
|
const restoreHook = hasRestore ? `
|
|
@@ -46,7 +44,7 @@ function generateHooksFile(name, schema) {
|
|
|
46
44
|
* @generated by omnify
|
|
47
45
|
*/
|
|
48
46
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
49
|
-
import { toast } from
|
|
47
|
+
import { toast } from ${JSON.stringify(toastImport)};
|
|
50
48
|
import type { ${name}Filters, ${name}BaseServiceType } from "./${name}Service";
|
|
51
49
|
import type { create${name}QueryKeys } from "./${name}QueryKeys";
|
|
52
50
|
|
package/dist/zod-generator.js
CHANGED
|
@@ -50,7 +50,7 @@ function autoDerivePlaceholder(label, locales) {
|
|
|
50
50
|
}
|
|
51
51
|
/** Type categories for Zod rule applicability. */
|
|
52
52
|
const ZOD_STRING_TYPES = new Set([
|
|
53
|
-
'String', 'Email', 'Password', 'Text', 'MediumText', 'LongText',
|
|
53
|
+
'String', 'Char', 'Email', 'Password', 'Text', 'MediumText', 'LongText',
|
|
54
54
|
'Slug', 'Url', 'Phone', 'Uuid',
|
|
55
55
|
]);
|
|
56
56
|
const ZOD_NUMERIC_TYPES = new Set(['Int', 'BigInt', 'TinyInt', 'Float', 'Decimal']);
|
|
@@ -172,6 +172,7 @@ function getZodSchemaForType(propDef, _fieldName, options) {
|
|
|
172
172
|
}
|
|
173
173
|
switch (propDef.type) {
|
|
174
174
|
case 'String':
|
|
175
|
+
case 'Char':
|
|
175
176
|
case 'Text':
|
|
176
177
|
case 'MediumText':
|
|
177
178
|
case 'LongText':
|