@famgia/omnify-laravel 0.0.84 → 0.0.85

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.cjs CHANGED
@@ -2659,6 +2659,129 @@ var SKIP_FIELDS = /* @__PURE__ */ new Set([
2659
2659
  "remember_token",
2660
2660
  "email_verified_at"
2661
2661
  ]);
2662
+ function getExampleValue(fieldName, type) {
2663
+ if (fieldName.endsWith("_lastname") && !fieldName.includes("kana")) {
2664
+ return "\u7530\u4E2D";
2665
+ }
2666
+ if (fieldName.endsWith("_firstname") && !fieldName.includes("kana")) {
2667
+ return "\u592A\u90CE";
2668
+ }
2669
+ if (fieldName.includes("kana_lastname") || fieldName.endsWith("_kana_lastname")) {
2670
+ return "\u30BF\u30CA\u30AB";
2671
+ }
2672
+ if (fieldName.includes("kana_firstname") || fieldName.endsWith("_kana_firstname")) {
2673
+ return "\u30BF\u30ED\u30A6";
2674
+ }
2675
+ if (fieldName.includes("email")) {
2676
+ return "user@example.com";
2677
+ }
2678
+ if (fieldName.includes("password")) {
2679
+ return "password123";
2680
+ }
2681
+ if (fieldName.includes("url") || fieldName.includes("website")) {
2682
+ return "https://example.com";
2683
+ }
2684
+ if (fieldName.endsWith("_id")) {
2685
+ return 1;
2686
+ }
2687
+ if (fieldName.endsWith("_at")) {
2688
+ return "2024-01-01T00:00:00Z";
2689
+ }
2690
+ if (type === "boolean") {
2691
+ return true;
2692
+ }
2693
+ if (type === "integer") {
2694
+ return 1;
2695
+ }
2696
+ return void 0;
2697
+ }
2698
+ function mapValidationToOpenApiType(rules, fieldName) {
2699
+ if (fieldName.includes("email")) {
2700
+ return { type: "string", format: "email" };
2701
+ }
2702
+ if (fieldName.includes("password")) {
2703
+ return { type: "string", format: "password" };
2704
+ }
2705
+ if (fieldName.includes("url") || fieldName.includes("website")) {
2706
+ return { type: "string", format: "uri" };
2707
+ }
2708
+ for (const rule of rules) {
2709
+ if (rule === "'integer'") {
2710
+ return { type: "integer" };
2711
+ }
2712
+ if (rule === "'boolean'") {
2713
+ return { type: "boolean" };
2714
+ }
2715
+ if (rule === "'numeric'") {
2716
+ return { type: "number" };
2717
+ }
2718
+ if (rule === "'array'") {
2719
+ return { type: "array" };
2720
+ }
2721
+ if (rule === "'email'") {
2722
+ return { type: "string", format: "email" };
2723
+ }
2724
+ if (rule === "'url'") {
2725
+ return { type: "string", format: "uri" };
2726
+ }
2727
+ if (rule === "'date'") {
2728
+ if (fieldName.endsWith("_at")) {
2729
+ return { type: "string", format: "date-time" };
2730
+ }
2731
+ return { type: "string", format: "date" };
2732
+ }
2733
+ }
2734
+ return { type: "string" };
2735
+ }
2736
+ function extractConstraints(rules) {
2737
+ const constraints = {};
2738
+ for (const rule of rules) {
2739
+ const maxMatch = rule.match(/'max:(\d+)'/);
2740
+ if (maxMatch) {
2741
+ constraints.maxLength = parseInt(maxMatch[1], 10);
2742
+ }
2743
+ const minMatch = rule.match(/'min:(\d+)'/);
2744
+ if (minMatch) {
2745
+ constraints.minLength = parseInt(minMatch[1], 10);
2746
+ }
2747
+ if (rule === "'nullable'") {
2748
+ constraints.nullable = true;
2749
+ }
2750
+ const inMatch = rule.match(/Rule::in\(\[([^\]]+)\]\)/);
2751
+ if (inMatch) {
2752
+ const values = inMatch[1].split(",").map((v) => v.trim().replace(/^'|'$/g, ""));
2753
+ constraints.enum = values;
2754
+ }
2755
+ }
2756
+ return constraints;
2757
+ }
2758
+ function formatRequestOpenApiProperty(prop, indent) {
2759
+ const parts = [`property: '${prop.property}'`, `type: '${prop.type}'`];
2760
+ if (prop.format) {
2761
+ parts.push(`format: '${prop.format}'`);
2762
+ }
2763
+ if (prop.maxLength) {
2764
+ parts.push(`maxLength: ${prop.maxLength}`);
2765
+ }
2766
+ if (prop.minLength) {
2767
+ parts.push(`minLength: ${prop.minLength}`);
2768
+ }
2769
+ if (prop.nullable) {
2770
+ parts.push(`nullable: true`);
2771
+ }
2772
+ if (prop.enum) {
2773
+ const enumStr = prop.enum.map((v) => `'${v}'`).join(", ");
2774
+ parts.push(`enum: [${enumStr}]`);
2775
+ }
2776
+ if (prop.example !== void 0) {
2777
+ if (typeof prop.example === "string") {
2778
+ parts.push(`example: '${escapePhpString2(prop.example)}'`);
2779
+ } else {
2780
+ parts.push(`example: ${prop.example}`);
2781
+ }
2782
+ }
2783
+ return `${indent}new OA\\Property(${parts.join(", ")})`;
2784
+ }
2662
2785
  function resolveOptions3(options) {
2663
2786
  return {
2664
2787
  baseRequestNamespace: options?.baseRequestNamespace ?? DEFAULT_OPTIONS2.baseRequestNamespace,
@@ -3192,12 +3315,82 @@ ${attributeLines.join("\n")}
3192
3315
  module: module2
3193
3316
  };
3194
3317
  }
