@autobe/agent 0.30.0-dev.20260315 → 0.30.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/LICENSE +661 -661
- package/lib/constants/AutoBeTemplateFileConstant.d.ts +1 -1
- package/lib/factory/consentFunctionCall.js +4 -4
- package/lib/factory/createAutoBeContext.js +24 -24
- package/lib/factory/createAutoBeMessageContent.js +6 -6
- package/lib/index.mjs +41 -41
- package/lib/orchestrate/analyze/histories/transformAnalyzeScenarioHistory.js +18 -18
- package/lib/orchestrate/analyze/histories/transformAnalyzeScenarioReviewHistory.js +13 -13
- package/lib/orchestrate/analyze/histories/transformAnalyzeSectionCrossFileReviewHistory.js +47 -47
- package/lib/orchestrate/analyze/histories/transformAnalyzeSectionReviewHistory.js +66 -66
- package/lib/orchestrate/analyze/histories/transformAnalyzeWriteSectionHistory.js +91 -91
- package/lib/orchestrate/analyze/histories/transformAnalyzeWriteSectionPatchHistory.js +46 -46
- package/lib/orchestrate/analyze/histories/transformAnalyzeWriteUnitHistory.js +72 -72
- package/lib/orchestrate/analyze/orchestrateAnalyzeScenario.js +1 -1
- package/lib/orchestrate/analyze/orchestrateAnalyzeSectionCrossFileReview.js +1 -1
- package/lib/orchestrate/analyze/orchestrateAnalyzeSectionReview.js +1 -1
- package/lib/orchestrate/analyze/orchestrateAnalyzeWriteSection.js +1 -1
- package/lib/orchestrate/analyze/orchestrateAnalyzeWriteSectionPatch.js +1 -1
- package/lib/orchestrate/analyze/orchestrateAnalyzeWriteUnit.js +1 -1
- package/lib/orchestrate/common/histories/transformCommonCorrectCastingHistory.js +5 -5
- package/lib/orchestrate/common/histories/transformPreliminaryHistory.js +44 -44
- package/lib/orchestrate/common/histories/transformPreviousAndLatestCorrectHistory.js +44 -44
- package/lib/orchestrate/common/internal/fixPrelminaryApplication.js +22 -22
- package/lib/orchestrate/common/internal/validatePreliminary.js +116 -116
- package/lib/orchestrate/facade/createAutoBeFacadeController.js +3 -3
- package/lib/orchestrate/facade/structures/transformFacadeStateMessage.js +20 -20
- package/lib/orchestrate/interface/histories/transformInterfaceActionEndpointReviewHistory.js +34 -34
- package/lib/orchestrate/interface/histories/transformInterfaceActionEndpointWriteHistory.js +37 -37
- package/lib/orchestrate/interface/histories/transformInterfaceAuthorizationHistory.js +56 -56
- package/lib/orchestrate/interface/histories/transformInterfaceBaseEndpointReviewHistory.js +26 -26
- package/lib/orchestrate/interface/histories/transformInterfaceBaseEndpointWriteHistory.js +28 -28
- package/lib/orchestrate/interface/histories/transformInterfaceEndpointAuthorizationSection.js +11 -11
- package/lib/orchestrate/interface/histories/transformInterfaceGroupHistory.js +38 -38
- package/lib/orchestrate/interface/histories/transformInterfaceOperationHistory.js +29 -29
- package/lib/orchestrate/interface/histories/transformInterfaceOperationParameterHistory.js +15 -15
- package/lib/orchestrate/interface/histories/transformInterfaceOperationReviewHistory.js +6 -6
- package/lib/orchestrate/interface/histories/transformInterfacePrerequisiteHistory.js +25 -25
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaCastingHistory.js +67 -67
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaComplementHistory.js +60 -60
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaRefineHistory.js +83 -83
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaRenameHistory.js +29 -29
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaReviewHistory.js +74 -74
- package/lib/orchestrate/interface/histories/transformInterfaceSchemaWriteHistory.js +46 -46
- package/lib/orchestrate/interface/orchestrateInterfaceAuthorization.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceEndpointReview.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceEndpointWrite.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceGroup.js +9 -9
- package/lib/orchestrate/interface/orchestrateInterfaceOperation.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceOperationReview.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfacePrerequisite.js +4 -4
- package/lib/orchestrate/interface/orchestrateInterfaceSchemaCasting.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceSchemaComplement.js +5 -5
- package/lib/orchestrate/interface/orchestrateInterfaceSchemaRefine.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceSchemaReview.js +1 -1
- package/lib/orchestrate/interface/orchestrateInterfaceSchemaWrite.js +1 -1
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceAuthorizationProgrammer.js +90 -90
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceEndpointProgrammer.js +6 -6
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceEndpointReviewProgrammer.js +25 -25
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceOperationProgrammer.js +71 -71
- package/lib/orchestrate/interface/programmers/AutoBeInterfacePrerequisiteProgrammer.js +162 -162
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaProgrammer.js +11 -11
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaPropertyReviseProgrammer.js +73 -73
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaRefineProgrammer.js +35 -35
- package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaReviewProgrammer.js +11 -11
- package/lib/orchestrate/interface/utils/AutoBeJsonSchemaFactory.js +4 -4
- package/lib/orchestrate/interface/utils/AutoBeJsonSchemaValidator.js +283 -283
- package/lib/orchestrate/interface/utils/fulfillJsonSchemaErrorMessages.js +76 -76
- package/lib/orchestrate/prisma/histories/transformPrismaAuthorizationHistory.js +76 -76
- package/lib/orchestrate/prisma/histories/transformPrismaAuthorizationReviewHistory.js +51 -51
- package/lib/orchestrate/prisma/histories/transformPrismaComponentReviewHistory.js +54 -54
- package/lib/orchestrate/prisma/histories/transformPrismaComponentsHistory.js +83 -83
- package/lib/orchestrate/prisma/histories/transformPrismaCorrectHistory.js +6 -6
- package/lib/orchestrate/prisma/histories/transformPrismaGroupHistory.js +22 -22
- package/lib/orchestrate/prisma/histories/transformPrismaGroupReviewHistory.js +41 -41
- package/lib/orchestrate/prisma/histories/transformPrismaSchemaHistory.js +39 -39
- package/lib/orchestrate/prisma/histories/transformPrismaSchemaReviewHistory.js +41 -41
- package/lib/orchestrate/prisma/orchestratePrismaAuthorization.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaAuthorizationReview.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaComponent.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaComponentReview.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaCorrect.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaGroup.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaGroupReview.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaSchema.js +1 -1
- package/lib/orchestrate/prisma/orchestratePrismaSchemaReview.js +1 -1
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationProgrammer.js +23 -23
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationReviewProgrammer.js +7 -7
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseComponentProgrammer.js +5 -5
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseComponentReviewProgrammer.js +20 -20
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseGroupProgrammer.js +13 -13
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseGroupReviewProgrammer.js +51 -51
- package/lib/orchestrate/prisma/programmers/AutoBeDatabaseSchemaProgrammer.js +25 -25
- package/lib/orchestrate/realize/histories/transformRealizeAuthorizationCorrectHistory.js +28 -28
- package/lib/orchestrate/realize/histories/transformRealizeAuthorizationWriteHistory.js +14 -14
- package/lib/orchestrate/realize/histories/transformRealizeCollectorCorrectHistory.js +42 -42
- package/lib/orchestrate/realize/histories/transformRealizeCollectorPlanHistory.js +27 -27
- package/lib/orchestrate/realize/histories/transformRealizeCollectorWriteHistory.js +57 -57
- package/lib/orchestrate/realize/histories/transformRealizeCorrectCastingHistory.js +35 -35
- package/lib/orchestrate/realize/histories/transformRealizeOperationCorrectHistory.js +14 -14
- package/lib/orchestrate/realize/histories/transformRealizeOperationWriteHistory.js +37 -37
- package/lib/orchestrate/realize/histories/transformRealizeOperationWriteHistory.js.map +1 -1
- package/lib/orchestrate/realize/histories/transformRealizeTransformerCorrectHistory.js +47 -47
- package/lib/orchestrate/realize/histories/transformRealizeTransformerPlanHistory.js +27 -27
- package/lib/orchestrate/realize/histories/transformRealizeTransformerWriteHistory.js +51 -51
- package/lib/orchestrate/realize/orchestrateRealizeAuthorizationCorrect.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeAuthorizationWrite.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeCollectorCorrectOverall.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeCollectorPlan.js +11 -11
- package/lib/orchestrate/realize/orchestrateRealizeCollectorWrite.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeOperationCorrectOverall.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeOperationWrite.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeTransformerCorrectOverall.js +1 -1
- package/lib/orchestrate/realize/orchestrateRealizeTransformerPlan.js +11 -11
- package/lib/orchestrate/realize/orchestrateRealizeTransformerWrite.js +1 -1
- package/lib/orchestrate/realize/programmers/AutoBeRealizeCollectorProgrammer.js +41 -41
- package/lib/orchestrate/realize/programmers/AutoBeRealizeOperationProgrammer.js +21 -21
- package/lib/orchestrate/realize/programmers/AutoBeRealizeTransformerProgrammer.js +57 -57
- package/lib/orchestrate/realize/utils/getRealizeWriteInputType.js +3 -3
- package/lib/orchestrate/realize/utils/printErrorHints.js +5 -5
- package/lib/orchestrate/test/compile/getTestImportStatements.js +9 -9
- package/lib/orchestrate/test/histories/transformTestAuthorizeWriteHistory.js +38 -38
- package/lib/orchestrate/test/histories/transformTestGenerationWriteHistory.js +79 -79
- package/lib/orchestrate/test/histories/transformTestOperationWriteHistory.js +162 -162
- package/lib/orchestrate/test/histories/transformTestPrepareWriteHistory.js +54 -54
- package/lib/orchestrate/test/histories/transformTestScenarioHistory.js +31 -31
- package/lib/orchestrate/test/histories/transformTestScenarioReviewHistory.js +27 -27
- package/lib/orchestrate/test/orchestrateTestScenario.js +1 -1
- package/lib/orchestrate/test/orchestrateTestScenarioReview.js +1 -1
- package/lib/orchestrate/test/programmers/AutoBeTestAuthorizeProgrammer.js +31 -31
- package/lib/orchestrate/test/programmers/AutoBeTestGenerateProgrammer.js +23 -23
- package/lib/orchestrate/test/programmers/AutoBeTestOperationProgrammer.js +6 -6
- package/lib/orchestrate/test/programmers/AutoBeTestPrepareProgrammer.js +34 -34
- package/lib/orchestrate/test/programmers/AutoBeTestScenarioProgrammer.js +39 -39
- package/lib/utils/predicateStateMessage.js +19 -19
- package/lib/utils/validateEmptyCode.js +16 -16
- package/package.json +5 -5
- package/src/AutoBeAgent.ts +374 -374
- package/src/AutoBeAgentBase.ts +126 -126
- package/src/AutoBeMockAgent.ts +254 -254
- package/src/constants/AutoBeConfigConstant.ts +173 -173
- package/src/constants/AutoBeTemplateFileConstant.ts +1 -1
- package/src/context/AutoBeContext.ts +107 -107
- package/src/context/AutoBeState.ts +66 -66
- package/src/context/AutoBeTokenUsage.ts +307 -307
- package/src/context/AutoBeTokenUsageComponent.ts +227 -227
- package/src/describe/describe.ts +47 -47
- package/src/describe/image/histories/transformImageDescribeDraftHistories.ts +16 -16
- package/src/describe/image/orchestrateImageDescribeDraft.ts +200 -200
- package/src/describe/image/structures/IAutoBeImageDescribeDraftApplication.ts +92 -92
- package/src/describe/imageDescribe.ts +64 -64
- package/src/factory/consentFunctionCall.ts +141 -141
- package/src/factory/createAgenticaHistory.ts +71 -71
- package/src/factory/createAutoBeContext.ts +584 -584
- package/src/factory/createAutoBeMessageContent.ts +36 -36
- package/src/factory/createAutoBeState.ts +20 -20
- package/src/factory/getAutoBeGenerated.ts +317 -317
- package/src/factory/getAutoBeRealizeGenerated.ts +31 -31
- package/src/factory/getCriticalCompiler.ts +52 -52
- package/src/factory/index.ts +1 -1
- package/src/factory/mergeSystemMessages.ts +60 -60
- package/src/factory/supportFunctionCallFallback.ts +214 -214
- package/src/factory/supportMistral.ts +138 -138
- package/src/index.ts +16 -16
- package/src/orchestrate/analyze/fillTocDeterministic.ts +280 -280
- package/src/orchestrate/analyze/histories/transformAnalyzeScenarioHistory.ts +55 -55
- package/src/orchestrate/analyze/histories/transformAnalyzeScenarioReviewHistory.ts +74 -74
- package/src/orchestrate/analyze/histories/transformAnalyzeSectionCrossFileReviewHistory.ts +179 -179
- package/src/orchestrate/analyze/histories/transformAnalyzeSectionReviewHistory.ts +163 -163
- package/src/orchestrate/analyze/histories/transformAnalyzeWriteSectionHistory.ts +191 -191
- package/src/orchestrate/analyze/histories/transformAnalyzeWriteSectionPatchHistory.ts +122 -122
- package/src/orchestrate/analyze/histories/transformAnalyzeWriteUnitHistory.ts +161 -161
- package/src/orchestrate/analyze/index.ts +7 -7
- package/src/orchestrate/analyze/orchestrateAnalyze.ts +1693 -1693
- package/src/orchestrate/analyze/orchestrateAnalyzeScenario.ts +267 -267
- package/src/orchestrate/analyze/orchestrateAnalyzeScenarioReview.ts +122 -122
- package/src/orchestrate/analyze/orchestrateAnalyzeSectionCrossFileReview.ts +146 -146
- package/src/orchestrate/analyze/orchestrateAnalyzeSectionReview.ts +154 -154
- package/src/orchestrate/analyze/orchestrateAnalyzeWriteSection.ts +388 -388
- package/src/orchestrate/analyze/orchestrateAnalyzeWriteSectionPatch.ts +407 -407
- package/src/orchestrate/analyze/orchestrateAnalyzeWriteUnit.ts +348 -348
- package/src/orchestrate/analyze/programmers/AutoBeAnalyzeProgrammer.ts +149 -149
- package/src/orchestrate/analyze/structures/FixedAnalyzeTemplate.ts +961 -961
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeScenarioApplication.ts +141 -141
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeScenarioReviewApplication.ts +96 -96
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeSectionCrossFileReviewApplication.ts +160 -160
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeSectionReviewApplication.ts +192 -192
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeWriteSectionApplication.ts +142 -142
- package/src/orchestrate/analyze/structures/IAutoBeAnalyzeWriteUnitApplication.ts +123 -123
- package/src/orchestrate/analyze/utils/buildConstraintConsistencyReport.ts +813 -813
- package/src/orchestrate/analyze/utils/buildErrorCodeRegistry.ts +324 -324
- package/src/orchestrate/analyze/utils/buildHardValidators.ts +88 -88
- package/src/orchestrate/analyze/utils/detectInventedEntities.ts +87 -87
- package/src/orchestrate/analyze/utils/detectProseConstraintConflicts.ts +319 -319
- package/src/orchestrate/analyze/utils/repairSectionReviewUtils.ts +181 -181
- package/src/orchestrate/analyze/utils/repairUtils.ts +60 -60
- package/src/orchestrate/analyze/utils/validateScenarioBasics.ts +104 -104
- package/src/orchestrate/common/AutoBePreliminaryController.ts +400 -400
- package/src/orchestrate/common/histories/transformCommonCorrectCastingHistory.ts +32 -32
- package/src/orchestrate/common/histories/transformPreliminaryHistory.ts +742 -742
- package/src/orchestrate/common/histories/transformPreviousAndLatestCorrectHistory.ts +77 -77
- package/src/orchestrate/common/internal/complementPreliminaryCollection.ts +263 -263
- package/src/orchestrate/common/internal/convertToSectionEntries.ts +57 -57
- package/src/orchestrate/common/internal/createPreliminaryCollection.ts +76 -76
- package/src/orchestrate/common/internal/fixPrelminaryApplication.ts +346 -346
- package/src/orchestrate/common/internal/validatePreliminary.ts +671 -671
- package/src/orchestrate/common/orchestrateCommonCorrectCasting.ts +223 -223
- package/src/orchestrate/common/orchestratePreliminary.ts +610 -610
- package/src/orchestrate/common/structures/AutoBePreliminaryRequest.ts +42 -42
- package/src/orchestrate/common/structures/IAnalysisSectionEntry.ts +35 -35
- package/src/orchestrate/common/structures/IAutoBeCommonCorrectCastingApplication.ts +72 -72
- package/src/orchestrate/common/structures/IAutoBeOrchestrateResult.ts +28 -28
- package/src/orchestrate/common/structures/IAutoBePreliminaryCollection.ts +52 -52
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetAnalysisSections.ts +34 -34
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetDatabaseSchemas.ts +31 -31
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetInterfaceOperations.ts +32 -32
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetInterfaceSchemas.ts +31 -31
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousAnalysisSections.ts +28 -28
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousDatabaseSchemas.ts +88 -88
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousInterfaceOperations.ts +102 -102
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousInterfaceSchemas.ts +105 -105
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetRealizeCollectors.ts +32 -32
- package/src/orchestrate/common/structures/IAutoBePreliminaryGetRealizeTransformers.ts +32 -32
- package/src/orchestrate/facade/createAutoBeFacadeController.ts +118 -118
- package/src/orchestrate/facade/histories/IAutoBeFacadeApplication.ts +139 -139
- package/src/orchestrate/facade/histories/IAutoBeFacadeApplicationProps.ts +8 -8
- package/src/orchestrate/facade/histories/IAutoBeFacadeApplicationResult.ts +9 -9
- package/src/orchestrate/facade/structures/transformFacadeStateMessage.ts +59 -59
- package/src/orchestrate/index.ts +5 -5
- package/src/orchestrate/interface/histories/transformInterfaceActionEndpointReviewHistory.ts +83 -83
- package/src/orchestrate/interface/histories/transformInterfaceActionEndpointWriteHistory.ts +78 -78
- package/src/orchestrate/interface/histories/transformInterfaceAuthorizationHistory.ts +129 -129
- package/src/orchestrate/interface/histories/transformInterfaceBaseEndpointReviewHistory.ts +74 -74
- package/src/orchestrate/interface/histories/transformInterfaceBaseEndpointWriteHistory.ts +68 -68
- package/src/orchestrate/interface/histories/transformInterfaceCommonHistory.ts +64 -64
- package/src/orchestrate/interface/histories/transformInterfaceEndpointAuthorizationSection.ts +42 -42
- package/src/orchestrate/interface/histories/transformInterfaceGroupHistory.ts +103 -103
- package/src/orchestrate/interface/histories/transformInterfaceOperationHistory.ts +81 -81
- package/src/orchestrate/interface/histories/transformInterfaceOperationParameterHistory.ts +53 -53
- package/src/orchestrate/interface/histories/transformInterfaceOperationReviewHistory.ts +49 -49
- package/src/orchestrate/interface/histories/transformInterfacePrerequisiteHistory.ts +94 -94
- package/src/orchestrate/interface/histories/transformInterfaceSchemaCastingHistory.ts +122 -122
- package/src/orchestrate/interface/histories/transformInterfaceSchemaComplementHistory.ts +192 -192
- package/src/orchestrate/interface/histories/transformInterfaceSchemaRefineHistory.ts +189 -189
- package/src/orchestrate/interface/histories/transformInterfaceSchemaRenameHistory.ts +63 -63
- package/src/orchestrate/interface/histories/transformInterfaceSchemaReviewHistory.ts +173 -173
- package/src/orchestrate/interface/histories/transformInterfaceSchemaWriteHistory.ts +88 -88
- package/src/orchestrate/interface/index.ts +6 -6
- package/src/orchestrate/interface/orchestrateInterface.ts +118 -118
- package/src/orchestrate/interface/orchestrateInterfaceActionEndpoint.ts +58 -58
- package/src/orchestrate/interface/orchestrateInterfaceAuthorization.ts +208 -208
- package/src/orchestrate/interface/orchestrateInterfaceBaseEndpoint.ts +55 -55
- package/src/orchestrate/interface/orchestrateInterfaceEndpoint.ts +67 -67
- package/src/orchestrate/interface/orchestrateInterfaceEndpointOverall.ts +80 -80
- package/src/orchestrate/interface/orchestrateInterfaceEndpointReview.ts +212 -212
- package/src/orchestrate/interface/orchestrateInterfaceEndpointWrite.ts +236 -236
- package/src/orchestrate/interface/orchestrateInterfaceGroup.ts +166 -166
- package/src/orchestrate/interface/orchestrateInterfaceOperation.ts +322 -322
- package/src/orchestrate/interface/orchestrateInterfaceOperationReview.ts +245 -245
- package/src/orchestrate/interface/orchestrateInterfacePrerequisite.ts +240 -240
- package/src/orchestrate/interface/orchestrateInterfaceSchema.ts +191 -191
- package/src/orchestrate/interface/orchestrateInterfaceSchemaCasting.ts +274 -274
- package/src/orchestrate/interface/orchestrateInterfaceSchemaComplement.ts +329 -329
- package/src/orchestrate/interface/orchestrateInterfaceSchemaRefine.ts +292 -292
- package/src/orchestrate/interface/orchestrateInterfaceSchemaRename.ts +266 -266
- package/src/orchestrate/interface/orchestrateInterfaceSchemaReview.ts +310 -310
- package/src/orchestrate/interface/orchestrateInterfaceSchemaWrite.ts +283 -283
- package/src/orchestrate/interface/programmers/AutoBeInterfaceAuthorizationProgrammer.ts +260 -260
- package/src/orchestrate/interface/programmers/AutoBeInterfaceEndpointProgrammer.ts +155 -155
- package/src/orchestrate/interface/programmers/AutoBeInterfaceEndpointReviewProgrammer.ts +139 -139
- package/src/orchestrate/interface/programmers/AutoBeInterfaceOperationProgrammer.ts +277 -277
- package/src/orchestrate/interface/programmers/AutoBeInterfacePrerequisiteProgrammer.ts +259 -259
- package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaProgrammer.ts +152 -152
- package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaPropertyReviseProgrammer.ts +347 -347
- package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaRefineProgrammer.ts +183 -183
- package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaReviewProgrammer.ts +263 -263
- package/src/orchestrate/interface/structures/IAutoBeInterfaceAuthorizationApplication.ts +135 -135
- package/src/orchestrate/interface/structures/IAutoBeInterfaceEndpointReviewApplication.ts +128 -128
- package/src/orchestrate/interface/structures/IAutoBeInterfaceEndpointWriteApplication.ts +133 -133
- package/src/orchestrate/interface/structures/IAutoBeInterfaceGroupApplication.ts +122 -122
- package/src/orchestrate/interface/structures/IAutoBeInterfaceOperationApplication.ts +178 -178
- package/src/orchestrate/interface/structures/IAutoBeInterfaceOperationReviewApplication.ts +175 -175
- package/src/orchestrate/interface/structures/IAutoBeInterfacePrerequisiteApplication.ts +130 -130
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaApplication.ts +121 -121
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaCastingApplication.ts +153 -153
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaComplementApplication.ts +123 -123
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaRefineApplication.ts +181 -181
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaRenameApplication.ts +45 -45
- package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaReviewApplication.ts +108 -108
- package/src/orchestrate/interface/utils/AutoBeJsonSchemaCollection.ts +42 -42
- package/src/orchestrate/interface/utils/AutoBeJsonSchemaFactory.ts +714 -714
- package/src/orchestrate/interface/utils/AutoBeJsonSchemaNamingConvention.ts +86 -86
- package/src/orchestrate/interface/utils/AutoBeJsonSchemaValidator.ts +725 -725
- package/src/orchestrate/interface/utils/fulfillJsonSchemaErrorMessages.ts +219 -219
- package/src/orchestrate/prisma/histories/transformPrismaAuthorizationHistory.ts +144 -144
- package/src/orchestrate/prisma/histories/transformPrismaAuthorizationReviewHistory.ts +104 -104
- package/src/orchestrate/prisma/histories/transformPrismaComponentReviewHistory.ts +97 -97
- package/src/orchestrate/prisma/histories/transformPrismaComponentsHistory.ts +136 -136
- package/src/orchestrate/prisma/histories/transformPrismaCorrectHistory.ts +41 -41
- package/src/orchestrate/prisma/histories/transformPrismaGroupHistory.ts +63 -63
- package/src/orchestrate/prisma/histories/transformPrismaGroupReviewHistory.ts +79 -79
- package/src/orchestrate/prisma/histories/transformPrismaSchemaHistory.ts +97 -97
- package/src/orchestrate/prisma/histories/transformPrismaSchemaReviewHistory.ts +126 -126
- package/src/orchestrate/prisma/index.ts +7 -7
- package/src/orchestrate/prisma/orchestratePrisma.ts +296 -296
- package/src/orchestrate/prisma/orchestratePrismaAuthorization.ts +173 -173
- package/src/orchestrate/prisma/orchestratePrismaAuthorizationReview.ts +183 -183
- package/src/orchestrate/prisma/orchestratePrismaComponent.ts +205 -205
- package/src/orchestrate/prisma/orchestratePrismaComponentReview.ts +205 -205
- package/src/orchestrate/prisma/orchestratePrismaCorrect.ts +265 -265
- package/src/orchestrate/prisma/orchestratePrismaGroup.ts +121 -121
- package/src/orchestrate/prisma/orchestratePrismaGroupReview.ts +141 -141
- package/src/orchestrate/prisma/orchestratePrismaSchema.ts +218 -218
- package/src/orchestrate/prisma/orchestratePrismaSchemaReview.ts +220 -220
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationProgrammer.ts +86 -86
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationReviewProgrammer.ts +103 -103
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseComponentProgrammer.ts +80 -80
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseComponentReviewProgrammer.ts +168 -168
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseGroupProgrammer.ts +63 -63
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseGroupReviewProgrammer.ts +210 -210
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseModelProgrammer.ts +57 -57
- package/src/orchestrate/prisma/programmers/AutoBeDatabaseSchemaProgrammer.ts +92 -92
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseAuthorizationApplication.ts +147 -147
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseAuthorizationReviewApplication.ts +153 -153
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseComponentApplication.ts +142 -142
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseComponentReviewApplication.ts +151 -151
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseCorrectApplication.ts +157 -157
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseGroupApplication.ts +117 -117
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseGroupReviewApplication.ts +154 -154
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseSchemaApplication.ts +144 -144
- package/src/orchestrate/prisma/structures/IAutoBeDatabaseSchemaReviewApplication.ts +138 -138
- package/src/orchestrate/realize/histories/transformRealizeAuthorizationCorrectHistory.ts +89 -89
- package/src/orchestrate/realize/histories/transformRealizeAuthorizationWriteHistory.ts +45 -45
- package/src/orchestrate/realize/histories/transformRealizeCollectorCorrectHistory.ts +137 -137
- package/src/orchestrate/realize/histories/transformRealizeCollectorPlanHistory.ts +64 -64
- package/src/orchestrate/realize/histories/transformRealizeCollectorWriteHistory.ts +147 -147
- package/src/orchestrate/realize/histories/transformRealizeCorrectCastingHistory.ts +69 -69
- package/src/orchestrate/realize/histories/transformRealizeOperationCorrectHistory.ts +94 -94
- package/src/orchestrate/realize/histories/transformRealizeOperationWriteHistory.ts +127 -127
- package/src/orchestrate/realize/histories/transformRealizeTransformerCorrectHistory.ts +147 -147
- package/src/orchestrate/realize/histories/transformRealizeTransformerPlanHistory.ts +61 -61
- package/src/orchestrate/realize/histories/transformRealizeTransformerWriteHistory.ts +133 -133
- package/src/orchestrate/realize/histories/transformRealizeWriteMembershipHistory.ts +30 -30
- package/src/orchestrate/realize/index.ts +3 -3
- package/src/orchestrate/realize/internal/orchestrateRealizeCorrectCasting.ts +444 -444
- package/src/orchestrate/realize/internal/orchestrateRealizeCorrectOverall.ts +449 -449
- package/src/orchestrate/realize/orchestrateRealize.ts +143 -143
- package/src/orchestrate/realize/orchestrateRealizeAuthorizationCorrect.ts +207 -207
- package/src/orchestrate/realize/orchestrateRealizeAuthorizationWrite.ts +209 -209
- package/src/orchestrate/realize/orchestrateRealizeCollector.ts +41 -41
- package/src/orchestrate/realize/orchestrateRealizeCollectorCorrectCasting.ts +43 -43
- package/src/orchestrate/realize/orchestrateRealizeCollectorCorrectOverall.ts +157 -157
- package/src/orchestrate/realize/orchestrateRealizeCollectorPlan.ts +264 -264
- package/src/orchestrate/realize/orchestrateRealizeCollectorWrite.ts +225 -225
- package/src/orchestrate/realize/orchestrateRealizeOperation.ts +48 -48
- package/src/orchestrate/realize/orchestrateRealizeOperationCorrectCasting.ts +69 -69
- package/src/orchestrate/realize/orchestrateRealizeOperationCorrectOverall.ts +173 -173
- package/src/orchestrate/realize/orchestrateRealizeOperationWrite.ts +262 -262
- package/src/orchestrate/realize/orchestrateRealizeTransformer.ts +41 -41
- package/src/orchestrate/realize/orchestrateRealizeTransformerCorrectCasting.ts +38 -38
- package/src/orchestrate/realize/orchestrateRealizeTransformerCorrectOverall.ts +160 -160
- package/src/orchestrate/realize/orchestrateRealizeTransformerPlan.ts +254 -254
- package/src/orchestrate/realize/orchestrateRealizeTransformerWrite.ts +226 -226
- package/src/orchestrate/realize/programmers/AutoBeRealizeCollectorProgrammer.ts +424 -424
- package/src/orchestrate/realize/programmers/AutoBeRealizeOperationProgrammer.ts +409 -409
- package/src/orchestrate/realize/programmers/AutoBeRealizeTransformerProgrammer.ts +451 -451
- package/src/orchestrate/realize/programmers/compileRealizeFiles.ts +65 -65
- package/src/orchestrate/realize/structures/IAutoBeRealizeAuthorizationCorrectApplication.ts +119 -119
- package/src/orchestrate/realize/structures/IAutoBeRealizeAuthorizationWriteApplication.ts +167 -167
- package/src/orchestrate/realize/structures/IAutoBeRealizeCollectorCorrectApplication.ts +191 -191
- package/src/orchestrate/realize/structures/IAutoBeRealizeCollectorPlanApplication.ts +177 -177
- package/src/orchestrate/realize/structures/IAutoBeRealizeCollectorWriteApplication.ts +207 -207
- package/src/orchestrate/realize/structures/IAutoBeRealizeFunctionFailure.ts +11 -11
- package/src/orchestrate/realize/structures/IAutoBeRealizeOperationCorrectApplication.ts +134 -134
- package/src/orchestrate/realize/structures/IAutoBeRealizeOperationWriteApplication.ts +138 -138
- package/src/orchestrate/realize/structures/IAutoBeRealizeScenarioResult.ts +32 -32
- package/src/orchestrate/realize/structures/IAutoBeRealizeTransformerCorrectApplication.ts +248 -248
- package/src/orchestrate/realize/structures/IAutoBeRealizeTransformerPlanApplication.ts +150 -150
- package/src/orchestrate/realize/structures/IAutoBeRealizeTransformerWriteApplication.ts +278 -278
- package/src/orchestrate/realize/utils/AuthorizationFileSystem.ts +10 -10
- package/src/orchestrate/realize/utils/AutoBeRealizeAuthorizationFileSystem.ts +9 -9
- package/src/orchestrate/realize/utils/AutoBeRealizeAuthorizationReplaceImport.ts +62 -62
- package/src/orchestrate/realize/utils/InternalFileSystem.ts +12 -12
- package/src/orchestrate/realize/utils/ProviderFileSystem.ts +4 -4
- package/src/orchestrate/realize/utils/filterDiagnostics.ts +25 -25
- package/src/orchestrate/realize/utils/generateTS2339Hints.ts +54 -54
- package/src/orchestrate/realize/utils/getRealizeWriteImportStatements.ts +42 -42
- package/src/orchestrate/realize/utils/getRealizeWriteInputType.ts +88 -88
- package/src/orchestrate/realize/utils/printErrorHints.ts +54 -54
- package/src/orchestrate/test/compile/getTestArtifacts.ts +124 -124
- package/src/orchestrate/test/compile/getTestExternalDeclarations.ts +39 -39
- package/src/orchestrate/test/compile/getTestImportStatements.ts +27 -27
- package/src/orchestrate/test/experimental/orchestrateTestCorrect.ast +237 -237
- package/src/orchestrate/test/experimental/orchestrateTestWrite.ast +322 -322
- package/src/orchestrate/test/experimental/transformTestCorrectHistories.ast +52 -52
- package/src/orchestrate/test/histories/transformTestAuthorizeWriteHistory.ts +80 -80
- package/src/orchestrate/test/histories/transformTestCorrectOverallHistory.ts +116 -116
- package/src/orchestrate/test/histories/transformTestGenerationWriteHistory.ts +118 -118
- package/src/orchestrate/test/histories/transformTestOperationWriteHistory.ts +287 -287
- package/src/orchestrate/test/histories/transformTestPrepareWriteHistory.ts +95 -95
- package/src/orchestrate/test/histories/transformTestScenarioHistory.ts +120 -120
- package/src/orchestrate/test/histories/transformTestScenarioReviewHistory.ts +99 -99
- package/src/orchestrate/test/histories/transformTestValidateEvent.ts +11 -11
- package/src/orchestrate/test/index.ts +5 -5
- package/src/orchestrate/test/internal/orchestrateTestCorrectCasting.ts +96 -96
- package/src/orchestrate/test/internal/orchestrateTestCorrectOverall.ts +219 -219
- package/src/orchestrate/test/internal/orchestrateTestCorrectRequest.ts +206 -206
- package/src/orchestrate/test/orchestrateTest.ts +161 -161
- package/src/orchestrate/test/orchestrateTestAuthorize.ts +125 -125
- package/src/orchestrate/test/orchestrateTestAuthorizeWrite.ts +263 -263
- package/src/orchestrate/test/orchestrateTestGenerate.ts +121 -121
- package/src/orchestrate/test/orchestrateTestGenerateWrite.ts +211 -211
- package/src/orchestrate/test/orchestrateTestOperation.ts +133 -133
- package/src/orchestrate/test/orchestrateTestOperationWrite.ts +221 -221
- package/src/orchestrate/test/orchestrateTestPrepare.ts +117 -117
- package/src/orchestrate/test/orchestrateTestPrepareWrite.ts +264 -264
- package/src/orchestrate/test/orchestrateTestScenario.ts +290 -290
- package/src/orchestrate/test/orchestrateTestScenarioReview.ts +262 -262
- package/src/orchestrate/test/programmers/AutoBeTestAuthorizeProgrammer.ts +149 -149
- package/src/orchestrate/test/programmers/AutoBeTestFunctionProgrammer.ts +90 -90
- package/src/orchestrate/test/programmers/AutoBeTestGenerateProgrammer.ts +170 -170
- package/src/orchestrate/test/programmers/AutoBeTestOperationProgrammer.ts +168 -168
- package/src/orchestrate/test/programmers/AutoBeTestPrepareProgrammer.ts +261 -261
- package/src/orchestrate/test/programmers/AutoBeTestScenarioProgrammer.ts +316 -316
- package/src/orchestrate/test/structures/IAutoBeTestArtifacts.ts +8 -8
- package/src/orchestrate/test/structures/IAutoBeTestAuthorizationWriteApplication.ts +113 -113
- package/src/orchestrate/test/structures/IAutoBeTestAuthorizeWriteResult.ts +10 -10
- package/src/orchestrate/test/structures/IAutoBeTestCorrectOverallApplication.ts +134 -134
- package/src/orchestrate/test/structures/IAutoBeTestCorrectRequestApplication.ts +152 -152
- package/src/orchestrate/test/structures/IAutoBeTestFunction.ts +10 -10
- package/src/orchestrate/test/structures/IAutoBeTestFunctionFailure.ts +10 -10
- package/src/orchestrate/test/structures/IAutoBeTestGenerateProcedure.ts +15 -15
- package/src/orchestrate/test/structures/IAutoBeTestGenerationWriteApplication.ts +145 -145
- package/src/orchestrate/test/structures/IAutoBeTestOperationProcedure.ts +17 -17
- package/src/orchestrate/test/structures/IAutoBeTestOperationWriteApplication.ts +162 -162
- package/src/orchestrate/test/structures/IAutoBeTestPrepareCorrectOverallApplication.ts +194 -194
- package/src/orchestrate/test/structures/IAutoBeTestPrepareProcedure.ts +8 -8
- package/src/orchestrate/test/structures/IAutoBeTestPrepareWriteApplication.ts +147 -147
- package/src/orchestrate/test/structures/IAutoBeTestProcedure.ts +10 -10
- package/src/orchestrate/test/structures/IAutoBeTestScenarioApplication.ts +105 -105
- package/src/orchestrate/test/structures/IAutoBeTestScenarioArtifacts.ts +8 -8
- package/src/orchestrate/test/structures/IAutoBeTestScenarioAuthorizationActor.ts +7 -7
- package/src/orchestrate/test/structures/IAutoBeTestScenarioReviewApplication.ts +133 -133
- package/src/orchestrate/test/utils/getPrerequisites.ts +51 -51
- package/src/orchestrate/test/utils/getReferenceIds.ts +26 -26
- package/src/orchestrate/test/utils/insertScriptToTestResult.ts +14 -14
- package/src/structures/IAutoBeConfig.ts +110 -110
- package/src/structures/IAutoBeOrchestrateHistory.ts +44 -44
- package/src/structures/IAutoBeProps.ts +102 -102
- package/src/structures/IAutoBeVendor.ts +113 -113
- package/src/utils/AutoBePreliminaryExhaustedError.ts +30 -30
- package/src/utils/AutoBeTimeoutError.ts +35 -35
- package/src/utils/EmbeddingProvider.ts +4 -4
- package/src/utils/LocalEmbeddingProvider.ts +145 -145
- package/src/utils/RAGRetrieval.ts +411 -411
- package/src/utils/TimedConversation.ts +125 -125
- package/src/utils/backoffRetry.ts +169 -169
- package/src/utils/divideArray.ts +35 -35
- package/src/utils/emplaceMap.ts +31 -31
- package/src/utils/executeCachedBatch.ts +67 -67
- package/src/utils/forceRetry.ts +15 -15
- package/src/utils/getEmbedder.ts +16 -16
- package/src/utils/parseTextFunctionCall.ts +437 -437
- package/src/utils/predicateStateMessage.ts +131 -131
- package/src/utils/validateEmptyCode.ts +73 -73
- package/src/utils/validateEnglishOnly.ts +224 -224
- package/README.md +0 -261
|
@@ -1,813 +1,813 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AutoBeAnalyze,
|
|
3
|
-
AutoBeAnalyzeWriteSectionEvent,
|
|
4
|
-
} from "@autobe/interface";
|
|
5
|
-
import YAML from "yaml";
|
|
6
|
-
|
|
7
|
-
type ConstraintSource = {
|
|
8
|
-
file: AutoBeAnalyze.IFileScenario;
|
|
9
|
-
sectionTitle: string;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
type ConstraintValue = {
|
|
13
|
-
normalized: string;
|
|
14
|
-
display: string;
|
|
15
|
-
sources: ConstraintSource[];
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
type ConstraintEntry = {
|
|
19
|
-
key: string;
|
|
20
|
-
values: Map<string, ConstraintValue>;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const YAML_CODE_BLOCK_REGEX = /```yaml\n([\s\S]*?)```/g;
|
|
24
|
-
|
|
25
|
-
export const buildConstraintConsistencyReport = (props: {
|
|
26
|
-
files: Array<{
|
|
27
|
-
file: AutoBeAnalyze.IFileScenario;
|
|
28
|
-
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
29
|
-
}>;
|
|
30
|
-
}): string => {
|
|
31
|
-
const constraints: Map<string, ConstraintEntry> = new Map();
|
|
32
|
-
let totalConstraints: number = 0;
|
|
33
|
-
|
|
34
|
-
for (const { file, sectionEvents } of props.files) {
|
|
35
|
-
for (const sectionsForModule of sectionEvents) {
|
|
36
|
-
for (const sectionEvent of sectionsForModule) {
|
|
37
|
-
for (const section of sectionEvent.sectionSections) {
|
|
38
|
-
const pairs = extractConstraints(section.content);
|
|
39
|
-
for (const { key, value } of pairs) {
|
|
40
|
-
totalConstraints++;
|
|
41
|
-
const normalized = normalizeValue(value);
|
|
42
|
-
if (!constraints.has(key)) {
|
|
43
|
-
constraints.set(key, {
|
|
44
|
-
key,
|
|
45
|
-
values: new Map(),
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
const entry = constraints.get(key)!;
|
|
49
|
-
if (!entry.values.has(normalized)) {
|
|
50
|
-
entry.values.set(normalized, {
|
|
51
|
-
normalized,
|
|
52
|
-
display: value.trim(),
|
|
53
|
-
sources: [],
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
entry.values.get(normalized)!.sources.push({
|
|
57
|
-
file,
|
|
58
|
-
sectionTitle: section.title,
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const conflicts: ConstraintEntry[] = [...constraints.values()].filter(
|
|
67
|
-
(entry) => entry.values.size > 1,
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
if (conflicts.length === 0) {
|
|
71
|
-
return [
|
|
72
|
-
"No numeric constraint conflicts detected.",
|
|
73
|
-
`Scanned ${totalConstraints} numeric constraints from YAML spec blocks.`,
|
|
74
|
-
].join("\n");
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const lines: string[] = [
|
|
78
|
-
`Detected ${conflicts.length} numeric constraint conflict(s).`,
|
|
79
|
-
`Scanned ${totalConstraints} numeric constraints from YAML spec blocks.`,
|
|
80
|
-
"",
|
|
81
|
-
"Conflicts:",
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
for (const entry of conflicts) {
|
|
85
|
-
lines.push(`- ${entry.key}:`);
|
|
86
|
-
for (const value of entry.values.values()) {
|
|
87
|
-
const sources = value.sources
|
|
88
|
-
.map((s) => `${s.file.filename} → ${s.sectionTitle}`)
|
|
89
|
-
.slice(0, 6)
|
|
90
|
-
.join("; ");
|
|
91
|
-
lines.push(` - ${value.display} (e.g., ${sources})`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return lines.join("\n");
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Extract numeric constraints from YAML spec blocks.
|
|
100
|
-
*
|
|
101
|
-
* Parses YAML code blocks and extracts Entity.attribute constraints that
|
|
102
|
-
* contain numeric values (e.g., length limits, quantity limits).
|
|
103
|
-
*/
|
|
104
|
-
const extractConstraints = (
|
|
105
|
-
content: string,
|
|
106
|
-
): Array<{ key: string; value: string }> => {
|
|
107
|
-
if (!content) return [];
|
|
108
|
-
const results: Array<{ key: string; value: string }> = [];
|
|
109
|
-
const yamlMatches = content.matchAll(YAML_CODE_BLOCK_REGEX);
|
|
110
|
-
|
|
111
|
-
for (const match of yamlMatches) {
|
|
112
|
-
const yamlContent = match[1] ?? "";
|
|
113
|
-
try {
|
|
114
|
-
const parsed = YAML.parse(yamlContent);
|
|
115
|
-
if (!parsed || typeof parsed !== "object") continue;
|
|
116
|
-
|
|
117
|
-
// Handle entity attribute YAML blocks
|
|
118
|
-
if (
|
|
119
|
-
typeof parsed.entity === "string" &&
|
|
120
|
-
Array.isArray(parsed.attributes)
|
|
121
|
-
) {
|
|
122
|
-
for (const attr of parsed.attributes) {
|
|
123
|
-
if (!attr || typeof attr.name !== "string") continue;
|
|
124
|
-
const constraintStr = String(attr.constraints ?? "");
|
|
125
|
-
if (!hasNumeric(constraintStr)) continue;
|
|
126
|
-
results.push({
|
|
127
|
-
key: `${parsed.entity}.${attr.name}`,
|
|
128
|
-
value: constraintStr,
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Handle error code YAML blocks (HTTP status codes)
|
|
134
|
-
if (Array.isArray(parsed.errors)) {
|
|
135
|
-
for (const err of parsed.errors) {
|
|
136
|
-
if (!err || typeof err.code !== "string") continue;
|
|
137
|
-
if (typeof err.http === "number") {
|
|
138
|
-
results.push({
|
|
139
|
-
key: `error.${err.code}.http`,
|
|
140
|
-
value: String(err.http),
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
} catch {
|
|
146
|
-
// skip parse errors
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return results;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const normalizeValue = (value: string): string =>
|
|
154
|
-
value
|
|
155
|
-
.toLowerCase()
|
|
156
|
-
.replace(/[–—]/g, "-")
|
|
157
|
-
.replace(/`/g, "")
|
|
158
|
-
.replace(/\s+/g, " ")
|
|
159
|
-
.trim();
|
|
160
|
-
|
|
161
|
-
const hasNumeric = (value: string): boolean => /\d/.test(value);
|
|
162
|
-
|
|
163
|
-
// ─── Structured Conflict Detection ───
|
|
164
|
-
|
|
165
|
-
export interface IConstraintConflict {
|
|
166
|
-
key: string;
|
|
167
|
-
values: Array<{
|
|
168
|
-
display: string;
|
|
169
|
-
files: string[];
|
|
170
|
-
}>;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Detect numeric constraint conflicts across files as structured data.
|
|
175
|
-
*
|
|
176
|
-
* Returns an array of conflicts where the same constraint key has different
|
|
177
|
-
* normalized values across files.
|
|
178
|
-
*/
|
|
179
|
-
export const detectConstraintConflicts = (props: {
|
|
180
|
-
files: Array<{
|
|
181
|
-
file: AutoBeAnalyze.IFileScenario;
|
|
182
|
-
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
183
|
-
}>;
|
|
184
|
-
}): IConstraintConflict[] => {
|
|
185
|
-
const constraints: Map<string, ConstraintEntry> = new Map();
|
|
186
|
-
|
|
187
|
-
for (const { file, sectionEvents } of props.files) {
|
|
188
|
-
for (const sectionsForModule of sectionEvents) {
|
|
189
|
-
for (const sectionEvent of sectionsForModule) {
|
|
190
|
-
for (const section of sectionEvent.sectionSections) {
|
|
191
|
-
const pairs = extractConstraints(section.content);
|
|
192
|
-
for (const { key, value } of pairs) {
|
|
193
|
-
const normalized = normalizeValue(value);
|
|
194
|
-
if (!constraints.has(key)) {
|
|
195
|
-
constraints.set(key, { key, values: new Map() });
|
|
196
|
-
}
|
|
197
|
-
const entry = constraints.get(key)!;
|
|
198
|
-
if (!entry.values.has(normalized)) {
|
|
199
|
-
entry.values.set(normalized, {
|
|
200
|
-
normalized,
|
|
201
|
-
display: value.trim(),
|
|
202
|
-
sources: [],
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
entry.values.get(normalized)!.sources.push({
|
|
206
|
-
file,
|
|
207
|
-
sectionTitle: section.title,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return [...constraints.values()]
|
|
216
|
-
.filter((entry) => entry.values.size > 1)
|
|
217
|
-
.map((entry) => ({
|
|
218
|
-
key: entry.key,
|
|
219
|
-
values: [...entry.values.values()].map((v) => ({
|
|
220
|
-
display: v.display,
|
|
221
|
-
files: [...new Set(v.sources.map((s) => s.file.filename))],
|
|
222
|
-
})),
|
|
223
|
-
}));
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
/** Build a map from filename → list of conflict feedback strings. */
|
|
227
|
-
export const buildFileConflictMap = (
|
|
228
|
-
conflicts: IConstraintConflict[],
|
|
229
|
-
): Map<string, string[]> => {
|
|
230
|
-
const map: Map<string, string[]> = new Map();
|
|
231
|
-
|
|
232
|
-
for (const conflict of conflicts) {
|
|
233
|
-
const allFiles = new Set(conflict.values.flatMap((v) => v.files));
|
|
234
|
-
const feedback =
|
|
235
|
-
`${conflict.key} has conflicting values: ` +
|
|
236
|
-
conflict.values
|
|
237
|
-
.map((v) => `"${v.display}" in [${v.files.join(", ")}]`)
|
|
238
|
-
.join(" vs ");
|
|
239
|
-
|
|
240
|
-
for (const filename of allFiles) {
|
|
241
|
-
if (!map.has(filename)) map.set(filename, []);
|
|
242
|
-
map.get(filename)!.push(feedback);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return map;
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
// ─── Attribute Duplicate Detection ───
|
|
250
|
-
|
|
251
|
-
export interface IAttributeDuplicate {
|
|
252
|
-
key: string;
|
|
253
|
-
files: string[];
|
|
254
|
-
hasValueConflict: boolean;
|
|
255
|
-
values?: Array<{
|
|
256
|
-
specification: string;
|
|
257
|
-
files: string[];
|
|
258
|
-
}>;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Detect cross-file attribute duplication from YAML spec blocks.
|
|
263
|
-
*
|
|
264
|
-
* Returns attributes that are defined in YAML blocks across multiple files.
|
|
265
|
-
*/
|
|
266
|
-
export const detectAttributeDuplicates = (props: {
|
|
267
|
-
files: Array<{
|
|
268
|
-
file: AutoBeAnalyze.IFileScenario;
|
|
269
|
-
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
270
|
-
}>;
|
|
271
|
-
}): IAttributeDuplicate[] => {
|
|
272
|
-
// key → { normalized spec → { display, files } }
|
|
273
|
-
const attributes: Map<
|
|
274
|
-
string,
|
|
275
|
-
Map<string, { display: string; files: Set<string> }>
|
|
276
|
-
> = new Map();
|
|
277
|
-
const allFilesByKey: Map<string, Set<string>> = new Map();
|
|
278
|
-
|
|
279
|
-
for (const { file, sectionEvents } of props.files) {
|
|
280
|
-
for (const sectionsForModule of sectionEvents) {
|
|
281
|
-
for (const sectionEvent of sectionsForModule) {
|
|
282
|
-
for (const section of sectionEvent.sectionSections) {
|
|
283
|
-
const specs = extractAttributeSpecs(section.content);
|
|
284
|
-
for (const { key, specification } of specs) {
|
|
285
|
-
if (!allFilesByKey.has(key)) allFilesByKey.set(key, new Set());
|
|
286
|
-
allFilesByKey.get(key)!.add(file.filename);
|
|
287
|
-
|
|
288
|
-
if (!attributes.has(key)) attributes.set(key, new Map());
|
|
289
|
-
const specMap = attributes.get(key)!;
|
|
290
|
-
const normalized = normalizeValue(specification);
|
|
291
|
-
if (!specMap.has(normalized)) {
|
|
292
|
-
specMap.set(normalized, {
|
|
293
|
-
display: specification.trim(),
|
|
294
|
-
files: new Set(),
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
specMap.get(normalized)!.files.add(file.filename);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return [...allFilesByKey.entries()]
|
|
305
|
-
.filter(([, files]) => files.size > 1)
|
|
306
|
-
.map(([key, files]) => {
|
|
307
|
-
const specMap = attributes.get(key)!;
|
|
308
|
-
const hasValueConflict = specMap.size > 1;
|
|
309
|
-
return {
|
|
310
|
-
key,
|
|
311
|
-
files: [...files],
|
|
312
|
-
hasValueConflict,
|
|
313
|
-
...(hasValueConflict
|
|
314
|
-
? {
|
|
315
|
-
values: [...specMap.values()].map((v) => ({
|
|
316
|
-
specification: v.display,
|
|
317
|
-
files: [...v.files],
|
|
318
|
-
})),
|
|
319
|
-
}
|
|
320
|
-
: {}),
|
|
321
|
-
};
|
|
322
|
-
});
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
export const buildFileAttributeDuplicateMap = (
|
|
326
|
-
duplicates: IAttributeDuplicate[],
|
|
327
|
-
): Map<string, string[]> => {
|
|
328
|
-
const map: Map<string, string[]> = new Map();
|
|
329
|
-
|
|
330
|
-
for (const dup of duplicates) {
|
|
331
|
-
let feedback: string;
|
|
332
|
-
if (dup.hasValueConflict && dup.values) {
|
|
333
|
-
feedback =
|
|
334
|
-
`${dup.key} has conflicting specifications across files: ` +
|
|
335
|
-
dup.values
|
|
336
|
-
.map((v) => `"${v.specification}" in [${v.files.join(", ")}]`)
|
|
337
|
-
.join(" vs ") +
|
|
338
|
-
`. Align to ONE canonical definition.`;
|
|
339
|
-
} else {
|
|
340
|
-
feedback =
|
|
341
|
-
`${dup.key} is fully specified in multiple files: [${dup.files.join(", ")}]. ` +
|
|
342
|
-
`Only ONE file should contain the full spec.`;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
for (const filename of dup.files) {
|
|
346
|
-
if (!map.has(filename)) map.set(filename, []);
|
|
347
|
-
map.get(filename)!.push(feedback);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
return map;
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
// ─── Enum Conflict Detection ───
|
|
355
|
-
|
|
356
|
-
export interface IEnumConflict {
|
|
357
|
-
key: string;
|
|
358
|
-
values: Array<{
|
|
359
|
-
enumSet: string;
|
|
360
|
-
display: string;
|
|
361
|
-
files: string[];
|
|
362
|
-
}>;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Detect enum value conflicts from YAML spec blocks.
|
|
367
|
-
*
|
|
368
|
-
* Scans YAML attribute blocks for enum-like constraints and detects when
|
|
369
|
-
* different files define different enum value sets for the same attribute.
|
|
370
|
-
*/
|
|
371
|
-
export const detectEnumConflicts = (props: {
|
|
372
|
-
files: Array<{
|
|
373
|
-
file: AutoBeAnalyze.IFileScenario;
|
|
374
|
-
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
375
|
-
}>;
|
|
376
|
-
}): IEnumConflict[] => {
|
|
377
|
-
type EnumValue = {
|
|
378
|
-
enumSet: string;
|
|
379
|
-
display: string;
|
|
380
|
-
files: Set<string>;
|
|
381
|
-
};
|
|
382
|
-
const enums: Map<string, Map<string, EnumValue>> = new Map();
|
|
383
|
-
|
|
384
|
-
for (const { file, sectionEvents } of props.files) {
|
|
385
|
-
for (const sectionsForModule of sectionEvents) {
|
|
386
|
-
for (const sectionEvent of sectionsForModule) {
|
|
387
|
-
for (const section of sectionEvent.sectionSections) {
|
|
388
|
-
const specs = extractEnumSpecs(section.content);
|
|
389
|
-
for (const { key, enumSet, display } of specs) {
|
|
390
|
-
if (!enums.has(key)) enums.set(key, new Map());
|
|
391
|
-
const entry = enums.get(key)!;
|
|
392
|
-
if (!entry.has(enumSet)) {
|
|
393
|
-
entry.set(enumSet, { enumSet, display, files: new Set() });
|
|
394
|
-
}
|
|
395
|
-
entry.get(enumSet)!.files.add(file.filename);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
return [...enums.entries()]
|
|
403
|
-
.filter(([, values]) => values.size > 1)
|
|
404
|
-
.map(([key, values]) => ({
|
|
405
|
-
key,
|
|
406
|
-
values: [...values.values()].map((v) => ({
|
|
407
|
-
enumSet: v.enumSet,
|
|
408
|
-
display: v.display,
|
|
409
|
-
files: [...v.files],
|
|
410
|
-
})),
|
|
411
|
-
}));
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
export const buildFileEnumConflictMap = (
|
|
415
|
-
conflicts: IEnumConflict[],
|
|
416
|
-
): Map<string, string[]> => {
|
|
417
|
-
const map: Map<string, string[]> = new Map();
|
|
418
|
-
|
|
419
|
-
for (const conflict of conflicts) {
|
|
420
|
-
const allFiles = new Set(conflict.values.flatMap((v) => v.files));
|
|
421
|
-
const feedback =
|
|
422
|
-
`${conflict.key} has conflicting enum values: ` +
|
|
423
|
-
conflict.values
|
|
424
|
-
.map((v) => `enum(${v.enumSet}) in [${v.files.join(", ")}]`)
|
|
425
|
-
.join(" vs ");
|
|
426
|
-
|
|
427
|
-
for (const filename of allFiles) {
|
|
428
|
-
if (!map.has(filename)) map.set(filename, []);
|
|
429
|
-
map.get(filename)!.push(feedback);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
return map;
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
// ─── Permission Rule Conflict Detection ───
|
|
437
|
-
|
|
438
|
-
export interface IPermissionConflict {
|
|
439
|
-
actorOperation: string;
|
|
440
|
-
rules: Array<{
|
|
441
|
-
condition: string;
|
|
442
|
-
files: string[];
|
|
443
|
-
}>;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Detect permission rule conflicts from YAML spec blocks.
|
|
448
|
-
*
|
|
449
|
-
* A conflict occurs when one YAML block allows an action but another doesn't
|
|
450
|
-
* include it for the same actor+resource.
|
|
451
|
-
*/
|
|
452
|
-
export const detectPermissionConflicts = (props: {
|
|
453
|
-
files: Array<{
|
|
454
|
-
file: AutoBeAnalyze.IFileScenario;
|
|
455
|
-
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
456
|
-
}>;
|
|
457
|
-
}): IPermissionConflict[] => {
|
|
458
|
-
// actor:resource → action → Set<filename>
|
|
459
|
-
const ruleMap: Map<string, Map<string, Set<string>>> = new Map();
|
|
460
|
-
|
|
461
|
-
for (const { file, sectionEvents } of props.files) {
|
|
462
|
-
for (const sectionsForModule of sectionEvents) {
|
|
463
|
-
for (const sectionEvent of sectionsForModule) {
|
|
464
|
-
for (const section of sectionEvent.sectionSections) {
|
|
465
|
-
const rules = extractPermissionRulesFromYaml(section.content);
|
|
466
|
-
for (const { actor, resource, actions } of rules) {
|
|
467
|
-
const key = `${actor.toLowerCase()}:${resource}`;
|
|
468
|
-
if (!ruleMap.has(key)) ruleMap.set(key, new Map());
|
|
469
|
-
const actionMap = ruleMap.get(key)!;
|
|
470
|
-
for (const action of actions) {
|
|
471
|
-
const normAction = action.toLowerCase();
|
|
472
|
-
if (!actionMap.has(normAction))
|
|
473
|
-
actionMap.set(normAction, new Set());
|
|
474
|
-
actionMap.get(normAction)!.add(file.filename);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// Permission conflicts are rare in YAML-based approach since
|
|
483
|
-
// 01-actors-and-auth is the canonical source. Return empty for now.
|
|
484
|
-
return [];
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
export const buildFilePermissionConflictMap = (
|
|
488
|
-
conflicts: IPermissionConflict[],
|
|
489
|
-
): Map<string, string[]> => {
|
|
490
|
-
const map: Map<string, string[]> = new Map();
|
|
491
|
-
|
|
492
|
-
for (const conflict of conflicts) {
|
|
493
|
-
const allFiles = new Set(conflict.rules.flatMap((r) => r.files));
|
|
494
|
-
const feedback =
|
|
495
|
-
`Permission conflict for "${conflict.actorOperation}": ` +
|
|
496
|
-
conflict.rules
|
|
497
|
-
.map((r) => `"${r.condition}" in [${r.files.join(", ")}]`)
|
|
498
|
-
.join(" vs ");
|
|
499
|
-
|
|
500
|
-
for (const filename of allFiles) {
|
|
501
|
-
if (!map.has(filename)) map.set(filename, []);
|
|
502
|
-
map.get(filename)!.push(feedback);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
return map;
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
// ─── State Field Conflict Detection ───
|
|
510
|
-
|
|
511
|
-
export interface IStateFieldConflict {
|
|
512
|
-
entity: string;
|
|
513
|
-
conflictType: string;
|
|
514
|
-
fields: Array<{
|
|
515
|
-
fieldName: string;
|
|
516
|
-
specification: string;
|
|
517
|
-
files: string[];
|
|
518
|
-
}>;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* Detect state field conflicts from YAML spec blocks.
|
|
523
|
-
*
|
|
524
|
-
* Known contradiction patterns:
|
|
525
|
-
*
|
|
526
|
-
* 1. Same entity has both `deletedAt` (datetime) and `isDeleted` (boolean)
|
|
527
|
-
* 2. Same entity has `status` (enum) and semantically equivalent `is*` booleans
|
|
528
|
-
*/
|
|
529
|
-
export const detectStateFieldConflicts = (props: {
|
|
530
|
-
files: Array<{
|
|
531
|
-
file: AutoBeAnalyze.IFileScenario;
|
|
532
|
-
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
533
|
-
}>;
|
|
534
|
-
}): IStateFieldConflict[] => {
|
|
535
|
-
// entity → { fieldName → { specification, files } }
|
|
536
|
-
const entityFields: Map<
|
|
537
|
-
string,
|
|
538
|
-
Map<string, { specification: string; files: Set<string> }>
|
|
539
|
-
> = new Map();
|
|
540
|
-
|
|
541
|
-
for (const { file, sectionEvents } of props.files) {
|
|
542
|
-
for (const sectionsForModule of sectionEvents) {
|
|
543
|
-
for (const sectionEvent of sectionsForModule) {
|
|
544
|
-
for (const section of sectionEvent.sectionSections) {
|
|
545
|
-
const specs = extractAttributeSpecs(section.content);
|
|
546
|
-
for (const { key, specification } of specs) {
|
|
547
|
-
const dotIndex = key.indexOf(".");
|
|
548
|
-
if (dotIndex < 0) continue;
|
|
549
|
-
const entity = key.slice(0, dotIndex);
|
|
550
|
-
const field = key.slice(dotIndex + 1).toLowerCase();
|
|
551
|
-
|
|
552
|
-
if (!entityFields.has(entity)) entityFields.set(entity, new Map());
|
|
553
|
-
const fields = entityFields.get(entity)!;
|
|
554
|
-
if (!fields.has(field))
|
|
555
|
-
fields.set(field, { specification, files: new Set() });
|
|
556
|
-
fields.get(field)!.files.add(file.filename);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
const conflicts: IStateFieldConflict[] = [];
|
|
564
|
-
|
|
565
|
-
for (const [entity, fields] of entityFields) {
|
|
566
|
-
const fieldNames = [...fields.keys()];
|
|
567
|
-
|
|
568
|
-
// Pattern 1: deletedAt + isDeleted on same entity
|
|
569
|
-
const hasDeletedAt = fieldNames.some(
|
|
570
|
-
(f) => f === "deletedat" || f === "deleted_at",
|
|
571
|
-
);
|
|
572
|
-
const hasIsDeleted = fieldNames.some(
|
|
573
|
-
(f) => f === "isdeleted" || f === "is_deleted",
|
|
574
|
-
);
|
|
575
|
-
|
|
576
|
-
if (hasDeletedAt && hasIsDeleted) {
|
|
577
|
-
const deletedAtField =
|
|
578
|
-
fields.get("deletedat") ?? fields.get("deleted_at");
|
|
579
|
-
const isDeletedField =
|
|
580
|
-
fields.get("isdeleted") ?? fields.get("is_deleted");
|
|
581
|
-
|
|
582
|
-
if (deletedAtField && isDeletedField) {
|
|
583
|
-
conflicts.push({
|
|
584
|
-
entity,
|
|
585
|
-
conflictType: "deletedAt vs isDeleted",
|
|
586
|
-
fields: [
|
|
587
|
-
{
|
|
588
|
-
fieldName: "deletedAt",
|
|
589
|
-
specification: deletedAtField.specification,
|
|
590
|
-
files: [...deletedAtField.files],
|
|
591
|
-
},
|
|
592
|
-
{
|
|
593
|
-
fieldName: "isDeleted",
|
|
594
|
-
specification: isDeletedField.specification,
|
|
595
|
-
files: [...isDeletedField.files],
|
|
596
|
-
},
|
|
597
|
-
],
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// Pattern 2: status (enum) + is* booleans
|
|
603
|
-
const statusField = fields.get("status");
|
|
604
|
-
if (statusField && /enum/i.test(statusField.specification)) {
|
|
605
|
-
const isBooleans = fieldNames.filter(
|
|
606
|
-
(f) =>
|
|
607
|
-
f.startsWith("is") && /boolean/i.test(fields.get(f)!.specification),
|
|
608
|
-
);
|
|
609
|
-
|
|
610
|
-
for (const boolField of isBooleans) {
|
|
611
|
-
const concept = boolField.slice(2).toLowerCase();
|
|
612
|
-
if (statusField.specification.toLowerCase().includes(concept)) {
|
|
613
|
-
const boolEntry = fields.get(boolField)!;
|
|
614
|
-
conflicts.push({
|
|
615
|
-
entity,
|
|
616
|
-
conflictType: `status enum includes "${concept}" but separate is${concept.charAt(0).toUpperCase() + concept.slice(1)} boolean also exists`,
|
|
617
|
-
fields: [
|
|
618
|
-
{
|
|
619
|
-
fieldName: "status",
|
|
620
|
-
specification: statusField.specification,
|
|
621
|
-
files: [...statusField.files],
|
|
622
|
-
},
|
|
623
|
-
{
|
|
624
|
-
fieldName: boolField,
|
|
625
|
-
specification: boolEntry.specification,
|
|
626
|
-
files: [...boolEntry.files],
|
|
627
|
-
},
|
|
628
|
-
],
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
return conflicts;
|
|
636
|
-
};
|
|
637
|
-
|
|
638
|
-
export const buildFileStateFieldConflictMap = (
|
|
639
|
-
conflicts: IStateFieldConflict[],
|
|
640
|
-
): Map<string, string[]> => {
|
|
641
|
-
const map: Map<string, string[]> = new Map();
|
|
642
|
-
|
|
643
|
-
for (const conflict of conflicts) {
|
|
644
|
-
const allFiles = new Set(conflict.fields.flatMap((f) => f.files));
|
|
645
|
-
const feedback =
|
|
646
|
-
`State field conflict for "${conflict.entity}": ${conflict.conflictType}. ` +
|
|
647
|
-
conflict.fields
|
|
648
|
-
.map(
|
|
649
|
-
(f) =>
|
|
650
|
-
`"${f.fieldName}: ${f.specification}" in [${f.files.join(", ")}]`,
|
|
651
|
-
)
|
|
652
|
-
.join(" vs ") +
|
|
653
|
-
`. Use ONE canonical approach.`;
|
|
654
|
-
|
|
655
|
-
for (const filename of allFiles) {
|
|
656
|
-
if (!map.has(filename)) map.set(filename, []);
|
|
657
|
-
map.get(filename)!.push(feedback);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
return map;
|
|
662
|
-
};
|
|
663
|
-
|
|
664
|
-
// ─── YAML-based Attribute Specs Extraction (shared) ───
|
|
665
|
-
|
|
666
|
-
const ENUM_PATTERN = /enum\s*[\(\[\{]([^)\]\}]+)[\)\]\}]/i;
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* Extract attribute specs from YAML code blocks.
|
|
670
|
-
*
|
|
671
|
-
* Parses YAML blocks with `entity` + `attributes` structure and returns
|
|
672
|
-
* Entity.attribute → constraints pairs.
|
|
673
|
-
*/
|
|
674
|
-
const extractAttributeSpecs = (
|
|
675
|
-
content: string,
|
|
676
|
-
): Array<{ key: string; specification: string }> => {
|
|
677
|
-
if (!content) return [];
|
|
678
|
-
const results: Array<{ key: string; specification: string }> = [];
|
|
679
|
-
const yamlMatches = content.matchAll(YAML_CODE_BLOCK_REGEX);
|
|
680
|
-
|
|
681
|
-
for (const match of yamlMatches) {
|
|
682
|
-
const yamlContent = match[1] ?? "";
|
|
683
|
-
try {
|
|
684
|
-
const parsed = YAML.parse(yamlContent);
|
|
685
|
-
if (
|
|
686
|
-
!parsed ||
|
|
687
|
-
typeof parsed !== "object" ||
|
|
688
|
-
typeof parsed.entity !== "string" ||
|
|
689
|
-
!Array.isArray(parsed.attributes)
|
|
690
|
-
)
|
|
691
|
-
continue;
|
|
692
|
-
|
|
693
|
-
for (const attr of parsed.attributes) {
|
|
694
|
-
if (!attr || typeof attr.name !== "string") continue;
|
|
695
|
-
const spec = [
|
|
696
|
-
attr.type ? String(attr.type) : "",
|
|
697
|
-
attr.constraints ? String(attr.constraints) : "",
|
|
698
|
-
]
|
|
699
|
-
.filter(Boolean)
|
|
700
|
-
.join(", ");
|
|
701
|
-
if (!spec) continue;
|
|
702
|
-
results.push({
|
|
703
|
-
key: `${parsed.entity}.${attr.name}`,
|
|
704
|
-
specification: spec,
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
} catch {
|
|
708
|
-
// skip parse errors
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
return results;
|
|
713
|
-
};
|
|
714
|
-
|
|
715
|
-
/** Extract enum specs from YAML attribute blocks. */
|
|
716
|
-
const extractEnumSpecs = (
|
|
717
|
-
content: string,
|
|
718
|
-
): Array<{ key: string; enumSet: string; display: string }> => {
|
|
719
|
-
if (!content) return [];
|
|
720
|
-
const results: Array<{ key: string; enumSet: string; display: string }> = [];
|
|
721
|
-
const yamlMatches = content.matchAll(YAML_CODE_BLOCK_REGEX);
|
|
722
|
-
|
|
723
|
-
for (const match of yamlMatches) {
|
|
724
|
-
const yamlContent = match[1] ?? "";
|
|
725
|
-
try {
|
|
726
|
-
const parsed = YAML.parse(yamlContent);
|
|
727
|
-
if (
|
|
728
|
-
!parsed ||
|
|
729
|
-
typeof parsed !== "object" ||
|
|
730
|
-
typeof parsed.entity !== "string" ||
|
|
731
|
-
!Array.isArray(parsed.attributes)
|
|
732
|
-
)
|
|
733
|
-
continue;
|
|
734
|
-
|
|
735
|
-
for (const attr of parsed.attributes) {
|
|
736
|
-
if (!attr || typeof attr.name !== "string") continue;
|
|
737
|
-
const typeStr = String(attr.type ?? "");
|
|
738
|
-
const constraintStr = String(attr.constraints ?? "");
|
|
739
|
-
const combined = `${typeStr} ${constraintStr}`;
|
|
740
|
-
|
|
741
|
-
const enumMatch = combined.match(ENUM_PATTERN);
|
|
742
|
-
if (!enumMatch) continue;
|
|
743
|
-
|
|
744
|
-
const rawEnumValues = enumMatch[1]!;
|
|
745
|
-
const enumSet = [
|
|
746
|
-
...new Set(
|
|
747
|
-
rawEnumValues
|
|
748
|
-
.split(/[|,]/)
|
|
749
|
-
.map((v) => v.trim().toLowerCase())
|
|
750
|
-
.filter((v) => v.length > 0),
|
|
751
|
-
),
|
|
752
|
-
]
|
|
753
|
-
.sort()
|
|
754
|
-
.join("|");
|
|
755
|
-
|
|
756
|
-
results.push({
|
|
757
|
-
key: `${parsed.entity}.${attr.name}`,
|
|
758
|
-
enumSet,
|
|
759
|
-
display: combined.trim(),
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
} catch {
|
|
763
|
-
// skip parse errors
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
return results;
|
|
768
|
-
};
|
|
769
|
-
|
|
770
|
-
/** Extract permission rules from YAML spec blocks. */
|
|
771
|
-
const extractPermissionRulesFromYaml = (
|
|
772
|
-
content: string,
|
|
773
|
-
): Array<{ actor: string; resource: string; actions: string[] }> => {
|
|
774
|
-
if (!content) return [];
|
|
775
|
-
const results: Array<{
|
|
776
|
-
actor: string;
|
|
777
|
-
resource: string;
|
|
778
|
-
actions: string[];
|
|
779
|
-
}> = [];
|
|
780
|
-
const yamlMatches = content.matchAll(YAML_CODE_BLOCK_REGEX);
|
|
781
|
-
|
|
782
|
-
for (const match of yamlMatches) {
|
|
783
|
-
const yamlContent = match[1] ?? "";
|
|
784
|
-
try {
|
|
785
|
-
const parsed = YAML.parse(yamlContent);
|
|
786
|
-
if (
|
|
787
|
-
!parsed ||
|
|
788
|
-
typeof parsed !== "object" ||
|
|
789
|
-
!Array.isArray(parsed.permissions)
|
|
790
|
-
)
|
|
791
|
-
continue;
|
|
792
|
-
|
|
793
|
-
for (const perm of parsed.permissions) {
|
|
794
|
-
if (
|
|
795
|
-
!perm ||
|
|
796
|
-
typeof perm.actor !== "string" ||
|
|
797
|
-
typeof perm.resource !== "string" ||
|
|
798
|
-
!Array.isArray(perm.actions)
|
|
799
|
-
)
|
|
800
|
-
continue;
|
|
801
|
-
results.push({
|
|
802
|
-
actor: perm.actor,
|
|
803
|
-
resource: perm.resource,
|
|
804
|
-
actions: perm.actions.map(String),
|
|
805
|
-
});
|
|
806
|
-
}
|
|
807
|
-
} catch {
|
|
808
|
-
// skip parse errors
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
return results;
|
|
813
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
AutoBeAnalyze,
|
|
3
|
+
AutoBeAnalyzeWriteSectionEvent,
|
|
4
|
+
} from "@autobe/interface";
|
|
5
|
+
import YAML from "yaml";
|
|
6
|
+
|
|
7
|
+
type ConstraintSource = {
|
|
8
|
+
file: AutoBeAnalyze.IFileScenario;
|
|
9
|
+
sectionTitle: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type ConstraintValue = {
|
|
13
|
+
normalized: string;
|
|
14
|
+
display: string;
|
|
15
|
+
sources: ConstraintSource[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type ConstraintEntry = {
|
|
19
|
+
key: string;
|
|
20
|
+
values: Map<string, ConstraintValue>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const YAML_CODE_BLOCK_REGEX = /```yaml\n([\s\S]*?)```/g;
|
|
24
|
+
|
|
25
|
+
export const buildConstraintConsistencyReport = (props: {
|
|
26
|
+
files: Array<{
|
|
27
|
+
file: AutoBeAnalyze.IFileScenario;
|
|
28
|
+
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
29
|
+
}>;
|
|
30
|
+
}): string => {
|
|
31
|
+
const constraints: Map<string, ConstraintEntry> = new Map();
|
|
32
|
+
let totalConstraints: number = 0;
|
|
33
|
+
|
|
34
|
+
for (const { file, sectionEvents } of props.files) {
|
|
35
|
+
for (const sectionsForModule of sectionEvents) {
|
|
36
|
+
for (const sectionEvent of sectionsForModule) {
|
|
37
|
+
for (const section of sectionEvent.sectionSections) {
|
|
38
|
+
const pairs = extractConstraints(section.content);
|
|
39
|
+
for (const { key, value } of pairs) {
|
|
40
|
+
totalConstraints++;
|
|
41
|
+
const normalized = normalizeValue(value);
|
|
42
|
+
if (!constraints.has(key)) {
|
|
43
|
+
constraints.set(key, {
|
|
44
|
+
key,
|
|
45
|
+
values: new Map(),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const entry = constraints.get(key)!;
|
|
49
|
+
if (!entry.values.has(normalized)) {
|
|
50
|
+
entry.values.set(normalized, {
|
|
51
|
+
normalized,
|
|
52
|
+
display: value.trim(),
|
|
53
|
+
sources: [],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
entry.values.get(normalized)!.sources.push({
|
|
57
|
+
file,
|
|
58
|
+
sectionTitle: section.title,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const conflicts: ConstraintEntry[] = [...constraints.values()].filter(
|
|
67
|
+
(entry) => entry.values.size > 1,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (conflicts.length === 0) {
|
|
71
|
+
return [
|
|
72
|
+
"No numeric constraint conflicts detected.",
|
|
73
|
+
`Scanned ${totalConstraints} numeric constraints from YAML spec blocks.`,
|
|
74
|
+
].join("\n");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const lines: string[] = [
|
|
78
|
+
`Detected ${conflicts.length} numeric constraint conflict(s).`,
|
|
79
|
+
`Scanned ${totalConstraints} numeric constraints from YAML spec blocks.`,
|
|
80
|
+
"",
|
|
81
|
+
"Conflicts:",
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
for (const entry of conflicts) {
|
|
85
|
+
lines.push(`- ${entry.key}:`);
|
|
86
|
+
for (const value of entry.values.values()) {
|
|
87
|
+
const sources = value.sources
|
|
88
|
+
.map((s) => `${s.file.filename} → ${s.sectionTitle}`)
|
|
89
|
+
.slice(0, 6)
|
|
90
|
+
.join("; ");
|
|
91
|
+
lines.push(` - ${value.display} (e.g., ${sources})`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return lines.join("\n");
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Extract numeric constraints from YAML spec blocks.
|
|
100
|
+
*
|
|
101
|
+
* Parses YAML code blocks and extracts Entity.attribute constraints that
|
|
102
|
+
* contain numeric values (e.g., length limits, quantity limits).
|
|
103
|
+
*/
|
|
104
|
+
const extractConstraints = (
|
|
105
|
+
content: string,
|
|
106
|
+
): Array<{ key: string; value: string }> => {
|
|
107
|
+
if (!content) return [];
|
|
108
|
+
const results: Array<{ key: string; value: string }> = [];
|
|
109
|
+
const yamlMatches = content.matchAll(YAML_CODE_BLOCK_REGEX);
|
|
110
|
+
|
|
111
|
+
for (const match of yamlMatches) {
|
|
112
|
+
const yamlContent = match[1] ?? "";
|
|
113
|
+
try {
|
|
114
|
+
const parsed = YAML.parse(yamlContent);
|
|
115
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
116
|
+
|
|
117
|
+
// Handle entity attribute YAML blocks
|
|
118
|
+
if (
|
|
119
|
+
typeof parsed.entity === "string" &&
|
|
120
|
+
Array.isArray(parsed.attributes)
|
|
121
|
+
) {
|
|
122
|
+
for (const attr of parsed.attributes) {
|
|
123
|
+
if (!attr || typeof attr.name !== "string") continue;
|
|
124
|
+
const constraintStr = String(attr.constraints ?? "");
|
|
125
|
+
if (!hasNumeric(constraintStr)) continue;
|
|
126
|
+
results.push({
|
|
127
|
+
key: `${parsed.entity}.${attr.name}`,
|
|
128
|
+
value: constraintStr,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Handle error code YAML blocks (HTTP status codes)
|
|
134
|
+
if (Array.isArray(parsed.errors)) {
|
|
135
|
+
for (const err of parsed.errors) {
|
|
136
|
+
if (!err || typeof err.code !== "string") continue;
|
|
137
|
+
if (typeof err.http === "number") {
|
|
138
|
+
results.push({
|
|
139
|
+
key: `error.${err.code}.http`,
|
|
140
|
+
value: String(err.http),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
// skip parse errors
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return results;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const normalizeValue = (value: string): string =>
|
|
154
|
+
value
|
|
155
|
+
.toLowerCase()
|
|
156
|
+
.replace(/[–—]/g, "-")
|
|
157
|
+
.replace(/`/g, "")
|
|
158
|
+
.replace(/\s+/g, " ")
|
|
159
|
+
.trim();
|
|
160
|
+
|
|
161
|
+
const hasNumeric = (value: string): boolean => /\d/.test(value);
|
|
162
|
+
|
|
163
|
+
// ─── Structured Conflict Detection ───
|
|
164
|
+
|
|
165
|
+
export interface IConstraintConflict {
|
|
166
|
+
key: string;
|
|
167
|
+
values: Array<{
|
|
168
|
+
display: string;
|
|
169
|
+
files: string[];
|
|
170
|
+
}>;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Detect numeric constraint conflicts across files as structured data.
|
|
175
|
+
*
|
|
176
|
+
* Returns an array of conflicts where the same constraint key has different
|
|
177
|
+
* normalized values across files.
|
|
178
|
+
*/
|
|
179
|
+
export const detectConstraintConflicts = (props: {
|
|
180
|
+
files: Array<{
|
|
181
|
+
file: AutoBeAnalyze.IFileScenario;
|
|
182
|
+
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
183
|
+
}>;
|
|
184
|
+
}): IConstraintConflict[] => {
|
|
185
|
+
const constraints: Map<string, ConstraintEntry> = new Map();
|
|
186
|
+
|
|
187
|
+
for (const { file, sectionEvents } of props.files) {
|
|
188
|
+
for (const sectionsForModule of sectionEvents) {
|
|
189
|
+
for (const sectionEvent of sectionsForModule) {
|
|
190
|
+
for (const section of sectionEvent.sectionSections) {
|
|
191
|
+
const pairs = extractConstraints(section.content);
|
|
192
|
+
for (const { key, value } of pairs) {
|
|
193
|
+
const normalized = normalizeValue(value);
|
|
194
|
+
if (!constraints.has(key)) {
|
|
195
|
+
constraints.set(key, { key, values: new Map() });
|
|
196
|
+
}
|
|
197
|
+
const entry = constraints.get(key)!;
|
|
198
|
+
if (!entry.values.has(normalized)) {
|
|
199
|
+
entry.values.set(normalized, {
|
|
200
|
+
normalized,
|
|
201
|
+
display: value.trim(),
|
|
202
|
+
sources: [],
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
entry.values.get(normalized)!.sources.push({
|
|
206
|
+
file,
|
|
207
|
+
sectionTitle: section.title,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return [...constraints.values()]
|
|
216
|
+
.filter((entry) => entry.values.size > 1)
|
|
217
|
+
.map((entry) => ({
|
|
218
|
+
key: entry.key,
|
|
219
|
+
values: [...entry.values.values()].map((v) => ({
|
|
220
|
+
display: v.display,
|
|
221
|
+
files: [...new Set(v.sources.map((s) => s.file.filename))],
|
|
222
|
+
})),
|
|
223
|
+
}));
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/** Build a map from filename → list of conflict feedback strings. */
|
|
227
|
+
export const buildFileConflictMap = (
|
|
228
|
+
conflicts: IConstraintConflict[],
|
|
229
|
+
): Map<string, string[]> => {
|
|
230
|
+
const map: Map<string, string[]> = new Map();
|
|
231
|
+
|
|
232
|
+
for (const conflict of conflicts) {
|
|
233
|
+
const allFiles = new Set(conflict.values.flatMap((v) => v.files));
|
|
234
|
+
const feedback =
|
|
235
|
+
`${conflict.key} has conflicting values: ` +
|
|
236
|
+
conflict.values
|
|
237
|
+
.map((v) => `"${v.display}" in [${v.files.join(", ")}]`)
|
|
238
|
+
.join(" vs ");
|
|
239
|
+
|
|
240
|
+
for (const filename of allFiles) {
|
|
241
|
+
if (!map.has(filename)) map.set(filename, []);
|
|
242
|
+
map.get(filename)!.push(feedback);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return map;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// ─── Attribute Duplicate Detection ───
|
|
250
|
+
|
|
251
|
+
export interface IAttributeDuplicate {
|
|
252
|
+
key: string;
|
|
253
|
+
files: string[];
|
|
254
|
+
hasValueConflict: boolean;
|
|
255
|
+
values?: Array<{
|
|
256
|
+
specification: string;
|
|
257
|
+
files: string[];
|
|
258
|
+
}>;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Detect cross-file attribute duplication from YAML spec blocks.
|
|
263
|
+
*
|
|
264
|
+
* Returns attributes that are defined in YAML blocks across multiple files.
|
|
265
|
+
*/
|
|
266
|
+
export const detectAttributeDuplicates = (props: {
|
|
267
|
+
files: Array<{
|
|
268
|
+
file: AutoBeAnalyze.IFileScenario;
|
|
269
|
+
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
270
|
+
}>;
|
|
271
|
+
}): IAttributeDuplicate[] => {
|
|
272
|
+
// key → { normalized spec → { display, files } }
|
|
273
|
+
const attributes: Map<
|
|
274
|
+
string,
|
|
275
|
+
Map<string, { display: string; files: Set<string> }>
|
|
276
|
+
> = new Map();
|
|
277
|
+
const allFilesByKey: Map<string, Set<string>> = new Map();
|
|
278
|
+
|
|
279
|
+
for (const { file, sectionEvents } of props.files) {
|
|
280
|
+
for (const sectionsForModule of sectionEvents) {
|
|
281
|
+
for (const sectionEvent of sectionsForModule) {
|
|
282
|
+
for (const section of sectionEvent.sectionSections) {
|
|
283
|
+
const specs = extractAttributeSpecs(section.content);
|
|
284
|
+
for (const { key, specification } of specs) {
|
|
285
|
+
if (!allFilesByKey.has(key)) allFilesByKey.set(key, new Set());
|
|
286
|
+
allFilesByKey.get(key)!.add(file.filename);
|
|
287
|
+
|
|
288
|
+
if (!attributes.has(key)) attributes.set(key, new Map());
|
|
289
|
+
const specMap = attributes.get(key)!;
|
|
290
|
+
const normalized = normalizeValue(specification);
|
|
291
|
+
if (!specMap.has(normalized)) {
|
|
292
|
+
specMap.set(normalized, {
|
|
293
|
+
display: specification.trim(),
|
|
294
|
+
files: new Set(),
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
specMap.get(normalized)!.files.add(file.filename);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return [...allFilesByKey.entries()]
|
|
305
|
+
.filter(([, files]) => files.size > 1)
|
|
306
|
+
.map(([key, files]) => {
|
|
307
|
+
const specMap = attributes.get(key)!;
|
|
308
|
+
const hasValueConflict = specMap.size > 1;
|
|
309
|
+
return {
|
|
310
|
+
key,
|
|
311
|
+
files: [...files],
|
|
312
|
+
hasValueConflict,
|
|
313
|
+
...(hasValueConflict
|
|
314
|
+
? {
|
|
315
|
+
values: [...specMap.values()].map((v) => ({
|
|
316
|
+
specification: v.display,
|
|
317
|
+
files: [...v.files],
|
|
318
|
+
})),
|
|
319
|
+
}
|
|
320
|
+
: {}),
|
|
321
|
+
};
|
|
322
|
+
});
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
export const buildFileAttributeDuplicateMap = (
|
|
326
|
+
duplicates: IAttributeDuplicate[],
|
|
327
|
+
): Map<string, string[]> => {
|
|
328
|
+
const map: Map<string, string[]> = new Map();
|
|
329
|
+
|
|
330
|
+
for (const dup of duplicates) {
|
|
331
|
+
let feedback: string;
|
|
332
|
+
if (dup.hasValueConflict && dup.values) {
|
|
333
|
+
feedback =
|
|
334
|
+
`${dup.key} has conflicting specifications across files: ` +
|
|
335
|
+
dup.values
|
|
336
|
+
.map((v) => `"${v.specification}" in [${v.files.join(", ")}]`)
|
|
337
|
+
.join(" vs ") +
|
|
338
|
+
`. Align to ONE canonical definition.`;
|
|
339
|
+
} else {
|
|
340
|
+
feedback =
|
|
341
|
+
`${dup.key} is fully specified in multiple files: [${dup.files.join(", ")}]. ` +
|
|
342
|
+
`Only ONE file should contain the full spec.`;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
for (const filename of dup.files) {
|
|
346
|
+
if (!map.has(filename)) map.set(filename, []);
|
|
347
|
+
map.get(filename)!.push(feedback);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return map;
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// ─── Enum Conflict Detection ───
|
|
355
|
+
|
|
356
|
+
export interface IEnumConflict {
|
|
357
|
+
key: string;
|
|
358
|
+
values: Array<{
|
|
359
|
+
enumSet: string;
|
|
360
|
+
display: string;
|
|
361
|
+
files: string[];
|
|
362
|
+
}>;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Detect enum value conflicts from YAML spec blocks.
|
|
367
|
+
*
|
|
368
|
+
* Scans YAML attribute blocks for enum-like constraints and detects when
|
|
369
|
+
* different files define different enum value sets for the same attribute.
|
|
370
|
+
*/
|
|
371
|
+
export const detectEnumConflicts = (props: {
|
|
372
|
+
files: Array<{
|
|
373
|
+
file: AutoBeAnalyze.IFileScenario;
|
|
374
|
+
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
375
|
+
}>;
|
|
376
|
+
}): IEnumConflict[] => {
|
|
377
|
+
type EnumValue = {
|
|
378
|
+
enumSet: string;
|
|
379
|
+
display: string;
|
|
380
|
+
files: Set<string>;
|
|
381
|
+
};
|
|
382
|
+
const enums: Map<string, Map<string, EnumValue>> = new Map();
|
|
383
|
+
|
|
384
|
+
for (const { file, sectionEvents } of props.files) {
|
|
385
|
+
for (const sectionsForModule of sectionEvents) {
|
|
386
|
+
for (const sectionEvent of sectionsForModule) {
|
|
387
|
+
for (const section of sectionEvent.sectionSections) {
|
|
388
|
+
const specs = extractEnumSpecs(section.content);
|
|
389
|
+
for (const { key, enumSet, display } of specs) {
|
|
390
|
+
if (!enums.has(key)) enums.set(key, new Map());
|
|
391
|
+
const entry = enums.get(key)!;
|
|
392
|
+
if (!entry.has(enumSet)) {
|
|
393
|
+
entry.set(enumSet, { enumSet, display, files: new Set() });
|
|
394
|
+
}
|
|
395
|
+
entry.get(enumSet)!.files.add(file.filename);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return [...enums.entries()]
|
|
403
|
+
.filter(([, values]) => values.size > 1)
|
|
404
|
+
.map(([key, values]) => ({
|
|
405
|
+
key,
|
|
406
|
+
values: [...values.values()].map((v) => ({
|
|
407
|
+
enumSet: v.enumSet,
|
|
408
|
+
display: v.display,
|
|
409
|
+
files: [...v.files],
|
|
410
|
+
})),
|
|
411
|
+
}));
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
export const buildFileEnumConflictMap = (
|
|
415
|
+
conflicts: IEnumConflict[],
|
|
416
|
+
): Map<string, string[]> => {
|
|
417
|
+
const map: Map<string, string[]> = new Map();
|
|
418
|
+
|
|
419
|
+
for (const conflict of conflicts) {
|
|
420
|
+
const allFiles = new Set(conflict.values.flatMap((v) => v.files));
|
|
421
|
+
const feedback =
|
|
422
|
+
`${conflict.key} has conflicting enum values: ` +
|
|
423
|
+
conflict.values
|
|
424
|
+
.map((v) => `enum(${v.enumSet}) in [${v.files.join(", ")}]`)
|
|
425
|
+
.join(" vs ");
|
|
426
|
+
|
|
427
|
+
for (const filename of allFiles) {
|
|
428
|
+
if (!map.has(filename)) map.set(filename, []);
|
|
429
|
+
map.get(filename)!.push(feedback);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return map;
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
// ─── Permission Rule Conflict Detection ───
|
|
437
|
+
|
|
438
|
+
export interface IPermissionConflict {
|
|
439
|
+
actorOperation: string;
|
|
440
|
+
rules: Array<{
|
|
441
|
+
condition: string;
|
|
442
|
+
files: string[];
|
|
443
|
+
}>;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Detect permission rule conflicts from YAML spec blocks.
|
|
448
|
+
*
|
|
449
|
+
* A conflict occurs when one YAML block allows an action but another doesn't
|
|
450
|
+
* include it for the same actor+resource.
|
|
451
|
+
*/
|
|
452
|
+
export const detectPermissionConflicts = (props: {
|
|
453
|
+
files: Array<{
|
|
454
|
+
file: AutoBeAnalyze.IFileScenario;
|
|
455
|
+
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
456
|
+
}>;
|
|
457
|
+
}): IPermissionConflict[] => {
|
|
458
|
+
// actor:resource → action → Set<filename>
|
|
459
|
+
const ruleMap: Map<string, Map<string, Set<string>>> = new Map();
|
|
460
|
+
|
|
461
|
+
for (const { file, sectionEvents } of props.files) {
|
|
462
|
+
for (const sectionsForModule of sectionEvents) {
|
|
463
|
+
for (const sectionEvent of sectionsForModule) {
|
|
464
|
+
for (const section of sectionEvent.sectionSections) {
|
|
465
|
+
const rules = extractPermissionRulesFromYaml(section.content);
|
|
466
|
+
for (const { actor, resource, actions } of rules) {
|
|
467
|
+
const key = `${actor.toLowerCase()}:${resource}`;
|
|
468
|
+
if (!ruleMap.has(key)) ruleMap.set(key, new Map());
|
|
469
|
+
const actionMap = ruleMap.get(key)!;
|
|
470
|
+
for (const action of actions) {
|
|
471
|
+
const normAction = action.toLowerCase();
|
|
472
|
+
if (!actionMap.has(normAction))
|
|
473
|
+
actionMap.set(normAction, new Set());
|
|
474
|
+
actionMap.get(normAction)!.add(file.filename);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Permission conflicts are rare in YAML-based approach since
|
|
483
|
+
// 01-actors-and-auth is the canonical source. Return empty for now.
|
|
484
|
+
return [];
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
export const buildFilePermissionConflictMap = (
|
|
488
|
+
conflicts: IPermissionConflict[],
|
|
489
|
+
): Map<string, string[]> => {
|
|
490
|
+
const map: Map<string, string[]> = new Map();
|
|
491
|
+
|
|
492
|
+
for (const conflict of conflicts) {
|
|
493
|
+
const allFiles = new Set(conflict.rules.flatMap((r) => r.files));
|
|
494
|
+
const feedback =
|
|
495
|
+
`Permission conflict for "${conflict.actorOperation}": ` +
|
|
496
|
+
conflict.rules
|
|
497
|
+
.map((r) => `"${r.condition}" in [${r.files.join(", ")}]`)
|
|
498
|
+
.join(" vs ");
|
|
499
|
+
|
|
500
|
+
for (const filename of allFiles) {
|
|
501
|
+
if (!map.has(filename)) map.set(filename, []);
|
|
502
|
+
map.get(filename)!.push(feedback);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return map;
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// ─── State Field Conflict Detection ───
|
|
510
|
+
|
|
511
|
+
export interface IStateFieldConflict {
|
|
512
|
+
entity: string;
|
|
513
|
+
conflictType: string;
|
|
514
|
+
fields: Array<{
|
|
515
|
+
fieldName: string;
|
|
516
|
+
specification: string;
|
|
517
|
+
files: string[];
|
|
518
|
+
}>;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Detect state field conflicts from YAML spec blocks.
|
|
523
|
+
*
|
|
524
|
+
* Known contradiction patterns:
|
|
525
|
+
*
|
|
526
|
+
* 1. Same entity has both `deletedAt` (datetime) and `isDeleted` (boolean)
|
|
527
|
+
* 2. Same entity has `status` (enum) and semantically equivalent `is*` booleans
|
|
528
|
+
*/
|
|
529
|
+
export const detectStateFieldConflicts = (props: {
|
|
530
|
+
files: Array<{
|
|
531
|
+
file: AutoBeAnalyze.IFileScenario;
|
|
532
|
+
sectionEvents: AutoBeAnalyzeWriteSectionEvent[][];
|
|
533
|
+
}>;
|
|
534
|
+
}): IStateFieldConflict[] => {
|
|
535
|
+
// entity → { fieldName → { specification, files } }
|
|
536
|
+
const entityFields: Map<
|
|
537
|
+
string,
|
|
538
|
+
Map<string, { specification: string; files: Set<string> }>
|
|
539
|
+
> = new Map();
|
|
540
|
+
|
|
541
|
+
for (const { file, sectionEvents } of props.files) {
|
|
542
|
+
for (const sectionsForModule of sectionEvents) {
|
|
543
|
+
for (const sectionEvent of sectionsForModule) {
|
|
544
|
+
for (const section of sectionEvent.sectionSections) {
|
|
545
|
+
const specs = extractAttributeSpecs(section.content);
|
|
546
|
+
for (const { key, specification } of specs) {
|
|
547
|
+
const dotIndex = key.indexOf(".");
|
|
548
|
+
if (dotIndex < 0) continue;
|
|
549
|
+
const entity = key.slice(0, dotIndex);
|
|
550
|
+
const field = key.slice(dotIndex + 1).toLowerCase();
|
|
551
|
+
|
|
552
|
+
if (!entityFields.has(entity)) entityFields.set(entity, new Map());
|
|
553
|
+
const fields = entityFields.get(entity)!;
|
|
554
|
+
if (!fields.has(field))
|
|
555
|
+
fields.set(field, { specification, files: new Set() });
|
|
556
|
+
fields.get(field)!.files.add(file.filename);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const conflicts: IStateFieldConflict[] = [];
|
|
564
|
+
|
|
565
|
+
for (const [entity, fields] of entityFields) {
|
|
566
|
+
const fieldNames = [...fields.keys()];
|
|
567
|
+
|
|
568
|
+
// Pattern 1: deletedAt + isDeleted on same entity
|
|
569
|
+
const hasDeletedAt = fieldNames.some(
|
|
570
|
+
(f) => f === "deletedat" || f === "deleted_at",
|
|
571
|
+
);
|
|
572
|
+
const hasIsDeleted = fieldNames.some(
|
|
573
|
+
(f) => f === "isdeleted" || f === "is_deleted",
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
if (hasDeletedAt && hasIsDeleted) {
|
|
577
|
+
const deletedAtField =
|
|
578
|
+
fields.get("deletedat") ?? fields.get("deleted_at");
|
|
579
|
+
const isDeletedField =
|
|
580
|
+
fields.get("isdeleted") ?? fields.get("is_deleted");
|
|
581
|
+
|
|
582
|
+
if (deletedAtField && isDeletedField) {
|
|
583
|
+
conflicts.push({
|
|
584
|
+
entity,
|
|
585
|
+
conflictType: "deletedAt vs isDeleted",
|
|
586
|
+
fields: [
|
|
587
|
+
{
|
|
588
|
+
fieldName: "deletedAt",
|
|
589
|
+
specification: deletedAtField.specification,
|
|
590
|
+
files: [...deletedAtField.files],
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
fieldName: "isDeleted",
|
|
594
|
+
specification: isDeletedField.specification,
|
|
595
|
+
files: [...isDeletedField.files],
|
|
596
|
+
},
|
|
597
|
+
],
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Pattern 2: status (enum) + is* booleans
|
|
603
|
+
const statusField = fields.get("status");
|
|
604
|
+
if (statusField && /enum/i.test(statusField.specification)) {
|
|
605
|
+
const isBooleans = fieldNames.filter(
|
|
606
|
+
(f) =>
|
|
607
|
+
f.startsWith("is") && /boolean/i.test(fields.get(f)!.specification),
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
for (const boolField of isBooleans) {
|
|
611
|
+
const concept = boolField.slice(2).toLowerCase();
|
|
612
|
+
if (statusField.specification.toLowerCase().includes(concept)) {
|
|
613
|
+
const boolEntry = fields.get(boolField)!;
|
|
614
|
+
conflicts.push({
|
|
615
|
+
entity,
|
|
616
|
+
conflictType: `status enum includes "${concept}" but separate is${concept.charAt(0).toUpperCase() + concept.slice(1)} boolean also exists`,
|
|
617
|
+
fields: [
|
|
618
|
+
{
|
|
619
|
+
fieldName: "status",
|
|
620
|
+
specification: statusField.specification,
|
|
621
|
+
files: [...statusField.files],
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
fieldName: boolField,
|
|
625
|
+
specification: boolEntry.specification,
|
|
626
|
+
files: [...boolEntry.files],
|
|
627
|
+
},
|
|
628
|
+
],
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return conflicts;
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
export const buildFileStateFieldConflictMap = (
|
|
639
|
+
conflicts: IStateFieldConflict[],
|
|
640
|
+
): Map<string, string[]> => {
|
|
641
|
+
const map: Map<string, string[]> = new Map();
|
|
642
|
+
|
|
643
|
+
for (const conflict of conflicts) {
|
|
644
|
+
const allFiles = new Set(conflict.fields.flatMap((f) => f.files));
|
|
645
|
+
const feedback =
|
|
646
|
+
`State field conflict for "${conflict.entity}": ${conflict.conflictType}. ` +
|
|
647
|
+
conflict.fields
|
|
648
|
+
.map(
|
|
649
|
+
(f) =>
|
|
650
|
+
`"${f.fieldName}: ${f.specification}" in [${f.files.join(", ")}]`,
|
|
651
|
+
)
|
|
652
|
+
.join(" vs ") +
|
|
653
|
+
`. Use ONE canonical approach.`;
|
|
654
|
+
|
|
655
|
+
for (const filename of allFiles) {
|
|
656
|
+
if (!map.has(filename)) map.set(filename, []);
|
|
657
|
+
map.get(filename)!.push(feedback);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return map;
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
// ─── YAML-based Attribute Specs Extraction (shared) ───
|
|
665
|
+
|
|
666
|
+
const ENUM_PATTERN = /enum\s*[\(\[\{]([^)\]\}]+)[\)\]\}]/i;
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Extract attribute specs from YAML code blocks.
|
|
670
|
+
*
|
|
671
|
+
* Parses YAML blocks with `entity` + `attributes` structure and returns
|
|
672
|
+
* Entity.attribute → constraints pairs.
|
|
673
|
+
*/
|
|
674
|
+
const extractAttributeSpecs = (
|
|
675
|
+
content: string,
|
|
676
|
+
): Array<{ key: string; specification: string }> => {
|
|
677
|
+
if (!content) return [];
|
|
678
|
+
const results: Array<{ key: string; specification: string }> = [];
|
|
679
|
+
const yamlMatches = content.matchAll(YAML_CODE_BLOCK_REGEX);
|
|
680
|
+
|
|
681
|
+
for (const match of yamlMatches) {
|
|
682
|
+
const yamlContent = match[1] ?? "";
|
|
683
|
+
try {
|
|
684
|
+
const parsed = YAML.parse(yamlContent);
|
|
685
|
+
if (
|
|
686
|
+
!parsed ||
|
|
687
|
+
typeof parsed !== "object" ||
|
|
688
|
+
typeof parsed.entity !== "string" ||
|
|
689
|
+
!Array.isArray(parsed.attributes)
|
|
690
|
+
)
|
|
691
|
+
continue;
|
|
692
|
+
|
|
693
|
+
for (const attr of parsed.attributes) {
|
|
694
|
+
if (!attr || typeof attr.name !== "string") continue;
|
|
695
|
+
const spec = [
|
|
696
|
+
attr.type ? String(attr.type) : "",
|
|
697
|
+
attr.constraints ? String(attr.constraints) : "",
|
|
698
|
+
]
|
|
699
|
+
.filter(Boolean)
|
|
700
|
+
.join(", ");
|
|
701
|
+
if (!spec) continue;
|
|
702
|
+
results.push({
|
|
703
|
+
key: `${parsed.entity}.${attr.name}`,
|
|
704
|
+
specification: spec,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
} catch {
|
|
708
|
+
// skip parse errors
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
return results;
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
/** Extract enum specs from YAML attribute blocks. */
|
|
716
|
+
const extractEnumSpecs = (
|
|
717
|
+
content: string,
|
|
718
|
+
): Array<{ key: string; enumSet: string; display: string }> => {
|
|
719
|
+
if (!content) return [];
|
|
720
|
+
const results: Array<{ key: string; enumSet: string; display: string }> = [];
|
|
721
|
+
const yamlMatches = content.matchAll(YAML_CODE_BLOCK_REGEX);
|
|
722
|
+
|
|
723
|
+
for (const match of yamlMatches) {
|
|
724
|
+
const yamlContent = match[1] ?? "";
|
|
725
|
+
try {
|
|
726
|
+
const parsed = YAML.parse(yamlContent);
|
|
727
|
+
if (
|
|
728
|
+
!parsed ||
|
|
729
|
+
typeof parsed !== "object" ||
|
|
730
|
+
typeof parsed.entity !== "string" ||
|
|
731
|
+
!Array.isArray(parsed.attributes)
|
|
732
|
+
)
|
|
733
|
+
continue;
|
|
734
|
+
|
|
735
|
+
for (const attr of parsed.attributes) {
|
|
736
|
+
if (!attr || typeof attr.name !== "string") continue;
|
|
737
|
+
const typeStr = String(attr.type ?? "");
|
|
738
|
+
const constraintStr = String(attr.constraints ?? "");
|
|
739
|
+
const combined = `${typeStr} ${constraintStr}`;
|
|
740
|
+
|
|
741
|
+
const enumMatch = combined.match(ENUM_PATTERN);
|
|
742
|
+
if (!enumMatch) continue;
|
|
743
|
+
|
|
744
|
+
const rawEnumValues = enumMatch[1]!;
|
|
745
|
+
const enumSet = [
|
|
746
|
+
...new Set(
|
|
747
|
+
rawEnumValues
|
|
748
|
+
.split(/[|,]/)
|
|
749
|
+
.map((v) => v.trim().toLowerCase())
|
|
750
|
+
.filter((v) => v.length > 0),
|
|
751
|
+
),
|
|
752
|
+
]
|
|
753
|
+
.sort()
|
|
754
|
+
.join("|");
|
|
755
|
+
|
|
756
|
+
results.push({
|
|
757
|
+
key: `${parsed.entity}.${attr.name}`,
|
|
758
|
+
enumSet,
|
|
759
|
+
display: combined.trim(),
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
} catch {
|
|
763
|
+
// skip parse errors
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return results;
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
/** Extract permission rules from YAML spec blocks. */
|
|
771
|
+
const extractPermissionRulesFromYaml = (
|
|
772
|
+
content: string,
|
|
773
|
+
): Array<{ actor: string; resource: string; actions: string[] }> => {
|
|
774
|
+
if (!content) return [];
|
|
775
|
+
const results: Array<{
|
|
776
|
+
actor: string;
|
|
777
|
+
resource: string;
|
|
778
|
+
actions: string[];
|
|
779
|
+
}> = [];
|
|
780
|
+
const yamlMatches = content.matchAll(YAML_CODE_BLOCK_REGEX);
|
|
781
|
+
|
|
782
|
+
for (const match of yamlMatches) {
|
|
783
|
+
const yamlContent = match[1] ?? "";
|
|
784
|
+
try {
|
|
785
|
+
const parsed = YAML.parse(yamlContent);
|
|
786
|
+
if (
|
|
787
|
+
!parsed ||
|
|
788
|
+
typeof parsed !== "object" ||
|
|
789
|
+
!Array.isArray(parsed.permissions)
|
|
790
|
+
)
|
|
791
|
+
continue;
|
|
792
|
+
|
|
793
|
+
for (const perm of parsed.permissions) {
|
|
794
|
+
if (
|
|
795
|
+
!perm ||
|
|
796
|
+
typeof perm.actor !== "string" ||
|
|
797
|
+
typeof perm.resource !== "string" ||
|
|
798
|
+
!Array.isArray(perm.actions)
|
|
799
|
+
)
|
|
800
|
+
continue;
|
|
801
|
+
results.push({
|
|
802
|
+
actor: perm.actor,
|
|
803
|
+
resource: perm.resource,
|
|
804
|
+
actions: perm.actions.map(String),
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
} catch {
|
|
808
|
+
// skip parse errors
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
return results;
|
|
813
|
+
};
|