@famgia/omnify-laravel 0.0.84 → 0.0.86

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/index.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  schemaToBlueprint,
25
25
  toColumnName,
26
26
  toTableName
27
- } from "./chunk-KKNVSIBX.js";
27
+ } from "./chunk-YVVAJA3T.js";
28
28
  export {
29
29
  formatColumnMethod,
30
30
  formatForeignKey,
package/dist/plugin.cjs CHANGED
@@ -2584,6 +2584,129 @@ var SKIP_FIELDS = /* @__PURE__ */ new Set([
2584
2584
  "remember_token",
2585
2585
  "email_verified_at"
2586
2586
  ]);
2587
+ function getExampleValue(fieldName, type) {
2588
+ if (fieldName.endsWith("_lastname") && !fieldName.includes("kana")) {
2589
+ return "\u7530\u4E2D";
2590
+ }
2591
+ if (fieldName.endsWith("_firstname") && !fieldName.includes("kana")) {
2592
+ return "\u592A\u90CE";
2593
+ }
2594
+ if (fieldName.includes("kana_lastname") || fieldName.endsWith("_kana_lastname")) {
2595
+ return "\u30BF\u30CA\u30AB";
2596
+ }
2597
+ if (fieldName.includes("kana_firstname") || fieldName.endsWith("_kana_firstname")) {
2598
+ return "\u30BF\u30ED\u30A6";
2599
+ }
2600
+ if (fieldName.includes("email")) {
2601
+ return "user@example.com";
2602
+ }
2603
+ if (fieldName.includes("password")) {
2604
+ return "password123";
2605
+ }
2606
+ if (fieldName.includes("url") || fieldName.includes("website")) {
2607
+ return "https://example.com";
2608
+ }
2609
+ if (fieldName.endsWith("_id")) {
2610
+ return 1;
2611
+ }
2612
+ if (fieldName.endsWith("_at")) {
2613
+ return "2024-01-01T00:00:00Z";
2614
+ }
2615
+ if (type === "boolean") {
2616
+ return true;
2617
+ }
2618
+ if (type === "integer") {
2619
+ return 1;
2620
+ }
2621
+ return void 0;
2622
+ }
2623
+ function mapValidationToOpenApiType(rules, fieldName) {
2624
+ if (fieldName.includes("email")) {
2625
+ return { type: "string", format: "email" };
2626
+ }
2627
+ if (fieldName.includes("password")) {
2628
+ return { type: "string", format: "password" };
2629
+ }
2630
+ if (fieldName.includes("url") || fieldName.includes("website")) {
2631
+ return { type: "string", format: "uri" };
2632
+ }
2633
+ for (const rule of rules) {
2634
+ if (rule === "'integer'") {
2635
+ return { type: "integer" };
2636
+ }
2637
+ if (rule === "'boolean'") {
2638
+ return { type: "boolean" };
2639
+ }
2640
+ if (rule === "'numeric'") {
2641
+ return { type: "number" };
2642
+ }
2643
+ if (rule === "'array'") {
2644
+ return { type: "array" };
2645
+ }
2646
+ if (rule === "'email'") {
2647
+ return { type: "string", format: "email" };
2648
+ }
2649
+ if (rule === "'url'") {
2650
+ return { type: "string", format: "uri" };
2651
+ }
2652
+ if (rule === "'date'") {
2653
+ if (fieldName.endsWith("_at")) {
2654
+ return { type: "string", format: "date-time" };
2655
+ }
2656
+ return { type: "string", format: "date" };
2657
+ }
2658
+ }
2659
+ return { type: "string" };
2660
+ }
2661
+ function extractConstraints(rules) {
2662
+ const constraints = {};
2663
+ for (const rule of rules) {
2664
+ const maxMatch = rule.match(/'max:(\d+)'/);
2665
+ if (maxMatch) {
2666
+ constraints.maxLength = parseInt(maxMatch[1], 10);
2667
+ }
2668
+ const minMatch = rule.match(/'min:(\d+)'/);
2669
+ if (minMatch) {
2670
+ constraints.minLength = parseInt(minMatch[1], 10);
2671
+ }
2672
+ if (rule === "'nullable'") {
2673
+ constraints.nullable = true;
2674
+ }
2675
+ const inMatch = rule.match(/Rule::in\(\[([^\]]+)\]\)/);
2676
+ if (inMatch) {
2677
+ const values = inMatch[1].split(",").map((v) => v.trim().replace(/^'|'$/g, ""));
2678
+ constraints.enum = values;
2679
+ }
2680
+ }
2681
+ return constraints;
2682
+ }
2683
+ function formatRequestOpenApiProperty(prop, indent) {
2684
+ const parts = [`property: '${prop.property}'`, `type: '${prop.type}'`];
2685
+ if (prop.format) {
2686
+ parts.push(`format: '${prop.format}'`);
2687
+ }
2688
+ if (prop.maxLength) {
2689
+ parts.push(`maxLength: ${prop.maxLength}`);
2690
+ }
2691
+ if (prop.minLength) {
2692
+ parts.push(`minLength: ${prop.minLength}`);
2693
+ }
2694
+ if (prop.nullable) {
2695
+ parts.push(`nullable: true`);
2696
+ }
2697
+ if (prop.enum) {
2698
+ const enumStr = prop.enum.map((v) => `'${v}'`).join(", ");
2699
+ parts.push(`enum: [${enumStr}]`);
2700
+ }
2701
+ if (prop.example !== void 0) {
2702
+ if (typeof prop.example === "string") {
2703
+ parts.push(`example: '${escapePhpString2(prop.example)}'`);
2704
+ } else {
2705
+ parts.push(`example: ${prop.example}`);
2706
+ }
2707
+ }
2708
+ return `${indent}new OA\\Property(${parts.join(", ")})`;
2709
+ }
2587
2710
  function resolveOptions3(options) {
2588
2711
  return {
2589
2712
  baseRequestNamespace: options?.baseRequestNamespace ?? DEFAULT_OPTIONS2.baseRequestNamespace,
@@ -3117,12 +3240,82 @@ ${attributeLines.join("\n")}
3117
3240
  module: module2
3118
3241
  };
3119
3242
  }