3195
- function generateStoreRequest(schema, options) {
3318
+ function generateRequestOpenApiProperties(schema, schemas, options, isStore) {
3319
+ const properties = [];
3320
+ const requiredFields = [];
3321
+ const schemaProps = schema.properties ?? {};
3322
+ for (const [propName, propDef] of Object.entries(schemaProps)) {
3323
+ const snakeName = toSnakeCase(propName);
3324
+ if (SKIP_FIELDS.has(snakeName)) continue;
3325
+ if (propDef.type === "Association") {
3326
+ const assoc = propDef;
3327
+ if (assoc.relation !== "ManyToOne" && assoc.relation !== "OneToOne") {
3328
+ continue;
3329
+ }
3330
+ const fkName = `${snakeName}_id`;
3331
+ const rules2 = isStore ? generateStoreRules(propName, propDef, schema, schemas, options) : generateUpdateRules(propName, propDef, schema, schemas, options);
3332
+ const openApiType2 = mapValidationToOpenApiType(rules2, fkName);
3333
+ const constraints2 = extractConstraints(rules2);
3334
+ const example2 = getExampleValue(fkName, openApiType2.type);
3335
+ const prop2 = {
3336
+ property: fkName,
3337
+ ...openApiType2,
3338
+ ...constraints2
3339
+ };
3340
+ if (example2 !== void 0) prop2.example = example2;
3341
+ properties.push(prop2);
3342
+ if (isStore && !propDef.nullable) {
3343
+ requiredFields.push(fkName);
3344
+ }
3345
+ continue;
3346
+ }
3347
+ const expandedFields = expandCompoundTypeFields(propName, propDef, options);
3348
+ if (expandedFields.length > 0) {
3349
+ for (const field of expandedFields) {
3350
+ const openApiType2 = mapValidationToOpenApiType(field.rules, field.fieldName);
3351
+ const constraints2 = extractConstraints(field.rules);
3352
+ const example2 = getExampleValue(field.fieldName, openApiType2.type);
3353
+ const prop2 = {
3354
+ property: field.fieldName,
3355
+ ...openApiType2,
3356
+ ...constraints2
3357
+ };
3358
+ if (example2 !== void 0) prop2.example = example2;
3359
+ properties.push(prop2);
3360
+ if (isStore && field.rules.includes("'required'")) {
3361
+ requiredFields.push(field.fieldName);
3362
+ }
3363
+ }
3364
+ continue;
3365
+ }
3366
+ const rules = isStore ? generateStoreRules(propName, propDef, schema, schemas, options) : generateUpdateRules(propName, propDef, schema, schemas, options);
3367
+ const openApiType = mapValidationToOpenApiType(rules, snakeName);
3368
+ const constraints = extractConstraints(rules);
3369
+ const example = getExampleValue(snakeName, openApiType.type);
3370
+ const prop = {
3371
+ property: snakeName,
3372
+ ...openApiType,
3373
+ ...constraints
3374
+ };
3375
+ if (example !== void 0) prop.example = example;
3376
+ properties.push(prop);
3377
+ if (isStore && rules.includes("'required'")) {
3378
+ requiredFields.push(snakeName);
3379
+ }
3380
+ }
3381
+ return { properties, requiredFields };
3382
+ }
3383
+ function generateStoreRequest(schema, schemas, options) {
3196
3384
  const className = toPascalCase(schema.name);
3197
3385
  const module2 = getModuleName(schema);
3198
3386
  const namespaceModule = module2 ? `\\${module2}` : "";
3199
3387
  const namespace = `${options.requestNamespace}${namespaceModule}`;
3200
3388
  const baseNamespace = `${options.baseRequestNamespace}${namespaceModule}`;
3389
+ const { properties, requiredFields } = generateRequestOpenApiProperties(schema, schemas, options, true);
3390
+ const propsIndent = " ";
3391
+ const openApiPropsFormatted = properties.map((prop) => formatRequestOpenApiProperty(prop, propsIndent)).join(",\n");
3392
+ const requiredArray = requiredFields.length > 0 ? `
3393
+ required: [${requiredFields.map((f) => `'${f}'`).join(", ")}],` : "";
3201
3394
  const content = `<?php
3202
3395
 
3203
3396
  /**
@@ -3208,8 +3401,15 @@ function generateStoreRequest(schema, options) {
3208
3401
 
3209
3402
  namespace ${namespace};
3210
3403
 
3404
+ use OpenApi\\Attributes as OA;
3211
3405
  use ${baseNamespace}\\${className}StoreRequestBase;
3212
3406
 
3407
+ #[OA\\Schema(
3408
+ schema: '${schema.name}StoreRequest',${requiredArray}
3409
+ properties: [
3410
+ ${openApiPropsFormatted},
3411
+ ]
3412
+ )]
3213
3413
  class ${className}StoreRequest extends ${className}StoreRequestBase
3214
3414
  {
3215
3415
  /**
@@ -3267,12 +3467,15 @@ class ${className}StoreRequest extends ${className}StoreRequestBase
3267
3467
  module: module2
3268
3468
  };
3269
3469
  }
3270
- function generateUpdateRequest(schema, options) {
3470
+ function generateUpdateRequest(schema, schemas, options) {
3271
3471
  const className = toPascalCase(schema.name);
3272
3472
  const module2 = getModuleName(schema);
3273
3473
  const namespaceModule = module2 ? `\\${module2}` : "";
3274
3474
  const namespace = `${options.requestNamespace}${namespaceModule}`;
3275
3475
  const baseNamespace = `${options.baseRequestNamespace}${namespaceModule}`;
3476
+ const { properties } = generateRequestOpenApiProperties(schema, schemas, options, false);
3477
+ const propsIndent = " ";
3478
+ const openApiPropsFormatted = properties.map((prop) => formatRequestOpenApiProperty(prop, propsIndent)).join(",\n");
3276
3479
  const content = `<?php
3277
3480
 
3278
3481
  /**
@@ -3283,8 +3486,15 @@ function generateUpdateRequest(schema, options) {
3283
3486
 
3284
3487
  namespace ${namespace};
3285
3488
 
3489
+ use OpenApi\\Attributes as OA;
3286
3490
  use ${baseNamespace}\\${className}UpdateRequestBase;
3287
3491
 
3492
+ #[OA\\Schema(
3493
+ schema: '${schema.name}UpdateRequest',
3494
+ properties: [
3495
+ ${openApiPropsFormatted},
3496
+ ]
3497
+ )]
3288
3498
  class ${className}UpdateRequest extends ${className}UpdateRequestBase
3289
3499
  {
3290
3500
  /**
@@ -3350,8 +3560,8 @@ function generateRequests(schemas, options) {
3350
3560
  if (schema.options?.hidden === true) continue;
3351
3561
  requests.push(generateStoreRequestBase(schema, schemas, resolved));
3352
3562
  requests.push(generateUpdateRequestBase(schema, schemas, resolved));
3353
- requests.push(generateStoreRequest(schema, resolved));
3354
- requests.push(generateUpdateRequest(schema, resolved));
3563
+ requests.push(generateStoreRequest(schema, schemas, resolved));
3564
+ requests.push(generateUpdateRequest(schema, schemas, resolved));
3355
3565
  }
3356
3566
  return requests;
3357
3567
  }
@@ -3373,6 +3583,155 @@ var SKIP_FIELDS2 = /* @__PURE__ */ new Set([
3373
3583
  "password",
3374
3584
  "remember_token"
3375
3585
  ]);
3586
+ function mapTsTypeToOpenApi(tsType, fieldName) {
3587
+ if (fieldName.includes("email")) {
3588
+ return { type: "string", format: "email" };
3589
+ }
3590
+ switch (tsType) {
3591
+ case "string":
3592
+ return { type: "string" };
3593
+ case "number":
3594
+ return { type: "number" };
3595
+ case "boolean":
3596
+ return { type: "boolean" };
3597
+ default:
3598
+ return { type: "string" };
3599
+ }
3600
+ }
3601
+ function getOpenApiType(propDef, fieldName) {
3602
+ if (fieldName.includes("email")) {
3603
+ return { type: "string", format: "email" };
3604
+ }
3605
+ if (fieldName.includes("password")) {
3606
+ return { type: "string", format: "password" };
3607
+ }
3608
+ switch (propDef.type) {
3609
+ case "String":
3610
+ case "Text":
3611
+ case "LongText":
3612
+ return { type: "string" };
3613
+ case "Int":
3614
+ case "BigInt":
3615
+ return { type: "integer" };
3616
+ case "Float":
3617
+ case "Decimal":
3618
+ return { type: "number" };
3619
+ case "Boolean":
3620
+ return { type: "boolean" };
3621
+ case "Date":
3622
+ return { type: "string", format: "date" };
3623
+ case "DateTime":
3624
+ case "Timestamp":
3625
+ return { type: "string", format: "date-time" };
3626
+ case "Email":
3627
+ return { type: "string", format: "email" };
3628
+ case "UUID":
3629
+ return { type: "string", format: "uuid" };
3630
+ case "JSON":
3631
+ return { type: "object" };
3632
+ default:
3633
+ return { type: "string" };
3634
+ }
3635
+ }
3636
+ function generateOpenApiProperties(schema, options) {
3637
+ const properties = [];
3638
+ const schemaProps = schema.properties ?? {};
3639
+ if (schema.options?.id !== false) {
3640
+ properties.push({
3641
+ property: "id",
3642
+ type: "integer",
3643
+ example: 1
3644
+ });
3645
+ }
3646
+ for (const [propName, propDef] of Object.entries(schemaProps)) {
3647
+ const snakeName = toSnakeCase(propName);
3648
+ if (SKIP_FIELDS2.has(snakeName)) continue;
3649
+ if (propDef.hidden === true) continue;
3650
+ if (propDef.type === "Association") continue;
3651
+ const typeDef = options.customTypes.get(propDef.type);
3652
+ if (typeDef?.compound && typeDef.expand) {
3653
+ for (const field of typeDef.expand) {
3654
+ const suffixSnake = toSnakeCase(field.suffix);
3655
+ const fieldName = `${snakeName}_${suffixSnake}`;
3656
+ const tsType = field.typescript?.type ?? "string";
3657
+ const openApiType2 = mapTsTypeToOpenApi(tsType, fieldName);
3658
+ const prop2 = {
3659
+ property: fieldName,
3660
+ ...openApiType2
3661
+ };
3662
+ const sqlDef = field.sql;
3663
+ if (sqlDef?.length) {
3664
+ prop2.maxLength = sqlDef.length;
3665
+ }
3666
+ properties.push(prop2);
3667
+ }
3668
+ if (typeDef.accessors) {
3669
+ for (const accessor of typeDef.accessors) {
3670
+ const accessorName = `${snakeName}_${toSnakeCase(accessor.name)}`;
3671
+ properties.push({
3672
+ property: accessorName,
3673
+ type: "string"
3674
+ });
3675
+ }
3676
+ }
3677
+ continue;
3678
+ }
3679
+ const openApiType = getOpenApiType(propDef, snakeName);
3680
+ const prop = {
3681
+ property: snakeName,
3682
+ ...openApiType
3683
+ };
3684
+ const length = propDef.length;
3685
+ if (length) {
3686
+ prop.maxLength = length;
3687
+ }
3688
+ if (propDef.nullable) {
3689
+ prop.nullable = true;
3690
+ }
3691
+ properties.push(prop);
3692
+ }
3693
+ if (schema.options?.timestamps !== false) {
3694
+ properties.push({
3695
+ property: "created_at",
3696
+ type: "string",
3697
+ format: "date-time"
3698
+ });
3699
+ properties.push({
3700
+ property: "updated_at",
3701
+ type: "string",
3702
+ format: "date-time"
3703
+ });
3704
+ }
3705
+ if (schema.options?.softDelete) {
3706
+ properties.push({
3707
+ property: "deleted_at",
3708
+ type: "string",
3709
+ format: "date-time",
3710
+ nullable: true
3711
+ });
3712
+ }
3713
+ return properties;
3714
+ }
3715
+ function formatOpenApiProperty(prop, indent) {
3716
+ const parts = [`property: '${prop.property}'`, `type: '${prop.type}'`];
3717
+ if (prop.format) {
3718
+ parts.push(`format: '${prop.format}'`);
3719
+ }
3720
+ if (prop.maxLength) {
3721
+ parts.push(`maxLength: ${prop.maxLength}`);
3722
+ }
3723
+ if (prop.nullable) {
3724
+ parts.push(`nullable: true`);
3725
+ }
3726
+ if (prop.example !== void 0) {
3727
+ if (typeof prop.example === "string") {
3728
+ parts.push(`example: '${prop.example}'`);
3729
+ } else {
3730
+ parts.push(`example: ${prop.example}`);
3731
+ }
3732
+ }
3733
+ return `${indent}new OA\\Property(${parts.join(", ")})`;
3734
+ }
3376
3735
  function resolveOptions4(options) {
3377
3736
  return {
3378
3737
  baseResourceNamespace: options?.baseResourceNamespace ?? DEFAULT_OPTIONS3.baseResourceNamespace,
@@ -3383,6 +3742,9 @@ function resolveOptions4(options) {
3383
3742
  locale: options?.locale ?? DEFAULT_OPTIONS3.locale
3384
3743
  };
3385
3744
  }
3745
+ function escapePhpString3(str) {
3746
+ return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
3747
+ }
3386
3748
  function getModuleName2(schema) {
3387
3749
  if (schema.module) {
3388
3750
  return schema.module;
@@ -3530,6 +3892,10 @@ function generateResource(schema, options) {
3530
3892
  const namespaceModule = module2 ? `\\${module2}` : "";
3531
3893
  const namespace = `${options.resourceNamespace}${namespaceModule}`;
3532
3894
  const baseNamespace = `${options.baseResourceNamespace}${namespaceModule}`;
3895
+ const openApiProps = generateOpenApiProperties(schema, options);
3896
+ const propsIndent = " ";
3897
+ const openApiPropsFormatted = openApiProps.map((prop) => formatOpenApiProperty(prop, propsIndent)).join(",\n");
3898
+ const description = schema.displayName ? typeof schema.displayName === "string" ? schema.displayName : schema.displayName["en"] ?? schema.name : `${schema.name} resource`;
3533
3899
  const content = `<?php
3534
3900
 
3535
3901
  /**
@@ -3541,8 +3907,16 @@ function generateResource(schema, options) {
3541
3907
  namespace ${namespace};
3542
3908
 
3543
3909
  use Illuminate\\Http\\Request;
3910
+ use OpenApi\\Attributes as OA;
3544
3911
  use ${baseNamespace}\\${className}ResourceBase;
3545
3912
 
3913
+ #[OA\\Schema(
3914
+ schema: '${schema.name}',
3915
+ description: '${escapePhpString3(description)}',
3916
+ properties: [
3917
+ ${openApiPropsFormatted},
3918
+ ]
3919
+ )]
3546
3920
  class ${className}Resource extends ${className}ResourceBase
3547
3921
  {
3548
3922
  /**