3120
- function generateStoreRequest(schema, options) {
3243
+ function generateRequestOpenApiProperties(schema, schemas, options, isStore) {
3244
+ const properties = [];
3245
+ const requiredFields = [];
3246
+ const schemaProps = schema.properties ?? {};
3247
+ for (const [propName, propDef] of Object.entries(schemaProps)) {
3248
+ const snakeName = toSnakeCase(propName);
3249
+ if (SKIP_FIELDS.has(snakeName)) continue;
3250
+ if (propDef.type === "Association") {
3251
+ const assoc = propDef;
3252
+ if (assoc.relation !== "ManyToOne" && assoc.relation !== "OneToOne") {
3253
+ continue;
3254
+ }
3255
+ const fkName = `${snakeName}_id`;
3256
+ const rules2 = isStore ? generateStoreRules(propName, propDef, schema, schemas, options) : generateUpdateRules(propName, propDef, schema, schemas, options);
3257
+ const openApiType2 = mapValidationToOpenApiType(rules2, fkName);
3258
+ const constraints2 = extractConstraints(rules2);
3259
+ const example2 = getExampleValue(fkName, openApiType2.type);
3260
+ const prop2 = {
3261
+ property: fkName,
3262
+ ...openApiType2,
3263
+ ...constraints2
3264
+ };
3265
+ if (example2 !== void 0) prop2.example = example2;
3266
+ properties.push(prop2);
3267
+ if (isStore && !propDef.nullable) {
3268
+ requiredFields.push(fkName);
3269
+ }
3270
+ continue;
3271
+ }
3272
+ const expandedFields = expandCompoundTypeFields(propName, propDef, options);
3273
+ if (expandedFields.length > 0) {
3274
+ for (const field of expandedFields) {
3275
+ const openApiType2 = mapValidationToOpenApiType(field.rules, field.fieldName);
3276
+ const constraints2 = extractConstraints(field.rules);
3277
+ const example2 = getExampleValue(field.fieldName, openApiType2.type);
3278
+ const prop2 = {
3279
+ property: field.fieldName,
3280
+ ...openApiType2,
3281
+ ...constraints2
3282
+ };
3283
+ if (example2 !== void 0) prop2.example = example2;
3284
+ properties.push(prop2);
3285
+ if (isStore && field.rules.includes("'required'")) {
3286
+ requiredFields.push(field.fieldName);
3287
+ }
3288
+ }
3289
+ continue;
3290
+ }
3291
+ const rules = isStore ? generateStoreRules(propName, propDef, schema, schemas, options) : generateUpdateRules(propName, propDef, schema, schemas, options);
3292
+ const openApiType = mapValidationToOpenApiType(rules, snakeName);
3293
+ const constraints = extractConstraints(rules);
3294
+ const example = getExampleValue(snakeName, openApiType.type);
3295
+ const prop = {
3296
+ property: snakeName,
3297
+ ...openApiType,
3298
+ ...constraints
3299
+ };
3300
+ if (example !== void 0) prop.example = example;
3301
+ properties.push(prop);
3302
+ if (isStore && rules.includes("'required'")) {
3303
+ requiredFields.push(snakeName);
3304
+ }
3305
+ }
3306
+ return { properties, requiredFields };
3307
+ }
3308
+ function generateStoreRequest(schema, schemas, options) {
3121
3309
  const className = toPascalCase(schema.name);
3122
3310
  const module2 = getModuleName(schema);
3123
3311
  const namespaceModule = module2 ? `\\${module2}` : "";
3124
3312
  const namespace = `${options.requestNamespace}${namespaceModule}`;
3125
3313
  const baseNamespace = `${options.baseRequestNamespace}${namespaceModule}`;
3314
+ const { properties, requiredFields } = generateRequestOpenApiProperties(schema, schemas, options, true);
3315
+ const propsIndent = " ";
3316
+ const openApiPropsFormatted = properties.map((prop) => formatRequestOpenApiProperty(prop, propsIndent)).join(",\n");
3317
+ const requiredArray = requiredFields.length > 0 ? `
3318
+ required: [${requiredFields.map((f) => `'${f}'`).join(", ")}],` : "";
3126
3319
  const content = `<?php
3127
3320
 
3128
3321
  /**
@@ -3133,8 +3326,15 @@ function generateStoreRequest(schema, options) {
3133
3326
 
3134
3327
  namespace ${namespace};
3135
3328
 
3329
+ use OpenApi\\Attributes as OA;
3136
3330
  use ${baseNamespace}\\${className}StoreRequestBase;
3137
3331
 
3332
+ #[OA\\Schema(
3333
+ schema: '${schema.name}StoreRequest',${requiredArray}
3334
+ properties: [
3335
+ ${openApiPropsFormatted},
3336
+ ]
3337
+ )]
3138
3338
  class ${className}StoreRequest extends ${className}StoreRequestBase
3139
3339
  {
3140
3340
  /**
@@ -3192,12 +3392,15 @@ class ${className}StoreRequest extends ${className}StoreRequestBase
3192
3392
  module: module2
3193
3393
  };
3194
3394
  }
3195
- function generateUpdateRequest(schema, options) {
3395
+ function generateUpdateRequest(schema, schemas, options) {
3196
3396
  const className = toPascalCase(schema.name);
3197
3397
  const module2 = getModuleName(schema);
3198
3398
  const namespaceModule = module2 ? `\\${module2}` : "";
3199
3399
  const namespace = `${options.requestNamespace}${namespaceModule}`;
3200
3400
  const baseNamespace = `${options.baseRequestNamespace}${namespaceModule}`;
3401
+ const { properties } = generateRequestOpenApiProperties(schema, schemas, options, false);
3402
+ const propsIndent = " ";
3403
+ const openApiPropsFormatted = properties.map((prop) => formatRequestOpenApiProperty(prop, propsIndent)).join(",\n");
3201
3404
  const content = `<?php
3202
3405
 
3203
3406
  /**
@@ -3208,8 +3411,15 @@ function generateUpdateRequest(schema, options) {
3208
3411
 
3209
3412
  namespace ${namespace};
3210
3413
 
3414
+ use OpenApi\\Attributes as OA;
3211
3415
  use ${baseNamespace}\\${className}UpdateRequestBase;
3212
3416
 
3417
+ #[OA\\Schema(
3418
+ schema: '${schema.name}UpdateRequest',
3419
+ properties: [
3420
+ ${openApiPropsFormatted},
3421
+ ]
3422
+ )]
3213
3423
  class ${className}UpdateRequest extends ${className}UpdateRequestBase
3214
3424
  {
3215
3425
  /**
@@ -3275,8 +3485,8 @@ function generateRequests(schemas, options) {
3275
3485
  if (schema.options?.hidden === true) continue;
3276
3486
  requests.push(generateStoreRequestBase(schema, schemas, resolved));
3277
3487
  requests.push(generateUpdateRequestBase(schema, schemas, resolved));
3278
- requests.push(generateStoreRequest(schema, resolved));
3279
- requests.push(generateUpdateRequest(schema, resolved));
3488
+ requests.push(generateStoreRequest(schema, schemas, resolved));
3489
+ requests.push(generateUpdateRequest(schema, schemas, resolved));
3280
3490
  }
3281
3491
  return requests;
3282
3492
  }
@@ -3298,6 +3508,157 @@ var SKIP_FIELDS2 = /* @__PURE__ */ new Set([
3298
3508
  "password",
3299
3509
  "remember_token"
3300
3510
  ]);
3511
+ function mapTsTypeToOpenApi(tsType, fieldName) {
3512
+ if (fieldName.includes("email")) {
3513
+ return { type: "string", format: "email" };
3514
+ }
3515
+ switch (tsType) {
3516
+ case "string":
3517
+ return { type: "string" };
3518
+ case "number":
3519
+ return { type: "number" };
3520
+ case "boolean":
3521
+ return { type: "boolean" };
3522
+ default:
3523
+ return { type: "string" };
3524
+ }
3525
+ }
3526
+ function getOpenApiType(propDef, fieldName) {
3527
+ switch (propDef.type) {
3528
+ case "Date":
3529
+ return { type: "string", format: "date" };
3530
+ case "DateTime":
3531
+ case "Timestamp":
3532
+ return { type: "string", format: "date-time" };
3533
+ }
3534
+ if (fieldName.includes("email") && !fieldName.endsWith("_at")) {
3535
+ return { type: "string", format: "email" };
3536
+ }
3537
+ if (fieldName.includes("password")) {
3538
+ return { type: "string", format: "password" };
3539
+ }
3540
+ switch (propDef.type) {
3541
+ case "String":
3542
+ case "Text":
3543
+ case "LongText":
3544
+ return { type: "string" };
3545
+ case "Int":
3546
+ case "BigInt":
3547
+ return { type: "integer" };
3548
+ case "Float":
3549
+ case "Decimal":
3550
+ return { type: "number" };
3551
+ case "Boolean":
3552
+ return { type: "boolean" };
3553
+ case "Email":
3554
+ return { type: "string", format: "email" };
3555
+ case "UUID":
3556
+ return { type: "string", format: "uuid" };
3557
+ case "JSON":
3558
+ return { type: "object" };
3559
+ default:
3560
+ return { type: "string" };
3561
+ }
3562
+ }
3563
+ function generateOpenApiProperties(schema, options) {
3564
+ const properties = [];
3565
+ const schemaProps = schema.properties ?? {};
3566
+ if (schema.options?.id !== false) {
3567
+ properties.push({
3568
+ property: "id",
3569
+ type: "integer",
3570
+ example: 1
3571
+ });
3572
+ }
3573
+ for (const [propName, propDef] of Object.entries(schemaProps)) {
3574
+ const snakeName = toSnakeCase(propName);
3575
+ if (SKIP_FIELDS2.has(snakeName)) continue;
3576
+ if (propDef.hidden === true) continue;
3577
+ if (propDef.type === "Association") continue;
3578
+ const typeDef = options.customTypes.get(propDef.type);
3579
+ if (typeDef?.compound && typeDef.expand) {
3580
+ for (const field of typeDef.expand) {
3581
+ const suffixSnake = toSnakeCase(field.suffix);
3582
+ const fieldName = `${snakeName}_${suffixSnake}`;
3583
+ const tsType = field.typescript?.type ?? "string";
3584
+ const openApiType2 = mapTsTypeToOpenApi(tsType, fieldName);
3585
+ const prop2 = {
3586
+ property: fieldName,
3587
+ ...openApiType2
3588
+ };
3589
+ const sqlDef = field.sql;
3590
+ if (sqlDef?.length) {
3591
+ prop2.maxLength = sqlDef.length;
3592
+ }
3593
+ properties.push(prop2);
3594
+ }
3595
+ if (typeDef.accessors) {
3596
+ for (const accessor of typeDef.accessors) {
3597
+ const accessorName = `${snakeName}_${toSnakeCase(accessor.name)}`;
3598
+ properties.push({
3599
+ property: accessorName,
3600
+ type: "string"
3601
+ });
3602
+ }
3603
+ }
3604
+ continue;
3605
+ }
3606
+ const openApiType = getOpenApiType(propDef, snakeName);
3607
+ const prop = {
3608
+ property: snakeName,
3609
+ ...openApiType
3610
+ };
3611
+ const length = propDef.length;
3612
+ if (length) {
3613
+ prop.maxLength = length;
3614
+ }
3615
+ if (propDef.nullable) {
3616
+ prop.nullable = true;
3617
+ }
3618
+ properties.push(prop);
3619
+ }
3620
+ if (schema.options?.timestamps !== false) {
3621
+ properties.push({
3622
+ property: "created_at",
3623
+ type: "string",
3624
+ format: "date-time"
3625
+ });
3626
+ properties.push({
3627
+ property: "updated_at",
3628
+ type: "string",
3629
+ format: "date-time"
3630
+ });
3631
+ }
3632
+ if (schema.options?.softDelete) {
3633
+ properties.push({
3634
+ property: "deleted_at",
3635
+ type: "string",
3636
+ format: "date-time",
3637
+ nullable: true
3638
+ });
3639
+ }
3640
+ return properties;
3641
+ }
3642
+ function formatOpenApiProperty(prop, indent) {
3643
+ const parts = [`property: '${prop.property}'`, `type: '${prop.type}'`];
3644
+ if (prop.format) {
3645
+ parts.push(`format: '${prop.format}'`);
3646
+ }
3647
+ if (prop.maxLength) {
3648
+ parts.push(`maxLength: ${prop.maxLength}`);
3649
+ }
3650
+ if (prop.nullable) {
3651
+ parts.push(`nullable: true`);
3652
+ }
3653
+ if (prop.example !== void 0) {
3654
+ if (typeof prop.example === "string") {
3655
+ parts.push(`example: '${prop.example}'`);
3656
+ } else {
3657
+ parts.push(`example: ${prop.example}`);
3658
+ }
3659
+ }
3660
+ return `${indent}new OA\\Property(${parts.join(", ")})`;
3661
+ }
3301
3662
  function resolveOptions4(options) {
3302
3663
  return {
3303
3664
  baseResourceNamespace: options?.baseResourceNamespace ?? DEFAULT_OPTIONS3.baseResourceNamespace,
@@ -3308,6 +3669,9 @@ function resolveOptions4(options) {
3308
3669
  locale: options?.locale ?? DEFAULT_OPTIONS3.locale
3309
3670
  };
3310
3671
  }
3672
+ function escapePhpString3(str) {
3673
+ return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
3674
+ }
3311
3675
  function getModuleName2(schema) {
3312
3676
  if (schema.module) {
3313
3677
  return schema.module;
@@ -3455,6 +3819,10 @@ function generateResource(schema, options) {
3455
3819
  const namespaceModule = module2 ? `\\${module2}` : "";
3456
3820
  const namespace = `${options.resourceNamespace}${namespaceModule}`;
3457
3821
  const baseNamespace = `${options.baseResourceNamespace}${namespaceModule}`;
3822
+ const openApiProps = generateOpenApiProperties(schema, options);
3823
+ const propsIndent = " ";
3824
+ const openApiPropsFormatted = openApiProps.map((prop) => formatOpenApiProperty(prop, propsIndent)).join(",\n");
3825
+ const description = schema.displayName ? typeof schema.displayName === "string" ? schema.displayName : schema.displayName["en"] ?? schema.name : `${schema.name} resource`;
3458
3826
  const content = `<?php
3459
3827
 
3460
3828
  /**
@@ -3466,8 +3834,16 @@ function generateResource(schema, options) {
3466
3834
  namespace ${namespace};
3467
3835
 
3468
3836
  use Illuminate\\Http\\Request;
3837
+ use OpenApi\\Attributes as OA;
3469
3838
  use ${baseNamespace}\\${className}ResourceBase;
3470
3839
 
3840
+ #[OA\\Schema(
3841
+ schema: '${schema.name}',
3842
+ description: '${escapePhpString3(description)}',
3843
+ properties: [
3844
+ ${openApiPropsFormatted},
3845
+ ]
3846
+ )]
3471
3847
  class ${className}Resource extends ${className}ResourceBase
3472
3848
  {
3473
3849
  /**